Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AssetBaseAnalysis.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 using SiliconStudio.Assets.Diagnostics;
8 using SiliconStudio.Core.Diagnostics;
9 using SiliconStudio.Core.Reflection;
10 
11 namespace SiliconStudio.Assets.Analysis
12 {
13  /// <summary>
14  /// An analysis to validate that all assets in a package have a valid <see cref="Asset.Base"/>.
15  /// In order to be valid, this analysis must be run after a <see cref="PackageAnalysis"/>
16  /// </summary>
18  {
19  /// <summary>
20  /// Initializes a new instance of the <see cref="AssetBaseAnalysis"/> class.
21  /// </summary>
22  /// <param name="packageSession">The package session.</param>
23  public AssetBaseAnalysis(PackageSession packageSession)
24  : base(packageSession)
25  {
26  }
27 
28  /// <summary>
29  /// Performs a wide package validation analysis.
30  /// </summary>
31  /// <param name="log">The log to output the result of the validation.</param>
32  public override void Run(ILogger log)
33  {
34  if (log == null) throw new ArgumentNullException("log");
35 
36  ValidateAssetBase(log);
37  }
38 
39  /// <summary>
40  /// Validates the inheritance of all assets in the package.
41  /// </summary>
42  /// <param name="log">The log to output the result of the analysis.</param>
43  /// <returns>A collection that contains all valid assets.</returns>
44  public HashSet<Asset> ValidateAssetBase(ILogger log)
45  {
46  var invalidAssets = new HashSet<Guid>();
47  var validAssets = new HashSet<Asset>();
48 
49  foreach (var package in Session.Packages)
50  {
51  foreach (var assetItem in package.Assets)
52  {
53  // This asset has been already flagged as invalid
54  if (invalidAssets.Contains(assetItem.Id))
55  {
56  continue;
57  }
58 
59  var result = ValidateAssetBase(assetItem);
60 
61  if (result.HasErrors)
62  {
63  // Copy errors to output log
64  result.CopyTo(log);
65 
66  invalidAssets.Add(assetItem.Id);
67 
68  // Value contains valid base asset, but they are invalid
69  // if any of the parent base are not valid
70  foreach (var baseItem in result.Value)
71  {
72  invalidAssets.Add(baseItem.Id);
73  }
74 
75  foreach (var logMessage in result.Messages.OfType<AssetLogMessage>())
76  {
77  invalidAssets.Add(logMessage.AssetReference.Id);
78  }
79  }
80  else
81  {
82  validAssets.Add(assetItem.Asset);
83  }
84  }
85  }
86 
87  return validAssets;
88  }
89 
90  /// <summary>
91  /// Validates the inheritance of an asset by checking base accessibility up to the root base.
92  /// </summary>
93  /// <param name="assetItem">The asset item.</param>
94  /// <returns>A logger result with a list of all the base in bottom-up orde.</returns>
95  public LoggerValueResult<List<Asset>> ValidateAssetBase(AssetItem assetItem)
96  {
97  var results = new LoggerValueResult<List<Asset>>();
98  results.Value.AddRange(ValidateAssetBase(assetItem, results));
99  return results;
100  }
101 
102  /// <summary>
103  /// Validates the inheritance of an asset by checking base accessibility up to the root base.
104  /// </summary>
105  /// <param name="assetItem">The asset item.</param>
106  /// <param name="log">The log to output the result of the analysis.</param>
107  /// <returns>A list of all the base in bottom-up order.</returns>
108  /// <exception cref="System.ArgumentNullException">asset
109  /// or
110  /// log</exception>
111  public List<Asset> ValidateAssetBase(AssetItem assetItem, ILogger log)
112  {
113  if (assetItem == null) throw new ArgumentNullException("asset");
114  if (log == null) throw new ArgumentNullException("log");
115 
116  var baseItems = new List<Asset>();
117 
118  // 1) Check that item is actually in the package and is the same instance
119  var assetItemFound = Session.FindAsset(assetItem.Id);
120  if (!ReferenceEquals(assetItem.Asset, assetItemFound.Asset))
121  {
122  var assetReference = assetItem.ToReference();
123  log.Error(assetItem.Package, assetReference, AssetMessageCode.AssetNotFound, assetReference);
124  return baseItems;
125  }
126 
127  // 2) Iterate on each base and perform validation
128  var currentAsset = assetItem;
129  while (currentAsset.Asset.Base != null)
130  {
131  // 2.1) Check that asset has not been already processed
132  if (baseItems.Contains(currentAsset.Asset))
133  {
134  // Else this is a circular reference
135  log.Error(assetItem.Package, currentAsset.ToReference(), AssetMessageCode.InvalidCircularReferences, baseItems.Select(item => item.Id));
136  break;
137  }
138 
139  // TODO: here we need to add a deep-scan of each base (including the root) for any embedded assets that are
140  //
141 
142  // 2.2) Check that base asset is existing
143  var baseAssetItem = Session.FindAsset(currentAsset.Asset.Base.Id);
144  if (baseAssetItem == null)
145  {
146  AssetLogMessage error;
147 
148  // If an asset with the same location is registered
149  // Add this asset as a reference in the error message
150  var newBaseAsset = Session.FindAsset(currentAsset.Asset.Base.Location);
151  if (newBaseAsset != null)
152  {
153  // If asset location exist, log a message with the new location, but don't perform any automatic fix
154  error = new AssetLogMessage(currentAsset.Package, currentAsset.ToReference(), LogMessageType.Error, AssetMessageCode.BaseChanged, currentAsset.Asset.Base.Location);
155  error.Related.Add(newBaseAsset.ToReference());
156  }
157  else
158  {
159  // Base was not found. The base asset has been removed.
160  error = new AssetLogMessage(currentAsset.Package, currentAsset.ToReference(), LogMessageType.Error, AssetMessageCode.BaseNotFound);
161  }
162 
163  // Set the member to Base.
164  error.Member = TypeDescriptorFactory.Default.Find(typeof(Asset))["Base"];
165 
166  // Log the error
167  log.Log(error);
168  break;
169  }
170  else
171  {
172  if (baseAssetItem.GetType() != assetItem.Asset.GetType())
173  {
174  log.Error(currentAsset.Package, currentAsset.ToReference(), AssetMessageCode.BaseInvalidType, baseAssetItem.GetType(), assetItem.Asset.GetType());
175  }
176  }
177 
178  currentAsset = baseAssetItem;
179  baseItems.Add(currentAsset.Asset);
180  }
181  return baseItems;
182  }
183 
184  private class AssetVisitor : DataVisitorBase
185  {
186  private readonly Asset rootAsset;
187  private readonly List<Asset> assets = new List<Asset>();
188 
189  public AssetVisitor(ITypeDescriptorFactory typeDescriptorFactory, Asset rootAsset)
190  : base(typeDescriptorFactory)
191  {
192  this.rootAsset = rootAsset;
193  }
194 
195  public List<Asset> Collect()
196  {
197  Visit(rootAsset);
198  return assets;
199  }
200 
201  public override void VisitObject(object obj, ObjectDescriptor descriptor, bool visitMembers)
202  {
203  if (obj is Asset && !ReferenceEquals(obj, rootAsset))
204  {
205  assets.Add((Asset)obj);
206  }
207  base.VisitObject(obj, descriptor, visitMembers);
208  }
209  }
210  }
211 }
HashSet< Asset > ValidateAssetBase(ILogger log)
Validates the inheritance of all assets in the package.
List< Asset > ValidateAssetBase(AssetItem assetItem, ILogger log)
Validates the inheritance of an asset by checking base accessibility up to the root base...
override void Run(ILogger log)
Performs a wide package validation analysis.
Base class for Asset.
Definition: Asset.cs:14
Default implementation of a ITypeDescriptor.
AssetMessageCode
A message code used by AssetLogMessage to identify an error/warning.
A visitor for serializable data (binary, yaml and editor).
Asset()
Initializes a new instance of the Asset class.
Definition: Asset.cs:26
An analysis to validate that all assets in a package have a valid Asset.Base. In order to be valid...
An asset item part of a Package accessible through SiliconStudio.Assets.Package.Assets.
Definition: AssetItem.cs:17
Base class for all Session and Asset integrity analysis.
A session for editing a package.
LoggerValueResult< List< Asset > > ValidateAssetBase(AssetItem assetItem)
Validates the inheritance of an asset by checking base accessibility up to the root base...
Provides a specialized LogMessage to give specific information about an asset.
AssetBaseAnalysis(PackageSession packageSession)
Initializes a new instance of the AssetBaseAnalysis class.
A factory to create an instance of a ITypeDescriptor
Asset Asset
Gets or sets the asset.
Definition: AssetItem.cs:197
Interface for logging.
Definition: ILogger.cs:8