/// <summary> /// Updates the paths in the given <see cref="YamlAssetMetadata{T}"/> instance to reflect that new <see cref="Guid"/> have been generated after cloning, /// when <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been used. /// </summary> /// <typeparam name="T">The type of content in the metadata.</typeparam> /// <param name="metadata">The metadata to update.</param> /// <param name="idRemapping">A dictionary representing the mapping between initial ids and their corresponding id in the cloned object.</param> /// <param name="basePath">If not null, this method will apply the remapping only for paths that are contained in the given base path.</param> public static void RemapIdentifiablePaths <T>(YamlAssetMetadata <T> metadata, Dictionary <Guid, Guid> idRemapping, YamlAssetPath basePath = null) { // Early exit if nothing to remap if (metadata == null || idRemapping == null) { return; } var replacements = new List <Tuple <YamlAssetPath, YamlAssetPath, T> >(); foreach (var entry in metadata) { // Skip paths that doesn't start with the given base path. if (basePath != null && !entry.Key.StartsWith(basePath)) { continue; } var newPath = new YamlAssetPath(entry.Key.Elements.Select(x => FixupIdentifier(x, idRemapping))); replacements.Add(Tuple.Create(entry.Key, newPath, entry.Value)); } // First remove everything, then re-add everything, in case we have a collision between an old path and a new path foreach (var replacement in replacements) { metadata.Remove(replacement.Item1); } foreach (var replacement in replacements) { metadata.Set(replacement.Item2, replacement.Item3); } }
public AssetNode ResolveObjectPath(YamlAssetPath path, out Index index, out bool overrideOnKey) { var currentNode = this; index = Index.Empty; overrideOnKey = false; for (var i = 0; i < path.Items.Count; i++) { var item = path.Items[i]; switch (item.Type) { case YamlAssetPath.ItemType.Member: index = Index.Empty; overrideOnKey = false; if (currentNode.Content.IsReference) { currentNode = (AssetNode)((IGraphNode)currentNode).Target; } string name = item.AsMember(); currentNode = (AssetNode)((IGraphNode)currentNode).TryGetChild(name); break; case YamlAssetPath.ItemType.Index: index = new Index(item.Value); overrideOnKey = true; if (currentNode.Content.IsReference && i < path.Items.Count - 1) { Index index1 = new Index(item.Value); currentNode = (AssetNode)((IGraphNode)currentNode).IndexedTarget(index1); } break; case YamlAssetPath.ItemType.ItemId: var ids = CollectionItemIdHelper.GetCollectionItemIds(currentNode.Content.Retrieve()); var key = ids.GetKey(item.AsItemId()); index = new Index(key); overrideOnKey = false; if (currentNode.Content.IsReference && i < path.Items.Count - 1) { Index index1 = new Index(key); currentNode = (AssetNode)((IGraphNode)currentNode).IndexedTarget(index1); } break; default: throw new ArgumentOutOfRangeException(); } // Something wrong happen, the node is unreachable. if (currentNode == null) { return(null); } } return(currentNode); }
public override object ReadDictionaryKey(ref ObjectContext objectContext, Type keyType) { var key = objectContext.Reader.Peek <Scalar>(); OverrideType[] overrideTypes; var keyName = TrimAndParseOverride(key.Value, out overrideTypes); key.Value = keyName; var keyValue = base.ReadDictionaryKey(ref objectContext, keyType); if (overrideTypes[0] != OverrideType.Base) { YamlAssetMetadata <OverrideType> overrides; if (!objectContext.SerializerContext.Properties.TryGetValue(OverrideDictionaryKey, out overrides)) { overrides = new YamlAssetMetadata <OverrideType>(); objectContext.SerializerContext.Properties.Add(OverrideDictionaryKey, overrides); } var path = GetCurrentPath(ref objectContext, true); ItemId id; object actualKey; if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, keyValue, out id, out actualKey)) { path.PushItemId(id); } else { path.PushIndex(key); } overrides.Set(path, overrideTypes[0]); } if (overrideTypes.Length > 1 && overrideTypes[1] != OverrideType.Base) { ItemId id; object actualKey; if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, keyValue, out id, out actualKey)) { YamlAssetMetadata <OverrideType> overrides; if (!objectContext.SerializerContext.Properties.TryGetValue(OverrideDictionaryKey, out overrides)) { overrides = new YamlAssetMetadata <OverrideType>(); objectContext.SerializerContext.Properties.Add(OverrideDictionaryKey, overrides); } var path = GetCurrentPath(ref objectContext, true); path.PushIndex(actualKey); overrides.Set(path, overrideTypes[1]); } } return(keyValue); }
public static YamlAssetPath ConvertPath(GraphNodePath path, int inNonIdentifiableType) { var currentNode = (AssetNode)path.RootNode; var result = new YamlAssetPath(); var i = 0; foreach (var item in path.Path) { switch (item.Type) { case GraphNodePath.ElementType.Member: var member = (string)item.Value; result.PushMember(member); currentNode = (AssetNode)((IGraphNode)currentNode).TryGetChild(member); break; case GraphNodePath.ElementType.Target: if (i < path.Path.Count - 1) { currentNode = (AssetNode)((IGraphNode)currentNode).Target; } break; case GraphNodePath.ElementType.Index: var index = (Index)item.Value; if (inNonIdentifiableType > 0 || currentNode.IsNonIdentifiableCollectionContent) { result.PushIndex(index.Value); } else { var id = currentNode.IndexToId(index); // Create a new id if we don't have any so far if (id == ItemId.Empty) { id = ItemId.New(); } result.PushItemId(id); } if (i < path.Path.Count - 1) { currentNode = (AssetNode)((IGraphNode)currentNode).IndexedTarget(index); } break; default: throw new ArgumentOutOfRangeException(); } ++i; } return(result); }
protected override bool ProcessObject(object obj, Type expectedType) { foreach (var unloadedObject in ItemsToReload) { // If a collection, stop at parent path level (since index will be already removed, we will never visit the target slot) // TODO: Check if the fact we didn't enter in an item with index affect visitor states // Other case, stop on the actual member (since we'll just visit null) var expectedPath = unloadedObject.Path.Decompose().Last().GetIndex() != null ? unloadedObject.ParentPath : unloadedObject.Path; if (CurrentPath.Match(expectedPath)) { var eventReader = new EventReader(new MemoryParser(unloadedObject.ParsingEvents)); var settings = Log != null ? new SerializerContextSettings { Logger = Log } : null; PropertyContainer properties; unloadedObject.UpdatedObject = AssetYamlSerializer.Default.Deserialize(eventReader, null, unloadedObject.ExpectedType, out properties, settings); // We will have broken references here because we are deserializing objects individually, so we don't pass any logger to discard warnings var metadata = YamlAssetSerializer.CreateAndProcessMetadata(properties, unloadedObject.UpdatedObject, false); var overrides = metadata.RetrieveMetadata(AssetObjectSerializerBackend.OverrideDictionaryKey); unloadedObject.Overrides = overrides; var references = metadata.RetrieveMetadata(AssetObjectSerializerBackend.ObjectReferencesKey); if (references != null) { var basePath = YamlAssetPath.FromMemberPath(CurrentPath, root); foreach (var reference in references) { var basePathWithIndex = basePath.Clone(); if (unloadedObject.GraphPathIndex != NodeIndex.Empty) { if (unloadedObject.ItemId == ItemId.Empty) { basePathWithIndex.PushIndex(unloadedObject.GraphPathIndex.Value); } else { basePathWithIndex.PushItemId(unloadedObject.ItemId); } } var actualPath = basePathWithIndex.Append(reference.Key); ObjectReferences.Set(actualPath, reference.Value); } } } } return(false); }
public static YamlAssetPath ConvertPath(GraphNodePath path, int inNonIdentifiableType) { var currentNode = (AssetNode)path.RootNode; var result = new YamlAssetPath(); var i = 0; foreach (var item in path.Path) { switch (item.Type) { case GraphNodePath.ElementType.Member: var member = (string)item.Value; result.PushMember(member); currentNode = (AssetNode)((IGraphNode)currentNode).TryGetChild(member); break; case GraphNodePath.ElementType.Target: if (i < path.Path.Count - 1) { currentNode = (AssetNode)((IGraphNode)currentNode).Target; } break; case GraphNodePath.ElementType.Index: var index = (Index)item.Value; if (inNonIdentifiableType > 0 || currentNode.IsNonIdentifiableCollectionContent) { result.PushIndex(index.Value); } else { var id = currentNode.IndexToId(index); // Create a new id if we don't have any so far if (id == ItemId.Empty) id = ItemId.New(); result.PushItemId(id); } if (i < path.Path.Count - 1) { currentNode = (AssetNode)((IGraphNode)currentNode).IndexedTarget(index); } break; default: throw new ArgumentOutOfRangeException(); } ++i; } return result; }
public void TestConcreteReferenceConcreteObjectSerialization() { var obj = new Container { Referenceable1 = new Referenceable { Id = GuidGenerator.Get(1), Value = "Test" } }; obj.Referenceable2 = obj.Referenceable1; var objectReferences = new YamlAssetMetadata <Guid>(); var path = new YamlAssetPath(); path.PushMember(nameof(Container.Referenceable2)); objectReferences.Set(path, obj.Referenceable2.Id); var yaml = SerializeAsString(obj, objectReferences); Assert.AreEqual(ConcreteReferenceConcreteObjectYaml, yaml); }
public void TestAbstracteferenceObjectAbstractSerialization() { var obj = new Container { Referenceable3 = new Referenceable { Id = GuidGenerator.Get(1), Value = "Test" } }; obj.Referenceable4 = (Referenceable)obj.Referenceable3; var objectReferences = new YamlAssetMetadata <Guid>(); var path = new YamlAssetPath(); path.PushMember(nameof(Container.Referenceable4)); objectReferences.Set(path, obj.Referenceable4.Id); var yaml = SerializeAsString(obj, objectReferences); Assert.AreEqual(AbstractReferenceAbstractObjectYaml, yaml); }
public override object ReadDictionaryValue(ref ObjectContext objectContext, Type valueType, object key) { var path = GetCurrentPath(ref objectContext, true); ItemId id; if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, key, out id)) { path.PushItemId(id); } else { path.PushIndex(key); } var valueObjectContext = new ObjectContext(objectContext.SerializerContext, null, objectContext.SerializerContext.FindTypeDescriptor(valueType)); SetCurrentPath(ref valueObjectContext, path); return(ReadYaml(ref valueObjectContext)); }
public override void WriteDictionaryValue(ref ObjectContext objectContext, object key, object value, Type valueType) { var path = GetCurrentPath(ref objectContext, true); ItemId id; if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, key, out id)) { path.PushItemId(id); } else { path.PushIndex(key); } var itemObjectContext = new ObjectContext(objectContext.SerializerContext, value, objectContext.SerializerContext.FindTypeDescriptor(valueType)); SetCurrentPath(ref itemObjectContext, path); WriteYaml(ref itemObjectContext); }
public override void WriteDictionaryKey(ref ObjectContext objectContext, object key, Type keyType) { YamlAssetMetadata <OverrideType> overrides; if (objectContext.SerializerContext.Properties.TryGetValue(OverrideDictionaryKey, out overrides)) { var itemPath = GetCurrentPath(ref objectContext, true); YamlAssetPath keyPath = null; ItemId id; object actualKey; if (YamlAssetPath.IsCollectionWithIdType(objectContext.Descriptor.Type, key, out id, out actualKey)) { keyPath = itemPath.Clone(); keyPath.PushIndex(actualKey); itemPath.PushItemId(id); } else { itemPath.PushIndex(key); } var overrideType = overrides.TryGet(itemPath); if ((overrideType & OverrideType.New) != 0) { objectContext.SerializerContext.Properties.Set(ItemIdSerializerBase.OverrideInfoKey, OverridePostfixes.PostFixNew.ToString()); } if ((overrideType & OverrideType.Sealed) != 0) { objectContext.SerializerContext.Properties.Set(ItemIdSerializerBase.OverrideInfoKey, OverridePostfixes.PostFixSealed.ToString()); } if (keyPath != null) { overrideType = overrides.TryGet(keyPath); if ((overrideType & OverrideType.New) != 0) { objectContext.SerializerContext.Properties.Set(KeyWithIdSerializer.OverrideKeyInfoKey, OverridePostfixes.PostFixNew.ToString()); } if ((overrideType & OverrideType.Sealed) != 0) { objectContext.SerializerContext.Properties.Set(KeyWithIdSerializer.OverrideKeyInfoKey, OverridePostfixes.PostFixSealed.ToString()); } } } base.WriteDictionaryKey(ref objectContext, key, keyType); }
public void TestAbstractNonIdentifiableReferenceableDictionarySerialization() { var obj = new NonIdentifiableCollectionContainer(); var item = new Referenceable { Id = GuidGenerator.Get(1), Value = "Test" }; obj.AbstractRefDictionary.Add("Item1", item); obj.AbstractRefDictionary.Add("Item2", item); var objectReferences = new YamlAssetMetadata <Guid>(); var path = new YamlAssetPath(); path.PushMember(nameof(CollectionContainer.AbstractRefDictionary)); path.PushIndex("Item1"); objectReferences.Set(path, GuidGenerator.Get(1)); var yaml = SerializeAsString(obj, objectReferences); Assert.AreEqual(AbstractNonIdentifiableReferenceableDictionaryYaml, yaml); }
public void TestConcreteNonIdentifiableReferenceableListSerialization() { var obj = new NonIdentifiableCollectionContainer(); var item = new Referenceable { Id = GuidGenerator.Get(1), Value = "Test" }; obj.ConcreteRefList.Add(item); obj.ConcreteRefList.Add(item); var objectReferences = new YamlAssetMetadata <Guid>(); var path = new YamlAssetPath(); path.PushMember(nameof(CollectionContainer.ConcreteRefList)); path.PushIndex(0); objectReferences.Set(path, GuidGenerator.Get(1)); var yaml = SerializeAsString(obj, objectReferences); Assert.AreEqual(ConcreteNonIdentifiableReferenceableListYaml, yaml); }
public void TestAbstractReferenceableListSerialization() { var obj = new CollectionContainer(); var item = new Referenceable { Id = GuidGenerator.Get(1), Value = "Test" }; obj.AbstractRefList.Add(item); obj.AbstractRefList.Add(item); var objectReferences = new YamlAssetMetadata <Guid>(); var ids = CollectionItemIdHelper.GetCollectionItemIds(obj.AbstractRefList); ids[0] = IdentifierGenerator.Get(1); ids[1] = IdentifierGenerator.Get(2); var path = new YamlAssetPath(); path.PushMember(nameof(CollectionContainer.AbstractRefList)); path.PushItemId(IdentifierGenerator.Get(1)); objectReferences.Set(path, GuidGenerator.Get(1)); var yaml = SerializeAsString(obj, objectReferences); Assert.AreEqual(AbstractReferenceableListYaml, yaml); }
public void TestConcreteReferenceableDictionarySerialization() { var obj = new CollectionContainer(); var item = new Referenceable { Id = GuidGenerator.Get(1), Value = "Test" }; obj.ConcreteRefDictionary.Add("Item1", item); obj.ConcreteRefDictionary.Add("Item2", item); var objectReferences = new YamlAssetMetadata <Guid>(); var ids = CollectionItemIdHelper.GetCollectionItemIds(obj.ConcreteRefDictionary); ids["Item1"] = IdentifierGenerator.Get(1); ids["Item2"] = IdentifierGenerator.Get(2); var path = new YamlAssetPath(); path.PushMember(nameof(CollectionContainer.ConcreteRefDictionary)); path.PushItemId(IdentifierGenerator.Get(1)); objectReferences.Set(path, GuidGenerator.Get(1)); var yaml = SerializeAsString(obj, objectReferences); Assert.AreEqual(ConcreteReferenceableDictionaryYaml, yaml); }
private static void SetCurrentPath(ref ObjectContext objectContext, YamlAssetPath path) { objectContext.Properties.Set(MemberPathKey, path); }
public static IAssetNode ResolveObjectPath([NotNull] IAssetNode rootNode, [NotNull] YamlAssetPath path, out Index index, out bool overrideOnKey) { var currentNode = rootNode; index = Index.Empty; overrideOnKey = false; for (var i = 0; i < path.Items.Count; i++) { var item = path.Items[i]; switch (item.Type) { case YamlAssetPath.ItemType.Member: index = Index.Empty; overrideOnKey = false; if (currentNode.IsReference) { var memberNode = currentNode as IMemberNode; if (memberNode == null) { throw new InvalidOperationException($"An IMemberNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)memberNode.Target; } var objectNode = currentNode as IObjectNode; if (objectNode == null) { throw new InvalidOperationException($"An IObjectNode was expected when processing the path [{path}]"); } var name = item.AsMember(); currentNode = (IAssetNode)objectNode.TryGetChild(name); break; case YamlAssetPath.ItemType.Index: index = new Index(item.Value); overrideOnKey = true; if (currentNode.IsReference && i < path.Items.Count - 1) { currentNode = (IAssetNode)currentNode.IndexedTarget(new Index(item.Value)); } break; case YamlAssetPath.ItemType.ItemId: var ids = CollectionItemIdHelper.GetCollectionItemIds(currentNode.Retrieve()); var key = ids.GetKey(item.AsItemId()); index = new Index(key); overrideOnKey = false; if (currentNode.IsReference && i < path.Items.Count - 1) { currentNode = (IAssetNode)currentNode.IndexedTarget(new Index(key)); } break; default: throw new ArgumentOutOfRangeException(); } // Something wrong happen, the node is unreachable. if (currentNode == null) { return(null); } } return(currentNode); }
public void TestGenerateOverridesForSerializationOfCollectionItem() { const string expectedYaml = @"!SiliconStudio.Assets.Quantum.Tests.Types+SomeObject,SiliconStudio.Assets.Quantum.Tests Value*: OverriddenString "; var asset = new Types.MyAsset4 { MyObjects = { new Types.SomeObject { Value = "String1" }, new Types.SomeObject { Value = "String2" } } }; var context = DeriveAssetTest<Types.MyAsset4>.DeriveAsset(asset); var derivedPropertyNode = (AssetNode)((IGraphNode)context.DerivedGraph.RootNode)[nameof(Types.MyAsset4.MyObjects)].IndexedTarget(new Index(1)); derivedPropertyNode[nameof(Types.SomeObject.Value)].Content.Update("OverriddenString"); var expectedPath = new YamlAssetPath(); expectedPath.PushMember(nameof(Types.SomeObject.Value)); var overrides = AssetPropertyGraph.GenerateOverridesForSerialization(derivedPropertyNode); Assert.AreEqual(1, overrides.Count); Assert.True(overrides.ContainsKey(expectedPath)); Assert.AreEqual(OverrideType.New, overrides[expectedPath]); // Test deserialization SerializeAndCompare(context.DerivedAsset.MyObjects[1], overrides, expectedYaml); bool aliasOccurred; var instance = (Types.SomeObject)AssetFileSerializer.Default.Load(DeriveAssetTest<Types.MyAsset9>.ToStream(expectedYaml), null, null, out aliasOccurred, out overrides); Assert.AreEqual("OverriddenString", instance.Value); Assert.AreEqual(1, overrides.Count); Assert.True(overrides.ContainsKey(expectedPath)); Assert.AreEqual(OverrideType.New, overrides[expectedPath]); }
/// <inheritdoc /> public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath) { var asset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)targetRootObject; var hierarchy = data as AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>; if (hierarchy == null) { return(false); } // Create a temporary asset to host the hierarchy to paste, so we have a property graph to manipulate it. var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(asset.GetType()); tempAsset.Hierarchy = hierarchy; // Use temporary containers so that any created nodes are discarded after the processing. var tempNodeContainer = new AssetNodeContainer { NodeBuilder = { NodeFactory = new AssetNodeFactory() } }; var definition = AssetQuantumRegistry.GetDefinition(asset.GetType()); var rootNode = tempNodeContainer.GetOrCreateNode(tempAsset); // If different asset or if at least one part already exists, create a custom clone. if (asset.Id != sourceId || hierarchy.Parts.Values.Any(part => asset.ContainsPart(part.Part.Id))) { // Clone again to create new ids for any IIdentifiable, but keep references to external object intact. var cloneExternalReferences = ExternalReferenceCollector.GetExternalReferences(definition, rootNode); hierarchy = AssetCloner.Clone(hierarchy, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, cloneExternalReferences, out var idRemapping); // Remap indices of parts in Hierarchy.Part AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, basePath); // Make new base instances ids in case the part are inherited. AssetPartsAnalysis.GenerateNewBaseInstanceIds(hierarchy); // Update the temporary asset with this cloned hierarchy. rootNode[nameof(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> .Hierarchy)].Update(hierarchy); } // Collect all referenceable objects from the target asset (where we're pasting) var targetPropertyGraph = graphContainer.TryGetGraph(asset.Id); var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode); // Replace references in the hierarchy being pasted by the real objects from the target asset. var externalReferences = new HashSet <Guid>(ExternalReferenceCollector.GetExternalReferences(definition, rootNode).Select(x => x.Id)); var visitor = new ObjectReferencePathGenerator(definition) { ShouldOutputReference = x => externalReferences.Contains(x) }; visitor.Visit(rootNode); FixupObjectReferences.FixupReferences(tempAsset, visitor.Result, referenceableObjects, true); data = hierarchy; return(true); }
public AssetNode ResolveObjectPath(YamlAssetPath path, out Index index, out bool overrideOnKey) { var currentNode = this; index = Index.Empty; overrideOnKey = false; for (var i = 0; i < path.Items.Count; i++) { var item = path.Items[i]; switch (item.Type) { case YamlAssetPath.ItemType.Member: index = Index.Empty; overrideOnKey = false; if (currentNode.Content.IsReference) { currentNode = (AssetNode)((IGraphNode)currentNode).Target; } string name = item.AsMember(); currentNode = (AssetNode)((IGraphNode)currentNode).TryGetChild(name); break; case YamlAssetPath.ItemType.Index: index = new Index(item.Value); overrideOnKey = true; if (currentNode.Content.IsReference && i < path.Items.Count - 1) { Index index1 = new Index(item.Value); currentNode = (AssetNode)((IGraphNode)currentNode).IndexedTarget(index1); } break; case YamlAssetPath.ItemType.ItemId: var ids = CollectionItemIdHelper.GetCollectionItemIds(currentNode.Content.Retrieve()); var key = ids.GetKey(item.AsItemId()); index = new Index(key); overrideOnKey = false; if (currentNode.Content.IsReference && i < path.Items.Count - 1) { Index index1 = new Index(key); currentNode = (AssetNode)((IGraphNode)currentNode).IndexedTarget(index1); } break; default: throw new ArgumentOutOfRangeException(); } // Something wrong happen, the node is unreachable. if (currentNode == null) return null; } return currentNode; }
public static YamlAssetPath ConvertPath(GraphNodePath path, int inNonIdentifiableType) { var currentNode = (IAssetNode)path.RootNode; var result = new YamlAssetPath(); var i = 0; foreach (var item in path.Path) { switch (item.Type) { case GraphNodePath.ElementType.Member: var member = (string)item.Value; result.PushMember(member); var objectNode = currentNode as IObjectNode; if (objectNode == null) { throw new InvalidOperationException($"An IObjectNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)objectNode.TryGetChild(member); break; case GraphNodePath.ElementType.Target: if (i < path.Path.Count - 1) { var targetingMemberNode = currentNode as IMemberNode; if (targetingMemberNode == null) { throw new InvalidOperationException($"An IMemberNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)targetingMemberNode.Target; } break; case GraphNodePath.ElementType.Index: var index = (Index)item.Value; var memberNode = currentNode as AssetMemberNode; if (memberNode == null) { throw new InvalidOperationException($"An AssetMemberNode was expected when processing the path [{path}]"); } if (inNonIdentifiableType > 0 || memberNode.IsNonIdentifiableCollectionContent) { result.PushIndex(index.Value); } else { var id = memberNode.IndexToId(index); // Create a new id if we don't have any so far if (id == ItemId.Empty) { id = ItemId.New(); } result.PushItemId(id); } if (i < path.Path.Count - 1) { currentNode = (IAssetNode)currentNode.IndexedTarget(index); } break; default: throw new ArgumentOutOfRangeException(); } ++i; } return(result); }
public static YamlAssetPath ConvertPath([NotNull] GraphNodePath path, int inNonIdentifiableType = 0) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var currentNode = (IAssetNode)path.RootNode; var result = new YamlAssetPath(); var i = 0; foreach (var item in path.Path) { switch (item.Type) { case GraphNodePath.ElementType.Member: { var member = item.Name; result.PushMember(member); var objectNode = currentNode as IObjectNode; if (objectNode == null) { throw new InvalidOperationException($"An IObjectNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)objectNode.TryGetChild(member); break; } case GraphNodePath.ElementType.Target: { if (i < path.Path.Count - 1) { var targetingMemberNode = currentNode as IMemberNode; if (targetingMemberNode == null) { throw new InvalidOperationException($"An IMemberNode was expected when processing the path [{path}]"); } currentNode = (IAssetNode)targetingMemberNode.Target; } break; } case GraphNodePath.ElementType.Index: { var index = item.Index; var objectNode = currentNode as AssetObjectNode; if (objectNode == null) { throw new InvalidOperationException($"An IObjectNode was expected when processing the path [{path}]"); } if (inNonIdentifiableType > 0 || !CollectionItemIdHelper.HasCollectionItemIds(objectNode.Retrieve())) { result.PushIndex(index.Value); } else { var id = objectNode.IndexToId(index); // Create a new id if we don't have any so far if (id == ItemId.Empty) { id = ItemId.New(); } result.PushItemId(id); } if (i < path.Path.Count - 1) { currentNode = (IAssetNode)objectNode.IndexedTarget(index); } break; } default: throw new ArgumentOutOfRangeException(); } ++i; } return(result); }
/// <inheritdoc /> public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath) { if (targetRootObject == null) { throw new ArgumentNullException(nameof(targetRootObject)); } if (data == null) { throw new ArgumentNullException(nameof(data)); } var asset = (Asset)targetRootObject; var targetPropertyGraph = graphContainer.TryGetGraph(asset.Id); // We use a container object in case the data itself is an object reference var container = isRootDataObjectReference ? new FixupContainer { Data = data } : data; var rootNode = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container); var externalReferences = ExternalReferenceCollector.GetExternalReferences(targetPropertyGraph.Definition, rootNode); try { // Clone to create new ids for any IIdentifiable, except passed external references that will be maintained Dictionary <Guid, Guid> idRemapping; data = AssetCloner.Clone <object>(data, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, externalReferences, out idRemapping); } // TODO: have a proper exception type for serialization failure catch (Exception) { // Note: this can fail if the type doesn't have a binary serializer. return(false); } var targetTypeDescriptor = TypeDescriptorFactory.Default.Find(targetMemberType); bool result; switch (targetTypeDescriptor.Category) { case DescriptorCategory.Collection: result = ConvertForCollection((CollectionDescriptor)targetTypeDescriptor, ref data); break; case DescriptorCategory.Dictionary: result = ConvertForDictionary((DictionaryDescriptor)targetTypeDescriptor, ref data); break; case DescriptorCategory.Primitive: case DescriptorCategory.Object: case DescriptorCategory.NotSupportedObject: case DescriptorCategory.Nullable: result = ConvertForProperty(targetTypeDescriptor.Type, ref data); break; case DescriptorCategory.Array: case DescriptorCategory.Custom: throw new NotSupportedException(); default: throw new ArgumentOutOfRangeException(); } if (!result) { return(false); } // Collect all referenceable objects from the target asset (where we're pasting) var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode); // We use a container object in case the data itself is an object reference container = isRootDataObjectReference ? new FixupContainer { Data = data } : data; rootNode = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container); // Generate YAML paths for the external reference so we can go through the normal deserialization fixup method. var externalReferenceIds = new HashSet <Guid>(externalReferences.Select(x => x.Id)); var visitor = new ObjectReferencePathGenerator(targetPropertyGraph.Definition) { ShouldOutputReference = x => externalReferenceIds.Contains(x) }; visitor.Visit(rootNode); // Fixup external references FixupObjectReferences.FixupReferences(container, visitor.Result, referenceableObjects, true); data = (container as FixupContainer)?.Data ?? data; return(true); }
public void TestGenerateOverridesForSerializationOfObjectMember() { const string expectedYaml = @"!SiliconStudio.Assets.Quantum.Tests.Types+SomeObject,SiliconStudio.Assets.Quantum.Tests Value*: OverriddenString "; var asset = new Types.MyAsset9 { MyObject = new Types.SomeObject { Value = "String1" } }; var context = DeriveAssetTest<Types.MyAsset9>.DeriveAsset(asset); var derivedPropertyNode = (AssetNode)((IGraphNode)context.DerivedGraph.RootNode)[nameof(Types.MyAsset9.MyObject)]; derivedPropertyNode.Target[nameof(Types.SomeObject.Value)].Content.Update("OverriddenString"); var expectedPath = new YamlAssetPath(); expectedPath.PushMember(nameof(Types.SomeObject.Value)); var overrides = AssetPropertyGraph.GenerateOverridesForSerialization(derivedPropertyNode); Assert.AreEqual(1, overrides.Count); Assert.True(overrides.ContainsKey(expectedPath)); Assert.AreEqual(OverrideType.New, overrides[expectedPath]); // We expect the same resulting path both from the member node and the target object node overrides = AssetPropertyGraph.GenerateOverridesForSerialization(derivedPropertyNode.Target); Assert.AreEqual(1, overrides.Count); Assert.True(overrides.ContainsKey(expectedPath)); Assert.AreEqual(OverrideType.New, overrides[expectedPath]); // Test deserialization SerializeAndCompare(context.DerivedAsset.MyObject, overrides, expectedYaml); bool aliasOccurred; var instance = (Types.SomeObject)AssetFileSerializer.Default.Load(DeriveAssetTestBase.ToStream(expectedYaml), null, null, out aliasOccurred, out overrides); Assert.AreEqual("OverriddenString", instance.Value); Assert.AreEqual(1, overrides.Count); Assert.True(overrides.ContainsKey(expectedPath)); Assert.AreEqual(OverrideType.New, overrides[expectedPath]); }
/// <inheritdoc /> public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath) { if (targetRootObject == null) { throw new ArgumentNullException(nameof(targetRootObject)); } if (data == null) { throw new ArgumentNullException(nameof(data)); } var collectionDescriptor = (CollectionDescriptor)TypeDescriptorFactory.Default.Find(targetRootObject.GetType()); var collection = data as IList <AssetItem>; if (collection == null) { collection = (IList <AssetItem>)Activator.CreateInstance(collectionDescriptor.Type, true); collectionDescriptor.Add(collection, data); } for (var i = 0; i < collection.Count; i++) { var assetItem = collection[i]; // If the asset already exists, clone it with new identifiers if (session.GetAssetById(assetItem.Id) != null) { // Create a derived asset and restore archetype to handle asset-specific cloning process. Dictionary <Guid, Guid> idRemapping; var clone = AssetCloner.Clone(assetItem.Asset, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, out idRemapping); var assetType = assetItem.Asset.GetType(); if (assetType.HasInterface(typeof(AssetCompositeHierarchy <,>))) { try { // TODO: Find a way to fallback to the asset or generalize for all asset composite dynamic assetComposite = clone; // Remap indices of parts in Hierarchy.Part var path = basePath.Clone(); path.PushItemId(CollectionItemIdHelper.GetCollectionItemIds(collection)[i]); AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, path); AssetPartsAnalysis.GenerateNewBaseInstanceIds(assetComposite.Hierarchy); } catch (RuntimeBinderException e) { e.Ignore(); } } // FIXME: rework this var postProcessor = session.ServiceProvider.Get <ICopyPasteService>().PostProcessors.FirstOrDefault(p => p.Accept(assetType)); postProcessor?.PostPasteDeserialization(clone); collection[i] = new AssetItem(assetItem.Location, clone); } } // Get the fixed-up value data = collection; return(true); }
/// <inheritdoc /> public abstract bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath);