XmlParser.cs :  » Web-Frameworks » Ingenious-MVC » Ingenious » Mvc » Configuration » Configurators » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Web Frameworks » Ingenious MVC 
Ingenious MVC » Ingenious » Mvc » Configuration » Configurators » XmlParser.cs
#region License

/**
 * Ingenious MVC : An MVC framework for .NET 2.0
 * Copyright (C) 2006, JDP Group
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Authors: - Kent Boogaart (kentcb@internode.on.net)
 *          - James Inge
 */

#endregion

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using Ingenious.Mvc.Factories;
using Ingenious.Mvc.Util;

namespace Ingenious.Mvc.Configuration.Configurators{
  /// <include file='XmlParser.doc.xml' path='/doc/member[@name="T:XmlParser"]/*'/>
  internal static class XmlParser
  {
    private const string IdAttribute = "id";
    private const string TypeAttribute = "type";
    private const string EnumerationAttribute = "enumeration";
    private const string StartingViewAttribute = "startingView";
    private const string ViewManagerAttribute = "viewManager";
    private const string ControllerAttribute = "controller";
    private static readonly Log _log = Log.CreateForNamespace(typeof(XmlParser));

    /// <include file='XmlParser.doc.xml' path='/doc/member[@name="M:Parse(System.Xml.XmlNode)"]/*'/>
    public static ConfigurationInfo Parse(XmlNode xml)
    {
      Debug.Assert(xml != null);

      _log.Information("Loading XSD schemas.");
      //first, load the main schema
      XmlSchema mainSchema = XmlSchema.Read(Assembly.GetExecutingAssembly().GetManifestResourceStream(typeof(XmlParser), "MVC.xsd"), null);
      _log.Verbose("Loaded main configuration schema.");
      //get all elements with a type attribute
      XmlNodeList typeNodes = xml.SelectNodes("//*[@type]");
      //create a schema set for all schemas
      XmlSchemaSet schemas = new XmlSchemaSet();
      schemas.Add(mainSchema);
      //track the IDs of all schemas added to the schema set
      IList<string> schemaIds = new List<string>();

      //load all sub-schemas
      foreach (XmlNode typeNode in typeNodes)
      {
        XmlAttribute typeAttribute = typeNode.Attributes[TypeAttribute];
        _log.Verbose("Attempting to find type '{0}'.", typeAttribute.Value);
        Type type = Type.GetType(typeAttribute.Value);

        if (type == null)
        {
          //try and get as much useful information into the exception message as possible. The ID would be most useful if present
          XmlAttribute idAttribute = typeNode.Attributes["id"];

          if (idAttribute == null)
          {
            _log.Verbose("Type '{0}' could not be loaded.", typeAttribute.Value);
            ExceptionHelper.Throw("Parse.typeNotFound", typeAttribute.Value, typeNode.Name);
          }
          else
          {
            _log.Verbose("Type '{0}' could not be loaded for element with ID '{1}'.", typeAttribute.Value, idAttribute.Value);
            ExceptionHelper.Throw("Parse.typeNotFoundWithId", typeAttribute.Value, typeNode.Name, idAttribute.Value);
          }
        }

        object[] attributes = type.GetCustomAttributes(typeof(XmlCustomParserAttribute), true);

        if (attributes.Length > 0)
        {
          IXmlCustomParser parser = ((XmlCustomParserAttribute) attributes[0]).GetXmlCustomParser();
          _log.Verbose("Type '{0}' has a custom parser of type '{1}'.", type.FullName, parser.GetType().FullName);
          XmlSchema subSchema = parser.Schema;
          Debug.Assert(subSchema != null, "Parser returned null from Schema property.");
          //schemas must be assigned an ID
          ExceptionHelper.ThrowIf(subSchema.Id == null, "FindSubSchemas.schemaIdNotSupplied", type.FullName);

          //note we only add the schema once
          if (!schemaIds.Contains(subSchema.Id))
          {
            _log.Verbose("Schema with ID '{0}' is not yet in the schema set - adding it.", subSchema.Id);
            schemas.Add(subSchema);
            schemaIds.Add(subSchema.Id);
          }
          else
          {
            _log.Verbose("Schema with ID '{0}' is already in the schema set - ignoring it.", subSchema.Id);
          }

          //need to associate any type name with the node
          if (parser.TypeName != null)
          {
            XmlAttribute xsiTypeAttribute = typeNode.OwnerDocument.CreateAttribute("type", "http://www.w3.org/2001/XMLSchema-instance");
            xsiTypeAttribute.Value = parser.TypeName;
            typeNode.Attributes.Append(xsiTypeAttribute);
          }
        }
      }

      //we have all schemas so compile them
      _log.Verbose("Compiling set of {0} schemas.", schemas.Count);
      schemas.ValidationEventHandler += new ValidationEventHandler(CompileErrorHandler);
      schemas.Compile();

      _log.Information("Validating configuration.");
      //create the validator
      XmlReader xmlReader = null;
      ValidateHelper validateHelper = new ValidateHelper();

      try
      {
        XmlReaderSettings readerSettings = new XmlReaderSettings();
        readerSettings.ValidationType = ValidationType.Schema;
        //add schemas to the validator
        readerSettings.Schemas.Add(schemas);
        //listen for validation problems
        readerSettings.ValidationEventHandler += new ValidationEventHandler(validateHelper.ValidationHandler);

        //create the reader
        xmlReader = XmlReader.Create(new XmlTextReader(new StringReader(xml.OuterXml)), readerSettings);
        //this validates the XML document
        while (xmlReader.Read()) ;
      }
      catch (Exception ex)
      {
        _log.Exception(ex);
        ExceptionHelper.Throw("Parse.problemValidating", ex);
      }
      finally
      {
        if (xmlReader != null)
        {
          xmlReader.Close();
        }
      }

      //we don't use ThrowIf here to make things a bit more efficient for the common case (no errors)
      if (validateHelper.HasErrors)
      {
        _log.Error("Validation errors were detected: {0}", validateHelper.ErrorMessage);
        ExceptionHelper.Throw("Parse.errorsDetected", Environment.NewLine, validateHelper.ErrorMessage);
      }

      _log.Information("Parsing configuration.");
      //now we have validated the document, time to parse it for the info we need
      ConfigurationInfo retVal = new ConfigurationInfo();

      //parse any factories
      Type factoryType;
      object customConfiguration;

      //controller factory
      if (ParseFactoryType(retVal, xml, "factories/controllerFactory", typeof(IControllerFactory), out factoryType, out customConfiguration))
      {
        retVal.ControllerFactory = (IControllerFactory) FactoryFactory.Instance.Create(factoryType, customConfiguration, typeof(IControllerFactory));
      }

      //view factory
      if (ParseFactoryType(retVal, xml, "factories/viewFactory", typeof(IViewFactory), out factoryType, out customConfiguration))
      {
        retVal.ViewFactory = (IViewFactory) FactoryFactory.Instance.Create(factoryType, customConfiguration, typeof(IViewFactory));
      }

      //view manager factory
      if (ParseFactoryType(retVal, xml, "factories/viewManagerFactory", typeof(IViewManagerFactory), out factoryType, out customConfiguration))
      {
        retVal.ViewManagerFactory = (IViewManagerFactory) FactoryFactory.Instance.Create(factoryType, customConfiguration, typeof(IViewManagerFactory));
      }

      //navigator factory
      if (ParseFactoryType(retVal, xml, "factories/navigatorFactory", typeof(INavigatorFactory), out factoryType, out customConfiguration))
      {
        retVal.NavigatorFactory = (INavigatorFactory) FactoryFactory.Instance.Create(factoryType, customConfiguration, typeof(INavigatorFactory));
      }

      //task factory
      if (ParseFactoryType(retVal, xml, "factories/taskFactory", typeof(ITaskFactory), out factoryType, out customConfiguration))
      {
        retVal.TaskFactory = (ITaskFactory) FactoryFactory.Instance.Create(factoryType, customConfiguration, typeof(ITaskFactory));
      }

      //parse the controller infos
      InfoCollection<Id<IController>, ControllerInfo> controllerInfos = new InfoCollection<Id<IController>, ControllerInfo>();
      ParseControllers(retVal, controllerInfos, xml);

      //parse the view manager infos
      InfoCollection<Id<IViewManager>, ViewManagerInfo> viewManagerInfos = new InfoCollection<Id<IViewManager>, ViewManagerInfo>();
      ParseViewManagers(retVal, viewManagerInfos, xml);

      //parse the view infos
      ParseViews(retVal, retVal.ViewInfos, xml, controllerInfos);

      //parse the navigator infos
      ParseNavigators(retVal, retVal.NavigatorInfos, xml, retVal.ViewInfos, viewManagerInfos);

      return retVal;
    }

    private static void CompileErrorHandler(object sender, ValidationEventArgs e)
    {
      _log.Error("Compile error: {0}", e.Message);
      _log.Exception(e.Exception);
      ExceptionHelper.Throw("CompileErrorHandler.compileError", e.Exception, e.Message);
    }

    private static bool ParseFactoryType(ConfigurationInfo configurationInfo, XmlNode xml, string xpath, Type factoryInterfaceType, out Type factoryType, out object customConfiguration)
    {
      Debug.Assert(xml != null);
      Debug.Assert(xpath != null);
      Debug.Assert(factoryInterfaceType != null);
      _log.Verbose("Parsing factory of type '{0}' for XPath '{1}'.", factoryInterfaceType.FullName, xpath);

      XmlNode node = xml.SelectSingleNode(xpath);

      if (node != null)
      {
        XmlAttribute typeAttribute = node.Attributes[TypeAttribute];
        _log.Verbose("Type '{0}' is configured as a factory of type '{1}'.", typeAttribute.Value, factoryInterfaceType.FullName);
        factoryType = Type.GetType(typeAttribute.Value);
        Debug.Assert(factoryType != null, "All types have already been loaded in an earlier phase - was expecting type to load.");
        ExceptionHelper.ThrowIf(!factoryInterfaceType.IsAssignableFrom(factoryType), "ParseFactory.typeDoesNotImplementInterface", factoryType.FullName, factoryInterfaceType.FullName);
        ExceptionHelper.ThrowIf(factoryType.GetConstructor(Type.EmptyTypes) == null, "ParseFactory.typeDoesNotHaveDefaultConstructor", factoryType.FullName);
        //parse any custom config for the factory
        customConfiguration = ParseCustomConfiguration(factoryType, configurationInfo, null, node);
        _log.Verbose("Type '{0}' successfully configured as a factory of type '{1}'.", factoryType.FullName, factoryInterfaceType.FullName);
        return true;
      }
      else
      {
        _log.Verbose("No configuration was found for factory of type '{0}' - using the default factory.", factoryInterfaceType.FullName);
        factoryType = null;
        customConfiguration = null;
        return false;
      }
    }

    private static void ParseControllers(ConfigurationInfo configurationInfo, InfoCollection<Id<IController>, ControllerInfo> controllerInfos, XmlNode xml)
    {
      Debug.Assert(configurationInfo != null);
      Debug.Assert(controllerInfos != null);
      Debug.Assert(xml != null);
      _log.Information("Parsing controller configuration.");

      XmlNode controllersNode = xml.SelectSingleNode("controllers");

      //controllers are optional
      if (controllersNode != null)
      {
        XmlAttribute enumerationAttribute = controllersNode.Attributes[EnumerationAttribute];
        XmlNodeList nodeList = controllersNode.SelectNodes("controller");
        Type enumerationType = null;

        if (enumerationAttribute != null)
        {
          enumerationType = XmlHelper.GetEnumeration(enumerationAttribute.Value);
        }

        foreach (XmlNode node in nodeList)
        {
          XmlAttribute idAttribute = node.Attributes[IdAttribute];
          XmlAttribute typeAttribute = node.Attributes[TypeAttribute];

          //if an enumeration is specified to identify controllers then make sure the ID is a valid enumeration member
          if (enumerationType != null)
          {
            XmlHelper.ValidateEnumerationMember(enumerationType, idAttribute.Value);
          }

          //create the controller info
          ControllerInfo controllerInfo = new ControllerInfo(idAttribute.Value);
          controllerInfo.Type = Type.GetType(typeAttribute.Value);
          Debug.Assert(controllerInfo.Type != null);

          _log.Verbose("Adding ControllerInfo with ID '{0}', type '{1}'.", controllerInfo.Id, controllerInfo.Type.FullName);
          controllerInfos.Add(controllerInfo);

          //parse any custom configuration for the controller
          controllerInfo.CustomConfiguration = ParseCustomConfiguration(controllerInfo.Type, configurationInfo, controllerInfo, node);
        }
      }
      else
      {
        _log.Verbose("No controller configuration found.");
      }
    }

    private static void ParseNavigators(ConfigurationInfo configurationInfo, InfoCollection<Id<INavigator>, NavigatorInfo> navigatorInfos, XmlNode xml, InfoCollection<Id<IView>, ViewInfo> viewInfos, InfoCollection<Id<IViewManager>, ViewManagerInfo> viewManagerInfos)
    {
      Debug.Assert(configurationInfo != null);
      Debug.Assert(navigatorInfos != null);
      Debug.Assert(xml != null);
      Debug.Assert(viewInfos != null);
      Debug.Assert(viewManagerInfos != null);
      _log.Information("Parsing navigator configuration.");

      XmlNode navigatorsNode = xml.SelectSingleNode("navigators");
      XmlAttribute enumerationAttribute = navigatorsNode.Attributes[EnumerationAttribute];
      XmlNodeList nodeList = navigatorsNode.SelectNodes("navigator");
      Type enumerationType = null;

      if (enumerationAttribute != null)
      {
        enumerationType = XmlHelper.GetEnumeration(enumerationAttribute.Value);
      }

      foreach (XmlNode node in nodeList)
      {
        XmlAttribute idAttribute = node.Attributes[IdAttribute];
        XmlAttribute typeAttribute = node.Attributes[TypeAttribute];
        XmlAttribute startingViewAttribute = node.Attributes[StartingViewAttribute];
        XmlAttribute viewManagerAttribute = node.Attributes[ViewManagerAttribute];

        //if an enumeration is specified to identify navigators then make sure the ID is a valid enumeration member
        if (enumerationType != null)
        {
          XmlHelper.ValidateEnumerationMember(enumerationType, idAttribute.Value);
        }

        //create the navigator info
        NavigatorInfo navigatorInfo = new NavigatorInfo(idAttribute.Value);
        navigatorInfo.Type = Type.GetType(typeAttribute.Value);
        navigatorInfo.ViewManagerInfo = viewManagerInfos[viewManagerAttribute.Value];

        //the starting view is optional
        if (startingViewAttribute != null)
        {
          _log.Verbose("Assigning starting view with ID '{0}' to navigator info.", startingViewAttribute.Value);
          navigatorInfo.StartViewInfo = viewInfos[startingViewAttribute.Value];

          if (navigatorInfo.StartViewInfo == null)
          {
            _log.Error("Starting view with ID '{0}' was not found for navigator with ID '{1}'.", startingViewAttribute.Value, navigatorInfo.Id);
            ExceptionHelper.Throw("ParseNavigators.startingViewNotFound", startingViewAttribute.Value, navigatorInfo.Id);
          }
        }

        Debug.Assert(navigatorInfo.Type != null);

        if (navigatorInfo.ViewManagerInfo == null)
        {
          _log.Error("View manager with ID '{0}' not found for navigator with ID '{1}'.", viewManagerAttribute.Value, navigatorInfo.Id);
          ExceptionHelper.Throw("ParseNavigators.viewManagerNotFound", viewManagerAttribute.Value, navigatorInfo.Id);
        }

        _log.Verbose("Adding NavigatorInfo with ID '{0}', type '{1}'.", navigatorInfo.Id, navigatorInfo.Type.FullName);
        navigatorInfos.Add(navigatorInfo);

        //parse any custom configuration for the navigator
        navigatorInfo.CustomConfiguration = ParseCustomConfiguration(navigatorInfo.Type, configurationInfo, navigatorInfo, node);
      }
    }

    private static void ParseViews(ConfigurationInfo configurationInfo, InfoCollection<Id<IView>, ViewInfo> viewInfos, XmlNode xml, InfoCollection<Id<IController>, ControllerInfo> controllerInfos)
    {
      Debug.Assert(configurationInfo != null);
      Debug.Assert(viewInfos != null);
      Debug.Assert(xml != null);
      Debug.Assert(controllerInfos != null);
      _log.Information("Parsing view configuration.");

      XmlNode viewsNode = xml.SelectSingleNode("views");
      XmlAttribute enumerationAttribute = viewsNode.Attributes[EnumerationAttribute];
      XmlNodeList nodeList = viewsNode.SelectNodes("view");
      Type enumerationType = null;

      if (enumerationAttribute != null)
      {
        enumerationType = XmlHelper.GetEnumeration(enumerationAttribute.Value);
      }

      foreach (XmlNode node in nodeList)
      {
        XmlAttribute idAttribute = node.Attributes[IdAttribute];
        XmlAttribute typeAttribute = node.Attributes[TypeAttribute];
        XmlAttribute controllerAttribute = node.Attributes[ControllerAttribute];

        //if an enumeration is specified to identify views then make sure the ID is a valid enumeration member
        if (enumerationType != null)
        {
          XmlHelper.ValidateEnumerationMember(enumerationType, idAttribute.Value);
        }

        //create the view info
        ViewInfo viewInfo = new ViewInfo(idAttribute.Value);
        viewInfo.Type = Type.GetType(typeAttribute.Value);

        //the controller is optional
        if (controllerAttribute != null)
        {
          viewInfo.ControllerInfo = controllerInfos[controllerAttribute.Value];

          if (viewInfo.ControllerInfo == null)
          {
            _log.Error("Controller with ID '{0}' was not found for view with ID '{1}'.", controllerAttribute.Value, viewInfo.Id);
            ExceptionHelper.Throw("ParseViews.controllerNotFound", controllerAttribute.Value, viewInfo.Id);
          }
        }

        Debug.Assert(viewInfo.Type != null);
        _log.Verbose("Adding ViewInfo with ID '{0}', type '{1}'.", viewInfo.Id, viewInfo.Type.FullName);
        viewInfos.Add(viewInfo);

        //parse any custom configuration for the view
        viewInfo.CustomConfiguration = ParseCustomConfiguration(viewInfo.Type, configurationInfo, viewInfo, node);
      }
    }

    private static void ParseViewManagers(ConfigurationInfo configurationInfo, InfoCollection<Id<IViewManager>, ViewManagerInfo> viewManagerInfos, XmlNode xml)
    {
      Debug.Assert(configurationInfo != null);
      Debug.Assert(viewManagerInfos != null);
      Debug.Assert(xml != null);
      _log.Information("Parsing view manager configuration.");

      XmlNode viewsNode = xml.SelectSingleNode("viewManagers");
      XmlAttribute enumerationAttribute = viewsNode.Attributes[EnumerationAttribute];
      XmlNodeList nodeList = viewsNode.SelectNodes("viewManager");
      Type enumerationType = null;

      if (enumerationAttribute != null)
      {
        enumerationType = XmlHelper.GetEnumeration(enumerationAttribute.Value);
      }

      foreach (XmlNode node in nodeList)
      {
        XmlAttribute idAttribute = node.Attributes[IdAttribute];
        XmlAttribute typeAttribute = node.Attributes[TypeAttribute];

        //if an enumeration is specified to identify view managers then make sure the ID is a valid enumeration member
        if (enumerationType != null)
        {
          XmlHelper.ValidateEnumerationMember(enumerationType, idAttribute.Value);
        }

        //create the view manager info
        ViewManagerInfo viewManagerInfo = new ViewManagerInfo(idAttribute.Value);
        viewManagerInfo.Type = Type.GetType(typeAttribute.Value);
        Debug.Assert(viewManagerInfo.Type != null);
        _log.Verbose("Adding ViewManagerInfo with ID '{0}', type '{1}'.", viewManagerInfo.Id, viewManagerInfo.Type.FullName);
        viewManagerInfos.Add(viewManagerInfo);

        //parse any custom configuration for the view manager
        viewManagerInfo.CustomConfiguration = ParseCustomConfiguration(viewManagerInfo.Type, configurationInfo, viewManagerInfo, node);
      }
    }

    private static object ParseCustomConfiguration(Type type, ConfigurationInfo configurationInfo, object parent, XmlNode xml)
    {
      Debug.Assert(configurationInfo != null);
      Debug.Assert(type != null);
      Debug.Assert(xml != null);
      _log.Verbose("Parsing custom configuration for type '{0}'.", type.FullName);

      object[] attributes = type.GetCustomAttributes(typeof(XmlCustomParserAttribute), true);

      if (attributes.Length == 0)
      {
        _log.Error("No '{0}' attribute found on type '{1}' - custom configuration can not be parsed.", typeof(XmlCustomParserAttribute).FullName, type.FullName);
        ExceptionHelper.ThrowIf(xml.HasChildNodes, "ParseCustomConfiguration.customParserNotFound", type.FullName, typeof(XmlCustomParserAttribute).FullName);
        return null;
      }
      else
      {
        Debug.Assert(attributes.Length == 1);
        //get the attribute
        XmlCustomParserAttribute parserAttribute = (XmlCustomParserAttribute) attributes[0];
        //create the parser
        IXmlCustomParser customConfigurationParser = parserAttribute.GetXmlCustomParser();
        _log.Verbose("Custom parser of type '{0}' found and created - parsing custom configuration.", customConfigurationParser.GetType().FullName);
        //use the parser to parse the XML
        return customConfigurationParser.Parse(configurationInfo, parent, xml);
      }
    }

    private sealed class ValidateHelper
    {
      private StringBuilder _errorMessage;

      public string ErrorMessage
      {
        get
        {
          return _errorMessage.ToString();
        }
      }

      public bool HasErrors
      {
        get
        {
          return (_errorMessage != null);
        }
      }

      public void ValidationHandler(object sender, ValidationEventArgs e)
      {
        //we cater for the common case (no errors) by only creating the StringBuilder if necessary. Once XML errors are fixed they are
        //fixed and we reduce penalties by not creating the StringBuilder unless necessary
        if (_errorMessage == null)
        {
          _errorMessage = new StringBuilder();
        }

        _errorMessage.Append(" - ").Append(e.Message).Append(Environment.NewLine);
      }
    }
  }
}
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.