/*
 * (C) Copyright IBM Corp. 1998  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package com.ibm.xml.parser;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;

import org.xml.sax.EntityResolver;
import org.xml.sax.DocumentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.HandlerBase;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.LocatorImpl;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.w3c.dom.Notation;

/**
 * <p>This is the <a href="http://www.megginson.com/SAX/">Simple API for XML (SAX)</a> 1.0
 * driver for IBM XML4J.  It implements the Parser and AttributeInterfaces as defined
 * by SAX, and the ErrorListener, TagHandler, and StreamProducer interfaces defined by XML4J.
 * <p>The Parser interface allows applications to register handlers for different 
 * types of events and to initiate a parse from a URI, or a character stream.
 * <p>The AttributeList interface enables XML4J to pass an instance to the SAX application 
 * as the second argument of each <var>startElement</var> event.  The instance provided will 
 * return valid results only during the scope of the <var>startElement</var> invocation.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see org.xml.sax.Parser
 * @see org.xml.sax.AttributeList
 * @see com.ibm.xml.parser.DefaultElementFactory
 */
public class SAXDriver extends DefaultElementFactory
    implements org.xml.sax.Parser,
               org.xml.sax.AttributeList,
               ErrorListener,
               TagHandler,
               StreamProducer {

   /**
    * Wrap SAXExceptions and any other exceptions thrown from the SAXDriver.
    */
    class ExceptionWrapper extends RuntimeException {
        
        SAXException    wrapped =   null;
        
         /**
          * Constructor.  Create a new ExceptionWrapper from an existing exception.
          * @param e    Exception to be wrapped.
          */
        ExceptionWrapper(SAXException e) {
            super(e.getMessage());
            this.wrapped = e;
        }
        
        /**
         * Returns the message from the embedded exception. 
         * @return          The error or warning message.
         */
        public String getMessage() {
            return this.wrapped.getMessage();
        }
    }

    static  HandlerBase     s_defaulthandler =  new HandlerBase();
            Parser          parser          =   null;
            Locale          locale          =   null;
            EntityResolver  entityHandler   =   s_defaulthandler;
            DTDHandler      dtdHandler      =   s_defaulthandler;
            DocumentHandler documentHandler =   s_defaulthandler;
            ErrorHandler    errorHandler    =   s_defaulthandler;
            TXAttribute[]   attributes      =   null;
            DTD             dtd             =   null;
            String          currentSystemID =   null;
            java.util.Stack stack           =   new java.util.Stack();
            int             depth           =   0;

   /**
     * Sets a locale for errors and warnings.  Locale changes are not supported in
     * the middle of a parse.
     * <p>This method is defined by SAX.
     * @param   locale  A Java Locale object.
     * @exception SAXException Throws an exception, using the previous or default
     *                         locale, if the requested locale is not supported.
     * @see org.xml.sax.SAXException
     * @see org.xml.sax.SAXParseException
     */
    public void setLocale(Locale locale) throws SAXException {
        this.locale = locale;
        if (null != this.parser)  this.parser.setLocale(locale);
    }

   /**
     * Register a custom entity resolver.  If no entity resolver is registered, XML4J
     * itself will resolve all system identifiers and open connections to entities. 
     * Applications may register a new or different entity resolver
     * in the middle of a parse, and XML4J will begin using the new resolver immediately.
     * <p>This method is defined by SAX.
     * @param   handler     The entity hander.
     * @see EntityResolver
     * @see HandlerBase
     */
    public void setEntityResolver(EntityResolver handler) {
        this.entityHandler = handler;
    }

   /**
     * Register a DTD event handler.  If no DTD handler is registered, XML4J will
     * silently ignore all DTD events.  Applications may register a new or different
     * handler in the middle of a parse, and XML4J will begin using the new handler 
     * immediately.
     * <p>This method is defined by SAX.
     * @param   handler The DTD handler.
     * @see DTDHandler
     * @see HandlerBase
     */
    public void setDTDHandler(DTDHandler handler) {
        this.dtdHandler = handler;
    }

   /**
     * Register a document event handler.  If no document handler is registered,  XML4J will
     * silently ignore all document events.
     * Applications may register a new or different handler in the
     * middle of a parse, and XML4J will begin using the new handler immediately.
     * <p>This method is defined by SAX.
     * @param    handler     The document handler.
     * @see DocumentHandler
     * @see HandlerBase
     */
    public void setDocumentHandler(DocumentHandler handler) {
        this.documentHandler = handler;
    }

   /**
     * Register an error event handler.  If no error handler is registered, XML4J will
     * silently ignore all error events except for fatalError, which will throw a 
     * SAXException.  Applications may register a new or different handler in the
     * middle of a parse, and XML4J will begin using the new handler immediately.
     * <p>This method is defined by SAX.
     * @param   handler     The error handler.
     * @see ErrorHandler
     * @see SAXException
     * @see HandlerBase
     */
    public void setErrorHandler(ErrorHandler handler) {
        this.errorHandler = handler;
    }

   /**
     * Begin parsing an XML document from any valid input source (a character stream or 
     * a byte stream).  Applications may not invoke this method while a parse is 
     * in progress (they should create a new Parser instead for each additional XML document).
     * Once a parse is complete, an application may reuse the same Parser object, possibly 
     * with a different input source.
     * <p>This method is defined by SAX.
     * @param   source  The input source for the top-level of the XML document.
     * @exception SAXException Any SAX exception, possibly wrapping another exception.
     * @see org.xml.sax.InputSource
     * @see #parse(java.lang.String)
     * @see #setEntityResolver
     * @see #setDTDHandler
     * @see #setDocumentHandler
     * @see #setErrorHandler
     */
    public void parse(InputSource isrc) throws SAXException {
        this.currentSystemID = isrc.getSystemId();
        this.documentHandler.startDocument();
        try {
            this.parser = new Parser(this.currentSystemID, this, this);
            if (null != this.locale)  this.parser.setLocale(this.locale);
            this.parser.setElementFactory(this);
            this.parser.setTagHandler(this);
            this.stack.push(this.currentSystemID);
            if (null != isrc.getByteStream()) {
                this.parser.readStream(isrc.getByteStream());
            } else if (null != isrc.getCharacterStream()) {
                this.parser.readStream(isrc.getCharacterStream());
            } else {
                this.parser.readStream(getInputStream(this.currentSystemID, null, this.currentSystemID));
            }
        } catch (ExceptionWrapper ew) {
            this.parser = null;
            throw ew.wrapped;
        } catch (Exception e) {
            this.parser = null;
            throw new SAXException(e);
        }
        this.documentHandler.endDocument();
        this.parser = null;
        this.locale = null;
        this.entityHandler = null;
        this.dtdHandler = null;
        this.documentHandler = null;
        this.errorHandler = null;
        this.attributes = null;
        this.dtd = null;
        this.currentSystemID = null;
        this.stack = null;
    }
    
   /**
     * Begin parsing an XML document from a system identifier (URI).  This method is a 
     * shortcut for the common case of reading a document from a system identifier.  It is 
     * the exact equivalent of the following:
     * <BLOCKQUOTE><PRE>parse(new InputSource(systemId));</PRE></BLOCKQUOTE>
     * <p>If the system identifier is a URL, it must be fully resolved by the application before
     * it is passed to XML4J.
     * <p>This method is defined by SAX.
     * @param   systemId    The system identifier (URI).
     * @exception SAXException Any SAX exception, possibly wrapping another exception.
     * @see #parse(org.xml.sax.InputSource)
     */
    public void parse(String systemId) throws SAXException {
        parse(new InputSource(systemId));
    }

   /**
     * Return the number of attributes in this list.
     * <p>This method is defined by SAX.
     * @return The number of attributes in the list.
     */
    public int getLength() {
        return this.attributes.length;
    }

   /**
     * Return the name of an attribute in this list according to the specified index <var>i</var>.
     * If the attribute name has a namespace prefix, the prefix will still be attached.
     * <p>This method is defined by SAX.
     * @param    i   The index of the attribute in the list (starting at 0).
     * @return       The name of the indexed attribute, or <var>null</var>
     *               if the index is out of range.
     * @see #getLength
     */
    public String getName(int i) {
        return this.attributes[i].getName();
    }

   /**
     * Return the type of an attribute in the list according to the specified index <var>i</var>.
     * <p>The attribute type is one of the strings "CDATA", "ID",
     * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
     * or "NOTATION" (always in upper case).
     * If the parser has not read a declaration for the attribute,
     * the value "CDATA" is returned.  For an enumerated attribute that is not a notation, the
     * parser will report the type as "NMTOKEN".
     * <p>This method is defined by SAX.
     * @param   i   The index of the attribute in the list (starting at 0).
     * @return      The attribute type as a string, or <var>null</var> if the index is out of range.
     * @see #getLength
     * @see #getType(java.lang.String)
     */
    public String getType(int i) {
        int t = this.attributes[i].getType();
        if (AttDef.UNKNOWN == t)  t = AttDef.CDATA;
        return AttDef.S_TYPESTR[t];
    }

    /**
     * Return the type of an attribute in the list according to the specified <var>name</var>.
     * The return value is the same as the return value for getType(int).
     * If the attribute name has a namespace prefix, the prefix must be attached.
     * <p>This method is defined by SAX.
     * @param   name    The name of the attribute.
     * @return          The attribute type as a string, or <var>null</var> if no such attribute exists.
     * @see #getType(int)
     */
    public String getType(String name) {
        int index = searchAttribute(name);
        return 0 > index ? null : getType(index);
    }

   /**
     * Return the value of an attribute in the list according to the specified
     * index <var>i</var>.  If the attribute value is a list of tokens (IDREFS,
     * ENTITIES, or NMTOKENS), the tokens will be concatenated
     * into a single string separated by whitespace.
     * <p>This method is defined by SAX.
     * @param   i   The index of the attribute in the list (starting at 0).
     * @return      The attribute value as a string, or <var>null</var> if the index is out
     *              of range.
     * @see #getLength
     * @see #getValue(java.lang.String)
     */
    public String getValue(int i) {
        return this.attributes[i].getValue();
    }

   /**
     * Return the value of an attribute in the list according to the specified <var>name</var>.
     * The return value is the same as the return value for getValue(int).
     * If the attribute name has a namespace prefix, the prefix must be attached.
     * <p>This method is defined by SAX.
     * @param   i   The index of the attribute in the list.
     * @return      The attribute value as a string, or <var>null</var> if no such attribute
     *              exists.
     * @see #getValue(int)
     */
    public String getValue(String name) {
        int ind = searchAttribute(name);
        return 0 > ind ? null : getValue(ind);
    }

   /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Returns the source of the input stream (could be a character stream or a byte stream) 
     * based on the entity specified by the system ID and/or public ID.
     * Any registered entity handler will be invoked to resolve the entity specified by the
     * system and public IDs.
     * <p>This method is defined by StreamProducer.
     * @param   name        CURRENTLY NOT IMPLEMENTED.
     * @param   publicID    Entity's public ID, or <var>null</var> if no public ID (see ExternalID for details).    
     * @param   systemID    Entity's system ID.    
     * @return              The resolved source of the input stream, or <var>null</var> if unable to resolve.
     * @exception IOException   Thrown if unable to open the source defined by the specified IDs.
     * @exception RuntimeException Thrown if the <code>entityHandler</code> is unable to resolve the entity from the specified IDs.
     * @see com.ibm.xml.parser.ExternalID
     * @see #closeInputStream
     */
    public Source getInputStream(String name, String publicID, String systemID)
        throws IOException, RuntimeException {
        if (null == systemID)  return null;
        try {
            InputSource isrc = this.entityHandler.resolveEntity(publicID, systemID);
            URL u = new URL(new URL(this.currentSystemID), systemID);
            this.stack.push(this.currentSystemID = u.toString());
            if (null == isrc || null != isrc.getSystemId()) {
                return new Source(u.openStream());
            } else if (null != isrc.getByteStream()) {
                return new Source(isrc.getByteStream());
            } else {
                return new Source(isrc.getCharacterStream());
            }
        } catch (MalformedURLException e1) {
            return new Source(new FileInputStream(systemID));
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
    }
    
   /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Removes the input stream currently in use. 
     * <p>This method is defined by StreamProducer.
     * @param   source      CURRENTLY NOT IMPLEMENTED.
     * @see #getInputStream
     */
    public void closeInputStream(Source source) {
        try {
            this.currentSystemID = (String)this.stack.pop();
        } catch (Exception e) {
        }
    }

    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Listen for XML4J parser errors, and reports these errors through any 
     * registered SAX error handler.
     * @param   fileName    Processing file name, or <var>null</var> if error or warning
     *                      occurs in a document.
     * @param   lineNo      The line number where the current document event ends, or 
     *                      <var>-1</var> if not available.  Note that this is the line 
     *                      position of the first character after the text associated with 
     *                      the document event.  Do NOT trust this value when the input 
     *                      stream includes invalid octet as its encoding.
     * @param   charOffset  The column number where the current document event ends, or
     *                      <var>-1</var> if not available.  Note that this is the column 
     *                      number of the first character after the text associated with the 
     *                      document event.  The first column in a line is position 1.  Do NOT
     *                      trust this value when the input stream includes invalid octet as its encoding.
     * @param   key         The object may be an instance of <code>String</code> or <code>Exception</code>.
     *                      When this object is <code>String</code>, this value may help 
     *                      classify <var>msg</var> as an error (begins with "E_") or warning
     *                      (begins with "W_").
     * @param   msg         The error or warning message.
     * <p>This method is defined by ErrorListener.
     * @exception RuntimeException Thrown if the <code>errorHandler</code> fails.
     * @see #setErrorHandler
     */
    public void error(String fileName, int lineNo, int charOffset, Object key, String msg)
        throws RuntimeException {
        LocatorImpl loc = new LocatorImpl();
        loc.setSystemId(fileName);
        loc.setLineNumber(lineNo);
        loc.setColumnNumber(charOffset);
        SAXParseException spe = new SAXParseException(msg, loc);
        try {
            if (key instanceof String && ((String)key).startsWith("W_")) {
                this.errorHandler.warning(spe);
            } else
                this.errorHandler.error(spe);
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
    }

    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a start tag (and any attributes)
     * has been parsed.  This method will notify any registered document handlers.
     * <p>This method is defined by TagHandler.
     * @param   element TXElement that was parsed.
     * @param   empty   Not used by this method.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     * @see #handleEndTag
     */
    public void handleStartTag(TXElement element, boolean empty) throws RuntimeException {
        this.depth ++;
        this.attributes = element.getAttributeArray();
        try {
            this.documentHandler.startElement(element.getName(), this);
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
        this.attributes = null;
    }
 
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that an end tag
     * has been parsed.  This method will notify any registered document handlers.
     * <p>This method is defined by TagHandler.
     * @param   el      TXElement that was parsed.
     * @param   empty   Not used by this method.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     * @see #handleStartTag
     */
    public void handleEndTag(TXElement el, boolean empty) throws RuntimeException {
        this.depth --;
        try {
            this.documentHandler.endElement(el.getName());
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
    }

    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a DTD has been parsed.
     * <p>This method is defined by ElementFactory.
     * @param   rootElementName Name of the root Element to associate with the DTD.
     * @param   externalID      The external ID associated with the DTD, or <var>null</var> if 
     *                          the DTD does not have an external ID.
     * @see com.ibm.xml.parser.DTD
     * @see com.ibm.xml.parser.ExternalID
     */
    public DTD createDTD(String rootElementName, ExternalID externalID) {
        return this.dtd = super.createDTD(rootElementName, externalID);
    }
                                                // parser.ElementFactory
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that an entity has been parsed.
     * This method will notify any registered DTD handlers if an external binary entity
     * is recognized.
     * <p>This method is defined by ElementFactory.
     * @param   name            The name of the entity.
     * @param   externalID      The external ID associated with the Entity, or <var>null</var> if 
     *                          the Entity does not have an external ID (i.e. it is an internal entity).
     * @param   isParameter     =true if a parameter Entity; otherwise =false.
     * @param   ndata           The notation associated with the binary Entity, or <var>null</var> if
     *                          the Entity is a text Entity.
     * @exception RuntimeException Thrown if the <code>dtdHandler</code> fails.
     * @see #setDTDHandler
     * @see com.ibm.xml.parser.Entity
     */
    public Entity createEntity(String name, ExternalID externalID,
                               boolean isParameter, String ndata) throws RuntimeException {
        if (null != ndata) {
            try {
                this.dtdHandler.unparsedEntityDecl(name,
                                                externalID.getPubidLiteral(),
                                                externalID.getSystemLiteral(),
                                                ndata);
            } catch (SAXException se) {
                throw new ExceptionWrapper(se);
            }
        }
        return super.createEntity(name, externalID, isParameter, ndata);
    }
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a Notation has been parsed.
     * This method will notify any registered DTD handlers.
     * <p>This method is defined by ElementFactory.
     * @param   name            The name of the Notation.
     * @param   externalID      The external ID associated with the Notation, or <var>null</var> if 
     *                          the Notation does not have an external ID (i.e. it is an internal entity).
     * @exception RuntimeException Thrown if the <code>dtdHandler</code> fails.
     * @see #setDTDHandler
     * @see com.ibm.xml.parser.ExternalID
     */
    public TXNotation createNotation(String name, ExternalID externalID) throws RuntimeException {
        try {
            this.dtdHandler.notationDecl(name, externalID.getPubidLiteral(), externalID.getSystemLiteral());
        } catch (SAXException se) {
            throw new ExceptionWrapper(se);
        }
        return super.createNotation(name, externalID);
    }
    
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that Text has been parsed.
     * This method will notify any registered document handlers.
     * <p>This method is defined by ElementFactory.
     * @param carray        an array of chracter including the actual content of the Text Node.
     * @param offset        a position of starting content.
     * @param length        a length of content
     * @param   ignorableWhiteSpace The boolean indicator which determines whether white space 
     *                              (blanks) is to be considered for this Text Node. 
     *                              =true indicates whitespace is ignored; =false indicates 
     *                              whitespace is significant.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     */
    public TXText createText(char[] carray, int offset, int length, boolean ignorableWhitespace)
        throws RuntimeException {
        if (0 < this.depth) {
            try {
                if (ignorableWhitespace)
                    this.documentHandler.ignorableWhitespace(carray, offset, length);
                else
                    this.documentHandler.characters(carray, offset, length);
            } catch (SAXException e) {
                throw new ExceptionWrapper(e);
            }
        }
        //return super.createText(carray, offset, length, ignorableWhitespace);
        return null;
    }
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a CDATA section has been parsed.
     * This method will notify any registered document handlers.
     * <p>This method is defined by ElementFactory.
     * @param   data                The actual data of the CDATA section.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     */
    public TXCDATASection createCDATA(String data) throws RuntimeException {
        try {
            char[] ac = data.toCharArray();
            this.documentHandler.characters(ac, 0, ac.length);
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
        return super.createCDATA(data);
    }
    
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a processing instruction (PI) has been parsed.
     * This method will notify any registered document handlers.
     * <p>This method is defined by ElementFactory.
     * @param name      The first token following the markup.
     * @param data      From the character immediately after <var>name</var> to the 
     *                    character immediately preceding the <code>?&gt;</code>.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     */
    public TXPI createPI(String name, String data) throws RuntimeException {
        try {
            this.documentHandler.processingInstruction(name, data);
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
        //return super.createPI(name, data);
        return null;
    }
    
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a namespacePI has been parsed.
     * This method will notify any registered document handlers.
     * <p>This method is defined by ElementFactory.
     * @param name          The first token following the markup (i.e. "xml:namespace").
     * @param data          From the character immediately after the name to the character immediately preceding the <code>?&gt;</code>.
     * @param nsURI         The value of the <code>ns=</code> attribute.
     * @param prefixName    The value of the <code>prefix=</code> attribute.
     * @param srcURI        The value of the <code>src=</code> attribute, or <var>null</var>.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     */
    public NamespacePI createNamespacePI(String name, String data, String nsURI, String prefixName, String srcURI)
      throws RuntimeException {
        try {
            this.documentHandler.processingInstruction(name, data);
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
        //return super.createNamespacePI(name, data, nsURI, prefixName, srcURI);
        return null;
    }
    
    /**
     * This method is for XML4J-internal use and should not be called by SAX applications.
     * <p>Called by the XML4J parser in order to signal that a stylesheetPI has been parsed.
     * This method will notify any registered document handlers.
     * <p>This method is defined by ElementFactory.
     * @param name          The first token following the markup (e.g. <CODE>"xml:stylesheet"</CODE>).
     * @param data          From the character immediately after <var>name</var> to the character immediately preceding the <code>?&gt;</code>.
     * @param type          The value of the <code>type=</code> attribute.
     * @param hrefURI       The value of the <code>href=</code> attribute.
     * @param title         The value of the <CODE>title=</CODE> attribute, or <var>null</var>.
     * @exception RuntimeException Thrown if the <code>documentHandler</code> fails.
     * @see #setDocumentHandler
     */
    public StylesheetPI createStylesheetPI(String name, String data, String type, String hrefURI, String title)
      throws RuntimeException {
        try {
            this.documentHandler.processingInstruction(name, data);
        } catch (SAXException e) {
            throw new ExceptionWrapper(e);
        }
        //return super.createStylesheetPI(name, data, type, hrefURI, title);
        return null;
    }

    private int searchAttribute(String n) {
        for (int i = 0;  i < this.attributes.length;  i ++) {
            if (n.equals(this.attributes[i].getName()))
                return i;
        }
        return -1;
    }

}
