4 using System.Collections;
5 using System.Collections.Generic;
8 using SiliconStudio.Core.Extensions;
9 using SiliconStudio.Core.Reflection;
10 using SiliconStudio.Quantum.References;
12 namespace SiliconStudio.Quantum
16 private class ReferenceInfo
18 public readonly Type ReferenceType;
19 public readonly
int EnumerableCount;
20 public ReferenceInfo(Type referenceType,
int enumerableCount)
22 ReferenceType = referenceType;
23 EnumerableCount = enumerableCount;
27 private readonly DefaultModelBuilder nodeBuilder;
28 private readonly Stack<ModelNode> contextStack =
new Stack<ModelNode>();
29 private readonly Queue<ObjectReference> references =
new Queue<ObjectReference>();
30 private readonly List<ModelNode> checkedNodes =
new List<ModelNode>();
35 if (nodeBuilder == null)
throw new ArgumentNullException(
"nodeBuilder");
36 this.nodeBuilder = nodeBuilder as DefaultModelBuilder;
37 if (this.nodeBuilder == null)
throw new ArgumentException(
@"This argument should be a DefaultModelBuilder",
"nodeBuilder");
51 public void Check(
ModelNode node,
object obj, Type type,
bool checkReferences)
56 throw new QuantumConsistencyException(
"The node content value [{0}]", obj.ToStringSafe(),
"The node content value [{0}]", node.Content.Value.ToStringSafe(), node);
62 while (rootNode != null)
64 if (rootNode.Parent != null)
67 if (rootNode.Content.Value != null)
69 var typeDescriptor = TypeDescriptorFactory.Find(rootNode.Content.Type);
70 PushContextNode(rootNode);
71 VisitObject(rootNode.Content.Value, typeDescriptor as
ObjectDescriptor,
true);
74 checkedNodes.Add(rootNode);
79 while (references.Count > 0)
81 var reference = references.Dequeue();
82 if (!checkedNodes.Contains(reference.TargetNode))
84 rootNode = (
ModelNode)reference.TargetNode;
95 var node = GetContextNode();
97 var referenceInfo = GetReferenceInfo(descriptor.
Type, obj);
98 if (node.Content.Reference == null)
100 if (referenceInfo != null && (node != rootNode || referenceInfo.ReferenceType == typeof(
ReferenceEnumerable)))
103 var memberCount = descriptor.Members.Count();
104 if (node.Children.Count != memberCount)
105 throw new QuantumConsistencyException(
"A node with [{0}] children", memberCount.ToStringSafe(),
"A node with [{0}] children", node.Children.Count.ToStringSafe(), node);
107 PushContextNode(node);
108 base.VisitObject(obj, descriptor,
true);
113 if (node.Content.Reference.GetType() != referenceInfo.ReferenceType)
114 throw new QuantumConsistencyException(
"Content with a [{0}]", referenceInfo.ReferenceType.Name,
"Content with a [{0}]", node.Content.Reference.GetType().Name, node);
115 if (node.Content.Value != obj)
116 throw new QuantumConsistencyException(
"The node content value [{0}]", obj.ToStringSafe(),
"The node content value [{0}]", node.Content.Value.ToStringSafe(), node);
117 if (node.Children.Count > 0)
118 throw new QuantumConsistencyException(
"A node with a reference and no child", null,
"A node with a reference and [{0}] children", node.Children.Count.ToStringSafe(), node);
120 AddReference(node, node.Content.Reference);
126 if (!nodeBuilder.NotifyNodeConstructing(containerDescriptor, member))
129 var node = GetContextNode();
135 catch (InvalidOperationException)
140 if (!IsPrimitiveType(child.
Content.Type))
142 PushContextNode(child);
151 var containerNode = GetContextNode();
153 var
count = descriptor.GetCollectionCount(collection);
154 var referenceInfo = GetReferenceInfo(descriptor.
Type, collection);
156 if (referenceInfo != null && referenceInfo.EnumerableCount != count)
157 throw new QuantumConsistencyException(
"A node with an EnumerableReference containing [{0}] items", referenceInfo.EnumerableCount.ToStringSafe(),
"A node with an EnumerableReference containing [{0}] items", count.ToStringSafe(), containerNode);
161 base.VisitCollection(collection, descriptor);
168 var containerNode = GetContextNode();
170 if (!IsPrimitiveType(descriptor.
KeyType))
175 var referenceInfo = GetReferenceInfo(descriptor.
Type, dictionary);
176 if (referenceInfo != null && referenceInfo.EnumerableCount != count)
177 throw new QuantumConsistencyException(
"A node with an EnumerableReference containing [{0}] items", referenceInfo.EnumerableCount.ToStringSafe(),
"A node with an EnumerableReference containing [{0}] items", count.ToStringSafe(), containerNode);
179 if (IsPrimitiveType(descriptor.
ValueType,
false) || IsEnumerable(descriptor.
ValueType))
181 base.VisitDictionary(dictionary, descriptor);
185 private ReferenceInfo GetReferenceInfo(Type type,
object value)
188 if (!type.IsClass || IsPrimitiveType(type))
191 ITypeDescriptor descriptor = value != null ? TypeDescriptorFactory.Find(value.GetType()) : null;
192 var valueType = GetElementValueType(descriptor);
195 if (valueType == null || !IsPrimitiveType(valueType,
false))
197 var refType = Reference.GetReferenceType(value, Reference.NotInCollection);
200 if (value == null)
throw new InvalidOperationException(
"The value is not expected to be null when its node should contains an ReferenceEnumerable");
202 return new ReferenceInfo(refType, enumerable.Cast<
object>().Count());
204 return new ReferenceInfo(refType, -1);
212 if (enumerableReference != null)
214 foreach (var itemReference
in enumerableReference)
216 AddObjectReference(referencer, itemReference);
225 private void AddObjectReference(IModelNode referencer,
ObjectReference reference)
228 throw new QuantumConsistencyException(
"A resolved reference",
"An unresolved reference", referencer);
230 if (referencer.Content.Reference == reference && referencer.Content.Value != reference.
TargetNode.
Content.Value)
231 throw new QuantumConsistencyException(
"Referenced node with same content value that its referencer",
"Referenced node with different content value that its referencer", referencer);
234 throw new QuantumConsistencyException(
"Referenced node with same Guid that the reference",
"Referenced node with different Guid that the reference", referencer);
236 references.Enqueue(reference);
239 private void PushContextNode(ModelNode node)
241 contextStack.Push(node);
244 private void PopContextNode()
249 private ModelNode GetContextNode()
251 return contextStack.Peek();
254 private static bool IsEnumerable(Type type)
259 private bool IsPrimitiveType(Type type,
bool includeAdditionalPrimitiveTypes =
true)
264 return type.IsPrimitive || type == typeof(
string) || type.IsEnum || (includeAdditionalPrimitiveTypes && PrimitiveTypes.Any(x => x.IsAssignableFrom(type)));
271 if (dictionaryDescriptor != null)
273 return dictionaryDescriptor.ValueType;
275 return collectionDescriptor != null ? collectionDescriptor.ElementType : null;
Provides a descriptor for a System.Collections.ICollection.
Type ElementType
Gets or sets the type of the element.
void Check(ModelNode node, object obj, Type type, bool checkReferences)
override void VisitObject(object obj, ObjectDescriptor descriptor, bool visitMembers)
Visits an object (either a class or a struct) The object.The descriptor.
string Name
Gets the name.
This class is the default implementation of the IModelNode.
Default implementation of a ITypeDescriptor.
ModelConsistencyCheckVisitor(INodeBuilder nodeBuilder)
A visitor for serializable data (binary, yaml and editor).
Describe a member of an object.
IContent Content
Gets the content of the IModelNode.
A class representing an enumeration of references to multiple objects.
override void VisitDictionary(object dictionary, DictionaryDescriptor descriptor)
Visits a dictionary. The dictionary.The descriptor.
Provides a descriptor for a System.Collections.IDictionary.
IReadOnlyCollection< IModelNode > Children
This interface provides objects and methods to build a nodal view model from a given object...
override void Reset()
Resets this instance (clears the cache of visited objects).
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.
An exception that occurs during consistency checks of Quantum objects, indicating that a IModelNode i...
Type ValueType
Gets the type of the value.
IModelNode TargetNode
Gets the model node targeted by this reference, if available.
Provides access members of a type.
override void VisitCollection(IEnumerable collection, CollectionDescriptor descriptor)
Visits a collection. The collection.The descriptor.
A class representing a reference to another object that has a different model.
override void VisitObjectMember(object container, ObjectDescriptor containerDescriptor, IMemberDescriptor member, object value)
Visits an object member. The container.The container descriptor.The member.The value.
Guid Guid
Gets or sets the System.Guid.
IReferencable.AddReference() event.
Type KeyType
Gets the type of the key.
Guid TargetGuid
Gets the Guid of the model node targeted by this reference, if available.