Represents a property path from a root ModelType with one or more steps.
Due to inheritance, property paths may branch as properties with similar names appear on siblings in the inheritance hierarchy.
Inheritance: IDisposable
Esempio n. 1
0
        /// <summary>
        /// Creates a new model filter for the specified root object.
        /// </summary>
        /// <param name="root"></param>
        /// <param name="paths"></param>
        public ModelFilter(ModelInstance root, string[] paths)
        {
            // Store the model root
            this.root = root;

            // Delay model notifications while loading the model
            ModelEventScope.Perform(() =>
            {
                // Subscribe to paths for each predicate
                ModelType rootType = root.Type;
                foreach (string p in paths)
                {
                    // Get the path for this predicate
                    ModelPath path = rootType.GetPath(p);

                    // Skip this predicate if it is already being monitored
                    if (pathModels.ContainsKey(path))
                    {
                        continue;
                    }

                    // Subscribe to path change events
                    path.Change += path_PathChanged;

                    // Get the model for this path and add the path and model to the list of paths
                    pathModels.Add(path, path.GetInstances(root));
                }
            });

            // Combine the models for each path into a universal model
            CombinePathModels();
        }
Esempio n. 2
0
        /// <summary>
        /// Attempts to initialize a <see cref="ModelSource"/> with the specified root type and path.
        /// </summary>
        /// <param name="rootType">The root type name, which is required for instance paths</param>
        /// <param name="path">The source path, which is either an instance path or a static path</param>
        /// <returns>True if the source was created, otherwise false</returns>
        bool InitializeFromTypeAndPath(ModelType rootType, string path, out ModelProperty sourceProperty)
        {
            // Instance Path
            ModelPath instancePath = null;

            sourceProperty = null;

            // clean up any array indices
            string indexFreePath = arraySyntaxRegex.Replace(path, "");

            try
            {
                if (rootType != null)
                {
                    rootType.TryGetPath(indexFreePath, out instancePath);
                }
            }
            catch
            { }
            if (instancePath != null)
            {
                InitializeFromModelPath(instancePath, path, out sourceProperty);
                return(true);
            }

            // Static Path
            else if (path.Contains('.'))
            {
                // Store the source path
                var sourceModelType = ModelContext.Current.GetModelType(path.Substring(0, path.LastIndexOf('.')));
                if (sourceModelType != null)
                {
                    sourceProperty = sourceModelType.Properties[path.Substring(path.LastIndexOf('.') + 1)];
                    if (sourceProperty != null && sourceProperty.IsStatic)
                    {
                        this.Path           = path;
                        this.IsStatic       = true;
                        this.SourceProperty = sourceProperty.Name;
                        this.SourceType     = sourceProperty.DeclaringType.Name;
                        return(true);
                    }
                }
            }
            return(false);
        }
Esempio n. 3
0
        /// <summary>
        /// Initialize the source from a valid <see cref="ModelPath"/>
        /// </summary>
        /// <param name="instancePath"></param>
        void InitializeFromModelPath(ModelPath instancePath, string path, out ModelProperty sourceProperty)
        {
            this.Path     = path;
            this.IsStatic = false;
            this.RootType = instancePath.RootType.Name;
            var tokens = tokenizer.Matches(path);

            this.steps = new SourceStep[tokens.Count];
            var rootType = instancePath.RootType;
            int i        = 0;

            sourceProperty = null;
            foreach (Match token in tokens)
            {
                sourceProperty = rootType.Properties[token.Groups["Property"].Value];
                if (sourceProperty == null)
                {
                    throw new ArgumentException(String.Format("Property {0} is not valid for type {1} in path {2}.", token.Groups["Property"].Value, rootType.Name, path));
                }
                int index;
                if (!Int32.TryParse(token.Groups["Index"].Value, out index))
                {
                    index = -1;
                }

                steps[i] = new SourceStep()
                {
                    Property = sourceProperty.Name, Index = index, DeclaringType = sourceProperty.DeclaringType, IsReferenceProperty = sourceProperty is ModelReferenceProperty
                };
                rootType = sourceProperty is ModelReferenceProperty ? ((ModelReferenceProperty)sourceProperty).PropertyType : null;
                i++;
            }

            this.SourceProperty = sourceProperty.Name;
            this.SourceType     = sourceProperty.DeclaringType.Name;
        }
Esempio n. 4
0
        /// <summary>
        /// Creates a new <see cref="ModelPath"/> instance for the specified root <see cref="ModelType"/>
        /// and path string.
        /// </summary>
        /// <param name="rootType"></param>
        /// <param name="path"></param>
        internal static ModelPath CreatePath(ModelType rootType, string path)
        {
            // Create the new path
            ModelPath newPath = new ModelPath()
            {
                RootType = rootType
            };

            // Create a temporary root step
            var root = new ModelStep(newPath);

            var steps = new List <ModelStep>()
            {
                root
            };
            var stack = new Stack <List <ModelStep> >();

            int expectedTokenStart = 0;

            var tokenMatches = pathParser.Matches(path);
            int tokenIndex   = 0;

            foreach (Match tokenMatch in tokenMatches)
            {
                // ensure there are no gaps between tokens except for whitespace
                if (tokenMatch.Index != expectedTokenStart && path.Substring(expectedTokenStart, tokenMatch.Index - expectedTokenStart - 1).Trim() != "")
                {
                    return(null);                    // throw new ArgumentException("The specified path, '" + path + "', is not valid. Character " + (expectedTokenStart));
                }
                expectedTokenStart = tokenMatch.Index + tokenMatch.Length;

                var token = tokenMatch.Value;
                switch (token[0])
                {
                case '{':
                    stack.Push(steps);
                    break;

                case '}':
                    steps = stack.Pop();
                    break;

                case ',':
                    steps = stack.Peek();
                    break;

                case '.':
                    break;

                case '<':
                    var filter = rootType.Context.GetModelType(token.Substring(1, token.Length - 2));
                    foreach (var step in steps)
                    {
                        if (step.Property is ModelReferenceProperty &&
                            (((ModelReferenceProperty)step.Property).PropertyType == filter ||
                             ((ModelReferenceProperty)step.Property).PropertyType.IsSubType(filter)))
                        {
                            step.Filter = filter;
                        }
                        else
                        {
                            RemoveStep(step);
                        }
                    }
                    break;

                default:

                    // Get the property name for the next step
                    var propertyName = token;

                    // Track the next steps
                    var nextSteps = new List <ModelStep>();


                    // Process each of the current steps
                    foreach (var step in steps)
                    {
                        // Ensure the current step is a valid reference property
                        if (step.Property != null && step.Property is ModelValueProperty)
                        {
                            return(null);                                    // throw new ArgumentException("Property '" + step.Property.Name + "' is a value property and cannot have child properties specified.");
                        }
                        // Get the type of the current step
                        var currentType = step.Property != null ? ((ModelReferenceProperty)step.Property).PropertyType : step.Path.RootType;
                        if (step.Filter != null)
                        {
                            if (step.Filter != currentType && !currentType.IsSubType(step.Filter))
                            {
                                return(null);                                        // throw new ArgumentException("Filter type '" + step.Filter.Name + "' is not a valid subtype of '" + currentType.Name + "'.");
                            }
                            currentType = step.Filter;
                        }

                        // Process the current and all subtypes, honoring any specified type filters
                        foreach (var type in currentType.GetDescendentsInclusive())
                        {
                            // Get the current property
                            var property = type.Properties[propertyName];

                            // Ensure the property is valid
                            if (property == null || property.IsStatic || (property.DeclaringType != type && type != currentType && currentType.Properties[propertyName] != null))
                            {
                                continue;
                            }

                            // Look ahead to see if this step is filtered
                            filter = tokenIndex < tokenMatches.Count - 1 && tokenMatches[tokenIndex + 1].Value.StartsWith("<") ?
                                     rootType.Context.GetModelType(tokenMatches[tokenIndex + 1].Value.Substring(1, tokenMatches[tokenIndex + 1].Length - 2)) :
                                     null;

                            // See if the step already exists for this property and filter or needs to be created
                            var nextStep = step.NextSteps.Where(s => s.Property == property && s.Filter == filter).FirstOrDefault();
                            if (nextStep == null)
                            {
                                nextStep = new ModelStep(newPath)
                                {
                                    Property = property, Filter = filter, PreviousStep = step.Property != null ? step : null
                                };
                                step.NextSteps.Add(nextStep);
                            }
                            nextSteps.Add(nextStep);
                        }

                        // Remove steps that do not lead to the end of the path
                        RemoveStep(step);
                    }

                    // Immediately exit if no steps were found matching the requested path
                    if (nextSteps.Count == 0)
                    {
                        return(null);
                    }

                    steps = nextSteps;
                    break;
                }

                ++tokenIndex;
            }

            // Throw an exception if there are unmatched property group delimiters
            if (stack.Count > 0)
            {
                throw new ArgumentException("Unclosed '{' in path: " + path, "path");
            }

            // Return null if the path was not valid
            if (!root.NextSteps.Any())
            {
                return(null);
            }

            // Otherwise, finish initializing and return the new path
            newPath.FirstSteps = root.NextSteps;
            newPath.Path       = path;
            return(newPath);
        }
Esempio n. 5
0
 internal ModelStep(ModelPath path)
 {
     this.Path      = path;
     this.NextSteps = new ModelStepList();
 }
Esempio n. 6
0
        /// <summary>
        /// Initialize the source from a valid <see cref="ModelPath"/>
        /// </summary>
        /// <param name="instancePath"></param>
        void InitializeFromModelPath(ModelPath instancePath, string path, out ModelProperty sourceProperty)
        {
            this.Path = path;
            this.IsStatic = false;
            this.RootType = instancePath.RootType.Name;
            var tokens = tokenizer.Matches(path);
            this.steps = new SourceStep[tokens.Count];
            var rootType = instancePath.RootType;
            int i = 0;
            sourceProperty = null;
            foreach(Match token in tokens)
            {
                sourceProperty = rootType.Properties[token.Groups["Property"].Value];
                if (sourceProperty == null)
                    throw new ArgumentException(String.Format("Property {0} is not valid for type {1} in path {2}.", token.Groups["Property"].Value, rootType.Name, path));
                int index;
                if (!Int32.TryParse(token.Groups["Index"].Value, out index))
                    index = -1;

                steps[i] = new SourceStep() { Property = sourceProperty.Name, Index = index, DeclaringType = sourceProperty.DeclaringType.Name, IsReferenceProperty = sourceProperty is ModelReferenceProperty };
                rootType = sourceProperty is ModelReferenceProperty ? ((ModelReferenceProperty)sourceProperty).PropertyType : null;
                i++;
            }

            this.SourceProperty = sourceProperty.Name;
            this.SourceType = sourceProperty.DeclaringType.Name;
        }
Esempio n. 7
0
 /// <summary>
 /// Creates a new <see cref="ModelSource"/> for the specified root type and path.
 /// </summary>
 /// <param name="path">The source model path</param>
 public ModelSource(ModelPath path)
 {
     ModelProperty property;
     InitializeFromModelPath(path, path.Path, out property);
 }
Esempio n. 8
0
 internal ModelPathChangeEvent(ModelInstance instance, ModelPath path)
     : base(instance)
 {
     this.path = path;
 }
Esempio n. 9
0
        /// <summary>
        /// Creates a new <see cref="ModelPath"/> instance for the specified root <see cref="ModelType"/>
        /// and path string.
        /// </summary>
        /// <param name="rootType"></param>
        /// <param name="path"></param>
        internal static ModelPath CreatePath(ModelType rootType, string path)
        {
            // Create the new path
            ModelPath newPath = new ModelPath()
            {
                RootType = rootType
            };

            // Create a temporary root step
            var root = new ModelStep(newPath);

            var steps = new List<ModelStep>() { root };
            var stack = new Stack<List<ModelStep>>();

            int expectedTokenStart = 0;

            var tokenMatches = pathParser.Matches(path);
            int tokenIndex = 0;
            foreach (Match tokenMatch in tokenMatches)
            {
                // ensure there are no gaps between tokens except for whitespace
                if (tokenMatch.Index != expectedTokenStart && path.Substring(expectedTokenStart, tokenMatch.Index - expectedTokenStart - 1).Trim() != "" )
                    return null; // throw new ArgumentException("The specified path, '" + path + "', is not valid. Character " + (expectedTokenStart));

                expectedTokenStart = tokenMatch.Index + tokenMatch.Length;

                var token = tokenMatch.Value;
                switch (token[0])
                {
                    case '{':
                        stack.Push(steps);
                        break;
                    case '}':
                        steps = stack.Pop();
                        break;
                    case ',':
                        steps = stack.Peek();
                        break;
                    case '.':
                        break;
                    case '<':
                        var filter = rootType.Context.GetModelType(token.Substring(1, token.Length - 2));
                        foreach (var step in steps)
                        {
                            if (step.Property is ModelReferenceProperty &&
                                (((ModelReferenceProperty)step.Property).PropertyType == filter ||
                                ((ModelReferenceProperty)step.Property).PropertyType.IsSubType(filter)))
                                step.Filter = filter;
                            else
                                RemoveStep(step);
                        }
                        break;
                    default:

                        // Get the property name for the next step
                        var propertyName = token;

                        // Track the next steps
                        var nextSteps = new List<ModelStep>();

                        // Process each of the current steps
                        foreach (var step in steps)
                        {
                            // Ensure the current step is a valid reference property
                            if (step.Property != null && step.Property is ModelValueProperty)
                                return null; // throw new ArgumentException("Property '" + step.Property.Name + "' is a value property and cannot have child properties specified.");

                            // Get the type of the current step
                            var currentType = step.Property != null ? ((ModelReferenceProperty)step.Property).PropertyType : step.Path.RootType;
                            if (step.Filter != null)
                            {
                                if (step.Filter != currentType && !currentType.IsSubType(step.Filter))
                                    return null; // throw new ArgumentException("Filter type '" + step.Filter.Name + "' is not a valid subtype of '" + currentType.Name + "'.");
                                currentType = step.Filter;
                            }

                            // Process the current and all subtypes, honoring any specified type filters
                            foreach (var type in currentType.GetDescendentsInclusive())
                            {
                                // Get the current property
                                var property = type.Properties[propertyName];

                                // Ensure the property is valid
                                if (property == null || property.IsStatic || (property.DeclaringType != type && type != currentType && currentType.Properties[propertyName] != null))
                                    continue;

                                // Look ahead to see if this step is filtered
                                filter = tokenIndex < tokenMatches.Count - 1 && tokenMatches[tokenIndex + 1].Value.StartsWith("<") ?
                                    rootType.Context.GetModelType(tokenMatches[tokenIndex + 1].Value.Substring(1, tokenMatches[tokenIndex + 1].Length - 2)) :
                                    null;

                                // See if the step already exists for this property and filter or needs to be created
                                var nextStep = step.NextSteps.Where(s => s.Property == property && s.Filter == filter).FirstOrDefault();
                                if (nextStep == null)
                                {
                                    nextStep = new ModelStep(newPath) { Property = property, Filter = filter, PreviousStep = step.Property != null ? step : null };
                                    step.NextSteps.Add(nextStep);
                                }
                                nextSteps.Add(nextStep);
                            }

                            // Remove steps that do not lead to the end of the path
                            RemoveStep(step);
                        }

                        // Immediately exit if no steps were found matching the requested path
                        if (nextSteps.Count == 0)
                            return null;

                        steps = nextSteps;
                        break;
                }

                ++tokenIndex;
            }

            // Throw an exception if there are unmatched property group delimiters
            if (stack.Count > 0)
                throw new ArgumentException("Unclosed '{' in path: " + path, "path");

            // Return null if the path was not valid
            if (!root.NextSteps.Any())
                return null;

            // Otherwise, finish initializing and return the new path
            newPath.FirstSteps = root.NextSteps;
            newPath.Path = path;
            return newPath;
        }
Esempio n. 10
0
        /// <summary>
        /// Creates a new <see cref="ModelSource"/> for the specified root type and path.
        /// </summary>
        /// <param name="path">The source model path</param>
        public ModelSource(ModelPath path)
        {
            ModelProperty property;

            InitializeFromModelPath(path, path.Path, out property);
        }
Esempio n. 11
0
 internal ModelPathChangeEvent(ModelInstance instance, ModelPath path)
     : base(instance)
 {
     this.path = path;
 }
Esempio n. 12
0
 internal ModelStep(ModelPath path)
 {
     this.Path = path;
     this.NextSteps = new ModelStepList();
 }