/*
* Copyright 2006, Dustin Metzgar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
namespace XmiCodeDomLib
{
/// <summary>
/// An XMI Parser that uses reflection. This version is easily debuggable but very slow.
/// </summary>
internal sealed class ReflectionXmiParser : IXmiParser
{
private Dictionary<string, object> _XmiObjects = new Dictionary<string, object>();
private Dictionary<string, Type> _ParseClasses;
public Dictionary<string, object> XmiObjects
{
get { return _XmiObjects; }
}
public ReflectionXmiParser(Dictionary<string, Type> dictParseClasses)
{
_ParseClasses = dictParseClasses;
}
public object ParseClass(XmlReader xr)
{
// Assume that we're already on the node.
string nodeName = xr.Name;
if (!_ParseClasses.ContainsKey(nodeName))
xr.Skip();
else
{
Type parseType = _ParseClasses[nodeName];
object parseObj = Activator.CreateInstance(parseType);
// This part parses the attributes that are still in the start element.
if (xr.HasAttributes)
{
xr.MoveToFirstAttribute();
do
{
try
{
string propName = XmiRoot.GetSafeName(xr.Name);
PropertyInfo pi = parseType.GetProperty(propName, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (pi != null)
{
pi.SetValue(parseObj, Convert.ChangeType(xr.Value, pi.PropertyType), null);
if (propName.ToLower() == "xmiid")
_XmiObjects.Add(xr.Value, parseObj);
}
}
catch
{
Console.Error.WriteLine("Could not parse attribute: " + xr.Name);
}
} while (xr.MoveToNextAttribute());
xr.MoveToContent();
}
if (xr.IsEmptyElement)
return parseObj;
// Now we need to look at the child nodes, because those will be attributes as well.
while (true)
{
xr.Read();
xr.MoveToContent();
if (xr.NodeType == XmlNodeType.EndElement)
break;
string attrName = xr.Name;
if (attrName.StartsWith(nodeName + "."))
{
attrName = attrName.Substring(nodeName.Length + 1);
attrName = XmiRoot.GetSafeName(attrName);
SetProperty(attrName, xr, parseType, parseObj, xr.Name);
}
else
{
bool found = false;
foreach (Type interfaceType in parseType.GetInterfaces())
{
object[] a = interfaceType.GetCustomAttributes(typeof(XmiParserAttribute), false);
if (a.Length > 0)
{
XmiParserAttribute xpa = a[0] as XmiParserAttribute;
if (attrName.StartsWith(xpa.XmiName + "."))
{
attrName = attrName.Substring(xpa.XmiName.Length + 1);
attrName = XmiRoot.GetSafeName(attrName);
found = true;
break;
}
}
}
if (found)
{
SetProperty(attrName, xr, parseType, parseObj, xr.Name);
}
else
{
xr.Skip();
}
}
}
return parseObj;
}
return null;
}
private void SetProperty(string attrName, XmlReader xr, Type parseType, object parseObj, string elemName)
{
PropertyInfo pi = parseType.GetProperty(attrName, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (pi != null)
{
if (pi.PropertyType != typeof(object) && pi.PropertyType.Namespace == "System")
{
if (xr.HasAttributes && xr.MoveToAttribute("xmi.value"))
{
pi.SetValue(parseObj, Convert.ChangeType(xr.Value, pi.PropertyType), null);
xr.MoveToContent();
}
else
{
xr.Read();
pi.SetValue(parseObj, Convert.ChangeType(xr.Value, pi.PropertyType), null);
xr.Read();
}
}
else
{
bool isList = false;
foreach (Type interfaceType in pi.PropertyType.GetInterfaces())
if (interfaceType == typeof(System.Collections.IList))
{
isList = true;
break;
}
if (isList)
{
System.Collections.IList lst = pi.GetValue(parseObj, null) as System.Collections.IList;
while (true)
{
xr.Read();
xr.MoveToContent();
if (xr.NodeType == XmlNodeType.EndElement)
break;
object obj = ParseClass(xr);
if (obj != null)
lst.Add(obj);
}
}
else
{
xr.Read();
xr.MoveToContent();
pi.SetValue(parseObj, ParseClass(xr), null);
while (xr.Name != elemName)
{
xr.Read();
xr.MoveToContent();
}
}
}
}
else
xr.Skip();
}
}
}