Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AssetRegistry.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 using System.Reflection;
7 using SharpYaml.Serialization;
8 using SiliconStudio.Assets.Serializers;
9 using SiliconStudio.Core;
10 using SiliconStudio.Core.Diagnostics;
11 using SiliconStudio.Core.Reflection;
12 using SiliconStudio.Core.VisualStudio;
13 using SiliconStudio.Core.Yaml;
15 
16 namespace SiliconStudio.Assets
17 {
18  /// <summary>
19  /// A registry for file extensions, <see cref="IAssetImporter"/>, <see cref="IAssetFactory"/>
20  /// and aliases associated with assets.
21  /// </summary>
22  public static class AssetRegistry
23  {
24  private static Logger log = GlobalLogger.GetLogger("Assets.Registry");
25  private static readonly SolutionPlatformCollection supportedPlatforms = new SolutionPlatformCollection();
26  private static readonly Dictionary<Type, string> RegisteredDefaultAssetExtension = new Dictionary<Type, string>();
27  private static readonly Dictionary<Type, IAssetFactory> RegisteredFactories = new Dictionary<Type, IAssetFactory>();
28  private static readonly Dictionary<Type, AssetDescription> RegisteredDescriptions = new Dictionary<Type, AssetDescription>();
29  private static readonly Dictionary<Guid, IAssetImporter> RegisteredImportersInternal = new Dictionary<Guid, IAssetImporter>();
30  private static readonly Dictionary<Type, int> RegisteredFormatVersions = new Dictionary<Type, int>();
31  private static readonly Dictionary<Type, Type[]> RegisteredFormatVersionUpdaterTypes = new Dictionary<Type, Type[]>();
32  private static readonly Dictionary<string, List<IAssetImporter>> RegisterImportExtensions = new Dictionary<string, List<IAssetImporter>>(StringComparer.InvariantCultureIgnoreCase);
33  private static readonly HashSet<string> RegisteredAssetFileExtensions = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
34  internal static readonly HashSet<Assembly> RegisteredAssemblies = new HashSet<Assembly>();
35  internal static readonly HashSet<IYamlSerializableFactory> RegisteredSerializerFactories = new HashSet<IYamlSerializableFactory>();
36  internal static readonly List<IDataCustomVisitor> RegisteredDataVisitNodes = new List<IDataCustomVisitor>();
37  private static Func<object, string, string> stringExpander;
38 
39  /// <summary>
40  /// Gets the supported platforms.
41  /// </summary>
42  /// <value>The supported platforms.</value>
43  public static SolutionPlatformCollection SupportedPlatforms
44  {
45  get
46  {
47  return supportedPlatforms;
48  }
49  }
50 
51  /// <summary>
52  /// Registers the supported platforms.
53  /// </summary>
54  /// <param name="platforms">The platforms.</param>
55  /// <exception cref="System.ArgumentNullException">platforms</exception>
56  public static void RegisterSupportedPlatforms(List<SolutionPlatform> platforms)
57  {
58  if (platforms == null) throw new ArgumentNullException("platforms");
59  if (supportedPlatforms.Count > 0) throw new InvalidOperationException("Cannot register new platforms. RegisterSupportedPlatforms can only be called once");
60 
61  supportedPlatforms.AddRange(platforms);
62  }
63 
64  /// <summary>
65  /// Registers the string expander used by the package references.
66  /// </summary>
67  /// <param name="expander">The expander.</param>
68  public static void RegisterStringExpander(Func<object, string, string> expander)
69  {
70  stringExpander = expander;
71  }
72 
73  /// <summary>
74  /// Expands a string using the registered string expander (<see cref="RegisterStringExpander"/>)
75  /// </summary>
76  /// <param name="context">The context.</param>
77  /// <param name="stringToExpand">The string to expand.</param>
78  /// <returns>System.String.</returns>
79  public static string ExpandString(object context, string stringToExpand)
80  {
81  if (stringExpander != null)
82  {
83  return stringExpander(context, stringToExpand);
84  }
85  return stringToExpand;
86  }
87 
88  /// <summary>
89  /// Gets the asset file extensions.
90  /// </summary>
91  /// <returns>System.String[][].</returns>
92  public static string[] GetAssetFileExtensions()
93  {
94  return RegisteredAssetFileExtensions.ToArray();
95  }
96 
97  /// <summary>
98  /// Gets an enumeration of registered importers.
99  /// </summary>
100  /// <value>The registered importers.</value>
101  public static IEnumerable<IAssetImporter> RegisteredImporters
102  {
103  get
104  {
105  lock (RegisteredImportersInternal)
106  {
107  return RegisteredImportersInternal.Values;
108  }
109  }
110  }
111 
112  /// <summary>
113  /// Determines whether the extension is an asset file type.
114  /// </summary>
115  /// <param name="extension">The extension.</param>
116  /// <returns><c>true</c> if [is asset file extension] [the specified extension]; otherwise, <c>false</c>.</returns>
117  public static bool IsAssetFileExtension(string extension)
118  {
119  if (extension == null) return false;
120  return RegisteredAssetFileExtensions.Contains(extension);
121  }
122 
123  /// <summary>
124  /// Gets the default extension associated with an asset.
125  /// </summary>
126  /// <param name="assetType">The type.</param>
127  /// <returns>System.String.</returns>
128  public static string GetDefaultExtension(Type assetType)
129  {
130  AssertAssetType(assetType);
131  string extension;
132  RegisteredDefaultAssetExtension.TryGetValue(assetType, out extension);
133  return extension;
134  }
135 
136  /// <summary>
137  /// Gets the current format version of an asset.
138  /// </summary>
139  /// <param name="assetType">The asset type.</param>
140  /// <returns>The current format version of this asset.</returns>
141  public static int GetFormatVersion(Type assetType)
142  {
143  AssertAssetType(assetType);
144  int version;
145  RegisteredFormatVersions.TryGetValue(assetType, out version);
146  return version;
147  }
148 
149  /// <summary>
150  /// Gets the current format version of an asset.
151  /// </summary>
152  /// <param name="assetType">The asset type.</param>
153  /// <returns>The current format version of this asset.</returns>
154  public static Type[] GetFormatVersionUpdaterTypes(Type assetType)
155  {
156  AssertAssetType(assetType);
157  Type[] updaters;
158  RegisteredFormatVersionUpdaterTypes.TryGetValue(assetType, out updaters);
159  return updaters;
160  }
161 
162  /// <summary>
163  /// Gets the default extension associated with an asset.
164  /// </summary>
165  /// <typeparam name="T">Type of the asset.</typeparam>
166  /// <returns>System.String.</returns>
167  public static string GetDefaultExtension<T>() where T : Asset
168  {
169  return GetDefaultExtension(typeof(T));
170  }
171 
172  /// <summary>
173  /// Returns an array of asset types that can be instanced with <see cref="NewDefaultInstance"/>.
174  /// </summary>
175  /// <returns>An array of <see cref="Type"/> elements.</returns>
176  public static Type[] GetInstantiableTypes()
177  {
178  return RegisteredFactories.Keys.ToArray();
179  }
180 
181  /// <summary>
182  /// Gets the description associated to the asset type, if available.
183  /// </summary>
184  /// <param name="assetType">Type of the asset.</param>
185  /// <returns>Am <see cref="AssetDescription"/> object, if available, or <c>null</c> otherwise.</returns>
186  public static AssetDescription GetDescription(Type assetType)
187  {
188  AssertAssetType(assetType);
189  AssetDescription description;
190  RegisteredDescriptions.TryGetValue(assetType, out description);
191  return description;
192  }
193 
194  /// <summary>
195  /// Returns an array of asset types that have a description.
196  /// </summary>
197  /// <returns>An array of <see cref="Type"/> elements.</returns>
198  public static Type[] GetDescribedTypes()
199  {
200  return RegisteredDescriptions.Keys.ToArray();
201  }
202 
203  /// <summary>
204  /// Creates a default instance for an asset type.
205  /// </summary>
206  /// <param name="assetType">Type of the asset.</param>
207  /// <returns>A new default instance of an asset.</returns>
208  public static Asset NewDefaultInstance(Type assetType)
209  {
210  AssertAssetType(assetType);
211  IAssetFactory factory;
212  RegisteredFactories.TryGetValue(assetType, out factory);
213 
214  // If no registered factory, creates directly the asset
215  if (factory == null)
216  {
217  return (Asset)Activator.CreateInstance(assetType);
218  }
219 
220  return factory.New();
221  }
222 
223 
224  /// <summary>
225  /// Determines whether [is importer supporting extension] [the specified extension].
226  /// </summary>
227  /// <param name="extension">The extension.</param>
228  /// <returns><c>true</c> if [is importer supporting extension] [the specified extension]; otherwise, <c>false</c>.</returns>
229  /// <exception cref="System.ArgumentNullException">extension</exception>
230  public static bool IsImporterSupportingExtension(string extension)
231  {
232  if (extension == null) throw new ArgumentNullException("extension");
233 
234  lock (RegisterImportExtensions)
235  {
236  return RegisterImportExtensions.ContainsKey(extension);
237  }
238  }
239 
240  /// <summary>
241  /// Finds the importer associated with an asset by the extension of the file to import.
242  /// </summary>
243  /// <param name="extension">The extension of the file to import.</param>
244  /// <returns>An instance of the importer of null if not found.</returns>
245  public static IEnumerable<IAssetImporter> FindImporterByExtension(string extension)
246  {
247  if (extension == null) throw new ArgumentNullException("extension");
248 
249  lock (RegisterImportExtensions)
250  {
251  List<IAssetImporter> importers;
252  if (RegisterImportExtensions.TryGetValue(extension, out importers))
253  {
254  var newImporters = new List<IAssetImporter>(importers);
255  return newImporters;
256  }
257  }
258  return Enumerable.Empty<IAssetImporter>();
259  }
260 
261  /// <summary>
262  /// Finds an importer by its id.
263  /// </summary>
264  /// <param name="importerId">The importer identifier.</param>
265  /// <returns>An instance of the importer of null if not found.</returns>
266  public static IAssetImporter FindImporterById(Guid importerId)
267  {
268  lock (RegisteredImportersInternal)
269  {
270  IAssetImporter importer;
271  if (RegisteredImportersInternal.TryGetValue(importerId, out importer))
272  {
273  return importer;
274  }
275  }
276  return null;
277  }
278 
279  /// <summary>
280  /// Registers a <see cref="IAssetImporter" /> for the specified asset type.
281  /// </summary>
282  /// <param name="importer">The importer.</param>
283  /// <exception cref="System.ArgumentNullException">importer</exception>
284  public static void RegisterImporter(IAssetImporter importer)
285  {
286  if (importer == null) throw new ArgumentNullException("importer");
287 
288  // Register this importer
289  lock (RegisteredImportersInternal)
290  {
291  if (RegisteredImportersInternal.ContainsKey(importer.Id))
292  return;
293  RegisteredImportersInternal[importer.Id] = importer;
294  }
295 
296  // Register file extensions to type
297  var extensions = FileUtility.GetFileExtensions(importer.SupportedFileExtensions);
298  lock (RegisterImportExtensions)
299  {
300  foreach (var extension in extensions)
301  {
302  List<IAssetImporter> importers;
303  if (!RegisterImportExtensions.TryGetValue(extension, out importers))
304  {
305  importers = new List<IAssetImporter>();
306  RegisterImportExtensions.Add(extension, importers);
307  }
308  if (!importers.Contains(importer))
309  {
310  importers.Add(importer);
311  // Always keep the list of importer sotred by their DisplayRank
312  importers.Sort( (left, right) => -left.DisplayRank.CompareTo(right.DisplayRank));
313  }
314  }
315  }
316  }
317 
318  /// <summary>
319  /// Registers a <see cref="IAssetFactory" /> for the specified asset type.
320  /// </summary>
321  /// <param name="assetType">Type of the asset.</param>
322  /// <param name="factory">The factory.</param>
323  public static void RegisterFactory(Type assetType, IAssetFactory factory)
324  {
325  AssertAssetType(assetType);
326  RegisteredFactories[assetType] = factory;
327  }
328 
329  /// <summary>
330  /// Registers a <see cref="AssetDescription"/> for the specified asset type.
331  /// </summary>
332  /// <param name="assetType">Type of the asset.</param>
333  /// <param name="description">The description.</param>
334  public static void RegisterDescription(Type assetType, AssetDescription description)
335  {
336  if (description == null) throw new ArgumentNullException("description");
337  AssertAssetType(assetType);
338  RegisteredDescriptions[assetType] = description;
339  }
340 
341  /// <summary>
342  /// Registers the asset assembly. This assembly should provide <see cref="Asset"/> objects, associated with
343  /// <see cref="ICompiler"/> and optionaly a <see cref="IAssetImporter"/>.
344  /// </summary>
345  /// <param name="assembly">The assembly.</param>
346  /// <exception cref="System.ArgumentNullException">assembly</exception>
347  /// <exception cref="AssetException">
348  /// Invalid compiler type [{0}], must inherit from IAssetImporter.ToFormat(assetCompiler.CompilerTypeName)
349  /// or
350  /// Unable to instantiate compiler [{0}].ToFormat(assetCompiler.CompilerTypeName)
351  /// or
352  /// Invalid importer type [{0}], must inherit from IAssetImporter.ToFormat(assetImporter.ImpoterTypeName)
353  /// or
354  /// Unable to instantiate importer [{0}].ToFormat(assetImporter.ImpoterTypeName)
355  /// </exception>
356  public static void RegisterAssembly(Assembly assembly)
357  {
358  if (assembly == null) throw new ArgumentNullException("assembly");
359 
360  if (RegisteredAssemblies.Contains(assembly))
361  {
362  return;
363  }
364  RegisteredAssemblies.Add(assembly);
365 
366  // Process Asset types.
367  foreach (var type in assembly.GetTypes())
368  {
369  // Register serializer factories
370  if (type.GetCustomAttribute<YamlSerializerFactoryAttribute>() != null)
371  {
372  if (typeof(IYamlSerializableFactory).IsAssignableFrom(type))
373  {
374  try
375  {
376  var yamlFactory = (IYamlSerializableFactory)Activator.CreateInstance(type);
377  RegisteredSerializerFactories.Add(yamlFactory);
378 
379  // TODO: Handle IDataCustomVisitor on its own instead of relying on the coupling with IYamlSerializableFactory
380  var dataCustomVisitor = yamlFactory as IDataCustomVisitor;
381  if (dataCustomVisitor != null)
382  {
383  RegisteredDataVisitNodes.Add(dataCustomVisitor);
384  }
385  }
386  catch (Exception ex)
387  {
388  log.Error("Unable to instantiate serializer factory [{0}]", ex, type);
389  }
390  }
391  }
392 
393  // Asset importer
394  if (typeof(IAssetImporter).IsAssignableFrom(type) && type.GetConstructor(new Type[0]) != null)
395  {
396  try
397  {
398  var importerInstance = Activator.CreateInstance(type) as IAssetImporter;
399 
400  // Register the importer instance
401  RegisterImporter(importerInstance);
402  }
403  catch (Exception ex)
404  {
405  log.Error("Unable to instantiate importer [{0}]", ex, type.Name);
406  }
407  }
408 
409 
410  // Only process Asset types
411  var assetType = type;
412  if (!typeof(Asset).IsAssignableFrom(assetType) || !assetType.IsClass)
413  {
414  continue;
415  }
416 
417  var isSourceCodeAsset = typeof(SourceCodeAsset).IsAssignableFrom(assetType);
418 
419  // Asset FileExtensions
420  var assetFileExtensionAttribute = assetType.GetCustomAttribute<AssetFileExtensionAttribute>();
421  if (assetFileExtensionAttribute != null && assetFileExtensionAttribute.FileExtensions != null)
422  {
423  var extensions = FileUtility.GetFileExtensions(assetFileExtensionAttribute.FileExtensions);
424  RegisteredDefaultAssetExtension[assetType] = extensions.FirstOrDefault();
425  foreach (var extension in extensions)
426  {
427  RegisteredAssetFileExtensions.Add(extension);
428 
429  // If the asset is a pure sourcecode asset, then register the serializer
430  if (isSourceCodeAsset)
431  {
432  SourceCodeAssetSerializer.RegisterExtension(assetType, extension);
433  }
434  }
435  }
436 
437  var assetFormatVersion = assetType.GetCustomAttribute<AssetFormatVersionAttribute>();
438  if (assetFormatVersion != null)
439  {
440  RegisteredFormatVersions.Add(assetType, assetFormatVersion.Version);
441  RegisteredFormatVersionUpdaterTypes.Add(assetType, assetFormatVersion.AssetUpdaterTypes);
442  }
443 
444  // Asset factory
445  var assetFactory = assetType.GetCustomAttribute<AssetFactoryAttribute>();
446  if (assetFactory != null)
447  {
448  // A null factory name means that the type is not instantiable
449  if (assetFactory.FactoryTypeName != null)
450  {
451  try
452  {
453  var factoryType = Type.GetType(assetFactory.FactoryTypeName);
454  if (factoryType == null)
455  {
456  log.Error("Unable to find factory [{0}] for asset [{1}]", assetFactory.FactoryTypeName, assetType);
457  goto labelAssetDescription;
458  }
459 
460  var factoryInstance = Activator.CreateInstance(factoryType) as IAssetFactory;
461  if (factoryInstance == null)
462  {
463  log.Error("Invalid factory type [{0}], must inherit from IAssetImporter", assetFactory.FactoryTypeName);
464  goto labelAssetDescription;
465  }
466 
467  RegisterFactory(assetType, factoryInstance);
468  }
469  catch (Exception ex)
470  {
471  if (ex is AssetException)
472  throw;
473 
474  throw new AssetException("Unable to instantiate factory [{0}]".ToFormat(assetFactory.FactoryTypeName), ex);
475  }
476  }
477  }
478  else
479  {
480  var assetConstructor = assetType.GetConstructor(Type.EmptyTypes);
481  if (assetConstructor != null)
482  {
483  RegisterFactory(assetType, null);
484  }
485  }
486  labelAssetDescription:
487 
488  // Asset description
489  var assetDescription = assetType.GetCustomAttribute<AssetDescriptionAttribute>();
490  if (assetDescription != null)
491  {
492  RegisterDescription(assetType, assetDescription.GetDescription());
493  }
494  }
495  }
496 
497  private static void AssertAssetType(Type assetType)
498  {
499  if (assetType == null)
500  throw new ArgumentNullException("assetType");
501 
502  if (!typeof(Asset).IsAssignableFrom(assetType))
503  throw new ArgumentException("Type [{0}] must be assignable to Asset".ToFormat(assetType), "assetType");
504  }
505 
506  static AssetRegistry()
507  {
508  // Statically find all assemblies related to assets and register them
509  var assemblies = AssemblyRegistry.Find(AssemblyCommonCategories.Assets);
510  foreach (var assembly in assemblies)
511  {
512  RegisterAssembly(assembly);
513  }
514  AssemblyRegistry.AssemblyRegistered += AssemblyRegistry_AssemblyRegistered;
515  }
516 
517  static void AssemblyRegistry_AssemblyRegistered(object sender, AssemblyRegisteredEventArgs e)
518  {
519  // Handle delay-loading assemblies
521  {
522  RegisterAssembly(e.Assembly);
523  }
524  }
525  }
526 }
Attribute to define for a IAssetFactory for a Asset.
static bool IsAssetFileExtension(string extension)
Determines whether the extension is an asset file type.
Describes what format version this asset currently uses, for asset upgrading.
static IAssetImporter FindImporterById(Guid importerId)
Finds an importer by its id.
Base class for Asset.
Definition: Asset.cs:14
Attribute use to tag a class that is implementing a IYamlSerializable or IYamlSerializableFactory and...
static AssetDescription GetDescription(Type assetType)
Gets the description associated to the asset type, if available.
A registry for file extensions, IAssetImporter, IAssetFactory and aliases associated with assets...
static void RegisterDescription(Type assetType, AssetDescription description)
Registers a AssetDescription for the specified asset type.
static int GetFormatVersion(Type assetType)
Gets the current format version of an asset.
static void RegisterImporter(IAssetImporter importer)
Registers a IAssetImporter for the specified asset type.
static Type[] GetDescribedTypes()
Returns an array of asset types that have a description.
Interface to create default instance of an asset type.
Definition: IAssetFactory.cs:8
Assembly Assembly
Gets the assembly that has been registered.
Base implementation for ILogger.
Definition: Logger.cs:10
static void RegisterSupportedPlatforms(List< SolutionPlatform > platforms)
Registers the supported platforms.
static void RegisterFactory(Type assetType, IAssetFactory factory)
Registers a IAssetFactory for the specified asset type.
static string GetDefaultExtension(Type assetType)
Gets the default extension associated with an asset.
Guid Id
Gets an unique identifier to identify the importer. See remarks.
An event occuring when an assembly is registered with AssemblyRegistry.
Common categories that can be used with AssemblyRegistry
A custom visitor used by DataVisitorBase.
static void RegisterStringExpander(Func< object, string, string > expander)
Registers the string expander used by the package references.
SiliconStudio.Core.Reflection.AttributeRegistry AttributeRegistry
static bool IsImporterSupportingExtension(string extension)
Determines whether [is importer supporting extension] [the specified extension].
const string Assets
The assembly is containing assets data.
Imports a raw asset into the asset system.
static Type[] GetInstantiableTypes()
Returns an array of asset types that can be instanced with NewDefaultInstance.
static void RegisterAssembly(Assembly assembly)
Registers the asset assembly. This assembly should provide Asset objects, associated with ICompiler a...
Contains user-friendly names and descriptions of an asset type.
static string[] GetAssetFileExtensions()
Gets the asset file extensions.
static Type[] GetFormatVersionUpdaterTypes(Type assetType)
Gets the current format version of an asset.
static IEnumerable< IAssetImporter > FindImporterByExtension(string extension)
Finds the importer associated with an asset by the extension of the file to import.
static string ExpandString(object context, string stringToExpand)
Expands a string using the registered string expander (RegisterStringExpander)
HashSet< string > Categories
Gets the new categories registered for the specified Assembly
A default implementation for IAttributeRegistry. This implementation allows to retrieve default attri...
Associates a file extension (e.g '.pfxfont') with a particular Asset.
static Asset NewDefaultInstance(Type assetType)
Creates a default instance for an asset type.
Associates user-friendly names and descriptions to an asset type.