diff options
Diffstat (limited to 'mcs/class/PresentationFramework/Mono.Windows.Serialization/XamlParser.cs')
-rw-r--r-- | mcs/class/PresentationFramework/Mono.Windows.Serialization/XamlParser.cs | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/mcs/class/PresentationFramework/Mono.Windows.Serialization/XamlParser.cs b/mcs/class/PresentationFramework/Mono.Windows.Serialization/XamlParser.cs new file mode 100644 index 00000000000..fd4d8220114 --- /dev/null +++ b/mcs/class/PresentationFramework/Mono.Windows.Serialization/XamlParser.cs @@ -0,0 +1,292 @@ +// +// XamlParser.cs +// +// Author: +// Iain McCoy (iain@mccoy.id.au) +// +// (C) 2005 Iain McCoy +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.IO; +using System.Xml; +using System.Reflection; +using System.Windows; + +namespace Mono.Windows.Serialization { + public class XamlParser { + public const string XAML_NAMESPACE = "http://schemas.microsoft.com/winfx/xaml/2005"; + private Mapper mapper = new Mapper(); + private XmlReader reader; + private XamlWriter writer; + + private enum CurrentType { Object, Property, AttachedProperty } + + private class ParserState { + public object obj; + public CurrentType type; + } + + private ParserState currentState = null; + private ArrayList oldStates = new ArrayList(); + + public XamlParser(string filename, XamlWriter writer) + { + reader = new XmlTextReader(filename); + this.writer = writer; + } + + public void Parse() + { + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.ProcessingInstruction: + parsePI(); + break; + case XmlNodeType.Element: + parseElement(); + break; + case XmlNodeType.EndElement: + parseEndElement(); + break; + case XmlNodeType.Text: + parseText(); + break; + case XmlNodeType.Whitespace: + // skip whitespace + break; + default: + Console.Out.WriteLine("Unknown element type " + reader.NodeType); + break; + } + } + } + void parsePI() + { + if (reader.Name != "Mapping") + Console.WriteLine("Unknown processing instruction"); + Mapping mapping = new Mapping(reader.Value); + mapper.AddMapping(mapping); + } + + void parseElement() + { + // This element must be an object if: + // - It's a direct child of a property element + // - It's a direct child of an IAddChild element + // and does not have a dot in its name + // + // parseObjectElement will verify the second case + // + // If it's a dotted name, then it is a property. + // What it is a property of depends on the bit of the + // name before the dot. + int dotPosition = reader.LocalName.IndexOf('.'); + if (dotPosition < 0 || + currentState.type == CurrentType.Property) { + parseObjectElement(); + return; + } + string beforeDot = reader.LocalName.Substring(0, dotPosition); + string afterDot = reader.LocalName.Substring(dotPosition + 1); + // If we've got this far, then currentState.Type == Object + if (isNameOfAncestorClass(beforeDot, (Type)currentState.obj)) + parseNormalPropertyElement(afterDot); + else + parseAttachedPropertyElement(beforeDot, afterDot); + } + + // check if the given name is the name of an ancestor of + // the given type + bool isNameOfAncestorClass(string name, Type t) + { + while (t.BaseType != null) { + if (t.Name == name) + return true; + t = t.BaseType; + } + return false; + } + + void parseText() + { + if (currentState.type == CurrentType.Object) + writer.CreateElementText(reader.Value); + else if (currentState.type == CurrentType.AttachedProperty) + writer.CreateAttachedPropertyText(reader.Value); + else + writer.CreatePropertyText(reader.Value); + } + + void parseNormalPropertyElement(string propertyName) + { + // preconditions: currentState.Type == Object + Type currentType = (Type)currentState.obj; + PropertyInfo prop = currentType.GetProperty(propertyName); + if (prop == null) { + Console.WriteLine("Property " + propertyName + " not found on " + currentType.Name); + return; + // TODO: exception + } + + ParserState newState = new ParserState(); + newState.type = CurrentType.Property; + newState.obj = prop; + oldStates.Add(currentState); + currentState = newState; + + writer.CreateProperty(propertyName); + + if (reader.HasAttributes) { + Console.WriteLine("Property node should not have attributes"); + return; + // TODO: exception + } + } + + void parseAttachedPropertyElement(string attachedTo, string propertyName) + { + Type typeAttachedTo = null; + FieldInfo propField; + DependencyProperty dp; + Type currentType = (Type)currentState.obj; + if (!currentType.IsSubclassOf(typeof(System.Windows.DependencyObject))) + throw new Exception("Attached properties can only be set on DependencyObjects (not " + currentType.Name + ")"); + foreach (ParserState state in oldStates) { + if (state.type == CurrentType.Object && + ((Type)state.obj).Name == attachedTo) { + typeAttachedTo = (Type)state.obj; + break; + } + } + if (typeAttachedTo == null) + throw new Exception("Nothing to attach to: " + attachedTo + "." + propertyName); + propField = typeAttachedTo.GetField(propertyName + "Property"); + if (propField == null) + throw new Exception("Property " + propertyName + " does not exist on " + attachedTo); + dp = (DependencyProperty)propField.GetValue(null); + + oldStates.Add(currentState); + currentState = new ParserState(); + currentState.obj = propField; + currentState.type = CurrentType.AttachedProperty; + + writer.CreateAttachedProperty(attachedTo, propertyName, dp.PropertyType.AssemblyQualifiedName); + } + + void parseObjectElement() + { + string parentName, objectName = null; + bool isEmpty = reader.IsEmptyElement; + + parentName = mapper.Resolve(reader.NamespaceURI, reader.Name); + objectName = reader.GetAttribute("Class", XAML_NAMESPACE); + if (Type.GetType(parentName).GetInterface("System.Windows.Serialization.IAddChild") == null) + {} //TODO: throw exception + if (currentState == null) { + createTopLevel(parentName, objectName); + } else { + addChild(parentName); + } + + if (reader.MoveToFirstAttribute()) { + do { + if (reader.LocalName.StartsWith("xmlns")) + continue; + if (reader.LocalName.IndexOf(".") < 0) + parseLocalPropertyAttribute(); + else + parseContextPropertyAttribute(); + } while (reader.MoveToNextAttribute()); + } + + + if (isEmpty) + writer.EndObject(); + } + + void parseLocalPropertyAttribute() + { + string propertyName = reader.LocalName; + Type currentType = (Type)currentState.obj; + PropertyInfo prop = currentType.GetProperty(propertyName); + if (prop == null) { + Console.WriteLine("Property " + propertyName + " not found on " + currentType.Name); + return; + // TODO: throw exception + } + + writer.CreateProperty(propertyName); + writer.CreatePropertyText(reader.Value); + + parseEndElement(); + } + + void parseContextPropertyAttribute() + { + throw new NotImplementedException("parseContextPropertyAttribute"); + } + + void parseEndElement() + { + if (currentState.type == CurrentType.Object) + writer.EndObject(); + else if (currentState.type == CurrentType.Property) + writer.EndProperty(); + else if (currentState.type == CurrentType.AttachedProperty) + writer.EndAttachedProperty(); + + + if (oldStates.Count == 0) { + currentState = null; + writer.Finish(); + return; + } + int lastIndex = oldStates.Count - 1; + currentState = (ParserState)oldStates[lastIndex]; + oldStates.RemoveAt(lastIndex); + } + + void createTopLevel(string parentName, string objectName) + { + Type t = Type.GetType(parentName); + currentState = new ParserState(); + currentState.type = CurrentType.Object; + currentState.obj = t; + if (objectName == null) { + objectName = "derived" + t.Name; + } + writer.CreateTopLevel(parentName, objectName); + } + + void addChild(string className) + { + writer.CreateObject(className); + oldStates.Add(currentState); + currentState = new ParserState(); + currentState.type = CurrentType.Object; + currentState.obj = Type.GetType(className); + } + } +} |