protected override Expression VisitMemberAccess(MemberExpression m) { base.VisitMemberAccess(m); ModelStep step; if (m.Expression != null && steps.TryGetValue(m.Expression, out step) && !(step.Property is ModelValueProperty)) { // Get the model type of the parent expression var type = step.Property == null ? path.RootType : ((ModelReferenceProperty)step.Property).PropertyType; // Determine if the member access represents a property on the model type var property = type.Properties[m.Member.Name]; // If the property exists on the model type, create and record a new step if (property != null) { var nextStep = step.NextSteps.FirstOrDefault(s => s.Property == property); if (nextStep == null) { nextStep = new ModelStep(path) { Property = property, PreviousStep = step }; step.NextSteps.Add(nextStep); } steps.Add(m, nextStep); } } return(m); }
protected override Expression VisitModelParameter(ModelExpression.ModelParameterExpression p) { // Add the root step if (rootStep == null) { rootStep = new ModelStep(path); steps.Add(p, rootStep); } return(p); }
/// <summary> /// Safely removes an invalid step from a path. /// </summary> /// <param name="step"></param> static void RemoveStep(ModelStep step) { // Remove steps that do not lead to the end of the path if (step.Property != null && !step.NextSteps.Any()) { var previousStep = step; while (previousStep != null && !previousStep.NextSteps.Any()) { previousStep.NextSteps.Remove(previousStep); previousStep = previousStep.PreviousStep; } } }
protected override Expression VisitModelMember(ModelExpression.ModelMemberExpression m) { base.VisitModelMember(m); ModelStep step; if (steps.TryGetValue(m.Expression, out step) && !(step.Property is ModelValueProperty)) { // Get the model type of the parent expression var type = step.Property == null ? path.RootType : ((ModelReferenceProperty)step.Property).PropertyType; // Make sure the type of the expression matches the declaring type of the property if (type != m.Property.DeclaringType && !m.Property.DeclaringType.IsSubType(type)) { return(m); } // Determine if the member access represents a property on the model type var property = m.Property; // Create and record a new step var nextStep = step.NextSteps.FirstOrDefault(s => s.Property == property); if (nextStep == null) { nextStep = new ModelStep(path) { Property = property, PreviousStep = step }; step.NextSteps.Add(nextStep); } if (!steps.ContainsKey(m)) { steps.Add(m, nextStep); } } return(m); }
/// <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); }
protected override Expression VisitParameter(ParameterExpression p) { base.VisitParameter(p); // Add the root step if (rootStep == null) { rootStep = new ModelStep(path); steps.Add(p, rootStep); } return p; }
protected override Expression VisitModelMember(ModelExpression.ModelMemberExpression m) { base.VisitModelMember(m); ModelStep step; if (steps.TryGetValue(m.Expression, out step) && !(step.Property is ModelValueProperty)) { // Get the model type of the parent expression var type = step.Property == null ? path.RootType : ((ModelReferenceProperty)step.Property).PropertyType; // Make sure the type of the expression matches the declaring type of the property if (type != m.Property.DeclaringType && !m.Property.DeclaringType.IsSubType(type)) return m; // Determine if the member access represents a property on the model type var property = m.Property; // Create and record a new step var nextStep = step.NextSteps.FirstOrDefault(s => s.Property == property); if (nextStep == null) { nextStep = new ModelStep(path) { Property = property, PreviousStep = step }; step.NextSteps.Add(nextStep); } if (!steps.ContainsKey(m)) steps.Add(m, nextStep); } return m; }
protected override Expression VisitMemberAccess(MemberExpression m) { base.VisitMemberAccess(m); ModelStep step; if (m.Expression != null && steps.TryGetValue(m.Expression, out step) && !(step.Property is ModelValueProperty)) { // Get the model type of the parent expression var type = step.Property == null ? path.RootType : ((ModelReferenceProperty)step.Property).PropertyType; // Determine if the member access represents a property on the model type var property = type.Properties[m.Member.Name]; // If the property exists on the model type, create and record a new step if (property != null) { var nextStep = step.NextSteps.FirstOrDefault(s => s.Property == property); if (nextStep == null) { nextStep = new ModelStep(path) { Property = property, PreviousStep = step }; step.NextSteps.Add(nextStep); } steps.Add(m, nextStep); } } return m; }
/// <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; }