ExpatParser.cs :  » XML-Parsers » SAX.NET » Kds » Xml » Expat » 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 » XML Parsers » SAX.NET 
SAX.NET » Kds » Xml » Expat » ExpatParser.cs
/*
 * This software is licensed according to the "Modified BSD License",
 * where the following substitutions are made in the license template:
 * <OWNER> = Karl Waclawek
 * <ORGANIZATION> = Karl Waclawek
 * <YEAR> = 2004, 2005, 2006
 * It can be obtained from http://opensource.org/licenses/bsd-license.html.
 */

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.CompilerServices;

namespace Kds.Xml.Expat{
  /// <summary>Delegate for reading data from a pinned managed buffer.</summary>
  /// <remarks> It is the responsibility of the caller to free bufferHandle.</remarks>
  /// <param name="count">Number of bytes to read, that is, size of buffer.</param>
  /// <param name="bufferHandle">Pinned GCHandle for buffer, or "empty" handle
  /// if no data were read.</param>
  /// <returns>Number of bytes actually read. If 0, bufferHandle was not pinned.</returns>
  public delegate int ReadBuffer(int count, out GCHandle bufferHandle);

  /// <summary>Completion status of parsing process.</summary>
  public enum ParseStatus: byte
  {
    Finished,
    Suspended,
    FatalError,
    Aborted
  }

  /**<summary>Base class for exceptions in Expat.</summary> */
  public class ExpatException: ApplicationException
  {
    public ExpatException() { }

    public ExpatException(string message): base(message) { }

    public ExpatException(string message, Exception e): base(message, e) { }
  }

  /**<summary>Exception class for parse errors in Expat.</summary> */
  public class ExpatParseException: ExpatException
  {
    XMLError error;

    public ExpatParseException(XMLError error) {
      this.error = error;
    }

    public ExpatParseException(XMLError error, string message) : base(message) {
      this.error = error;
    }

    public ExpatParseException(XMLError error, string message, Exception e) : base(message, e) {
      this.error = error;
    }

    /// <summary>Expat error code.</summary>
    public XMLError Error {
      get { return error; }
    }

    public override string Message {
      get {
        string msg = base.Message;
        if (!string.IsNullOrEmpty(msg))
          msg += Environment.NewLine;
        msg += "Expat error: " + LibExpat.XMLErrorString(error) + ".";
        return msg;
      }
    }
  }

  /**<summary>Holds entity specific information while the entity
   * is being parsed. Can be subclassed to add more fields.</summary>
   * <remarks>Override the <see cref="Start"/>, <see cref="Finish"/>,
   * <see cref="Cleanup"/> and <see cref="Reset"/> methods accordingly.</remarks>
   */
  public unsafe class EntityParseContext<E, X, U>: CriticalFinalizerObject
    where E: EntityParseContext<E, X, U>, new()
    where X: ExpatParser<X, E, U>
    where U: class
  {
    // volatile, to better avoid access violations!
    private volatile XMLParser* xmlParser = null;
    private ReadBuffer read;

    internal E parent;
    internal X parser;
    internal XMLError error = XMLError.NONE;

    ~EntityParseContext()
    {
      // A finalizer thread and an application thread could be simultaneously
      // active if the object got resurrected!
      lock (this) {
        XMLParser* parser = xmlParser;
        if (parser != null) {
          LibExpat.XMLParserFree(parser);
          xmlParser = null;
        }
      }
    }

    /// <summary>Cleans up unmanaged resources.</summary>
    /// <remarks>Called at end of child entity, after <see cref="Finish"/> and
    /// before <see cref="Reset"/>. Gets called for root entity only when the
    /// <see cref="ExpatParser&lt;X, E, U>">ExpatParser</see> instance that owns
    /// it gets disposed.</remarks>
    protected internal virtual void Cleanup()
    {
      lock (this) {
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          XMLParser* parser = xmlParser;
          if (parser != null) {
            LibExpat.XMLParserFree(parser);
            xmlParser = null;
          }
        }
      }
      GC.SuppressFinalize(this);
    }

    // for use with root entity only
    internal void Init(ReadBuffer read)
    {
      Debug.Assert(read != null);
      this.read = read;
    }

    internal void InitAsParent(string encoding, bool namespaces)
    {
      XMLParser* parser;
      lock (this) {
        if (xmlParser != null)
          throw new ExpatException(ExpatParser<X, E, U>.InternalStateError);
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          if (namespaces)
            parser = LibExpat.XMLParserCreateNS(encoding, ExpatUtils.NSSep);
          else
            parser = LibExpat.XMLParserCreate(encoding);
          xmlParser = parser;
          GC.ReRegisterForFinalize(this);
        }
      }
      if (parser == null)
        throw new OutOfMemoryException();
    }

    internal void InitAsChild(XMLParser* parentParser, char* context, string encoding)
    {
      Debug.Assert(parentParser != null);
      XMLParser* parser;
      lock (this) {
        if (xmlParser != null)
          throw new ExpatException(ExpatParser<X, E, U>.InternalStateError);
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          parser = LibExpat.XMLExternalEntityParserCreate(parentParser, context, encoding);
          xmlParser = parser;
          GC.ReRegisterForFinalize(this);
        }
      }
      if (parser == null)
        throw new OutOfMemoryException();
    }

    /// <summary>Called when an external entity reference or a request for an external DTD
    /// subset is encountered. Must try to resolve the entity reference or DTD subset request
    /// and initialize the new entity context. Not implemented - must be overridden.</summary>
    /// <remarks>When returning <c>false</c>, then <c>read</c> and <c>encoding</c>
    /// will be set to <c>null</c>.</remarks>
    /// <param name="context">Expat specific parsing context structure.</param>
    /// <param name="baseUri">Base URI. Can be <c>null</c>.</param>
    /// <param name="systemId">System identifier for entity. <c>null</c> for foreign DTD.</param>
    /// <param name="publicId">Public identifier for entity.</param>
    /// <param name="read">Source for the entity's data.</param>
    /// <param name="encoding">Encoding of the entity. Can be <c>null</c>.</param>
    /// <returns><c>false</c>, if the entity should be skipped, <c>true</c> otherwise.</returns>
    protected virtual bool Start(
      char* context,
      char* baseUri,
      char* systemId,
      char* publicId,
      out ReadBuffer read,
      out string encoding)
    {
      read = null;
      encoding = null;
      return false;  // skip entity by default
    }

    /// <summary>The opposite to <see cref="Start"/> - override to perform steps
    /// required at end of external entity.</summary>
    /// <remarks>Called at end of parsing child entity, before <see cref="Cleanup"/> 
    /// and <see cref="Reset"/> are called, never called for root entity.</remarks>
    protected virtual void Finish()
    {
      // No-op by default. Override in derived class
    }

    // for use with (external) child entities only
    internal bool Open(
      char* context, char* baseUri, char* systemId, char* publicId, out string encoding)
    {
      bool result = Start(context, baseUri, systemId, publicId, out read, out encoding);
      Debug.Assert(!result || read != null);
      return result;
    }

    // for use with (external) child entities
    internal void Close()
    {
      Debug.Assert(Parent != null);
      try {
        Finish();
      }
      finally {
        Cleanup();
        Reset();
      }
    }

    /// <summary>Resets state of instance to be used again.</summary>
    /// <remarks>Called at end of parsing entity, after <see cref="Finish"/> and
    /// <see cref="Cleanup"/> are called. Also called for root entity, unlike
    /// <see cref="Finish"/> and <see cref="Cleanup"/>.</remarks>
    protected internal virtual void Reset()
    {
      read = null;
      error = XMLError.NONE;
    }

    internal XMLParser* XmlParser
    {
      get { return xmlParser; }
    }

    public X Parser
    {
      get { return parser; }
    }

    public ReadBuffer Read
    {
      get { return read; }
    }

    public E Parent
    {
      get { return parent; }
    }

    public XMLError Error
    {
      get { return error; }
    }
  }

  /**<summary>Object wrapper for Expat parser instance.</summary>
   * <remarks>Not thread-safe. Accessing an ExpatParser instance from multiple
   * threads can lead to memory access violations and leaks.</remarks>
   */
  public unsafe class ExpatParser<X, E, U>: IDisposable
    where X: ExpatParser<X, E, U>
    where E: EntityParseContext<E, X, U>, new()
    where U: class
  {
    private bool isDisposed = false;
    private bool isDone = false;
    /// <summary>GCHandle passed from unmanaged code to call-back functions,
    /// so that the call-back can access the ExpatParser instance.</summary>
    private GCHandle parserHandle;
    // stack of entity specific EntityParseContext instances, used while parsing
    private E entityContext;
    // linked list of unused EntityParseContext instances - must never be null
    private E freeEntityContext;

    private bool namespaces;
    private string encoding;
    private U userData;
    private Exception error = null;
    private bool skipInternal = false;
    private XMLParamEntityParsing paramEntityParsing = XMLParamEntityParsing.ALWAYS;

    public const string IllegalWhenParsing = "Illegal when parsing.";
    public const string InternalStateError = "Internal parser state error.";

    private E CreateEntityContext()
    {
      E result = new E();
      result.parser = (X)this;
      return result;
    }

    public ExpatParser(string encoding, bool namespaces, U userData)
    {
      freeEntityContext = CreateEntityContext();
      this.encoding = encoding;
      this.namespaces = namespaces;
      this.userData = userData;
      // Use a weak handle since the handle and the target have the same life-cycle
      this.parserHandle = GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection);
      // we always have a document entity parser instance
      PushDocumentEntityParseContext(encoding, namespaces);
    }

    ~ExpatParser()
    {
      Cleanup();
    }

    private void CheckNotDisposed()
    {
      if (isDisposed)
        throw new ObjectDisposedException(this.GetType().Name);
    }

    private void CheckNotParsing()
    {
      if (Parsing)
        throw new ExpatException(IllegalWhenParsing);
    }

    // this re-assigns all handlers except for the UnknownEncodingHandler
    protected void ReAssignHandlers(XMLParser* parser)
    {
      LibExpat.XMLSetElementDeclHandler(parser, elementDeclHandler);
      LibExpat.XMLSetAttlistDeclHandler(parser, attlistDeclHandler);
      LibExpat.XMLSetXmlDeclHandler(parser, xmlDeclHandler);
      LibExpat.XMLSetElementHandler(parser, startElementHandler, endElementHandler);
      LibExpat.XMLSetCharacterDataHandler(parser, characterDataHandler);
      LibExpat.XMLSetProcessingInstructionHandler(parser, processingInstructionHandler);
      LibExpat.XMLSetCommentHandler(parser, commentHandler);
      LibExpat.XMLSetCdataSectionHandler(parser, startCdataSectionHandler, endCdataSectionHandler);
      if (skipInternal)
        LibExpat.XMLSetDefaultHandler(parser, defaultHandler);
      else
        LibExpat.XMLSetDefaultHandlerExpand(parser, defaultHandler);
      LibExpat.XMLSetDoctypeDeclHandler(parser, startDoctypeDeclHandler, endDoctypeDeclHandler);
      LibExpat.XMLSetEntityDeclHandler(parser, entityDeclHandler);
      LibExpat.XMLSetNotationDeclHandler(parser, notationDeclHandler);
      LibExpat.XMLSetNamespaceDeclHandler(parser, startNamespaceDeclHandler, endNamespaceDeclHandler);
      LibExpat.XMLSetNotStandaloneHandler(parser, notStandaloneHandler);
      LibExpat.XMLSetSkippedEntityHandler(parser, skippedEntityHandler);
      // this is a private handler that always needs to be set
      LibExpat.XMLSetExternalEntityRefHandler(parser, externalEntityRefHandler);
    }

    // this sets all non-null handlers except for the UnknownEncodingHandler
    protected void ReInitializeHandlers(XMLParser* parser)
    {
      if (elementDeclHandler != null)
        LibExpat.XMLSetElementDeclHandler(parser, elementDeclHandler);
      if (attlistDeclHandler != null)
        LibExpat.XMLSetAttlistDeclHandler(parser, attlistDeclHandler);
      if (xmlDeclHandler != null)
        LibExpat.XMLSetXmlDeclHandler(parser, xmlDeclHandler);
      if (startElementHandler != null || endElementHandler != null)
        LibExpat.XMLSetElementHandler(parser, startElementHandler, endElementHandler);
      if (characterDataHandler != null)
        LibExpat.XMLSetCharacterDataHandler(parser, characterDataHandler);
      if (processingInstructionHandler != null)
        LibExpat.XMLSetProcessingInstructionHandler(parser, processingInstructionHandler);
      if (commentHandler != null)
        LibExpat.XMLSetCommentHandler(parser, commentHandler);
      if (startCdataSectionHandler != null || endCdataSectionHandler != null)
        LibExpat.XMLSetCdataSectionHandler(parser, startCdataSectionHandler, endCdataSectionHandler);
      if (defaultHandler != null) {
        if (skipInternal)
          LibExpat.XMLSetDefaultHandler(parser, defaultHandler);
        else
          LibExpat.XMLSetDefaultHandlerExpand(parser, defaultHandler);
      }
      if (startDoctypeDeclHandler != null || endDoctypeDeclHandler != null)
        LibExpat.XMLSetDoctypeDeclHandler(parser, startDoctypeDeclHandler, endDoctypeDeclHandler);
      if (entityDeclHandler != null)
        LibExpat.XMLSetEntityDeclHandler(parser, entityDeclHandler);
      if (notationDeclHandler != null)
        LibExpat.XMLSetNotationDeclHandler(parser, notationDeclHandler);
      if (startNamespaceDeclHandler != null || endNamespaceDeclHandler != null)
        LibExpat.XMLSetNamespaceDeclHandler(parser, startNamespaceDeclHandler, endNamespaceDeclHandler);
      if (notStandaloneHandler != null)
        LibExpat.XMLSetNotStandaloneHandler(parser, notStandaloneHandler);
      if (skippedEntityHandler != null)
        LibExpat.XMLSetSkippedEntityHandler(parser, skippedEntityHandler);
      // this is a private handler that always needs to be set
      LibExpat.XMLSetExternalEntityRefHandler(parser, externalEntityRefHandler);
    }

    private void Reset()
    {
      error = null;
      while (entityContext.Parent != null)
        PopChildEntityParseContext();
      // now reset root entity context
      entityContext.Reset();
    }

    private XMLParser* ResetParser()
    {
      Reset();
      XMLParser* parser = entityContext.XmlParser;
      if (parser != null && LibExpat.XMLParserReset(parser, encoding) == XMLBool.FALSE)
        throw new ExpatException(InternalStateError);
      // all handlers have been cleared (except for unknownEncodingHandler)
      LibExpat.XMLSetUserData(parser, (IntPtr)parserHandle);
      ReInitializeHandlers(parser);
      LibExpat.XMLSetParamEntityParsing(parser, paramEntityParsing);
      return parser;
    }

    public void Reset(string encoding, bool namespaces, bool nsTriplets)
    {
      CheckNotParsing();
      this.encoding = encoding;
      XMLParser* parser;
      if (this.namespaces != namespaces) {
        Reset();
        this.namespaces = namespaces;
        entityContext.Cleanup();
        entityContext.InitAsParent(encoding, namespaces);
        parser = entityContext.XmlParser;
        ConfigureParentParser(parser);
        ReInitializeHandlers(parser);
        LibExpat.XMLSetParamEntityParsing(parser, paramEntityParsing);
      }
      else
        parser = ResetParser();
      LibExpat.XMLSetReturnNSTriplet(parser, nsTriplets ? 1 : 0);
      isDone = false;
    }

    public bool Parsing
    {
      get {
        CheckNotDisposed();
        XMLParsingStatus status;
        LibExpat.XMLGetParsingStatus(entityContext.XmlParser, out status);
        bool parsing = status.Parsing == XMLParsing.PARSING || status.Parsing == XMLParsing.SUSPENDED;
        return !isDone && parsing;
      }
    }

    public U UserData
    {
      get {
        CheckNotDisposed();
        return userData; 
      }
      set {
        CheckNotDisposed();
        userData = value;
      }
    }

    public E EntityContext
    {
      get { return entityContext; }
    }

    public Exception Error
    {
      get { return error; }
    }

    public bool Namespaces
    {
      get { return namespaces; }
    }

    public string Encoding
    {
      get { return encoding; }
    }

    public byte* GetInputContext(ref int offset, ref int size)
    {
      return LibExpat.XMLGetInputContext(entityContext.XmlParser, ref offset, ref size);
    }

    public long CurrentLineNumber
    {
      get { return unchecked((long)LibExpat.XMLGetCurrentLineNumber(entityContext.XmlParser)); }
    }

    public long CurrentColumnNumber
    {
      get { return unchecked((long)LibExpat.XMLGetCurrentColumnNumber(entityContext.XmlParser)); }
    }

    public bool SetParamEntityParsing(XMLParamEntityParsing peParsing)
    {
      bool result = LibExpat.XMLSetParamEntityParsing(entityContext.XmlParser, peParsing) != 0;
      if (result)
        paramEntityParsing = peParsing;
      return result;
    }

    public bool UseForeignDtd
    {
      set {
        LibExpat.XMLUseForeignDTD(entityContext.XmlParser, value ? XMLBool.TRUE : XMLBool.FALSE);
      }
    }

    public bool NSTriplets
    {
      set { LibExpat.XMLSetReturnNSTriplet(entityContext.XmlParser, value ? 1 : 0); }
    }

    public int SpecifiedAttributeCount
    {
      get { return LibExpat.XMLGetSpecifiedAttributeCount(entityContext.XmlParser); }
    }

    public void FreeContentmodel(XMLContent* model)
    {
      LibExpat.XMLFreeContentModel(entityContext.XmlParser, model);
    }

    public XMLParsingStatus ParsingStatus
    {
      get {
        XMLParsingStatus result;
        LibExpat.XMLGetParsingStatus(entityContext.XmlParser, out result);
        return result;
      }
    }

    private void PushEntityParseContext()
    {
      E parentContext = entityContext;
      E newContext = freeEntityContext;
      if (newContext.Parent == null)
        freeEntityContext = CreateEntityContext();  // make sure it is never null
      else
        freeEntityContext = newContext.Parent;
      entityContext = newContext;
      entityContext.parent = parentContext;
    }

    private void ConfigureParentParser(XMLParser* xmlParser)
    {
      if (namespaces)
        LibExpat.XMLSetReturnNSTriplet(xmlParser, 1);
      LibExpat.XMLSetUserData(xmlParser, (IntPtr)parserHandle);
      // this is a private handler that always needs to be set
      LibExpat.XMLSetExternalEntityRefHandler(xmlParser, externalEntityRefHandler);
    }

    // call only once
    private void PushDocumentEntityParseContext(string encoding, bool namespaces)
    {
      PushEntityParseContext();
      entityContext.InitAsParent(encoding, namespaces);
      ConfigureParentParser(entityContext.XmlParser);
    }

    private void PushChildEntityParseContext(
      XMLParser* parentParser, char* context, string encoding)
    {
      PushEntityParseContext();
      try {
        entityContext.InitAsChild(parentParser, context, encoding);
      }
      catch {
        PopChildEntityParseContext();
        throw;
      }
    }

    // Precondition: entityContext != null! Not checked!
    private void PopChildEntityParseContext()
    {
      E parentContext = freeEntityContext;
      freeEntityContext = entityContext;
      entityContext = entityContext.Parent;
      // clean up "popped" instance - after entityContext has changed
      freeEntityContext.parent = parentContext;
      freeEntityContext.Close();
      /* In expat, a child parser (for parsing external references) will
         * inherit all context and handlers from the parent, but will not
         * give changes back to the parent parser. This is a problem when
         * handlers or configuration items have changed. Therefore, we have
         * to track those changes and update the configuration of the parent
         * parser when returning from parsing an external entity.
         */
      ReAssignHandlers(entityContext.XmlParser);
    }

    private void Cleanup()
    {
      if (parserHandle.IsAllocated)
        parserHandle.Free();
    }

    /* IDisposable */

    // do not use instance after calling this
    public void Dispose()
    {
      if (isDisposed)
        return;
      // clean up left-over EntityParseContext instances
      E entCtx = entityContext;
      while (entCtx != null) {
        entCtx.Cleanup();
        entCtx = entCtx.Parent;
      }
      entityContext = entCtx;

      Cleanup();
      // resources cleaned up - no need to have object finalized
      GC.SuppressFinalize(this);
      isDisposed = true;
    }

    /* SkipInternal */

    public bool SkipInternal
    {
      get { return skipInternal; }
    }

    /* ElementDeclHandler */

    private XMLElementDeclHandler elementDeclHandler;

    /// <summary>Handler for element declarations.</summary>
    public XMLElementDeclHandler ElementDeclHandler
    {
      get { return elementDeclHandler; }
      set {
        CheckNotDisposed();
        elementDeclHandler = value;
        LibExpat.XMLSetElementDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* AttlistDeclHandler */

    private XMLAttlistDeclHandler attlistDeclHandler;

    /// <summary>Handler for attribute list declarations.</summary>
    public XMLAttlistDeclHandler AttlistDeclHandler
    {
      get { return attlistDeclHandler; }
      set {
        CheckNotDisposed();
        attlistDeclHandler = value;
        LibExpat.XMLSetAttlistDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* XmlDeclHandler */

    private XMLXmlDeclHandler xmlDeclHandler;

    /// <summary>Handler for XML declarations.</summary>
    public XMLXmlDeclHandler XmlDeclHandler
    {
      get { return xmlDeclHandler; }
      set {
        CheckNotDisposed();
        xmlDeclHandler = value;
        LibExpat.XMLSetXmlDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* StartElementHandler */

    private XMLStartElementHandler startElementHandler;

    /// <summary>Handler for start tag.</summary>
    public XMLStartElementHandler StartElementHandler
    {
      get { return startElementHandler; }
      set {
        CheckNotDisposed();
        startElementHandler = value;
        LibExpat.XMLSetStartElementHandler(entityContext.XmlParser, value);
      }
    }

    /* EndElementHandler */

    private XMLEndElementHandler endElementHandler;

    /// <summary>Handler for end tag.</summary>
    public XMLEndElementHandler EndElementHandler
    {
      get { return endElementHandler; }
      set {
        CheckNotDisposed();
        endElementHandler = value;
        LibExpat.XMLSetEndElementHandler(entityContext.XmlParser, value);
      }
    }

    /* CharacterDataHandler */

    private XMLCharacterDataHandler characterDataHandler;

    /// <summary>Handler for character data.</summary>
    public XMLCharacterDataHandler CharacterDataHandler
    {
      get { return characterDataHandler; }
      set {
        CheckNotDisposed();
        characterDataHandler = value;
        LibExpat.XMLSetCharacterDataHandler(entityContext.XmlParser, value);
      }
    }

    /* ProcessingInstructionHandler */

    private XMLProcessingInstructionHandler processingInstructionHandler;

    /// <summary>Handler for processing instructions.</summary>
    public XMLProcessingInstructionHandler ProcessingInstructionHandler
    {
      get { return processingInstructionHandler; }
      set {
        CheckNotDisposed();
        processingInstructionHandler = value;
        LibExpat.XMLSetProcessingInstructionHandler(entityContext.XmlParser, value);
      }
    }

    /* CommentHandler */

    private XMLCommentHandler commentHandler;

    /// <summary>Handler for comments.</summary>
    public XMLCommentHandler CommentHandler
    {
      get { return commentHandler; }
      set {
        CheckNotDisposed();
        commentHandler = value;
        LibExpat.XMLSetCommentHandler(entityContext.XmlParser, value);
      }
    }

    /* StartCdataSectionHandler */

    private XMLStartCdataSectionHandler startCdataSectionHandler;

    /// <summary>Handler for start of CDATA section.</summary>
    public XMLStartCdataSectionHandler StartCdataSectionHandler
    {
      get { return startCdataSectionHandler; }
      set {
        CheckNotDisposed();
        startCdataSectionHandler = value;
        LibExpat.XMLSetStartCdataSectionHandler(entityContext.XmlParser, value);
      }
    }

    /* EndCdataSectionHandler */

    private XMLEndCdataSectionHandler endCdataSectionHandler;

    /// <summary>Handler for end of CDATA section.</summary>
    public XMLEndCdataSectionHandler EndCdataSectionHandler
    {
      get { return endCdataSectionHandler; }
      set {
        CheckNotDisposed();
        endCdataSectionHandler = value;
        LibExpat.XMLSetEndCdataSectionHandler(entityContext.XmlParser, value);
      }
    }

    /* DefaultHandler */

    private XMLDefaultHandler defaultHandler;

    /// <summary>Handler for unhandled events.</summary>
    public XMLDefaultHandler DefaultHandler
    {
      get { return defaultHandler; }
    }

    /// <summary>Handler for unhandled events.</summary>
    /// <remarks>Will optionally skip internal entities.</remarks>
    public void SetDefaultHandler(XMLDefaultHandler value, bool skipInternal)
    {
      CheckNotDisposed();
      defaultHandler = value;
      this.skipInternal = skipInternal;
      if (skipInternal)
        LibExpat.XMLSetDefaultHandler(entityContext.XmlParser, value);
      else
        LibExpat.XMLSetDefaultHandlerExpand(entityContext.XmlParser, value);
    }

    /* StartDoctypeDeclHandler */

    private XMLStartDoctypeDeclHandler startDoctypeDeclHandler;

    /// <summary>Handler for start of Doctype declaration.</summary>
    public XMLStartDoctypeDeclHandler StartDoctypeDeclHandler
    {
      get { return startDoctypeDeclHandler; }
      set {
        CheckNotDisposed();
        startDoctypeDeclHandler = value;
        LibExpat.XMLSetStartDoctypeDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* EndDoctypeDeclHandler */

    private XMLEndDoctypeDeclHandler endDoctypeDeclHandler;

    /// <summary>Handler for end of Doctype declaration.</summary>
    public XMLEndDoctypeDeclHandler EndDoctypeDeclHandler
    {
      get { return endDoctypeDeclHandler; }
      set {
        CheckNotDisposed();
        endDoctypeDeclHandler = value;
        LibExpat.XMLSetEndDoctypeDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* EntityDeclHandler */

    private XMLEntityDeclHandler entityDeclHandler;

    /// <summary>Handler for entity declarations.</summary>
    public XMLEntityDeclHandler EntityDeclHandler
    {
      get { return entityDeclHandler; }
      set {
        CheckNotDisposed();
        entityDeclHandler = value;
        LibExpat.XMLSetEntityDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* NotationDeclHandler */

    private XMLNotationDeclHandler notationDeclHandler;

    /// <summary>Handler for notation declarations.</summary>
    public XMLNotationDeclHandler NotationDeclHandler
    {
      get { return notationDeclHandler; }
      set {
        CheckNotDisposed();
        notationDeclHandler = value;
        LibExpat.XMLSetNotationDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* StartNamespaceDeclHandler */

    private XMLStartNamespaceDeclHandler startNamespaceDeclHandler;

    /// <summary>Handler called when namespace scope starts.</summary>
    public XMLStartNamespaceDeclHandler StartNamespaceDeclHandler
    {
      get { return startNamespaceDeclHandler; }
      set {
        CheckNotDisposed();
        startNamespaceDeclHandler = value;
        LibExpat.XMLSetStartNamespaceDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* EndNamespaceDeclHandler */

    private XMLEndNamespaceDeclHandler endNamespaceDeclHandler;

    /// <summary>Handler called when namespace scope ends.</summary>
    public XMLEndNamespaceDeclHandler EndNamespaceDeclHandler
    {
      get { return endNamespaceDeclHandler; }
      set {
        CheckNotDisposed();
        endNamespaceDeclHandler = value;
        LibExpat.XMLSetEndNamespaceDeclHandler(entityContext.XmlParser, value);
      }
    }

    /* NotStandaloneHandler */

    private XMLNotStandaloneHandler notStandaloneHandler;

    /// <summary>Handler called when document is not "standalone".</summary>
    /// <remarks>If this handler returns XMLStatus.ERROR, then
    /// the parser will return an XMLError.NOT_STANDALONE error.</remarks>
    public XMLNotStandaloneHandler NotStandaloneHandler
    {
      get { return notStandaloneHandler; }
      set {
        CheckNotDisposed();
        notStandaloneHandler = value;
        LibExpat.XMLSetNotStandaloneHandler(entityContext.XmlParser, value);
      }
    }

    /* SkippedEntityHandler */

    private XMLSkippedEntityHandler skippedEntityHandler;

    /// <summary>Handler called when an entity reference needed to be skipped.</summary>
    public XMLSkippedEntityHandler SkippedEntityHandler
    {
      get { return skippedEntityHandler; }
      set {
        CheckNotDisposed();
        skippedEntityHandler = value;
        LibExpat.XMLSetSkippedEntityHandler(entityContext.XmlParser, value);
      }
    }

    /// <summary>This references keeps the ExternalEntityRefHandlerImpl delegate from
    /// being garbage collected.</summary>
    private XMLExternalEntityRefHandler externalEntityRefHandler = ExternalEntityRefHandlerImpl;

    /// <summary>Handler called by Expat when an external entity needs to be parsed.</summary>
    /// <remarks>If entities are nested, this handler may be called recursively.</remarks>
    private static int
    ExternalEntityRefHandlerImpl(XMLParser* parser,
                                 char* context,
                                 char* baseUri,
                                 char* systemId,
                                 char* publicId)
    {
      int result = (int)XMLStatus.OK;

      GCHandle parserHandle = (GCHandle)LibExpat.XMLGetUserData(parser);
      X expatParser = (X)parserHandle.Target;

      E newEntityContext = expatParser.freeEntityContext;  // should always be != null
      string encoding;
      try {
        if (!newEntityContext.Open(context, baseUri, systemId, publicId, out encoding))
          return result;
      }
      catch (Exception e) {
        expatParser.error = e;
        result = (int)XMLStatus.ERROR;
        return result;
      }

      expatParser.PushChildEntityParseContext(parser, context, encoding);

      ParseStatus status = ExpatUtils.Parse(
        newEntityContext.XmlParser, newEntityContext.Read, out newEntityContext.error);

      switch (status) {
        case ParseStatus.Finished:
          expatParser.PopChildEntityParseContext();
          break;
        case ParseStatus.Suspended:
          // must suspend parent parser as well - don't check return value
          LibExpat.XMLStopParser(parser, XMLBool.TRUE);
          break;
        case ParseStatus.FatalError:
          result = (int)XMLStatus.ERROR;
          break;
        case ParseStatus.Aborted:
          // must abort parent parser as well - don't check return value
          LibExpat.XMLStopParser(parser, XMLBool.FALSE);
          break;
      }
      return result;
    }

    /* UnknownEncodingHandler */

    private XMLUnknownEncodingHandler unknownEncodingHandler;

    public void SetUnknownEncodingHandler(XMLUnknownEncodingHandler handler,
                                          IntPtr encodingHandlerData)
    {
      CheckNotParsing();
      unknownEncodingHandler = handler;
      LibExpat.XMLSetUnknownEncodingHandler(
        entityContext.XmlParser, handler, encodingHandlerData);
    }

    /* Main Parse method */

    public ParseStatus Parse(ReadBuffer read)
    {
      CheckNotParsing();
      XMLParser* parser;
      if (isDone) {
        parser = ResetParser();
        isDone = false;
      }
      else 
        parser = entityContext.XmlParser;
      entityContext.Init(read);
      ParseStatus status;
      try {
        status = ExpatUtils.Parse(parser, read, out entityContext.error);
        isDone = status != ParseStatus.Suspended;
      }
      catch {
        isDone = true;
        throw;
      }
      return status;
    }

    /* Resumable features */

#if EXPAT_1_95_8_UP
    public bool Suspend()
    {
      CheckNotDisposed();
      bool result;
      XMLParser* parser = entityContext.XmlParser;
      result = LibExpat.XMLStopParser(parser, XMLBool.TRUE) == XMLStatus.OK;
      if (result)
        entityContext.error = XMLError.NONE;
      else
        entityContext.error = LibExpat.XMLGetErrorCode(parser);
      return result;
    }

    public bool Abort()
    {
      CheckNotDisposed();
      bool result;
      XMLParser* parser = entityContext.XmlParser;
      // no cleanup here, as we are in the middle of a call-back
      result = LibExpat.XMLStopParser(parser, XMLBool.FALSE) == XMLStatus.OK;
      if (result)
        entityContext.error = XMLError.NONE;
      else
        entityContext.error = LibExpat.XMLGetErrorCode(parser);
      return result;
    }

    public ParseStatus Resume()
    {
      CheckNotDisposed();
      ParseStatus status;
      for (; ; ) {
        XMLParser* parser = entityContext.XmlParser;
        try {
          status = ExpatUtils.ResumeParsing(parser, entityContext.Read, out entityContext.error);
          isDone = status != ParseStatus.Suspended;
        }
        catch {
          isDone = true;
          throw;
        }
        if (status == ParseStatus.Finished && entityContext.Parent != null)
          PopChildEntityParseContext();  // return to parsing parent entity
        else
          break;
      }
      return status;
    }
#endif
  }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.