Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ModelContainer.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.Core.Extensions;
8 using SiliconStudio.Quantum.Contents;
9 using SiliconStudio.Quantum.References;
10 
11 namespace SiliconStudio.Quantum
12 {
13  /// <summary>
14  /// A container used to store models and resolve references between them.
15  /// </summary>
16  public class ModelContainer
17  {
18  private readonly Dictionary<Guid, IModelNode> modelsByGuid = new Dictionary<Guid, IModelNode>();
19  private readonly IGuidContainer guidContainer;
20 
21  /// <summary>
22  /// Create a new instance of <see cref="ModelContainer"/>.
23  /// </summary>
24  /// <param name="instantiateGuidContainer">Indicate whether to create a <see cref="GuidContainer"/> to store Guid per data object. This can be useful to retrieve an existing model from a data object.</param>
25  public ModelContainer(bool instantiateGuidContainer = true)
26  {
27  if (instantiateGuidContainer)
28  guidContainer = new GuidContainer();
29  NodeBuilder = CreateDefaultNodeBuilder();
30  }
31 
32  /// <summary>
33  /// Create a new instance of <see cref="ModelContainer"/>. This constructor allows to provide a <see cref="IGuidContainer"/>,
34  /// in order to share object <see cref="Guid"/> between different <see cref="ModelContainer"/>.
35  /// </summary>
36  /// <param name="guidContainer">A <see cref="IGuidContainer"/> to use to ensure the unicity of guid associated to data objects. Cannot be <c>null</c></param>
37  public ModelContainer(IGuidContainer guidContainer)
38  {
39  if (guidContainer == null) throw new ArgumentNullException("guidContainer");
40  this.guidContainer = guidContainer;
41  NodeBuilder = CreateDefaultNodeBuilder();
42  }
43 
44  /// <summary>
45  /// Gets an enumerable of the registered models.
46  /// </summary>
47  public IEnumerable<IModelNode> Models { get { return modelsByGuid.Values; } }
48 
49  /// <summary>
50  /// Gets an enumerable of the registered models.
51  /// </summary>
52  public IEnumerable<Guid> Guids { get { return modelsByGuid.Keys; } }
53 
54  /// <summary>
55  /// Gets or set the visitor to use to create models. Default value is a <see cref="DefaultModelBuilder"/> constructed with default parameters.
56  /// </summary>
57  public INodeBuilder NodeBuilder { get; set; }
58 
59  /// <summary>
60  /// Gets the model associated to a data object, if it exists. If the ModelContainer has been constructed without <see cref="IGuidContainer"/>, this method will throw an exception.
61  /// </summary>
62  /// <param name="rootObject">The data object.</param>
63  /// <returns>The <see cref="IModelNode"/> associated to the given object if available, or <c>null</c> otherwise.</returns>
64  public IModelNode GetModelNode(object rootObject)
65  {
66  if (guidContainer == null) throw new InvalidOperationException("This ModelContainer has no GuidContainer and can't retrieve Guid associated to a data object.");
67  Guid guid = guidContainer.GetGuid(rootObject);
68  return guid == Guid.Empty ? null : GetModelNode(guid);
69  }
70 
71  /// <summary>
72  /// Gets the model associated to the given Guid, if it exists.
73  /// </summary>
74  /// <param name="guid">The Guid.</param>
75  /// <returns>The <see cref="IModelNode"/> associated to the given Guid if available, or <c>null</c> otherwise.</returns>
76  public IModelNode GetModelNode(Guid guid)
77  {
78  IModelNode result;
79  return modelsByGuid.TryGetValue(guid, out result) ? result : null;
80  }
81 
82  /// <summary>
83  /// Gets the <see cref="Guid"/> associated to a data object, if it exists. If the ModelContainer has been constructed without <see cref="IGuidContainer"/>, this method will throw an exception.
84  /// </summary>
85  /// <param name="rootObject">The data object.</param>
86  /// <param name="type">Thetype of the data object.</param>
87  /// <returns>The <see cref="Guid"/> associated to the given object if available, or <see cref="Guid.Empty"/> otherwise.</returns>
88  public Guid GetGuid(object rootObject, Type type)
89  {
90  if (guidContainer == null) throw new InvalidOperationException("This ModelContainer has no GuidContainer and can't retrieve Guid associated to a data object.");
91  return guidContainer.GetGuid(rootObject);
92  }
93 
94  /// <summary>
95  /// Gets the model associated to a data object, if it exists, or create a new model for the object otherwise.
96  /// </summary>
97  /// <param name="rootObject">The data object.</param>
98  /// <param name="type">The type of the data object.</param>
99  /// <returns>The <see cref="IModelNode"/> associated to the given object.</returns>
100  public IModelNode GetOrCreateModelNode(object rootObject, Type type)
101  {
102  IModelNode result = null;
103  if (guidContainer != null && (rootObject == null || !rootObject.GetType().IsValueType))
104  {
105  result = GetModelNode(rootObject);
106  }
107 
108  return result ?? CreateModelNode(rootObject, type);
109  }
110 
111  /// <summary>
112  /// Removes a model that was previously registered.
113  /// </summary>
114  /// <param name="guid">The guid of the model to remove.</param>
115  /// <returns><c>true</c> if a model has been actually removed, <c>false</c> otherwise.</returns>
116  public bool RemoveModelNode(Guid guid)
117  {
118  if (guidContainer != null)
119  {
120  guidContainer.UnregisterGuid(guid);
121  }
122  return modelsByGuid.Remove(guid);
123  }
124 
125  /// <summary>
126  /// Removes all models that were previously registered.
127  /// </summary>
128  public void Clear()
129  {
130  if (guidContainer != null)
131  {
132  guidContainer.Clear();
133  }
134  modelsByGuid.Clear();
135  }
136 
137  /// <summary>
138  /// Refresh all references contained in the given node, creating new models for newly referenced objects.
139  /// </summary>
140  /// <param name="node">The node to update</param>
141  public void UpdateReferences(IModelNode node)
142  {
143  UpdateReferences(node, true);
144  }
145 
146  public int CollectGarbage(IEnumerable<object> objectsToKeep)
147  {
148  var guidToRemove = modelsByGuid.Keys.ToDictionary(guid => guid);
149  foreach (var obj in objectsToKeep)
150  {
151  CollectGarbageRecursively(obj, guidToRemove);
152  }
153  int guidRemoved = 0;
154  foreach (var guid in guidToRemove.Where(x => x.Value != Guid.Empty).Select(x => x.Value))
155  {
156  guidContainer.UnregisterGuid(guid);
157  modelsByGuid.Remove(guid);
158  ++guidRemoved;
159  }
160  return guidRemoved;
161  }
162 
163  private void CollectGarbageRecursively(object obj, Dictionary<Guid, Guid> guidToRemove)
164  {
165  var model = GetModelNode(obj);
166  if (model != null)
167  {
168  guidToRemove[model.Guid] = Guid.Empty;
169  foreach (var child in model.Children)
170  {
171  if (child.Content.IsReference)
172  {
173  var enumRef = child.Content.Reference as ReferenceEnumerable;
174  var objRef = child.Content.Reference as ObjectReference;
175  if (enumRef != null)
176  {
177  foreach (var itemRef in enumRef)
178  {
179  CollectGarbageRecursively(itemRef.ObjectValue, guidToRemove);
180  }
181  }
182  if (objRef != null)
183  {
184  CollectGarbageRecursively(objRef.ObjectValue, guidToRemove);
185  }
186  }
187  }
188  }
189  }
190 
191  private IModelNode CreateModelNode(object rootObject, Type type)
192  {
193  if (rootObject != null && !type.IsInstanceOfType(rootObject)) throw new ArgumentException(@"The given type does not match the given object.", "rootObject");
194 
195  Guid guid = Guid.NewGuid();
196 
197  // Retrieve results
198  if (guidContainer != null && rootObject != null && !rootObject.GetType().IsValueType)
199  guid = guidContainer.GetOrCreateGuid(rootObject);
200 
201  IModelNode result = NodeBuilder.Build(rootObject, type, guid);
202 
203  if (result != null)
204  {
205  // Register reference objects
206  modelsByGuid.Add(result.Guid, result);
207 
208  // Create or update model for referenced objects
209  UpdateReferences(result, false);
210  }
211 
212  return result;
213  }
214 
215  private void UpdateReferences(IModelNode node, bool refreshReferences)
216  {
217  // If the node was holding a reference, refresh the reference
218  if (node.Content.IsReference)
219  {
220  if (refreshReferences)
221  node.Content.Reference.Refresh(node.Content.Value);
222 
223  UpdateOrCreateReferenceTarget(node.Content.Reference, node.Content, refreshReferences);
224  }
225  else
226  {
227  // Otherwise refresh potential references in its children.
228  foreach (var child in node.Children.SelectDeep(x => x.Children).Where(x => x.Content.IsReference))
229  {
230  if (refreshReferences)
231  child.Content.Reference.Refresh(child.Content.Value);
232 
233  UpdateOrCreateReferenceTarget(child.Content.Reference, child.Content, refreshReferences);
234  }
235  }
236  }
237 
238  private void UpdateOrCreateReferenceTarget(IReference reference, IContent content, bool refreshReferences, Stack<object> indices = null)
239  {
240  if (reference == null) throw new ArgumentNullException("reference");
241  if (content == null) throw new ArgumentNullException("content");
242 
243  var referenceEnumerable = reference as ReferenceEnumerable;
244  if (referenceEnumerable != null)
245  {
246  if (indices == null)
247  indices = new Stack<object>();
248 
249  foreach (var itemReference in referenceEnumerable)
250  {
251  indices.Push(itemReference.Index);
252  UpdateOrCreateReferenceTarget(itemReference, content, refreshReferences, indices);
253  indices.Pop();
254  }
255  }
256  else
257  {
258  var singleReference = ((ObjectReference)reference);
259  if (singleReference.TargetNode != null && singleReference.TargetNode.Content.Value != reference.ObjectValue)
260  {
261  singleReference.Clear();
262  }
263 
264  if (singleReference.TargetNode == null)
265  {
266  IModelNode node = GetOrCreateModelNode(reference.ObjectValue, reference.Type);
267  if (node != null)
268  {
269  singleReference.SetTarget(node);
270  var structContent = node.Content as BoxedContent;
271  if (structContent != null)
272  {
273  structContent.BoxedStructureOwner = content;
274  structContent.BoxedStructureOwnerIndices = indices != null ? indices.Reverse().ToArray() : null;
275  }
276 
277  // If the node is a reference itself (that can happen for example for lists of lists)
278  if (singleReference.TargetNode.Content.IsReference)
279  {
280  var targetContent = singleReference.TargetNode.Content;
281  // Then we refresh this reference
282  if (refreshReferences)
283  targetContent.Reference.Refresh(targetContent.Value);
284 
285  UpdateOrCreateReferenceTarget(targetContent.Reference, targetContent, refreshReferences);
286  }
287 
288  // Otherwise refresh potential references in its children.
289  foreach (var child in node.Children.SelectDeep(x => x.Children).Where(x => x.Content.IsReference))
290  {
291  if (refreshReferences)
292  child.Content.Reference.Refresh(child.Content.Value);
293 
294  UpdateOrCreateReferenceTarget(child.Content.Reference, child.Content, refreshReferences);
295  }
296  }
297  }
298  }
299  }
300 
301  private static INodeBuilder CreateDefaultNodeBuilder()
302  {
303  var nodeBuilder = new DefaultModelBuilder();
304  return nodeBuilder;
305  }
306  }
307 }
IModelNode GetModelNode(Guid guid)
Gets the model associated to the given Guid, if it exists.
A container used to store models and resolve references between them.
Keys
Enumeration for keys.
Definition: Keys.cs:8
void UpdateReferences(IModelNode node)
Refresh all references contained in the given node, creating new models for newly referenced objects...
ModelContainer(IGuidContainer guidContainer)
Create a new instance of ModelContainer. This constructor allows to provide a IGuidContainer, in order to share object Guid between different ModelContainer.
Content of a IModelNode.
Definition: IContent.cs:13
bool RemoveModelNode(Guid guid)
Removes a model that was previously registered.
A class representing an enumeration of references to multiple objects.
Base interface for Guid containers, object that can store a unique identifier for a collection of obj...
object ObjectValue
Gets the data object targeted by this reference, if available.
Definition: IReference.cs:12
IModelNode GetModelNode(object rootObject)
Gets the model associated to a data object, if it exists. If the ModelContainer has been constructed ...
ModelContainer(bool instantiateGuidContainer=true)
Create a new instance of ModelContainer.
Type Type
Gets the type of object targeted by this reference, if available.
Definition: IReference.cs:17
This interface provides objects and methods to build a nodal view model from a given object...
Definition: INodeBuilder.cs:14
Handles Guid generation and storage for model nodes.
void Clear()
Removes all models that were previously registered.
A class representing a reference to another object that has a different model.
The IModelNode interface represents a node in a model object. A model object is represented by a grap...
Definition: IModelNode.cs:16
Guid GetGuid(object rootObject, Type type)
Gets the Guid associated to a data object, if it exists. If the ModelContainer has been constructed w...
int CollectGarbage(IEnumerable< object > objectsToKeep)
IModelNode GetOrCreateModelNode(object rootObject, Type type)
Gets the model associated to a data object, if it exists, or create a new model for the object otherw...