Example #1
0
        /// <summary>
        /// Builds visitor for visiting <typeparamref name="TRootType"/> instance.
        /// </summary>
        /// <param name="depth">
        /// The depth (level) of object graph that visitor will be able to visit.
        /// Decreases build performance for large values.
        /// </param>
        /// <param name="guard">
        /// If given, property guard will significantly decrease build time by avoiding circular references between property types.
        /// If you wish to use it and intencionaly allow obvious property type cycles like hierarchy structures, use [VisitorHierarchy] attribute on property that forms hierarchy.
        /// </param>
        /// <param name="supportsCloning">
        /// In order to support object cloning while visiting in IVisitor.Visit() method, many cloning expressions should be built, which is very costly time-wise.
        /// To significantly decrease build time of visitors that don't need to clone objects, set this parameter to false (omits cloning expressions creation).
        /// </param>
        /// <returns>IVisitor for visiting instances of type <typeparamref name="TRootType"/></returns>
        public IVisitor Build(int depth = Constants.MaxDepth, PropertyGuard guard = null, bool supportsCloning = true)
        {
            var visitor = new ElementVisitor <TRootType>(null, null, guard, supportsCloning);

            Build(visitor, depth);
            return(visitor);
        }
Example #2
0
        private void Build(ElementVisitor visitor, int depth)
        {
            if (depth > Constants.MaxDepth)
            {
                throw new Exception(string.Format("Depth of visit cannot be more than {0}.", Constants.MaxDepth));
            }

            if (depth < 0)
            {
                return;
            }

            var currentNodeType = visitor.ElementType;

            foreach (var property in currentNodeType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                //Trying to find property match, first by name, owner type and property type. If not found, we will try only with property type.

                var match = _properties.FirstOrDefault(p => p.ElementType == property.DeclaringType && p.PropertyName == property.Name && p.PropertyType == property.PropertyType);

                if (match == null)
                {
                    match = _properties.FirstOrDefault(p => p.ElementType == null && p.PropertyName == null && p.PropertyType == property.PropertyType);
                }

                if (match != null)
                {
                    visitor.AddProperty(property.PropertyType, property.Name, match.GetNewValue);
                }

                if (Util.IsSimpleType(property.PropertyType))
                {
                    continue;
                }

                //If property type is not primitive, we will assume we should add it as an element/collection, but after it's being built and turns out it's 'empty', we will remove it.

                if (Util.IsDictionary(property.PropertyType))
                {
                    var dictionaryItemType = Util.GetDictionaryItemsType(property.PropertyType);

                    if (Util.IsSimpleType(dictionaryItemType))
                    {
                        continue;
                    }

                    var isHierarchy = property.GetCustomAttribute <VisitorHierarchyAttribute>() != null;

                    var childVisitor = visitor.AddDictionary(dictionaryItemType, property.PropertyType, property.Name, isHierarchy);

                    if (childVisitor == null) //AddDirection() will return null in case of issues like circular reference.
                    {
                        continue;
                    }

                    Build(childVisitor, depth - 1);

                    if (!childVisitor.AnyElement && !childVisitor.AnyCollection && !childVisitor.AnyDictionary && !childVisitor.AnyProperty)
                    {
                        visitor.RemoveDictionary(property.PropertyType, property.Name);
                    }
                }
                else if (Util.IsGenericEnumerable(property.PropertyType) || Util.ImplementsGenericIEnumerable(property.PropertyType))
                {
                    var collectionItemType = Util.GetCollectionItemsType(property.PropertyType);

                    if (Util.IsSimpleType(collectionItemType))
                    {
                        continue;
                    }

                    var isHierarchy = property.GetCustomAttribute <VisitorHierarchyAttribute>() != null;

                    var childVisitor = visitor.AddCollection(collectionItemType, property.PropertyType, property.Name, isHierarchy);

                    if (childVisitor == null) //AddCollection() will return null in case of issues like circular reference.
                    {
                        continue;
                    }

                    Build(childVisitor, depth - 1);

                    if (!childVisitor.AnyElement && !childVisitor.AnyCollection && !childVisitor.AnyProperty)
                    {
                        visitor.RemoveCollection(property.PropertyType, property.Name);
                    }
                }
                else
                {
                    var isHierarchy = property.GetCustomAttribute <VisitorHierarchyAttribute>() != null;

                    var childVisitor = visitor.AddElement(property.PropertyType, property.Name, isHierarchy);

                    if (childVisitor == null) //AddElement() will return null in case of issues like circular reference.
                    {
                        continue;
                    }

                    Build(childVisitor, depth - 1);

                    if (!childVisitor.AnyElement && !childVisitor.AnyCollection && !childVisitor.AnyProperty)
                    {
                        visitor.RemoveElement(property.PropertyType, property.Name);
                    }
                }
            }
        }