/// <summary> /// Creates an asset that inherits from this asset. /// </summary> /// <param name="baseLocation">The location of this asset.</param> /// <param name="idRemapping">A dictionary in which will be stored all the <see cref="Guid"/> remapping done for the child asset.</param> /// <returns>An asset that inherits this asset instance</returns> public virtual Asset CreateChildAsset(string baseLocation, IDictionary <Guid, Guid> idRemapping = null) { if (baseLocation == null) { throw new ArgumentNullException(nameof(baseLocation)); } // Clone this asset to make the base var assetBase = (Asset)AssetCloner.Clone(this); // Clone it again without the base and without overrides (as we want all parameters to inherit from base) var newAsset = (Asset)AssetCloner.Clone(assetBase, AssetClonerFlags.RemoveOverrides); // Create a new identifier for this asset var newId = Guid.NewGuid(); // Register this new identifier in the remapping dictionary idRemapping?.Add(newAsset.Id, newId); // Write the new id into the new asset. newAsset.Id = newId; // Create the base of this asset newAsset.Base = new AssetBase(baseLocation, assetBase); return(newAsset); }
/// <summary> /// Creates an asset that inherits from this asset. /// </summary> /// <param name="baseLocation">The location of this asset.</param> /// <param name="idRemapping">A dictionary in which will be stored all the <see cref="Guid"/> remapping done for the child asset.</param> /// <returns>An asset that inherits this asset instance</returns> // TODO: turn internal protected and expose only AssetItem.CreateDerivedAsset() public virtual Asset CreateDerivedAsset(string baseLocation, out Dictionary <Guid, Guid> idRemapping) { if (baseLocation == null) { throw new ArgumentNullException(nameof(baseLocation)); } // Make sure we have identifiers for all items AssetCollectionItemIdHelper.GenerateMissingItemIds(this); // Clone this asset without overrides (as we want all parameters to inherit from base) var newAsset = AssetCloner.Clone(this, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, out idRemapping); // Create a new identifier for this asset var newId = AssetId.New(); // Register this new identifier in the remapping dictionary idRemapping?.Add((Guid)newAsset.Id, (Guid)newId); // Write the new id into the new asset. newAsset.Id = newId; // Create the base of this asset newAsset.Archetype = new AssetReference(Id, baseLocation); return(newAsset); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return(null); } var cloner = new AssetCloner(asset, flags); return(cloner.Clone()); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="keepOnlySealedOverride">if set to <c>true</c> to discard override information except sealed.</param> /// <returns>A clone of the asset.</returns> /// TODO: This code is not efficient as it is using YAML serialization for cloning assets public static object Clone(object asset, bool keepOnlySealedOverride = false) { if (asset == null) { return(null); } var cloner = new AssetCloner(asset, keepOnlySealedOverride); return(cloner.Clone()); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <param name="externalIdentifiable"></param> /// <param name="idRemapping">A dictionary containing the remapping of <see cref="IIdentifiable.Id"/> if <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been passed to the cloner.</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags, HashSet <IIdentifiable> externalIdentifiable, [NotNull] out Dictionary <Guid, Guid> idRemapping) { if (asset == null) { idRemapping = new Dictionary <Guid, Guid>(); return(null); } var cloner = new AssetCloner(asset, flags, externalIdentifiable); var newObject = cloner.Clone(out idRemapping); return(newObject); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <param name="idRemapping">A dictionary containing the remapping of <see cref="IIdentifiable.Id"/> if <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been passed to the cloner.</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags, out Dictionary <Guid, Guid> idRemapping) { if (asset == null) { idRemapping = null; return(null); } var cloner = new AssetCloner(asset, flags); var newObject = cloner.Clone(out idRemapping); return(newObject); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return(null); } var cloner = new AssetCloner(asset, flags); Dictionary <Guid, Guid> idMapping; var newObject = cloner.Clone(out idMapping); return(newObject); }
/// <summary> /// Clones this instance without the attached package. /// </summary> /// <param name="newLocation">The new location that will be used in the cloned <see cref="AssetItem" />. If this parameter /// is null, it keeps the original location of the asset.</param> /// <param name="newAsset">The new asset that will be used in the cloned <see cref="AssetItem" />. If this parameter /// is null, it clones the original asset. otherwise, the specified asset is used as-is in the new <see cref="AssetItem" /> /// (no clone on newAsset is performed)</param> /// <param name="copyPackage">if set to <c>true</c> copy package information, only used by the <see cref="AssetDependencyManager" />.</param> /// <returns>A clone of this instance.</returns> internal AssetItem Clone(bool copyPackage, UFile newLocation = null, Asset newAsset = null) { // Set the package after the new AssetItem(), to make sure that isDirty is not going to call a notification on the // package var item = new AssetItem(newLocation ?? location, newAsset ?? (Asset)AssetCloner.Clone(Asset), copyPackage ? Package : null) { isDirty = isDirty, SourceFolder = SourceFolder }; return(item); }
/// <summary> /// Generates a runtime hash id from the serialization of this asset. /// </summary> /// <param name="asset">The asset to get the runtime hash id</param> /// <param name="flags">Flags used to control the serialization process</param> /// <returns>An object id</returns> internal static ObjectId ComputeHash(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return(ObjectId.Empty); } var cloner = new AssetCloner(asset, flags); var result = cloner.GetHashId(); return(result); }
/// <summary> /// Clones this instance without the attached package. /// </summary> /// <param name="newLocation">The new location that will be used in the cloned <see cref="AssetItem" />. If this parameter /// is null, it keeps the original location of the asset.</param> /// <param name="newAsset">The new asset that will be used in the cloned <see cref="AssetItem" />. If this parameter /// is null, it clones the original asset. otherwise, the specified asset is used as-is in the new <see cref="AssetItem" /> /// (no clone on newAsset is performed)</param> /// <param name="copyPackage">if set to <c>true</c> copy package information, only used by the <see cref="AssetDependencyManager" />.</param> /// <param name="flags">Flags used with <see cref="AssetCloner.Clone"/>.</param> /// <returns>A clone of this instance.</returns> internal AssetItem Clone(bool keepPackage, UFile newLocation = null, Asset newAsset = null, AssetClonerFlags flags = AssetClonerFlags.None) { // Set the package after the new AssetItem(), to make sure that isDirty is not going to call a notification on the // package var item = new AssetItem(newLocation ?? location, newAsset ?? AssetCloner.Clone(Asset, flags), keepPackage ? Package : null) { isDirty = isDirty, SourceFolder = SourceFolder, SourceProject = SourceProject, Overrides = Overrides != null ? new Dictionary <YamlAssetPath, OverrideType>(Overrides) : null }; return(item); }
public AssetItem Clone(bool keepPackage, UFile newLocation = null, Asset newAsset = null, AssetClonerFlags flags = AssetClonerFlags.None) { // Set the package after the new AssetItem(), to make sure that isDirty is not going to call a notification on the // package var item = new AssetItem(newLocation ?? location, newAsset ?? AssetCloner.Clone(Asset, flags), keepPackage ? Package : null) { isDirty = isDirty, SourceFolder = SourceFolder, SourceProject = SourceProject, version = Version }; YamlMetadata.CopyInto(item.YamlMetadata); return(item); }
/// <summary> /// Deep clone this package. /// </summary> /// <param name="deepCloneAsset">if set to <c>true</c> assets will stored in this package will be also deeply cloned.</param> /// <returns>The package cloned.</returns> public Package Clone(bool deepCloneAsset) { // Use a new ShadowRegistry to copy override parameters // Clone this asset var package = (Package)AssetCloner.Clone(this); package.FullPath = FullPath; foreach (var asset in Assets) { var newAsset = deepCloneAsset ? (Asset)AssetCloner.Clone(asset.Asset) : asset.Asset; var assetItem = new AssetItem(asset.Location, newAsset); package.Assets.Add(assetItem); } return(package); }
/// <summary> /// Clones a sub-hierarchy of this asset. /// </summary> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="cleanReference">If true, any reference to a part external to the cloned hierarchy will be set to null.</param> /// <param name="idRemapping">A dictionary containing the mapping of ids from the source parts to the new parts.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{TAssetPartDesign, TAssetPart}"/> corresponding to the cloned parts.</returns> /// <remarks>The parts passed to this methods must be independent in the hierarchy.</remarks> public AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies(IEnumerable <Guid> sourceRootIds, bool cleanReference, out Dictionary <Guid, Guid> idRemapping) { // Note: Instead of copying the whole asset (with its potentially big hierarchy), // we first copy the asset only (without the hierarchy), then the sub-hierarchy to extract. var subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>(); foreach (var rootId in sourceRootIds) { if (!Hierarchy.Parts.ContainsKey(rootId)) { throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds)); } subTreeHierarchy.RootPartIds.Add(rootId); subTreeHierarchy.Parts.Add(Hierarchy.Parts[rootId]); foreach (var subTreePart in EnumerateChildParts(Hierarchy.Parts[rootId].Part, true)) { subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreePart.Id]); } } // clone the parts of the sub-tree var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy); foreach (var rootEntity in clonedHierarchy.RootPartIds) { PostClonePart(clonedHierarchy.Parts[rootEntity].Part); } if (cleanReference) { ClearPartReferences(clonedHierarchy); } // Generate part mapping idRemapping = new Dictionary <Guid, Guid>(); foreach (var partDesign in clonedHierarchy.Parts) { // Generate new Id var newId = Guid.NewGuid(); // Update mappings idRemapping.Add(partDesign.Part.Id, newId); // Update part with new id partDesign.Part.Id = newId; } // Rewrite part references // Should we nullify invalid references? AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping); return(clonedHierarchy); }
/// <summary> /// Creates a child asset that is inheriting the values of this asset. /// </summary> /// <returns>A new asset inheriting the values of this asset.</returns> public Asset CreateChildAsset() { // Clone this asset var assetBase = (Asset)AssetCloner.Clone(Asset); // Remove the base assetBase.Base = null; // Clone it again without the base and without overrides (as we want all parameters to inherit from base) var newAsset = (Asset)AssetCloner.Clone(assetBase, true); // Sets a new identifier for this asset newAsset.Id = Guid.NewGuid(); // Create the base of this asset newAsset.Base = new AssetBase(Location, assetBase); return(newAsset); }
/// <summary> /// Creates an asset that inherits from this asset. /// </summary> /// <param name="location">The location of this asset.</param> /// <returns>An asset that inherits this asset instance</returns> public virtual Asset CreateChildAsset(string location) { if (location == null) { throw new ArgumentNullException(nameof(location)); } // Clone this asset to make the base var assetBase = (Asset)AssetCloner.Clone(this); // Clone it again without the base and without overrides (as we want all parameters to inherit from base) var newAsset = (Asset)AssetCloner.Clone(assetBase, AssetClonerFlags.RemoveOverrides); // Sets a new identifier for this asset newAsset.Id = Guid.NewGuid(); // Create the base of this asset newAsset.Base = new AssetBase(location, assetBase); return(newAsset); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return(null); } var cloner = new AssetCloner(asset, flags); var newObject = cloner.Clone(); // By default, a clone doesn't copy the base/baseParts for Asset if ((flags & AssetClonerFlags.KeepBases) == 0) { var newAsset = newObject as Asset; if (newAsset != null) { newAsset.Base = null; newAsset.BaseParts = null; } } return(newObject); }
/// <summary> /// Fixes asset import that were imported by the previous method. Add a AssetImport.SourceHash and ImporterId /// </summary> /// <param name="item">The item.</param> private static void FixAssetImport(AssetItem item) { // TODO: this whole method is a temporary migration. This should be removed in the next version var assetImport = item.Asset as AssetImport; if (assetImport == null || assetImport.Source == null) { return; } // If the asset has a source but no import base, then we are going to simulate an original import if (assetImport.Base == null) { var fileExtension = assetImport.Source.GetFileExtension(); var assetImportBase = (AssetImport)AssetCloner.Clone(assetImport); assetImportBase.SetAsRootImport(); assetImportBase.SetDefaults(); // Setup default importer if (!String.IsNullOrEmpty(fileExtension)) { var importerId = AssetRegistry.FindImporterByExtension(fileExtension).FirstOrDefault(); if (importerId != null) { assetImport.ImporterId = importerId.Id; } } var assetImportTracked = assetImport as AssetImportTracked; if (assetImportTracked != null) { assetImportTracked.SourceHash = ObjectId.Empty; } assetImport.Base = new AssetBase(assetImportBase); item.IsDirty = true; } }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return null; } var cloner = new AssetCloner(asset, flags); var newObject = cloner.Clone(); // By default, a clone doesn't copy the base/baseParts for Asset if ((flags & AssetClonerFlags.KeepBases) == 0) { var newAsset = newObject as Asset; if (newAsset != null) { newAsset.Base = null; newAsset.BaseParts = null; } } return newObject; }
/// <summary> /// Clones a sub-hierarchy of this asset. /// </summary> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="cleanReference">If true, any reference to a part external to the cloned hierarchy will be set to null.</param> /// <param name="generateNewIdsForIdentifiableObjects">If true, the cloned objects that implement <see cref="IIdentifiable"/> will have new ids.</param> /// <param name="idRemapping">A dictionary containing the remapping of <see cref="IIdentifiable.Id"/> if <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been passed to the cloner.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{TAssetPartDesign, TAssetPart}"/> corresponding to the cloned parts.</returns> /// <remarks>The parts passed to this methods must be independent in the hierarchy.</remarks> public AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies(IEnumerable <Guid> sourceRootIds, bool cleanReference, bool generateNewIdsForIdentifiableObjects, bool generateNewBaseInstanceIds, out Dictionary <Guid, Guid> idRemapping) { // Note: Instead of copying the whole asset (with its potentially big hierarchy), // we first copy the asset only (without the hierarchy), then the sub-hierarchy to extract. var subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>(); foreach (var rootId in sourceRootIds) { if (!Hierarchy.Parts.ContainsKey(rootId)) { throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds)); } subTreeHierarchy.RootPartIds.Add(rootId); subTreeHierarchy.Parts.Add(Hierarchy.Parts[rootId]); foreach (var subTreePart in EnumerateChildParts(Hierarchy.Parts[rootId].Part, true)) { subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreePart.Id]); } } // clone the parts of the sub-tree var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy, generateNewIdsForIdentifiableObjects ? AssetClonerFlags.GenerateNewIdsForIdentifiableObjects : AssetClonerFlags.None, out idRemapping); // Remap ids from the root id collection to the new ids generated during cloning AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping); foreach (var rootEntity in clonedHierarchy.RootPartIds) { PostClonePart(clonedHierarchy.Parts[rootEntity].Part); } if (cleanReference) { // set to null reference outside of the sub-tree var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(GetType()); tempAsset.Hierarchy = clonedHierarchy; tempAsset.FixupPartReferences(); } else { // restore initial ids for reference outside of the subtree, so they can be fixed up later. var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(GetType()); tempAsset.Hierarchy = clonedHierarchy; var visitor = new AssetCompositePartReferenceCollector(); visitor.VisitAsset(tempAsset); var references = visitor.Result; var revertedIdMapping = idRemapping.ToDictionary(x => x.Value, x => x.Key); foreach (var referencedPart in references.Select(x => x.AssetPart).OfType <IIdentifiable>()) { var realPart = tempAsset.ResolvePartReference(referencedPart); if (realPart == null) { referencedPart.Id = revertedIdMapping[referencedPart.Id]; } } } if (generateNewBaseInstanceIds) { AssetPartsAnalysis.GenerateNewBaseInstanceIds(clonedHierarchy); } return(clonedHierarchy); }
/// <summary> /// Clones the specified asset using asset serialization. /// </summary> /// <param name="asset">The asset.</param> /// <param name="flags">Flags used to control the cloning process</param> /// <returns>A clone of the asset.</returns> public static object Clone(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return null; } var cloner = new AssetCloner(asset, flags); return cloner.Clone(); }
/// <summary> /// Saves this package and all dirty assets. See remarks. /// </summary> /// <param name="log">The log.</param> /// <exception cref="System.ArgumentNullException">log</exception> /// <remarks>When calling this method directly, it does not handle moving assets between packages. /// Call <see cref="PackageSession.Save" /> instead.</remarks> public void Save(ILogger log) { if (log == null) { throw new ArgumentNullException("log"); } if (FullPath == null) { log.Error(this, null, AssetMessageCode.PackageCannotSave, "null"); return; } // Use relative paths when saving var analysis = new PackageAnalysis(this, new PackageAnalysisParameters() { SetDirtyFlagOnAssetWhenFixingUFile = false, ConvertUPathTo = UPathType.Relative, IsProcessingUPaths = true }); analysis.Run(log); try { // Update source folders UpdateSourceFolders(); if (IsDirty) { try { // Notifies the dependency manager that a package with the specified path is being saved if (session != null && session.HasDependencyManager) { session.DependencyManager.AddFileBeingSaveDuringSessionSave(FullPath); } AssetSerializer.Save(FullPath, this); IsDirty = false; } catch (Exception ex) { log.Error(this, null, AssetMessageCode.PackageCannotSave, ex, FullPath); return; } } foreach (var asset in Assets) { if (asset.IsDirty) { var assetPath = asset.FullPath; try { // Notifies the dependency manager that an asset with the specified path is being saved if (session != null && session.HasDependencyManager) { session.DependencyManager.AddFileBeingSaveDuringSessionSave(assetPath); } // Incject a copy of the base into the current asset when saving var assetBase = asset.Asset.Base; if (assetBase != null && !assetBase.IsRootImport) { var assetBaseItem = session != null?session.FindAsset(assetBase.Id) : Assets.Find(assetBase.Id); if (assetBaseItem != null) { var newBase = (Asset)AssetCloner.Clone(assetBaseItem.Asset); newBase.Base = null; asset.Asset.Base = new AssetBase(asset.Asset.Base.Location, newBase); } } AssetSerializer.Save(assetPath, asset.Asset); asset.IsDirty = false; } catch (Exception ex) { log.Error(this, asset.ToReference(), AssetMessageCode.AssetCannotSave, ex, assetPath); } } } Assets.IsDirty = false; // Save properties like the Paradox version used PackageSessionHelper.SaveProperties(this); } finally { // Rollback all relative UFile to absolute paths analysis.Parameters.ConvertUPathTo = UPathType.Absolute; analysis.Run(); } }
/// <summary> /// Generates a runtime hash id from the serialization of this asset. /// </summary> /// <param name="asset">The asset to get the runtime hash id</param> /// <param name="flags">Flags used to control the serialization process</param> /// <returns>An object id</returns> internal static ObjectId ComputeHash(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { if (asset == null) { return ObjectId.Empty; } var cloner = new AssetCloner(asset, flags); var result = cloner.GetHashId(); return result; }
/// <summary> /// Computes a stable hash from an asset including all meta informations (ids, overrides). /// </summary> /// <param name="asset">An object instance</param> /// <param name="flags">Flags used to control the serialization process</param> /// <returns>a stable hash</returns> public static ObjectId Compute(object asset, AssetClonerFlags flags = AssetClonerFlags.None) { return(AssetCloner.ComputeHash(asset, flags)); }