context: //
  // XApiReader.cs
  //
  // Author:
  //   Jb Evain (jbevain@novell.com)
  //
  // (C) 2007 Novell, Inc.
  //
  // 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.Linq;
  using System.Text;
  using System.Xml.XPath;
  
  using Mono.Cecil;
  
  namespace Mono.Linker {
  
  	public class XApiReader {
  
  		static readonly string _name = "name";
  		static readonly string _ns = string.Empty;
  
  		LinkContext _context;
  		XPathDocument _document;
  		IXApiVisitor _visitor;
  
  		AssemblyDefinition _assembly;
  		string _namespace;
  		Stack _types = new Stack ();
  		StringBuilder _signature;
  
  		public XApiReader (XPathDocument document, IXApiVisitor visitor)
  		{
  			_document = document;
  			_visitor = visitor;
  		}
  
  		public void Process (LinkContext context)
  		{
  			_context = context;
  			ProcessAssemblies (_document.CreateNavigator ());
  		}
  
  		void OnAssembly (XPathNavigator nav)
  		{
  			_assembly = GetAssembly (nav);
  
  			_visitor.OnAssembly (nav, _assembly);
  
  			ProcessAttributes (nav);
  			ProcessNamespaces (nav);
  		}
  
  		AssemblyDefinition GetAssembly (XPathNavigator nav)
  		{
  			AssemblyNameReference name = new AssemblyNameReference (
  				GetName (nav),
  				new Version (GetAttribute (nav, "version")));
  
  			AssemblyDefinition assembly = _context.Resolve (name);
  			ProcessReferences (assembly);
  			return assembly;
  		}
  
  		void ProcessReferences (AssemblyDefinition assembly)
  		{
  			foreach (AssemblyNameReference name in assembly.MainModule.AssemblyReferences)
  				_context.Resolve (name);
  		}
  
  		void OnAttribute (XPathNavigator nav)
  		{
  			_visitor.OnAttribute (nav);
  		}
  
  		void PushType (TypeDefinition type)
  		{
  			_types.Push (type);
  		}
  
  		TypeDefinition PeekType ()
  		{
  			return (TypeDefinition) _types.Peek ();
  		}
  
  		TypeDefinition PopType ()
  		{
  			return (TypeDefinition) _types.Pop ();
  		}
  
  		void OnNamespace (XPathNavigator nav)
  		{
  			_namespace = GetName (nav);
  
  			ProcessClasses (nav);
  		}
  
  		void OnClass (XPathNavigator nav)
  		{
  			string name = GetClassName (nav);
  
  			TypeDefinition type = _assembly.MainModule.GetType (name);
  			if (type == null)
  				return;
  
  			_visitor.OnClass (nav, type);
  
  			PushType (type);
  
  			ProcessAttributes (nav);
  			ProcessInterfaces (nav);
  			ProcessFields (nav);
  			ProcessMethods (nav);
  			ProcessConstructors (nav);
  			ProcessProperties (nav);
  			ProcessEvents (nav);
  			ProcessClasses (nav);
  
  			PopType ();
  		}
  
  		string GetClassName (XPathNavigator nav)
  		{
  			if (IsNestedClass ())
  				return PeekType ().FullName + "/" + GetName (nav);
  
  			return _namespace + "." + GetName (nav);
  		}
  
  		bool IsNestedClass ()
  		{
  			return _types.Count > 0;
  		}
  
  		void OnField (XPathNavigator nav)
  		{
  			TypeDefinition declaring = PeekType ();
  
  			FieldDefinition field = declaring.Fields.FirstOrDefault (f => f.Name == GetName (nav));
  			if (field != null)
  				_visitor.OnField (nav, field);
  
  			ProcessAttributes (nav);
  		}
  
  		void OnInterface (XPathNavigator nav)
  		{
  			string name = GetName (nav);
  
  			TypeDefinition type = _context.GetType (GetTypeName (name));
  			if (type != null)
  				_visitor.OnInterface (nav, type);
  		}
  
  		void OnMethod (XPathNavigator nav)
  		{
  			InitMethodSignature (nav);
  
  			ProcessParameters (nav);
  
  			string signature = GetMethodSignature ();
  
  			MethodDefinition method = GetMethod (signature);
  			if (method != null)
  				_visitor.OnMethod (nav, method);
  
  			ProcessAttributes (nav);
  		}
  
  		MethodDefinition GetMethod (string signature)
  		{
  			return GetMethod (PeekType ().Methods, signature);
  		}
  
  		static MethodDefinition GetMethod (ICollection methods, string signature)
  		{
  			foreach (MethodDefinition method in methods)
  				if (signature == GetSignature (method))
  					return method;
  
  			return null;
  		}
  
  		static string GetSignature (MethodDefinition method)
  		{
  			return method.ToString ().Replace ("<", "[").Replace (">", "]");
  		}
  
  		string GetMethodSignature ()
  		{
  			_signature.Append (")");
  			return _signature.ToString ();
  		}
  
  		void InitMethodSignature (XPathNavigator nav)
  		{
  			_signature = new StringBuilder ();
  
  			string returntype = GetAttribute (nav, "returntype");
  			if (returntype == null || returntype.Length == 0)
  				returntype = "System.Void";
  
  			_signature.Append (NormalizeTypeName (returntype));
  			_signature.Append (" ");
  			_signature.Append (PeekType ().FullName);
  			_signature.Append ("::");
  
  			string name = GetName (nav);
  			_signature.Append (GetMethodName (name));
  
  			_signature.Append ("(");
  		}
  
  		static string GetMethodName (string name)
  		{
  			return GetStringBefore (name, "(");
  		}
  
  		static string NormalizeTypeName (string name)
  		{
  			return name.Replace ("+", "/").Replace ("<", "[").Replace (">", "]");
  		}
  
  		static string GetTypeName (string name)
  		{
  			return GetStringBefore (NormalizeTypeName (name), "[");
  		}
  
  		static string GetStringBefore (string str, string marker)
  		{
  			int pos = str.IndexOf (marker);
  			if (pos == -1)
  				return str;
  
  			return str.Substring (0, pos);
  		}
  
  		void OnParameter (XPathNavigator nav)
  		{
  			string type = GetAttribute (nav, "type");
  			int pos = int.Parse (GetAttribute (nav, "position"));
  
  			if (pos > 0)
  				_signature.Append (",");
  			_signature.Append (NormalizeTypeName (type));
  		}
  
  		void OnConstructor (XPathNavigator nav)
  		{
  			InitMethodSignature (nav);
  
  			ProcessParameters (nav);
  
  			string signature = GetMethodSignature ();
  
  			MethodDefinition ctor = GetMethod (signature);
  			if (ctor != null)
  				_visitor.OnConstructor (nav, ctor);
  
  			ProcessAttributes (nav);
  		}
  
  		void OnProperty (XPathNavigator nav)
  		{
  			string name = GetName (nav);
  			TypeDefinition type = PeekType ();
  
  			var property = type.Properties.FirstOrDefault (p => p.Name == name);
  			if (property != null)
  				_visitor.OnProperty (nav, property);
  
  			ProcessAttributes (nav);
  			ProcessMethods (nav);
  		}
  
  		void OnEvent (XPathNavigator nav)
  		{
  			string name = GetName (nav);
  			TypeDefinition type = PeekType ();
  
  			EventDefinition evt = type.Events.FirstOrDefault (e => e.Name == name);
  			if (evt != null)
  				_visitor.OnEvent (nav, evt);
  
  			ProcessAttributes (nav);
  		}
  
  		void ProcessAssemblies (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "assemblies//assembly", new OnChildren (OnAssembly));
  		}
  
  		void ProcessAttributes (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "attributes//attribute", new OnChildren (OnAttribute));
  		}
  
  		void ProcessNamespaces (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "namespaces//namespace", new OnChildren (OnNamespace));
  		}
  
  		void ProcessClasses (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "classes//class", new OnChildren (OnClass));
  		}
  
  		void ProcessInterfaces (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "intefaces//interface", new OnChildren (OnInterface));
  		}
  
  		void ProcessFields (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "fields//field", new OnChildren (OnField));
  		}
  
  		void ProcessMethods (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "methods//method", new OnChildren (OnMethod));
  		}
  
  		void ProcessConstructors (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "constructors//constructor", new OnChildren (OnConstructor));
  		}
  
  		void ProcessParameters (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "parameters//parameter", new OnChildren (OnParameter));
  		}
  
  		void ProcessProperties (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "properties//property", new OnChildren (OnProperty));
  		}
  
  		void ProcessEvents (XPathNavigator nav)
  		{
  			ProcessChildren (nav, "events//event", new OnChildren (OnEvent));
  		}
  
  		static void ProcessChildren (XPathNavigator nav, string children, OnChildren action)
  		{
  			XPathNodeIterator iterator = nav.Select (children);
  			while (iterator.MoveNext ())
  				action (iterator.Current);
  		}
  
  
gt: 		delegate void OnChildren (XPathNavigator nav);