internal static object GetMemberValue(this XamlObject xobj, XamlMember xm) { if (xm.IsUnknown) { return(null); } if (xm.IsAttachable) { return(xobj.GetRawValue()); // attachable property value } // FIXME: this looks like an ugly hack. Is this really true? What if there's MarkupExtension that uses another MarkupExtension type as a member type. var obj = xobj.Context.GetRawValue(); if (xm == XamlLanguage.Initialization) { return(obj); } if (xm == XamlLanguage.Items) // collection itself. { return(obj); } if (xm == XamlLanguage.Arguments) // object itself { return(obj); } if (xm == XamlLanguage.PositionalParameters) { return(xobj.GetRawValue()); // dummy value } return(xm.Invoker.GetValue(xobj.GetRawValue())); }
IEnumerable <XamlNodeMember> GetNodeMembers(XamlObject xobj, IValueSerializerContext vsctx) { // XData.XmlReader is not returned. if (xobj.Type == XamlLanguage.XData) { yield return(new XamlNodeMember(xobj, XamlLanguage.XData.GetMember("Text"))); yield break; } // FIXME: find out why root Reference has PositionalParameters. if (xobj.GetRawValue() != root && xobj.Type == XamlLanguage.Reference) { yield return(new XamlNodeMember(xobj, XamlLanguage.PositionalParameters)); } else { var inst = xobj.GetRawValue(); var atts = new KeyValuePair <AttachableMemberIdentifier, object> [AttachablePropertyServices.GetAttachedPropertyCount(inst)]; AttachablePropertyServices.CopyPropertiesTo(inst, atts, 0); foreach (var p in atts) { var axt = ctx.GetXamlType(p.Key.DeclaringType); yield return(new XamlNodeMember(new XamlObject(axt, p.Value), axt.GetAttachableMember(p.Key.MemberName))); } foreach (var xm in xobj.Type.GetAllObjectReaderMembersByType(vsctx)) { yield return(new XamlNodeMember(xobj, xm)); } } }
string GetReferenceName(XamlObject xobj) { var xm = xobj.Type.GetAliasedProperty(XamlLanguage.Name); if (xm != null) { return((string)xm.Invoker.GetValue(xobj.GetRawValue())); } return("__ReferenceID" + used_reference_ids++); }
IEnumerable <XamlNodeInfo> GetItemsNodes(XamlMember xm, XamlObject xobj) { var obj = xobj.GetRawValue(); if (obj == null) { yield break; } var ie = xobj.Type.Invoker.GetItems(obj); while (ie.MoveNext()) { var iobj = ie.Current; // If it is dictionary, then retrieve the key, and rewrite the item as the Value part. object ikey = null; if (xobj.Type.IsDictionary) { Type kvpType = iobj.GetType(); bool isNonGeneric = kvpType == typeof(DictionaryEntry); var kp = isNonGeneric ? null : kvpType.GetProperty("Key"); var vp = isNonGeneric ? null : kvpType.GetProperty("Value"); ikey = isNonGeneric ? ((DictionaryEntry)iobj).Key : kp.GetValue(iobj, null); iobj = isNonGeneric ? ((DictionaryEntry)iobj).Value : vp.GetValue(iobj, null); } var wobj = TypeExtensionMethods.GetExtensionWrapped(iobj); var xiobj = new XamlObject(GetType(wobj), wobj); if (ikey != null) { // Key member is written *inside* the item object. // // It is messy, but Key and Value are *sorted*. In most cases Key goes first, but for example PositionalParameters comes first. // To achieve this behavior, we compare XamlLanguage.Key and value's Member and returns in order. It's all nasty hack, but at least it could be achieved like this! var en = GetNodes(null, xiobj).ToArray(); yield return(en [0]); // StartObject var xknm = new XamlNodeMember(xobj, XamlLanguage.Key); var nodes1 = en.Skip(1).Take(en.Length - 2); var nodes2 = GetKeyNodes(ikey, xobj.Type.KeyType, xknm); foreach (var xn in EnumerateMixingMember(nodes1, XamlLanguage.Key, nodes2)) { yield return(xn); } yield return(en [en.Length - 1]); } else { foreach (var xn in GetNodes(null, xiobj)) { yield return(xn); } } } }
IEnumerable<XamlNodeInfo> GetNodes (XamlMember xm, XamlObject xobj, XamlType overrideMemberType, bool partOfPositionalParameters) { // collection items: each item is exposed as a standalone object that has StartObject, EndObject and contents. if (xm == XamlLanguage.Items) { foreach (var xn in GetItemsNodes (xm, xobj)) yield return xn; yield break; } // Arguments: each argument is written as a standalone object if (xm == XamlLanguage.Arguments) { foreach (var argm in xobj.Type.GetSortedConstructorArguments ()) { var argv = argm.Invoker.GetValue (xobj.GetRawValue ()); var xarg = new XamlObject (argm.Type, argv); foreach (var cn in GetNodes (null, xarg)) yield return cn; } yield break; } // PositionalParameters: items are from constructor arguments, written as Value node sequentially. Note that not all of them are in simple string value. Also, null values are not written as NullExtension if (xm == XamlLanguage.PositionalParameters) { foreach (var argm in xobj.Type.GetSortedConstructorArguments ()) { foreach (var cn in GetNodes (argm, new XamlObject (argm.Type, xobj.GetMemberValue (argm)), null, true)) yield return cn; } yield break; } if (xm == XamlLanguage.Initialization) { yield return new XamlNodeInfo (TypeExtensionMethods.GetStringValue (xobj.Type, xm, xobj.GetRawValue (), value_serializer_ctx)); yield break; } // Value - only for non-top-level node (thus xm != null) if (xm != null) { // overrideMemberType is (so far) used for XamlLanguage.Key. var xtt = overrideMemberType ?? xm.Type; if (!xtt.IsMarkupExtension && // this condition is to not serialize MarkupExtension whose type has TypeConverterAttribute (e.g. StaticExtension) as a string. (xtt.IsContentValue (value_serializer_ctx) || xm.IsContentValue (value_serializer_ctx))) { // though null value is special: it is written as a standalone object. var val = xobj.GetRawValue (); if (val == null) { if (!partOfPositionalParameters) foreach (var xn in GetNodes (null, null_object)) yield return xn; else yield return new XamlNodeInfo (String.Empty); } else yield return new XamlNodeInfo (TypeExtensionMethods.GetStringValue (xtt, xm, val, value_serializer_ctx)); yield break; } } // collection items: return GetObject and Items. if (xm != null && xm.Type.IsCollection && !xm.IsWritePublic) { yield return new XamlNodeInfo (XamlNodeType.GetObject, xobj); // Write Items member only when there are items (i.e. do not write it if it is empty). var xnm = new XamlNodeMember (xobj, XamlLanguage.Items); var en = GetNodes (XamlLanguage.Items, xnm.Value).GetEnumerator (); if (en.MoveNext ()) { yield return new XamlNodeInfo (XamlNodeType.StartMember, xnm); do { yield return en.Current; } while (en.MoveNext ()); yield return new XamlNodeInfo (XamlNodeType.EndMember, xnm); } yield return new XamlNodeInfo (XamlNodeType.EndObject, xobj); } else if (xm != null && xm.Type.IsXData) { var sw = new StringWriter (); var xw = XmlWriter.Create (sw, new XmlWriterSettings () { OmitXmlDeclaration = true, ConformanceLevel = ConformanceLevel.Auto }); var val = xobj.GetRawValue () as IXmlSerializable; if (val == null) yield break; // do not output anything val.WriteXml (xw); xw.Close (); var obj = new XData () { Text = sw.ToString () }; foreach (var xn in GetNodes (null, new XamlObject (XamlLanguage.XData, obj))) yield return xn; } else { // Object - could become Reference var val = xobj.GetRawValue (); if (!xobj.Type.IsContentValue (value_serializer_ctx) && val != null) { string refName = NameResolver.GetName (val); if (refName != null) { // The target object is already retrieved, so we don't return the same object again. NameResolver.SaveAsReferenced (val); // Record it as named object. // Then return Reference object instead. foreach (var xn in GetNodes (null, new XamlObject (XamlLanguage.Reference, new Reference (refName)))) yield return xn; yield break; } else { // The object appeared in the xaml tree for the first time. So we store the reference with a unique name so that it could be referenced later. refName = GetReferenceName (xobj); if (NameResolver.IsCollectingReferences && NameResolver.Contains (refName)) throw new InvalidOperationException (String.Format ("There is already an object of type {0} named as '{1}'. Object names must be unique.", val.GetType (), refName)); NameResolver.SetNamedObject (refName, val, true); // probably fullyInitialized is always true here. } } yield return new XamlNodeInfo (XamlNodeType.StartObject, xobj); // If this object is referenced and there is no [RuntimeNameProperty] member, then return Name property in addition. if (val != null && xobj.Type.GetAliasedProperty (XamlLanguage.Name) == null) { string name = NameResolver.GetReferencedName (val); if (name != null) { var sobj = new XamlObject (XamlLanguage.String, name); foreach (var cn in GetMemberNodes (new XamlNodeMember (sobj, XamlLanguage.Name), new XamlNodeInfo [] { new XamlNodeInfo (name)})) yield return cn; } } foreach (var xn in GetObjectMemberNodes (xobj)) yield return xn; yield return new XamlNodeInfo (XamlNodeType.EndObject, xobj); } }
IEnumerable<XamlNodeInfo> GetItemsNodes (XamlMember xm, XamlObject xobj) { var obj = xobj.GetRawValue (); if (obj == null) yield break; var ie = xobj.Type.Invoker.GetItems (obj); while (ie.MoveNext ()) { var iobj = ie.Current; // If it is dictionary, then retrieve the key, and rewrite the item as the Value part. object ikey = null; if (xobj.Type.IsDictionary) { Type kvpType = iobj.GetType (); bool isNonGeneric = kvpType == typeof (DictionaryEntry); var kp = isNonGeneric ? null : kvpType.GetProperty ("Key"); var vp = isNonGeneric ? null : kvpType.GetProperty ("Value"); ikey = isNonGeneric ? ((DictionaryEntry) iobj).Key : kp.GetValue (iobj, null); iobj = isNonGeneric ? ((DictionaryEntry) iobj).Value : vp.GetValue (iobj, null); } var wobj = TypeExtensionMethods.GetExtensionWrapped (iobj); var xiobj = new XamlObject (GetType (wobj), wobj); if (ikey != null) { // Key member is written *inside* the item object. // // It is messy, but Key and Value are *sorted*. In most cases Key goes first, but for example PositionalParameters comes first. // To achieve this behavior, we compare XamlLanguage.Key and value's Member and returns in order. It's all nasty hack, but at least it could be achieved like this! var en = GetNodes (null, xiobj).ToArray (); yield return en [0]; // StartObject var xknm = new XamlNodeMember (xobj, XamlLanguage.Key); var nodes1 = en.Skip (1).Take (en.Length - 2); var nodes2 = GetKeyNodes (ikey, xobj.Type.KeyType, xknm); foreach (var xn in EnumerateMixingMember (nodes1, XamlLanguage.Key, nodes2)) yield return xn; yield return en [en.Length - 1]; } else foreach (var xn in GetNodes (null, xiobj)) yield return xn; } }
IEnumerable<XamlNodeMember> GetNodeMembers (XamlObject xobj, IValueSerializerContext vsctx) { // XData.XmlReader is not returned. if (xobj.Type == XamlLanguage.XData) { yield return new XamlNodeMember (xobj, XamlLanguage.XData.GetMember ("Text")); yield break; } // FIXME: find out why root Reference has PositionalParameters. if (xobj.GetRawValue () != root && xobj.Type == XamlLanguage.Reference) yield return new XamlNodeMember (xobj, XamlLanguage.PositionalParameters); else { var inst = xobj.GetRawValue (); var atts = new KeyValuePair<AttachableMemberIdentifier,object> [AttachablePropertyServices.GetAttachedPropertyCount (inst)]; AttachablePropertyServices.CopyPropertiesTo (inst, atts, 0); foreach (var p in atts) { var axt = ctx.GetXamlType (p.Key.DeclaringType); yield return new XamlNodeMember (new XamlObject (axt, p.Value), axt.GetAttachableMember (p.Key.MemberName)); } foreach (var xm in xobj.Type.GetAllObjectReaderMembersByType (vsctx)) yield return new XamlNodeMember (xobj, xm); } }
string GetReferenceName (XamlObject xobj) { var xm = xobj.Type.GetAliasedProperty (XamlLanguage.Name); if (xm != null) return (string) xm.Invoker.GetValue (xobj.GetRawValue ()); return "__ReferenceID" + used_reference_ids++; }
IEnumerable<XamlNodeInfo> GetNodes (XamlMember xm, XamlObject xobj, XamlType overrideMemberType) { // collection items: each item is exposed as a standalone object that has StartObject, EndObject and contents. if (xm == XamlLanguage.Items) { foreach (var xn in GetItemsNodes (xm, xobj)) yield return xn; yield break; } // Arguments: each argument is written as a standalone object if (xm == XamlLanguage.Arguments) { foreach (var argm in xobj.Type.GetSortedConstructorArguments ()) { var argv = argm.Invoker.GetValue (xobj.GetRawValue ()); var xarg = new XamlObject (argm.Type, argv); foreach (var cn in GetNodes (null, xarg)) yield return cn; } yield break; } // PositionalParameters: items are from constructor arguments, written as Value node sequentially. Note that not all of them are in simple string value. if (xm == XamlLanguage.PositionalParameters) { foreach (var argm in xobj.Type.GetSortedConstructorArguments ()) { #if true // FIXME: wrong (see the comment above) // Unlike XamlLanguage.Items, it only outputs string value. So, convert values here. var argv = TypeExtensionMethods.GetStringValue (argm.Type, argm, xobj.GetMemberValue (argm), value_serializer_ctx); yield return new XamlNodeInfo ((string) argv); #else var argv = argm.Invoker.GetValue (xobj.GetRawValue ()); var xarg = new XamlObject (argm.Type, argv); foreach (var cn in GetNodes (null, xarg)) yield return cn; #endif } yield break; } if (xm == XamlLanguage.Initialization) { yield return new XamlNodeInfo (TypeExtensionMethods.GetStringValue (xobj.Type, xm, xobj.GetRawValue (), value_serializer_ctx)); yield break; } // Value - only for non-top-level node (thus xm != null) if (xm != null) { // overrideMemberType is (so far) used for XamlLanguage.Key. var xtt = overrideMemberType ?? xm.Type; if (xtt.IsContentValue (value_serializer_ctx) || xm.IsContentValue (value_serializer_ctx)) { // though null value is special: it is written as a standalone object. var val = xobj.GetRawValue (); if (val == null) foreach (var xn in GetNodes (null, null_object)) yield return xn; else yield return new XamlNodeInfo (TypeExtensionMethods.GetStringValue (xtt, xm, val, value_serializer_ctx)); yield break; } } // collection items: return GetObject and Items. if (xm != null && xm.Type.IsCollection && !xm.IsWritePublic) { yield return new XamlNodeInfo (XamlNodeType.GetObject, xobj); // Write Items member only when there are items (i.e. do not write it if it is empty). var xnm = new XamlNodeMember (xobj, XamlLanguage.Items); var en = GetNodes (XamlLanguage.Items, xnm.Value).GetEnumerator (); if (en.MoveNext ()) { yield return new XamlNodeInfo (XamlNodeType.StartMember, xnm); do { yield return en.Current; } while (en.MoveNext ()); yield return new XamlNodeInfo (XamlNodeType.EndMember, xnm); } yield return new XamlNodeInfo (XamlNodeType.EndObject, xobj); } else { // Object yield return new XamlNodeInfo (XamlNodeType.StartObject, xobj); foreach (var xn in GetObjectMemberNodes (xobj)) yield return xn; yield return new XamlNodeInfo (XamlNodeType.EndObject, xobj); } }
IEnumerable<XamlNodeMember> GetNodeMembers (XamlObject xobj, IValueSerializerContext vsctx) { // XData.XmlReader is not returned. if (xobj.Type == XamlLanguage.XData) { yield return new XamlNodeMember (xobj, XamlLanguage.XData.GetMember ("Text")); yield break; } // FIXME: find out why root Reference has PositionalParameters. if (xobj.GetRawValue() != root && xobj.Type == XamlLanguage.Reference) yield return new XamlNodeMember (xobj, XamlLanguage.PositionalParameters); else foreach (var xm in xobj.Type.GetAllObjectReaderMembersByType (vsctx)) yield return new XamlNodeMember (xobj, xm); }
IEnumerable <XamlNodeInfo> GetNodes(XamlMember xm, XamlObject xobj, XamlType overrideMemberType, bool partOfPositionalParameters) { // collection items: each item is exposed as a standalone object that has StartObject, EndObject and contents. if (xm == XamlLanguage.Items) { foreach (var xn in GetItemsNodes(xm, xobj)) { yield return(xn); } yield break; } // Arguments: each argument is written as a standalone object if (xm == XamlLanguage.Arguments) { foreach (var argm in xobj.Type.GetSortedConstructorArguments()) { var argv = argm.Invoker.GetValue(xobj.GetRawValue()); var xarg = new XamlObject(argm.Type, argv); foreach (var cn in GetNodes(null, xarg)) { yield return(cn); } } yield break; } // PositionalParameters: items are from constructor arguments, written as Value node sequentially. Note that not all of them are in simple string value. Also, null values are not written as NullExtension if (xm == XamlLanguage.PositionalParameters) { foreach (var argm in xobj.Type.GetSortedConstructorArguments()) { foreach (var cn in GetNodes(argm, new XamlObject(argm.Type, xobj.GetMemberValue(argm)), null, true)) { yield return(cn); } } yield break; } if (xm == XamlLanguage.Initialization) { yield return(new XamlNodeInfo(TypeExtensionMethods.GetStringValue(xobj.Type, xm, xobj.GetRawValue(), value_serializer_ctx))); yield break; } // Value - only for non-top-level node (thus xm != null) if (xm != null) { // overrideMemberType is (so far) used for XamlLanguage.Key. var xtt = overrideMemberType ?? xm.Type; if (!xtt.IsMarkupExtension && // this condition is to not serialize MarkupExtension whose type has TypeConverterAttribute (e.g. StaticExtension) as a string. (xtt.IsContentValue(value_serializer_ctx) || xm.IsContentValue(value_serializer_ctx))) { // though null value is special: it is written as a standalone object. var val = xobj.GetRawValue(); if (val == null) { if (!partOfPositionalParameters) { foreach (var xn in GetNodes(null, null_object)) { yield return(xn); } } else { yield return(new XamlNodeInfo(String.Empty)); } } else { yield return(new XamlNodeInfo(TypeExtensionMethods.GetStringValue(xtt, xm, val, value_serializer_ctx))); } yield break; } } // collection items: return GetObject and Items. if (xm != null && xm.Type.IsCollection && !xm.IsWritePublic) { yield return(new XamlNodeInfo(XamlNodeType.GetObject, xobj)); // Write Items member only when there are items (i.e. do not write it if it is empty). var xnm = new XamlNodeMember(xobj, XamlLanguage.Items); var en = GetNodes(XamlLanguage.Items, xnm.Value).GetEnumerator(); if (en.MoveNext()) { yield return(new XamlNodeInfo(XamlNodeType.StartMember, xnm)); do { yield return(en.Current); } while (en.MoveNext()); yield return(new XamlNodeInfo(XamlNodeType.EndMember, xnm)); } yield return(new XamlNodeInfo(XamlNodeType.EndObject, xobj)); } else if (xm != null && xm.Type.IsXData) { var sw = new StringWriter(); var xw = XmlWriter.Create(sw, new XmlWriterSettings() { OmitXmlDeclaration = true, ConformanceLevel = ConformanceLevel.Auto }); var val = xobj.GetRawValue() as IXmlSerializable; if (val == null) { yield break; // do not output anything } val.WriteXml(xw); xw.Close(); var obj = new XData() { Text = sw.ToString() }; foreach (var xn in GetNodes(null, new XamlObject(XamlLanguage.XData, obj))) { yield return(xn); } } else { // Object - could become Reference var val = xobj.GetRawValue(); if (!xobj.Type.IsContentValue(value_serializer_ctx) && val != null) { string refName = NameResolver.GetName(val); if (refName != null) { // The target object is already retrieved, so we don't return the same object again. NameResolver.SaveAsReferenced(val); // Record it as named object. // Then return Reference object instead. foreach (var xn in GetNodes(null, new XamlObject(XamlLanguage.Reference, new Reference(refName)))) { yield return(xn); } yield break; } else { // The object appeared in the xaml tree for the first time. So we store the reference with a unique name so that it could be referenced later. refName = GetReferenceName(xobj); if (NameResolver.IsCollectingReferences && NameResolver.Contains(refName)) { throw new InvalidOperationException(String.Format("There is already an object of type {0} named as '{1}'. Object names must be unique.", val.GetType(), refName)); } NameResolver.SetNamedObject(refName, val, true); // probably fullyInitialized is always true here. } } yield return(new XamlNodeInfo(XamlNodeType.StartObject, xobj)); // If this object is referenced and there is no [RuntimeNameProperty] member, then return Name property in addition. if (val != null && xobj.Type.GetAliasedProperty(XamlLanguage.Name) == null) { string name = NameResolver.GetReferencedName(val); if (name != null) { var sobj = new XamlObject(XamlLanguage.String, name); foreach (var cn in GetMemberNodes(new XamlNodeMember(sobj, XamlLanguage.Name), new XamlNodeInfo [] { new XamlNodeInfo(name) })) { yield return(cn); } } } foreach (var xn in GetObjectMemberNodes(xobj)) { yield return(xn); } yield return(new XamlNodeInfo(XamlNodeType.EndObject, xobj)); } }