/*
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.ukit.dom;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.sun.ukit.xml.Input;
import com.sun.ukit.xml.Pair;
import com.sun.ukit.xml.Parser;
/**
* XML non-validating DOM parser.
*
* @see com.sun.ukit.xml.Parser
*/
/* pkg */ final class ParserDOM
extends Parser
{
// SAX handlers
/* pkg */ ErrorHandler mHandErr; // the error handler
/* pkg */ EntityResolver mHandEnt; // the entity resolver
private XDoc mDoc;
private Node mParent;
/**
* Constructor.
*/
/* pkg */ ParserDOM(boolean isNSAware, XDoc doc)
{
super();
mIsNSAware = isNSAware;
mDoc = doc;
mParent = mDoc;
}
/**
* Parse an XML document.
*
* <p>The application can use this method to instruct the XML
* reader to begin parsing an XML document from any valid input
* source (a character stream, a byte stream, or a URI).</p>
*
* <p>Applications may not invoke this method while a parse is in
* progress (they should create a new parser instead for each
* nested XML document). Once a parse is complete, an
* application may reuse the same parser object, possibly with a
* different input source.</p>
*
* <p>This method is synchronous: it will not return until parsing
* has ended. If a client application wants to terminate
* parsing early, it should throw an exception.</p>
*
* @param is The input source for the top-level of the XML document.
* @exception org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @exception java.io.IOException An IO exception from the parser,
* possibly from a byte stream or character stream
* supplied by the application.
* @see org.xml.sax.InputSource
* @see org.xml.sax.EntityResolver
* @see org.xml.sax.ErrorHandler
*/
public Document parse(InputSource is)
throws IOException, SAXException
{
if (is == null)
throw new IllegalArgumentException("");
// Set up the document
mInp = new Input(BUFFSIZE_READER);
mPh = PH_BEFORE_DOC; // before parsing
try {
setinp(is);
} catch(SAXException saxe) {
throw saxe;
} catch(IOException ioe) {
throw ioe;
} catch(RuntimeException rte) {
throw rte;
} catch(Exception e) {
panic(e.toString());
}
return parse();
}
/**
* Parse the XML document content using specified handlers and an
* input source.
*
* @exception IOException If any IO errors occur.
* @exception SAXException If the underlying parser throws a SAXException
* while parsing.
*/
private Document parse()
throws SAXException, IOException
{
init();
mDoc._setBuild(true);
try {
if (mPh != PH_MISC_DTD)
mPh = PH_MISC_DTD; // misc before DTD
mEvt = EV_NULL;
// XML document prolog
do {
switch (step()) {
case EV_ELM:
case EV_ELMS:
mPh = PH_DOCELM;
break;
case EV_COMM:
case EV_PI:
case EV_PENT:
case EV_UENT:
case EV_NOT:
break;
case EV_DTDS:
if (mPh >= PH_DTD)
panic(FAULT);
mPh = PH_DTD; // DTD
break;
case EV_DTDE:
if (mPh != PH_DTD)
panic(FAULT);
mPh = PH_DTD_MISC; // misc after DTD
break;
default:
panic(FAULT);
}
} while (mPh < PH_DOCELM); // misc before DTD
// XML document starting with document's element
do {
switch (mEvt) {
case EV_ELMS:
// Set new parent node
step();
break;
case EV_ELM:
case EV_ELME:
// Restore previous parent node
mParent = mParent.getParentNode();
while (mPref.list == mElm) {
mPref = del(mPref);
}
// Remove the top element tag
mElm = del(mElm);
if (mElm == null)
mPh = PH_DOCELM_MISC;
else
step();
break;
case EV_TEXT:
case EV_WSPC:
case EV_CDAT:
case EV_COMM:
case EV_PI:
case EV_ENT:
step();
break;
default:
panic(FAULT);
}
} while (mPh == PH_DOCELM);
// Misc after document's element
do {
if (wsskip() == EOS)
break;
switch (step()) {
case EV_COMM:
case EV_PI:
break;
default:
panic(FAULT);
}
} while (mPh == PH_DOCELM_MISC);
mPh = PH_AFTER_DOC; // parsing is completed
} catch(SAXException saxe) {
throw saxe;
} catch(IOException ioe) {
throw ioe;
} catch(RuntimeException rte) {
throw rte;
} catch(Exception e) {
panic(e.toString());
} finally {
mDoc._setBuild(false);
cleanup();
}
return mDoc;
}
/**
* DTD post-processing.
* Process all default attributes and set these attributes on document.
* This method sets all the declared element and attribute names as a
* String and value for all the default attributes. This method also sets
* all the default attributes on the document.
*/
protected void dtdpost()
throws Exception
{
super.dtdpost();
// Set all default attributes on the document object
for (Pair elm = mAttL; elm != null; elm = elm.next) {
elm.name = elm.qname(); // need qName string for DOM
for (Pair att = elm.list; att != null; att = att.next) {
if ((att.num & 0x3) == 0x3)
mDoc._setDefAttr(elm.name, att.qname(), att.value);
}
}
}
/**
* Creates a new element and sets it as current parent.
*/
protected void attrs()
throws Exception
{
// Count default attributes.
int defnum = 0;
for(Pair next = mElm.list; next != null; next = next.next) {
if ((next.num & 0x3) == 0x3) // next has default value
defnum++;
}
super.attrs(); // process all element's attributes
// Create a new element and set it as current parent.
XElm elm = new ElmImp(mElm, defnum, mDoc);
// Set new parent
mParent.appendChild(elm);
mParent = elm;
}
/**
* Reports document type.
*
* @param name The name of the entity.
* @param pubid The public identifier of the DTD or <code>null</code>.
* @param sysid The system identifier of the DTD or <code>null</code>.
* @param dtdint The DTD internal subset or <code>null</code>.
*/
protected void docType(
String name, String pubid, String sysid, char[] dtdint)
{
mDoc.appendChild(
mDoc.getImplementation().createDocumentType(name, pubid, sysid));
}
/**
* Reports a comment.
*
* @param text The comment text starting from first character.
* @param length The number of characters in comment.
*/
protected void comm(char[] text, int length)
{
if (mPh != PH_DTD)
mParent.appendChild(mDoc.createComment(new String(text, 0, length)));
}
/**
* Reports a processing instruction.
*
* @param target The processing instruction target name.
* @param body The processing instruction body text.
*/
protected void pi(String target, String body)
throws SAXException
{
if (mPh != PH_DTD)
mParent.appendChild(mDoc.createProcessingInstruction(target, body));
}
/**
* Reports new namespace prefix.
* The Namespace prefix (<code>mPref.name</code>) being declared and
* the Namespace URI (<code>mPref.value</code>) the prefix is mapped
* to. An empty string is used for the default element namespace,
* which has no prefix.
*/
protected void newPrefix()
throws SAXException
{
}
/**
* Reports skipped entity name.
*
* @param name The entity name.
*/
protected void skippedEnt(String name)
throws SAXException
{
if (mPh != PH_DTD)
mParent.appendChild(mDoc.createEntityReference(name));
}
/**
* Returns an <code>InputSource</code> for specified entity or
* <code>null</code>.
*
* @param name The name of the entity.
* @param pubid The public identifier of the entity.
* @param sysid The system identifier of the entity.
*/
protected InputSource resolveEnt(String name, String pubid, String sysid)
throws SAXException, IOException
{
return (mHandEnt != null)? mHandEnt.resolveEntity(pubid, sysid): null;
}
/**
* Reports internal parsed entity.
*
* @param name The entity name.
* @param value The entity replacement text.
*/
protected void intparsedEntDecl(String name, char[] value)
throws Exception
{
DocTypeImp doctyp = (DocTypeImp)mDoc.getDoctype();
if (doctyp.entmap._find(name) < 0) {
EntImp ent = new EntImp(name, mDoc);
ent._appendChild((XNode)mDoc.createTextNode(new String(value)));
doctyp.entmap._append(ent);
}
}
/**
* Reports external parsed entity.
*
* @param name The entity name.
* @param pubid The entity public identifier, may be null.
* @param name The entity system identifier, may be null.
*/
protected void extparsedEntDecl(String name, String pubid, String sysid)
throws Exception
{
DocTypeImp doctyp = (DocTypeImp)mDoc.getDoctype();
if (doctyp.entmap._find(name) < 0)
doctyp.entmap._append(new EntImp(name, pubid, sysid, null, mDoc));
}
/**
* Reports notation declaration.
*
* @param name The notation's name.
* @param pubid The notation's public identifier, or null if none was given.
* @param sysid The notation's system identifier, or null if none was given.
*/
protected void notDecl(String name, String pubid, String sysid)
throws SAXException
{
DocTypeImp doctyp = (DocTypeImp)mDoc.getDoctype();
if (doctyp.notmap._find(name) < 0)
doctyp.notmap._append(new NotImp(name, pubid, sysid, mDoc));
}
/**
* Reports unparsed entity name.
*
* @param name The unparsed entity's name.
* @param pubid The entity's public identifier, or null if none was given.
* @param sysid The entity's system identifier.
* @param notation The name of the associated notation.
*/
protected void unparsedEntDecl(
String name, String pubid, String sysid, String notation)
throws SAXException
{
DocTypeImp doctyp = (DocTypeImp)mDoc.getDoctype();
if (doctyp.entmap._find(name) < 0)
doctyp.entmap._append(new EntImp(name, pubid, sysid, notation, mDoc));
}
/**
* Notifies the handler about fatal parsing error.
*
* @param msg The problem description message.
*/
protected void panic(String msg)
throws SAXException
{
SAXParseException spe = new SAXParseException(msg, null);
if (mHandErr != null)
mHandErr.fatalError(spe);
throw spe; // [#1.2] fatal error definition
}
/**
* Reports characters and empties the parser's buffer.
* This method is called only if parser is going to return control to
* the main loop. This means that this method may use parser buffer
* to report white space without copying characters to temporary
* buffer.
*/
protected void bflash()
throws SAXException
{
String str = (mBuffIdx >= 0)? new String(mBuff, 0, (mBuffIdx + 1)): "";
// Textual data has been read
switch (mEvt) {
case EV_TEXT:
if (mBuffIdx >= 0)
mParent.appendChild(mDoc.createTextNode(str));
break;
case EV_CDAT:
mParent.appendChild(mDoc.createCDATASection(str));
break;
default:
panic(FAULT);
}
mBuffIdx = -1;
}
/**
* Reports white space characters and empties the parser's buffer.
* This method is called only if parser is going to return control to
* the main loop. This means that this method may use parser buffer
* to report white space without copying characters to temporary
* buffer.
*/
protected void bflash_ws()
throws SAXException
{
if (mBuffIdx >= 0) {
if ((mElm.id & FLAG_XMLSPC_PRESERVE) != 0) {
// Textual data has been read
mParent.appendChild(mDoc.createTextNode(
new String(mBuff, 0, (mBuffIdx + 1))));
}
mBuffIdx = -1;
}
}
}