private void PrepareValueCloneGraph <T>(ref T source, ref T target, CloneType typeData) where T : struct
        {
            // Determine the object Type and early-out if it's just plain old data
            if (typeData == null)
            {
                typeData = GetCloneType(typeof(T));
            }
            if (typeData.IsCopyByAssignment)
            {
                return;
            }
            if (!typeData.InvestigateOwnership)
            {
                return;
            }

            object    lastObject    = this.currentObject;
            CloneType lastCloneType = this.currentCloneType;

            this.currentObject    = source;
            this.currentCloneType = typeData;

            this.PrepareValueChildCloneGraph <T>(ref source, ref target, typeData);

            this.currentObject    = lastObject;
            this.currentCloneType = lastCloneType;
        }
        private void PerformCopyValue <T>(ref T source, ref T target, CloneType typeData) where T : struct
        {
            if (typeData == null)
            {
                typeData = GetCloneType(typeof(T));
            }

            object    lastObject    = this.currentObject;
            CloneType lastCloneType = this.currentCloneType;

            this.currentObject    = source;
            this.currentCloneType = typeData;

            if (typeData.IsCopyByAssignment)
            {
                target = source;
            }
            else
            {
                this.PerformCopyChildValue(ref source, ref target, typeData);
            }

            this.currentObject    = lastObject;
            this.currentCloneType = lastCloneType;
        }
        private void PrepareObjectChildCloneGraph(object source, object target, CloneType typeData)
        {
            // If the object is a simple and shallow type, there's nothing to investigate.
            if (!typeData.InvestigateOwnership)
            {
                return;
            }

            // If it's an array, we'll need to traverse its elements
            if (typeData.IsArray)
            {
                CloneType elementTypeData = typeData.ElementType.CouldBeDerived ? null : typeData.ElementType;
                Array     sourceArray     = source as Array;
                Array     targetArray     = target as Array;
                for (int i = 0; i < sourceArray.Length; i++)
                {
                    object sourceElementValue = sourceArray.GetValue(i);
                    object targetElementValue = targetArray.Length > i?targetArray.GetValue(i) : null;

                    this.PrepareObjectCloneGraph(
                        sourceElementValue,
                        targetElementValue,
                        elementTypeData);
                }
            }
            // If it's an object, we'll need to traverse its fields
            else if (typeData.PrecompiledSetupFunc != null)
            {
                typeData.PrecompiledSetupFunc(source, target, this);
            }
        }
        private CloneBehavior GetCloneBehavior(CloneType sourceType, bool lockBehavior, out object acquiredLock)
        {
            CloneBehavior defaultBehavior = (sourceType != null) ? sourceType.DefaultCloneBehavior : CloneBehavior.ChildObject;

            // Local behavior rules
            acquiredLock = null;
            var localBehaviorData = this.localBehavior.Data;

            for (int i = this.localBehavior.Count - 1; i >= 0; i--)
            {
                if (localBehaviorData[i].Locked)
                {
                    continue;
                }
                if (localBehaviorData[i].TargetType == null || (sourceType != null && localBehaviorData[i].TargetType.IsAssignableFrom(sourceType.Type)))
                {
                    acquiredLock = localBehaviorData[i];
                    localBehaviorData[i].Locked = lockBehavior;
                    CloneBehavior behavior = localBehaviorData[i].Behavior;
                    return((behavior != CloneBehavior.Default) ? behavior : defaultBehavior);
                }
            }

            // Global behavior rules
            return(defaultBehavior);
        }
        private void PerformCopyObject(object source, object target, CloneType typeData)
        {
            // Early-out for same-instance values
            if (object.ReferenceEquals(source, target))
            {
                return;
            }

            // Early-out for null values
            if (object.ReferenceEquals(source, null))
            {
                if (typeData == null)
                {
                    typeData = GetCloneType(target.GetType());
                }
                if (!typeData.IsMergeSurrogate)
                {
                    return;
                }
            }

            // If we already handled this object, back out to avoid loops.
            if (typeData == null)
            {
                typeData = GetCloneType(source.GetType());
            }
            if (!this.PushCurrentObject(source, typeData))
            {
                return;
            }

            object    lastObject    = this.currentObject;
            CloneType lastCloneType = this.currentCloneType;

            this.currentObject    = source;
            this.currentCloneType = typeData;

            // Check whether there is a surrogare for this object
            ICloneExplicit customSource;

            if (typeData.Surrogate != null)
            {
                typeData.Surrogate.CopyDataTo(source, target, this);
            }
            // If it implements custom cloning behavior, use that
            else if ((customSource = source as ICloneExplicit) != null)
            {
                customSource.CopyDataTo(target, this);
            }
            // Otherwise, traverse its child objects using default behavior
            else if (!typeData.IsCopyByAssignment)
            {
                this.PerformCopyChildObject(source, target, typeData);
            }

            this.currentObject    = lastObject;
            this.currentCloneType = lastCloneType;
            this.PopCurrentObject(source, typeData);
        }
 private void PrepareValueChildCloneGraph <T>(ref T source, ref T target, CloneType typeData) where T : struct
 {
     CloneType.ValueSetupFunc <T> typedSetupFunc = typeData.PrecompiledValueSetupFunc as CloneType.ValueSetupFunc <T>;
     if (typedSetupFunc != null)
     {
         typedSetupFunc(ref source, ref target, this);
     }
 }
 private void PerformCopyChildValue <T>(ref T source, ref T target, CloneType typeData) where T : struct
 {
     CloneType.ValueAssignmentFunc <T> typedAssignmentFunc = typeData.PrecompiledValueAssignmentFunc as CloneType.ValueAssignmentFunc <T>;
     if (typedAssignmentFunc != null)
     {
         typedAssignmentFunc(ref source, ref target, this);
     }
 }
示例#8
0
 public CloneField(FieldInfo field, CloneType typeInfo, CloneFieldFlags flags, CloneBehaviorAttribute behavior, bool isAlwaysReference)
 {
     this.field             = field;
     this.typeInfo          = typeInfo;
     this.flags             = flags;
     this.behavior          = behavior;
     this.isAlwaysReference = isAlwaysReference;
 }
        private void PerformCopyField(object source, object target, FieldInfo field, bool isPlainOldData)
        {
            // Perform the quick version for plain old data
            if (isPlainOldData)
            {
                field.SetValue(target, field.GetValue(source));
            }
            // If this field stores a value type, no assignment or mapping is necessary. Just handle the struct.
            else if (field.FieldType.GetTypeInfo().IsValueType)
            {
                object sourceFieldValue = field.GetValue(source);
                object targetFieldValue = field.GetValue(target);
                this.PerformCopyObject(
                    sourceFieldValue,
                    targetFieldValue,
                    GetCloneType(field.FieldType));
                field.SetValue(target, targetFieldValue);
            }
            // If it's a reference type, the value needs to be mapped from source to target
            else
            {
                object sourceFieldValue = field.GetValue(source);
                object targetFieldValue;

                // If there is no source value, check if we're dealing with a merge surrogate and get the old target value when necessary.
                bool      sourceNullMerge = false;
                CloneType typeData        = null;
                if (object.ReferenceEquals(sourceFieldValue, null))
                {
                    sourceFieldValue = field.GetValue(target);
                    if (!object.ReferenceEquals(sourceFieldValue, null))
                    {
                        typeData = GetCloneType(sourceFieldValue.GetType());
                        if (typeData.IsMergeSurrogate)
                        {
                            sourceNullMerge = true;
                        }
                        else
                        {
                            sourceFieldValue = null;
                        }
                    }
                }

                // Perform target mapping and assign the copied value to the target field
                targetFieldValue = this.GetTargetOf(sourceFieldValue);
                this.PerformCopyObject(sourceNullMerge ? null : sourceFieldValue, targetFieldValue, typeData);
                field.SetValue(target, targetFieldValue);
            }
        }
        /// <summary>
        /// Ends the current clone operation by clearing all the working data that was
        /// allocated in the process.
        /// </summary>
        /// <param name="preserveMapping">
        /// If true, the mapping between source and target object graph is preserved.
        ///
        /// This can be useful for doing a partial clone operation that is later continued or
        /// repeated using the same <see cref="CloneProvider"/> instance. Since the mapping is
        /// already present, performance of subsequent clone operations within the same object
        /// graph can benefit by this.
        /// </param>
        private void EndCloneOperation(bool preserveMapping)
        {
            this.sourceRoot       = null;
            this.currentObject    = null;
            this.currentCloneType = null;

            this.localBehavior.Clear();
            this.lateSetupSchedule.Clear();
            this.handledObjects.Clear();

            if (!preserveMapping)
            {
                this.ClearCachedMapping();
            }
        }
        private void PrepareCloneGraph()
        {
            // Visit the object graph in order to determine which objects to clone
            this.PrepareObjectCloneGraph(this.sourceRoot, this.targetRoot, null, CloneBehavior.ChildObject);
            this.localBehavior.Clear();

            // Perform late setup for surrogate objects that required it
            foreach (LateSetupEntry lateSetup in this.lateSetupSchedule)
            {
                CloneType       typeData  = GetCloneType((lateSetup.Source ?? lateSetup.Target).GetType());
                ICloneSurrogate surrogate = typeData.Surrogate;

                object lateSetupTarget = lateSetup.Target;
                surrogate.LateSetup(lateSetup.Source, ref lateSetupTarget, this);
                this.SetTargetOf(lateSetup.Source ?? lateSetup.Target, lateSetupTarget);
            }
        }
        void ICloneOperation.HandleObject <T>(T source, ref T target)
        {
            // If we're just handling ourselfs, don't bother doing anything else.
            // Since "fallback to default" is triggered by source being equal to the currently handled object,
            // we need to make sure that this cannot be triggered accidentally by auto-generated clone lambdas.
            // Only allow the fallback when the object in question is actually cloned by user code!
            bool calledFromUserCode = this.currentObject is ICloneExplicit || this.currentCloneType.Surrogate != null;

            if (object.ReferenceEquals(source, this.currentObject) && calledFromUserCode)
            {
                if (!this.currentCloneType.IsCopyByAssignment)
                {
                    this.PerformCopyChildObject(source, target, this.currentCloneType);
                }
                return;
            }

            // If there is no source value, check if we're dealing with a merge surrogate and get the old target value when necessary.
            bool      sourceNullMerge = false;
            CloneType typeData        = null;

            if (object.ReferenceEquals(source, null))
            {
                source = target;
                if (!object.ReferenceEquals(source, null))
                {
                    typeData = GetCloneType(source.GetType());
                    if (typeData.IsMergeSurrogate)
                    {
                        sourceNullMerge = true;
                    }
                    else
                    {
                        source = default(T);
                    }
                }
            }

            // Perform target mapping and assign the copied value to the target field
            object registeredTarget = this.GetTargetOf(source);

            target = (T)registeredTarget;
            this.PerformCopyObject(sourceNullMerge ? default(T) : source, target, typeData);
        }
        /// <summary>
        /// Returns the <see cref="CloneType"/> of a Type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        protected internal static CloneType GetCloneType(Type type)
        {
            if (type == null)
            {
                return(null);
            }

            CloneType result;

            if (cloneTypeCache.TryGetValue(type, out result))
            {
                return(result);
            }

            result = new CloneType(type);
            cloneTypeCache[type] = result;
            result.Init();
            return(result);
        }
        private void PerformCopyChildObject(object source, object target, CloneType typeData)
        {
            // Handle array data
            if (typeData.IsArray)
            {
                Array     sourceArray       = source as Array;
                Array     targetArray       = target as Array;
                CloneType sourceElementType = typeData.ElementType;

                // If the array contains plain old data, no further handling is required
                if (sourceElementType.IsCopyByAssignment)
                {
                    sourceArray.CopyTo(targetArray, 0);
                }
                // If the array contains a value type, handle each element in order to allow them to perform a mapping
                else if (sourceElementType.Type.IsValueType)
                {
                    for (int i = 0; i < sourceArray.Length; ++i)
                    {
                        object sourceElement = sourceArray.GetValue(i);
                        object targetElement = targetArray.GetValue(i);
                        this.PerformCopyObject(
                            sourceElement,
                            targetElement,
                            sourceElementType);
                        targetArray.SetValue(targetElement, i);
                    }
                }
                // If it contains reference types, a direct element mapping is necessary, as well as complex value handling
                else
                {
                    bool couldRequireMerge = sourceElementType.CouldBeDerived || sourceElementType.IsMergeSurrogate;
                    for (int i = 0; i < sourceArray.Length; ++i)
                    {
                        CloneType elementTypeData = sourceElementType.CouldBeDerived ? null : sourceElementType;

                        object sourceElement = sourceArray.GetValue(i);
                        object targetElement;

                        // If there is no source value, check if we're dealing with a merge surrogate and get the old target value when necessary.
                        bool sourceNullMerge = false;
                        if (couldRequireMerge && object.ReferenceEquals(sourceElement, null))
                        {
                            if (elementTypeData == null || elementTypeData.IsMergeSurrogate)
                            {
                                sourceElement = targetArray.GetValue(i);
                                if (!object.ReferenceEquals(sourceElement, null))
                                {
                                    if (elementTypeData == null)
                                    {
                                        elementTypeData = GetCloneType(sourceElement.GetType());
                                    }
                                    if (elementTypeData.IsMergeSurrogate)
                                    {
                                        sourceNullMerge = true;
                                    }
                                    else
                                    {
                                        sourceElement = null;
                                    }
                                }
                            }
                        }

                        // Perform target mapping and assign the copied value to the target field
                        targetElement = this.GetTargetOf(sourceElement);
                        this.PerformCopyObject(sourceNullMerge ? null : sourceElement, targetElement, elementTypeData);
                        targetArray.SetValue(targetElement, i);
                    }
                }
            }
            // Handle structural data
            else
            {
                // When available, take the shortcut for assigning all POD fields
                if (typeData.PrecompiledAssignmentFunc != null)
                {
                    typeData.PrecompiledAssignmentFunc(source, target, this);
                }
                // Otherwise, fall back to reflection. This is currently necessary for value types.
                else
                {
                    for (int i = 0; i < typeData.FieldData.Length; i++)
                    {
                        if ((typeData.FieldData[i].Flags & CloneFieldFlags.IdentityRelevant) != CloneFieldFlags.None && this.context.PreserveIdentity)
                        {
                            continue;
                        }
                        this.PerformCopyField(source, target, typeData.FieldData[i].Field, typeData.FieldData[i].FieldType.IsCopyByAssignment);
                    }
                }
            }
        }
示例#15
0
        public void Init()
        {
            if (this.surrogate != null)
            {
                return;
            }
            if (this.copyByAssignment)
            {
                return;
            }

            if (this.type.IsArray)
            {
                this.investigateOwnership = !(this.elementType.IsCopyByAssignment || (this.elementType.Type.IsValueType && !this.elementType.InvestigateOwnership));
                return;
            }
            else
            {
                this.investigateOwnership = typeof(ICloneExplicit).GetTypeInfo().IsAssignableFrom(this.type) || this.surrogate != null;
            }

            // Retrieve field data
            List <CloneField> fieldData = new List <CloneField>();

            foreach (FieldInfo field in this.type.DeclaredFieldsDeep())
            {
                if (field.IsStatic)
                {
                    continue;
                }
                if (field.IsInitOnly)
                {
                    continue;
                }
                if (field.HasAttributeCached <ManuallyClonedAttribute>())
                {
                    continue;
                }
                if (field.DeclaringType.GetTypeInfo().HasAttributeCached <ManuallyClonedAttribute>())
                {
                    continue;
                }

                CloneFieldFlags     flags       = CloneFieldFlags.None;
                CloneFieldAttribute fieldAttrib = field.GetAttributesCached <CloneFieldAttribute>().FirstOrDefault();
                if (fieldAttrib != null)
                {
                    flags = fieldAttrib.Flags;
                }

                if (field.HasAttributeCached <DontSerializeAttribute>() && !flags.HasFlag(CloneFieldFlags.DontSkip))
                {
                    continue;
                }
                if (flags.HasFlag(CloneFieldFlags.Skip))
                {
                    continue;
                }

                CloneBehaviorAttribute behaviorAttrib = field.GetAttributesCached <CloneBehaviorAttribute>().FirstOrDefault();
                CloneType fieldType         = CloneProvider.GetCloneType(field.FieldType);
                bool      isAlwaysReference =
                    (behaviorAttrib != null) &&
                    (behaviorAttrib.TargetType == null || field.FieldType.GetTypeInfo().IsAssignableFrom(behaviorAttrib.TargetType.GetTypeInfo())) &&
                    (behaviorAttrib.Behavior == CloneBehavior.Reference);

                // Can this field own any objects itself?
                if (!this.investigateOwnership)
                {
                    bool fieldCanOwnObjects = true;
                    if (fieldType.IsCopyByAssignment)
                    {
                        fieldCanOwnObjects = false;
                    }
                    if (isAlwaysReference)
                    {
                        fieldCanOwnObjects = false;
                    }
                    if (fieldType.Type.IsValueType && !fieldType.InvestigateOwnership)
                    {
                        fieldCanOwnObjects = false;
                    }

                    if (fieldCanOwnObjects)
                    {
                        this.investigateOwnership = true;
                    }
                }

                CloneField fieldEntry = new CloneField(field, fieldType, flags, behaviorAttrib, isAlwaysReference);
                fieldData.Add(fieldEntry);
            }
            this.fieldData = fieldData.ToArray();

            // Build precompile functions for setup and (partially) assignment
            this.CompileAssignmentFunc();
            this.CompileSetupFunc();
            this.CompileValueAssignmentFunc();
            this.CompileValueSetupFunc();
        }
        private void PrepareObjectCloneGraph(object source, object target, CloneType typeData, CloneBehavior behavior = CloneBehavior.Default)
        {
            // Early-out for null values
            if (object.ReferenceEquals(source, null))
            {
                if (object.ReferenceEquals(target, null))
                {
                    return;
                }
                if (typeData == null)
                {
                    typeData = GetCloneType(target.GetType());
                }
                if (!typeData.IsMergeSurrogate)
                {
                    return;
                }
            }

            // Determine the object Type and early-out if it's just plain old data
            if (typeData == null)
            {
                typeData = GetCloneType(source.GetType());
            }
            if (typeData.IsCopyByAssignment)
            {
                return;
            }
            if (typeData.Type.IsValueType && !typeData.InvestigateOwnership)
            {
                return;
            }

            // Determine cloning behavior for this object
            object behaviorLock = null;

            if (!typeData.Type.IsValueType && !object.ReferenceEquals(source, null))
            {
                // If we already registered a target for that source, stop right here.
                if (this.targetMapping.ContainsKey(source))
                {
                    return;
                }

                // If no specific behavior was specified, fetch the default one set by class and field attributes
                if (behavior == CloneBehavior.Default)
                {
                    behavior = this.GetCloneBehavior(typeData, true, out behaviorLock);
                }
                // Apply the current behavior
                if (behavior != CloneBehavior.ChildObject)
                {
                    this.UnlockCloneBehavior(behaviorLock);
                    return;
                }

                // If the target doesn't match the source, discard it
                if (target != null && target.GetType() != typeData.Type.AsType())
                {
                    target = null;
                }
            }

            object    lastObject    = this.currentObject;
            CloneType lastCloneType = this.currentCloneType;

            this.currentObject    = source;
            this.currentCloneType = typeData;

            // If it's a value type, use the fast lane without surrogate and custom checks
            if (typeData.Type.IsValueType)
            {
                if (object.ReferenceEquals(target, null))
                {
                    target = typeData.Type.CreateInstanceOf();
                }
                this.PrepareObjectChildCloneGraph(source, target, typeData);
            }
            // Check whether there is a surrogate for this object
            else if (typeData.Surrogate != null)
            {
                bool requireLateSetup;
                typeData.Surrogate.SetupCloneTargets(source, target, out requireLateSetup, this);
                if (requireLateSetup)
                {
                    this.lateSetupSchedule.Add(new LateSetupEntry(source, target));
                }
            }
            // Otherwise, use the default algorithm
            else
            {
                // Create a new target array. Always necessary due to their immutable size.
                Array originalTargetArray = null;
                if (typeData.IsArray)
                {
                    Array sourceArray = source as Array;
                    originalTargetArray = target as Array;
                    target = Array.CreateInstance(typeData.ElementType.Type.AsType(), sourceArray.Length);
                }
                // Only create target object when no reuse is possible
                else if (object.ReferenceEquals(target, null))
                {
                    target = typeData.Type.CreateInstanceOf();
                }

                // Create a mapping from the source object to the target object
                this.SetTargetOf(source, target);

                // If we are dealing with an array, use the original one for object reuse mapping
                if (originalTargetArray != null)
                {
                    target = originalTargetArray;
                }

                // If it implements custom cloning behavior, use that
                ICloneExplicit customSource = source as ICloneExplicit;
                if (customSource != null)
                {
                    customSource.SetupCloneTargets(target, this);
                }
                // Otherwise, traverse its child objects using default behavior
                else
                {
                    this.PrepareObjectChildCloneGraph(source, target, typeData);
                }
            }

            this.currentObject    = lastObject;
            this.currentCloneType = lastCloneType;
            this.UnlockCloneBehavior(behaviorLock);
        }
 /// <summary>
 /// Removes the specified source object from the handled object stack
 /// after finishing its copy step.
 /// </summary>
 /// <param name="source"></param>
 /// <param name="typeData"></param>
 /// <returns></returns>
 private bool PopCurrentObject(object source, CloneType typeData)
 {
     return(typeData.Type.IsValueType || object.ReferenceEquals(source, null) || this.handledObjects.Remove(source));
 }