private static bool _structuralEquals(this BplObject object1, BplObject object2, HashSet<BplObject> visited) { // assumption: object1 != object2 if (object1 == null || object2 == null) return false; if (object1.Class != object2.Class) return false; var flag = true; visited.Add(object1); foreach (var prop in object1.Class.Properties.Where(p => !p.IsCalculated)) { if (prop.IsPrimitive) { flag = Object.Equals(prop.GetValue(object1), prop.GetValue(object2)); } else { var refs1 = prop.GetReferences(object1).ToArray(); var refs2 = prop.GetReferences(object2).ToArray(); flag = (refs1.Length == refs2.Length); for (var j = 0; flag && j < refs1.Length; j++) { var r1 = refs1[j]; var r2 = refs2[j]; if (r1 != r2 && !visited.Contains(r1)) { flag = _structuralEquals(r1, r2, visited); } } } if (!flag) break; } visited.Remove(object1); return flag; }
/// <summary>Formats the input BPL object into the output XML document.</summary> public override bool Format(BplObject input) { if (!PrepareFormatter(input)) return false; try { OutputXml = new XDocument { Declaration = new XDeclaration("1.0", "UTF-8", "yes") }; _serializeObject(OutputXml, Input, null); NSMapping.Write(OutputXml.Root); } catch (Exception e) { AddException(e); } if (Success) { try { using (var strWriter = new Utf8StringWriter(CultureInfo.InvariantCulture)) { using (var xmlWriter = XmlWriter.Create(strWriter, _getWriterSettings())) { OutputXml.Save(xmlWriter); } Output = strWriter.ToString(); } } catch (Exception e) { AddException(e); } } if (!Success) { Output = null; OutputXml = null; } return Success; }
/// <summary>Adds a target object and indexes it under the specified id.</summary> /// <returns><c>true</c> if the target was successfully added; <c>false</c> if an object with the specified id already exists.</returns> public bool AddTarget(BplIdentity targetId, BplObject target) { if (_targets.ContainsKey(targetId)) { return false; } else { _targets[targetId] = target; return true; } }
private bool _inScope(BplObject target) { if (_closure.Contains(target)) return true; if (target is BplContextNode) { var parent = ((BplContextNode)target).Parent; if (parent != null && _inScope(parent)) { _closure.Add(target); return true; } } return false; }
/// <summary>Formats the input BPL object into the output XML document.</summary> public override bool Format(BplObject input) { if (!PrepareFormatter(input)) return false; try { var writer = new BplBinaryWriter(); writer.Serialize(input); Output = _toHexString(writer.ToByteArray()); } catch (Exception e) { AddException(e); } return Success; }
/// <summary>Creates a new <see cref="BplObjectProxy"/> instance that wraps the given BPL object.</summary> /// <param name="source">The source BPL object on the debuggee-side.</param> protected BplObjectProxy(BplObject source) : this() { BplClass = source.Class.Name; BplNamespace = source.Class.Namespace.Name; IsEmpty = !source.Children.Any(); if (IsEmpty) { IsLoaded = true; } else { Nodes.Add(new TreeNode()); } var nameProperty = source.Class.GetProperty("Name"); if (nameProperty != null) { NameProperty = nameProperty.GetValue(source) as string; } Text = BplClass; }
/// <summary>Formats the input BPL object into the output XML document.</summary> public override bool Format(BplObject input) { if (!PrepareFormatter(input)) return false; _output = new TextBuffer(this); try { _serializeObject(null, Input); _injectMapping(NSMapping); Output = _output.ToString(); } catch (Exception e) { AddException(e); } _output = null; return Success; }
private bool _isEqual(BplObject o1, BplObject o2) { var w1 = new BplBinaryWriter(); w1.Serialize(o1); var b1 = w1.ToByteArray(); var w2 = new BplBinaryWriter(); w2.Serialize(o2); var b2 = w2.ToByteArray(); if (b1.Length != b2.Length) { return false; } for (var i = 0; i < b1.Length; i++) { if (b1[i] != b2[i]) { return false; } } return true; }
/// <summary>Creates a new <see cref="BplTrigger"/> instance.</summary> protected BplTrigger(BplObject source, string path, BplTriggerHandler handler) { PathString = path; var pathStrings = PathString.Split(@"\s*\;\s*"); var pathCollection = new List<BplPropertyPath>(); for (var i = 0; i < pathStrings.Length; i++) { pathCollection.Add(new BplPropertyPath(pathStrings[i].Trim())); } PathCollection = new ReadOnlyCollection<BplPropertyPath>(pathCollection); if (PathCollection.Count == 0) { BplRuntimeException.Throw("Path string is empty"); } Source = source; TargetMethod = handler.Method; var targetObject = handler.Target; if (targetObject != null) { _targetObject = new WeakReference(targetObject); _isInstanceHandler = true; } }
/// <summary>Validates the target object and if there are no errors converts it to a string reference. The target object must /// have a valid and non-empty identity property, and must be contained at some level under the root object.</summary> public string ToReference(BplObject target, BplProperty property) { var targetEntity = target as BplEntity; if (target == null) { BplRuntimeException.Throw("Association target is invalid or missing."); } var targetId = targetEntity.Id; if (targetId.IsEmpty) { BplRuntimeException.Throw("Association target {0} has no Id.", target); } bool? flag = null; if (CustomVerifier != null) { flag = CustomVerifier(target); } if (!flag.HasValue) { flag = (target.IsA<BplTaxonomy>() || _inScope(target)); } if (!flag.Value) { BplRuntimeException.Throw("Association target {0} is out of serialization scope. It must be either contained in {1} or be a taxonomy.".Substitute(target, Root)); } return targetId.ToString(property.ReferencedClass.IdentityScope); }
private void _deserializeAssociation(BplObject bplObject, BplProperty property, XAttribute target) { _resolver.AddSource(target.Value, bplObject, property); }
private void _deserializeContainer(BplObject bplObject, BplProperty property, XElement[] children) { var collection = property.GetValue(bplObject) as IBplCollection; for (var i = 0; i < children.Length; i++) { var childObject = _deserializeObject(children[i]); if (childObject != null) { collection.Add(childObject); } } }
private void _deserializeContainer(BplObject bplObject, BplProperty property, XElement child) { var childObject = _deserializeObject(child); if (childObject != null) { property.SetValue(bplObject, childObject); } }
private void _deserializeArray(BplObject bplObject, BplProperty property, XElement[] children) { var arrayValues = new object[children.Length]; var arrayConverter = BplLanguage.XmlConverters[property]; var itemConverter = BplLanguage.XmlConverters[property.PrimitiveItemType]; var itemTag = XNamespaces.none + property.PrimitiveItemType.TagName.LocalName; for (var i = 0; i < children.Length; i++) { var child = children[i]; try { if (child.Name != itemTag) { throw new Exception("Expected tag name '{0}'".Substitute(itemTag)); } arrayValues[i] = _parseScalar(property, itemConverter, child); } catch (Exception e) { AddError("Invalid value in BPL property '{0}[{1}]', {:where}. {2}", property.Name, i, e.Message, child); } } if (arrayConverter is BplArrayConverter) { var array = ((BplArrayConverter)arrayConverter).CreateInstance(null, arrayValues); property.SetValue(bplObject, array); } else { AddError("Could not instantiate BPL array property '{0}'", property.Name); } }
private void _deserializeArray(BplObject bplObject, BplProperty property, XAttribute attr) { try { var value = BplLanguage.XmlConverters[property].Parse(attr.Value); property.SetValue(bplObject, value); } catch (Exception e) { AddError("Invalid value in BPL property '{:name}', {:where}. {0}", e.Message, attr); } }
// serializes an association (singleton) private void _serializeAssociation(XElement element, BplProperty property, BplObject bplObject) { if (bplObject != null) { try { element.Add(new XAttribute(property.TagName, Verifier.ToReference(bplObject, property))); } catch (BplRuntimeException e) { AddException(e); } } }
/// <summary>Throws a <see cref="BplRuntimeException"/> with the given parameters.</summary> public static void Throw(string message, BplObject sourceObject) { var formattedMessage = Assumption.TidyMessage(message.Substitute(sourceObject)); throw new BplRuntimeException(formattedMessage, null, sourceObject, null); }
/// <summary>Tests the visualizer (see remarks at the head of this class).</summary> public static void TestVisualizer(BplObject source) { var dbg = new VisualizerDevelopmentHost(source, typeof(BplDebuggerVisualizer), typeof(BplDebuggeeObjectSource)); dbg.ShowVisualizer(); }
/// <summary>Creates a new <see cref="BplItemReplaced"/> instance.</summary> internal BplItemReplaced(BplObject source, BplProperty property, int index, BplContextNode oldItem, BplContextNode newItem) : base(source, property) { Index = index; OldItem = oldItem; NewItem = newItem; }
/// <summary>Dettaches this node from the specified source object.</summary> internal void DetachFrom(BplObject source, BplProperty property) { if (source == null || property == null) { BplRuntimeException.Throw("Source object or property are missing"); } if (source.IsSealed) { BplRuntimeException.Throw("Node cannot be detached from a sealed object"); } var isEntity = (this is BplEntity); var isTaxonomy = (this is BplTaxonomy); if ((source is BplTaxonomy) && !isTaxonomy) { BplRuntimeException.Throw("'{0}' cannot be referenced from a taxonomy", this); } if (property.IsContainer) { // property is a container, so we need to reparent the node if (Parent != source || ParentContainer != property) { // this happens when the node has been attached to another container return; } else { // this happens when the node has been truly detached Parent = null; ParentContainer = null; } } else { // property is an assocation, so we need to update the backward associations if (!isEntity) { // association target must be an entity BplRuntimeException.Throw("'{0}' cannot be the target of association since it is not an entity", this); } //if (!isTaxonomy) { // no backward association from taxonomies Sources.RemoveTuple(source, property); //} } // detach the node and all its dependents from this context if (Context != null) { _detachFromContext(this); } }
/// <summary>Adds a source object/property reference to the specified target id.</summary> /// <returns><c>true</c> if the source was successfully added; <c>false</c> otherwise.</returns> public bool AddSource(string targetId, BplObject source, BplProperty property) { var fullTargetId = BplIdentity.Get(targetId, property.ReferencedClass.IdentityScope); _pending.Add(new BplPendingReference(source, property, fullTargetId)); return true; }
/// <summary>Creates a new <see cref="BplItemRemoved"/> instance.</summary> internal BplItemRemoved(BplObject source, BplProperty property, int oldIndex, BplContextNode oldItem) : base(source, property) { OldIndex = oldIndex; OldItem = oldItem; }
// recursively serializes a BPL object private void _serializeObject(XContainer parent, BplObject bplObject, BplClass expectedClass) { if (bplObject == null) return; var bplClass = bplObject.Class; var schema = bplClass.Schema; var prefix = BplLanguage.Schemas[schema]; NSMapping.AddIfNeeded(prefix, schema); XElement element = null; if (expectedClass == null || schema == expectedClass.Schema) { element = new XElement(bplClass.TagName); } else { NSMapping.AddIfNeeded("xsi", xsi); element = new XElement(expectedClass.TagName); element.Add(new XAttribute(xsi + "type", prefix + ":" + bplClass.TagName.LocalName)); } var identityProperty = bplClass.IdentityProperty; if (identityProperty != null) { var id = (BplIdentity)identityProperty.GetValue(bplObject); if (!id.IsEmpty) { element.Add(new XAttribute(identityProperty.TagName, id.ToString(bplClass.IdentityScope))); } else if (bplClass.IsA<BplEntity>()) { AddError("{0} is missing Id.", bplObject); } } // invariant: write the properties according to their "ProtocolIndex" order foreach (var property in bplClass.PropertiesSorted) { if (property.IsCalculated || property.IsVirtual || property == identityProperty) continue; var value = property.GetValue(bplObject); if (value == null) continue; if (!ShouldSerialize(property, bplObject)) continue; if (property.IsPrimitive) { if (property.IsScalar) { _serializeScalar(element, property, value); } else if (property.IsArray) { _serializeArray(element, property, (IEnumerable)value); } } else if (property.IsContainer) { if (property.IsReference) { _serializeContainer(element, property, (BplObject)value); } else { _serializeContainer(element, property, (IBplCollection)value); } } else if (property.IsAssociation) { if (property.IsReference) { _serializeAssociation(element, property, (BplObject)value); } else { _serializeAssociation(element, property, (IBplCollection)value); } } } parent.Add(element); }
private void _deserializeAssociation(BplObject bplObject, BplProperty property, XElement[] targets) { var idTagName = XNamespaces.none + BplPrimitive.Get<BplIdentity>().TagName.LocalName; for (var i = 0; i < targets.Length; i++) { var target = targets[i]; if (target.Name != idTagName) { AddError("Invalid value in BPL property '{0}[{1}]', {:where}. Expected tag name '{2}'.", property.Name, i, idTagName, target); } _resolver.AddSource(target.Value, bplObject, property); } }
/// <summary>Creates a new <see cref="BplRuntimeException"/> instance.</summary> private BplRuntimeException(String message, Exception inner, BplObject sourceObject, BplProperty sourceProperty) : base(message, inner) { SourceObject = sourceObject; SourceProperty = sourceProperty; }
private void _deserializeScalar(BplObject bplObject, BplProperty property, XObject node) { try { var value = _parseScalar(property, BplLanguage.XmlConverters[property], node); property.SetValue(bplObject, value); } catch (Exception e) { AddError("Invalid value in BPL property '{:name}', {:where}. {0}", e.Message, node); } }
/// <summary>Throws a <see cref="BplRuntimeException"/> with the given parameters.</summary> public static void Throw(string message, Exception innerException, BplObject sourceObject, BplProperty sourceProperty) { var formattedMessage = Assumption.TidyMessage(message.Substitute(sourceObject, sourceProperty.Name)); throw new BplRuntimeException(formattedMessage, innerException, sourceObject, sourceProperty); }
// serializes a container (singleton) private void _serializeContainer(XElement element, BplProperty property, BplObject bplObject) { if (bplObject == null) return; var singleton = new XElement(property.TagName); var targetClass = property.ReferencedClass; _serializeObject(singleton, bplObject, targetClass); element.Add(singleton); }
/// <summary>Attaches this node to the specified source object.</summary> internal void AttachTo(BplObject source, BplProperty property) { if (source == null || property == null) { BplRuntimeException.Throw("Source object or property are missing"); } if (source.IsSealed) { BplRuntimeException.Throw("Node cannot be attached to a sealed object"); } var isEntity = (this is BplEntity); var isTaxonomy = (this is BplTaxonomy); var sourceIsTaxonomy = (source is BplTaxonomy); if (sourceIsTaxonomy && !isTaxonomy) { BplRuntimeException.Throw("'{0}' cannot be referenced from a taxonomy", this); } if (property.IsContainer) { // property is a container, so we need to reparent the node var oldParent = Parent; var oldContainer = ParentContainer; if (oldParent == source && oldContainer == property) { BplRuntimeException.Throw("Node is already contained in '{1}' in {0}.", source, property); } // attach to new parent Parent = source; ParentContainer = property; // detach from old parent (this is done after attaching the new parent, so the node does not get disconnected from the context) if (oldContainer != null) { if (oldContainer.IsReference) { oldContainer.SetValue(oldParent, null); } else { oldContainer.GetCollection(oldParent).Remove(this); } } } else { // property is an assocation, so we need to update the backward associations if (!isEntity) { // association target must be an entity BplRuntimeException.Throw("'{0}' cannot be the target of association since it is not an entity", this); } if (!(sourceIsTaxonomy ^ isTaxonomy)) { // no backward association between taxonomies and non-taxonomies Sources.AddTuple(source, property); } } // attach the source and target to same context var sourceContext = BplContext.GetOwner(source); var targetContext = Context; if (sourceContext != targetContext) { if (sourceContext == null) { _attachToContext((BplContextNode)source, targetContext); } else if (targetContext == null) { _attachToContext(this, sourceContext); } else { _verifyContextCompatibility(sourceContext, targetContext); } } }
// Internal constructor (to prevent direct inheritance by external plugins) internal BplCollectionChange(BplObject source, BplProperty property) : base(source, property) { }