diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | fdb2704414a9ed92394ada0d1395e4db86889465 (patch) | |
tree | 9b591a4a50054274a197f02b3ccb51313681879f /xml/src/main/java/org | |
download | libcore-fdb2704414a9ed92394ada0d1395e4db86889465.zip libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.gz libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.bz2 |
Initial Contribution
Diffstat (limited to 'xml/src/main/java/org')
102 files changed, 25989 insertions, 0 deletions
diff --git a/xml/src/main/java/org/apache/harmony/xml/ExpatAttributes.java b/xml/src/main/java/org/apache/harmony/xml/ExpatAttributes.java new file mode 100644 index 0000000..ed6cb11 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/ExpatAttributes.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml; + +import org.xml.sax.Attributes; + +/** + * Wraps native attribute array. + */ +abstract class ExpatAttributes implements Attributes { + + /** + * Since we don't do validation, pretty much everything is CDATA type. + */ + private static final String CDATA = "CDATA"; + + /** + * Gets the number of attributes. + */ + public abstract int getLength(); + + /** + * Gets the pointer to the parser. We need this so we can get to the + * interned string pool. + */ + abstract int getParserPointer(); + + /** + * Gets the pointer to the underlying attribute array. Can be 0 if the + * length is 0. + */ + public abstract int getPointer(); + + public String getURI(int index) { + if (index < 0 || index >= getLength()) { + return null; + } + return getURI(getParserPointer(), getPointer(), index); + } + + public String getLocalName(int index) { + return (index < 0 || index >= getLength()) + ? null + : getLocalName(getParserPointer(), getPointer(), index); + } + + public String getQName(int index) { + return (index < 0 || index >= getLength()) + ? null + : getQName(getParserPointer(), getPointer(), index); + } + + public String getType(int index) { + return (index < 0 || index >= getLength()) ? null : CDATA; + } + + public String getValue(int index) { + return (index < 0 || index >= getLength()) + ? null + : getValue(getPointer(), index); + } + + public int getIndex(String uri, String localName) { + if (uri == null) { + throw new NullPointerException("uri"); + } + if (localName == null) { + throw new NullPointerException("local name"); + } + int pointer = getPointer(); + if (pointer == 0) { + return -1; + } + return getIndex(pointer, uri, localName); + } + + public int getIndex(String qName) { + if (qName == null) { + throw new NullPointerException("uri"); + } + int pointer = getPointer(); + if (pointer == 0) { + return -1; + } + return getIndex(pointer, qName); + } + + public String getType(String uri, String localName) { + if (uri == null) { + throw new NullPointerException("uri"); + } + if (localName == null) { + throw new NullPointerException("local name"); + } + return getIndex(uri, localName) == -1 ? null : CDATA; + } + + public String getType(String qName) { + return getIndex(qName) == -1 ? null : CDATA; + } + + public String getValue(String uri, String localName) { + if (uri == null) { + throw new NullPointerException("uri"); + } + if (localName == null) { + throw new NullPointerException("local name"); + } + int pointer = getPointer(); + if (pointer == 0) { + return null; + } + return getValue(pointer, uri, localName); + } + + public String getValue(String qName) { + if (qName == null) { + throw new NullPointerException("qName"); + } + int pointer = getPointer(); + if (pointer == 0) { + return null; + } + return getValue(pointer, qName); + } + + static native String getURI(int pointer, int attributePointer, int index); + static native String getLocalName(int pointer, int attributePointer, + int index); + static native String getQName(int pointer, int attributePointer, + int index); + static native String getValue(int attributePointer, int index); + static native int getIndex(int attributePointer, String uri, + String localName); + static native int getIndex(int attributePointer, String qName); + static native String getValue(int attributePointer, + String uri, String localName); + static native String getValue(int attributePointer, String qName); + static native void freeAttributes(int pointer); +} diff --git a/xml/src/main/java/org/apache/harmony/xml/ExpatException.java b/xml/src/main/java/org/apache/harmony/xml/ExpatException.java new file mode 100644 index 0000000..da3db9d --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/ExpatException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml; + +/** + * Used internally to propogate Expat errors. We convert these exceptions into + * SAXParseExceptions before propogating them to the client. + */ +class ExpatException extends Exception { + + public ExpatException(String message) { + super(message); + } +} diff --git a/xml/src/main/java/org/apache/harmony/xml/ExpatParser.java b/xml/src/main/java/org/apache/harmony/xml/ExpatParser.java new file mode 100644 index 0000000..60d74b8 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/ExpatParser.java @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.ext.LexicalHandler; + +import java.io.Reader; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URLConnection; +import java.net.URL; +import java.util.logging.Logger; +import java.util.logging.Level; + +/** + * Adapts SAX API to the Expat native XML parser. Not intended for reuse + * across documents. + * + * @see org.apache.harmony.xml.ExpatPullParser + * @see org.apache.harmony.xml.ExpatReader + */ +class ExpatParser { + + private static final int BUFFER_SIZE = 8096; // in bytes + + /** Pointer to XML_Parser instance. */ + private int pointer; + + private boolean inStartElement = false; + private int attributeCount = -1; + private int attributePointer = 0; + + private final Locator locator = new ExpatLocator(); + + private final ExpatReader xmlReader; + + private final String publicId; + private final String systemId; + + private final String encoding; + + private final ExpatAttributes attributes = new CurrentAttributes(); + + private static final String OUTSIDE_START_ELEMENT + = "Attributes can only be used within the scope of startElement()."; + + /** We default to UTF-8 when the user doesn't specify an encoding. */ + private static final String DEFAULT_ENCODING = "UTF-8"; + + /** Encoding used for Java chars, used to parse Readers and Strings */ + /*package*/ static final String CHARACTER_ENCODING = "UTF-16"; + + /** Timeout for HTTP connections (in ms) */ + private static final int TIMEOUT = 20 * 1000; + + /** + * Constructs a new parser with the specified encoding. + */ + /*package*/ ExpatParser(String encoding, ExpatReader xmlReader, + boolean processNamespaces, String publicId, String systemId) { + this.publicId = publicId; + this.systemId = systemId; + + this.xmlReader = xmlReader; + + /* + * TODO: Let Expat try to guess the encoding instead of defaulting. + * Unfortunately, I don't know how to tell which encoding Expat picked, + * so I won't know how to encode "<externalEntity>" below. The solution + * I think is to fix Expat to not require the "<externalEntity>" + * workaround. + */ + this.encoding = encoding == null ? DEFAULT_ENCODING : encoding; + this.pointer = initialize( + this.encoding, + processNamespaces + ); + } + + /** + * Used by {@link EntityParser}. + */ + private ExpatParser(String encoding, ExpatReader xmlReader, int pointer, + String publicId, String systemId) { + this.encoding = encoding; + this.xmlReader = xmlReader; + this.pointer = pointer; + this.systemId = systemId; + this.publicId = publicId; + } + + /** + * Initializes native resources. + * + * @return the pointer to the native parser + */ + private native int initialize(String encoding, boolean namespacesEnabled); + + /** + * Called at the start of an element. + * + * @param uri namespace URI of element or "" if namespace processing is + * disabled + * @param localName local name of element or "" if namespace processing is + * disabled + * @param qName qualified name or "" if namespace processing is enabled + * @param attributePointer pointer to native attribute char*--we keep + * a separate pointer so we can detach it from the parser instance + * @param attributeCount number of attributes + */ + /*package*/ void startElement(String uri, String localName, String qName, + int attributePointer, int attributeCount) throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler == null) { + return; + } + + try { + inStartElement = true; + this.attributePointer = attributePointer; + this.attributeCount = attributeCount; + + contentHandler.startElement( + uri, localName, qName, this.attributes); + } + finally { + inStartElement = false; + this.attributeCount = -1; + this.attributePointer = 0; + } + } + + /*package*/ void endElement(String uri, String localName, String qName) + throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.endElement(uri, localName, qName); + } + } + + /*package*/ void text(char[] text, int length) throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.characters(text, 0, length); + } + } + + /*package*/ void comment(char[] text, int length) throws SAXException { + LexicalHandler lexicalHandler = xmlReader.lexicalHandler; + if (lexicalHandler != null) { + lexicalHandler.comment(text, 0, length); + } + } + + /*package*/ void startCdata() throws SAXException { + LexicalHandler lexicalHandler = xmlReader.lexicalHandler; + if (lexicalHandler != null) { + lexicalHandler.startCDATA(); + } + } + + /*package*/ void endCdata() throws SAXException { + LexicalHandler lexicalHandler = xmlReader.lexicalHandler; + if (lexicalHandler != null) { + lexicalHandler.endCDATA(); + } + } + + /*package*/ void startNamespace(String prefix, String uri) + throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.startPrefixMapping(prefix, uri); + } + } + + /*package*/ void endNamespace(String prefix) throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.endPrefixMapping(prefix); + } + } + + /*package*/ void startDtd(String name, String publicId, String systemId) + throws SAXException { + LexicalHandler lexicalHandler = xmlReader.lexicalHandler; + if (lexicalHandler != null) { + lexicalHandler.startDTD(name, publicId, systemId); + } + } + + /*package*/ void endDtd() throws SAXException { + LexicalHandler lexicalHandler = xmlReader.lexicalHandler; + if (lexicalHandler != null) { + lexicalHandler.endDTD(); + } + } + + /*package*/ void processingInstruction(String target, String data) + throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.processingInstruction(target, data); + } + } + + /** + * Handles an external entity. + * + * @param context to be passed back to Expat when we parse the entity + * @param publicId the publicId of the entity + * @param systemId the systemId of the entity + */ + /*package*/ void handleExternalEntity(String context, String publicId, + String systemId) throws SAXException, IOException { + EntityResolver entityResolver = xmlReader.entityResolver; + if (entityResolver == null) { + return; + } + + /* + * The spec. is terribly under-specified here. It says that if the + * systemId is a URL, we should try to resolve it, but it doesn't + * specify how to tell whether or not the systemId is a URL let alone + * how to resolve it. + * + * Other implementations do various insane things. We try to keep it + * simple: if the systemId parses as a URI and it's relative, we try to + * resolve it against the parent document's systemId. If anything goes + * wrong, we go with the original systemId. If crazybob had designed + * the API, he would have left all resolving to the EntityResolver. + */ + if (this.systemId != null) { + try { + URI systemUri = new URI(systemId); + if (!systemUri.isAbsolute() && !systemUri.isOpaque()) { + // It could be relative (or it may not be a URI at all!) + URI baseUri = new URI(this.systemId); + systemUri = baseUri.resolve(systemUri); + + // Replace systemId w/ resolved URI + systemId = systemUri.toString(); + } + } catch (Exception e) { + Logger.getLogger(ExpatParser.class.getName()).log(Level.INFO, + "Could not resolve '" + systemId + "' relative to" + + " '" + this.systemId + "' at " + locator, e); + } + } + + InputSource inputSource = entityResolver.resolveEntity( + publicId, systemId); + if (inputSource == null) { + /* + * The spec. actually says that we should try to treat systemId + * as a URL and download and parse its contents here, but an + * entity resolver can easily accomplish the same by returning + * new InputSource(systemId). + * + * Downloading external entities by default would result in several + * unwanted DTD downloads, not to mention pose a security risk + * when parsing untrusted XML (http://tinyurl.com/56ggrk), + * so we just do nothing instead. This also enables the user to + * opt out of entity parsing when using + * {@link org.xml.sax.helpers.DefaultHandler}, something that + * wouldn't be possible otherwise. + */ + return; + } + + String encoding = pickEncoding(inputSource); + int pointer = createEntityParser(this.pointer, context, encoding); + try { + EntityParser entityParser = new EntityParser(encoding, xmlReader, + pointer, inputSource.getPublicId(), + inputSource.getSystemId()); + + parseExternalEntity(entityParser, inputSource); + } finally { + releaseParser(pointer); + } + } + + /** + * Picks an encoding for an external entity. Defaults to UTF-8. + */ + private String pickEncoding(InputSource inputSource) { + Reader reader = inputSource.getCharacterStream(); + if (reader != null) { + return CHARACTER_ENCODING; + } + + String encoding = inputSource.getEncoding(); + return encoding == null ? DEFAULT_ENCODING : encoding; + } + + /** + * Parses the the external entity provided by the input source. + */ + private void parseExternalEntity(ExpatParser entityParser, + InputSource inputSource) throws IOException, SAXException { + /* + * Expat complains if the external entity isn't wrapped with a root + * element so we add one and ignore it later on during parsing. + */ + + // Try the character stream. + Reader reader = inputSource.getCharacterStream(); + if (reader != null) { + try { + entityParser.append("<externalEntity>"); + entityParser.parseFragment(reader); + entityParser.append("</externalEntity>"); + } finally { + // TODO: Don't eat original exception when close() throws. + reader.close(); + } + return; + } + + // Try the byte stream. + InputStream in = inputSource.getByteStream(); + if (in != null) { + try { + entityParser.append("<externalEntity>" + .getBytes(entityParser.encoding)); + entityParser.parseFragment(in); + entityParser.append("</externalEntity>" + .getBytes(entityParser.encoding)); + } finally { + // TODO: Don't eat original exception when close() throws. + in.close(); + } + return; + } + + // Make sure we use the user-provided systemId. + String systemId = inputSource.getSystemId(); + if (systemId == null) { + // TODO: We could just try our systemId here. + throw new ParseException("No input specified.", locator); + } + + // Try the system id. + in = openUrl(systemId); + try { + entityParser.append("<externalEntity>" + .getBytes(entityParser.encoding)); + entityParser.parseFragment(in); + entityParser.append("</externalEntity>" + .getBytes(entityParser.encoding)); + } finally { + in.close(); + } + } + + /** + * Creates a native entity parser. + * + * @param parentPointer pointer to parent Expat parser + * @param context passed to {@link #handleExternalEntity} + * @param encoding + * @return pointer to native parser + */ + private static native int createEntityParser(int parentPointer, + String context, String encoding); + + /** + * Appends part of an XML document. This parser will parse the given XML to + * the extent possible and dispatch to the appropriate methods. + * + * @param xml a whole or partial snippet of XML + * @throws SAXException if an error occurs during parsing + */ + /*package*/ void append(String xml) throws SAXException { + try { + append(this.pointer, xml, false); + } catch (ExpatException e) { + throw new ParseException(e.getMessage(), this.locator); + } + } + + private native void append(int pointer, String xml, boolean isFinal) + throws SAXException, ExpatException; + + /** + * Appends part of an XML document. This parser will parse the given XML to + * the extent possible and dispatch to the appropriate methods. + * + * @param xml a whole or partial snippet of XML + * @param offset into the char[] + * @param length of characters to use + * @throws SAXException if an error occurs during parsing + */ + /*package*/ void append(char[] xml, int offset, int length) + throws SAXException { + try { + append(this.pointer, xml, offset, length); + } catch (ExpatException e) { + throw new ParseException(e.getMessage(), this.locator); + } + } + + private native void append(int pointer, char[] xml, int offset, + int length) throws SAXException, ExpatException; + + /** + * Appends part of an XML document. This parser will parse the given XML to + * the extent possible and dispatch to the appropriate methods. + * + * @param xml a whole or partial snippet of XML + * @throws SAXException if an error occurs during parsing + */ + /*package*/ void append(byte[] xml) throws SAXException { + append(xml, 0, xml.length); + } + + /** + * Appends part of an XML document. This parser will parse the given XML to + * the extent possible and dispatch to the appropriate methods. + * + * @param xml a whole or partial snippet of XML + * @param offset into the byte[] + * @param length of bytes to use + * @throws SAXException if an error occurs during parsing + */ + /*package*/ void append(byte[] xml, int offset, int length) + throws SAXException { + try { + append(this.pointer, xml, offset, length); + } catch (ExpatException e) { + throw new ParseException(e.getMessage(), this.locator); + } + } + + private native void append(int pointer, byte[] xml, int offset, + int length) throws SAXException, ExpatException; + + /** + * Parses an XML document from the given input stream. + */ + /*package*/ void parseDocument(InputStream in) throws IOException, + SAXException { + startDocument(); + parseFragment(in); + finish(); + endDocument(); + } + + /** + * Parses an XML Document from the given reader. + */ + /*package*/ void parseDocument(Reader in) throws IOException, SAXException { + startDocument(); + parseFragment(in); + finish(); + endDocument(); + } + + /** + * Parses XML from the given Reader. + */ + private void parseFragment(Reader in) throws IOException, SAXException { + char[] buffer = new char[BUFFER_SIZE / 2]; + int length; + while ((length = in.read(buffer)) != -1) { + try { + append(this.pointer, buffer, 0, length); + } catch (ExpatException e) { + throw new ParseException(e.getMessage(), locator); + } + } + } + + /** + * Parses XML from the given input stream. + */ + private void parseFragment(InputStream in) + throws IOException, SAXException { + byte[] buffer = new byte[BUFFER_SIZE]; + int length; + while ((length = in.read(buffer)) != -1) { + try { + append(this.pointer, buffer, 0, length); + } catch (ExpatException e) { + throw new ParseException(e.getMessage(), this.locator); + } + } + } + + private void startDocument() throws SAXException { + ContentHandler contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.setDocumentLocator(this.locator); + contentHandler.startDocument(); + } + } + + private void endDocument() throws SAXException { + ContentHandler contentHandler; + contentHandler = xmlReader.contentHandler; + if (contentHandler != null) { + contentHandler.endDocument(); + } + } + + /** + * Indicate that we're finished parsing. + * + * @throws SAXException if the xml is incomplete + */ + /*package*/ void finish() throws SAXException { + try { + append(this.pointer, "", true); + } catch (ExpatException e) { + throw new ParseException(e.getMessage(), this.locator); + } + } + + @Override + @SuppressWarnings("FinalizeDoesntCallSuperFinalize") + protected synchronized void finalize() throws Throwable { + if (this.pointer != 0) { + release(this.pointer); + this.pointer = 0; + } + } + + /** + * Releases all native objects. + */ + private native void release(int pointer); + + /** + * Releases native parser only. + */ + private static native void releaseParser(int pointer); + + /** + * Initialize static resources. + */ + private static native void staticInitialize(String emptyString); + + static { + staticInitialize(""); + } + + /** + * Gets the current line number within the XML file. + */ + private int line() { + return line(this.pointer); + } + + private static native int line(int pointer); + + /** + * Gets the current column number within the XML file. + */ + private int column() { + return column(this.pointer); + } + + private static native int column(int pointer); + + /** + * Clones the current attributes so they can be used outside of + * startElement(). + */ + /*package*/ Attributes cloneAttributes() { + if (!inStartElement) { + throw new IllegalStateException(OUTSIDE_START_ELEMENT); + } + + if (attributeCount == 0) { + return ClonedAttributes.EMPTY; + } + + int clonePointer + = cloneAttributes(this.attributePointer, this.attributeCount); + return new ClonedAttributes(pointer, clonePointer, attributeCount); + } + + private static native int cloneAttributes(int pointer, int attributeCount); + + /** + * Used for cloned attributes. + */ + private static class ClonedAttributes extends ExpatAttributes { + + private static final Attributes EMPTY = new ClonedAttributes(0, 0, 0); + + private final int parserPointer; + private int pointer; + private final int length; + + /** + * Constructs a Java wrapper for native attributes. + * + * @param parserPointer pointer to the parse, can be 0 if length is 0. + * @param pointer pointer to the attributes array, can be 0 if the + * length is 0. + * @param length number of attributes + */ + private ClonedAttributes(int parserPointer, int pointer, int length) { + this.parserPointer = parserPointer; + this.pointer = pointer; + this.length = length; + } + + @Override + public int getParserPointer() { + return this.parserPointer; + } + + @Override + public int getPointer() { + return pointer; + } + + @Override + public int getLength() { + return length; + } + + @Override + @SuppressWarnings("FinalizeDoesntCallSuperFinalize") + protected synchronized void finalize() throws Throwable { + if (pointer != 0) { + freeAttributes(pointer); + pointer = 0; + } + } + } + + private class ExpatLocator implements Locator { + + public String getPublicId() { + return publicId; + } + + public String getSystemId() { + return systemId; + } + + public int getLineNumber() { + return line(); + } + + public int getColumnNumber() { + return column(); + } + + @Override + public String toString() { + return "Locator[publicId: " + publicId + ", systemId: " + systemId + + ", line: " + getLineNumber() + + ", column: " + getColumnNumber() + "]"; + } + } + + /** + * Attributes that are only valid during startElement(). + */ + private class CurrentAttributes extends ExpatAttributes { + + @Override + public int getParserPointer() { + return pointer; + } + + @Override + public int getPointer() { + if (!inStartElement) { + throw new IllegalStateException(OUTSIDE_START_ELEMENT); + } + return attributePointer; + } + + @Override + public int getLength() { + if (!inStartElement) { + throw new IllegalStateException(OUTSIDE_START_ELEMENT); + } + return attributeCount; + } + } + + /** + * Includes line and column in the message. + */ + private static class ParseException extends SAXParseException { + + private ParseException(String message, Locator locator) { + super(makeMessage(message, locator), locator); + } + + private static String makeMessage(String message, Locator locator) { + return makeMessage(message, locator.getLineNumber(), + locator.getColumnNumber()); + } + + private static String makeMessage( + String message, int line, int column) { + return "At line " + line + ", column " + + column + ": " + message; + } + } + + /** + * Opens an InputStream for the given URL. + */ + /*package*/ static InputStream openUrl(String url) throws IOException { + try { + URLConnection urlConnection = new URL(url).openConnection(); + urlConnection.setConnectTimeout(TIMEOUT); + urlConnection.setReadTimeout(TIMEOUT); + urlConnection.setDoInput(true); + urlConnection.setDoOutput(false); + return urlConnection.getInputStream(); + } catch (Exception e) { + IOException ioe = new IOException("Couldn't open " + url); + ioe.initCause(e); + throw ioe; + } + } + + /** + * Parses an external entity. + */ + private static class EntityParser extends ExpatParser { + + private int depth = 0; + + private EntityParser(String encoding, ExpatReader xmlReader, + int pointer, String publicId, String systemId) { + super(encoding, xmlReader, pointer, publicId, systemId); + } + + @Override + void startElement(String uri, String localName, String qName, + int attributePointer, int attributeCount) throws SAXException { + /* + * Skip topmost element generated by our workaround in + * {@link #handleExternalEntity}. + */ + if (depth++ > 0) { + super.startElement(uri, localName, qName, attributePointer, + attributeCount); + } + } + + @Override + void endElement(String uri, String localName, String qName) + throws SAXException { + if (--depth > 0) { + super.endElement(uri, localName, qName); + } + } + + @Override + @SuppressWarnings("FinalizeDoesntCallSuperFinalize") + protected synchronized void finalize() throws Throwable { + /* + * Don't release our native resources. We do so explicitly in + * {@link #handleExternalEntity} and we don't want to release the + * parsing context--our parent is using it. + */ + } + } +} + diff --git a/xml/src/main/java/org/apache/harmony/xml/ExpatPullParser.java b/xml/src/main/java/org/apache/harmony/xml/ExpatPullParser.java new file mode 100644 index 0000000..4759718 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/ExpatPullParser.java @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +/** + * Fast, partial XmlPullParser implementation based upon Expat. Does not + * support validation or {@code DOCTYPE} processing. + */ +public class ExpatPullParser implements XmlPullParser { + /** + * This feature is identified by http://xmlpull.org/v1/doc/features.html#relaxed + * If this feature is supported that means that XmlPull parser will be + * lenient when checking XML well formedness. + * NOTE: use it only if XML input is not well-formed and in general usage + * if this feature is discouraged + * NOTE: as there is no definition of what is relaxed XML parsing + * therefore what parser will do completely depends on implementation used + */ + public static final String FEATURE_RELAXED = + "http://xmlpull.org/v1/doc/features.html#relaxed"; + + private static final int BUFFER_SIZE = 8096; + + private static final String NOT_A_START_TAG = "This is not a start tag."; + + private Document document; + private boolean processNamespaces = false; + private boolean relaxed = false; + + public void setFeature(String name, boolean state) + throws XmlPullParserException { + if (name == null) { + // Required by API. + throw new IllegalArgumentException("Null feature name"); + } + + if (name.equals(FEATURE_PROCESS_NAMESPACES)) { + processNamespaces = state; + return; + } + + if (name.equals(FEATURE_RELAXED)) { + relaxed = true; + return; + } + + // You're free to turn these features off because we don't support them. + if (!state && (name.equals(FEATURE_REPORT_NAMESPACE_ATTRIBUTES) + || name.equals(FEATURE_PROCESS_DOCDECL) + || name.equals(FEATURE_VALIDATION))) { + return; + } + + throw new XmlPullParserException("Unsupported feature: " + name); + } + + public boolean getFeature(String name) { + if (name == null) { + // Required by API. + throw new IllegalArgumentException("Null feature name"); + } + + // We always support namespaces, but no other features. + return name.equals(FEATURE_PROCESS_NAMESPACES) && processNamespaces; + } + + /** + * Returns true if this parser processes namespaces. + * + * @see #setNamespaceProcessingEnabled(boolean) + */ + public boolean isNamespaceProcessingEnabled() { + return processNamespaces; + } + + /** + * Enables or disables namespace processing. Set to false by default. + * + * @see #isNamespaceProcessingEnabled() + */ + public void setNamespaceProcessingEnabled(boolean processNamespaces) { + this.processNamespaces = processNamespaces; + } + + public void setProperty(String name, Object value) + throws XmlPullParserException { + if (name == null) { + // Required by API. + throw new IllegalArgumentException("Null feature name"); + } + + // We don't support any properties. + throw new XmlPullParserException("Properties aren't supported."); + } + + public Object getProperty(String name) { + return null; + } + + public void setInput(Reader in) throws XmlPullParserException { + this.document = new CharDocument(in, processNamespaces); + } + + public void setInput(InputStream in, String encodingName) + throws XmlPullParserException { + this.document = new ByteDocument(in, encodingName, processNamespaces); + } + + public String getInputEncoding() { + return this.document.getEncoding(); + } + + /** + * Not supported. + * + * @throws UnsupportedOperationException always + */ + public void defineEntityReplacementText(String entityName, + String replacementText) throws XmlPullParserException { + throw new UnsupportedOperationException(); + } + + public int getNamespaceCount(int depth) throws XmlPullParserException { + return document.currentEvent.namespaceStack.countAt(depth); + } + + public String getNamespacePrefix(int pos) throws XmlPullParserException { + String prefix = document.currentEvent.namespaceStack.prefixAt(pos); + @SuppressWarnings("StringEquality") + boolean hasPrefix = prefix != ""; + return hasPrefix ? prefix : null; + } + + public String getNamespaceUri(int pos) throws XmlPullParserException { + return document.currentEvent.namespaceStack.uriAt(pos); + } + + public String getNamespace(String prefix) { + // In XmlPullParser API, null == default namespace. + if (prefix == null) { + // Internally, we use empty string instead of null. + prefix = ""; + } + + return document.currentEvent.namespaceStack.uriFor(prefix); + } + + public int getDepth() { + return this.document.getDepth(); + } + + public String getPositionDescription() { + return "line " + getLineNumber() + ", column " + getColumnNumber(); + } + + /** + * Not supported. + * + * @return {@literal -1} always + */ + public int getLineNumber() { + // We would have to record the line number in each event. + return -1; + } + + /** + * Not supported. + * + * @return {@literal -1} always + */ + public int getColumnNumber() { + // We would have to record the column number in each event. + return -1; + } + + public boolean isWhitespace() throws XmlPullParserException { + if (getEventType() != TEXT) { + throw new XmlPullParserException("Not on text."); + } + + String text = getText(); + + if (text.length() == 0) { + return true; + } + + int length = text.length(); + for (int i = 0; i < length; i++) { + if (!Character.isWhitespace(text.charAt(i))) { + return false; + } + } + + return true; + } + + public String getText() { + final StringBuilder builder = this.document.currentEvent.getText(); + return builder == null ? null : builder.toString(); + } + + public char[] getTextCharacters(int[] holderForStartAndLength) { + final StringBuilder builder = this.document.currentEvent.getText(); + + final int length = builder.length(); + char[] characters = new char[length]; + builder.getChars(0, length, characters, 0); + + holderForStartAndLength[0] = 0; + holderForStartAndLength[1] = length; + + return characters; + } + + public String getNamespace() { + return this.document.currentEvent.getNamespace(); + } + + public String getName() { + return this.document.currentEvent.getName(); + } + + /** + * Not supported. + * + * @throws UnsupportedOperationException always + */ + public String getPrefix() { + throw new UnsupportedOperationException(); + } + + public boolean isEmptyElementTag() throws XmlPullParserException { + return this.document.isCurrentElementEmpty(); + } + + public int getAttributeCount() { + return this.document.currentEvent.getAttributeCount(); + } + + public String getAttributeNamespace(int index) { + return this.document.currentEvent.getAttributeNamespace(index); + } + + public String getAttributeName(int index) { + return this.document.currentEvent.getAttributeName(index); + } + + /** + * Not supported. + * + * @throws UnsupportedOperationException always + */ + public String getAttributePrefix(int index) { + throw new UnsupportedOperationException(); + } + + public String getAttributeType(int index) { + return "CDATA"; + } + + public boolean isAttributeDefault(int index) { + return false; + } + + public String getAttributeValue(int index) { + return this.document.currentEvent.getAttributeValue(index); + } + + public String getAttributeValue(String namespace, String name) { + return this.document.currentEvent.getAttributeValue(namespace, name); + } + + public int getEventType() throws XmlPullParserException { + return this.document.currentEvent.getType(); + } + + public int next() throws XmlPullParserException, IOException { + return this.document.dequeue(); + } + + /** + * Not supported. + * + * @throws UnsupportedOperationException always + */ + public int nextToken() throws XmlPullParserException, IOException { + throw new UnsupportedOperationException(); + } + + public void require(int type, String namespace, String name) + throws XmlPullParserException, IOException { + if (type != getEventType() + || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) { + throw new XmlPullParserException("expected " + + TYPES[type] + getPositionDescription()); + } + } + + public String nextText() throws XmlPullParserException, IOException { + if (this.document.currentEvent.getType() != START_TAG) + throw new XmlPullParserException("Not on start tag."); + + int next = this.document.dequeue(); + switch (next) { + case TEXT: return getText(); + case END_TAG: return ""; + default: throw new XmlPullParserException( + "Unexpected event type: " + TYPES[next]); + } + } + + public int nextTag() throws XmlPullParserException, IOException { + int eventType = next(); + if (eventType == TEXT && isWhitespace()) { + eventType = next(); + } + if (eventType != START_TAG && eventType != END_TAG) { + throw new XmlPullParserException( + "Expected start or end tag", this, null); + } + return eventType; + } + + /** + * Immutable namespace stack. Pushing a new namespace on to the stack + * only results in one object allocation. Most operations are O(N) where + * N is the stack size. Accessing recently pushed namespaces, like those + * for the current element, is significantly faster. + */ + static class NamespaceStack { + + /** An empty stack. */ + static final NamespaceStack EMPTY = new NamespaceStack(); + + private final NamespaceStack parent; + private final String prefix; + private final String uri; + private final int index; + private final int depth; + + /** + * Constructs an actual namespace stack node. Internally, the nodes + * and the stack are one in the same making for a very efficient + * implementation. The user just sees an immutable stack and the + * builder. + */ + private NamespaceStack(NamespaceStack parent, String prefix, + String uri, int depth) { + this.parent = parent; + this.prefix = prefix; + this.uri = uri; + this.index = parent.index + 1; + this.depth = depth; + } + + /** + * Constructs a dummy node which only serves to point to the bottom + * of the stack. Using an actual node instead of null simplifies the + * code. + */ + private NamespaceStack() { + this.parent = null; + this.prefix = null; + this.uri = null; + + // This node has an index of -1 since the actual first node in the + // stack has index 0. + this.index = -1; + + // The actual first node will have a depth of 1. + this.depth = 0; + } + + String uriFor(String prefix) { + for (NamespaceStack node = this; node.index >= 0; + node = node.parent) { + if (node.prefix.equals(prefix)) { + return node.uri; + } + } + + // Not found. + return null; + } + + /** + * Gets the prefix at the given index in the stack. + */ + String prefixAt(int index) { + return nodeAt(index).prefix; + } + + /** + * Gets the URI at the given index in the stack. + */ + String uriAt(int index) { + return nodeAt(index).uri; + } + + private NamespaceStack nodeAt(int index) { + if (index > this.index) { + throw new IndexOutOfBoundsException("Index > size."); + } + if (index < 0) { + throw new IndexOutOfBoundsException("Index < 0."); + } + + NamespaceStack node = this; + while (index != node.index) { + node = node.parent; + } + return node; + } + + /** + * Gets the size of the stack at the given element depth. + */ + int countAt(int depth) { + if (depth > this.depth) { + throw new IndexOutOfBoundsException("Depth > maximum."); + } + if (depth < 0) { + throw new IndexOutOfBoundsException("Depth < 0."); + } + + NamespaceStack node = this; + while (depth < node.depth) { + node = node.parent; + } + return node.index + 1; + } + + /** Builds a NamespaceStack. */ + static class Builder { + + NamespaceStack top = EMPTY; + + /** + * Pushes a namespace onto the stack. + * + * @param depth of the element upon which the namespace was + * declared + */ + void push(String prefix, String uri, int depth) { + top = new NamespaceStack(top, prefix, uri, depth); + } + + /** + * Pops all namespaces from the given element depth. + */ + void pop(int depth) { + // Remove all nodes at the specified depth. + while (top != null && top.depth == depth) { + top = top.parent; + } + } + + /** Returns the current stack. */ + NamespaceStack build() { + return top; + } + } + } + + /** + * Base class for events. Implements event chaining and defines event API + * along with common implementations which can be overridden. + */ + static abstract class Event { + + /** Element depth at the time of this event. */ + final int depth; + + /** The namespace stack at the time of this event. */ + final NamespaceStack namespaceStack; + + /** Next event in the queue. */ + Event next = null; + + Event(int depth, NamespaceStack namespaceStack) { + this.depth = depth; + this.namespaceStack = namespaceStack; + } + + void setNext(Event next) { + this.next = next; + } + + Event getNext() { + return next; + } + + StringBuilder getText() { + return null; + } + + String getNamespace() { + return null; + } + + String getName() { + return null; + } + + int getAttributeCount() { + return -1; + } + + String getAttributeNamespace(int index) { + throw new IndexOutOfBoundsException(NOT_A_START_TAG); + } + + String getAttributeName(int index) { + throw new IndexOutOfBoundsException(NOT_A_START_TAG); + } + + String getAttributeValue(int index) { + throw new IndexOutOfBoundsException(NOT_A_START_TAG); + } + + abstract int getType(); + + String getAttributeValue(String namespace, String name) { + throw new IndexOutOfBoundsException(NOT_A_START_TAG); + } + + public int getDepth() { + return this.depth; + } + } + + static class StartDocumentEvent extends Event { + + public StartDocumentEvent() { + super(0, NamespaceStack.EMPTY); + } + + @Override + int getType() { + return START_DOCUMENT; + } + } + + static class StartTagEvent extends Event { + + final String name; + final String namespace; + final Attributes attributes; + final boolean processNamespaces; + + StartTagEvent(String namespace, + String name, + ExpatParser expatParser, + int depth, + NamespaceStack namespaceStack, + boolean processNamespaces) { + super(depth, namespaceStack); + this.namespace = namespace; + this.name = name; + this.attributes = expatParser.cloneAttributes(); + this.processNamespaces = processNamespaces; + } + + @Override + String getNamespace() { + return namespace; + } + + @Override + String getName() { + return name; + } + + @Override + int getAttributeCount() { + return attributes.getLength(); + } + + @Override + String getAttributeNamespace(int index) { + return attributes.getURI(index); + } + + @Override + String getAttributeName(int index) { + return processNamespaces ? attributes.getLocalName(index) + : attributes.getQName(index); + } + + @Override + String getAttributeValue(int index) { + return attributes.getValue(index); + } + + @Override + String getAttributeValue(String namespace, String name) { + if (namespace == null) { + namespace = ""; + } + + return attributes.getValue(namespace, name); + } + + @Override + int getType() { + return START_TAG; + } + } + + static class EndTagEvent extends Event { + + final String namespace; + final String localName; + + EndTagEvent(String namespace, String localName, int depth, + NamespaceStack namespaceStack) { + super(depth, namespaceStack); + this.namespace = namespace; + this.localName = localName; + } + + @Override + String getName() { + return this.localName; + } + + @Override + String getNamespace() { + return this.namespace; + } + + @Override + int getType() { + return END_TAG; + } + } + + static class TextEvent extends Event { + + final StringBuilder builder; + + public TextEvent(int initialCapacity, int depth, + NamespaceStack namespaceStack) { + super(depth, namespaceStack); + this.builder = new StringBuilder(initialCapacity); + } + + @Override + int getType() { + return TEXT; + } + + @Override + StringBuilder getText() { + return this.builder; + } + + void append(char[] text, int start, int length) { + builder.append(text, start, length); + } + } + + static class EndDocumentEvent extends Event { + + EndDocumentEvent() { + super(0, NamespaceStack.EMPTY); + } + + @Override + Event getNext() { + throw new IllegalStateException("End of document."); + } + + @Override + void setNext(Event next) { + throw new IllegalStateException("End of document."); + } + + @Override + int getType() { + return END_DOCUMENT; + } + } + + /** + * Encapsulates the parsing context of the current document. + */ + abstract class Document { + + final String encoding; + final ExpatParser parser; + final boolean processNamespaces; + + TextEvent textEvent = null; + boolean finished = false; + + Document(String encoding, boolean processNamespaces) { + this.encoding = encoding; + this.processNamespaces = processNamespaces; + + ExpatReader xmlReader = new ExpatReader(); + xmlReader.setContentHandler(new SaxHandler()); + + this.parser = new ExpatParser( + encoding, xmlReader, processNamespaces, null, null); + } + + /** Namespace stack builder. */ + NamespaceStack.Builder namespaceStackBuilder + = new NamespaceStack.Builder(); + + Event currentEvent = new StartDocumentEvent(); + Event last = currentEvent; + + /** + * Sends some more XML to the parser. + */ + void pump() throws IOException, XmlPullParserException { + if (this.finished) { + return; + } + + int length = buffer(); + + // End of document. + if (length == -1) { + this.finished = true; + if (!relaxed) { + try { + parser.finish(); + } catch (SAXException e) { + throw new XmlPullParserException( + "Premature end of document.", ExpatPullParser.this, e); + } + } + add(new EndDocumentEvent()); + return; + } + + if (length == 0) { + return; + } + + flush(parser, length); + } + + /** + * Reads data into the buffer. + * + * @return the length of data buffered or {@code -1} if we've reached + * the end of the data. + */ + abstract int buffer() throws IOException; + + /** + * Sends buffered data to the parser. + * + * @param parser the parser to flush to + * @param length of data buffered + */ + abstract void flush(ExpatParser parser, int length) + throws XmlPullParserException; + + /** + * Adds an event. + */ + void add(Event event) { + // Flush pre-exising text event if necessary. + if (textEvent != null) { + last.setNext(textEvent); + last = textEvent; + textEvent = null; + } + + last.setNext(event); + last = event; + } + + /** + * Moves to the next event in the queue. + * + * @return type of next event + */ + int dequeue() throws XmlPullParserException, IOException { + Event next; + + while ((next = currentEvent.getNext()) == null) { + pump(); + } + + currentEvent.next = null; + currentEvent = next; + + return currentEvent.getType(); + } + + String getEncoding() { + return this.encoding; + } + + int getDepth() { + return currentEvent.getDepth(); + } + + /** + * Returns true if we're on a start element and the next event is + * its corresponding end element. + * + * @throws XmlPullParserException if we aren't on a start element + */ + boolean isCurrentElementEmpty() throws XmlPullParserException { + if (currentEvent.getType() != START_TAG) { + throw new XmlPullParserException(NOT_A_START_TAG); + } + + Event next; + + try { + while ((next = currentEvent.getNext()) == null) { + pump(); + } + } catch (IOException ex) { + throw new XmlPullParserException(ex.toString()); + } + + return next.getType() == END_TAG; + } + + private class SaxHandler implements ContentHandler { + + int depth = 0; + + public void startPrefixMapping(String prefix, String uri) + throws SAXException { + // Depth + 1--we aren't actually in the element yet. + namespaceStackBuilder.push(prefix, uri, depth + 1); + } + + public void startElement(String uri, String localName, String qName, + Attributes attributes) { + String name = processNamespaces ? localName : qName; + + add(new StartTagEvent(uri, name, parser, ++this.depth, + namespaceStackBuilder.build(), processNamespaces)); + } + + public void endElement(String uri, String localName, String qName) { + String name = processNamespaces ? localName : qName; + + int depth = this.depth--; + add(new EndTagEvent(uri, name, depth, + namespaceStackBuilder.build())); + namespaceStackBuilder.pop(depth); + } + + public void characters(char ch[], int start, int length) { + // Ignore empty strings. + if (length == 0) { + return; + } + + // Start a new text event if necessary. + if (textEvent == null) { + textEvent = new TextEvent(length, this.depth, + namespaceStackBuilder.build()); + } + + // Append to an existing text event. + textEvent.append(ch, start, length); + } + + public void setDocumentLocator(Locator locator) {} + public void startDocument() throws SAXException {} + public void endDocument() throws SAXException {} + public void endPrefixMapping(String prefix) throws SAXException {} + public void ignorableWhitespace(char ch[], int start, int length) + throws SAXException {} + public void processingInstruction(String target, String data) + throws SAXException {} + public void skippedEntity(String name) throws SAXException {} + } + } + + class CharDocument extends Document { + + final char[] buffer = new char[BUFFER_SIZE / 2]; + final Reader in; + + CharDocument(Reader in, boolean processNamespaces) { + super("UTF-16", processNamespaces); + this.in = in; + } + + @Override + int buffer() throws IOException { + return in.read(buffer); + } + + @Override + void flush(ExpatParser parser, int length) + throws XmlPullParserException { + try { + parser.append(buffer, 0, length); + } catch (SAXException e) { + throw new XmlPullParserException( + "Error parsing document.", ExpatPullParser.this, e); + } + } + } + + class ByteDocument extends Document { + + final byte[] buffer = new byte[BUFFER_SIZE]; + final InputStream in; + + ByteDocument(InputStream in, String encoding, + boolean processNamespaces) { + super(encoding, processNamespaces); + this.in = in; + } + + @Override + int buffer() throws IOException { + return in.read(buffer); + } + + @Override + void flush(ExpatParser parser, int length) + throws XmlPullParserException { + try { + parser.append(buffer, 0, length); + } catch (SAXException e) { + throw new XmlPullParserException( + "Error parsing document.", ExpatPullParser.this, e); + } + } + } +} diff --git a/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java b/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java new file mode 100644 index 0000000..a6a83a0 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml; + +import org.xml.sax.ContentHandler; +import org.xml.sax.DTDHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.LexicalHandler; + +import java.io.IOException; +import java.io.Reader; +import java.io.InputStream; +import java.util.logging.Logger; + +/** + * SAX wrapper around Expat. Interns strings. Does not support validation. + * Does not support {@link DTDHandler}. + */ +public class ExpatReader implements XMLReader { + + private static final Logger logger + = Logger.getLogger(ExpatReader.class.getName()); + + /* + * ExpatParser accesses these fields directly during parsing. The user + * should be able to safely change them during parsing. + */ + /*package*/ ContentHandler contentHandler; + /*package*/ EntityResolver entityResolver; + /*package*/ ErrorHandler errorHandler; + /*package*/ LexicalHandler lexicalHandler; + + private boolean processNamespaces = true; + private boolean processNamespacePrefixes = false; + + private static final String LEXICAL_HANDLER_PROPERTY + = "http://xml.org/sax/properties/lexical-handler"; + + private static class Feature { + + private static final String BASE_URI = "http://xml.org/sax/features/"; + + private static final String VALIDATION = BASE_URI + "validation"; + private static final String NAMESPACES = BASE_URI + "namespaces"; + private static final String NAMESPACE_PREFIXES + = BASE_URI + "namespace-prefixes"; + private static final String STRING_INTERNING + = BASE_URI + "string-interning"; + } + + public boolean getFeature(String name) + throws SAXNotRecognizedException, SAXNotSupportedException { + if (name == null) { + throw new NullPointerException("name"); + } + + if (name.equals(Feature.VALIDATION)) { + return false; + } + + if (name.equals(Feature.NAMESPACES)) { + return processNamespaces; + } + + if (name.equals(Feature.NAMESPACE_PREFIXES)) { + return processNamespacePrefixes; + } + + if (name.equals(Feature.STRING_INTERNING)) { + return true; + } + + throw new SAXNotRecognizedException(name); + } + + public void setFeature(String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException { + if (name == null) { + throw new NullPointerException("name"); + } + + if (name.equals(Feature.VALIDATION)) { + if (value) { + throw new SAXNotSupportedException("Cannot enable " + name); + } else { + // Default. + return; + } + } + + if (name.equals(Feature.NAMESPACES)) { + processNamespaces = value; + return; + } + + if (name.equals(Feature.NAMESPACE_PREFIXES)) { + processNamespacePrefixes = value; + return; + } + + if (name.equals(Feature.STRING_INTERNING)) { + if (value) { + // Default. + return; + } else { + throw new SAXNotSupportedException("Cannot disable " + name); + } + } + + throw new SAXNotRecognizedException(name); + } + + public Object getProperty(String name) + throws SAXNotRecognizedException, SAXNotSupportedException { + if (name == null) { + throw new NullPointerException("name"); + } + + if (name.equals(LEXICAL_HANDLER_PROPERTY)) { + return lexicalHandler; + } + + throw new SAXNotRecognizedException(name); + } + + public void setProperty(String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException { + if (name == null) { + throw new NullPointerException("name"); + } + + if (name.equals(LEXICAL_HANDLER_PROPERTY)) { + // The object must implement LexicalHandler + if (value instanceof LexicalHandler) { + this.lexicalHandler = (LexicalHandler) value; + return; + } + throw new SAXNotSupportedException("value doesn't implement " + + "org.xml.sax.ext.LexicalHandler"); + } + + throw new SAXNotRecognizedException(name); + } + + public void setEntityResolver(EntityResolver resolver) { + this.entityResolver = resolver; + } + + public EntityResolver getEntityResolver() { + return entityResolver; + } + + /** + * Not implemented. + */ + public void setDTDHandler(DTDHandler ignored) { + logger.warning("DTD handlers aren't supported."); + } + + /** + * Always returns null. + */ + public DTDHandler getDTDHandler() { + return null; + } + + public void setContentHandler(ContentHandler handler) { + this.contentHandler = handler; + } + + public ContentHandler getContentHandler() { + return this.contentHandler; + } + + public void setErrorHandler(ErrorHandler handler) { + this.errorHandler = handler; + } + + public ErrorHandler getErrorHandler() { + return errorHandler; + } + + /** + * Returns the current lexical handler. + * + * @return the current lexical handler, or null if none has been registered + * @see #setLexicalHandler + */ + public LexicalHandler getLexicalHandler() { + return lexicalHandler; + } + + /** + * Registers a lexical event handler. Supports neither + * {@link LexicalHandler#startEntity(String)} nor + * {@link LexicalHandler#endEntity(String)}. + * + * <p>If the application does not register a lexical handler, all + * lexical events reported by the SAX parser will be silently + * ignored.</p> + * + * <p>Applications may register a new or different handler in the + * middle of a parse, and the SAX parser must begin using the new + * handler immediately.</p> + * + * @param lexicalHandler listens for lexical events + * @see #getLexicalHandler() + */ + public void setLexicalHandler(LexicalHandler lexicalHandler) { + this.lexicalHandler = lexicalHandler; + } + + /** + * Returns true if this SAX parser processes namespaces. + * + * @see #setNamespaceProcessingEnabled(boolean) + */ + public boolean isNamespaceProcessingEnabled() { + return processNamespaces; + } + + /** + * Enables or disables namespace processing. Set to true by default. If you + * enable namespace processing, the parser will invoke + * {@link ContentHandler#startPrefixMapping(String, String)} and + * {@link ContentHandler#endPrefixMapping(String)}, and it will filter + * out namespace declarations from element attributes. + * + * @see #isNamespaceProcessingEnabled() + */ + public void setNamespaceProcessingEnabled(boolean processNamespaces) { + this.processNamespaces = processNamespaces; + } + + public void parse(InputSource input) throws IOException, SAXException { + if (processNamespacePrefixes == processNamespaces) { + /* + * Expat has XML_SetReturnNSTriplet, but that still doesn't + * include xmlns attributes like this feature requires. We may + * have to implement namespace processing ourselves if we want + * this (not too difficult). We obviously "support" namespace + * prefixes if namespaces are disabled. + */ + throw new SAXNotSupportedException("The 'namespace-prefix' " + + "feature is not supported while the 'namespaces' " + + "feature is enabled."); + } + + // Try the character stream. + Reader reader = input.getCharacterStream(); + if (reader != null) { + try { + parse(reader, input.getPublicId(), input.getSystemId()); + } finally { + // TODO: Don't eat original exception when close() throws. + reader.close(); + } + return; + } + + // Try the byte stream. + InputStream in = input.getByteStream(); + String encoding = input.getEncoding(); + if (in != null) { + try { + parse(in, encoding, input.getPublicId(), input.getSystemId()); + } finally { + // TODO: Don't eat original exception when close() throws. + in.close(); + } + return; + } + + String systemId = input.getSystemId(); + if (systemId == null) { + throw new SAXException("No input specified."); + } + + // Try the system id. + in = ExpatParser.openUrl(systemId); + try { + parse(in, encoding, input.getPublicId(), systemId); + } finally { + in.close(); + } + } + + private void parse(Reader in, String publicId, String systemId) + throws IOException, SAXException { + ExpatParser parser = new ExpatParser( + ExpatParser.CHARACTER_ENCODING, + this, + processNamespaces, + publicId, + systemId + ); + parser.parseDocument(in); + } + + private void parse(InputStream in, String encoding, String publicId, + String systemId) throws IOException, SAXException { + ExpatParser parser = new ExpatParser( + encoding, + this, + processNamespaces, + publicId, + systemId + ); + parser.parseDocument(in); + } + + public void parse(String systemId) throws IOException, SAXException { + parse(new InputSource(systemId)); + } +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java new file mode 100644 index 0000000..a39e0c4 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class AttrImpl extends NodeImpl implements Attr { + + // Maintained by ElementImpl. + ElementImpl ownerElement; + + private boolean namespaceAware; + + private String namespaceURI; + + private String localName; + + private String prefix; + + private String value; + + AttrImpl(DocumentImpl document, String namespaceURI, String qualifiedName) { + super(document); + + namespaceAware = true; + this.namespaceURI = namespaceURI; + + if (qualifiedName == null || "".equals(qualifiedName)) { + throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); + } + + int prefixSeparator = qualifiedName.lastIndexOf(":"); + if (prefixSeparator != -1) { + setPrefix(qualifiedName.substring(0, prefixSeparator)); + qualifiedName = qualifiedName.substring(prefixSeparator + 1); + } + + localName = qualifiedName; + + if ("".equals(localName)) { + throw new DOMException(DOMException.NAMESPACE_ERR, localName); + } + + if ("xmlns".equals(localName) && !"http://www.w3.org/2000/xmlns/".equals(namespaceURI)) { + throw new DOMException(DOMException.NAMESPACE_ERR, localName); + } + + if (!document.isXMLIdentifier(localName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, localName); + } + + value = ""; + } + + AttrImpl(DocumentImpl document, String name) { + super(document); + + this.namespaceAware = false; + + int prefixSeparator = name.lastIndexOf(":"); + if (prefixSeparator != -1) { + String prefix = name.substring(0, prefixSeparator); + String localName = name.substring(prefixSeparator + 1); + + if (!document.isXMLIdentifier(prefix) || !document.isXMLIdentifier(localName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); + } + } else { + if (!document.isXMLIdentifier(name)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); + } + } + + this.localName = name; + } + + @Override + public String getLocalName() { + return namespaceAware ? localName : null; + } + + public String getName() { + return (prefix != null ? prefix + ":" : "") + localName; + } + + @Override + public String getNamespaceURI() { + return namespaceURI; + } + + @Override + public String getNodeName() { + return getName(); + } + + public short getNodeType() { + return Node.ATTRIBUTE_NODE; + } + + @Override + public String getNodeValue() { + return getValue(); + } + + public Element getOwnerElement() { + return ownerElement; + } + + @Override + public String getPrefix() { + return prefix; + } + + public boolean getSpecified() { + return value != null; + } + + public String getValue() { + return value; + } + + @Override + public void setNodeValue(String value) throws DOMException { + setValue(value); + } + + @Override + public void setPrefix(String prefix) { + if (!namespaceAware) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + + if (prefix != null) { + if (namespaceURI == null || !document.isXMLIdentifier(prefix) || "xmlns".equals(prefix)) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + + if ("xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(namespaceURI)) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + } + + this.prefix = prefix; + } + + public void setValue(String value) throws DOMException { + this.value = value; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/CDATASectionImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/CDATASectionImpl.java new file mode 100644 index 0000000..f54e5f4 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/CDATASectionImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.CDATASection; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class CDATASectionImpl extends TextImpl implements CDATASection { + + CDATASectionImpl(DocumentImpl document, String data) { + super(document, data); + } + + @Override + public String getNodeName() { + return "#cdata-section"; + } + + @Override + public short getNodeType() { + return Node.CDATA_SECTION_NODE; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/CharacterDataImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/CharacterDataImpl.java new file mode 100644 index 0000000..010cb22 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/CharacterDataImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.CharacterData; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public abstract class CharacterDataImpl extends LeafNodeImpl implements + CharacterData { + + private StringBuffer buffer; + + CharacterDataImpl(DocumentImpl document, String data) { + super(document); + setData(data); + } + + public void appendData(String arg) throws DOMException { + buffer.append(arg); + } + + public void deleteData(int offset, int count) throws DOMException { + buffer.delete(offset, offset + count); + } + + public String getData() throws DOMException { + return buffer.toString(); + } + + public int getLength() { + return buffer.length(); + } + + @Override + public String getNodeValue() { + return getData(); + } + + public void insertData(int offset, String arg) throws DOMException { + try { + buffer.insert(offset, arg); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, null); + } + } + + public void replaceData(int offset, int count, String arg) + throws DOMException { + try { + buffer.replace(offset, offset + count, arg); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, null); + } + } + + public void setData(String data) throws DOMException { + buffer = new StringBuffer(data); + } + + public String substringData(int offset, int count) throws DOMException { + try { + return buffer.substring(offset, offset + count); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, null); + } + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/CommentImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/CommentImpl.java new file mode 100644 index 0000000..6c1f446 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/CommentImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Comment; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class CommentImpl extends CharacterDataImpl implements Comment { + + CommentImpl(DocumentImpl document, String data) { + super(document, data); + } + + @Override + public String getNodeName() { + return "#comment"; + } + + @Override + public short getNodeType() { + return Node.COMMENT_NODE; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java new file mode 100644 index 0000000..4e13d19 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class DOMImplementationImpl implements DOMImplementation { + + // Singleton instance. + private static DOMImplementationImpl instance; + + DOMImplementationImpl() { + } + + public Document createDocument(String namespaceURI, String qualifiedName, + DocumentType doctype) throws DOMException { + return new DocumentImpl(this, namespaceURI, qualifiedName, doctype); + } + + public DocumentType createDocumentType(String qualifiedName, + String publicId, String systemId) throws DOMException { + return new DocumentTypeImpl(this, qualifiedName, publicId, systemId); + } + + public boolean hasFeature(String feature, String version) { + // We claim to support DOM Core Level 1 & 2, nothing else. + + if ("Core".equalsIgnoreCase(feature) || "XML".equalsIgnoreCase(feature)) { + if (version == null || "".equals(version) || "1.0".equals(version) || "2.0".equals(version)) { + return true; + } + } + + return false; + } + + /** + * Requests the singleton instance of the class. Creates it first, if + * necessary. + * + * @return The singleton Android DOMImplementationImpl instance. + */ + public static DOMImplementationImpl getInstance() { + if (instance == null) { + instance = new DOMImplementationImpl(); + } + + return instance; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentFragmentImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentFragmentImpl.java new file mode 100644 index 0000000..88e6175 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentFragmentImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class DocumentFragmentImpl extends InnerNodeImpl implements + DocumentFragment { + + DocumentFragmentImpl(DocumentImpl document) { + super(document); + } + + @Override + public String getNodeName() { + return "#document-fragment"; + } + + @Override + public short getNodeType() { + return Node.DOCUMENT_FRAGMENT_NODE; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java new file mode 100644 index 0000000..fbb1cac --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.CharacterData; +import org.w3c.dom.Comment; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class DocumentImpl extends InnerNodeImpl implements Document { + + private DOMImplementation domImplementation; + + DocumentImpl(DOMImplementationImpl impl, String namespaceURI, + String qualifiedName, DocumentType doctype) { + super(null); + + this.domImplementation = impl; + // this.document = this; + + if (doctype != null) { + appendChild(doctype); + } + + if (qualifiedName != null) { + appendChild(createElementNS(namespaceURI, qualifiedName)); + } + } + + private static boolean isXMLIdentifierStart(char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_'); + } + + private static boolean isXMLIdentifierPart(char c) { + return isXMLIdentifierStart(c) || (c >= '0' && c <= '9') || (c == '-') || (c == '.'); + } + + static boolean isXMLIdentifier(String s) { + if (s.length() == 0) { + return false; + } + + if (!isXMLIdentifierStart(s.charAt(0))) { + return false; + } + + for (int i = 1; i < s.length(); i++) { + if (!isXMLIdentifierPart(s.charAt(i))) { + return false; + } + } + + return true; + } + + /** + * Clones a node and (if requested) its children. The source node(s) may + * have been created by a different DocumentImpl or even DOM implementation. + * + * @param node The node to clone. + * @param deep If true, a deep copy is created (including all child nodes). + * + * @return The new node. + */ + Node cloneNode(Node node, boolean deep) throws DOMException { + Node target; + + switch (node.getNodeType()) { + case Node.ATTRIBUTE_NODE: { + Attr source = (Attr)node; + target = createAttributeNS(source.getNamespaceURI(), source.getLocalName()); + target.setPrefix(source.getPrefix()); + target.setNodeValue(source.getNodeValue()); + break; + } + case Node.CDATA_SECTION_NODE: { + CharacterData source = (CharacterData)node; + target = createCDATASection(source.getData()); + break; + } + case Node.COMMENT_NODE: { + Comment source = (Comment)node; + target = createComment(source.getData()); + break; + } + case Node.DOCUMENT_FRAGMENT_NODE: { + // Source is irrelevant in this case. + target = createDocumentFragment(); + break; + } + case Node.DOCUMENT_NODE: { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a Document node"); + } + case Node.DOCUMENT_TYPE_NODE: { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a DocumentType node"); + } + case Node.ELEMENT_NODE: { + Element source = (Element)node; + target = createElementNS(source.getNamespaceURI(), source.getLocalName()); + target.setPrefix(source.getPrefix()); + + NamedNodeMap map = source.getAttributes(); + for (int i = 0; i < map.getLength(); i++) { + Attr attr = (Attr)map.item(i); + ((Element)target).setAttributeNodeNS((Attr)cloneNode(attr, deep)); + } + break; + } + case Node.ENTITY_NODE: { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone an Entity node"); + } + case Node.ENTITY_REFERENCE_NODE: { + EntityReference source = (EntityReference)node; + target = createEntityReference(source.getNodeName()); + break; + } + case Node.NOTATION_NODE: { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a Notation node"); + } + case Node.PROCESSING_INSTRUCTION_NODE: { + ProcessingInstruction source = (ProcessingInstruction)node; + target = createProcessingInstruction(source.getTarget(), source.getData()); + break; + } + case Node.TEXT_NODE: { + Text source = (Text)node; + target = createTextNode(source.getData()); + break; + } + default: { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone unknown node type " + node.getNodeType() + " (" + node.getClass().getSimpleName() + ")"); + } + } + + if (deep) { + NodeList list = node.getChildNodes(); + for (int i = 0; i < list.getLength(); i++) { + Node child = cloneNode(list.item(i), deep); + target.appendChild(child); + } + } + + return target; + } + + public AttrImpl createAttribute(String name) throws DOMException { + return new AttrImpl(this, name); + } + + public Attr createAttributeNS(String namespaceURI, String qualifiedName) + throws DOMException { + return new AttrImpl(this, namespaceURI, qualifiedName); + } + + public CDATASection createCDATASection(String data) throws DOMException { + return new CDATASectionImpl(this, data); + } + + public Comment createComment(String data) { + return new CommentImpl(this, data); + } + + public DocumentFragment createDocumentFragment() { + return new DocumentFragmentImpl(this); + } + + public Element createElement(String tagName) throws DOMException { + return new ElementImpl(this, tagName); + } + + public Element createElementNS(String namespaceURI, String qualifiedName) + throws DOMException { + return new ElementImpl(this, namespaceURI, qualifiedName); + } + + public EntityReference createEntityReference(String name) + throws DOMException { + return new EntityReferenceImpl(this, name); + } + + public ProcessingInstruction createProcessingInstruction(String target, + String data) throws DOMException { + return new ProcessingInstructionImpl(this, target, data); + } + + public Text createTextNode(String data) { + return new TextImpl(this, data); + } + + public DocumentType getDoctype() { + for (int i = 0; i < children.size(); i++) { + if (children.get(i) instanceof DocumentType) { + return (DocumentType) children.get(i); + } + } + + return null; + } + + public Element getDocumentElement() { + for (int i = 0; i < children.size(); i++) { + if (children.get(i) instanceof Element) { + return (Element) children.get(i); + } + } + + return null; + } + + public Element getElementById(String elementId) { + ElementImpl root = (ElementImpl) getDocumentElement(); + + return (root == null ? null : root.getElementById(elementId)); + } + + public NodeList getElementsByTagName(String tagname) { + ElementImpl root = (ElementImpl) getDocumentElement(); + + return (root == null ? new NodeListImpl() + : root.getElementsByTagName(tagname)); + } + + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { + ElementImpl root = (ElementImpl) getDocumentElement(); + + return (root == null ? new NodeListImpl() : root.getElementsByTagNameNS( + namespaceURI, localName)); + } + + public DOMImplementation getImplementation() { + return domImplementation; + } + + @Override + public String getNodeName() { + return "#document"; + } + + @Override + public short getNodeType() { + return Node.DOCUMENT_NODE; + } + + public Node importNode(Node importedNode, boolean deep) throws DOMException { + return cloneNode(importedNode, deep); + } + + @Override + public Node insertChildAt(Node newChild, int index) throws DOMException { + // Make sure we have at most one root element and one DTD element. + if (newChild instanceof Element && getDocumentElement() != null) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, + "Only one root element allowed"); + } else if (newChild instanceof DocumentType && getDoctype() != null) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, + "Only one DOCTYPE element allowed"); + } + + return super.insertChildAt(newChild, index); + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java new file mode 100644 index 0000000..df40d4b --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.DOMException; +import org.w3c.dom.DocumentType; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class DocumentTypeImpl extends LeafNodeImpl implements DocumentType { + + private String qualifiedName; + + private String publicId; + + private String systemId; + + DocumentTypeImpl(DOMImplementationImpl impl, String qualifiedName, + String publicId, String systemId) { + super(null); + + if (qualifiedName == null || "".equals(qualifiedName)) { + throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); + } + + int prefixSeparator = qualifiedName.lastIndexOf(":"); + if (prefixSeparator != -1) { + String prefix = qualifiedName.substring(0, prefixSeparator); + String localName = qualifiedName.substring(prefixSeparator + 1); + + if (!DocumentImpl.isXMLIdentifier(prefix)) { + throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); + } + + if (!DocumentImpl.isXMLIdentifier(localName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName); + } + } else { + if (!DocumentImpl.isXMLIdentifier(qualifiedName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName); + } + } + + this.qualifiedName = qualifiedName; + this.publicId = publicId; + this.systemId = systemId; + } + + @Override + public String getNodeName() { + return qualifiedName; + } + + @Override + public short getNodeType() { + return Node.DOCUMENT_TYPE_NODE; + } + + public NamedNodeMap getEntities() { + // TODO Dummy. Implement this later, if at all (we're DOM level 2 only). + return null; + } + + public String getInternalSubset() { + // TODO Dummy. Implement this later, if at all (we're DOM level 2 only). + return null; + } + + public String getName() { + return qualifiedName; + } + + public NamedNodeMap getNotations() { + // TODO Dummy. Implement this later, if at all (we're DOM level 2 only). + return null; + } + + public String getPublicId() { + return publicId; + } + + public String getSystemId() { + return systemId; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java new file mode 100644 index 0000000..3b44ff3 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class ElementImpl extends InnerNodeImpl implements Element { + + private boolean namespaceAware; + + private String namespaceURI; + + private String prefix; + + private String localName; + + private List<AttrImpl> attributes = new ArrayList<AttrImpl>(); + + ElementImpl(DocumentImpl document, String namespaceURI, String qualifiedName) { + super(document); + + this.namespaceAware = true; + this.namespaceURI = namespaceURI; + + if (qualifiedName == null || "".equals(qualifiedName)) { + throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); + } + + int p = qualifiedName.lastIndexOf(":"); + if (p != -1) { + setPrefix(qualifiedName.substring(0, p)); + qualifiedName = qualifiedName.substring(p + 1); + } + + if (!document.isXMLIdentifier(qualifiedName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName); + } + + this.localName = qualifiedName; + } + + ElementImpl(DocumentImpl document, String name) { + super(document); + + this.namespaceAware = false; + + int p = name.lastIndexOf(":"); + if (p != -1) { + String prefix = name.substring(0, p); + String localName = name.substring(p + 1); + + if (!document.isXMLIdentifier(prefix) || !document.isXMLIdentifier(localName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); + } + } else { + if (!document.isXMLIdentifier(name)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); + } + } + + this.localName = name; + } + + private int indexOfAttribute(String name) { + for (int i = 0; i < attributes.size(); i++) { + AttrImpl attr = attributes.get(i); + if (attr.matchesName(name, false)) { + return i; + } + } + + return -1; + } + + private int indexOfAttributeNS(String namespaceURI, String localName) { + for (int i = 0; i < attributes.size(); i++) { + AttrImpl attr = attributes.get(i); + if (attr.matchesNameNS(namespaceURI, localName, false)) { + return i; + } + } + + return -1; + } + + public String getAttribute(String name) { + Attr attr = getAttributeNode(name); + + if (attr == null) { + return ""; + } + + return attr.getValue(); + } + + public String getAttributeNS(String namespaceURI, String localName) { + Attr attr = getAttributeNodeNS(namespaceURI, localName); + + if (attr == null) { + return ""; + } + + return attr.getValue(); + } + + public Attr getAttributeNode(String name) { + int i = indexOfAttribute(name); + + if (i == -1) { + return null; + } + + return attributes.get(i); + } + + public Attr getAttributeNodeNS(String namespaceURI, String localName) { + int i = indexOfAttributeNS(namespaceURI, localName); + + if (i == -1) { + return null; + } + + return attributes.get(i); + } + + @Override + public NamedNodeMap getAttributes() { + return new ElementAttrNamedNodeMapImpl(); + } + + Element getElementById(String name) { + if (name.equals(getAttribute("id"))) { + return this; + } + + for (NodeImpl node : children) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = ((ElementImpl) node).getElementById(name); + if (element != null) { + return element; + } + } + } + + return null; + } + + public NodeList getElementsByTagName(String name) { + NodeListImpl list = new NodeListImpl(); + getElementsByTagName(list, name); + return list; + } + + void getElementsByTagName(NodeListImpl list, String name) { + if (matchesName(name, true)) { + list.add(this); + } + + for (NodeImpl node : children) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + ((ElementImpl) node).getElementsByTagName(list, name); + } + } + } + + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { + NodeListImpl list = new NodeListImpl(); + getElementsByTagNameNS(list, namespaceURI, localName); + return list; + } + + void getElementsByTagNameNS(NodeListImpl list, String namespaceURI, + String localName) { + if (matchesNameNS(namespaceURI, localName, true)) { + list.add(this); + } + + for (NodeImpl node : children) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + ((ElementImpl) node).getElementsByTagNameNS(list, namespaceURI, + localName); + } + } + } + + @Override + public String getLocalName() { + return namespaceAware ? localName : null; + } + + @Override + public String getNamespaceURI() { + return namespaceURI; + } + + @Override + public String getNodeName() { + return getTagName(); + } + + public short getNodeType() { + return Node.ELEMENT_NODE; + } + + @Override + public String getPrefix() { + return prefix; + } + + public String getTagName() { + return (prefix != null ? prefix + ":" : "") + localName; + } + + public boolean hasAttribute(String name) { + return indexOfAttribute(name) != -1; + } + + public boolean hasAttributeNS(String namespaceURI, String localName) { + return indexOfAttributeNS(namespaceURI, localName) != -1; + } + + @Override + public boolean hasAttributes() { + return !attributes.isEmpty(); + } + + public void removeAttribute(String name) throws DOMException { + int i = indexOfAttribute(name); + + if (i != -1) { + attributes.remove(i); + } + } + + public void removeAttributeNS(String namespaceURI, String localName) + throws DOMException { + int i = indexOfAttributeNS(namespaceURI, localName); + + if (i != -1) { + attributes.remove(i); + } + } + + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + AttrImpl oldAttrImpl = (AttrImpl) oldAttr; + + if (oldAttrImpl.getOwnerElement() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, null); + } + + attributes.remove(oldAttr); + oldAttrImpl.ownerElement = null; + + return oldAttrImpl; + } + + public void setAttribute(String name, String value) throws DOMException { + Attr attr = getAttributeNode(name); + + if (attr == null) { + attr = document.createAttribute(name); + setAttributeNode(attr); + } + + attr.setValue(value); + } + + public void setAttributeNS(String namespaceURI, String qualifiedName, + String value) throws DOMException { + Attr attr = getAttributeNodeNS(namespaceURI, qualifiedName); + + if (attr == null) { + attr = document.createAttributeNS(namespaceURI, qualifiedName); + setAttributeNodeNS(attr); + } + + attr.setValue(value); + } + + public Attr setAttributeNode(Attr newAttr) throws DOMException { + AttrImpl newAttrImpl = (AttrImpl) newAttr; + + if (newAttrImpl.document != this.getOwnerDocument()) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + if (newAttrImpl.getOwnerElement() != null) { + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null); + } + + AttrImpl oldAttrImpl = null; + + int i = indexOfAttribute(newAttr.getName()); + if (i != -1) { + oldAttrImpl = attributes.get(i); + attributes.remove(i); + } + + attributes.add(newAttrImpl); + newAttrImpl.ownerElement = this; + + return oldAttrImpl; + } + + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + AttrImpl newAttrImpl = (AttrImpl) newAttr; + + if (newAttrImpl.document != this.getOwnerDocument()) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + if (newAttrImpl.getOwnerElement() != null) { + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null); + } + + AttrImpl oldAttrImpl = null; + + int i = indexOfAttributeNS(newAttr.getNamespaceURI(), newAttr.getLocalName()); + if (i != -1) { + oldAttrImpl = attributes.get(i); + attributes.remove(i); + } + + attributes.add(newAttrImpl); + newAttrImpl.ownerElement = this; + + return oldAttrImpl; + } + + @Override + public void setPrefix(String prefix) { + if (!namespaceAware) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + + if (prefix != null) { + if (namespaceURI == null || !document.isXMLIdentifier(prefix)) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + + if ("xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(namespaceURI)) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + } + + this.prefix = prefix; + } + + public class ElementAttrNamedNodeMapImpl implements NamedNodeMap { + + public int getLength() { + return ElementImpl.this.attributes.size(); + } + + private int indexOfItem(String name) { + return ElementImpl.this.indexOfAttribute(name); + } + + private int indexOfItemNS(String namespaceURI, String localName) { + return ElementImpl.this.indexOfAttributeNS(namespaceURI, localName); + } + + public Node getNamedItem(String name) { + return ElementImpl.this.getAttributeNode(name); + } + + public Node getNamedItemNS(String namespaceURI, String localName) { + return ElementImpl.this.getAttributeNodeNS(namespaceURI, localName); + } + + public Node item(int index) { + return ElementImpl.this.attributes.get(index); + } + + public Node removeNamedItem(String name) throws DOMException { + int i = indexOfItem(name); + + if (i == -1) { + throw new DOMException(DOMException.NOT_FOUND_ERR, null); + } + + return ElementImpl.this.attributes.remove(i); + } + + public Node removeNamedItemNS(String namespaceURI, String localName) + throws DOMException { + int i = indexOfItemNS(namespaceURI, localName); + + if (i == -1) { + throw new DOMException(DOMException.NOT_FOUND_ERR, null); + } + + return ElementImpl.this.attributes.remove(i); + } + + public Node setNamedItem(Node arg) throws DOMException { + if (!(arg instanceof Attr)) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + return ElementImpl.this.setAttributeNode((Attr)arg); + } + + public Node setNamedItemNS(Node arg) throws DOMException { + if (!(arg instanceof Attr)) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + return ElementImpl.this.setAttributeNodeNS((Attr)arg); + } + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/EntityImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/EntityImpl.java new file mode 100644 index 0000000..463e863 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/EntityImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Document; +import org.w3c.dom.Entity; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class EntityImpl extends NodeImpl implements Entity { + + private String notationName; + + private String publicID; + + private String systemID; + + EntityImpl(DocumentImpl document, String notationName, String publicID, + String systemID) { + super(document); + this.notationName = notationName; + this.publicID = publicID; + this.systemID = systemID; + } + + @Override + public String getNodeName() { + return getNotationName(); + } + + @Override + public short getNodeType() { + return Node.ENTITY_NODE; + } + + public String getNotationName() { + return notationName; + } + + public String getPublicId() { + return publicID; + } + + public String getSystemId() { + return systemID; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/EntityReferenceImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/EntityReferenceImpl.java new file mode 100644 index 0000000..f612a15 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/EntityReferenceImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Document; +import org.w3c.dom.EntityReference; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class EntityReferenceImpl extends LeafNodeImpl implements EntityReference { + + private String name; + + EntityReferenceImpl(DocumentImpl document, String name) { + super(document); + this.name = name; + } + + @Override + public String getNodeName() { + return name; + } + + @Override + public short getNodeType() { + return Node.ENTITY_REFERENCE_NODE; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java new file mode 100644 index 0000000..f71d289 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + * <p> + * This class represents a Node that has a parent Node as well as (potentially) + * a number of children. + */ +public abstract class InnerNodeImpl extends LeafNodeImpl { + + // Maintained by LeafNodeImpl and ElementImpl. + List<LeafNodeImpl> children = new ArrayList<LeafNodeImpl>(); + + public InnerNodeImpl(DocumentImpl document) { + super(document); + } + + public Node appendChild(Node newChild) throws DOMException { + return insertChildAt(newChild, children.size()); + } + + public NodeList getChildNodes() { + NodeListImpl list = new NodeListImpl(); + + for (NodeImpl node : children) { + list.add(node); + } + + return list; + } + + public Node getFirstChild() { + return (!children.isEmpty() ? children.get(0) : null); + } + + public Node getLastChild() { + return (!children.isEmpty() ? children.get(children.size() - 1) : null); + } + + public Node getNextSibling() { + if (parent == null || index >= parent.children.size()) { + return null; + } + + return parent.children.get(index + 1); + } + + public boolean hasChildNodes() { + return children.size() != 0; + } + + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + LeafNodeImpl refChildImpl = (LeafNodeImpl) refChild; + + if (refChildImpl.document != document) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + if (refChildImpl.parent != this) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + return insertChildAt(newChild, refChildImpl.index); + } + + /** + * Inserts a new child node into this node at a given position. If the new + * node is already child of another node, it is first removed from there. + * This method is the generalization of the appendChild() and insertBefore() + * methods. + * + * @param newChild The new child node to add. + * @param index The index at which to insert the new child node. + * + * @return The node added. + * + * @throws DOMException If the attempted operation violates the XML/DOM + * well-formedness rules. + */ + public Node insertChildAt(Node newChild, int index) throws DOMException { + LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild; + + if (document != null && newChildImpl.document != null && newChildImpl.document != document) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + if (newChildImpl.isParentOf(this)) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + if (newChildImpl.parent != null) { + int oldIndex = newChildImpl.index; + newChildImpl.parent.children.remove(oldIndex); + newChildImpl.parent.refreshIndices(oldIndex); + } + + children.add(index, newChildImpl); + newChildImpl.parent = this; + refreshIndices(index); + + return newChild; + } + + public boolean isParentOf(Node node) { + LeafNodeImpl nodeImpl = (LeafNodeImpl) node; + + while (nodeImpl != null) { + if (nodeImpl == this) { + return true; + } + + nodeImpl = nodeImpl.parent; + } + + return false; + } + + @Override + public void normalize() { + Node nextNode = null; + + for (int i = children.size() - 1; i >= 0; i--) { + Node thisNode = children.get(i); + + thisNode.normalize(); + + if (thisNode.getNodeType() == Node.TEXT_NODE) { + if (nextNode != null && nextNode.getNodeType() == Node.TEXT_NODE) { + ((Text)thisNode).setData(thisNode.getNodeValue() + nextNode.getNodeValue()); + removeChild(nextNode); + } + + if ("".equals(thisNode.getNodeValue())) { + removeChild(thisNode); + nextNode = null; + } else { + nextNode = thisNode; + } + } + } + } + + private void refreshIndices(int fromIndex) { + for (int i = fromIndex; i < children.size(); i++) { + children.get(i).index = i; + } + } + + public Node removeChild(Node oldChild) throws DOMException { + LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild; + + if (oldChildImpl.document != document) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + if (oldChildImpl.parent != this) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + int index = oldChildImpl.index; + children.remove(index); + oldChildImpl.parent = null; + refreshIndices(index); + + return oldChild; + } + + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild; + LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild; + + if (oldChildImpl.document != document + || newChildImpl.document != document) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + if (oldChildImpl.parent != this || newChildImpl.isParentOf(this)) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + int index = oldChildImpl.index; + children.set(index, newChildImpl); + oldChildImpl.parent = null; + newChildImpl.parent = this; + refreshIndices(index); + + return oldChildImpl; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/LeafNodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/LeafNodeImpl.java new file mode 100644 index 0000000..5f9698e --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/LeafNodeImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + * <p> + * This class represents a Node that has a parent Node, but no children. + */ +public abstract class LeafNodeImpl extends NodeImpl { + + // Maintained by InnerNodeImpl. + InnerNodeImpl parent; + + // Maintained by InnerNodeImpl. + int index; + + LeafNodeImpl(DocumentImpl document) { + super(document); + } + + public Node getNextSibling() { + if (parent == null || index + 1 >= parent.children.size()) { + return null; + } + + return parent.children.get(index + 1); + } + + public Node getParentNode() { + return parent; + } + + public Node getPreviousSibling() { + if (parent == null || index == 0) { + return null; + } + + return parent.children.get(index - 1); + } + + boolean isParentOf(Node node) { + return false; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NamedNodeMapImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NamedNodeMapImpl.java new file mode 100644 index 0000000..0952d83 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/NamedNodeMapImpl.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class NamedNodeMapImpl implements NamedNodeMap { + + private Class<?> type; + + private List<NodeImpl> list; + + NamedNodeMapImpl(Class<?> type) { + list = new ArrayList<NodeImpl>(); + this.type = type; + } + + NamedNodeMapImpl(List<NodeImpl> list, Class<?> type) { + this.list = list; + this.type = type; + } + + public int getLength() { + return list.size(); + } + + private int indexOfItem(String name) { + for (int i = 0; i < list.size(); i++) { + NodeImpl node = list.get(i); + if (node.matchesName(name, false)) { + return i; + } + } + + return -1; + } + + private int indexOfItemNS(String namespaceURI, String localName) { + for (int i = 0; i < list.size(); i++) { + NodeImpl node = list.get(i); + if (node.matchesNameNS(namespaceURI, localName, false)) { + return i; + } + } + + return -1; + } + + public Node getNamedItem(String name) { + int i = indexOfItem(name); + + return (i == -1 ? null : item(i)); + } + + public Node getNamedItemNS(String namespaceURI, String localName) { + int i = indexOfItemNS(namespaceURI, localName); + + return (i == -1 ? null : item(i)); + } + + public Node item(int index) { + return list.get(index); + } + + public Node removeNamedItem(String name) throws DOMException { + int i = indexOfItem(name); + + if (i == -1) { + throw new DOMException(DOMException.NOT_FOUND_ERR, null); + } + + return list.remove(i); + } + + public Node removeNamedItemNS(String namespaceURI, String localName) + throws DOMException { + int i = indexOfItemNS(namespaceURI, localName); + + if (i == -1) { + throw new DOMException(DOMException.NOT_FOUND_ERR, null); + } + + return list.remove(i); + } + + public Node setNamedItem(Node arg) throws DOMException { + // Ensure we only accept nodes of the correct type. + if (!type.isAssignableFrom(arg.getClass())) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + // All nodes in the map must belong to the same document. + if (list.size() != 0) { + Document document = list.get(0).getOwnerDocument(); + + if (document != null && arg.getOwnerDocument() != document) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + } + +// TODO Theoretically we should ensure that the nodes don't have a parent. +// if (newAttrImpl.getOwnerElement() != null) { +// throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null); +// } + + int i = indexOfItem(arg.getNodeName()); + + if (i != -1) { + list.remove(i); + } + + list.add((NodeImpl)arg); + return arg; + } + + public Node setNamedItemNS(Node arg) throws DOMException { + // Ensure we only accept nodes of the correct type. + if (!type.isAssignableFrom(arg.getClass())) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + // All nodes in the map must belong to the same document. + if (list.size() != 0) { + Document document = list.get(0).getOwnerDocument(); + + if (document != null && arg.getOwnerDocument() != document) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + } + +// TODO Theoretically we should ensure that the nodes don't have a parent. +// if (newAttrImpl.getOwnerElement() != null) { +// throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null); +// } + + int i = indexOfItemNS(arg.getNamespaceURI(), arg.getLocalName()); + + if (i != -1) { + list.remove(i); + } + + list.add((NodeImpl)arg); + return arg; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java new file mode 100644 index 0000000..ca6fb8f --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + * <p> + * This class represents a Node that has neither a parent nor children. + */ +public abstract class NodeImpl implements Node { + + private static final NodeList EMPTY_LIST = new NodeListImpl(); + + // Maintained by InnerNodeImpl and ElementImpl. + DocumentImpl document; + + NodeImpl(DocumentImpl document) { + this.document = document; + } + + public Node appendChild(Node newChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + public Node cloneNode(boolean deep) { + return document.cloneNode(this, deep); + } + + public NamedNodeMap getAttributes() { + return null; + } + + public NodeList getChildNodes() { + return EMPTY_LIST; + } + + public Node getFirstChild() { + return null; + } + + public Node getLastChild() { + return null; + } + + public String getLocalName() { + return null; + } + + public String getNamespaceURI() { + return null; + } + + public Node getNextSibling() { + return null; + } + + public String getNodeName() { + return null; + } + + public abstract short getNodeType(); + + public String getNodeValue() throws DOMException { + return null; + } + + public Document getOwnerDocument() { + return document; + } + + public Node getParentNode() { + return null; + } + + public String getPrefix() { + return null; + } + + public Node getPreviousSibling() { + return null; + } + + public boolean hasAttributes() { + return false; + } + + public boolean hasChildNodes() { + return false; + } + + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + public boolean isSupported(String feature, String version) { + return DOMImplementationImpl.getInstance().hasFeature(feature, version); + } + + public void normalize() { + } + + public Node removeChild(Node oldChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); + } + + public void setNodeValue(String nodeValue) throws DOMException { + } + + public void setPrefix(String prefix) throws DOMException { + } + + /** + * Checks whether a required string matches an actual string. This utility + * method is used for comparing namespaces and such. It takes into account + * null arguments and the "*" special case. + * + * @param required The required string. + * @param actual The actual string. + * @return True if and only if the actual string matches the required one. + */ + private static boolean matchesName(String required, String actual, boolean wildcard) { + if (wildcard && "*".equals(required)) { + return true; + } + + if (required == null) { + return (actual == null); + } + + return required.equals(actual); + } + + /** + * Checks whether this node's name matches a required name. It takes into + * account null arguments and the "*" special case. + * + * @param name The required name. + * @param wildcard TODO + * @return True if and only if the actual name matches the required one. + */ + public boolean matchesName(String name, boolean wildcard) { + return matchesName(name, getNodeName(), wildcard); + } + + /** + * Checks whether this node's namespace and local name match a required + * pair of namespace and local name. It takes into account null arguments + * and the "*" special case. + * + * @param namespaceURI The required namespace. + * @param localName The required local name. + * @param wildcard TODO + * @return True if and only if the actual namespace and local name match + * the required pair of namespace and local name. + */ + public boolean matchesNameNS(String namespaceURI, String localName, boolean wildcard) { + return matchesName(namespaceURI, getNamespaceURI(), wildcard) && matchesName(localName, getLocalName(), wildcard); + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NodeListImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NodeListImpl.java new file mode 100644 index 0000000..caa56af --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/NodeListImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class NodeListImpl implements NodeList { + + private List<NodeImpl> children; + + NodeListImpl() { + children = new ArrayList<NodeImpl>(); + } + + NodeListImpl(List<NodeImpl> list) { + children = list; + } + + void add(NodeImpl node) { + children.add(node); + } + + public int getLength() { + return children.size(); + } + + public Node item(int index) { + if (index >= children.size()) { + return null; + } else { + return children.get(index); + } + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NotationImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NotationImpl.java new file mode 100644 index 0000000..4d91d75 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/NotationImpl.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.Notation; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class NotationImpl extends LeafNodeImpl implements Notation { + + private String notationName; + + private String publicID; + + private String systemID; + + NotationImpl(DocumentImpl document, String notationName, String publicID, + String systemID) { + super(document); + } + + @Override + public String getNodeName() { + return notationName; + } + + @Override + public short getNodeType() { + return Node.NOTATION_NODE; + } + + public String getPublicId() { + return publicID; + } + + public String getSystemId() { + return systemID; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/ProcessingInstructionImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/ProcessingInstructionImpl.java new file mode 100644 index 0000000..c3610d5 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/ProcessingInstructionImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.ProcessingInstruction; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class ProcessingInstructionImpl extends LeafNodeImpl implements + ProcessingInstruction { + + private String target; + + private String data; + + ProcessingInstructionImpl(DocumentImpl document, String target, String data) { + super(document); + this.target = target; + this.data = data; + } + + public String getData() { + return data; + } + + @Override + public String getNodeName() { + return target; + } + + @Override + public short getNodeType() { + return Node.PROCESSING_INSTRUCTION_NODE; + } + + @Override + public String getNodeValue() { + return data; + } + + public String getTarget() { + return target; + } + + public void setData(String data) throws DOMException { + this.data = data; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java new file mode 100644 index 0000000..3553546 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.dom; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +/** + * Provides a straightforward implementation of the corresponding W3C DOM + * interface. The class is used internally only, thus only notable members that + * are not in the original interface are documented (the W3C docs are quite + * extensive). Hope that's ok. + * <p> + * Some of the fields may have package visibility, so other classes belonging to + * the DOM implementation can easily access them while maintaining the DOM tree + * structure. + */ +public class TextImpl extends CharacterDataImpl implements Text { + + TextImpl(DocumentImpl document, String data) { + super(document, data); + } + + @Override + public String getNodeName() { + return "#text"; + } + + @Override + public short getNodeType() { + return Node.TEXT_NODE; + } + + @Override + public String getNodeValue() { + return getData(); + } + + public Text splitText(int offset) throws DOMException { + Text newText = getOwnerDocument().createTextNode( + substringData(offset, getLength() - offset)); + deleteData(0, offset); + + Node refNode = getNextSibling(); + if (refNode == null) { + getParentNode().appendChild(newText); + } else { + getParentNode().insertBefore(newText, refNode); + } + + return this; + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java b/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java new file mode 100644 index 0000000..4b8eef3 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.parsers; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +/** + * Provides a straightforward DocumentBuilderFactory implementation based on + * XMLPull/KXML. The class is used internally only, thus only notable members + * that are not already in the abstract superclass are documented. Hope that's + * ok. + */ +public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { + + private static final String NAMESPACES = + "http://xml.org/sax/features/namespaces"; + + private static final String VALIDATION = + "http://xml.org/sax/features/validation"; + + @Override + public Object getAttribute(String name) throws IllegalArgumentException { + throw new IllegalArgumentException(name); + } + + @Override + public boolean getFeature(String name) throws ParserConfigurationException { + if (name == null) { + throw new NullPointerException(); + } + + if (NAMESPACES.equals(name)) { + return isNamespaceAware(); + } else if (VALIDATION.equals(name)) { + return isValidating(); + } else { + throw new ParserConfigurationException(name); + } + } + + @Override + public DocumentBuilder newDocumentBuilder() + throws ParserConfigurationException { + if (isValidating()) { + throw new ParserConfigurationException( + "No validating DocumentBuilder implementation available"); + } + + /** + * TODO If Android is going to support a different DocumentBuilder + * implementations, this should be wired here. If we wanted to + * allow different implementations, these could be distinguished by + * a special feature (like http://www.org.apache.harmony.com/xml/expat) + * or by throwing the full SPI monty at it. + */ + DocumentBuilderImpl builder = new DocumentBuilderImpl(); + + builder.setIgnoreComments(isIgnoringComments()); + builder.setIgnoreElementContentWhitespace( + isIgnoringElementContentWhitespace()); + builder.setNamespaceAware(isNamespaceAware()); + + // TODO What about expandEntityReferences? + + return builder; + } + + @Override + public void setAttribute(String name, Object value) + throws IllegalArgumentException { + throw new IllegalArgumentException(name); + } + + @Override + public void setFeature(String name, boolean value) + throws ParserConfigurationException { + if (name == null) { + throw new NullPointerException(); + } + + if (NAMESPACES.equals(name)) { + setNamespaceAware(value); + } else if (VALIDATION.equals(name)) { + setValidating(value); + } else { + throw new ParserConfigurationException(name); + } + } + +} diff --git a/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java new file mode 100644 index 0000000..f831c8b --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.parsers; + +import java.io.IOException; +import java.util.StringTokenizer; + +import javax.xml.parsers.DocumentBuilder; + +import org.kxml2.io.KXmlParser; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +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 org.xml.sax.helpers.LocatorImpl; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import org.apache.harmony.xml.dom.DOMImplementationImpl; + +/** + * Provides a straightforward DocumentBuilder implementation based on + * XMLPull/KXML. The class is used internally only, thus only notable members + * that are not already in the abstract superclass are documented. Hope that's + * ok. + */ +class DocumentBuilderImpl extends DocumentBuilder { + + private static DOMImplementation dom = DOMImplementationImpl.getInstance(); + + private EntityResolver entityResolver; + + private ErrorHandler errorHandler; + + private boolean ignoreComments; + + private boolean ignoreElementContentWhitespace; + + private boolean namespaceAware; + + DocumentBuilderImpl() { + // Do nothing. + } + + @Override + public DOMImplementation getDOMImplementation() { + return dom; + } + + /** + * Reflects whether this DocumentBuilder is configured to ignore comments. + * + * @return True if and only if comments are ignored. + */ + public boolean isIgnoringComments() { + return ignoreComments; + } + + /** + * Reflects whether this DocumentBuilder is configured to ignore element + * content whitespace. + * + * @return True if and only if whitespace element content is ignored. + */ + public boolean isIgnoringElementContentWhitespace() { + return ignoreElementContentWhitespace; + } + + @Override + public boolean isNamespaceAware() { + return namespaceAware; + } + + @Override + public boolean isValidating() { + return false; + } + + @Override + public Document newDocument() { + return dom.createDocument(null, null, null); + } + + @Override + public Document parse(InputSource source) throws SAXException, IOException { + if (source == null) { + throw new IllegalArgumentException(); + } + + Document document = newDocument(); + + try { + XmlPullParser parser = new KXmlParser(); + + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, + namespaceAware); + + if (source.getByteStream() != null) { + parser.setInput(source.getByteStream(), source.getEncoding()); + } else if (source.getCharacterStream() != null) { + parser.setInput(source.getCharacterStream()); + } else { + // TODO Accept other sources as well? + throw new SAXParseException( + "InputSource needs either stream or reader", null); + } + + if(parser.nextToken() == XmlPullParser.END_DOCUMENT) { + throw new SAXParseException( + "Unexpected end of document", null); + } + + parse(parser, document, document, XmlPullParser.END_DOCUMENT); + + parser.require(XmlPullParser.END_DOCUMENT, null, null); + } catch (XmlPullParserException ex) { + if(ex.getDetail() instanceof IOException) { + throw (IOException)ex.getDetail(); + } + if(ex.getDetail() instanceof RuntimeException) { + throw (RuntimeException)ex.getDetail(); + } + + LocatorImpl locator = new LocatorImpl(); + + locator.setPublicId(source.getPublicId()); + locator.setSystemId(source.getSystemId()); + locator.setLineNumber(ex.getLineNumber()); + locator.setColumnNumber(ex.getColumnNumber()); + + SAXParseException newEx = new SAXParseException(ex.getMessage(), + locator); + + if (errorHandler != null) { + errorHandler.error(newEx); + } + + throw newEx; + } + + return document; + } + + /** + * Implements the whole parsing of the XML document. The XML pull parser is + * actually more of a tokenizer, and we are doing a classical recursive + * descent parsing (the method invokes itself for XML elements). Our + * approach to parsing does accept some illegal documents (more than one + * root element, for example). The assumption is that the DOM implementation + * throws the proper exceptions in these cases. + * + * @param parser The XML pull parser we're reading from. + * @param document The document we're building. + * @param node The node we're currently on (initially the document itself). + * @param endToken The token that will end this recursive call. Either + * XmlPullParser.END_DOCUMENT or XmlPullParser.END_TAG. + * + * @throws XmlPullParserException If a parsing error occurs. + * @throws IOException If a general IO error occurs. + */ + private void parse(XmlPullParser parser, Document document, Node node, + int endToken) throws XmlPullParserException, IOException { + + int token = parser.getEventType(); + + /* + * The main parsing loop. The precondition is that we are already on the + * token to be processed. This holds for each iteration of the loop, so + * the inner statements have to ensure that (in particular the recursive + * call). + */ + while (token != endToken && token != XmlPullParser.END_DOCUMENT) { + if (token == XmlPullParser.PROCESSING_INSTRUCTION) { + /* + * Found a processing instructions. We need to split the token + * text at the first whitespace character. + */ + String text = parser.getText(); + + int dot = text.indexOf(' '); + + String target = (dot != -1 ? text.substring(0, dot) : text); + String data = (dot != -1 ? text.substring(dot + 1) : ""); + + node.appendChild(document.createProcessingInstruction(target, + data)); + } else if (token == XmlPullParser.DOCDECL) { + /* + * Found a document type declaration. Unfortunately KXML doesn't + * have the necessary details. Do we parse it ourselves, or do + * we silently ignore it, since it isn't mandatory in DOM 2 + * anyway? + */ + StringTokenizer tokenizer = new StringTokenizer(parser.getText()); + if (tokenizer.hasMoreTokens()) { + String name = tokenizer.nextToken(); + String pubid = null; + String sysid = null; + + if (tokenizer.hasMoreTokens()) { + String text = tokenizer.nextToken(); + + if ("SYSTEM".equals(text)) { + if (tokenizer.hasMoreTokens()) { + sysid = tokenizer.nextToken(); + } + } else if ("PUBLIC".equals(text)) { + if (tokenizer.hasMoreTokens()) { + pubid = tokenizer.nextToken(); + } + if (tokenizer.hasMoreTokens()) { + sysid = tokenizer.nextToken(); + } + } + } + + if (pubid != null && pubid.length() >= 2 && pubid.startsWith("\"") && pubid.endsWith("\"")) { + pubid = pubid.substring(1, pubid.length() - 1); + } + + if (sysid != null && sysid.length() >= 2 && sysid.startsWith("\"") && sysid.endsWith("\"")) { + sysid = sysid.substring(1, sysid.length() - 1); + } + + document.appendChild(dom.createDocumentType(name, pubid, sysid)); + } + + } else if (token == XmlPullParser.COMMENT) { + /* + * Found a comment. We simply take the token text, but we only + * create a node if the client wants to see comments at all. + */ + if (!ignoreComments) { + node.appendChild(document.createComment(parser.getText())); + } + } else if (token == XmlPullParser.IGNORABLE_WHITESPACE) { + /* + * Found some ignorable whitespace. We simply take the token + * text, but we only create a node if the client wants to see + * whitespace at all. + */ + if (!ignoreElementContentWhitespace) { + node.appendChild(document.createTextNode(parser.getText())); + } + } else if (token == XmlPullParser.TEXT) { + /* + * Found a piece of text. That's the easiest case. We simply + * take it and create a corresponding node. + */ + node.appendChild(document.createTextNode(parser.getText())); + } else if (token == XmlPullParser.CDSECT) { + /* + * Found a CDATA section. That's also trivial. We simply + * take it and create a corresponding node. + */ + node.appendChild(document.createCDATASection(parser.getText())); + } else if (token == XmlPullParser.ENTITY_REF) { + /* + * Found an entity reference. If an entity resolver is + * installed, we replace it by text (if possible). Otherwise we + * add an entity reference node. + */ + String entity = parser.getName(); + + if (entityResolver != null) { + // TODO Implement this... + } + + String replacement = resolveStandardEntity(entity); + if (replacement != null) { + node.appendChild(document.createTextNode(replacement)); + } else { + node.appendChild(document.createEntityReference(entity)); + } + } else if (token == XmlPullParser.START_TAG) { + /* + * Found an element start tag. We create an element node with + * the proper info and attributes. We then invoke parse() + * recursively to handle the next level of nesting. When we + * return from this call, we check that we are on the proper + * element end tag. The whole handling differs somewhat + * depending on whether the parser is namespace-aware or not. + */ + if (namespaceAware) { + // Collect info for element node + String namespace = parser.getNamespace(); + String name = parser.getName(); + String prefix = parser.getPrefix(); + + if ("".equals(namespace)) { + namespace = null; + } + + // Create element node and wire it correctly + Element element = document.createElementNS(namespace, name); + element.setPrefix(prefix); + node.appendChild(element); + + for (int i = 0; i < parser.getAttributeCount(); i++) { + // Collect info for a single attribute node + String attrNamespace = parser.getAttributeNamespace(i); + String attrPrefix = parser.getAttributePrefix(i); + String attrName = parser.getAttributeName(i); + String attrValue = parser.getAttributeValue(i); + + if ("".equals(attrNamespace)) { + attrNamespace = null; + } + + // Create attribute node and wire it correctly + Attr attr = document.createAttributeNS(attrNamespace, attrName); + attr.setPrefix(attrPrefix); + attr.setValue(attrValue); + element.setAttributeNodeNS(attr); + } + + // Recursive descent + token = parser.nextToken(); + parse(parser, document, element, XmlPullParser.END_TAG); + + // Expect the element's end tag here + parser.require(XmlPullParser.END_TAG, namespace, name); + + } else { + // Collect info for element node + String name = parser.getName(); + + // Create element node and wire it correctly + Element element = document.createElement(name); + node.appendChild(element); + + for (int i = 0; i < parser.getAttributeCount(); i++) { + // Collect info for a single attribute node + String attrName = parser.getAttributeName(i); + String attrValue = parser.getAttributeValue(i); + + // Create attribute node and wire it correctly + Attr attr = document.createAttribute(attrName); + attr.setValue(attrValue); + element.setAttributeNode(attr); + } + + // Recursive descent + token = parser.nextToken(); + parse(parser, document, element, XmlPullParser.END_TAG); + + // Expect the element's end tag here + parser.require(XmlPullParser.END_TAG, "", name); + } + } + + token = parser.nextToken(); + } + } + + @Override + public void setEntityResolver(EntityResolver resolver) { + entityResolver = resolver; + } + + @Override + public void setErrorHandler(ErrorHandler handler) { + errorHandler = handler; + } + + /** + * Controls whether this DocumentBuilder ignores comments. + * + * @param value Turns comment ignorance on or off. + */ + public void setIgnoreComments(boolean value) { + ignoreComments = value; + } + + /** + * Controls whether this DocumentBuilder ignores element content whitespace. + * + * @param value Turns element whitespace content ignorance on or off. + */ + public void setIgnoreElementContentWhitespace(boolean value) { + ignoreElementContentWhitespace = value; + } + + /** + * Controls whether this DocumentBuilder is namespace-aware. + * + * @param value Turns namespace awareness on or off. + */ + public void setNamespaceAware(boolean value) { + namespaceAware = value; + } + + /** + * Resolves one of the five standard XML entities. + * + * @param entity The name of the entity to resolve, not including + * the ampersand or the semicolon. + * + * @return The proper replacement, or null, if the entity is unknown. + */ + private String resolveStandardEntity(String entity) { + if ("lt".equals(entity)) { + return "<"; + } else if ("gt".equals(entity)) { + return ">"; + } else if ("amp".equals(entity)) { + return "&"; + } else if ("apos".equals(entity)) { + return "'"; + } else if ("quot".equals(entity)) { + return "\""; + } else { + return null; + } + } +} diff --git a/xml/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java b/xml/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java new file mode 100644 index 0000000..6846216 --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.parsers; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.SAXNotRecognizedException; + +/** + * Provides a straightforward SAXParserFactory implementation based on + * Expat. The class is used internally only, thus only notable members + * that are not already in the abstract superclass are documented. + */ +public class SAXParserFactoryImpl extends SAXParserFactory { + + private static final String NAMESPACES + = "http://xml.org/sax/features/namespaces"; + + private static final String VALIDATION + = "http://xml.org/sax/features/validation"; + + private Map<String, Boolean> features = new HashMap<String, Boolean>(); + + @Override + public boolean getFeature(String name) throws SAXNotRecognizedException { + if (name == null) { + throw new NullPointerException(); + } + + if (!name.startsWith("http://xml.org/sax/features/")) { + throw new SAXNotRecognizedException(name); + } + + return Boolean.TRUE.equals(features.get(name)); + } + + @Override + public boolean isNamespaceAware() { + try { + return getFeature(NAMESPACES); + } catch (SAXNotRecognizedException ex) { + throw new AssertionError(ex); + } + } + + @Override + public boolean isValidating() { + try { + return getFeature(VALIDATION); + } catch (SAXNotRecognizedException ex) { + throw new AssertionError(ex); + } + } + + @Override + public SAXParser newSAXParser() throws ParserConfigurationException { + if (isValidating()) { + throw new ParserConfigurationException( + "No validating SAXParser implementation available"); + } + + try { + return new SAXParserImpl(features); + } catch (Exception ex) { + throw new ParserConfigurationException(ex.toString()); + } + } + + @Override + public void setFeature(String name, boolean value) throws SAXNotRecognizedException { + if (name == null) { + throw new NullPointerException(); + } + + if (!name.startsWith("http://xml.org/sax/features/")) { + throw new SAXNotRecognizedException(name); + } + + if (value) { + features.put(name, Boolean.TRUE); + } else { + // This is needed to disable features that are enabled by default. + features.put(name, Boolean.FALSE); + } + } + + @Override + public void setNamespaceAware(boolean value) { + try { + setFeature(NAMESPACES, value); + } catch (SAXNotRecognizedException ex) { + throw new AssertionError(ex); + } + } + + @Override + public void setValidating(boolean value) { + try { + setFeature(VALIDATION, value); + } catch (SAXNotRecognizedException ex) { + throw new AssertionError(ex); + } + } +} diff --git a/xml/src/main/java/org/apache/harmony/xml/parsers/SAXParserImpl.java b/xml/src/main/java/org/apache/harmony/xml/parsers/SAXParserImpl.java new file mode 100644 index 0000000..b3af61f --- /dev/null +++ b/xml/src/main/java/org/apache/harmony/xml/parsers/SAXParserImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xml.parsers; + +import org.apache.harmony.xml.ExpatReader; + +import java.util.Map; + +import javax.xml.parsers.SAXParser; + +import org.xml.sax.Parser; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderAdapter; + +/** + * Provides a straightforward SAXParser implementation based on ExpatReader. + * The class is used internally only, thus only notable members that are not + * already in the abstract superclass are documented. Hope that's ok. + */ +class SAXParserImpl extends SAXParser { + + private XMLReader reader; + + private Parser parser; + + SAXParserImpl(Map<String, Boolean> features) + throws SAXNotRecognizedException, SAXNotSupportedException { + reader = new ExpatReader(); + + for (Map.Entry<String,Boolean> entry : features.entrySet()) { + reader.setFeature(entry.getKey(), entry.getValue()); + } + } + + @Override + public Parser getParser() { + if (parser == null) { + parser = new XMLReaderAdapter(reader); + } + + return parser; + } + + @Override + public Object getProperty(String name) throws SAXNotRecognizedException, + SAXNotSupportedException { + return reader.getProperty(name); + } + + @Override + public XMLReader getXMLReader() { + return reader; + } + + @Override + public boolean isNamespaceAware() { + try { + return reader.getFeature("http://xml.org/sax/features/namespaces"); + } catch (SAXException ex) { + return false; + } + } + + @Override + public boolean isValidating() { + return false; + } + + @Override + public void setProperty(String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException { + reader.setProperty(name, value); + } +} diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java new file mode 100644 index 0000000..8125745 --- /dev/null +++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java @@ -0,0 +1,1438 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + +// Contributors: Paul Hackenberger (unterminated entity handling in relaxed mode) + +package org.kxml2.io; + +import java.io.*; +import java.util.*; + +import org.xmlpull.v1.*; + +/** A simple, pull based XML parser. This classe replaces the kXML 1 + XmlParser class and the corresponding event classes. */ + +public class KXmlParser implements XmlPullParser { + + private Object location; + static final private String UNEXPECTED_EOF = "Unexpected EOF"; + static final private String ILLEGAL_TYPE = "Wrong event type"; + static final private int LEGACY = 999; + static final private int XML_DECL = 998; + + // general + + private String version; + private Boolean standalone; + + private boolean processNsp; + private boolean relaxed; + private HashMap entityMap; + private int depth; + private String[] elementStack = new String[16]; + private String[] nspStack = new String[8]; + private int[] nspCounts = new int[4]; + + // source + + private Reader reader; + private String encoding; + private char[] srcBuf; + + private int srcPos; + private int srcCount; + + private int line; + private int column; + + // txtbuffer + + private char[] txtBuf = new char[128]; + private int txtPos; + + // Event-related + + private int type; + //private String text; + private boolean isWhitespace; + private String namespace; + private String prefix; + private String name; + + private boolean degenerated; + private int attributeCount; + private String[] attributes = new String[16]; + private int stackMismatch = 0; + private String error; + + /** + * A separate peek buffer seems simpler than managing + * wrap around in the first level read buffer */ + + private int[] peek = new int[2]; + private int peekCount; + private boolean wasCR; + + private boolean unresolved; + private boolean token; + + public KXmlParser() { +// srcBuf = new char[Runtime.getRuntime().freeMemory() >= 1048576 ? 8192 : 128]; + + // XXX: We don't have a Runtime class at this time. + srcBuf = new char[8192]; + } + + private final boolean isProp(String n1, boolean prop, String n2) { + if (!n1.startsWith("http://xmlpull.org/v1/doc/")) + return false; + if (prop) + return n1.substring(42).equals(n2); + else + return n1.substring(40).equals(n2); + } + + private final boolean adjustNsp() throws XmlPullParserException { + + boolean any = false; + + for (int i = 0; i < attributeCount << 2; i += 4) { + // * 4 - 4; i >= 0; i -= 4) { + + String attrName = attributes[i + 2]; + int cut = attrName.indexOf(':'); + String prefix; + + if (cut != -1) { + prefix = attrName.substring(0, cut); + attrName = attrName.substring(cut + 1); + } + else if (attrName.equals("xmlns")) { + prefix = attrName; + attrName = null; + } + else + continue; + + if (!prefix.equals("xmlns")) { + any = true; + } + else { + int j = (nspCounts[depth]++) << 1; + + nspStack = ensureCapacity(nspStack, j + 2); + nspStack[j] = attrName; + nspStack[j + 1] = attributes[i + 3]; + + if (attrName != null && attributes[i + 3].equals("")) + error("illegal empty namespace"); + + // prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ()); + + //System.out.println (prefixMap); + + System.arraycopy( + attributes, + i + 4, + attributes, + i, + ((--attributeCount) << 2) - i); + + i -= 4; + } + } + + if (any) { + for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) { + + String attrName = attributes[i + 2]; + int cut = attrName.indexOf(':'); + + if (cut == 0 && !relaxed) + throw new RuntimeException( + "illegal attribute name: " + attrName + " at " + this); + + else if (cut != -1) { + String attrPrefix = attrName.substring(0, cut); + + attrName = attrName.substring(cut + 1); + + String attrNs = getNamespace(attrPrefix); + + if (attrNs == null && !relaxed) + throw new RuntimeException( + "Undefined Prefix: " + attrPrefix + " in " + this); + + attributes[i] = attrNs; + attributes[i + 1] = attrPrefix; + attributes[i + 2] = attrName; + + /* + if (!relaxed) { + for (int j = (attributeCount << 2) - 4; j > i; j -= 4) + if (attrName.equals(attributes[j + 2]) + && attrNs.equals(attributes[j])) + exception( + "Duplicate Attribute: {" + + attrNs + + "}" + + attrName); + } + */ + } + } + } + + int cut = name.indexOf(':'); + + if (cut == 0) + error("illegal tag name: " + name); + + if (cut != -1) { + prefix = name.substring(0, cut); + name = name.substring(cut + 1); + } + + this.namespace = getNamespace(prefix); + + if (this.namespace == null) { + if (prefix != null) + error("undefined prefix: " + prefix); + this.namespace = NO_NAMESPACE; + } + + return any; + } + + private final String[] ensureCapacity(String[] arr, int required) { + if (arr.length >= required) + return arr; + String[] bigger = new String[required + 16]; + System.arraycopy(arr, 0, bigger, 0, arr.length); + return bigger; + } + + private final void error(String desc) throws XmlPullParserException { + if (relaxed) { + if (error == null) + error = "ERR: " + desc; + } + else + exception(desc); + } + + private final void exception(String desc) throws XmlPullParserException { + throw new XmlPullParserException( + desc.length() < 100 ? desc : desc.substring(0, 100) + "\n", + this, + null); + } + + /** + * common base for next and nextToken. Clears the state, except from + * txtPos and whitespace. Does not set the type variable */ + + private final void nextImpl() throws IOException, XmlPullParserException { + + if (reader == null) + exception("No Input specified"); + + if (type == END_TAG) + depth--; + + while (true) { + attributeCount = -1; + + // degenerated needs to be handled before error because of possible + // processor expectations(!) + + if (degenerated) { + degenerated = false; + type = END_TAG; + return; + } + + + if (error != null) { + for (int i = 0; i < error.length(); i++) + push(error.charAt(i)); + // text = error; + error = null; + type = COMMENT; + return; + } + + + if (relaxed + && (stackMismatch > 0 || (peek(0) == -1 && depth > 0))) { + int sp = (depth - 1) << 2; + type = END_TAG; + namespace = elementStack[sp]; + prefix = elementStack[sp + 1]; + name = elementStack[sp + 2]; + if (stackMismatch != 1) + error = "missing end tag /" + name + " inserted"; + if (stackMismatch > 0) + stackMismatch--; + return; + } + + prefix = null; + name = null; + namespace = null; + // text = null; + + type = peekType(); + + switch (type) { + + case ENTITY_REF : + pushEntity(); + return; + + case START_TAG : + parseStartTag(false); + return; + + case END_TAG : + parseEndTag(); + return; + + case END_DOCUMENT : + return; + + case TEXT : + pushText('<', !token); + if (depth == 0) { + if (isWhitespace) + type = IGNORABLE_WHITESPACE; + // make exception switchable for instances.chg... !!!! + // else + // exception ("text '"+getText ()+"' not allowed outside root element"); + } + return; + + default : + type = parseLegacy(token); + if (type != XML_DECL) + return; + } + } + } + + private final int parseLegacy(boolean push) + throws IOException, XmlPullParserException { + + String req = ""; + int term; + int result; + int prev = 0; + + read(); // < + int c = read(); + + if (c == '?') { + if ((peek(0) == 'x' || peek(0) == 'X') + && (peek(1) == 'm' || peek(1) == 'M')) { + + if (push) { + push(peek(0)); + push(peek(1)); + } + read(); + read(); + + if ((peek(0) == 'l' || peek(0) == 'L') && peek(1) <= ' ') { + + if (line != 1 || column > 4) + error("PI must not start with xml"); + + parseStartTag(true); + + if (attributeCount < 1 || !"version".equals(attributes[2])) + error("version expected"); + + version = attributes[3]; + + int pos = 1; + + if (pos < attributeCount + && "encoding".equals(attributes[2 + 4])) { + encoding = attributes[3 + 4]; + pos++; + } + + if (pos < attributeCount + && "standalone".equals(attributes[4 * pos + 2])) { + String st = attributes[3 + 4 * pos]; + if ("yes".equals(st)) + standalone = new Boolean(true); + else if ("no".equals(st)) + standalone = new Boolean(false); + else + error("illegal standalone value: " + st); + pos++; + } + + if (pos != attributeCount) + error("illegal xmldecl"); + + isWhitespace = true; + txtPos = 0; + + return XML_DECL; + } + } + + /* int c0 = read (); + int c1 = read (); + int */ + + term = '?'; + result = PROCESSING_INSTRUCTION; + } + else if (c == '!') { + if (peek(0) == '-') { + result = COMMENT; + req = "--"; + term = '-'; + } + else if (peek(0) == '[') { + result = CDSECT; + req = "[CDATA["; + term = ']'; + push = true; + } + else { + result = DOCDECL; + req = "DOCTYPE"; + term = -1; + } + } + else { + error("illegal: <" + c); + return COMMENT; + } + + for (int i = 0; i < req.length(); i++) + read(req.charAt(i)); + + if (result == DOCDECL) + parseDoctype(push); + else { + while (true) { + c = read(); + if (c == -1){ + error(UNEXPECTED_EOF); + return COMMENT; + } + + if (push) + push(c); + + if ((term == '?' || c == term) + && peek(0) == term + && peek(1) == '>') + break; + + prev = c; + } + + if (term == '-' && prev == '-') + error("illegal comment delimiter: --->"); + + read(); + read(); + + if (push && term != '?') + txtPos--; + + } + return result; + } + + /** precondition: <! consumed */ + + private final void parseDoctype(boolean push) + throws IOException, XmlPullParserException { + + int nesting = 1; + boolean quoted = false; + + // read(); + + while (true) { + int i = read(); + switch (i) { + + case -1 : + error(UNEXPECTED_EOF); + return; + + case '\'' : + quoted = !quoted; + break; + + case '<' : + if (!quoted) + nesting++; + break; + + case '>' : + if (!quoted) { + if ((--nesting) == 0) + return; + } + break; + } + if (push) + push(i); + } + } + + /* precondition: </ consumed */ + + private final void parseEndTag() + throws IOException, XmlPullParserException { + + read(); // '<' + read(); // '/' + name = readName(); + skip(); + read('>'); + + int sp = (depth - 1) << 2; + + if (depth == 0) { + error("element stack empty"); + type = COMMENT; + return; + } + + if (!name.equals(elementStack[sp + 3])) { + error("expected: /" + elementStack[sp + 3] + " read: " + name); + + // become case insensitive in relaxed mode + + int probe = sp; + while (probe >= 0 && !name.toLowerCase().equals(elementStack[probe + 3].toLowerCase())) { + stackMismatch++; + probe -= 4; + } + + if (probe < 0) { + stackMismatch = 0; + // text = "unexpected end tag ignored"; + type = COMMENT; + return; + } + } + + namespace = elementStack[sp]; + prefix = elementStack[sp + 1]; + name = elementStack[sp + 2]; + } + + private final int peekType() throws IOException { + switch (peek(0)) { + case -1 : + return END_DOCUMENT; + case '&' : + return ENTITY_REF; + case '<' : + switch (peek(1)) { + case '/' : + return END_TAG; + case '?' : + case '!' : + return LEGACY; + default : + return START_TAG; + } + default : + return TEXT; + } + } + + private final String get(int pos) { + return new String(txtBuf, pos, txtPos - pos); + } + + /* + private final String pop (int pos) { + String result = new String (txtBuf, pos, txtPos - pos); + txtPos = pos; + return result; + } + */ + + private final void push(int c) { + + isWhitespace &= c <= ' '; + + if (txtPos == txtBuf.length) { + char[] bigger = new char[txtPos * 4 / 3 + 4]; + System.arraycopy(txtBuf, 0, bigger, 0, txtPos); + txtBuf = bigger; + } + + txtBuf[txtPos++] = (char) c; + } + + /** Sets name and attributes */ + + private final void parseStartTag(boolean xmldecl) + throws IOException, XmlPullParserException { + + if (!xmldecl) + read(); + name = readName(); + attributeCount = 0; + + while (true) { + skip(); + + int c = peek(0); + + if (xmldecl) { + if (c == '?') { + read(); + read('>'); + return; + } + } + else { + if (c == '/') { + degenerated = true; + read(); + skip(); + read('>'); + break; + } + + if (c == '>' && !xmldecl) { + read(); + break; + } + } + + if (c == -1) { + error(UNEXPECTED_EOF); + //type = COMMENT; + return; + } + + String attrName = readName(); + + if (attrName.length() == 0) { + error("attr name expected"); + //type = COMMENT; + break; + } + + int i = (attributeCount++) << 2; + + attributes = ensureCapacity(attributes, i + 4); + + attributes[i++] = ""; + attributes[i++] = null; + attributes[i++] = attrName; + + skip(); + + if (peek(0) != '=') { + error("Attr.value missing f. "+attrName); + attributes[i] = "1"; + } + else { + read('='); + skip(); + int delimiter = peek(0); + + if (delimiter != '\'' && delimiter != '"') { + error("attr value delimiter missing!"); + delimiter = ' '; + } + else + read(); + + int p = txtPos; + pushText(delimiter, true); + + attributes[i] = get(p); + txtPos = p; + + if (delimiter != ' ') + read(); // skip endquote + } + } + + int sp = depth++ << 2; + + elementStack = ensureCapacity(elementStack, sp + 4); + elementStack[sp + 3] = name; + + if (depth >= nspCounts.length) { + int[] bigger = new int[depth + 4]; + System.arraycopy(nspCounts, 0, bigger, 0, nspCounts.length); + nspCounts = bigger; + } + + nspCounts[depth] = nspCounts[depth - 1]; + + /* + if(!relaxed){ + for (int i = attributeCount - 1; i > 0; i--) { + for (int j = 0; j < i; j++) { + if (getAttributeName(i).equals(getAttributeName(j))) + exception("Duplicate Attribute: " + getAttributeName(i)); + } + } + } + */ + if (processNsp) + adjustNsp(); + else + namespace = ""; + + elementStack[sp] = namespace; + elementStack[sp + 1] = prefix; + elementStack[sp + 2] = name; + } + + /** + * result: isWhitespace; if the setName parameter is set, + * the name of the entity is stored in "name" */ + + private final void pushEntity() + throws IOException, XmlPullParserException { + + push(read()); // & + + + int pos = txtPos; + + while (true) { + int c = read(); + if (c == ';') + break; + if (c < 128 + && (c < '0' || c > '9') + && (c < 'a' || c > 'z') + && (c < 'A' || c > 'Z') + && c != '_' + && c != '-' + && c != '#') { + if(!relaxed){ + error("unterminated entity ref"); + } + //; ends with:"+(char)c); + if (c != -1) + push(c); + return; + } + + push(c); + } + + String code = get(pos); + txtPos = pos - 1; + if (token && type == ENTITY_REF){ + name = code; + } + + if (code.charAt(0) == '#') { + int c = + (code.charAt(1) == 'x' + ? Integer.parseInt(code.substring(2), 16) + : Integer.parseInt(code.substring(1))); + push(c); + return; + } + + String result = (String) entityMap.get(code); + + unresolved = result == null; + + if (unresolved) { + if (!token) + error("unresolved: &" + code + ";"); + } + else { + for (int i = 0; i < result.length(); i++) + push(result.charAt(i)); + } + } + + /** types: + '<': parse to any token (for nextToken ()) + '"': parse to quote + ' ': parse to whitespace or '>' + */ + + private final void pushText(int delimiter, boolean resolveEntities) + throws IOException, XmlPullParserException { + + int next = peek(0); + int cbrCount = 0; + + while (next != -1 && next != delimiter) { // covers eof, '<', '"' + + if (delimiter == ' ') + if (next <= ' ' || next == '>') + break; + + if (next == '&') { + if (!resolveEntities) + break; + + pushEntity(); + } + else if (next == '\n' && type == START_TAG) { + read(); + push(' '); + } + else + push(read()); + + if (next == '>' && cbrCount >= 2 && delimiter != ']') + error("Illegal: ]]>"); + + if (next == ']') + cbrCount++; + else + cbrCount = 0; + + next = peek(0); + } + } + + private final void read(char c) + throws IOException, XmlPullParserException { + int a = read(); + if (a != c) + error("expected: '" + c + "' actual: '" + ((char) a) + "'"); + } + + private final int read() throws IOException { + int result; + + if (peekCount == 0) + result = peek(0); + else { + result = peek[0]; + peek[0] = peek[1]; + } + // else { + // result = peek[0]; + // System.arraycopy (peek, 1, peek, 0, peekCount-1); + // } + peekCount--; + + column++; + + if (result == '\n') { + + line++; + column = 1; + } + + return result; + } + + /** Does never read more than needed */ + + private final int peek(int pos) throws IOException { + + while (pos >= peekCount) { + + int nw; + + if (srcBuf.length <= 1) + nw = reader.read(); + else if (srcPos < srcCount) + nw = srcBuf[srcPos++]; + else { + srcCount = reader.read(srcBuf, 0, srcBuf.length); + if (srcCount <= 0) + nw = -1; + else + nw = srcBuf[0]; + + srcPos = 1; + } + + if (nw == '\r') { + wasCR = true; + peek[peekCount++] = '\n'; + } + else { + if (nw == '\n') { + if (!wasCR) + peek[peekCount++] = '\n'; + } + else + peek[peekCount++] = nw; + + wasCR = false; + } + } + + return peek[pos]; + } + + private final String readName() + throws IOException, XmlPullParserException { + + int pos = txtPos; + int c = peek(0); + if ((c < 'a' || c > 'z') + && (c < 'A' || c > 'Z') + && c != '_' + && c != ':' + && c < 0x0c0 + && !relaxed) + error("name expected"); + + do { + push(read()); + c = peek(0); + } + while ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_' + || c == '-' + || c == ':' + || c == '.' + || c >= 0x0b7); + + String result = get(pos); + txtPos = pos; + return result; + } + + private final void skip() throws IOException { + + while (true) { + int c = peek(0); + if (c > ' ' || c == -1) + break; + read(); + } + } + + // public part starts here... + + public void setInput(Reader reader) throws XmlPullParserException { + this.reader = reader; + + line = 1; + column = 0; + type = START_DOCUMENT; + name = null; + namespace = null; + degenerated = false; + attributeCount = -1; + encoding = null; + version = null; + standalone = null; + + if (reader == null) + return; + + srcPos = 0; + srcCount = 0; + peekCount = 0; + depth = 0; + + entityMap = new HashMap(); + entityMap.put("amp", "&"); + entityMap.put("apos", "'"); + entityMap.put("gt", ">"); + entityMap.put("lt", "<"); + entityMap.put("quot", "\""); + } + + public void setInput(InputStream is, String _enc) + throws XmlPullParserException { + + srcPos = 0; + srcCount = 0; + String enc = _enc; + + if (is == null) + throw new IllegalArgumentException(); + + try { + + if (enc == null) { + // read four bytes + + int chk = 0; + + while (srcCount < 4) { + int i = is.read(); + if (i == -1) + break; + chk = (chk << 8) | i; + srcBuf[srcCount++] = (char) i; + } + + if (srcCount == 4) { + switch (chk) { + case 0x00000FEFF : + enc = "UTF-32BE"; + srcCount = 0; + break; + + case 0x0FFFE0000 : + enc = "UTF-32LE"; + srcCount = 0; + break; + + case 0x03c : + enc = "UTF-32BE"; + srcBuf[0] = '<'; + srcCount = 1; + break; + + case 0x03c000000 : + enc = "UTF-32LE"; + srcBuf[0] = '<'; + srcCount = 1; + break; + + case 0x0003c003f : + enc = "UTF-16BE"; + srcBuf[0] = '<'; + srcBuf[1] = '?'; + srcCount = 2; + break; + + case 0x03c003f00 : + enc = "UTF-16LE"; + srcBuf[0] = '<'; + srcBuf[1] = '?'; + srcCount = 2; + break; + + case 0x03c3f786d : + while (true) { + int i = is.read(); + if (i == -1) + break; + srcBuf[srcCount++] = (char) i; + if (i == '>') { + String s = new String(srcBuf, 0, srcCount); + int i0 = s.indexOf("encoding"); + if (i0 != -1) { + while (s.charAt(i0) != '"' + && s.charAt(i0) != '\'') + i0++; + char deli = s.charAt(i0++); + int i1 = s.indexOf(deli, i0); + enc = s.substring(i0, i1); + } + break; + } + } + + default : + if ((chk & 0x0ffff0000) == 0x0FEFF0000) { + enc = "UTF-16BE"; + srcBuf[0] = + (char) ((srcBuf[2] << 8) | srcBuf[3]); + srcCount = 1; + } + else if ((chk & 0x0ffff0000) == 0x0fffe0000) { + enc = "UTF-16LE"; + srcBuf[0] = + (char) ((srcBuf[3] << 8) | srcBuf[2]); + srcCount = 1; + } + else if ((chk & 0x0ffffff00) == 0x0EFBBBF00) { + enc = "UTF-8"; + srcBuf[0] = srcBuf[3]; + srcCount = 1; + } + } + } + } + + if (enc == null) + enc = "UTF-8"; + + int sc = srcCount; + setInput(new InputStreamReader(is, enc)); + encoding = _enc; + srcCount = sc; + } + catch (Exception e) { + throw new XmlPullParserException( + "Invalid stream or encoding: " + e.toString(), + this, + e); + } + } + + public boolean getFeature(String feature) { + if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) + return processNsp; + else if (isProp(feature, false, "relaxed")) + return relaxed; + else + return false; + } + + public String getInputEncoding() { + return encoding; + } + + public void defineEntityReplacementText(String entity, String value) + throws XmlPullParserException { + if (entityMap == null) + throw new RuntimeException("entity replacement text must be defined after setInput!"); + entityMap.put(entity, value); + } + + public Object getProperty(String property) { + if (isProp(property, true, "xmldecl-version")) + return version; + if (isProp(property, true, "xmldecl-standalone")) + return standalone; + if (isProp(property, true, "location")) + return location != null ? location : reader.toString(); + return null; + } + + public int getNamespaceCount(int depth) { + if (depth > this.depth) + throw new IndexOutOfBoundsException(); + return nspCounts[depth]; + } + + public String getNamespacePrefix(int pos) { + return nspStack[pos << 1]; + } + + public String getNamespaceUri(int pos) { + return nspStack[(pos << 1) + 1]; + } + + public String getNamespace(String prefix) { + + if ("xml".equals(prefix)) + return "http://www.w3.org/XML/1998/namespace"; + if ("xmlns".equals(prefix)) + return "http://www.w3.org/2000/xmlns/"; + + for (int i = (getNamespaceCount(depth) << 1) - 2; i >= 0; i -= 2) { + if (prefix == null) { + if (nspStack[i] == null) + return nspStack[i + 1]; + } + else if (prefix.equals(nspStack[i])) + return nspStack[i + 1]; + } + return null; + } + + public int getDepth() { + return depth; + } + + public String getPositionDescription() { + + StringBuffer buf = + new StringBuffer(type < TYPES.length ? TYPES[type] : "unknown"); + buf.append(' '); + + if (type == START_TAG || type == END_TAG) { + if (degenerated) + buf.append("(empty) "); + buf.append('<'); + if (type == END_TAG) + buf.append('/'); + + if (prefix != null) + buf.append("{" + namespace + "}" + prefix + ":"); + buf.append(name); + + int cnt = attributeCount << 2; + for (int i = 0; i < cnt; i += 4) { + buf.append(' '); + if (attributes[i + 1] != null) + buf.append( + "{" + attributes[i] + "}" + attributes[i + 1] + ":"); + buf.append(attributes[i + 2] + "='" + attributes[i + 3] + "'"); + } + + buf.append('>'); + } + else if (type == IGNORABLE_WHITESPACE); + else if (type != TEXT) + buf.append(getText()); + else if (isWhitespace) + buf.append("(whitespace)"); + else { + String text = getText(); + if (text.length() > 16) + text = text.substring(0, 16) + "..."; + buf.append(text); + } + + buf.append("@"+line + ":" + column); + if(location != null){ + buf.append(" in "); + buf.append(location); + } + else if(reader != null){ + buf.append(" in "); + buf.append(reader.toString()); + } + return buf.toString(); + } + + public int getLineNumber() { + return line; + } + + public int getColumnNumber() { + return column; + } + + public boolean isWhitespace() throws XmlPullParserException { + if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT) + exception(ILLEGAL_TYPE); + return isWhitespace; + } + + public String getText() { + return type < TEXT + || (type == ENTITY_REF && unresolved) ? null : get(0); + } + + public char[] getTextCharacters(int[] poslen) { + if (type >= TEXT) { + if (type == ENTITY_REF) { + poslen[0] = 0; + poslen[1] = name.length(); + return name.toCharArray(); + } + poslen[0] = 0; + poslen[1] = txtPos; + return txtBuf; + } + + poslen[0] = -1; + poslen[1] = -1; + return null; + } + + public String getNamespace() { + return namespace; + } + + public String getName() { + return name; + } + + public String getPrefix() { + return prefix; + } + + public boolean isEmptyElementTag() throws XmlPullParserException { + if (type != START_TAG) + exception(ILLEGAL_TYPE); + return degenerated; + } + + public int getAttributeCount() { + return attributeCount; + } + + public String getAttributeType(int index) { + return "CDATA"; + } + + public boolean isAttributeDefault(int index) { + return false; + } + + public String getAttributeNamespace(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[index << 2]; + } + + public String getAttributeName(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[(index << 2) + 2]; + } + + public String getAttributePrefix(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[(index << 2) + 1]; + } + + public String getAttributeValue(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[(index << 2) + 3]; + } + + public String getAttributeValue(String namespace, String name) { + + for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) { + if (attributes[i + 2].equals(name) + && (namespace == null || attributes[i].equals(namespace))) + return attributes[i + 3]; + } + + return null; + } + + public int getEventType() throws XmlPullParserException { + return type; + } + + public int next() throws XmlPullParserException, IOException { + + txtPos = 0; + isWhitespace = true; + int minType = 9999; + token = false; + + do { + nextImpl(); + if (type < minType) + minType = type; + // if (curr <= TEXT) type = curr; + } + while (minType > ENTITY_REF // ignorable + || (minType >= TEXT && peekType() >= TEXT)); + + type = minType; + if (type > TEXT) + type = TEXT; + + return type; + } + + public int nextToken() throws XmlPullParserException, IOException { + + isWhitespace = true; + txtPos = 0; + + token = true; + nextImpl(); + return type; + } + + // + // utility methods to make XML parsing easier ... + + public int nextTag() throws XmlPullParserException, IOException { + + next(); + if (type == TEXT && isWhitespace) + next(); + + if (type != END_TAG && type != START_TAG) + exception("unexpected type"); + + return type; + } + + public void require(int type, String namespace, String name) + throws XmlPullParserException, IOException { + + if (type != this.type + || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) + exception( + "expected: " + TYPES[type] + " {" + namespace + "}" + name); + } + + public String nextText() throws XmlPullParserException, IOException { + if (type != START_TAG) + exception("precondition: START_TAG"); + + next(); + + String result; + + if (type == TEXT) { + result = getText(); + next(); + } + else + result = ""; + + if (type != END_TAG) + exception("END_TAG expected"); + + return result; + } + + public void setFeature(String feature, boolean value) + throws XmlPullParserException { + if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) + processNsp = value; + else if (isProp(feature, false, "relaxed")) + relaxed = value; + else + exception("unsupported feature: " + feature); + } + + public void setProperty(String property, Object value) + throws XmlPullParserException { + if(isProp(property, true, "location")) + location = value; + else + throw new XmlPullParserException("unsupported property: " + property); + } + + /** + * Skip sub tree that is currently porser positioned on. + * <br>NOTE: parser must be on START_TAG and when funtion returns + * parser will be positioned on corresponding END_TAG. + */ + + // Implementation copied from Alek's mail... + + public void skipSubTree() throws XmlPullParserException, IOException { + require(START_TAG, null, null); + int level = 1; + while (level > 0) { + int eventType = next(); + if (eventType == END_TAG) { + --level; + } + else if (eventType == START_TAG) { + ++level; + } + } + } +} diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java new file mode 100644 index 0000000..d63ed04 --- /dev/null +++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java @@ -0,0 +1,562 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + + +package org.kxml2.io; + +import java.io.*; +import org.xmlpull.v1.*; + +public class KXmlSerializer implements XmlSerializer { + + // static final String UNDEFINED = ":"; + + // BEGIN android-added + /** size (in characters) for the write buffer */ + private static final int WRITE_BUFFER_SIZE = 500; + // END android-added + + // BEGIN android-changed + // (Guarantee that the writer is always buffered.) + private BufferedWriter writer; + // END android-changed + + private boolean pending; + private int auto; + private int depth; + + private String[] elementStack = new String[12]; + //nsp/prefix/name + private int[] nspCounts = new int[4]; + private String[] nspStack = new String[8]; + //prefix/nsp; both empty are "" + private boolean[] indent = new boolean[4]; + private boolean unicode; + private String encoding; + + private final void check(boolean close) throws IOException { + if (!pending) + return; + + depth++; + pending = false; + + if (indent.length <= depth) { + boolean[] hlp = new boolean[depth + 4]; + System.arraycopy(indent, 0, hlp, 0, depth); + indent = hlp; + } + indent[depth] = indent[depth - 1]; + + for (int i = nspCounts[depth - 1]; + i < nspCounts[depth]; + i++) { + writer.write(' '); + writer.write("xmlns"); + if (!"".equals(nspStack[i * 2])) { + writer.write(':'); + writer.write(nspStack[i * 2]); + } + else if ("".equals(getNamespace()) && !"".equals(nspStack[i * 2 + 1])) + throw new IllegalStateException("Cannot set default namespace for elements in no namespace"); + writer.write("=\""); + writeEscaped(nspStack[i * 2 + 1], '"'); + writer.write('"'); + } + + if (nspCounts.length <= depth + 1) { + int[] hlp = new int[depth + 8]; + System.arraycopy(nspCounts, 0, hlp, 0, depth + 1); + nspCounts = hlp; + } + + nspCounts[depth + 1] = nspCounts[depth]; + // nspCounts[depth + 2] = nspCounts[depth]; + + writer.write(close ? " />" : ">"); + } + + private final void writeEscaped(String s, int quot) + throws IOException { + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '\n': + case '\r': + case '\t': + if(quot == -1) + writer.write(c); + else + writer.write("&#"+((int) c)+';'); + break; + case '&' : + writer.write("&"); + break; + case '>' : + writer.write(">"); + break; + case '<' : + writer.write("<"); + break; + case '"' : + case '\'' : + if (c == quot) { + writer.write( + c == '"' ? """ : "'"); + break; + } + default : + //if(c < ' ') + // throw new IllegalArgumentException("Illegal control code:"+((int) c)); + + if (c >= ' ' && c !='@' && (c < 127 || unicode)) + writer.write(c); + else + writer.write("&#" + ((int) c) + ";"); + + } + } + } + + /* + private final void writeIndent() throws IOException { + writer.write("\r\n"); + for (int i = 0; i < depth; i++) + writer.write(' '); + }*/ + + public void docdecl(String dd) throws IOException { + writer.write("<!DOCTYPE"); + writer.write(dd); + writer.write(">"); + } + + public void endDocument() throws IOException { + while (depth > 0) { + endTag( + elementStack[depth * 3 - 3], + elementStack[depth * 3 - 1]); + } + flush(); + } + + public void entityRef(String name) throws IOException { + check(false); + writer.write('&'); + writer.write(name); + writer.write(';'); + } + + public boolean getFeature(String name) { + //return false; + return ( + "http://xmlpull.org/v1/doc/features.html#indent-output" + .equals( + name)) + ? indent[depth] + : false; + } + + public String getPrefix(String namespace, boolean create) { + try { + return getPrefix(namespace, false, create); + } + catch (IOException e) { + throw new RuntimeException(e.toString()); + } + } + + private final String getPrefix( + String namespace, + boolean includeDefault, + boolean create) + throws IOException { + + for (int i = nspCounts[depth + 1] * 2 - 2; + i >= 0; + i -= 2) { + if (nspStack[i + 1].equals(namespace) + && (includeDefault || !nspStack[i].equals(""))) { + String cand = nspStack[i]; + for (int j = i + 2; + j < nspCounts[depth + 1] * 2; + j++) { + if (nspStack[j].equals(cand)) { + cand = null; + break; + } + } + if (cand != null) + return cand; + } + } + + if (!create) + return null; + + String prefix; + + if ("".equals(namespace)) + prefix = ""; + else { + do { + prefix = "n" + (auto++); + for (int i = nspCounts[depth + 1] * 2 - 2; + i >= 0; + i -= 2) { + if (prefix.equals(nspStack[i])) { + prefix = null; + break; + } + } + } + while (prefix == null); + } + + boolean p = pending; + pending = false; + setPrefix(prefix, namespace); + pending = p; + return prefix; + } + + public Object getProperty(String name) { + throw new RuntimeException("Unsupported property"); + } + + public void ignorableWhitespace(String s) + throws IOException { + text(s); + } + + public void setFeature(String name, boolean value) { + if ("http://xmlpull.org/v1/doc/features.html#indent-output" + .equals(name)) { + indent[depth] = value; + } + else + throw new RuntimeException("Unsupported Feature"); + } + + public void setProperty(String name, Object value) { + throw new RuntimeException( + "Unsupported Property:" + value); + } + + public void setPrefix(String prefix, String namespace) + throws IOException { + + check(false); + if (prefix == null) + prefix = ""; + if (namespace == null) + namespace = ""; + + String defined = getPrefix(namespace, true, false); + + // boil out if already defined + + if (prefix.equals(defined)) + return; + + int pos = (nspCounts[depth + 1]++) << 1; + + if (nspStack.length < pos + 1) { + String[] hlp = new String[nspStack.length + 16]; + System.arraycopy(nspStack, 0, hlp, 0, pos); + nspStack = hlp; + } + + nspStack[pos++] = prefix; + nspStack[pos] = namespace; + } + + public void setOutput(Writer writer) { + // BEGIN android-changed + // Guarantee that the writer is always buffered. + if (writer instanceof BufferedWriter) { + this.writer = (BufferedWriter) writer; + } else { + this.writer = new BufferedWriter(writer, WRITE_BUFFER_SIZE); + } + // END android-changed + + // elementStack = new String[12]; //nsp/prefix/name + //nspCounts = new int[4]; + //nspStack = new String[8]; //prefix/nsp + //indent = new boolean[4]; + + nspCounts[0] = 2; + nspCounts[1] = 2; + nspStack[0] = ""; + nspStack[1] = ""; + nspStack[2] = "xml"; + nspStack[3] = "http://www.w3.org/XML/1998/namespace"; + pending = false; + auto = 0; + depth = 0; + + unicode = false; + } + + public void setOutput(OutputStream os, String encoding) + throws IOException { + if (os == null) + throw new IllegalArgumentException(); + setOutput( + encoding == null + ? new OutputStreamWriter(os) + : new OutputStreamWriter(os, encoding)); + this.encoding = encoding; + if (encoding != null + && encoding.toLowerCase().startsWith("utf")) + unicode = true; + } + + public void startDocument( + String encoding, + Boolean standalone) + throws IOException { + writer.write("<?xml version='1.0' "); + + if (encoding != null) { + this.encoding = encoding; + if (encoding.toLowerCase().startsWith("utf")) + unicode = true; + } + + if (this.encoding != null) { + writer.write("encoding='"); + writer.write(this.encoding); + writer.write("' "); + } + + if (standalone != null) { + writer.write("standalone='"); + writer.write( + standalone.booleanValue() ? "yes" : "no"); + writer.write("' "); + } + writer.write("?>"); + } + + public XmlSerializer startTag(String namespace, String name) + throws IOException { + check(false); + + // if (namespace == null) + // namespace = ""; + + if (indent[depth]) { + writer.write("\r\n"); + for (int i = 0; i < depth; i++) + writer.write(" "); + } + + int esp = depth * 3; + + if (elementStack.length < esp + 3) { + String[] hlp = new String[elementStack.length + 12]; + System.arraycopy(elementStack, 0, hlp, 0, esp); + elementStack = hlp; + } + + String prefix = + namespace == null + ? "" + : getPrefix(namespace, true, true); + + if ("".equals(namespace)) { + for (int i = nspCounts[depth]; + i < nspCounts[depth + 1]; + i++) { + if ("".equals(nspStack[i * 2]) && !"".equals(nspStack[i * 2 + 1])) { + throw new IllegalStateException("Cannot set default namespace for elements in no namespace"); + } + } + } + + elementStack[esp++] = namespace; + elementStack[esp++] = prefix; + elementStack[esp] = name; + + writer.write('<'); + if (!"".equals(prefix)) { + writer.write(prefix); + writer.write(':'); + } + + writer.write(name); + + pending = true; + + return this; + } + + public XmlSerializer attribute( + String namespace, + String name, + String value) + throws IOException { + if (!pending) + throw new IllegalStateException("illegal position for attribute"); + + // int cnt = nspCounts[depth]; + + if (namespace == null) + namespace = ""; + + // depth--; + // pending = false; + + String prefix = + "".equals(namespace) + ? "" + : getPrefix(namespace, false, true); + + // pending = true; + // depth++; + + /* if (cnt != nspCounts[depth]) { + writer.write(' '); + writer.write("xmlns"); + if (nspStack[cnt * 2] != null) { + writer.write(':'); + writer.write(nspStack[cnt * 2]); + } + writer.write("=\""); + writeEscaped(nspStack[cnt * 2 + 1], '"'); + writer.write('"'); + } + */ + + writer.write(' '); + if (!"".equals(prefix)) { + writer.write(prefix); + writer.write(':'); + } + writer.write(name); + writer.write('='); + char q = value.indexOf('"') == -1 ? '"' : '\''; + writer.write(q); + writeEscaped(value, q); + writer.write(q); + + return this; + } + + public void flush() throws IOException { + check(false); + writer.flush(); + } + /* + public void close() throws IOException { + check(); + writer.close(); + } + */ + public XmlSerializer endTag(String namespace, String name) + throws IOException { + + if (!pending) + depth--; + // if (namespace == null) + // namespace = ""; + + if ((namespace == null + && elementStack[depth * 3] != null) + || (namespace != null + && !namespace.equals(elementStack[depth * 3])) + || !elementStack[depth * 3 + 2].equals(name)) + throw new IllegalArgumentException("</{"+namespace+"}"+name+"> does not match start"); + + if (pending) { + check(true); + depth--; + } + else { + if (indent[depth + 1]) { + writer.write("\r\n"); + for (int i = 0; i < depth; i++) + writer.write(" "); + } + + writer.write("</"); + String prefix = elementStack[depth * 3 + 1]; + if (!"".equals(prefix)) { + writer.write(prefix); + writer.write(':'); + } + writer.write(name); + writer.write('>'); + } + + nspCounts[depth + 1] = nspCounts[depth]; + return this; + } + + public String getNamespace() { + return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 3]; + } + + public String getName() { + return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 1]; + } + + public int getDepth() { + return pending ? depth + 1 : depth; + } + + public XmlSerializer text(String text) throws IOException { + check(false); + indent[depth] = false; + writeEscaped(text, -1); + return this; + } + + public XmlSerializer text(char[] text, int start, int len) + throws IOException { + text(new String(text, start, len)); + return this; + } + + public void cdsect(String data) throws IOException { + check(false); + writer.write("<![CDATA["); + writer.write(data); + writer.write("]]>"); + } + + public void comment(String comment) throws IOException { + check(false); + writer.write("<!--"); + writer.write(comment); + writer.write("-->"); + } + + public void processingInstruction(String pi) + throws IOException { + check(false); + writer.write("<?"); + writer.write(pi); + writer.write("?>"); + } +} diff --git a/xml/src/main/java/org/kxml2/kdom/Document.java b/xml/src/main/java/org/kxml2/kdom/Document.java new file mode 100644 index 0000000..859334c --- /dev/null +++ b/xml/src/main/java/org/kxml2/kdom/Document.java @@ -0,0 +1,129 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + + +package org.kxml2.kdom; + +import java.io.*; + +import org.xmlpull.v1.*; +/** The document consists of some legacy events and a single root + element. This class basically adds some consistency checks to + Node. */ + +public class Document extends Node { + + protected int rootIndex = -1; + String encoding; + Boolean standalone; + + /** returns "#document" */ + + public String getEncoding () { + return encoding; + } + + public void setEncoding(String enc) { + this.encoding = enc; + } + + public void setStandalone (Boolean standalone) { + this.standalone = standalone; + } + + public Boolean getStandalone() { + return standalone; + } + + + public String getName() { + return "#document"; + } + + /** Adds a child at the given index position. Throws + an exception when a second root element is added */ + + public void addChild(int index, int type, Object child) { + if (type == ELEMENT) { + // if (rootIndex != -1) + // throw new RuntimeException("Only one document root element allowed"); + + rootIndex = index; + } + else if (rootIndex >= index) + rootIndex++; + + super.addChild(index, type, child); + } + + /** reads the document and checks if the last event + is END_DOCUMENT. If not, an exception is thrown. + The end event is consumed. For parsing partial + XML structures, consider using Node.parse (). */ + + public void parse(XmlPullParser parser) + throws IOException, XmlPullParserException { + + parser.require(XmlPullParser.START_DOCUMENT, null, null); + parser.nextToken (); + + encoding = parser.getInputEncoding(); + standalone = (Boolean)parser.getProperty ("http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone"); + + super.parse(parser); + + if (parser.getEventType() != XmlPullParser.END_DOCUMENT) + throw new RuntimeException("Document end expected!"); + + } + + public void removeChild(int index) { + if (index == rootIndex) + rootIndex = -1; + else if (index < rootIndex) + rootIndex--; + + super.removeChild(index); + } + + /** returns the root element of this document. */ + + public Element getRootElement() { + if (rootIndex == -1) + throw new RuntimeException("Document has no root element!"); + + return (Element) getChild(rootIndex); + } + + + /** Writes this node to the given XmlWriter. For node and document, + this method is identical to writeChildren, except that the + stream is flushed automatically. */ + + public void write(XmlSerializer writer) + throws IOException { + + writer.startDocument(encoding, standalone); + writeChildren(writer); + writer.endDocument(); + } + + +}
\ No newline at end of file diff --git a/xml/src/main/java/org/kxml2/kdom/Element.java b/xml/src/main/java/org/kxml2/kdom/Element.java new file mode 100644 index 0000000..61d5111 --- /dev/null +++ b/xml/src/main/java/org/kxml2/kdom/Element.java @@ -0,0 +1,336 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + +package org.kxml2.kdom; + +import java.io.*; +import java.util.*; + +import org.xmlpull.v1.*; + +/** + * In order to create an element, please use the createElement method + * instead of invoking the constructor directly. The right place to + * add user defined initialization code is the init method. */ + +public class Element extends Node { + + protected String namespace; + protected String name; + protected ArrayList attributes; + protected Node parent; + protected ArrayList prefixes; + + public Element() { + } + + /** + * called when all properties are set, but before children + * are parsed. Please do not use setParent for initialization + * code any longer. */ + + public void init() { + } + + + + + /** + * removes all children and attributes */ + + public void clear() { + attributes = null; + children = null; + } + + /** + * Forwards creation request to parent if any, otherwise + * calls super.createElement. */ + + public Element createElement( + String namespace, + String name) { + + return (this.parent == null) + ? super.createElement(namespace, name) + : this.parent.createElement(namespace, name); + } + + /** + * Returns the number of attributes of this element. */ + + public int getAttributeCount() { + return attributes == null ? 0 : attributes.size(); + } + + public String getAttributeNamespace (int index) { + return ((String []) attributes.get(index)) [0]; + } + + public String getAttributeName (int index) { + return ((String []) attributes.get(index)) [1]; + } + + + public String getAttributeValue (int index) { + return ((String []) attributes.get(index)) [2]; + } + + + public String + getAttributeValue(String name) + { + return getAttributeValue(null, name); + } + + public String getAttributeValue (String namespace, String name) { + for (int i = 0; i < getAttributeCount (); i++) { + if (name.equals (getAttributeName (i)) + && (namespace == null || namespace.equals (getAttributeNamespace(i)))) { + return getAttributeValue (i); + } + } + return null; + } + + /** + * Returns the root node, determined by ascending to the + * all parents un of the root element. */ + + public Node getRoot() { + + Element current = this; + + while (current.parent != null) { + if (!(current.parent instanceof Element)) return current.parent; + current = (Element) current.parent; + } + + return current; + } + + /** + * returns the (local) name of the element */ + + public String getName() { + return name; + } + + /** + * returns the namespace of the element */ + + public String getNamespace() { + return namespace; + } + + + /** + * returns the namespace for the given prefix */ + + public String getNamespaceUri (String prefix) { + int cnt = getNamespaceCount (); + for (int i = 0; i < cnt; i++) { + if (prefix == getNamespacePrefix (i) || + (prefix != null && prefix.equals (getNamespacePrefix (i)))) + return getNamespaceUri (i); + } + return parent instanceof Element ? ((Element) parent).getNamespaceUri (prefix) : null; + } + + + /** + * returns the number of declared namespaces, NOT including + * parent elements */ + + public int getNamespaceCount () { + return (prefixes == null ? 0 : prefixes.size ()); + } + + + public String getNamespacePrefix (int i) { + return ((String []) prefixes.get(i)) [0]; + } + + public String getNamespaceUri (int i) { + return ((String []) prefixes.get(i)) [1]; + } + + + /** + * Returns the parent node of this element */ + + public Node getParent() { + return parent; + } + + /* + * Returns the parent element if available, null otherwise + + public Element getParentElement() { + return (parent instanceof Element) + ? ((Element) parent) + : null; + } +*/ + + /** + * Builds the child elements from the given Parser. By overwriting + * parse, an element can take complete control over parsing its + * subtree. */ + + public void parse(XmlPullParser parser) + throws IOException, XmlPullParserException { + + for (int i = parser.getNamespaceCount (parser.getDepth () - 1); + i < parser.getNamespaceCount (parser.getDepth ()); i++) { + setPrefix (parser.getNamespacePrefix (i), parser.getNamespaceUri(i)); + } + + + for (int i = 0; i < parser.getAttributeCount (); i++) + setAttribute (parser.getAttributeNamespace (i), +// parser.getAttributePrefix (i), + parser.getAttributeName (i), + parser.getAttributeValue (i)); + + + // if (prefixMap == null) throw new RuntimeException ("!!"); + + init(); + + + if (parser.isEmptyElementTag()) + parser.nextToken (); + else { + parser.nextToken (); + super.parse(parser); + + if (getChildCount() == 0) + addChild(IGNORABLE_WHITESPACE, ""); + } + + parser.require( + XmlPullParser.END_TAG, + getNamespace(), + getName()); + + parser.nextToken (); + } + + + /** + * Sets the given attribute; a value of null removes the attribute */ + + public void setAttribute (String namespace, String name, String value) { + if (attributes == null) + attributes = new ArrayList(); + + if (namespace == null) + namespace = ""; + + for (int i = attributes.size()-1; i >=0; i--){ + String[] attribut = (String[]) attributes.get(i); + if (attribut[0].equals(namespace) && + attribut[1].equals(name)){ + + if (value == null) { + attributes.remove(i); + } + else { + attribut[2] = value; + } + return; + } + } + + attributes.add(new String[] {namespace, name, value}); + } + + + /** + * Sets the given prefix; a namespace value of null removess the + * prefix */ + + public void setPrefix (String prefix, String namespace) { + if (prefixes == null) prefixes = new ArrayList(); + prefixes.add(new String [] {prefix, namespace}); + } + + + /** + * sets the name of the element */ + + public void setName(String name) { + this.name = name; + } + + /** + * sets the namespace of the element. Please note: For no + * namespace, please use Xml.NO_NAMESPACE, null is not a legal + * value. Currently, null is converted to Xml.NO_NAMESPACE, but + * future versions may throw an exception. */ + + public void setNamespace(String namespace) { + if (namespace == null) + throw new NullPointerException ("Use \"\" for empty namespace"); + this.namespace = namespace; + } + + /** + * Sets the Parent of this element. Automatically called from the + * add method. Please use with care, you can simply + * create inconsitencies in the document tree structure using + * this method! */ + + protected void setParent(Node parent) { + this.parent = parent; + } + + + /** + * Writes this element and all children to the given XmlWriter. */ + + public void write(XmlSerializer writer) + throws IOException { + + if (prefixes != null) { + for (int i = 0; i < prefixes.size(); i++) { + writer.setPrefix (getNamespacePrefix (i), getNamespaceUri (i)); + } + } + + writer.startTag( + getNamespace(), + getName()); + + int len = getAttributeCount(); + + for (int i = 0; i < len; i++) { + writer.attribute( + getAttributeNamespace(i), + getAttributeName(i), + getAttributeValue(i)); + } + + writeChildren(writer); + + writer.endTag(getNamespace (), getName ()); + } +} diff --git a/xml/src/main/java/org/kxml2/kdom/Node.java b/xml/src/main/java/org/kxml2/kdom/Node.java new file mode 100644 index 0000000..4855893 --- /dev/null +++ b/xml/src/main/java/org/kxml2/kdom/Node.java @@ -0,0 +1,366 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + +package org.kxml2.kdom; + +import java.util.*; +import java.io.*; +import org.xmlpull.v1.*; +/** A common base class for Document and Element, also used for + storing XML fragments. */ + +public class Node { //implements XmlIO{ + + public static final int DOCUMENT = 0; + public static final int ELEMENT = 2; + public static final int TEXT = 4; + public static final int CDSECT = 5; + public static final int ENTITY_REF = 6; + public static final int IGNORABLE_WHITESPACE = 7; + public static final int PROCESSING_INSTRUCTION = 8; + public static final int COMMENT = 9; + public static final int DOCDECL = 10; + + protected ArrayList children; + protected StringBuffer types; + + /** inserts the given child object of the given type at the + given index. */ + + public void addChild(int index, int type, Object child) { + + if (child == null) + throw new NullPointerException(); + + if (children == null) { + children = new ArrayList(); + types = new StringBuffer(); + } + + if (type == ELEMENT) { + if (!(child instanceof Element)) + throw new RuntimeException("Element obj expected)"); + + ((Element) child).setParent(this); + } + else if (!(child instanceof String)) + throw new RuntimeException("String expected"); + + children.add(index, child); + types.insert(index, (char) type); + } + + /** convenience method for addChild (getChildCount (), child) */ + + public void addChild(int type, Object child) { + addChild(getChildCount(), type, child); + } + + /** Builds a default element with the given properties. Elements + should always be created using this method instead of the + constructor in order to enable construction of specialized + subclasses by deriving custom Document classes. Please note: + For no namespace, please use Xml.NO_NAMESPACE, null is not a + legal value. Currently, null is converted to Xml.NO_NAMESPACE, + but future versions may throw an exception. */ + + public Element createElement(String namespace, String name) { + + Element e = new Element(); + e.namespace = namespace == null ? "" : namespace; + e.name = name; + return e; + } + + /** Returns the child object at the given index. For child + elements, an Element object is returned. For all other child + types, a String is returned. */ + + public Object getChild(int index) { + return children.get(index); + } + + /** Returns the number of child objects */ + + public int getChildCount() { + return children == null ? 0 : children.size(); + } + + /** returns the element at the given index. If the node at the + given index is a text node, null is returned */ + + public Element getElement(int index) { + Object child = getChild(index); + return (child instanceof Element) ? (Element) child : null; + } + + /** Returns the element with the given namespace and name. If the + element is not found, or more than one matching elements are + found, an exception is thrown. */ + + public Element getElement(String namespace, String name) { + + int i = indexOf(namespace, name, 0); + int j = indexOf(namespace, name, i + 1); + + if (i == -1 || j != -1) + throw new RuntimeException( + "Element {" + + namespace + + "}" + + name + + (i == -1 ? " not found in " : " more than once in ") + + this); + + return getElement(i); + } + + /* returns "#document-fragment". For elements, the element name is returned + + public String getName() { + return "#document-fragment"; + } + + /** Returns the namespace of the current element. For Node + and Document, Xml.NO_NAMESPACE is returned. + + public String getNamespace() { + return ""; + } + + public int getNamespaceCount () { + return 0; + } + + /** returns the text content if the element has text-only + content. Throws an exception for mixed content + + public String getText() { + + StringBuffer buf = new StringBuffer(); + int len = getChildCount(); + + for (int i = 0; i < len; i++) { + if (isText(i)) + buf.append(getText(i)); + else if (getType(i) == ELEMENT) + throw new RuntimeException("not text-only content!"); + } + + return buf.toString(); + } + */ + + /** Returns the text node with the given index or null if the node + with the given index is not a text node. */ + + public String getText(int index) { + return (isText(index)) ? (String) getChild(index) : null; + } + + /** Returns the type of the child at the given index. Possible + types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */ + + public int getType(int index) { + return types.charAt(index); + } + + /** Convenience method for indexOf (getNamespace (), name, + startIndex). + + public int indexOf(String name, int startIndex) { + return indexOf(getNamespace(), name, startIndex); + } + */ + + /** Performs search for an element with the given namespace and + name, starting at the given start index. A null namespace + matches any namespace, please use Xml.NO_NAMESPACE for no + namespace). returns -1 if no matching element was found. */ + + public int indexOf(String namespace, String name, int startIndex) { + + int len = getChildCount(); + + for (int i = startIndex; i < len; i++) { + + Element child = getElement(i); + + if (child != null + && name.equals(child.getName()) + && (namespace == null || namespace.equals(child.getNamespace()))) + return i; + } + return -1; + } + + public boolean isText(int i) { + int t = getType(i); + return t == TEXT || t == IGNORABLE_WHITESPACE || t == CDSECT; + } + + /** Recursively builds the child elements from the given parser + until an end tag or end document is found. + The end tag is not consumed. */ + + public void parse(XmlPullParser parser) + throws IOException, XmlPullParserException { + + boolean leave = false; + + do { + int type = parser.getEventType(); + + // System.out.println(parser.getPositionDescription()); + + switch (type) { + + case XmlPullParser.START_TAG : + { + Element child = + createElement( + parser.getNamespace(), + parser.getName()); + // child.setAttributes (event.getAttributes ()); + addChild(ELEMENT, child); + + // order is important here since + // setparent may perform some init code! + + child.parse(parser); + break; + } + + case XmlPullParser.END_DOCUMENT : + case XmlPullParser.END_TAG : + leave = true; + break; + + default : + if (parser.getText() != null) + addChild( + type == XmlPullParser.ENTITY_REF ? TEXT : type, + parser.getText()); + else if ( + type == XmlPullParser.ENTITY_REF + && parser.getName() != null) { + addChild(ENTITY_REF, parser.getName()); + } + parser.nextToken(); + } + } + while (!leave); + } + + /** Removes the child object at the given index */ + + public void removeChild(int idx) { + children.remove(idx); + + /*** Modification by HHS - start ***/ + // types.deleteCharAt (index); + /***/ + int n = types.length() - 1; + + for (int i = idx; i < n; i++) + types.setCharAt(i, types.charAt(i + 1)); + + types.setLength(n); + + /*** Modification by HHS - end ***/ + } + + /* returns a valid XML representation of this Element including + attributes and children. + public String toString() { + try { + ByteArrayOutputStream bos = + new ByteArrayOutputStream(); + XmlWriter xw = + new XmlWriter(new OutputStreamWriter(bos)); + write(xw); + xw.close(); + return new String(bos.toByteArray()); + } + catch (IOException e) { + throw new RuntimeException(e.toString()); + } + } + */ + + /** Writes this node to the given XmlWriter. For node and document, + this method is identical to writeChildren, except that the + stream is flushed automatically. */ + + public void write(XmlSerializer writer) throws IOException { + writeChildren(writer); + writer.flush(); + } + + /** Writes the children of this node to the given XmlWriter. */ + + public void writeChildren(XmlSerializer writer) throws IOException { + if (children == null) + return; + + int len = children.size(); + + for (int i = 0; i < len; i++) { + int type = getType(i); + Object child = children.get(i); + switch (type) { + case ELEMENT : + ((Element) child).write(writer); + break; + + case TEXT : + writer.text((String) child); + break; + + case IGNORABLE_WHITESPACE : + writer.ignorableWhitespace((String) child); + break; + + case CDSECT : + writer.cdsect((String) child); + break; + + case COMMENT : + writer.comment((String) child); + break; + + case ENTITY_REF : + writer.entityRef((String) child); + break; + + case PROCESSING_INSTRUCTION : + writer.processingInstruction((String) child); + break; + + case DOCDECL : + writer.docdecl((String) child); + break; + + default : + throw new RuntimeException("Illegal type: " + type); + } + } + } +} diff --git a/xml/src/main/java/org/kxml2/wap/Wbxml.java b/xml/src/main/java/org/kxml2/wap/Wbxml.java new file mode 100644 index 0000000..5b0c2d3 --- /dev/null +++ b/xml/src/main/java/org/kxml2/wap/Wbxml.java @@ -0,0 +1,49 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + +package org.kxml2.wap; + + +/** contains the WBXML constants */ + + +public interface Wbxml { + + static public final int SWITCH_PAGE = 0; + static public final int END = 1; + static public final int ENTITY = 2; + static public final int STR_I = 3; + static public final int LITERAL = 4; + static public final int EXT_I_0 = 0x40; + static public final int EXT_I_1 = 0x41; + static public final int EXT_I_2 = 0x42; + static public final int PI = 0x43; + static public final int LITERAL_C = 0x44; + static public final int EXT_T_0 = 0x80; + static public final int EXT_T_1 = 0x81; + static public final int EXT_T_2 = 0x82; + static public final int STR_T = 0x83; + static public final int LITERAL_A = 0x084; + static public final int EXT_0 = 0x0c0; + static public final int EXT_1 = 0x0c1; + static public final int EXT_2 = 0x0c2; + static public final int OPAQUE = 0x0c3; + static public final int LITERAL_AC = 0x0c4; +} diff --git a/xml/src/main/java/org/kxml2/wap/WbxmlParser.java b/xml/src/main/java/org/kxml2/wap/WbxmlParser.java new file mode 100644 index 0000000..c3852eb --- /dev/null +++ b/xml/src/main/java/org/kxml2/wap/WbxmlParser.java @@ -0,0 +1,1050 @@ +/* Copyright (c) 2002,2003,2004 Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + +// Contributors: Bjorn Aadland, Chris Bartley, Nicola Fankhauser, +// Victor Havin, Christian Kurzke, Bogdan Onoiu, +// Jain Sanjay, David Santoro. + +package org.kxml2.wap; + +import java.io.*; +import java.util.Vector; +import java.util.Hashtable; + +import org.xmlpull.v1.*; + + +public class WbxmlParser implements XmlPullParser { + + public static final int WAP_EXTENSION = 64; + + static final private String UNEXPECTED_EOF = + "Unexpected EOF"; + static final private String ILLEGAL_TYPE = + "Wrong event type"; + + private InputStream in; + + private int TAG_TABLE = 0; + private int ATTR_START_TABLE = 1; + private int ATTR_VALUE_TABLE = 2; + + private String[] attrStartTable; + private String[] attrValueTable; + private String[] tagTable; + private byte[] stringTable; + private Hashtable cacheStringTable = null; + private boolean processNsp; + + private int depth; + private String[] elementStack = new String[16]; + private String[] nspStack = new String[8]; + private int[] nspCounts = new int[4]; + + private int attributeCount; + private String[] attributes = new String[16]; + private int nextId = -2; + + private Vector tables = new Vector(); + + int version; + int publicIdentifierId; + int charSet; + + // StartTag current; + // ParseEvent next; + + private String prefix; + private String namespace; + private String name; + private String text; + // private String encoding; + private Object wapExtensionData; + private int wapExtensionCode; + + private int type; + private int codePage; + + private boolean degenerated; + private boolean isWhitespace; + private String encoding = null; + + public boolean getFeature(String feature) { + if (XmlPullParser + .FEATURE_PROCESS_NAMESPACES + .equals(feature)) + return processNsp; + else + return false; + } + + public String getInputEncoding() { + // should return someting depending on charSet here!!!!! + return encoding; + } + + public void defineEntityReplacementText( + String entity, + String value) + throws XmlPullParserException { + + // just ignore, has no effect + } + + public Object getProperty(String property) { + return null; + } + + public int getNamespaceCount(int depth) { + if (depth > this.depth) + throw new IndexOutOfBoundsException(); + return nspCounts[depth]; + } + + public String getNamespacePrefix(int pos) { + return nspStack[pos << 1]; + } + + public String getNamespaceUri(int pos) { + return nspStack[(pos << 1) + 1]; + } + + public String getNamespace(String prefix) { + + if ("xml".equals(prefix)) + return "http://www.w3.org/XML/1998/namespace"; + if ("xmlns".equals(prefix)) + return "http://www.w3.org/2000/xmlns/"; + + for (int i = (getNamespaceCount(depth) << 1) - 2; + i >= 0; + i -= 2) { + if (prefix == null) { + if (nspStack[i] == null) + return nspStack[i + 1]; + } + else if (prefix.equals(nspStack[i])) + return nspStack[i + 1]; + } + return null; + } + + public int getDepth() { + return depth; + } + + public String getPositionDescription() { + + StringBuffer buf = + new StringBuffer( + type < TYPES.length ? TYPES[type] : "unknown"); + buf.append(' '); + + if (type == START_TAG || type == END_TAG) { + if (degenerated) + buf.append("(empty) "); + buf.append('<'); + if (type == END_TAG) + buf.append('/'); + + if (prefix != null) + buf.append("{" + namespace + "}" + prefix + ":"); + buf.append(name); + + int cnt = attributeCount << 2; + for (int i = 0; i < cnt; i += 4) { + buf.append(' '); + if (attributes[i + 1] != null) + buf.append( + "{" + + attributes[i] + + "}" + + attributes[i + + 1] + + ":"); + buf.append( + attributes[i + + 2] + + "='" + + attributes[i + + 3] + + "'"); + } + + buf.append('>'); + } + else if (type == IGNORABLE_WHITESPACE); + else if (type != TEXT) + buf.append(getText()); + else if (isWhitespace) + buf.append("(whitespace)"); + else { + String text = getText(); + if (text.length() > 16) + text = text.substring(0, 16) + "..."; + buf.append(text); + } + + return buf.toString(); + } + + public int getLineNumber() { + return -1; + } + + public int getColumnNumber() { + return -1; + } + + public boolean isWhitespace() + throws XmlPullParserException { + if (type != TEXT + && type != IGNORABLE_WHITESPACE + && type != CDSECT) + exception(ILLEGAL_TYPE); + return isWhitespace; + } + + public String getText() { + return text; + } + + public char[] getTextCharacters(int[] poslen) { + if (type >= TEXT) { + poslen[0] = 0; + poslen[1] = text.length(); + char[] buf = new char[text.length()]; + text.getChars(0, text.length(), buf, 0); + return buf; + } + + poslen[0] = -1; + poslen[1] = -1; + return null; + } + + public String getNamespace() { + return namespace; + } + + public String getName() { + return name; + } + + public String getPrefix() { + return prefix; + } + + public boolean isEmptyElementTag() + throws XmlPullParserException { + if (type != START_TAG) + exception(ILLEGAL_TYPE); + return degenerated; + } + + public int getAttributeCount() { + return attributeCount; + } + + public String getAttributeType(int index) { + return "CDATA"; + } + + public boolean isAttributeDefault(int index) { + return false; + } + + public String getAttributeNamespace(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[index << 2]; + } + + public String getAttributeName(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[(index << 2) + 2]; + } + + public String getAttributePrefix(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[(index << 2) + 1]; + } + + public String getAttributeValue(int index) { + if (index >= attributeCount) + throw new IndexOutOfBoundsException(); + return attributes[(index << 2) + 3]; + } + + public String getAttributeValue( + String namespace, + String name) { + + for (int i = (attributeCount << 2) - 4; + i >= 0; + i -= 4) { + if (attributes[i + 2].equals(name) + && (namespace == null + || attributes[i].equals(namespace))) + return attributes[i + 3]; + } + + return null; + } + + public int getEventType() throws XmlPullParserException { + return type; + } + + public int next() throws XmlPullParserException, IOException { + + isWhitespace = true; + int minType = 9999; + + while (true) { + + String save = text; + + nextImpl(); + + if (type < minType) + minType = type; + + if (minType > CDSECT) continue; // no "real" event so far + + if (minType >= TEXT) { // text, see if accumulate + + if (save != null) text = text == null ? save : save + text; + + switch(peekId()) { + case Wbxml.ENTITY: + case Wbxml.STR_I: + case Wbxml.LITERAL: + case Wbxml.LITERAL_C: + case Wbxml.LITERAL_A: + case Wbxml.LITERAL_AC: continue; + } + } + + break; + } + + type = minType; + + if (type > TEXT) + type = TEXT; + + return type; + } + + + public int nextToken() throws XmlPullParserException, IOException { + + isWhitespace = true; + nextImpl(); + return type; + } + + + + public int nextTag() throws XmlPullParserException, IOException { + + next(); + if (type == TEXT && isWhitespace) + next(); + + if (type != END_TAG && type != START_TAG) + exception("unexpected type"); + + return type; + } + + + public String nextText() throws XmlPullParserException, IOException { + if (type != START_TAG) + exception("precondition: START_TAG"); + + next(); + + String result; + + if (type == TEXT) { + result = getText(); + next(); + } + else + result = ""; + + if (type != END_TAG) + exception("END_TAG expected"); + + return result; + } + + + public void require(int type, String namespace, String name) + throws XmlPullParserException, IOException { + + if (type != this.type + || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) + exception( + "expected: " + TYPES[type] + " {" + namespace + "}" + name); + } + + + public void setInput(Reader reader) throws XmlPullParserException { + exception("InputStream required"); + } + + public void setInput(InputStream in, String enc) + throws XmlPullParserException { + + this.in = in; + + try { + version = readByte(); + publicIdentifierId = readInt(); + + if (publicIdentifierId == 0) + readInt(); + + int charset = readInt(); // skip charset + + if (null == enc){ + switch (charset){ + case 4: encoding = "ISO-8859-1"; break; + case 106: encoding = "UTF-8"; break; + // add more if you need them + // http://www.iana.org/assignments/character-sets + // case MIBenum: encoding = Name break; + default: throw new UnsupportedEncodingException(""+charset); + } + }else{ + encoding = enc; + } + + int strTabSize = readInt(); + stringTable = new byte[strTabSize]; + + int ok = 0; + while(ok < strTabSize){ + int cnt = in.read(stringTable, ok, strTabSize - ok); + if(cnt <= 0) break; + ok += cnt; + } + + selectPage(0, true); + selectPage(0, false); + } + catch (IOException e) { + exception("Illegal input format"); + } + } + + public void setFeature(String feature, boolean value) + throws XmlPullParserException { + if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) + processNsp = value; + else + exception("unsupported feature: " + feature); + } + + public void setProperty(String property, Object value) + throws XmlPullParserException { + throw new XmlPullParserException("unsupported property: " + property); + } + + // ---------------------- private / internal methods + + private final boolean adjustNsp() + throws XmlPullParserException { + + boolean any = false; + + for (int i = 0; i < attributeCount << 2; i += 4) { + // * 4 - 4; i >= 0; i -= 4) { + + String attrName = attributes[i + 2]; + int cut = attrName.indexOf(':'); + String prefix; + + if (cut != -1) { + prefix = attrName.substring(0, cut); + attrName = attrName.substring(cut + 1); + } + else if (attrName.equals("xmlns")) { + prefix = attrName; + attrName = null; + } + else + continue; + + if (!prefix.equals("xmlns")) { + any = true; + } + else { + int j = (nspCounts[depth]++) << 1; + + nspStack = ensureCapacity(nspStack, j + 2); + nspStack[j] = attrName; + nspStack[j + 1] = attributes[i + 3]; + + if (attrName != null + && attributes[i + 3].equals("")) + exception("illegal empty namespace"); + + // prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ()); + + //System.out.println (prefixMap); + + System.arraycopy( + attributes, + i + 4, + attributes, + i, + ((--attributeCount) << 2) - i); + + i -= 4; + } + } + + if (any) { + for (int i = (attributeCount << 2) - 4; + i >= 0; + i -= 4) { + + String attrName = attributes[i + 2]; + int cut = attrName.indexOf(':'); + + if (cut == 0) + throw new RuntimeException( + "illegal attribute name: " + + attrName + + " at " + + this); + + else if (cut != -1) { + String attrPrefix = + attrName.substring(0, cut); + + attrName = attrName.substring(cut + 1); + + String attrNs = getNamespace(attrPrefix); + + if (attrNs == null) + throw new RuntimeException( + "Undefined Prefix: " + + attrPrefix + + " in " + + this); + + attributes[i] = attrNs; + attributes[i + 1] = attrPrefix; + attributes[i + 2] = attrName; + + for (int j = (attributeCount << 2) - 4; + j > i; + j -= 4) + if (attrName.equals(attributes[j + 2]) + && attrNs.equals(attributes[j])) + exception( + "Duplicate Attribute: {" + + attrNs + + "}" + + attrName); + } + } + } + + int cut = name.indexOf(':'); + + if (cut == 0) + exception("illegal tag name: " + name); + else if (cut != -1) { + prefix = name.substring(0, cut); + name = name.substring(cut + 1); + } + + this.namespace = getNamespace(prefix); + + if (this.namespace == null) { + if (prefix != null) + exception("undefined prefix: " + prefix); + this.namespace = NO_NAMESPACE; + } + + return any; + } + + private final void setTable(int page, int type, String[] table) { + if(stringTable != null){ + throw new RuntimeException("setXxxTable must be called before setInput!"); + } + while(tables.size() < 3*page +3){ + tables.addElement(null); + } + tables.setElementAt(table, page*3+type); + } + + + + + + private final void exception(String desc) + throws XmlPullParserException { + throw new XmlPullParserException(desc, this, null); + } + + + private void selectPage(int nr, boolean tags) throws XmlPullParserException{ + if(tables.size() == 0 && nr == 0) return; + + if(nr*3 > tables.size()) + exception("Code Page "+nr+" undefined!"); + + if(tags) + tagTable = (String[]) tables.elementAt(nr * 3 + TAG_TABLE); + else { + attrStartTable = (String[]) tables.elementAt(nr * 3 + ATTR_START_TABLE); + attrValueTable = (String[]) tables.elementAt(nr * 3 + ATTR_VALUE_TABLE); + } + } + + private final void nextImpl() + throws IOException, XmlPullParserException { + + String s; + + if (type == END_TAG) { + depth--; + } + + if (degenerated) { + type = XmlPullParser.END_TAG; + degenerated = false; + return; + } + + text = null; + prefix = null; + name = null; + + int id = peekId (); + while(id == Wbxml.SWITCH_PAGE){ + nextId = -2; + selectPage(readByte(), true); + id = peekId(); + } + nextId = -2; + + switch (id) { + case -1 : + type = XmlPullParser.END_DOCUMENT; + break; + + case Wbxml.END : + { + int sp = (depth - 1) << 2; + + type = END_TAG; + namespace = elementStack[sp]; + prefix = elementStack[sp + 1]; + name = elementStack[sp + 2]; + } + break; + + case Wbxml.ENTITY : + { + type = ENTITY_REF; + char c = (char) readInt(); + text = "" + c; + name = "#" + ((int) c); + } + + break; + + case Wbxml.STR_I : + type = TEXT; + text = readStrI(); + break; + + case Wbxml.EXT_I_0 : + case Wbxml.EXT_I_1 : + case Wbxml.EXT_I_2 : + case Wbxml.EXT_T_0 : + case Wbxml.EXT_T_1 : + case Wbxml.EXT_T_2 : + case Wbxml.EXT_0 : + case Wbxml.EXT_1 : + case Wbxml.EXT_2 : + case Wbxml.OPAQUE : + parseWapExtension(id); + break; + + case Wbxml.PI : + throw new RuntimeException("PI curr. not supp."); + // readPI; + // break; + + case Wbxml.STR_T : + { + type = TEXT; + text = readStrT(); + } + break; + + default : + parseElement(id); + } + // } + // while (next == null); + + // return next; + } + + + + public void parseWapExtension(int id) + throws IOException, XmlPullParserException { + + type = WAP_EXTENSION; + wapExtensionCode = id; + + switch (id) { + case Wbxml.EXT_I_0 : + case Wbxml.EXT_I_1 : + case Wbxml.EXT_I_2 : + wapExtensionData = readStrI(); + break; + + case Wbxml.EXT_T_0 : + case Wbxml.EXT_T_1 : + case Wbxml.EXT_T_2 : + wapExtensionData = new Integer(readInt()); + break; + + case Wbxml.EXT_0 : + case Wbxml.EXT_1 : + case Wbxml.EXT_2 : + break; + + case Wbxml.OPAQUE : + { + int len = readInt(); + byte[] buf = new byte[len]; + + for (int i = 0; + i < len; + i++) // enhance with blockread! + buf[i] = (byte) readByte(); + + wapExtensionData = buf; + } // case OPAQUE + break; + + default: + exception("illegal id: "+id); + } // SWITCH + } + + public void readAttr() throws IOException, XmlPullParserException { + + int id = readByte(); + int i = 0; + + while (id != 1) { + + while(id == Wbxml.SWITCH_PAGE){ + selectPage(readByte(), false); + id = readByte(); + } + + String name = resolveId(attrStartTable, id); + StringBuffer value; + + int cut = name.indexOf('='); + + if (cut == -1) + value = new StringBuffer(); + else { + value = + new StringBuffer(name.substring(cut + 1)); + name = name.substring(0, cut); + } + + id = readByte(); + while (id > 128 + || id == Wbxml.SWITCH_PAGE + || id == Wbxml.ENTITY + || id == Wbxml.STR_I + || id == Wbxml.STR_T + || (id >= Wbxml.EXT_I_0 && id <= Wbxml.EXT_I_2) + || (id >= Wbxml.EXT_T_0 && id <= Wbxml.EXT_T_2)) { + + switch (id) { + case Wbxml.SWITCH_PAGE : + selectPage(readByte(), false); + break; + + case Wbxml.ENTITY : + value.append((char) readInt()); + break; + + case Wbxml.STR_I : + value.append(readStrI()); + break; + + case Wbxml.EXT_I_0 : + case Wbxml.EXT_I_1 : + case Wbxml.EXT_I_2 : + case Wbxml.EXT_T_0 : + case Wbxml.EXT_T_1 : + case Wbxml.EXT_T_2 : + case Wbxml.EXT_0 : + case Wbxml.EXT_1 : + case Wbxml.EXT_2 : + case Wbxml.OPAQUE : + + throw new RuntimeException("wap extension in attr not supported yet"); + + /* + ParseEvent e = parseWapExtension(id); + if (!(e.getType() != Xml.TEXT + && e.getType() != Xml.WHITESPACE)) + throw new RuntimeException("parse WapExtension must return Text Event in order to work inside Attributes!"); + + value.append(e.getText()); + + //value.append (handleExtension (id)); // skip EXT in ATTR + //break; + */ + + case Wbxml.STR_T : + value.append(readStrT()); + break; + + default : + value.append( + resolveId(attrValueTable, id)); + } + + id = readByte(); + } + + attributes = ensureCapacity(attributes, i + 4); + + attributes[i++] = ""; + attributes[i++] = null; + attributes[i++] = name; + attributes[i++] = value.toString(); + + attributeCount++; + } + } + + private int peekId () throws IOException { + if (nextId == -2) { + nextId = in.read (); + } + return nextId; + } + + + + + String resolveId(String[] tab, int id) throws IOException { + int idx = (id & 0x07f) - 5; + if (idx == -1) + return readStrT(); + if (idx < 0 + || tab == null + || idx >= tab.length + || tab[idx] == null) + throw new IOException("id " + id + " undef."); + + return tab[idx]; + } + + void parseElement(int id) + throws IOException, XmlPullParserException { + + type = START_TAG; + name = resolveId(tagTable, id & 0x03f); + + attributeCount = 0; + if ((id & 128) != 0) { + readAttr(); + } + + degenerated = (id & 64) == 0; + + int sp = depth++ << 2; + + // transfer to element stack + + elementStack = ensureCapacity(elementStack, sp + 4); + elementStack[sp + 3] = name; + + if (depth >= nspCounts.length) { + int[] bigger = new int[depth + 4]; + System.arraycopy(nspCounts, 0, bigger, 0, nspCounts.length); + nspCounts = bigger; + } + + nspCounts[depth] = nspCounts[depth - 1]; + + for (int i = attributeCount - 1; i > 0; i--) { + for (int j = 0; j < i; j++) { + if (getAttributeName(i) + .equals(getAttributeName(j))) + exception( + "Duplicate Attribute: " + + getAttributeName(i)); + } + } + + if (processNsp) + adjustNsp(); + else + namespace = ""; + + elementStack[sp] = namespace; + elementStack[sp + 1] = prefix; + elementStack[sp + 2] = name; + + } + + private final String[] ensureCapacity( + String[] arr, + int required) { + if (arr.length >= required) + return arr; + String[] bigger = new String[required + 16]; + System.arraycopy(arr, 0, bigger, 0, arr.length); + return bigger; + } + + int readByte() throws IOException { + int i = in.read(); + if (i == -1) + throw new IOException("Unexpected EOF"); + return i; + } + + int readInt() throws IOException { + int result = 0; + int i; + + do { + i = readByte(); + result = (result << 7) | (i & 0x7f); + } + while ((i & 0x80) != 0); + + return result; + } + + String readStrI() throws IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + boolean wsp = true; + while (true){ + int i = in.read(); + if (i == 0){ + break; + } + if (i == -1){ + throw new IOException(UNEXPECTED_EOF); + } + if (i > 32){ + wsp = false; + } + buf.write(i); + } + isWhitespace = wsp; + String result = new String(buf.toByteArray(), encoding); + buf.close(); + return result; + } + + String readStrT() throws IOException { + int pos = readInt(); + // As the main reason of stringTable is compression we build a cache of Strings + // stringTable is supposed to help create Strings from parts which means some cache hit rate + // This will help to minimize the Strings created when invoking readStrT() repeatedly + if (cacheStringTable == null){ + //Lazy init if device is not using StringTable but inline 0x03 strings + cacheStringTable = new Hashtable(); + } + String forReturn = (String) cacheStringTable.get(new Integer(pos)); + if (forReturn == null){ + + int end = pos; + while(end < stringTable.length && stringTable[end] != '\0'){ + end++; + } + forReturn = new String(stringTable, pos, end-pos, encoding); + cacheStringTable.put(new Integer(pos), forReturn); + } + return forReturn; + } + + /** + * Sets the tag table for a given page. + * The first string in the array defines tag 5, the second tag 6 etc. + */ + + public void setTagTable(int page, String[] table) { + setTable(page, TAG_TABLE, table); + + // this.tagTable = tagTable; + // if (page != 0) + // throw new RuntimeException("code pages curr. not supp."); + } + + /** Sets the attribute start Table for a given page. + * The first string in the array defines attribute + * 5, the second attribute 6 etc. Please use the + * character '=' (without quote!) as delimiter + * between the attribute name and the (start of the) value + */ + + public void setAttrStartTable( + int page, + String[] table) { + + setTable(page, ATTR_START_TABLE, table); + } + + /** Sets the attribute value Table for a given page. + * The first string in the array defines attribute value 0x85, + * the second attribute value 0x86 etc. + */ + + public void setAttrValueTable( + int page, + String[] table) { + + setTable(page, ATTR_VALUE_TABLE, table); + } + +} diff --git a/xml/src/main/java/org/kxml2/wap/WbxmlSerializer.java b/xml/src/main/java/org/kxml2/wap/WbxmlSerializer.java new file mode 100644 index 0000000..e4447b0 --- /dev/null +++ b/xml/src/main/java/org/kxml2/wap/WbxmlSerializer.java @@ -0,0 +1,418 @@ +/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ + +//Contributors: Bogdan Onoiu (Genderal character encoding abstraction and UTF-8 Support) + +package org.kxml2.wap; + +import java.io.*; +import java.util.*; + +import org.xmlpull.v1.*; + +// TODO: make some of the "direct" WBXML token writing methods public?? + +/** + * A class for writing WBXML. + * + */ + + + +public class WbxmlSerializer implements XmlSerializer { + + Hashtable stringTable = new Hashtable(); + + OutputStream out; + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + ByteArrayOutputStream stringTableBuf = new ByteArrayOutputStream(); + + String pending; + int depth; + String name; + String namespace; + Vector attributes = new Vector(); + + Hashtable attrStartTable = new Hashtable(); + Hashtable attrValueTable = new Hashtable(); + Hashtable tagTable = new Hashtable(); + + private int attrPage; + private int tagPage; + + private String encoding = null; + + + public XmlSerializer attribute(String namespace, String name, String value) { + attributes.addElement(name); + attributes.addElement(value); + return this; + } + + + public void cdsect (String cdsect) throws IOException{ + text (cdsect); + } + + + + /* silently ignore comment */ + + public void comment (String comment) { + } + + + public void docdecl (String docdecl) { + throw new RuntimeException ("Cannot write docdecl for WBXML"); + } + + + public void entityRef (String er) { + throw new RuntimeException ("EntityReference not supported for WBXML"); + } + + public int getDepth() { + return depth; + } + + + public boolean getFeature (String name) { + return false; + } + + public String getNamespace() { + throw new RuntimeException("NYI"); + } + + public String getName() { + throw new RuntimeException("NYI"); + } + + public String getPrefix(String nsp, boolean create) { + throw new RuntimeException ("NYI"); + } + + + public Object getProperty (String name) { + return null; + } + + public void ignorableWhitespace (String sp) { + } + + + public void endDocument() throws IOException { + writeInt(out, stringTableBuf.size()); + + // write StringTable + + out.write(stringTableBuf.toByteArray()); + + // write buf + + out.write(buf.toByteArray()); + + // ready! + + out.flush(); + } + + + /** ATTENTION: flush cannot work since Wbxml documents require + need buffering. Thus, this call does nothing. */ + + public void flush() { + } + + + public void checkPending(boolean degenerated) throws IOException { + if (pending == null) + return; + + int len = attributes.size(); + + int[] idx = (int[]) tagTable.get(pending); + + // if no entry in known table, then add as literal + if (idx == null) { + buf.write( + len == 0 + ? (degenerated ? Wbxml.LITERAL : Wbxml.LITERAL_C) + : (degenerated ? Wbxml.LITERAL_A : Wbxml.LITERAL_AC)); + + writeStrT(pending); + } + else { + if(idx[0] != tagPage){ + tagPage=idx[0]; + buf.write(0); + buf.write(tagPage); + } + + buf.write( + len == 0 + ? (degenerated ? idx[1] : idx[1] | 64) + : (degenerated + ? idx[1] | 128 + : idx[1] | 192)); + + } + + for (int i = 0; i < len;) { + idx = (int[]) attrStartTable.get(attributes.elementAt(i)); + + if (idx == null) { + buf.write(Wbxml.LITERAL); + writeStrT((String) attributes.elementAt(i)); + } + else { + if(idx[0] != attrPage){ + attrPage = idx[1]; + buf.write(0); + buf.write(attrPage); + } + buf.write(idx[1]); + } + idx = (int[]) attrValueTable.get(attributes.elementAt(++i)); + if (idx == null) { + buf.write(Wbxml.STR_I); + writeStrI(buf, (String) attributes.elementAt(i)); + } + else { + if(idx[0] != attrPage){ + attrPage = idx[1]; + buf.write(0); + buf.write(attrPage); + } + buf.write(idx[1]); + } + ++i; + } + + if (len > 0) + buf.write(Wbxml.END); + + pending = null; + attributes.removeAllElements(); + } + + + public void processingInstruction(String pi) { + throw new RuntimeException ("PI NYI"); + } + + + public void setFeature(String name, boolean value) { + throw new IllegalArgumentException ("unknown feature "+name); + } + + + + public void setOutput (Writer writer) { + throw new RuntimeException ("Wbxml requires an OutputStream!"); + } + + public void setOutput (OutputStream out, String encoding) throws IOException { + + if (encoding != null) throw new IllegalArgumentException ("encoding not yet supported for WBXML"); + + this.out = out; + + buf = new ByteArrayOutputStream(); + stringTableBuf = new ByteArrayOutputStream(); + + // ok, write header + } + + + public void setPrefix(String prefix, String nsp) { + throw new RuntimeException("NYI"); + } + + public void setProperty(String property, Object value) { + throw new IllegalArgumentException ("unknown property "+property); + } + + + public void startDocument(String s, Boolean b) throws IOException{ + out.write(0x03); // version 1.3 + // http://www.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.htm + out.write(0x01); // unknown or missing public identifier + + // default encoding is UTF-8 + String[] encodings = { "UTF-8", "ISO-8859-1" }; + if (s == null || s.toUpperCase().equals(encodings[0])){ + encoding = encodings[0]; + out.write(106); + }else if (true == s.toUpperCase().equals(encodings[1])){ + encoding = encodings[1]; + out.write(0x04); + }else{ + throw new UnsupportedEncodingException(s); + } + } + + + public XmlSerializer startTag(String namespace, String name) throws IOException { + + if (namespace != null && !"".equals(namespace)) + throw new RuntimeException ("NSP NYI"); + + //current = new State(current, prefixMap, name); + + checkPending(false); + pending = name; + depth++; + + return this; + } + + public XmlSerializer text(char[] chars, int start, int len) throws IOException { + + checkPending(false); + + buf.write(Wbxml.STR_I); + writeStrI(buf, new String(chars, start, len)); + + return this; + } + + public XmlSerializer text(String text) throws IOException { + + checkPending(false); + + buf.write(Wbxml.STR_I); + writeStrI(buf, text); + + return this; + } + + + + public XmlSerializer endTag(String namespace, String name) throws IOException { + + // current = current.prev; + + if (pending != null) + checkPending(true); + else + buf.write(Wbxml.END); + + depth--; + + return this; + } + + /** currently ignored! */ + + public void writeLegacy(int type, String data) { + } + + // ------------- internal methods -------------------------- + + static void writeInt(OutputStream out, int i) throws IOException { + byte[] buf = new byte[5]; + int idx = 0; + + do { + buf[idx++] = (byte) (i & 0x7f); + i = i >> 7; + } + while (i != 0); + + while (idx > 1) { + out.write(buf[--idx] | 0x80); + } + out.write(buf[0]); + } + + static void writeStrI(OutputStream out, String s) throws IOException { + for (int i = 0; i < s.length(); i++) { + out.write((byte) s.charAt(i)); + } + out.write(0); + } + + void writeStrT(String s) throws IOException { + + Integer idx = (Integer) stringTable.get(s); + + if (idx == null) { + idx = new Integer(stringTableBuf.size()); + stringTable.put(s, idx); + writeStrI(stringTableBuf, s); + stringTableBuf.flush(); + } + + writeInt(buf, idx.intValue()); + } + + /** + * Sets the tag table for a given page. + * The first string in the array defines tag 5, the second tag 6 etc. + */ + + public void setTagTable(int page, String[] tagTable) { + // clear entries in tagTable! + if (page != 0) + return; + + for (int i = 0; i < tagTable.length; i++) { + if (tagTable[i] != null) { + Object idx = new int[]{page, i+5}; + this.tagTable.put(tagTable[i], idx); + } + } + } + + /** + * Sets the attribute start Table for a given page. + * The first string in the array defines attribute + * 5, the second attribute 6 etc. + * Please use the + * character '=' (without quote!) as delimiter + * between the attribute name and the (start of the) value + */ + public void setAttrStartTable(int page, String[] attrStartTable) { + + for (int i = 0; i < attrStartTable.length; i++) { + if (attrStartTable[i] != null) { + Object idx = new int[] {page, i + 5}; + this.attrStartTable.put(attrStartTable[i], idx); + } + } + } + + /** + * Sets the attribute value Table for a given page. + * The first string in the array defines attribute value 0x85, + * the second attribute value 0x86 etc. + */ + public void setAttrValueTable(int page, String[] attrValueTable) { + // clear entries in this.table! + for (int i = 0; i < attrValueTable.length; i++) { + if (attrValueTable[i] != null) { + Object idx = new int[]{page, i + 0x085}; + this.attrValueTable.put(attrValueTable[i], idx); + } + } + } +} diff --git a/xml/src/main/java/org/kxml2/wap/syncml/SyncML.java b/xml/src/main/java/org/kxml2/wap/syncml/SyncML.java new file mode 100644 index 0000000..5ea8496 --- /dev/null +++ b/xml/src/main/java/org/kxml2/wap/syncml/SyncML.java @@ -0,0 +1,192 @@ +package org.kxml2.wap.syncml; + +import org.kxml2.wap.*; + +public abstract class SyncML { + + + // SyncML-Common (-//SYNCML//DTD SyncML 1.2//EN and -//SYNCML//DTD MetInf 1.2//EN) support + + public static WbxmlParser createParser() { + WbxmlParser p = new WbxmlParser(); + p.setTagTable(0, TAG_TABLE_0); + p.setTagTable(1, TAG_TABLE_1); + return p; + } + + public static WbxmlSerializer createSerializer() { + WbxmlSerializer s = new WbxmlSerializer(); + s.setTagTable(0, TAG_TABLE_0); + s.setTagTable(1, TAG_TABLE_1); + return s; + } + + + // SyncML-Common + DMDDF (-//OMA//DTD-DM-DDF 1.2//EN) support + + public static WbxmlParser createDMParser() { + WbxmlParser p = createParser(); + p.setTagTable(2, TAG_TABLE_2_DM); + return p; + } + + public static WbxmlSerializer createDMSerializer() { + WbxmlSerializer s = createSerializer(); + s.setTagTable(2, TAG_TABLE_2_DM); + return s; + } + + // Tables + + public static final String [] TAG_TABLE_0 = { + + // -//SYNCML//DTD SyncML 1.2//EN + + "Add", // 0x05 + "Alert", // 0x06 + "Archive", // 0x07 + "Atomic", // 0x08 + "Chal", // 0x09 + "Cmd", // 0x0a + "CmdID", // 0x0b + "CmdRef", // 0x0c + "Copy", // 0x0d + "Cred", // 0x0e + "Data", // 0x0f + "Delete", // 0x10 + "Exec", // 0x11 + "Final", // 0x12 + "Get", // 0x13 + "Item", // 0x14 + "Lang", // 0x15 + "LocName", // 0x16 + "LocURI", // 0x17 + "Map", // 0x18 + "MapItem", // 0x19 + "Meta", // 0x1a + "MsgID", // 0x1b + "MsgRef", // 0x1c + "NoResp", // 0x1d + "NoResults", // 0x1e + "Put", // 0x1f + "Replace", // 0x20 + "RespURI", // 0x21 + "Results", // 0x22 + "Search", // 0x23 + "Sequence", // 0x24 + "SessionID", // 0x25 + "SftDel", // 0x26 + "Source", // 0x27 + "SourceRef", // 0x28 + "Status", // 0x29 + "Sync", // 0x2a + "SyncBody", // 0x2b + "SyncHdr", // 0x2c + "SyncML", // 0x2d + "Target", // 0x2e + "TargetRef", // 0x2f + "Reserved for future use", // 0x30 + "VerDTD", // 0x31 + "VerProto", // 0x32 + "NumberOfChanged",// 0x33 + "MoreData", // 0x34 + "Field", // 0x35 + "Filter", // 0x36 + "Record", // 0x37 + "FilterType", // 0x38 + "SourceParent", // 0x39 + "TargetParent", // 0x3a + "Move", // 0x3b + "Correlator" // 0x3c + }; + + public static final String [] TAG_TABLE_1 = { + + // -//SYNCML//DTD MetInf 1.2//EN + + "Anchor", // 0x05 + "EMI", // 0x06 + "Format", // 0x07 + "FreeID", // 0x08 + "FreeMem", // 0x09 + "Last", // 0x0a + "Mark", // 0x0b + "MaxMsgSize", // 0x0c + "Mem", // 0x0d + "MetInf", // 0x0e + "Next", // 0x0f + "NextNonce", // 0x10 + "SharedMem", // 0x11 + "Size", // 0x12 + "Type", // 0x13 + "Version", // 0x14 + "MaxObjSize", // 0x15 + "FieldLevel" // 0x16 + + }; + + public static final String [] TAG_TABLE_2_DM = { + + // -//OMA//DTD-DM-DDF 1.2//EN + + "AccessType", // 0x05 + "ACL", // 0x06 + "Add", // 0x07 + "b64", // 0x08 + "bin", // 0x09 + "bool", // 0x0a + "chr", // 0x0b + "CaseSense", // 0x0c + "CIS", // 0x0d + "Copy", // 0x0e + "CS", // 0x0f + "date", // 0x10 + "DDFName", // 0x11 + "DefaultValue", // 0x12 + "Delete", // 0x13 + "Description", // 0x14 + "DDFFormat", // 0x15 + "DFProperties", // 0x16 + "DFTitle", // 0x17 + "DFType", // 0x18 + "Dynamic", // 0x19 + "Exec", // 0x1a + "float", // 0x1b + "Format", // 0x1c + "Get", // 0x1d + "int", // 0x1e + "Man", // 0x1f + "MgmtTree", // 0x20 + "MIME", // 0x21 + "Mod", // 0x22 + "Name", // 0x23 + "Node", // 0x24 + "node", // 0x25 + "NodeName", // 0x26 + "null", // 0x27 + "Occurence", // 0x28 + "One", // 0x29 + "OneOrMore", // 0x2a + "OneOrN", // 0x2b + "Path", // 0x2c + "Permanent", // 0x2d + "Replace", // 0x2e + "RTProperties", // 0x2f + "Scope", // 0x30 + "Size", // 0x31 + "time", // 0x32 + "Title", // 0x33 + "TStamp", // 0x34 + "Type", // 0x35 + "Value", // 0x36 + "VerDTD", // 0x37 + "VerNo", // 0x38 + "xml", // 0x39 + "ZeroOrMore", // 0x3a + "ZeroOrN", // 0x3b + "ZeroOrOne" // 0x3c + + }; + +} + diff --git a/xml/src/main/java/org/kxml2/wap/wml/Wml.java b/xml/src/main/java/org/kxml2/wap/wml/Wml.java new file mode 100644 index 0000000..7e925d8 --- /dev/null +++ b/xml/src/main/java/org/kxml2/wap/wml/Wml.java @@ -0,0 +1,233 @@ +package org.kxml2.wap.wml; + +import org.kxml2.wap.*; + + +/** This class contains the wml coding tables for elements + * and attributes needed by the WmlParser. + */ + + +public abstract class Wml { + + /** Creates a WbxmlParser with the WML code pages set */ + + public static WbxmlParser createParser() { + WbxmlParser p = new WbxmlParser(); + p.setTagTable(0, TAG_TABLE); + p.setAttrStartTable(0, ATTR_START_TABLE); + p.setAttrValueTable(0, ATTR_VALUE_TABLE); + return p; + } + + public static WbxmlSerializer createSerializer() { + WbxmlSerializer s = new WbxmlSerializer(); + s.setTagTable(0, TAG_TABLE); + s.setAttrStartTable(0, ATTR_START_TABLE); + s.setAttrValueTable(0, ATTR_VALUE_TABLE); + return s; + } + + + public static final String [] TAG_TABLE = { + + null, // 05 + null, // 06 + null, // 07 + null, // 08 + null, // 09 + null, // 0A + null, // 0B + null, // 0C + null, // 0D + null, // 0E + null, // 0F + + null, // 10 + null, // 11 + null, // 12 + null, // 13 + null, // 14 + null, // 15 + null, // 16 + null, // 17 + null, // 18 + null, // 19 + null, // 1A + null, // 1B + "a", // 1C + "td", // 1D + "tr", // 1E + "table", // 1F + + "p", // 20 + "postfield", // 21 + "anchor", // 22 + "access", // 23 + "b", // 24 + "big", // 25 + "br", // 26 + "card", // 27 + "do", // 28 + "em", // 29 + "fieldset", // 2A + "go", // 2B + "head", // 2C + "i", // 2D + "img", // 2E + "input", // 2F + + "meta", // 30 + "noop", // 31 + "prev", // 32 + "onevent", // 33 + "optgroup", // 34 + "option", // 35 + "refresh", // 36 + "select", // 37 + "small", // 38 + "strong", // 39 + null, // 3A + "template", // 3B + "timer", // 3C + "u", // 3D + "setvar", // 3E + "wml", // 3F + }; + + + public static final String [] ATTR_START_TABLE = { + "accept-charset", // 05 + "align=bottom", // 06 + "align=center", // 07 + "align=left", // 08 + "align=middle", // 09 + "align=right", // 0A + "align=top", // 0B + "alt", // 0C + "content", // 0D + null, // 0E + "domain", // 0F + + "emptyok=false", // 10 + "emptyok=true", // 11 + "format", // 12 + "height", // 13 + "hspace", // 14 + "ivalue", // 15 + "iname", // 16 + null, // 17 + "label", // 18 + "localsrc", // 19 + "maxlength", // 1A + "method=get", // 1B + "method=post", // 1C + "mode=nowrap", // 1D + "mode=wrap", // 1E + "multiple=false", // 1F + + "multiple=true", // 20 + "name", // 21 + "newcontext=false", // 22 + "newcontext=true", // 23 + "onpick", // 24 + "onenterbackward", // 25 + "onenterforward", // 26 + "ontimer", // 27 + "optimal=false", // 28 + "optimal=true", // 29 + "path", // 2A + null, // 2B + null, // 2C + null, // 2D + "scheme", // 2E + "sendreferer=false", // 2F + + "sendreferer=true", // 30 + "size", // 31 + "src", // 32 + "ordered=true", // 33 + "ordered=false", // 34 + "tabindex", // 35 + "title", // 36 + "type", // 37 + "type=accept", // 38 + "type=delete", // 39 + "type=help", // 3A + "type=password", // 3B + "type=onpick", // 3C + "type=onenterbackward", // 3D + "type=onenterforward", // 3E + "type=ontimer", // 3F + + null, // 40 + null, // 41 + null, // 42 + null, // 43 + null, // 44 + "type=options", // 45 + "type=prev", // 46 + "type=reset", // 47 + "type=text", // 48 + "type=vnd.", // 49 + "href", // 4A + "href=http://", // 4B + "href=https://", // 4C + "value", // 4D + "vspace", // 4E + "width", // 4F + + "xml:lang", // 50 + null, // 51 + "align", // 52 + "columns", // 53 + "class", // 54 + "id", // 55 + "forua=false", // 56 + "forua=true", // 57 + "src=http://", // 58 + "src=https://", // 59 + "http-equiv", // 5A + "http-equiv=Content-Type", // 5B + "content=application/vnd.wap.wmlc;charset=", // 5C + "http-equiv=Expires", // 5D + null, // 5E + null, // 5F + }; + + + public static final String [] ATTR_VALUE_TABLE = { + ".com/", // 85 + ".edu/", // 86 + ".net/", // 87 + ".org/", // 88 + "accept", // 89 + "bottom", // 8A + "clear", // 8B + "delete", // 8C + "help", // 8D + "http://", // 8E + "http://www.", // 8F + + "https://", // 90 + "https://www.", // 91 + null, // 92 + "middle", // 93 + "nowrap", // 94 + "onpick", // 95 + "onenterbackward", // 96 + "onenterforward", // 97 + "ontimer", // 98 + "options", // 99 + "password", // 9A + "reset", // 9B + null, // 9C + "text", // 9D + "top", // 9E + "unknown", // 9F + + "wrap", // A0 + "www.", // A1 + }; +} + diff --git a/xml/src/main/java/org/kxml2/wap/wv/WV.java b/xml/src/main/java/org/kxml2/wap/wv/WV.java new file mode 100644 index 0000000..e2afbfb --- /dev/null +++ b/xml/src/main/java/org/kxml2/wap/wv/WV.java @@ -0,0 +1,593 @@ +package org.kxml2.wap.wv; + +import java.io.IOException; + +import org.kxml2.wap.*; + +/* + + * WV.java + + * + + * Created on 25 September 2003, 10:40 + + */ + + + + + + /** + * Wireless Village CSP 1.1 ("OMA-WV-CSP-V1_1-20021001-A.pdf") + * Wireless Village CSP 1.2 ("OMA-IMPS-WV-CSP_WBXML-v1_2-20030221-C.PDF") + * There are some bugs in the 1.2 spec but this is Ok. 1.2 is candidate + * + + * @author Bogdan Onoiu + + */ + +public abstract class WV { + + + + + + public static WbxmlParser createParser () throws IOException { + + WbxmlParser parser = new WbxmlParser(); + + parser.setTagTable (0, WV.tagTablePage0); + parser.setTagTable (1, WV.tagTablePage1); + parser.setTagTable (2, WV.tagTablePage2); + parser.setTagTable (3, WV.tagTablePage3); + parser.setTagTable (4, WV.tagTablePage4); + parser.setTagTable (5, WV.tagTablePage5); + parser.setTagTable (6, WV.tagTablePage6); + parser.setTagTable (7, WV.tagTablePage7); + parser.setTagTable (8, WV.tagTablePage8); + parser.setTagTable (9, WV.tagTablePage9); + parser.setTagTable (10, WV.tagTablePageA); + + parser.setAttrStartTable (0, WV.attrStartTable); + + parser.setAttrValueTable (0, WV.attrValueTable); + + return parser; + } + + + + public static final String [] tagTablePage0 = { + /* Common ... continue on Page 0x09 */ + "Acceptance", //0x00, 0x05 + "AddList", //0x00, 0x06 + "AddNickList", //0x00, 0x07 + "SName", //0x00, 0x08 + "WV-CSP-Message", //0x00, 0x09 + "ClientID", //0x00, 0x0A + "Code", //0x00, 0x0B + "ContactList", //0x00, 0x0C + "ContentData", //0x00, 0x0D + "ContentEncoding",//0x00, 0x0E + "ContentSize", //0x00, 0x0F + "ContentType", //0x00, 0x10 + "DateTime", //0x00, 0x11 + "Description", //0x00, 0x12 + "DetailedResult", //0x00, 0x13 + "EntityList", //0x00, 0x14 + "Group", //0x00, 0x15 + "GroupID", //0x00, 0x16 + "GroupList", //0x00, 0x17 + "InUse", //0x00, 0x18 + "Logo", //0x00, 0x19 + "MessageCount", //0x00, 0x1A + "MessageID", //0x00, 0x1B + "MessageURI", //0x00, 0x1C + "MSISDN", //0x00, 0x1D + "Name", //0x00, 0x1E + "NickList", //0x00, 0x1F + "NickName", //0x00, 0x20 + "Poll", //0x00, 0x21 + "Presence", //0x00, 0x22 + "PresenceSubList",//0x00, 0x23 + "PresenceValue", //0x00, 0x24 + "Property", //0x00, 0x25 + "Qualifier", //0x00, 0x26 + "Recipient", //0x00, 0x27 + "RemoveList", //0x00, 0x28 + "RemoveNickList", //0x00, 0x29 + "Result", //0x00, 0x2A + "ScreenName", //0x00, 0x2B + "Sender", //0x00, 0x2C + "Session", //0x00, 0x2D + "SessionDescriptor",//0x00, 0x2E + "SessionID", //0x00, 0x2F + "SessionType", //0x00, 0x30 + "Status", //0x00, 0x31 + "Transaction", //0x00, 0x32 + "TransactionContent",//0x00, 0x33 + "TransactionDescriptor",//0x00, 0x34 + "TransactionID", //0x00, 0x35 + "TransactionMode",//0x00, 0x36 + "URL", //0x00, 0x37 + "URLList", //0x00, 0x38 + "User", //0x00, 0x39 + "UserID", //0x00, 0x3A + "UserList", //0x00, 0x3B + "Validity", //0x00, 0x3C + "Value", //0x00, 0x3D + }; + + public static final String [] tagTablePage1 = { + /* Access ... continue on Page 0x0A */ + "AllFunctions", // 0x01, 0x05 + "AllFunctionsRequest", // 0x01, 0x06 + "CancelInvite-Request", // 0x01, 0x07 + "CancelInviteUser-Request", // 0x01, 0x08 + "Capability", // 0x01, 0x09 + "CapabilityList", // 0x01, 0x0A + "CapabilityRequest", // 0x01, 0x0B + "ClientCapability-Request", // 0x01, 0x0C + "ClientCapability-Response",// 0x01, 0x0D + "DigestBytes", // 0x01, 0x0E + "DigestSchema", // 0x01, 0x0F + "Disconnect", // 0x01, 0x10 + "Functions", // 0x01, 0x11 + "GetSPInfo-Request", // 0x01, 0x12 + "GetSPInfo-Response", // 0x01, 0x13 + "InviteID", // 0x01, 0x14 + "InviteNote", // 0x01, 0x15 + "Invite-Request", // 0x01, 0x16 + "Invite-Response", // 0x01, 0x17 + "InviteType", // 0x01, 0x18 + "InviteUser-Request", // 0x01, 0x19 + "InviteUser-Response", // 0x01, 0x1A + "KeepAlive-Request", // 0x01, 0x1B + "KeepAliveTime", // 0x01, 0x1C + "Login-Request", // 0x01, 0x1D + "Login-Response", // 0x01, 0x1E + "Logout-Request", // 0x01, 0x1F + "Nonce", // 0x01, 0x20 + "Password", // 0x01, 0x21 + "Polling-Request", // 0x01, 0x22 + "ResponseNote", // 0x01, 0x23 + "SearchElement", // 0x01, 0x24 + "SearchFindings", // 0x01, 0x25 + "SearchID", // 0x01, 0x26 + "SearchIndex", // 0x01, 0x27 + "SearchLimit", // 0x01, 0x28 + "KeepAlive-Response", // 0x01, 0x29 + "SearchPairList", // 0x01, 0x2A + "Search-Request", // 0x01, 0x2B + "Search-Response", // 0x01, 0x2C + "SearchResult", // 0x01, 0x2D + "Service-Request", // 0x01, 0x2E + "Service-Response", // 0x01, 0x2F + "SessionCookie", // 0x01, 0x30 + "StopSearch-Request", // 0x01, 0x31 + "TimeToLive", // 0x01, 0x32 + "SearchString", // 0x01, 0x33 + "CompletionFlag", // 0x01, 0x34 + null, // 0x01, 0x35 + "ReceiveList", // 0x01, 0x36 /* WV 1.2 */ + "VerifyID-Request", // 0x01, 0x37 /* WV 1.2 */ + "Extended-Request", // 0x01, 0x38 /* WV 1.2 */ + "Extended-Response", // 0x01, 0x39 /* WV 1.2 */ + "AgreedCapabilityList", // 0x01, 0x3A /* WV 1.2 */ + "Extended-Data", // 0x01, 0x3B /* WV 1.2 */ + "OtherServer", // 0x01, 0x3C /* WV 1.2 */ + "PresenceAttributeNSName",//0x01, 0x3D /* WV 1.2 */ + "SessionNSName", // 0x01, 0x3E /* WV 1.2 */ + "TransactionNSName", // 0x01, 0x3F /* WV 1.2 */ + }; + + public static final String [] tagTablePage2 = { + /* Service ... continue on Page 0x08 */ + "ADDGM", // 0x02, 0x05 + "AttListFunc", // 0x02, 0x06 + "BLENT", // 0x02, 0x07 + "CAAUT", // 0x02, 0x08 + "CAINV", // 0x02, 0x09 + "CALI", // 0x02, 0x0A + "CCLI", // 0x02, 0x0B + "ContListFunc", // 0x02, 0x0C + "CREAG", // 0x02, 0x0D + "DALI", // 0x02, 0x0E + "DCLI", // 0x02, 0x0F + "DELGR", // 0x02, 0x10 + "FundamentalFeat",//0x02, 0x11 + "FWMSG", // 0x02, 0x12 + "GALS", // 0x02, 0x13 + "GCLI", // 0x02, 0x14 + "GETGM", // 0x02, 0x15 + "GETGP", // 0x02, 0x16 + "GETLM", // 0x02, 0x17 + "GETM", // 0x02, 0x18 + "GETPR", // 0x02, 0x19 + "GETSPI", // 0x02, 0x1A + "GETWL", // 0x02, 0x1B + "GLBLU", // 0x02, 0x1C + "GRCHN", // 0x02, 0x1D + "GroupAuthFunc",// 0x02, 0x1E + "GroupFeat", // 0x02, 0x1F + "GroupMgmtFunc",// 0x02, 0x20 + "GroupUseFunc", // 0x02, 0x21 + "IMAuthFunc", // 0x02, 0x22 + "IMFeat", // 0x02, 0x23 + "IMReceiveFunc",// 0x02, 0x24 + "IMSendFunc", // 0x02, 0x25 + "INVIT", // 0x02, 0x26 + "InviteFunc", // 0x02, 0x27 + "MBRAC", // 0x02, 0x28 + "MCLS", // 0x02, 0x29 + "MDELIV", // 0x02, 0x2A + "NEWM", // 0x02, 0x2B + "NOTIF", // 0x02, 0x2C + "PresenceAuthFunc",//0x02, 0x2D + "PresenceDeliverFunc",//0x02, 0x2E + "PresenceFeat", // 0x02, 0x2F + "REACT", // 0x02, 0x30 + "REJCM", // 0x02, 0x31 + "REJEC", // 0x02, 0x32 + "RMVGM", // 0x02, 0x33 + "SearchFunc", // 0x02, 0x34 + "ServiceFunc", // 0x02, 0x35 + "SETD", // 0x02, 0x36 + "SETGP", // 0x02, 0x37 + "SRCH", // 0x02, 0x38 + "STSRC", // 0x02, 0x39 + "SUBGCN", // 0x02, 0x3A + "UPDPR", // 0x02, 0x3B + "WVCSPFeat", // 0x02, 0x3C + "MF", // 0x02, 0x3D /* WV 1.2 */ + "MG", // 0x02, 0x3E /* WV 1.2 */ + "MM" // 0x02, 0x3F /* WV 1.2 */ + }; + + public static final String [] tagTablePage3 = { + /* Client Capability */ + "AcceptedCharset", // 0x03, 0x05 + "AcceptedContentLength", // 0x03, 0x06 + "AcceptedContentType", // 0x03, 0x07 + "AcceptedTransferEncoding", // 0x03, 0x08 + "AnyContent", // 0x03, 0x09 + "DefaultLanguage", // 0x03, 0x0A + "InitialDeliveryMethod", // 0x03, 0x0B + "MultiTrans", // 0x03, 0x0C + "ParserSize", // 0x03, 0x0D + "ServerPollMin", // 0x03, 0x0E + "SupportedBearer", // 0x03, 0x0F + "SupportedCIRMethod", // 0x03, 0x10 + "TCPAddress", // 0x03, 0x11 + "TCPPort", // 0x03, 0x12 + "UDPPort" // 0x03, 0x13 + }; + + public static final String [] tagTablePage4 = { + /* Presence Primitive */ + "CancelAuth-Request", // 0x04, 0x05 + "ContactListProperties", // 0x04, 0x06 + "CreateAttributeList-Request", // 0x04, 0x07 + "CreateList-Request", // 0x04, 0x08 + "DefaultAttributeList", // 0x04, 0x09 + "DefaultContactList", // 0x04, 0x0A + "DefaultList", // 0x04, 0x0B + "DeleteAttributeList-Request", // 0x04, 0x0C + "DeleteList-Request", // 0x04, 0x0D + "GetAttributeList-Request", // 0x04, 0x0E + "GetAttributeList-Response", // 0x04, 0x0F + "GetList-Request", // 0x04, 0x10 + "GetList-Response", // 0x04, 0x11 + "GetPresence-Request", // 0x04, 0x12 + "GetPresence-Response", // 0x04, 0x13 + "GetWatcherList-Request", // 0x04, 0x14 + "GetWatcherList-Response", // 0x04, 0x15 + "ListManage-Request", // 0x04, 0x16 + "ListManage-Response", // 0x04, 0x17 + "UnsubscribePresence-Request", // 0x04, 0x18 + "PresenceAuth-Request", // 0x04, 0x19 + "PresenceAuth-User", // 0x04, 0x1A + "PresenceNotification-Request", // 0x04, 0x1B + "UpdatePresence-Request", // 0x04, 0x1C + "SubscribePresence-Request", // 0x04, 0x1D + "Auto-Subscribe", // 0x04, 0x1E /* WV 1.2 */ + "GetReactiveAuthStatus-Request",// 0x04, 0x1F /* WV 1.2 */ + "GetReactiveAuthStatus-Response",// 0x04, 0x20 /* WV 1.2 */ + }; + + public static final String [] tagTablePage5 = { + /* Presence Attribute */ + "Accuracy", // 0x05, 0x05 + "Address", // 0x05, 0x06 + "AddrPref", // 0x05, 0x07 + "Alias", // 0x05, 0x08 + "Altitude", // 0x05, 0x09 + "Building", // 0x05, 0x0A + "Caddr", // 0x05, 0x0B + "City", // 0x05, 0x0C + "ClientInfo", // 0x05, 0x0D + "ClientProducer", // 0x05, 0x0E + "ClientType", // 0x05, 0x0F + "ClientVersion", // 0x05, 0x10 + "CommC", // 0x05, 0x11 + "CommCap", // 0x05, 0x12 + "ContactInfo", // 0x05, 0x13 + "ContainedvCard", // 0x05, 0x14 + "Country", // 0x05, 0x15 + "Crossing1", // 0x05, 0x16 + "Crossing2", // 0x05, 0x17 + "DevManufacturer", // 0x05, 0x18 + "DirectContent", // 0x05, 0x19 + "FreeTextLocation", // 0x05, 0x1A + "GeoLocation", // 0x05, 0x1B + "Language", // 0x05, 0x1C + "Latitude", // 0x05, 0x1D + "Longitude", // 0x05, 0x1E + "Model", // 0x05, 0x1F + "NamedArea", // 0x05, 0x20 + "OnlineStatus", // 0x05, 0x21 + "PLMN", // 0x05, 0x22 + "PrefC", // 0x05, 0x23 + "PreferredContacts",// 0x05, 0x24 + "PreferredLanguage",// 0x05, 0x25 + "PreferredContent", // 0x05, 0x26 + "PreferredvCard", // 0x05, 0x27 + "Registration", // 0x05, 0x28 + "StatusContent", // 0x05, 0x29 + "StatusMood", // 0x05, 0x2A + "StatusText", // 0x05, 0x2B + "Street", // 0x05, 0x2C + "TimeZone", // 0x05, 0x2D + "UserAvailability", // 0x05, 0x2E + "Cap", // 0x05, 0x2F + "Cname", // 0x05, 0x30 + "Contact", // 0x05, 0x31 + "Cpriority", // 0x05, 0x32 + "Cstatus", // 0x05, 0x33 + "Note", // 0x05, 0x34 /* WV 1.2 */ + "Zone", // 0x05, 0x35 + null, + "Inf_link", // 0x05, 0x37 /* WV 1.2 */ + "InfoLink", // 0x05, 0x38 /* WV 1.2 */ + "Link", // 0x05, 0x39 /* WV 1.2 */ + "Text", // 0x05, 0x3A /* WV 1.2 */ + }; + + public static final String [] tagTablePage6 = { + /* Messaging */ + "BlockList", // 0x06, 0x05 +// "BlockUser-Request", // 0x06, 0x06 //This is a bug in the spec + "BlockEntity-Request", // 0x06, 0x06 + "DeliveryMethod", // 0x06, 0x07 + "DeliveryReport", // 0x06, 0x08 + "DeliveryReport-Request", // 0x06, 0x09 + "ForwardMessage-Request", // 0x06, 0x0A + "GetBlockedList-Request", // 0x06, 0x0B + "GetBlockedList-Response", // 0x06, 0x0C + "GetMessageList-Request", // 0x06, 0x0D + "GetMessageList-Response", // 0x06, 0x0E + "GetMessage-Request", // 0x06, 0x0F + "GetMessage-Response", // 0x06, 0x10 + "GrantList", // 0x06, 0x11 + "MessageDelivered", // 0x06, 0x12 + "MessageInfo", // 0x06, 0x13 + "MessageNotification", // 0x06, 0x14 + "NewMessage", // 0x06, 0x15 + "RejectMessage-Request", // 0x06, 0x16 + "SendMessage-Request", // 0x06, 0x17 + "SendMessage-Response", // 0x06, 0x18 + "SetDeliveryMethod-Request",// 0x06, 0x19 + "DeliveryTime", // 0x06, 0x1A + }; + + public static final String [] tagTablePage7 = { + /* Group */ + "AddGroupMembers-Request", // 0x07, 0x05 + "Admin", // 0x07, 0x06 + "CreateGroup-Request", // 0x07, 0x07 + "DeleteGroup-Request", // 0x07, 0x08 + "GetGroupMembers-Request", // 0x07, 0x09 + "GetGroupMembers-Response", // 0x07, 0x0A + "GetGroupProps-Request", // 0x07, 0x0B + "GetGroupProps-Response", // 0x07, 0x0C + "GroupChangeNotice", // 0x07, 0x0D + "GroupProperties", // 0x07, 0x0E + "Joined", // 0x07, 0x0F + "JoinedRequest", // 0x07, 0x10 + "JoinGroup-Request", // 0x07, 0x11 + "JoinGroup-Response", // 0x07, 0x12 + "LeaveGroup-Request", // 0x07, 0x13 + "LeaveGroup-Response", // 0x07, 0x14 + "Left", // 0x07, 0x15 + "MemberAccess-Request", // 0x07, 0x16 + "Mod", // 0x07, 0x17 + "OwnProperties", // 0x07, 0x18 + "RejectList-Request", // 0x07, 0x19 + "RejectList-Response", // 0x07, 0x1A + "RemoveGroupMembers-Request",// 0x07, 0x1B + "SetGroupProps-Request", // 0x07, 0x1C + "SubscribeGroupNotice-Request", // 0x07, 0x1D + "SubscribeGroupNotice-Response",// 0x07, 0x1E + "Users", // 0x07, 0x1F + "WelcomeNote", // 0x07, 0x20 + "JoinGroup", // 0x07, 0x21 + "SubscribeNotification", // 0x07, 0x22 + "SubscribeType", // 0x07, 0x23 + "GetJoinedUsers-Request", // 0x07, 0x24 /* WV 1.2 */ + "GetJoinedUsers-Response", // 0x07, 0x25 /* WV 1.2 */ + "AdminMapList", // 0x07, 0x26 /* WV 1.2 */ + "AdminMapping", // 0x07, 0x27 /* WV 1.2 */ + "Mapping", // 0x07, 0x28 /* WV 1.2 */ + "ModMapping", // 0x07, 0x29 /* WV 1.2 */ + "UserMapList", // 0x07, 0x2A /* WV 1.2 */ + "UserMapping", // 0x07, 0x2B /* WV 1.2 */ + }; + + public static final String [] tagTablePage8 = { + /* Service ... continued */ + "MP", // 0x08, 0x05 /* WV 1.2 */ + "GETAUT", // 0x08, 0x06 /* WV 1.2 */ + "GETJU", // 0x08, 0x07 /* WV 1.2 */ + "VRID", // 0x08, 0x08 /* WV 1.2 */ + "VerifyIDFunc", // 0x08, 0x09 /* WV 1.2 */ + }; + + public static final String [] tagTablePage9 = { + /* Common ... continued */ + "CIR", // 0x09, 0x05 /* WV 1.2 */ + "Domain", // 0x09, 0x06 /* WV 1.2 */ + "ExtBlock", // 0x09, 0x07 /* WV 1.2 */ + "HistoryPeriod", // 0x09, 0x08 /* WV 1.2 */ + "IDList", // 0x09, 0x09 /* WV 1.2 */ + "MaxWatcherList", // 0x09, 0x0A /* WV 1.2 */ + "ReactiveAuthState", // 0x09, 0x0B /* WV 1.2 */ + "ReactiveAuthStatus", // 0x09, 0x0C /* WV 1.2 */ + "ReactiveAuthStatusList", // 0x09, 0x0D /* WV 1.2 */ + "Watcher", // 0x09, 0x0E /* WV 1.2 */ + "WatcherStatus" // 0x09, 0x0F /* WV 1.2 */ + }; + + public static final String [] tagTablePageA = { + /* Access ... continued */ + "WV-CSP-NSDiscovery-Request", //0x0A, 0x05 /* WV 1.2 */ + "WV-CSP-NSDiscovery-Response", //0x0A, 0x06 /* WV 1.2 */ + "VersionList" //0x0A, 0x07 /* WV 1.2 */ + }; + + public static final String [] attrStartTable = { + "xmlns=http://www.wireless-village.org/CSP",// 0x00, 0x05 + "xmlns=http://www.wireless-village.org/PA", // 0x00, 0x06 + "xmlns=http://www.wireless-village.org/TRC",// 0x00, 0x07 + "xmlns=http://www.openmobilealliance.org/DTD/WV-CSP", // 0x00, 0x08 + "xmlns=http://www.openmobilealliance.org/DTD/WV-PA", // 0x00, 0x09 + "xmlns=http://www.openmobilealliance.org/DTD/WV-TRC", // 0x00, 0x0A + }; + + public static final String [] attrValueTable = { + + "AccessType", // 0x00 /* Common value token */ + "ActiveUsers", // 0x01 /* Common value token */ + "Admin", // 0x02 /* Common value token */ + "application/", // 0x03 /* Common value token */ + "application/vnd.wap.mms-message", // 0x04 /* Common value token */ + "application/x-sms", // 0x05 /* Common value token */ + "AutoJoin", // 0x06 /* Common value token */ + "BASE64", // 0x07 /* Common value token */ + "Closed", // 0x08 /* Common value token */ + "Default", // 0x09 /* Common value token */ + "DisplayName", // 0x0a /* Common value token */ + "F", // 0x0b /* Common value token */ + "G", // 0x0c /* Common value token */ + "GR", // 0x0d /* Common value token */ + "http://", // 0x0e /* Common value token */ + "https://", // 0x0f /* Common value token */ + "image/", // 0x10 /* Common value token */ + "Inband", // 0x11 /* Common value token */ + "IM", // 0x12 /* Common value token */ + "MaxActiveUsers", // 0x13 /* Common value token */ + "Mod", // 0x14 /* Common value token */ + "Name", // 0x15 /* Common value token */ + "None", // 0x16 /* Common value token */ + "N", // 0x17 /* Common value token */ + "Open", // 0x18 /* Common value token */ + "Outband", // 0x19 /* Common value token */ + "PR", // 0x1a /* Common value token */ + "Private", // 0x1b /* Common value token */ + "PrivateMessaging", // 0x1c /* Common value token */ + "PrivilegeLevel", // 0x1d /* Common value token */ + "Public", // 0x1e /* Common value token */ + "P", // 0x1f /* Common value token */ + "Request", // 0x20 /* Common value token */ + "Response", // 0x21 /* Common value token */ + "Restricted", // 0x22 /* Common value token */ + "ScreenName", // 0x23 /* Common value token */ + "Searchable", // 0x24 /* Common value token */ + "S", // 0x25 /* Common value token */ + "SC", // 0x26 /* Common value token */ + "text/", // 0x27 /* Common value token */ + "text/plain", // 0x28 /* Common value token */ + "text/x-vCalendar", // 0x29 /* Common value token */ + "text/x-vCard", // 0x2a /* Common value token */ + "Topic", // 0x2b /* Common value token */ + "T", // 0x2c /* Common value token */ + "Type", // 0x2d /* Common value token */ + "U", // 0x2e /* Common value token */ + "US", // 0x2f /* Common value token */ + "www.wireless-village.org", // 0x30 /* Common value token */ + "AutoDelete", // 0x31 /* Common value token */ /* WV 1.2 */ + "GM", // 0x32 /* Common value token */ /* WV 1.2 */ + "Validity", // 0x33 /* Common value token */ /* WV 1.2 */ + "ShowID", // 0x34 /* Common value token */ /* WV 1.2 */ + "GRANTED", // 0x35 /* Common value token */ /* WV 1.2 */ + "PENDING", // 0x36 /* Common value token */ /* WV 1.2 */ + null, // 0x37 + null, // 0x38 + null, // 0x39 + null, // 0x3a + null, // 0x3b + null, // 0x3c + "GROUP_ID", // 0x3d /* Access value token */ + "GROUP_NAME", // 0x3e /* Access value token */ + "GROUP_TOPIC", // 0x3f /* Access value token */ + "GROUP_USER_ID_JOINED", // 0x40 /* Access value token */ + "GROUP_USER_ID_OWNER", // 0x41 /* Access value token */ + "HTTP", // 0x42 /* Access value token */ + "SMS", // 0x43 /* Access value token */ + "STCP", // 0x44 /* Access value token */ + "SUDP", // 0x45 /* Access value token */ + "USER_ALIAS", // 0x46 /* Access value token */ + "USER_EMAIL_ADDRESS", // 0x47 /* Access value token */ + "USER_FIRST_NAME", // 0x48 /* Access value token */ + "USER_ID", // 0x49 /* Access value token */ + "USER_LAST_NAME", // 0x4a /* Access value token */ + "USER_MOBILE_NUMBER", // 0x4b /* Access value token */ + "USER_ONLINE_STATUS", // 0x4c /* Access value token */ + "WAPSMS", // 0x4d /* Access value token */ + "WAPUDP", // 0x4e /* Access value token */ + "WSP", // 0x4f /* Access value token */ + "GROUP_USER_ID_AUTOJOIN", // 0x50 /* Access value token */ /* WV 1.2 */ + null, // 0x51 + null, // 0x52 + null, // 0x53 + null, // 0x54 + null, // 0x55 + null, // 0x56 + null, // 0x57 + null, // 0x58 + null, // 0x59 + null, // 0x5a + "ANGRY", // 0x5b /* Presence value token */ + "ANXIOUS", // 0x5c /* Presence value token */ + "ASHAMED", // 0x5d /* Presence value token */ + "AUDIO_CALL", // 0x5e /* Presence value token */ + "AVAILABLE", // 0x5f /* Presence value token */ + "BORED", // 0x60 /* Presence value token */ + "CALL", // 0x61 /* Presence value token */ + "CLI", // 0x62 /* Presence value token */ + "COMPUTER", // 0x63 /* Presence value token */ + "DISCREET", // 0x64 /* Presence value token */ + "EMAIL", // 0x65 /* Presence value token */ + "EXCITED", // 0x66 /* Presence value token */ + "HAPPY", // 0x67 /* Presence value token */ + "IM", // 0x68 /* Presence value token */ + "IM_OFFLINE", // 0x69 /* Presence value token */ + "IM_ONLINE", // 0x6a /* Presence value token */ + "IN_LOVE", // 0x6b /* Presence value token */ + "INVINCIBLE", // 0x6c /* Presence value token */ + "JEALOUS", // 0x6d /* Presence value token */ + "MMS", // 0x6e /* Presence value token */ + "MOBILE_PHONE", // 0x6f /* Presence value token */ + "NOT_AVAILABLE", // 0x70 /* Presence value token */ + "OTHER", // 0x71 /* Presence value token */ + "PDA", // 0x72 /* Presence value token */ + "SAD", // 0x73 /* Presence value token */ + "SLEEPY", // 0x74 /* Presence value token */ + "SMS", // 0x75 /* Presence value token */ + "VIDEO_CALL", // 0x76 /* Presence value token */ + "VIDEO_STREAM", // 0x77 /* Presence value token */ + }; + + +} diff --git a/xml/src/main/java/org/w3c/dom/Attr.java b/xml/src/main/java/org/w3c/dom/Attr.java new file mode 100644 index 0000000..3bfde52 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Attr.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>Attr</code> interface represents an attribute in an + * <code>Element</code> object. Typically the allowable values for the + * attribute are defined in a document type definition. + * <p><code>Attr</code> objects inherit the <code>Node</code> interface, but + * since they are not actually child nodes of the element they describe, the + * DOM does not consider them part of the document tree. Thus, the + * <code>Node</code> attributes <code>parentNode</code>, + * <code>previousSibling</code>, and <code>nextSibling</code> have a + * <code>null</code> value for <code>Attr</code> objects. The DOM takes the + * view that attributes are properties of elements rather than having a + * separate identity from the elements they are associated with; this should + * make it more efficient to implement such features as default attributes + * associated with all elements of a given type. Furthermore, + * <code>Attr</code> nodes may not be immediate children of a + * <code>DocumentFragment</code>. However, they can be associated with + * <code>Element</code> nodes contained within a + * <code>DocumentFragment</code>. In short, users and implementors of the + * DOM need to be aware that <code>Attr</code> nodes have some things in + * common with other objects inheriting the <code>Node</code> interface, but + * they also are quite distinct. + * <p> The attribute's effective value is determined as follows: if this + * attribute has been explicitly assigned any value, that value is the + * attribute's effective value; otherwise, if there is a declaration for + * this attribute, and that declaration includes a default value, then that + * default value is the attribute's effective value; otherwise, the + * attribute does not exist on this element in the structure model until it + * has been explicitly added. Note that the <code>nodeValue</code> attribute + * on the <code>Attr</code> instance can also be used to retrieve the string + * version of the attribute's value(s). + * <p>In XML, where the value of an attribute can contain entity references, + * the child nodes of the <code>Attr</code> node may be either + * <code>Text</code> or <code>EntityReference</code> nodes (when these are + * in use; see the description of <code>EntityReference</code> for + * discussion). Because the DOM Core is not aware of attribute types, it + * treats all attribute values as simple strings, even if the DTD or schema + * declares them as having tokenized types. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Attr extends Node { + /** + * Returns the name of this attribute. + * + * @return the name of the attribute. + */ + public String getName(); + + /** + * If this attribute was explicitly given a value in the original + * document, this is <code>true</code>; otherwise, it is + * <code>false</code>. Note that the implementation is in charge of this + * attribute, not the user. If the user changes the value of the + * attribute (even if it ends up having the same value as the default + * value) then the <code>specified</code> flag is automatically flipped + * to <code>true</code>. To re-specify the attribute as the default + * value from the DTD, the user must delete the attribute. The + * implementation will then make a new attribute available with + * <code>specified</code> set to <code>false</code> and the default + * value (if one exists). + * <br>In summary: If the attribute has an assigned value in the document + * then <code>specified</code> is <code>true</code>, and the value is + * the assigned value. If the attribute has no assigned value in the + * document and has a default value in the DTD, then + * <code>specified</code> is <code>false</code>, and the value is the + * default value in the DTD. If the attribute has no assigned value in + * the document and has a value of #IMPLIED in the DTD, then the + * attribute does not appear in the structure model of the document. If + * the <code>ownerElement</code> attribute is <code>null</code> (i.e. + * because it was just created or was set to <code>null</code> by the + * various removal and cloning operations) <code>specified</code> is + * <code>true</code>. + * + * @return <code>true</code> if the attribute was specified, + * <code>false</false> otherwise. + */ + public boolean getSpecified(); + + /** + * Returns the value of the attribute is returned as a string. + * Character and general entity references are replaced with their + * values. See also the method <code>getAttribute</code> on the + * <code>Element</code> interface. + * + * @return the attribute value as a string. + */ + public String getValue(); + + /** + * Sets the value of the attribute. This creates a <code>Text</code> node + * with the unparsed contents of the string. I.e. any characters that an + * XML processor would recognize as markup are instead treated as literal + * text. See also the method <code>setAttribute</code> on the + * <code>Element</code> + * interface. + * + * @param value the new attribute value. + * + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly. + */ + public void setValue(String value) + throws DOMException; + + /** + * The <code>Element</code> node this attribute is attached to or + * <code>null</code> if this attribute is not in use. + * + * @return the owner <code>Element</element>, or <code>null</code> is the + * attribute is unused. + * + * @since DOM Level 2 + */ + public Element getOwnerElement(); + +} diff --git a/xml/src/main/java/org/w3c/dom/CDATASection.java b/xml/src/main/java/org/w3c/dom/CDATASection.java new file mode 100644 index 0000000..9e74734 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/CDATASection.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +/** + * CDATA sections are used to escape blocks of text containing characters that + * would otherwise be regarded as markup. The only delimiter that is + * recognized in a CDATA section is the "]]>" string that ends the CDATA + * section. CDATA sections cannot be nested. Their primary purpose is for + * including material such as XML fragments, without needing to escape all + * the delimiters. + * <p>The <code>DOMString</code> attribute of the <code>Text</code> node holds + * the text that is contained by the CDATA section. Note that this may + * contain characters that need to be escaped outside of CDATA sections and + * that, depending on the character encoding ("charset") chosen for + * serialization, it may be impossible to write out some characters as part + * of a CDATA section. + * <p> The <code>CDATASection</code> interface inherits from the + * <code>CharacterData</code> interface through the <code>Text</code> + * interface. Adjacent <code>CDATASection</code> nodes are not merged by use + * of the <code>normalize</code> method of the <code>Node</code> interface. + * Because no markup is recognized within a <code>CDATASection</code>, + * character numeric references cannot be used as an escape mechanism when + * serializing. Therefore, action needs to be taken when serializing a + * <code>CDATASection</code> with a character encoding where some of the + * contained characters cannot be represented. Failure to do so would not + * produce well-formed XML.One potential solution in the serialization + * process is to end the CDATA section before the character, output the + * character using a character reference or entity reference, and open a new + * CDATA section for any further characters in the text node. Note, however, + * that some code conversion libraries at the time of writing do not return + * an error or exception when a character is missing from the encoding, + * making the task of ensuring that data is not corrupted on serialization + * more difficult. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface CDATASection extends Text { +} diff --git a/xml/src/main/java/org/w3c/dom/CharacterData.java b/xml/src/main/java/org/w3c/dom/CharacterData.java new file mode 100644 index 0000000..47386e9 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/CharacterData.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>CharacterData</code> interface extends Node with a set of + * attributes and methods for accessing character data in the DOM. For + * clarity this set is defined here rather than on each object that uses + * these attributes and methods. No DOM objects correspond directly to + * <code>CharacterData</code>, though <code>Text</code> and others do + * inherit the interface from it. All <code>offsets</code> in this interface + * start from <code>0</code>. + * <p>As explained in the <code>DOMString</code> interface, text strings in + * the DOM are represented in UTF-16, i.e. as a sequence of 16-bit units. In + * the following, the term 16-bit units is used whenever necessary to + * indicate that indexing on CharacterData is done in 16-bit units. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface CharacterData extends Node { + /** + * Returns the character data of the node that implements this interface. + * The DOM implementation may not put arbitrary limits on the amount of data + * that may be stored in a <code>CharacterData</code> node. However, + * implementation limits may mean that the entirety of a node's data may + * not fit into a single <code>DOMString</code>. In such cases, the user + * may call <code>substringData</code> to retrieve the data in + * appropriately sized pieces. + * + * @return the character data as a string. + * + * @exception DOMException + * DOMSTRING_SIZE_ERR: Raised when it would return more characters than + * fit in a <code>DOMString</code> variable on the implementation + * platform. + */ + public String getData() + throws DOMException; + /** + * Sets the character data of the node that implements this interface. The + * DOM implementation may not put arbitrary limits on the amount of data + * that may be stored in a <code>CharacterData</code> node. However, + * implementation limits may mean that the entirety of a node's data may + * not fit into a single <code>DOMString</code>. In such cases, the user + * may call <code>substringData</code> to retrieve the data in + * appropriately sized pieces. + * + * @param data the new character data. + * + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly. + */ + public void setData(String data) + throws DOMException; + + /** + * The number of 16-bit units that are available through <code>data</code> + * and the <code>substringData</code> method below. This may have the + * value zero, i.e., <code>CharacterData</code> nodes may be empty. + * + * @return the length in characters. + */ + public int getLength(); + + /** + * Extracts a range of data from the node. + * @param offset Start offset of substring to extract. + * @param count The number of 16-bit units to extract. + * @return The specified substring. If the sum of <code>offset</code> and + * <code>count</code> exceeds the <code>length</code>, then all 16-bit + * units to the end of the data are returned. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is + * negative or greater than the number of 16-bit units in + * <code>data</code>, or if the specified <code>count</code> is + * negative. + * <br>DOMSTRING_SIZE_ERR: Raised if the specified range of text does + * not fit into a <code>DOMString</code>. + */ + public String substringData(int offset, + int count) + throws DOMException; + + /** + * Append the string to the end of the character data of the node. Upon + * success, <code>data</code> provides access to the concatenation of + * <code>data</code> and the <code>DOMString</code> specified. + * @param arg The <code>DOMString</code> to append. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public void appendData(String arg) + throws DOMException; + + /** + * Insert a string at the specified 16-bit unit offset. + * @param offset The character offset at which to insert. + * @param arg The <code>DOMString</code> to insert. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is + * negative or greater than the number of 16-bit units in + * <code>data</code>. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public void insertData(int offset, + String arg) + throws DOMException; + + /** + * Remove a range of 16-bit units from the node. Upon success, + * <code>data</code> and <code>length</code> reflect the change. + * @param offset The offset from which to start removing. + * @param count The number of 16-bit units to delete. If the sum of + * <code>offset</code> and <code>count</code> exceeds + * <code>length</code> then all 16-bit units from <code>offset</code> + * to the end of the data are deleted. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is + * negative or greater than the number of 16-bit units in + * <code>data</code>, or if the specified <code>count</code> is + * negative. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public void deleteData(int offset, + int count) + throws DOMException; + + /** + * Replace the characters starting at the specified 16-bit unit offset + * with the specified string. + * @param offset The offset from which to start replacing. + * @param count The number of 16-bit units to replace. If the sum of + * <code>offset</code> and <code>count</code> exceeds + * <code>length</code>, then all 16-bit units to the end of the data + * are replaced; (i.e., the effect is the same as a <code>remove</code> + * method call with the same range, followed by an <code>append</code> + * method invocation). + * @param arg The <code>DOMString</code> with which the range must be + * replaced. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is + * negative or greater than the number of 16-bit units in + * <code>data</code>, or if the specified <code>count</code> is + * negative. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public void replaceData(int offset, + int count, + String arg) + throws DOMException; + +} diff --git a/xml/src/main/java/org/w3c/dom/Comment.java b/xml/src/main/java/org/w3c/dom/Comment.java new file mode 100644 index 0000000..1097921 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Comment.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +/** + * This interface inherits from <code>CharacterData</code> and represents the + * content of a comment, i.e., all the characters between the starting ' + * <code><!--</code>' and ending '<code>--></code>'. Note that this is + * the definition of a comment in XML, and, in practice, HTML, although some + * HTML tools may implement the full SGML comment structure. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Comment extends CharacterData { +} diff --git a/xml/src/main/java/org/w3c/dom/DOMException.java b/xml/src/main/java/org/w3c/dom/DOMException.java new file mode 100644 index 0000000..76bac3c --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/DOMException.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * DOM operations only raise exceptions in "exceptional" circumstances, i.e., + * when an operation is impossible to perform (either for logical reasons, + * because data is lost, or because the implementation has become unstable). + * In general, DOM methods return specific error values in ordinary + * processing situations, such as out-of-bound errors when using + * <code>NodeList</code>. + * <p>Implementations should raise other exceptions under other circumstances. + * For example, implementations should raise an implementation-dependent + * exception if a <code>null</code> argument is passed. + * <p>Some languages and object systems do not support the concept of + * exceptions. For such systems, error conditions may be indicated using + * native error reporting mechanisms. For some bindings, for example, + * methods may return error codes similar to those listed in the + * corresponding method descriptions. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public class DOMException extends RuntimeException { + /** + * Creates a new <code>DOMException</code> with the given error code and + * human-readable message. + * @param code the error code. + * @param message the human-readable message. + */ + public DOMException(short code, String message) { + super(message); + this.code = code; + } + /** + * Holds the error code of the <code>DOMException</code>. + */ + public short code; + // ExceptionCode + /** + * If index or size is negative, or greater than the allowed value + */ + public static final short INDEX_SIZE_ERR = 1; + /** + * If the specified range of text does not fit into a DOMString + */ + public static final short DOMSTRING_SIZE_ERR = 2; + /** + * If any node is inserted somewhere it doesn't belong + */ + public static final short HIERARCHY_REQUEST_ERR = 3; + /** + * If a node is used in a different document than the one that created it + * (that doesn't support it) + */ + public static final short WRONG_DOCUMENT_ERR = 4; + /** + * If an invalid or illegal character is specified, such as in a name. See + * production 2 in the XML specification for the definition of a legal + * character, and production 5 for the definition of a legal name + * character. + */ + public static final short INVALID_CHARACTER_ERR = 5; + /** + * If data is specified for a node which does not support data + */ + public static final short NO_DATA_ALLOWED_ERR = 6; + /** + * If an attempt is made to modify an object where modifications are not + * allowed + */ + public static final short NO_MODIFICATION_ALLOWED_ERR = 7; + /** + * If an attempt is made to reference a node in a context where it does + * not exist + */ + public static final short NOT_FOUND_ERR = 8; + /** + * If the implementation does not support the requested type of object or + * operation. + */ + public static final short NOT_SUPPORTED_ERR = 9; + /** + * If an attempt is made to add an attribute that is already in use + * elsewhere + */ + public static final short INUSE_ATTRIBUTE_ERR = 10; + /** + * If an attempt is made to use an object that is not, or is no longer, + * usable. + * @since DOM Level 2 + */ + public static final short INVALID_STATE_ERR = 11; + /** + * If an invalid or illegal string is specified. + * @since DOM Level 2 + */ + public static final short SYNTAX_ERR = 12; + /** + * If an attempt is made to modify the type of the underlying object. + * @since DOM Level 2 + */ + public static final short INVALID_MODIFICATION_ERR = 13; + /** + * If an attempt is made to create or change an object in a way which is + * incorrect with regard to namespaces. + * @since DOM Level 2 + */ + public static final short NAMESPACE_ERR = 14; + /** + * If a parameter or an operation is not supported by the underlying + * object. + * @since DOM Level 2 + */ + public static final short INVALID_ACCESS_ERR = 15; + +} diff --git a/xml/src/main/java/org/w3c/dom/DOMImplementation.java b/xml/src/main/java/org/w3c/dom/DOMImplementation.java new file mode 100644 index 0000000..dcb19c9 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/DOMImplementation.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +/** + * The <code>DOMImplementation</code> interface provides a number of methods + * for performing operations that are independent of any particular instance + * of the document object model. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface DOMImplementation { + /** + * Test if the DOM implementation implements a specific feature. + * @param feature The name of the feature to test (case-insensitive). The + * values used by DOM features are defined throughout the DOM Level 2 + * specifications and listed in the section. The name must be an XML + * name. To avoid possible conflicts, as a convention, names referring + * to features defined outside the DOM specification should be made + * unique by reversing the name of the Internet domain name of the + * person (or the organization that the person belongs to) who defines + * the feature, component by component, and using this as a prefix. + * For instance, the W3C SVG Working Group defines the feature + * "org.w3c.dom.svg". + * @param version This is the version number of the feature to test. In + * Level 2, the string can be either "2.0" or "1.0". If the version is + * not specified, supporting any version of the feature causes the + * method to return <code>true</code>. + * @return <code>true</code> if the feature is implemented in the + * specified version, <code>false</code> otherwise. + */ + public boolean hasFeature(String feature, + String version); + + /** + * Creates an empty <code>DocumentType</code> node. Entity declarations + * and notations are not made available. Entity reference expansions and + * default attribute additions do not occur. It is expected that a + * future version of the DOM will provide a way for populating a + * <code>DocumentType</code>. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param qualifiedName The qualified name of the document type to be + * created. + * @param publicId The external subset public identifier. + * @param systemId The external subset system identifier. + * @return A new <code>DocumentType</code> node with + * <code>Node.ownerDocument</code> set to <code>null</code>. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified name + * contains an illegal character. + * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is + * malformed. + * @since DOM Level 2 + */ + public DocumentType createDocumentType(String qualifiedName, + String publicId, + String systemId) + throws DOMException; + + /** + * Creates an XML <code>Document</code> object of the specified type with + * its document element. HTML-only DOM implementations do not need to + * implement this method. + * @param namespaceURI The namespace URI of the document element to create. + * @param qualifiedName The qualified name of the document element to be + * created. + * @param doctype The type of document to be created or <code>null</code>. + * When <code>doctype</code> is not <code>null</code>, its + * <code>Node.ownerDocument</code> attribute is set to the document + * being created. + * @return A new <code>Document</code> object. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified name + * contains an illegal character. + * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is + * malformed, if the <code>qualifiedName</code> has a prefix and the + * <code>namespaceURI</code> is <code>null</code>, or if the + * <code>qualifiedName</code> has a prefix that is "xml" and the + * <code>namespaceURI</code> is different from " + * http://www.w3.org/XML/1998/namespace" . + * <br>WRONG_DOCUMENT_ERR: Raised if <code>doctype</code> has already + * been used with a different document or was created from a different + * implementation. + * @since DOM Level 2 + */ + public Document createDocument(String namespaceURI, + String qualifiedName, + DocumentType doctype) + throws DOMException; + +} diff --git a/xml/src/main/java/org/w3c/dom/Document.java b/xml/src/main/java/org/w3c/dom/Document.java new file mode 100644 index 0000000..3beddaa --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Document.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>Document</code> interface represents the entire HTML or XML + * document. Conceptually, it is the root of the document tree, and provides + * the primary access to the document's data. + * <p>Since elements, text nodes, comments, processing instructions, etc. + * cannot exist outside the context of a <code>Document</code>, the + * <code>Document</code> interface also contains the factory methods needed + * to create these objects. The <code>Node</code> objects created have a + * <code>ownerDocument</code> attribute which associates them with the + * <code>Document</code> within whose context they were created. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Document extends Node { + /** + * The Document Type Declaration (see <code>DocumentType</code>) + * associated with this document. For HTML documents as well as XML + * documents without a document type declaration this returns + * <code>null</code>. The DOM Level 2 does not support editing the + * Document Type Declaration. <code>docType</code> cannot be altered in + * any way, including through the use of methods inherited from the + * <code>Node</code> interface, such as <code>insertNode</code> or + * <code>removeNode</code>. + * + * @return the Document Type Declaration associated with this document, if + * any. + */ + public DocumentType getDoctype(); + + /** + * The <code>DOMImplementation</code> object that handles this document. A + * DOM application may use objects from multiple implementations. + * + * @return <code>DOMImplementation</code> object that handles this document. + */ + public DOMImplementation getImplementation(); + + /** + * This is a convenience attribute that allows direct access to the child + * node that is the root element of the document. For HTML documents, + * this is the element with the tagName "HTML". + * + * @return the root element of this document. + */ + public Element getDocumentElement(); + + /** + * Creates an element of the type specified. Note that the instance + * returned implements the <code>Element</code> interface, so attributes + * can be specified directly on the returned object. + * <br>In addition, if there are known attributes with default values, + * <code>Attr</code> nodes representing them are automatically created + * and attached to the element. + * <br>To create an element with a qualified name and namespace URI, use + * the <code>createElementNS</code> method. + * @param tagName The name of the element type to instantiate. For XML, + * this is case-sensitive. For HTML, the <code>tagName</code> + * parameter may be provided in any case, but it must be mapped to the + * canonical uppercase form by the DOM implementation. + * @return A new <code>Element</code> object with the + * <code>nodeName</code> attribute set to <code>tagName</code>, and + * <code>localName</code>, <code>prefix</code>, and + * <code>namespaceURI</code> set to <code>null</code>. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified name contains an + * illegal character. + */ + public Element createElement(String tagName) + throws DOMException; + + /** + * Creates an empty <code>DocumentFragment</code> object. + * @return A new <code>DocumentFragment</code>. + */ + public DocumentFragment createDocumentFragment(); + + /** + * Creates a <code>Text</code> node given the specified string. + * @param data The data for the node. + * @return The new <code>Text</code> object. + */ + public Text createTextNode(String data); + + /** + * Creates a <code>Comment</code> node given the specified string. + * @param data The data for the node. + * @return The new <code>Comment</code> object. + */ + public Comment createComment(String data); + + /** + * Creates a <code>CDATASection</code> node whose value is the specified + * string. + * @param data The data for the <code>CDATASection</code> contents. + * @return The new <code>CDATASection</code> object. + * @exception DOMException + * NOT_SUPPORTED_ERR: Raised if this document is an HTML document. + */ + public CDATASection createCDATASection(String data) + throws DOMException; + + /** + * Creates a <code>ProcessingInstruction</code> node given the specified + * name and data strings. + * @param target The target part of the processing instruction. + * @param data The data for the node. + * @return The new <code>ProcessingInstruction</code> object. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified target contains an + * illegal character. + * <br>NOT_SUPPORTED_ERR: Raised if this document is an HTML document. + */ + public ProcessingInstruction createProcessingInstruction(String target, + String data) + throws DOMException; + + /** + * Creates an <code>Attr</code> of the given name. Note that the + * <code>Attr</code> instance can then be set on an <code>Element</code> + * using the <code>setAttributeNode</code> method. + * <br>To create an attribute with a qualified name and namespace URI, use + * the <code>createAttributeNS</code> method. + * @param name The name of the attribute. + * @return A new <code>Attr</code> object with the <code>nodeName</code> + * attribute set to <code>name</code>, and <code>localName</code>, + * <code>prefix</code>, and <code>namespaceURI</code> set to + * <code>null</code>. The value of the attribute is the empty string. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified name contains an + * illegal character. + */ + public Attr createAttribute(String name) + throws DOMException; + + /** + * Creates an <code>EntityReference</code> object. In addition, if the + * referenced entity is known, the child list of the + * <code>EntityReference</code> node is made the same as that of the + * corresponding <code>Entity</code> node.If any descendant of the + * <code>Entity</code> node has an unbound namespace prefix, the + * corresponding descendant of the created <code>EntityReference</code> + * node is also unbound; (its <code>namespaceURI</code> is + * <code>null</code>). The DOM Level 2 does not support any mechanism to + * resolve namespace prefixes. + * @param name The name of the entity to reference. + * @return The new <code>EntityReference</code> object. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified name contains an + * illegal character. + * <br>NOT_SUPPORTED_ERR: Raised if this document is an HTML document. + */ + public EntityReference createEntityReference(String name) + throws DOMException; + + /** + * Returns a <code>NodeList</code> of all the <code>Elements</code> with a + * given tag name in the order in which they are encountered in a + * preorder traversal of the <code>Document</code> tree. + * @param tagname The name of the tag to match on. The special value "*" + * matches all tags. + * @return A new <code>NodeList</code> object containing all the matched + * <code>Elements</code>. + */ + public NodeList getElementsByTagName(String tagname); + + /** + * Imports a node from another document to this document. The returned + * node has no parent; (<code>parentNode</code> is <code>null</code>). + * The source node is not altered or removed from the original document; + * this method creates a new copy of the source node. + * <br>For all nodes, importing a node creates a node object owned by the + * importing document, with attribute values identical to the source + * node's <code>nodeName</code> and <code>nodeType</code>, plus the + * attributes related to namespaces (<code>prefix</code>, + * <code>localName</code>, and <code>namespaceURI</code>). As in the + * <code>cloneNode</code> operation on a <code>Node</code>, the source + * node is not altered. + * <br>Additional information is copied as appropriate to the + * <code>nodeType</code>, attempting to mirror the behavior expected if + * a fragment of XML or HTML source was copied from one document to + * another, recognizing that the two documents may have different DTDs + * in the XML case. The following list describes the specifics for each + * type of node. + * <dl> + * <dt>ATTRIBUTE_NODE</dt> + * <dd>The <code>ownerElement</code> attribute + * is set to <code>null</code> and the <code>specified</code> flag is + * set to <code>true</code> on the generated <code>Attr</code>. The + * descendants of the source <code>Attr</code> are recursively imported + * and the resulting nodes reassembled to form the corresponding subtree. + * Note that the <code>deep</code> parameter has no effect on + * <code>Attr</code> nodes; they always carry their children with them + * when imported.</dd> + * <dt>DOCUMENT_FRAGMENT_NODE</dt> + * <dd>If the <code>deep</code> option + * was set to <code>true</code>, the descendants of the source element + * are recursively imported and the resulting nodes reassembled to form + * the corresponding subtree. Otherwise, this simply generates an empty + * <code>DocumentFragment</code>.</dd> + * <dt>DOCUMENT_NODE</dt> + * <dd><code>Document</code> + * nodes cannot be imported.</dd> + * <dt>DOCUMENT_TYPE_NODE</dt> + * <dd><code>DocumentType</code> + * nodes cannot be imported.</dd> + * <dt>ELEMENT_NODE</dt> + * <dd>Specified attribute nodes of the + * source element are imported, and the generated <code>Attr</code> + * nodes are attached to the generated <code>Element</code>. Default + * attributes are not copied, though if the document being imported into + * defines default attributes for this element name, those are assigned. + * If the <code>importNode</code> <code>deep</code> parameter was set to + * <code>true</code>, the descendants of the source element are + * recursively imported and the resulting nodes reassembled to form the + * corresponding subtree.</dd> + * <dt>ENTITY_NODE</dt> + * <dd><code>Entity</code> nodes can be + * imported, however in the current release of the DOM the + * <code>DocumentType</code> is readonly. Ability to add these imported + * nodes to a <code>DocumentType</code> will be considered for addition + * to a future release of the DOM.On import, the <code>publicId</code>, + * <code>systemId</code>, and <code>notationName</code> attributes are + * copied. If a <code>deep</code> import is requested, the descendants + * of the the source <code>Entity</code> are recursively imported and + * the resulting nodes reassembled to form the corresponding subtree.</dd> + * <dt> + * ENTITY_REFERENCE_NODE</dt> + * <dd>Only the <code>EntityReference</code> itself is + * copied, even if a <code>deep</code> import is requested, since the + * source and destination documents might have defined the entity + * differently. If the document being imported into provides a + * definition for this entity name, its value is assigned.</dd> + * <dt>NOTATION_NODE</dt> + * <dd> + * <code>Notation</code> nodes can be imported, however in the current + * release of the DOM the <code>DocumentType</code> is readonly. Ability + * to add these imported nodes to a <code>DocumentType</code> will be + * considered for addition to a future release of the DOM.On import, the + * <code>publicId</code> and <code>systemId</code> attributes are copied. + * Note that the <code>deep</code> parameter has no effect on + * <code>Notation</code> nodes since they never have any children.</dd> + * <dt> + * PROCESSING_INSTRUCTION_NODE</dt> + * <dd>The imported node copies its + * <code>target</code> and <code>data</code> values from those of the + * source node.</dd> + * <dt>TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE</dt> + * <dd>These three + * types of nodes inheriting from <code>CharacterData</code> copy their + * <code>data</code> and <code>length</code> attributes from those of + * the source node.</dd> + * + * @param importedNode The node to import. + * @param deep If <code>true</code>, recursively import the subtree under + * the specified node; if <code>false</code>, import only the node + * itself, as explained above. This has no effect on <code>Attr</code> + * , <code>EntityReference</code>, and <code>Notation</code> nodes. + * @return The imported node that belongs to this <code>Document</code>. + * @exception DOMException + * NOT_SUPPORTED_ERR: Raised if the type of node being imported is not + * supported. + * @since DOM Level 2 + */ + public Node importNode(Node importedNode, + boolean deep) + throws DOMException; + + /** + * Creates an element of the given qualified name and namespace URI. + * HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the element to create. + * @param qualifiedName The qualified name of the element type to + * instantiate. + * @return A new <code>Element</code> object with the following + * attributes:AttributeValue<code>Node.nodeName</code> + * <code>qualifiedName</code><code>Node.namespaceURI</code> + * <code>namespaceURI</code><code>Node.prefix</code>prefix, extracted + * from <code>qualifiedName</code>, or <code>null</code> if there is + * no prefix<code>Node.localName</code>local name, extracted from + * <code>qualifiedName</code><code>Element.tagName</code> + * <code>qualifiedName</code> + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified name + * contains an illegal character. + * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is + * malformed, if the <code>qualifiedName</code> has a prefix and the + * <code>namespaceURI</code> is <code>null</code>, or if the + * <code>qualifiedName</code> has a prefix that is "xml" and the + * <code>namespaceURI</code> is different from " + * http://www.w3.org/XML/1998/namespace" . + * @since DOM Level 2 + */ + public Element createElementNS(String namespaceURI, + String qualifiedName) + throws DOMException; + + /** + * Creates an attribute of the given qualified name and namespace URI. + * HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the attribute to create. + * @param qualifiedName The qualified name of the attribute to instantiate. + * @return A new <code>Attr</code> object with the following attributes: + * AttributeValue<code>Node.nodeName</code>qualifiedName + * <code>Node.namespaceURI</code><code>namespaceURI</code> + * <code>Node.prefix</code>prefix, extracted from + * <code>qualifiedName</code>, or <code>null</code> if there is no + * prefix<code>Node.localName</code>local name, extracted from + * <code>qualifiedName</code><code>Attr.name</code> + * <code>qualifiedName</code><code>Node.nodeValue</code>the empty + * string + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified name + * contains an illegal character. + * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is + * malformed, if the <code>qualifiedName</code> has a prefix and the + * <code>namespaceURI</code> is <code>null</code>, if the + * <code>qualifiedName</code> has a prefix that is "xml" and the + * <code>namespaceURI</code> is different from " + * http://www.w3.org/XML/1998/namespace", or if the + * <code>qualifiedName</code> is "xmlns" and the + * <code>namespaceURI</code> is different from " + * http://www.w3.org/2000/xmlns/". + * @since DOM Level 2 + */ + public Attr createAttributeNS(String namespaceURI, + String qualifiedName) + throws DOMException; + + /** + * Returns a <code>NodeList</code> of all the <code>Elements</code> with a + * given local name and namespace URI in the order in which they are + * encountered in a preorder traversal of the <code>Document</code> tree. + * @param namespaceURI The namespace URI of the elements to match on. The + * special value "*" matches all namespaces. + * @param localName The local name of the elements to match on. The + * special value "*" matches all local names. + * @return A new <code>NodeList</code> object containing all the matched + * <code>Elements</code>. + * @since DOM Level 2 + */ + public NodeList getElementsByTagNameNS(String namespaceURI, + String localName); + + /** + * Returns the <code>Element</code> whose <code>ID</code> is given by + * <code>elementId</code>. If no such element exists, returns + * <code>null</code>. Behavior is not defined if more than one element + * has this <code>ID</code>. The DOM implementation must have + * information that says which attributes are of type ID. Attributes + * with the name "ID" are not of type ID unless so defined. + * Implementations that do not know whether attributes are of type ID or + * not are expected to return <code>null</code>. + * @param elementId The unique <code>id</code> value for an element. + * @return The matching element. + * @since DOM Level 2 + */ + public Element getElementById(String elementId); + +} diff --git a/xml/src/main/java/org/w3c/dom/DocumentFragment.java b/xml/src/main/java/org/w3c/dom/DocumentFragment.java new file mode 100644 index 0000000..6ade30c --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/DocumentFragment.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +/** + * <code>DocumentFragment</code> is a "lightweight" or "minimal" + * <code>Document</code> object. It is very common to want to be able to + * extract a portion of a document's tree or to create a new fragment of a + * document. Imagine implementing a user command like cut or rearranging a + * document by moving fragments around. It is desirable to have an object + * which can hold such fragments and it is quite natural to use a Node for + * this purpose. While it is true that a <code>Document</code> object could + * fulfill this role, a <code>Document</code> object can potentially be a + * heavyweight object, depending on the underlying implementation. What is + * really needed for this is a very lightweight object. + * <code>DocumentFragment</code> is such an object. + * <p>Furthermore, various operations -- such as inserting nodes as children + * of another <code>Node</code> -- may take <code>DocumentFragment</code> + * objects as arguments; this results in all the child nodes of the + * <code>DocumentFragment</code> being moved to the child list of this node. + * <p>The children of a <code>DocumentFragment</code> node are zero or more + * nodes representing the tops of any sub-trees defining the structure of + * the document. <code>DocumentFragment</code> nodes do not need to be + * well-formed XML documents (although they do need to follow the rules + * imposed upon well-formed XML parsed entities, which can have multiple top + * nodes). For example, a <code>DocumentFragment</code> might have only one + * child and that child node could be a <code>Text</code> node. Such a + * structure model represents neither an HTML document nor a well-formed XML + * document. + * <p>When a <code>DocumentFragment</code> is inserted into a + * <code>Document</code> (or indeed any other <code>Node</code> that may + * take children) the children of the <code>DocumentFragment</code> and not + * the <code>DocumentFragment</code> itself are inserted into the + * <code>Node</code>. This makes the <code>DocumentFragment</code> very + * useful when the user wishes to create nodes that are siblings; the + * <code>DocumentFragment</code> acts as the parent of these nodes so that + * the user can use the standard methods from the <code>Node</code> + * interface, such as <code>insertBefore</code> and <code>appendChild</code>. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface DocumentFragment extends Node { +} diff --git a/xml/src/main/java/org/w3c/dom/DocumentType.java b/xml/src/main/java/org/w3c/dom/DocumentType.java new file mode 100644 index 0000000..685c073 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/DocumentType.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * Each <code>Document</code> has a <code>doctype</code> attribute whose value + * is either <code>null</code> or a <code>DocumentType</code> object. The + * <code>DocumentType</code> interface in the DOM Core provides an interface + * to the list of entities that are defined for the document, and little + * else because the effect of namespaces and the various XML schema efforts + * on DTD representation are not clearly understood as of this writing. + * <p>The DOM Level 2 doesn't support editing <code>DocumentType</code> nodes. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface DocumentType extends Node { + /** + * The name of DTD; i.e., the name immediately following the + * <code>DOCTYPE</code> keyword. + * + * @return the name of the DTD. + */ + public String getName(); + + /** + * A <code>NamedNodeMap</code> containing the general entities, both + * external and internal, declared in the DTD. Parameter entities are + * not contained. Duplicates are discarded. For example in: + * <pre><!DOCTYPE + * ex SYSTEM "ex.dtd" [ <!ENTITY foo "foo"> <!ENTITY bar + * "bar"> <!ENTITY bar "bar2"> <!ENTITY % baz "baz"> + * ]> <ex/></pre> + * the interface provides access to <code>foo</code> + * and the first declaration of <code>bar</code> but not the second + * declaration of <code>bar</code> or <code>baz</code>. Every node in + * this map also implements the <code>Entity</code> interface. + * <br>The DOM Level 2 does not support editing entities, therefore + * <code>entities</code> cannot be altered in any way. + * + * @return the entities declared in this DTD. + */ + public NamedNodeMap getEntities(); + + /** + * A <code>NamedNodeMap</code> containing the notations declared in the + * DTD. Duplicates are discarded. Every node in this map also implements + * the <code>Notation</code> interface. + * <br>The DOM Level 2 does not support editing notations, therefore + * <code>notations</code> cannot be altered in any way. + * + * @return the notations declared in this DTD. + */ + public NamedNodeMap getNotations(); + + /** + * The public identifier of the external subset. + * + * @return the public identifier. + * + * @since DOM Level 2 + */ + public String getPublicId(); + + /** + * The system identifier of the external subset. + * + * @return the system identifier. + * + * @since DOM Level 2 + */ + public String getSystemId(); + + /** + * The internal subset as a string. The actual content returned depends on + * how much information is available to the implementation. This may + * vary depending on various parameters, including the XML processor + * used to build the document. + * + * @return the internal subset. + * + * @since DOM Level 2 + */ + public String getInternalSubset(); + +} diff --git a/xml/src/main/java/org/w3c/dom/Element.java b/xml/src/main/java/org/w3c/dom/Element.java new file mode 100644 index 0000000..72e3478 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Element.java @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>Element</code> interface represents an element in an HTML or XML + * document. Elements may have attributes associated with them; since the + * <code>Element</code> interface inherits from <code>Node</code>, the + * generic <code>Node</code> interface attribute <code>attributes</code> may + * be used to retrieve the set of all attributes for an element. There are + * methods on the <code>Element</code> interface to retrieve either an + * <code>Attr</code> object by name or an attribute value by name. In XML, + * where an attribute value may contain entity references, an + * <code>Attr</code> object should be retrieved to examine the possibly + * fairly complex sub-tree representing the attribute value. On the other + * hand, in HTML, where all attributes have simple string values, methods to + * directly access an attribute value can safely be used as a convenience.In + * DOM Level 2, the method <code>normalize</code> is inherited from the + * <code>Node</code> interface where it was moved. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Element extends Node { + /** + * The name of the element. For example, in: + * <pre> <elementExample + * id="demo"> ... </elementExample> , </pre> + * <code>tagName</code> has + * the value <code>"elementExample"</code>. Note that this is + * case-preserving in XML, as are all of the operations of the DOM. The + * HTML DOM returns the <code>tagName</code> of an HTML element in the + * canonical uppercase form, regardless of the case in the source HTML + * document. + * + * @return the name of the element. + */ + public String getTagName(); + + /** + * Retrieves an attribute value by name. + * @param name The name of the attribute to retrieve. + * @return The <code>Attr</code> value as a string, or the empty string + * if that attribute does not have a specified or default value. + */ + public String getAttribute(String name); + + /** + * Adds a new attribute. If an attribute with that name is already present + * in the element, its value is changed to be that of the value + * parameter. This value is a simple string; it is not parsed as it is + * being set. So any markup (such as syntax to be recognized as an + * entity reference) is treated as literal text, and needs to be + * appropriately escaped by the implementation when it is written out. + * In order to assign an attribute value that contains entity + * references, the user must create an <code>Attr</code> node plus any + * <code>Text</code> and <code>EntityReference</code> nodes, build the + * appropriate subtree, and use <code>setAttributeNode</code> to assign + * it as the value of an attribute. + * <br>To set an attribute with a qualified name and namespace URI, use + * the <code>setAttributeNS</code> method. + * @param name The name of the attribute to create or alter. + * @param value Value to set in string form. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified name contains an + * illegal character. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public void setAttribute(String name, + String value) + throws DOMException; + + /** + * Removes an attribute by name. If the removed attribute is known to have + * a default value, an attribute immediately appears containing the + * default value as well as the corresponding namespace URI, local name, + * and prefix when applicable. + * <br>To remove an attribute by local name and namespace URI, use the + * <code>removeAttributeNS</code> method. + * @param name The name of the attribute to remove. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public void removeAttribute(String name) + throws DOMException; + + /** + * Retrieves an attribute node by name. + * <br>To retrieve an attribute node by qualified name and namespace URI, + * use the <code>getAttributeNodeNS</code> method. + * @param name The name (<code>nodeName</code>) of the attribute to + * retrieve. + * @return The <code>Attr</code> node with the specified name ( + * <code>nodeName</code>) or <code>null</code> if there is no such + * attribute. + */ + public Attr getAttributeNode(String name); + + /** + * Adds a new attribute node. If an attribute with that name ( + * <code>nodeName</code>) is already present in the element, it is + * replaced by the new one. + * <br>To add a new attribute node with a qualified name and namespace + * URI, use the <code>setAttributeNodeNS</code> method. + * @param newAttr The <code>Attr</code> node to add to the attribute list. + * @return If the <code>newAttr</code> attribute replaces an existing + * attribute, the replaced <code>Attr</code> node is returned, + * otherwise <code>null</code> is returned. + * @exception DOMException + * WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from a + * different document than the one that created the element. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * <br>INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already an + * attribute of another <code>Element</code> object. The DOM user must + * explicitly clone <code>Attr</code> nodes to re-use them in other + * elements. + */ + public Attr setAttributeNode(Attr newAttr) + throws DOMException; + + /** + * Removes the specified attribute node. If the removed <code>Attr</code> + * has a default value it is immediately replaced. The replacing + * attribute has the same namespace URI and local name, as well as the + * original prefix, when applicable. + * @param oldAttr The <code>Attr</code> node to remove from the attribute + * list. + * @return The <code>Attr</code> node that was removed. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * <br>NOT_FOUND_ERR: Raised if <code>oldAttr</code> is not an attribute + * of the element. + */ + public Attr removeAttributeNode(Attr oldAttr) + throws DOMException; + + /** + * Returns a <code>NodeList</code> of all descendant <code>Elements</code> + * with a given tag name, in the order in which they are encountered in + * a preorder traversal of this <code>Element</code> tree. + * @param name The name of the tag to match on. The special value "*" + * matches all tags. + * @return A list of matching <code>Element</code> nodes. + */ + public NodeList getElementsByTagName(String name); + + /** + * Retrieves an attribute value by local name and namespace URI. HTML-only + * DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the attribute to retrieve. + * @param localName The local name of the attribute to retrieve. + * @return The <code>Attr</code> value as a string, or the empty string + * if that attribute does not have a specified or default value. + * @since DOM Level 2 + */ + public String getAttributeNS(String namespaceURI, + String localName); + + /** + * Adds a new attribute. If an attribute with the same local name and + * namespace URI is already present on the element, its prefix is + * changed to be the prefix part of the <code>qualifiedName</code>, and + * its value is changed to be the <code>value</code> parameter. This + * value is a simple string; it is not parsed as it is being set. So any + * markup (such as syntax to be recognized as an entity reference) is + * treated as literal text, and needs to be appropriately escaped by the + * implementation when it is written out. In order to assign an + * attribute value that contains entity references, the user must create + * an <code>Attr</code> node plus any <code>Text</code> and + * <code>EntityReference</code> nodes, build the appropriate subtree, + * and use <code>setAttributeNodeNS</code> or + * <code>setAttributeNode</code> to assign it as the value of an + * attribute. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the attribute to create or + * alter. + * @param qualifiedName The qualified name of the attribute to create or + * alter. + * @param value The value to set in string form. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified name + * contains an illegal character. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is + * malformed, if the <code>qualifiedName</code> has a prefix and the + * <code>namespaceURI</code> is <code>null</code>, if the + * <code>qualifiedName</code> has a prefix that is "xml" and the + * <code>namespaceURI</code> is different from " + * http://www.w3.org/XML/1998/namespace", or if the + * <code>qualifiedName</code> is "xmlns" and the + * <code>namespaceURI</code> is different from " + * http://www.w3.org/2000/xmlns/". + * @since DOM Level 2 + */ + public void setAttributeNS(String namespaceURI, + String qualifiedName, + String value) + throws DOMException; + + /** + * Removes an attribute by local name and namespace URI. If the removed + * attribute has a default value it is immediately replaced. The + * replacing attribute has the same namespace URI and local name, as + * well as the original prefix. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the attribute to remove. + * @param localName The local name of the attribute to remove. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * @since DOM Level 2 + */ + public void removeAttributeNS(String namespaceURI, + String localName) + throws DOMException; + + /** + * Retrieves an <code>Attr</code> node by local name and namespace URI. + * HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the attribute to retrieve. + * @param localName The local name of the attribute to retrieve. + * @return The <code>Attr</code> node with the specified attribute local + * name and namespace URI or <code>null</code> if there is no such + * attribute. + * @since DOM Level 2 + */ + public Attr getAttributeNodeNS(String namespaceURI, + String localName); + + /** + * Adds a new attribute. If an attribute with that local name and that + * namespace URI is already present in the element, it is replaced by + * the new one. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param newAttr The <code>Attr</code> node to add to the attribute list. + * @return If the <code>newAttr</code> attribute replaces an existing + * attribute with the same local name and namespace URI, the replaced + * <code>Attr</code> node is returned, otherwise <code>null</code> is + * returned. + * @exception DOMException + * WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from a + * different document than the one that created the element. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * <br>INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already an + * attribute of another <code>Element</code> object. The DOM user must + * explicitly clone <code>Attr</code> nodes to re-use them in other + * elements. + * @since DOM Level 2 + */ + public Attr setAttributeNodeNS(Attr newAttr) + throws DOMException; + + /** + * Returns a <code>NodeList</code> of all the descendant + * <code>Elements</code> with a given local name and namespace URI in + * the order in which they are encountered in a preorder traversal of + * this <code>Element</code> tree. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the elements to match on. The + * special value "*" matches all namespaces. + * @param localName The local name of the elements to match on. The + * special value "*" matches all local names. + * @return A new <code>NodeList</code> object containing all the matched + * <code>Elements</code>. + * @since DOM Level 2 + */ + public NodeList getElementsByTagNameNS(String namespaceURI, + String localName); + + /** + * Returns <code>true</code> when an attribute with a given name is + * specified on this element or has a default value, <code>false</code> + * otherwise. + * @param name The name of the attribute to look for. + * @return <code>true</code> if an attribute with the given name is + * specified on this element or has a default value, <code>false</code> + * otherwise. + * @since DOM Level 2 + */ + public boolean hasAttribute(String name); + + /** + * Returns <code>true</code> when an attribute with a given local name and + * namespace URI is specified on this element or has a default value, + * <code>false</code> otherwise. HTML-only DOM implementations do not + * need to implement this method. + * @param namespaceURI The namespace URI of the attribute to look for. + * @param localName The local name of the attribute to look for. + * @return <code>true</code> if an attribute with the given local name + * and namespace URI is specified or has a default value on this + * element, <code>false</code> otherwise. + * @since DOM Level 2 + */ + public boolean hasAttributeNS(String namespaceURI, + String localName); + +} diff --git a/xml/src/main/java/org/w3c/dom/Entity.java b/xml/src/main/java/org/w3c/dom/Entity.java new file mode 100644 index 0000000..5b09608 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Entity.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * This interface represents an entity, either parsed or unparsed, in an XML + * document. Note that this models the entity itself not the entity + * declaration. <code>Entity</code> declaration modeling has been left for a + * later Level of the DOM specification. + * <p>The <code>nodeName</code> attribute that is inherited from + * <code>Node</code> contains the name of the entity. + * <p>An XML processor may choose to completely expand entities before the + * structure model is passed to the DOM; in this case there will be no + * <code>EntityReference</code> nodes in the document tree. + * <p>XML does not mandate that a non-validating XML processor read and + * process entity declarations made in the external subset or declared in + * external parameter entities. This means that parsed entities declared in + * the external subset need not be expanded by some classes of applications, + * and that the replacement value of the entity may not be available. When + * the replacement value is available, the corresponding <code>Entity</code> + * node's child list represents the structure of that replacement text. + * Otherwise, the child list is empty. + * <p>The DOM Level 2 does not support editing <code>Entity</code> nodes; if a + * user wants to make changes to the contents of an <code>Entity</code>, + * every related <code>EntityReference</code> node has to be replaced in the + * structure model by a clone of the <code>Entity</code>'s contents, and + * then the desired changes must be made to each of those clones instead. + * <code>Entity</code> nodes and all their descendants are readonly. + * <p>An <code>Entity</code> node does not have any parent.If the entity + * contains an unbound namespace prefix, the <code>namespaceURI</code> of + * the corresponding node in the <code>Entity</code> node subtree is + * <code>null</code>. The same is true for <code>EntityReference</code> + * nodes that refer to this entity, when they are created using the + * <code>createEntityReference</code> method of the <code>Document</code> + * interface. The DOM Level 2 does not support any mechanism to resolve + * namespace prefixes. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Entity extends Node { + /** + * The public identifier associated with the entity, if specified. If the + * public identifier was not specified, this is <code>null</code>. + * + * @return the public identifier of the entity or <code>null</code>, if the + * entity does not have a public identifier. + */ + public String getPublicId(); + + /** + * The system identifier associated with the entity, if specified. If the + * system identifier was not specified, this is <code>null</code>. + * + * @return the system identifier of the entity or <code>null</code>, if the + * entity does not have a system identifier. + */ + public String getSystemId(); + + /** + * For unparsed entities, the name of the notation for the entity. For + * parsed entities, this is <code>null</code>. + * + * @return the notation name of the entity or <code>null</code>, if the + * entity is parsed. + */ + public String getNotationName(); + +} diff --git a/xml/src/main/java/org/w3c/dom/EntityReference.java b/xml/src/main/java/org/w3c/dom/EntityReference.java new file mode 100644 index 0000000..ff3cf9d --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/EntityReference.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +/** + * <code>EntityReference</code> objects may be inserted into the structure + * model when an entity reference is in the source document, or when the + * user wishes to insert an entity reference. Note that character references + * and references to predefined entities are considered to be expanded by + * the HTML or XML processor so that characters are represented by their + * Unicode equivalent rather than by an entity reference. Moreover, the XML + * processor may completely expand references to entities while building the + * structure model, instead of providing <code>EntityReference</code> + * objects. If it does provide such objects, then for a given + * <code>EntityReference</code> node, it may be that there is no + * <code>Entity</code> node representing the referenced entity. If such an + * <code>Entity</code> exists, then the subtree of the + * <code>EntityReference</code> node is in general a copy of the + * <code>Entity</code> node subtree. However, this may not be true when an + * entity contains an unbound namespace prefix. In such a case, because the + * namespace prefix resolution depends on where the entity reference is, the + * descendants of the <code>EntityReference</code> node may be bound to + * different namespace URIs. + * <p>As for <code>Entity</code> nodes, <code>EntityReference</code> nodes and + * all their descendants are readonly. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface EntityReference extends Node { +} diff --git a/xml/src/main/java/org/w3c/dom/NamedNodeMap.java b/xml/src/main/java/org/w3c/dom/NamedNodeMap.java new file mode 100644 index 0000000..a6cbf09 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/NamedNodeMap.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * Objects implementing the <code>NamedNodeMap</code> interface are used to + * represent collections of nodes that can be accessed by name. Note that + * <code>NamedNodeMap</code> does not inherit from <code>NodeList</code>; + * <code>NamedNodeMaps</code> are not maintained in any particular order. + * Objects contained in an object implementing <code>NamedNodeMap</code> may + * also be accessed by an ordinal index, but this is simply to allow + * convenient enumeration of the contents of a <code>NamedNodeMap</code>, + * and does not imply that the DOM specifies an order to these Nodes. + * <p><code>NamedNodeMap</code> objects in the DOM are live. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface NamedNodeMap { + /** + * Retrieves a node specified by name. + * @param name The <code>nodeName</code> of a node to retrieve. + * @return A <code>Node</code> (of any type) with the specified + * <code>nodeName</code>, or <code>null</code> if it does not identify + * any node in this map. + */ + public Node getNamedItem(String name); + + /** + * Adds a node using its <code>nodeName</code> attribute. If a node with + * that name is already present in this map, it is replaced by the new + * one. + * <br>As the <code>nodeName</code> attribute is used to derive the name + * which the node must be stored under, multiple nodes of certain types + * (those that have a "special" string value) cannot be stored as the + * names would clash. This is seen as preferable to allowing nodes to be + * aliased. + * @param arg A node to store in this map. The node will later be + * accessible using the value of its <code>nodeName</code> attribute. + * @return If the new <code>Node</code> replaces an existing node the + * replaced <code>Node</code> is returned, otherwise <code>null</code> + * is returned. + * @exception DOMException + * WRONG_DOCUMENT_ERR: Raised if <code>arg</code> was created from a + * different document than the one that created this map. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly. + * <br>INUSE_ATTRIBUTE_ERR: Raised if <code>arg</code> is an + * <code>Attr</code> that is already an attribute of another + * <code>Element</code> object. The DOM user must explicitly clone + * <code>Attr</code> nodes to re-use them in other elements. + */ + public Node setNamedItem(Node arg) + throws DOMException; + + /** + * Removes a node specified by name. When this map contains the attributes + * attached to an element, if the removed attribute is known to have a + * default value, an attribute immediately appears containing the + * default value as well as the corresponding namespace URI, local name, + * and prefix when applicable. + * @param name The <code>nodeName</code> of the node to remove. + * @return The node removed from this map if a node with such a name + * exists. + * @exception DOMException + * NOT_FOUND_ERR: Raised if there is no node named <code>name</code> in + * this map. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly. + */ + public Node removeNamedItem(String name) + throws DOMException; + + /** + * Returns the <code>index</code>th item in the map. If <code>index</code> + * is greater than or equal to the number of nodes in this map, this + * returns <code>null</code>. + * @param index Index into this map. + * @return The node at the <code>index</code>th position in the map, or + * <code>null</code> if that is not a valid index. + */ + public Node item(int index); + + /** + * The number of nodes in this map. The range of valid child node indices + * is <code>0</code> to <code>length-1</code> inclusive. + * + * @return the length of the map. + */ + public int getLength(); + + /** + * Retrieves a node specified by local name and namespace URI. HTML-only + * DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the node to retrieve. + * @param localName The local name of the node to retrieve. + * @return A <code>Node</code> (of any type) with the specified local + * name and namespace URI, or <code>null</code> if they do not + * identify any node in this map. + * @since DOM Level 2 + */ + public Node getNamedItemNS(String namespaceURI, + String localName); + + /** + * Adds a node using its <code>namespaceURI</code> and + * <code>localName</code>. If a node with that namespace URI and that + * local name is already present in this map, it is replaced by the new + * one. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param arg A node to store in this map. The node will later be + * accessible using the value of its <code>namespaceURI</code> and + * <code>localName</code> attributes. + * @return If the new <code>Node</code> replaces an existing node the + * replaced <code>Node</code> is returned, otherwise <code>null</code> + * is returned. + * @exception DOMException + * WRONG_DOCUMENT_ERR: Raised if <code>arg</code> was created from a + * different document than the one that created this map. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly. + * <br>INUSE_ATTRIBUTE_ERR: Raised if <code>arg</code> is an + * <code>Attr</code> that is already an attribute of another + * <code>Element</code> object. The DOM user must explicitly clone + * <code>Attr</code> nodes to re-use them in other elements. + * @since DOM Level 2 + */ + public Node setNamedItemNS(Node arg) + throws DOMException; + + /** + * Removes a node specified by local name and namespace URI. A removed + * attribute may be known to have a default value when this map contains + * the attributes attached to an element, as returned by the attributes + * attribute of the <code>Node</code> interface. If so, an attribute + * immediately appears containing the default value as well as the + * corresponding namespace URI, local name, and prefix when applicable. + * <br>HTML-only DOM implementations do not need to implement this method. + * @param namespaceURI The namespace URI of the node to remove. + * @param localName The local name of the node to remove. + * @return The node removed from this map if a node with such a local + * name and namespace URI exists. + * @exception DOMException + * NOT_FOUND_ERR: Raised if there is no node with the specified + * <code>namespaceURI</code> and <code>localName</code> in this map. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly. + * @since DOM Level 2 + */ + public Node removeNamedItemNS(String namespaceURI, + String localName) + throws DOMException; + +} diff --git a/xml/src/main/java/org/w3c/dom/Node.java b/xml/src/main/java/org/w3c/dom/Node.java new file mode 100644 index 0000000..c91c46d --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Node.java @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>Node</code> interface is the primary datatype for the entire + * Document Object Model. It represents a single node in the document tree. + * While all objects implementing the <code>Node</code> interface expose + * methods for dealing with children, not all objects implementing the + * <code>Node</code> interface may have children. For example, + * <code>Text</code> nodes may not have children, and adding children to + * such nodes results in a <code>DOMException</code> being raised. + * <p>The attributes <code>nodeName</code>, <code>nodeValue</code> and + * <code>attributes</code> are included as a mechanism to get at node + * information without casting down to the specific derived interface. In + * cases where there is no obvious mapping of these attributes for a + * specific <code>nodeType</code> (e.g., <code>nodeValue</code> for an + * <code>Element</code> or <code>attributes</code> for a <code>Comment</code> + * ), this returns <code>null</code>. Note that the specialized interfaces + * may contain additional and more convenient mechanisms to get and set the + * relevant information. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Node { + // NodeType + /** + * The node is an <code>Element</code>. + */ + public static final short ELEMENT_NODE = 1; + /** + * The node is an <code>Attr</code>. + */ + public static final short ATTRIBUTE_NODE = 2; + /** + * The node is a <code>Text</code> node. + */ + public static final short TEXT_NODE = 3; + /** + * The node is a <code>CDATASection</code>. + */ + public static final short CDATA_SECTION_NODE = 4; + /** + * The node is an <code>EntityReference</code>. + */ + public static final short ENTITY_REFERENCE_NODE = 5; + /** + * The node is an <code>Entity</code>. + */ + public static final short ENTITY_NODE = 6; + /** + * The node is a <code>ProcessingInstruction</code>. + */ + public static final short PROCESSING_INSTRUCTION_NODE = 7; + /** + * The node is a <code>Comment</code>. + */ + public static final short COMMENT_NODE = 8; + /** + * The node is a <code>Document</code>. + */ + public static final short DOCUMENT_NODE = 9; + /** + * The node is a <code>DocumentType</code>. + */ + public static final short DOCUMENT_TYPE_NODE = 10; + /** + * The node is a <code>DocumentFragment</code>. + */ + public static final short DOCUMENT_FRAGMENT_NODE = 11; + /** + * The node is a <code>Notation</code>. + */ + public static final short NOTATION_NODE = 12; + + /** + * The name of this node, depending on its type; see the table above. + * + * @return the name of the node. + */ + public String getNodeName(); + + /** + * Returns the value of this node, depending on its type; see the table + * above. + * + * @return the value of the node. + * + * @exception DOMException + * DOMSTRING_SIZE_ERR: Raised when it would return more characters than + * fit in a <code>DOMString</code> variable on the implementation + * platform. + */ + public String getNodeValue() + throws DOMException; + /** + * Sets the value of this node, depending on its type; see the table above. + * When it is defined to be <code>null</code>, setting it has no effect. + * + * @param nodeValue the new value of the node. + * + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly. + */ + public void setNodeValue(String nodeValue) + throws DOMException; + + /** + * A code representing the type of the underlying object, as defined above. + * + * @return the type of the node. + */ + public short getNodeType(); + + /** + * The parent of this node. All nodes, except <code>Attr</code>, + * <code>Document</code>, <code>DocumentFragment</code>, + * <code>Entity</code>, and <code>Notation</code> may have a parent. + * However, if a node has just been created and not yet added to the + * tree, or if it has been removed from the tree, this is + * <code>null</code>. + * + * @return the parent node, if any. + */ + public Node getParentNode(); + + /** + * A <code>NodeList</code> that contains all children of this node. If + * there are no children, this is a <code>NodeList</code> containing no + * nodes. + * + * @return the list of children, which may be empty. + */ + public NodeList getChildNodes(); + + /** + * The first child of this node. If there is no such node, this returns + * <code>null</code>. + * + * @return the first child of the node, if any. + */ + public Node getFirstChild(); + + /** + * The last child of this node. If there is no such node, this returns + * <code>null</code>. + * + * @return the last child of the node, if any. + */ + public Node getLastChild(); + + /** + * The node immediately preceding this node. If there is no such node, + * this returns <code>null</code>. + * + * @return the preceding node, if any. + */ + public Node getPreviousSibling(); + + /** + * The node immediately following this node. If there is no such node, + * this returns <code>null</code>. + * + * @return the following node, if any. + */ + public Node getNextSibling(); + + /** + * A <code>NamedNodeMap</code> containing the attributes of this node (if + * it is an <code>Element</code>) or <code>null</code> otherwise. + * + * @return the attributes of the node, which may be an empty map, or + * <code>null</code>, if this the node cannot have any attributes. + */ + public NamedNodeMap getAttributes(); + + /** + * The <code>Document</code> object associated with this node. This is + * also the <code>Document</code> object used to create new nodes. When + * this node is a <code>Document</code> or a <code>DocumentType</code> + * which is not used with any <code>Document</code> yet, this is + * <code>null</code>. + * + * @return the document this node belongs to, if any. + * + * @version DOM Level 2 + */ + public Document getOwnerDocument(); + + /** + * Inserts the node <code>newChild</code> before the existing child node + * <code>refChild</code>. If <code>refChild</code> is <code>null</code>, + * insert <code>newChild</code> at the end of the list of children. + * <br>If <code>newChild</code> is a <code>DocumentFragment</code> object, + * all of its children are inserted, in the same order, before + * <code>refChild</code>. If the <code>newChild</code> is already in the + * tree, it is first removed. + * @param newChild The node to insert. + * @param refChild The reference node, i.e., the node before which the new + * node must be inserted. + * @return The node being inserted. + * @exception DOMException + * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not + * allow children of the type of the <code>newChild</code> node, or if + * the node to insert is one of this node's ancestors. + * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created + * from a different document than the one that created this node. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly or + * if the parent of the node being inserted is readonly. + * <br>NOT_FOUND_ERR: Raised if <code>refChild</code> is not a child of + * this node. + */ + public Node insertBefore(Node newChild, + Node refChild) + throws DOMException; + + /** + * Replaces the child node <code>oldChild</code> with <code>newChild</code> + * in the list of children, and returns the <code>oldChild</code> node. + * <br>If <code>newChild</code> is a <code>DocumentFragment</code> object, + * <code>oldChild</code> is replaced by all of the + * <code>DocumentFragment</code> children, which are inserted in the + * same order. If the <code>newChild</code> is already in the tree, it + * is first removed. + * @param newChild The new node to put in the child list. + * @param oldChild The node being replaced in the list. + * @return The node replaced. + * @exception DOMException + * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not + * allow children of the type of the <code>newChild</code> node, or if + * the node to put in is one of this node's ancestors. + * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created + * from a different document than the one that created this node. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node or the parent of + * the new node is readonly. + * <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of + * this node. + */ + public Node replaceChild(Node newChild, + Node oldChild) + throws DOMException; + + /** + * Removes the child node indicated by <code>oldChild</code> from the list + * of children, and returns it. + * @param oldChild The node being removed. + * @return The node removed. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of + * this node. + */ + public Node removeChild(Node oldChild) + throws DOMException; + + /** + * Adds the node <code>newChild</code> to the end of the list of children + * of this node. If the <code>newChild</code> is already in the tree, it + * is first removed. + * @param newChild The node to add.If it is a <code>DocumentFragment</code> + * object, the entire contents of the document fragment are moved + * into the child list of this node + * @return The node added. + * @exception DOMException + * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not + * allow children of the type of the <code>newChild</code> node, or if + * the node to append is one of this node's ancestors. + * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created + * from a different document than the one that created this node. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public Node appendChild(Node newChild) + throws DOMException; + + /** + * Returns whether this node has any children. + * @return <code>true</code> if this node has any children, + * <code>false</code> otherwise. + */ + public boolean hasChildNodes(); + + /** + * Returns a duplicate of this node, i.e., serves as a generic copy + * constructor for nodes. The duplicate node has no parent; ( + * <code>parentNode</code> is <code>null</code>.). + * <br>Cloning an <code>Element</code> copies all attributes and their + * values, including those generated by the XML processor to represent + * defaulted attributes, but this method does not copy any text it + * contains unless it is a deep clone, since the text is contained in a + * child <code>Text</code> node. Cloning an <code>Attribute</code> + * directly, as opposed to be cloned as part of an <code>Element</code> + * cloning operation, returns a specified attribute ( + * <code>specified</code> is <code>true</code>). Cloning any other type + * of node simply returns a copy of this node. + * <br>Note that cloning an immutable subtree results in a mutable copy, + * but the children of an <code>EntityReference</code> clone are readonly + * . In addition, clones of unspecified <code>Attr</code> nodes are + * specified. And, cloning <code>Document</code>, + * <code>DocumentType</code>, <code>Entity</code>, and + * <code>Notation</code> nodes is implementation dependent. + * @param deep If <code>true</code>, recursively clone the subtree under + * the specified node; if <code>false</code>, clone only the node + * itself (and its attributes, if it is an <code>Element</code>). + * @return The duplicate node. + */ + public Node cloneNode(boolean deep); + + /** + * Puts all <code>Text</code> nodes in the full depth of the sub-tree + * underneath this <code>Node</code>, including attribute nodes, into a + * "normal" form where only structure (e.g., elements, comments, + * processing instructions, CDATA sections, and entity references) + * separates <code>Text</code> nodes, i.e., there are neither adjacent + * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can + * be used to ensure that the DOM view of a document is the same as if + * it were saved and re-loaded, and is useful when operations (such as + * XPointer lookups) that depend on a particular document tree + * structure are to be used.In cases where the document contains + * <code>CDATASections</code>, the normalize operation alone may not be + * sufficient, since XPointers do not differentiate between + * <code>Text</code> nodes and <code>CDATASection</code> nodes. + * @version DOM Level 2 + */ + public void normalize(); + + /** + * Tests whether the DOM implementation implements a specific feature and + * that feature is supported by this node. + * @param feature The name of the feature to test. This is the same name + * which can be passed to the method <code>hasFeature</code> on + * <code>DOMImplementation</code>. + * @param version This is the version number of the feature to test. In + * Level 2, version 1, this is the string "2.0". If the version is not + * specified, supporting any version of the feature will cause the + * method to return <code>true</code>. + * @return Returns <code>true</code> if the specified feature is + * supported on this node, <code>false</code> otherwise. + * @since DOM Level 2 + */ + public boolean isSupported(String feature, + String version); + + /** + * The namespace URI of this node, or <code>null</code> if it is + * unspecified. + * <br>This is not a computed value that is the result of a namespace + * lookup based on an examination of the namespace declarations in + * scope. It is merely the namespace URI given at creation time. + * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and + * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 + * method, such as <code>createElement</code> from the + * <code>Document</code> interface, this is always <code>null</code>.Per + * the Namespaces in XML Specification an attribute does not inherit + * its namespace from the element it is attached to. If an attribute is + * not explicitly given a namespace, it simply has no namespace. + * + * @return the namespace URI, if any. + * + * @since DOM Level 2 + */ + public String getNamespaceURI(); + + /** + * Returns the namespace prefix of this node, or <code>null</code> if it is + * unspecified. + * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and + * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 + * method, such as <code>createElement</code> from the + * <code>Document</code> interface, this is always <code>null</code>. + * + * @return the namespace prefix, if any. + * + * @exception DOMException + * @since DOM Level 2 + */ + public String getPrefix(); + + /** + * Sets the namespace prefix of this node. + * <br>Note that setting this attribute, when permitted, changes the + * <code>nodeName</code> attribute, which holds the qualified name, as + * well as the <code>tagName</code> and <code>name</code> attributes of + * the <code>Element</code> and <code>Attr</code> interfaces, when + * applicable. + * <br>Note also that changing the prefix of an attribute that is known to + * have a default value, does not make a new attribute with the default + * value and the original prefix appear, since the + * <code>namespaceURI</code> and <code>localName</code> do not change. + * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and + * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 + * method, such as <code>createElement</code> from the + * <code>Document</code> interface, this is always <code>null</code>. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified prefix contains an + * illegal character. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + * <br>NAMESPACE_ERR: Raised if the specified <code>prefix</code> is + * malformed, if the <code>namespaceURI</code> of this node is + * <code>null</code>, if the specified prefix is "xml" and the + * <code>namespaceURI</code> of this node is different from " + * http://www.w3.org/XML/1998/namespace", if this node is an attribute + * and the specified prefix is "xmlns" and the + * <code>namespaceURI</code> of this node is different from " + * http://www.w3.org/2000/xmlns/", or if this node is an attribute and + * the <code>qualifiedName</code> of this node is "xmlns" . + * + * @param prefix the new namespace prefix. + * + * @since DOM Level 2 + */ + public void setPrefix(String prefix) + throws DOMException; + + /** + * Returns the local part of the qualified name of this node. + * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and + * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 + * method, such as <code>createElement</code> from the + * <code>Document</code> interface, this is always <code>null</code>. + * + * @return the local name, if any. + * + * @since DOM Level 2 + */ + public String getLocalName(); + + /** + * Returns whether this node (if it is an element) has any attributes. + * @return <code>true</code> if this node has any attributes, + * <code>false</code> otherwise. + * @since DOM Level 2 + */ + public boolean hasAttributes(); + +} diff --git a/xml/src/main/java/org/w3c/dom/NodeList.java b/xml/src/main/java/org/w3c/dom/NodeList.java new file mode 100644 index 0000000..0dbadda --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/NodeList.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>NodeList</code> interface provides the abstraction of an ordered + * collection of nodes, without defining or constraining how this collection + * is implemented. <code>NodeList</code> objects in the DOM are live. + * <p>The items in the <code>NodeList</code> are accessible via an integral + * index, starting from 0. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface NodeList { + /** + * Returns the <code>index</code>th item in the collection. If + * <code>index</code> is greater than or equal to the number of nodes in + * the list, this returns <code>null</code>. + * @param index Index into the collection. + * @return The node at the <code>index</code>th position in the + * <code>NodeList</code>, or <code>null</code> if that is not a valid + * index. + */ + public Node item(int index); + + /** + * The number of nodes in the list. The range of valid child node indices + * is 0 to <code>length-1</code> inclusive. + * + * @return the length of the list. + */ + public int getLength(); + +} diff --git a/xml/src/main/java/org/w3c/dom/Notation.java b/xml/src/main/java/org/w3c/dom/Notation.java new file mode 100644 index 0000000..00c702f --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Notation.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * This interface represents a notation declared in the DTD. A notation either + * declares, by name, the format of an unparsed entity (see section 4.7 of + * the XML 1.0 specification ), or is used for formal declaration of + * processing instruction targets (see section 2.6 of the XML 1.0 + * specification ). The <code>nodeName</code> attribute inherited from + * <code>Node</code> is set to the declared name of the notation. + * <p>The DOM Level 1 does not support editing <code>Notation</code> nodes; + * they are therefore readonly. + * <p>A <code>Notation</code> node does not have any parent. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Notation extends Node { + /** + * The public identifier of this notation. If the public identifier was + * not specified, this is <code>null</code>. + * + * @return the public identifier of this notation, if any. + */ + public String getPublicId(); + + /** + * The system identifier of this notation. If the system identifier was + * not specified, this is <code>null</code>. + * + * @return the system identifier of this notation, if any. + */ + public String getSystemId(); + +} diff --git a/xml/src/main/java/org/w3c/dom/ProcessingInstruction.java b/xml/src/main/java/org/w3c/dom/ProcessingInstruction.java new file mode 100644 index 0000000..a702aa6 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/ProcessingInstruction.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +//BEGIN android-note +//Filled some gaps in the documentation and refactored parts of the existing +//documentation to make the Doclet happy. +//END android-note + +/** + * The <code>ProcessingInstruction</code> interface represents a "processing + * instruction", used in XML as a way to keep processor-specific information + * in the text of the document. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface ProcessingInstruction extends Node { + /** + * The target of this processing instruction. XML defines this as being + * the first token following the markup that begins the processing + * instruction. + * + * @return the target of this processing instruction. + */ + public String getTarget(); + + /** + * Returns the content of this processing instruction. This is from the + * first non white space character after the target to the character + * immediately preceding the <code>?></code>. + * + * @return the data of this processing instruction. + */ + public String getData(); + + /** + * Sets the content of this processing instruction. This is from the first + * non white space character after the target to the character immediately + * preceding the <code>?></code>. + * + * @param data the new data of the processing instruction. + * + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly. + */ + public void setData(String data) + throws DOMException; + +} diff --git a/xml/src/main/java/org/w3c/dom/Text.java b/xml/src/main/java/org/w3c/dom/Text.java new file mode 100644 index 0000000..4d1746b --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/Text.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +package org.w3c.dom; + +// BEGIN android-note +// Cleaned up @param tags that seemed to be missing spaces between +// the parameter name and the start of the description. +// END android-note + +/** + * The <code>Text</code> interface inherits from <code>CharacterData</code> + * and represents the textual content (termed character data in XML) of an + * <code>Element</code> or <code>Attr</code>. If there is no markup inside + * an element's content, the text is contained in a single object + * implementing the <code>Text</code> interface that is the only child of + * the element. If there is markup, it is parsed into the information items + * (elements, comments, etc.) and <code>Text</code> nodes that form the list + * of children of the element. + * <p>When a document is first made available via the DOM, there is only one + * <code>Text</code> node for each block of text. Users may create adjacent + * <code>Text</code> nodes that represent the contents of a given element + * without any intervening markup, but should be aware that there is no way + * to represent the separations between these nodes in XML or HTML, so they + * will not (in general) persist between DOM editing sessions. The + * <code>normalize()</code> method on <code>Node</code> merges any such + * adjacent <code>Text</code> objects into a single node for each block of + * text. + * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113'>Document Object Model (DOM) Level 2 Core Specification</a>. + */ +public interface Text extends CharacterData { + /** + * Breaks this node into two nodes at the specified <code>offset</code>, + * keeping both in the tree as siblings. After being split, this node + * will contain all the content up to the <code>offset</code> point. A + * new node of the same type, which contains all the content at and + * after the <code>offset</code> point, is returned. If the original + * node had a parent node, the new node is inserted as the next sibling + * of the original node. When the <code>offset</code> is equal to the + * length of this node, the new node has no data. + * @param offset The 16-bit unit offset at which to split, starting from + * <code>0</code>. + * @return The new node, of the same type as this node. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if the specified offset is negative or greater + * than the number of 16-bit units in <code>data</code>. + * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + */ + public Text splitText(int offset) + throws DOMException; + +} diff --git a/xml/src/main/java/org/w3c/dom/package.html b/xml/src/main/java/org/w3c/dom/package.html new file mode 100644 index 0000000..8189944 --- /dev/null +++ b/xml/src/main/java/org/w3c/dom/package.html @@ -0,0 +1,12 @@ +<html> + <body> + <p> + Provides the official W3C Java bindings for the Document Object Model, + level 2 core. XML documents returned by + {@link javax.xml.parsers.DocumentBuilder} are accessed and manipulated + through these interfaces. + </p> + + @since Android 1.0 + </body> +</html>
\ No newline at end of file diff --git a/xml/src/main/java/org/xml/sax/AttributeList.java b/xml/src/main/java/org/xml/sax/AttributeList.java new file mode 100644 index 0000000..9285eac --- /dev/null +++ b/xml/src/main/java/org/xml/sax/AttributeList.java @@ -0,0 +1,193 @@ +// SAX Attribute List Interface. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: AttributeList.java,v 1.7 2004/04/26 17:34:34 dmegginson Exp $ + +package org.xml.sax; + +/** + * Interface for an element's attribute specifications. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This is the original SAX1 interface for reporting an element's + * attributes. Unlike the new {@link org.xml.sax.Attributes Attributes} + * interface, it does not support Namespace-related information.</p> + * + * <p>When an attribute list is supplied as part of a + * {@link org.xml.sax.DocumentHandler#startElement startElement} + * event, the list will return valid results only during the + * scope of the event; once the event handler returns control + * to the parser, the attribute list is invalid. To save a + * persistent copy of the attribute list, use the SAX1 + * {@link org.xml.sax.helpers.AttributeListImpl AttributeListImpl} + * helper class.</p> + * + * <p>An attribute list includes only attributes that have been + * specified or defaulted: #IMPLIED attributes will not be included.</p> + * + * <p>There are two ways for the SAX application to obtain information + * from the AttributeList. First, it can iterate through the entire + * list:</p> + * + * <pre> + * public void startElement (String name, AttributeList atts) { + * for (int i = 0; i < atts.getLength(); i++) { + * String name = atts.getName(i); + * String type = atts.getType(i); + * String value = atts.getValue(i); + * [...] + * } + * } + * </pre> + * + * <p>(Note that the result of getLength() will be zero if there + * are no attributes.) + * + * <p>As an alternative, the application can request the value or + * type of specific attributes:</p> + * + * <pre> + * public void startElement (String name, AttributeList atts) { + * String identifier = atts.getValue("id"); + * String label = atts.getValue("label"); + * [...] + * } + * </pre> + * + * @deprecated This interface has been replaced by the SAX2 + * {@link org.xml.sax.Attributes Attributes} + * interface, which includes Namespace support. + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.DocumentHandler#startElement startElement + * @see org.xml.sax.helpers.AttributeListImpl AttributeListImpl + */ +public interface AttributeList { + + + //////////////////////////////////////////////////////////////////// + // Iteration methods. + //////////////////////////////////////////////////////////////////// + + + /** + * Return the number of attributes in this list. + * + * <p>The SAX parser may provide attributes in any + * arbitrary order, regardless of the order in which they were + * declared or specified. The number of attributes may be + * zero.</p> + * + * @return The number of attributes in the list. + */ + public abstract int getLength (); + + + /** + * Return the name of an attribute in this list (by position). + * + * <p>The names must be unique: the SAX parser shall not include the + * same attribute twice. Attributes without values (those declared + * #IMPLIED without a value specified in the start tag) will be + * omitted from the list.</p> + * + * <p>If the attribute name has a namespace prefix, the prefix + * will still be attached.</p> + * + * @param i The index of the attribute in the list (starting at 0). + * @return The name of the indexed attribute, or null + * if the index is out of range. + * @see #getLength + */ + public abstract String getName (int i); + + + /** + * Return the type of an attribute in the list (by position). + * + * <p>The attribute type is one of the strings "CDATA", "ID", + * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", + * or "NOTATION" (always in upper case).</p> + * + * <p>If the parser has not read a declaration for the attribute, + * or if the parser does not report attribute types, then it must + * return the value "CDATA" as stated in the XML 1.0 Recommentation + * (clause 3.3.3, "Attribute-Value Normalization").</p> + * + * <p>For an enumerated attribute that is not a notation, the + * parser will report the type as "NMTOKEN".</p> + * + * @param i The index of the attribute in the list (starting at 0). + * @return The attribute type as a string, or + * null if the index is out of range. + * @see #getLength + * @see #getType(java.lang.String) + */ + public abstract String getType (int i); + + + /** + * Return the value of an attribute in the list (by position). + * + * <p>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> + * + * @param i The index of the attribute in the list (starting at 0). + * @return The attribute value as a string, or + * null if the index is out of range. + * @see #getLength + * @see #getValue(java.lang.String) + */ + public abstract String getValue (int i); + + + + //////////////////////////////////////////////////////////////////// + // Lookup methods. + //////////////////////////////////////////////////////////////////// + + + /** + * Return the type of an attribute in the list (by name). + * + * <p>The return value is the same as the return value for + * getType(int).</p> + * + * <p>If the attribute name has a namespace prefix in the document, + * the application must include the prefix here.</p> + * + * @param name The name of the attribute. + * @return The attribute type as a string, or null if no + * such attribute exists. + * @see #getType(int) + */ + public abstract String getType (String name); + + + /** + * Return the value of an attribute in the list (by name). + * + * <p>The return value is the same as the return value for + * getValue(int).</p> + * + * <p>If the attribute name has a namespace prefix in the document, + * the application must include the prefix here.</p> + * + * @param name the name of the attribute to return + * @return The attribute value as a string, or null if + * no such attribute exists. + * @see #getValue(int) + */ + public abstract String getValue (String name); + +} + +// end of AttributeList.java diff --git a/xml/src/main/java/org/xml/sax/Attributes.java b/xml/src/main/java/org/xml/sax/Attributes.java new file mode 100644 index 0000000..b25432d --- /dev/null +++ b/xml/src/main/java/org/xml/sax/Attributes.java @@ -0,0 +1,257 @@ +// Attributes.java - attribute list with Namespace support +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the public domain. +// $Id: Attributes.java,v 1.13 2004/03/18 12:28:05 dmegginson Exp $ + +package org.xml.sax; + + +/** + * Interface for a list of XML attributes. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This interface allows access to a list of attributes in + * three different ways:</p> + * + * <ol> + * <li>by attribute index;</li> + * <li>by Namespace-qualified name; or</li> + * <li>by qualified (prefixed) name.</li> + * </ol> + * + * <p>The list will not contain attributes that were declared + * #IMPLIED but not specified in the start tag. It will also not + * contain attributes used as Namespace declarations (xmlns*) unless + * the <code>http://xml.org/sax/features/namespace-prefixes</code> + * feature is set to <var>true</var> (it is <var>false</var> by + * default). + * Because SAX2 conforms to the original "Namespaces in XML" + * recommendation, it normally does not + * give namespace declaration attributes a namespace URI. + * </p> + * + * <p>Some SAX2 parsers may support using an optional feature flag + * (<code>http://xml.org/sax/features/xmlns-uris</code>) to request + * that those attributes be given URIs, conforming to a later + * backwards-incompatible revision of that recommendation. (The + * attribute's "local name" will be the prefix, or "xmlns" when + * defining a default element namespace.) For portability, handler + * code should always resolve that conflict, rather than requiring + * parsers that can change the setting of that feature flag. </p> + * + * <p>If the namespace-prefixes feature (see above) is + * <var>false</var>, access by qualified name may not be available; if + * the <code>http://xml.org/sax/features/namespaces</code> feature is + * <var>false</var>, access by Namespace-qualified names may not be + * available.</p> + * + * <p>This interface replaces the now-deprecated SAX1 {@link + * org.xml.sax.AttributeList AttributeList} interface, which does not + * contain Namespace support. In addition to Namespace support, it + * adds the <var>getIndex</var> methods (below).</p> + * + * <p>The order of attributes in the list is unspecified, and will + * vary from implementation to implementation.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.helpers.AttributesImpl + * @see org.xml.sax.ext.DeclHandler#attributeDecl + */ +public interface Attributes +{ + + + //////////////////////////////////////////////////////////////////// + // Indexed access. + //////////////////////////////////////////////////////////////////// + + + /** + * Return the number of attributes in the list. + * + * <p>Once you know the number of attributes, you can iterate + * through the list.</p> + * + * @return The number of attributes in the list. + * @see #getURI(int) + * @see #getLocalName(int) + * @see #getQName(int) + * @see #getType(int) + * @see #getValue(int) + */ + public abstract int getLength (); + + + /** + * Look up an attribute's Namespace URI by index. + * + * @param index The attribute index (zero-based). + * @return The Namespace URI, or the empty string if none + * is available, or null if the index is out of + * range. + * @see #getLength + */ + public abstract String getURI (int index); + + + /** + * Look up an attribute's local name by index. + * + * @param index The attribute index (zero-based). + * @return The local name, or the empty string if Namespace + * processing is not being performed, or null + * if the index is out of range. + * @see #getLength + */ + public abstract String getLocalName (int index); + + + /** + * Look up an attribute's XML qualified (prefixed) name by index. + * + * @param index The attribute index (zero-based). + * @return The XML qualified name, or the empty string + * if none is available, or null if the index + * is out of range. + * @see #getLength + */ + public abstract String getQName (int index); + + + /** + * Look up an attribute's type by index. + * + * <p>The attribute type is one of the strings "CDATA", "ID", + * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", + * or "NOTATION" (always in upper case).</p> + * + * <p>If the parser has not read a declaration for the attribute, + * or if the parser does not report attribute types, then it must + * return the value "CDATA" as stated in the XML 1.0 Recommendation + * (clause 3.3.3, "Attribute-Value Normalization").</p> + * + * <p>For an enumerated attribute that is not a notation, the + * parser will report the type as "NMTOKEN".</p> + * + * @param index The attribute index (zero-based). + * @return The attribute's type as a string, or null if the + * index is out of range. + * @see #getLength + */ + public abstract String getType (int index); + + + /** + * Look up an attribute's value by index. + * + * <p>If the attribute value is a list of tokens (IDREFS, + * ENTITIES, or NMTOKENS), the tokens will be concatenated + * into a single string with each token separated by a + * single space.</p> + * + * @param index The attribute index (zero-based). + * @return The attribute's value as a string, or null if the + * index is out of range. + * @see #getLength + */ + public abstract String getValue (int index); + + + + //////////////////////////////////////////////////////////////////// + // Name-based query. + //////////////////////////////////////////////////////////////////// + + + /** + * Look up the index of an attribute by Namespace name. + * + * @param uri The Namespace URI, or the empty string if + * the name has no Namespace URI. + * @param localName The attribute's local name. + * @return The index of the attribute, or -1 if it does not + * appear in the list. + */ + public int getIndex (String uri, String localName); + + + /** + * Look up the index of an attribute by XML qualified (prefixed) name. + * + * @param qName The qualified (prefixed) name. + * @return The index of the attribute, or -1 if it does not + * appear in the list. + */ + public int getIndex (String qName); + + + /** + * Look up an attribute's type by Namespace name. + * + * <p>See {@link #getType(int) getType(int)} for a description + * of the possible types.</p> + * + * @param uri The Namespace URI, or the empty String if the + * name has no Namespace URI. + * @param localName The local name of the attribute. + * @return The attribute type as a string, or null if the + * attribute is not in the list or if Namespace + * processing is not being performed. + */ + public abstract String getType (String uri, String localName); + + + /** + * Look up an attribute's type by XML qualified (prefixed) name. + * + * <p>See {@link #getType(int) getType(int)} for a description + * of the possible types.</p> + * + * @param qName The XML qualified name. + * @return The attribute type as a string, or null if the + * attribute is not in the list or if qualified names + * are not available. + */ + public abstract String getType (String qName); + + + /** + * Look up an attribute's value by Namespace name. + * + * <p>See {@link #getValue(int) getValue(int)} for a description + * of the possible values.</p> + * + * @param uri The Namespace URI, or the empty String if the + * name has no Namespace URI. + * @param localName The local name of the attribute. + * @return The attribute value as a string, or null if the + * attribute is not in the list. + */ + public abstract String getValue (String uri, String localName); + + + /** + * Look up an attribute's value by XML qualified (prefixed) name. + * + * <p>See {@link #getValue(int) getValue(int)} for a description + * of the possible values.</p> + * + * @param qName The XML qualified name. + * @return The attribute value as a string, or null if the + * attribute is not in the list or if qualified names + * are not available. + */ + public abstract String getValue (String qName); + +} + +// end of Attributes.java diff --git a/xml/src/main/java/org/xml/sax/ContentHandler.java b/xml/src/main/java/org/xml/sax/ContentHandler.java new file mode 100644 index 0000000..db66c0d --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ContentHandler.java @@ -0,0 +1,419 @@ +// ContentHandler.java - handle main document content. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the public domain. +// $Id: ContentHandler.java,v 1.13 2004/04/26 17:50:49 dmegginson Exp $ + +package org.xml.sax; + + +/** + * Receive notification of the logical content of a document. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This is the main interface that most SAX applications + * implement: if the application needs to be informed of basic parsing + * events, it implements this interface and registers an instance with + * the SAX parser using the {@link org.xml.sax.XMLReader#setContentHandler + * setContentHandler} method. The parser uses the instance to report + * basic document-related events like the start and end of elements + * and character data.</p> + * + * <p>The order of events in this interface is very important, and + * mirrors the order of information in the document itself. For + * example, all of an element's content (character data, processing + * instructions, and/or subelements) will appear, in order, between + * the startElement event and the corresponding endElement event.</p> + * + * <p>This interface is similar to the now-deprecated SAX 1.0 + * DocumentHandler interface, but it adds support for Namespaces + * and for reporting skipped entities (in non-validating XML + * processors).</p> + * + * <p>Implementors should note that there is also a + * <code>ContentHandler</code> class in the <code>java.net</code> + * package; that means that it's probably a bad idea to do</p> + * + * <pre>import java.net.*; + * import org.xml.sax.*; + * </pre> + * + * <p>In fact, "import ...*" is usually a sign of sloppy programming + * anyway, so the user should consider this a feature rather than a + * bug.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1+ (sax2r3pre1) + * @see org.xml.sax.XMLReader + * @see org.xml.sax.DTDHandler + * @see org.xml.sax.ErrorHandler + */ +public interface ContentHandler +{ + + /** + * Receive an object for locating the origin of SAX document events. + * + * <p>SAX parsers are strongly encouraged (though not absolutely + * required) to supply a locator: if it does so, it must supply + * the locator to the application by invoking this method before + * invoking any of the other methods in the ContentHandler + * interface.</p> + * + * <p>The locator allows the application to determine the end + * position of any document-related event, even if the parser is + * not reporting an error. Typically, the application will + * use this information for reporting its own errors (such as + * character content that does not match an application's + * business rules). The information returned by the locator + * is probably not sufficient for use with a search engine.</p> + * + * <p>Note that the locator will return correct information only + * during the invocation SAX event callbacks after + * {@link #startDocument startDocument} returns and before + * {@link #endDocument endDocument} is called. The + * application should not attempt to use it at any other time.</p> + * + * @param locator an object that can return the location of + * any SAX document event + * @see org.xml.sax.Locator + */ + public void setDocumentLocator (Locator locator); + + + /** + * Receive notification of the beginning of a document. + * + * <p>The SAX parser will invoke this method only once, before any + * other event callbacks (except for {@link #setDocumentLocator + * setDocumentLocator}).</p> + * + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + * @see #endDocument + */ + public void startDocument () + throws SAXException; + + + /** + * Receive notification of the end of a document. + * + * <p><strong>There is an apparent contradiction between the + * documentation for this method and the documentation for {@link + * org.xml.sax.ErrorHandler#fatalError}. Until this ambiguity is + * resolved in a future major release, clients should make no + * assumptions about whether endDocument() will or will not be + * invoked when the parser has reported a fatalError() or thrown + * an exception.</strong></p> + * + * <p>The SAX parser will invoke this method only once, and it will + * be the last method invoked during the parse. The parser shall + * not invoke this method until it has either abandoned parsing + * (because of an unrecoverable error) or reached the end of + * input.</p> + * + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + * @see #startDocument + */ + public void endDocument() + throws SAXException; + + + /** + * Begin the scope of a prefix-URI Namespace mapping. + * + * <p>The information from this event is not necessary for + * normal Namespace processing: the SAX XML reader will + * automatically replace prefixes for element and attribute + * names when the <code>http://xml.org/sax/features/namespaces</code> + * feature is <var>true</var> (the default).</p> + * + * <p>There are cases, however, when applications need to + * use prefixes in character data or in attribute values, + * where they cannot safely be expanded automatically; the + * start/endPrefixMapping event supplies the information + * to the application to expand prefixes in those contexts + * itself, if necessary.</p> + * + * <p>Note that start/endPrefixMapping events are not + * guaranteed to be properly nested relative to each other: + * all startPrefixMapping events will occur immediately before the + * corresponding {@link #startElement startElement} event, + * and all {@link #endPrefixMapping endPrefixMapping} + * events will occur immediately after the corresponding + * {@link #endElement endElement} event, + * but their order is not otherwise + * guaranteed.</p> + * + * <p>There should never be start/endPrefixMapping events for the + * "xml" prefix, since it is predeclared and immutable.</p> + * + * @param prefix the Namespace prefix being declared. + * An empty string is used for the default element namespace, + * which has no prefix. + * @param uri the Namespace URI the prefix is mapped to + * @throws org.xml.sax.SAXException the client may throw + * an exception during processing + * @see #endPrefixMapping + * @see #startElement + */ + public void startPrefixMapping (String prefix, String uri) + throws SAXException; + + + /** + * End the scope of a prefix-URI mapping. + * + * <p>See {@link #startPrefixMapping startPrefixMapping} for + * details. These events will always occur immediately after the + * corresponding {@link #endElement endElement} event, but the order of + * {@link #endPrefixMapping endPrefixMapping} events is not otherwise + * guaranteed.</p> + * + * @param prefix the prefix that was being mapped. + * This is the empty string when a default mapping scope ends. + * @throws org.xml.sax.SAXException the client may throw + * an exception during processing + * @see #startPrefixMapping + * @see #endElement + */ + public void endPrefixMapping (String prefix) + throws SAXException; + + + /** + * Receive notification of the beginning of an element. + * + * <p>The Parser will invoke this method at the beginning of every + * element in the XML document; there will be a corresponding + * {@link #endElement endElement} event for every startElement event + * (even when the element is empty). All of the element's content will be + * reported, in order, before the corresponding endElement + * event.</p> + * + * <p>This event allows up to three name components for each + * element:</p> + * + * <ol> + * <li>the Namespace URI;</li> + * <li>the local name; and</li> + * <li>the qualified (prefixed) name.</li> + * </ol> + * + * <p>Any or all of these may be provided, depending on the + * values of the <var>http://xml.org/sax/features/namespaces</var> + * and the <var>http://xml.org/sax/features/namespace-prefixes</var> + * properties:</p> + * + * <ul> + * <li>the Namespace URI and local name are required when + * the namespaces property is <var>true</var> (the default), and are + * optional when the namespaces property is <var>false</var> (if one is + * specified, both must be);</li> + * <li>the qualified name is required when the namespace-prefixes property + * is <var>true</var>, and is optional when the namespace-prefixes property + * is <var>false</var> (the default).</li> + * </ul> + * + * <p>Note that the attribute list provided will contain only + * attributes with explicit values (specified or defaulted): + * #IMPLIED attributes will be omitted. The attribute list + * will contain attributes used for Namespace declarations + * (xmlns* attributes) only if the + * <code>http://xml.org/sax/features/namespace-prefixes</code> + * property is true (it is false by default, and support for a + * true value is optional).</p> + * + * <p>Like {@link #characters characters()}, attribute values may have + * characters that need more than one <code>char</code> value. </p> + * + * @param uri the Namespace URI, or the empty string if the + * element has no Namespace URI or if Namespace + * processing is not being performed + * @param localName the local name (without prefix), or the + * empty string if Namespace processing is not being + * performed + * @param qName the qualified name (with prefix), or the + * empty string if qualified names are not available + * @param atts the attributes attached to the element. If + * there are no attributes, it shall be an empty + * Attributes object. The value of this object after + * startElement returns is undefined + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + * @see #endElement + * @see org.xml.sax.Attributes + * @see org.xml.sax.helpers.AttributesImpl + */ + public void startElement (String uri, String localName, + String qName, Attributes atts) + throws SAXException; + + + /** + * Receive notification of the end of an element. + * + * <p>The SAX parser will invoke this method at the end of every + * element in the XML document; there will be a corresponding + * {@link #startElement startElement} event for every endElement + * event (even when the element is empty).</p> + * + * <p>For information on the names, see startElement.</p> + * + * @param uri the Namespace URI, or the empty string if the + * element has no Namespace URI or if Namespace + * processing is not being performed + * @param localName the local name (without prefix), or the + * empty string if Namespace processing is not being + * performed + * @param qName the qualified XML name (with prefix), or the + * empty string if qualified names are not available + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + */ + public void endElement (String uri, String localName, + String qName) + throws SAXException; + + + /** + * Receive notification of character data. + * + * <p>The Parser will call this method to report each chunk of + * character data. SAX parsers may return all contiguous character + * data in a single chunk, or they may split it into several + * chunks; however, all of the characters in any single event + * must come from the same external entity so that the Locator + * provides useful information.</p> + * + * <p>The application must not attempt to read from the array + * outside of the specified range.</p> + * + * <p>Individual characters may consist of more than one Java + * <code>char</code> value. There are two important cases where this + * happens, because characters can't be represented in just sixteen bits. + * In one case, characters are represented in a <em>Surrogate Pair</em>, + * using two special Unicode values. Such characters are in the so-called + * "Astral Planes", with a code point above U+FFFF. A second case involves + * composite characters, such as a base character combining with one or + * more accent characters. </p> + * + * <p> Your code should not assume that algorithms using + * <code>char</code>-at-a-time idioms will be working in character + * units; in some cases they will split characters. This is relevant + * wherever XML permits arbitrary characters, such as attribute values, + * processing instruction data, and comments as well as in data reported + * from this method. It's also generally relevant whenever Java code + * manipulates internationalized text; the issue isn't unique to XML.</p> + * + * <p>Note that some parsers will report whitespace in element + * content using the {@link #ignorableWhitespace ignorableWhitespace} + * method rather than this one (validating parsers <em>must</em> + * do so).</p> + * + * @param ch the characters from the XML document + * @param start the start position in the array + * @param length the number of characters to read from the array + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + * @see #ignorableWhitespace + * @see org.xml.sax.Locator + */ + public void characters (char ch[], int start, int length) + throws SAXException; + + + /** + * Receive notification of ignorable whitespace in element content. + * + * <p>Validating Parsers must use this method to report each chunk + * of whitespace in element content (see the W3C XML 1.0 + * recommendation, section 2.10): non-validating parsers may also + * use this method if they are capable of parsing and using + * content models.</p> + * + * <p>SAX parsers may return all contiguous whitespace in a single + * chunk, or they may split it into several chunks; however, all of + * the characters in any single event must come from the same + * external entity, so that the Locator provides useful + * information.</p> + * + * <p>The application must not attempt to read from the array + * outside of the specified range.</p> + * + * @param ch the characters from the XML document + * @param start the start position in the array + * @param length the number of characters to read from the array + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + * @see #characters + */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException; + + + /** + * Receive notification of a processing instruction. + * + * <p>The Parser will invoke this method once for each processing + * instruction found: note that processing instructions may occur + * before or after the main document element.</p> + * + * <p>A SAX parser must never report an XML declaration (XML 1.0, + * section 2.8) or a text declaration (XML 1.0, section 4.3.1) + * using this method.</p> + * + * <p>Like {@link #characters characters()}, processing instruction + * data may have characters that need more than one <code>char</code> + * value. </p> + * + * @param target the processing instruction target + * @param data the processing instruction data, or null if + * none was supplied. The data does not include any + * whitespace separating it from the target + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + */ + public void processingInstruction (String target, String data) + throws SAXException; + + + /** + * Receive notification of a skipped entity. + * This is not called for entity references within markup constructs + * such as element start tags or markup declarations. (The XML + * recommendation requires reporting skipped external entities. + * SAX also reports internal entity expansion/non-expansion, except + * within markup constructs.) + * + * <p>The Parser will invoke this method each time the entity is + * skipped. Non-validating processors may skip entities if they + * have not seen the declarations (because, for example, the + * entity was declared in an external DTD subset). All processors + * may skip external entities, depending on the values of the + * <code>http://xml.org/sax/features/external-general-entities</code> + * and the + * <code>http://xml.org/sax/features/external-parameter-entities</code> + * properties.</p> + * + * @param name the name of the skipped entity. If it is a + * parameter entity, the name will begin with '%', and if + * it is the external DTD subset, it will be the string + * "[dtd]" + * @throws org.xml.sax.SAXException any SAX exception, possibly + * wrapping another exception + */ + public void skippedEntity (String name) + throws SAXException; +} + +// end of ContentHandler.java diff --git a/xml/src/main/java/org/xml/sax/DTDHandler.java b/xml/src/main/java/org/xml/sax/DTDHandler.java new file mode 100644 index 0000000..13d5eee --- /dev/null +++ b/xml/src/main/java/org/xml/sax/DTDHandler.java @@ -0,0 +1,117 @@ +// SAX DTD handler. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: DTDHandler.java,v 1.8 2002/01/30 21:13:43 dbrownell Exp $ + +package org.xml.sax; + +/** + * Receive notification of basic DTD-related events. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>If a SAX application needs information about notations and + * unparsed entities, then the application implements this + * interface and registers an instance with the SAX parser using + * the parser's setDTDHandler method. The parser uses the + * instance to report notation and unparsed entity declarations to + * the application.</p> + * + * <p>Note that this interface includes only those DTD events that + * the XML recommendation <em>requires</em> processors to report: + * notation and unparsed entity declarations.</p> + * + * <p>The SAX parser may report these events in any order, regardless + * of the order in which the notations and unparsed entities were + * declared; however, all DTD events must be reported after the + * document handler's startDocument event, and before the first + * startElement event. + * (If the {@link org.xml.sax.ext.LexicalHandler LexicalHandler} is + * used, these events must also be reported before the endDTD event.) + * </p> + * + * <p>It is up to the application to store the information for + * future use (perhaps in a hash table or object tree). + * If the application encounters attributes of type "NOTATION", + * "ENTITY", or "ENTITIES", it can use the information that it + * obtained through this interface to find the entity and/or + * notation corresponding with the attribute value.</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.XMLReader#setDTDHandler + */ +public interface DTDHandler { + + + /** + * Receive notification of a notation declaration event. + * + * <p>It is up to the application to record the notation for later + * reference, if necessary; + * notations may appear as attribute values and in unparsed entity + * declarations, and are sometime used with processing instruction + * target names.</p> + * + * <p>At least one of publicId and systemId must be non-null. + * If a system identifier is present, and it is a URL, the SAX + * parser must resolve it fully before passing it to the + * application through this event.</p> + * + * <p>There is no guarantee that the notation declaration will be + * reported before any unparsed entities that use it.</p> + * + * @param name The notation name. + * @param publicId The notation's public identifier, or null if + * none was given. + * @param systemId The notation's system identifier, or null if + * none was given. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see #unparsedEntityDecl + * @see org.xml.sax.Attributes + */ + public abstract void notationDecl (String name, + String publicId, + String systemId) + throws SAXException; + + + /** + * Receive notification of an unparsed entity declaration event. + * + * <p>Note that the notation name corresponds to a notation + * reported by the {@link #notationDecl notationDecl} event. + * It is up to the application to record the entity for later + * reference, if necessary; + * unparsed entities may appear as attribute values. + * </p> + * + * <p>If the system identifier is a URL, the parser must resolve it + * fully before passing it to the application.</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @param name The unparsed entity's name. + * @param publicId The entity's public identifier, or null if none + * was given. + * @param systemId The entity's system identifier. + * @param notationName The name of the associated notation. + * @see #notationDecl + * @see org.xml.sax.Attributes + */ + public abstract void unparsedEntityDecl (String name, + String publicId, + String systemId, + String notationName) + throws SAXException; + +} + +// end of DTDHandler.java diff --git a/xml/src/main/java/org/xml/sax/DocumentHandler.java b/xml/src/main/java/org/xml/sax/DocumentHandler.java new file mode 100644 index 0000000..500fe4c --- /dev/null +++ b/xml/src/main/java/org/xml/sax/DocumentHandler.java @@ -0,0 +1,232 @@ +// SAX document handler. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: DocumentHandler.java,v 1.6 2002/01/30 21:13:43 dbrownell Exp $ + +package org.xml.sax; + +/** + * Receive notification of general document events. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This was the main event-handling interface for SAX1; in + * SAX2, it has been replaced by {@link org.xml.sax.ContentHandler + * ContentHandler}, which provides Namespace support and reporting + * of skipped entities. This interface is included in SAX2 only + * to support legacy SAX1 applications.</p> + * + * <p>The order of events in this interface is very important, and + * mirrors the order of information in the document itself. For + * example, all of an element's content (character data, processing + * instructions, and/or subelements) will appear, in order, between + * the startElement event and the corresponding endElement event.</p> + * + * <p>Application writers who do not want to implement the entire + * interface can derive a class from HandlerBase, which implements + * the default functionality; parser writers can instantiate + * HandlerBase to obtain a default handler. The application can find + * the location of any document event using the Locator interface + * supplied by the Parser through the setDocumentLocator method.</p> + * + * @deprecated This interface has been replaced by the SAX2 + * {@link org.xml.sax.ContentHandler ContentHandler} + * interface, which includes Namespace support. + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.Parser#setDocumentHandler + * @see org.xml.sax.Locator + * @see org.xml.sax.HandlerBase + */ +public interface DocumentHandler { + + + /** + * Receive an object for locating the origin of SAX document events. + * + * <p>SAX parsers are strongly encouraged (though not absolutely + * required) to supply a locator: if it does so, it must supply + * the locator to the application by invoking this method before + * invoking any of the other methods in the DocumentHandler + * interface.</p> + * + * <p>The locator allows the application to determine the end + * position of any document-related event, even if the parser is + * not reporting an error. Typically, the application will + * use this information for reporting its own errors (such as + * character content that does not match an application's + * business rules). The information returned by the locator + * is probably not sufficient for use with a search engine.</p> + * + * <p>Note that the locator will return correct information only + * during the invocation of the events in this interface. The + * application should not attempt to use it at any other time.</p> + * + * @param locator An object that can return the location of + * any SAX document event. + * @see org.xml.sax.Locator + */ + public abstract void setDocumentLocator (Locator locator); + + + /** + * Receive notification of the beginning of a document. + * + * <p>The SAX parser will invoke this method only once, before any + * other methods in this interface or in DTDHandler (except for + * setDocumentLocator).</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + */ + public abstract void startDocument () + throws SAXException; + + + /** + * Receive notification of the end of a document. + * + * <p>The SAX parser will invoke this method only once, and it will + * be the last method invoked during the parse. The parser shall + * not invoke this method until it has either abandoned parsing + * (because of an unrecoverable error) or reached the end of + * input.</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + */ + public abstract void endDocument () + throws SAXException; + + + /** + * Receive notification of the beginning of an element. + * + * <p>The Parser will invoke this method at the beginning of every + * element in the XML document; there will be a corresponding + * endElement() event for every startElement() event (even when the + * element is empty). All of the element's content will be + * reported, in order, before the corresponding endElement() + * event.</p> + * + * <p>If the element name has a namespace prefix, the prefix will + * still be attached. Note that the attribute list provided will + * contain only attributes with explicit values (specified or + * defaulted): #IMPLIED attributes will be omitted.</p> + * + * @param name The element type name. + * @param atts The attributes attached to the element, if any. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see #endElement + * @see org.xml.sax.AttributeList + */ + public abstract void startElement (String name, AttributeList atts) + throws SAXException; + + + /** + * Receive notification of the end of an element. + * + * <p>The SAX parser will invoke this method at the end of every + * element in the XML document; there will be a corresponding + * startElement() event for every endElement() event (even when the + * element is empty).</p> + * + * <p>If the element name has a namespace prefix, the prefix will + * still be attached to the name.</p> + * + * @param name The element type name + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + */ + public abstract void endElement (String name) + throws SAXException; + + + /** + * Receive notification of character data. + * + * <p>The Parser will call this method to report each chunk of + * character data. SAX parsers may return all contiguous character + * data in a single chunk, or they may split it into several + * chunks; however, all of the characters in any single event + * must come from the same external entity, so that the Locator + * provides useful information.</p> + * + * <p>The application must not attempt to read from the array + * outside of the specified range.</p> + * + * <p>Note that some parsers will report whitespace using the + * ignorableWhitespace() method rather than this one (validating + * parsers must do so).</p> + * + * @param ch The characters from the XML document. + * @param start The start position in the array. + * @param length The number of characters to read from the array. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see #ignorableWhitespace + * @see org.xml.sax.Locator + */ + public abstract void characters (char ch[], int start, int length) + throws SAXException; + + + /** + * Receive notification of ignorable whitespace in element content. + * + * <p>Validating Parsers must use this method to report each chunk + * of ignorable whitespace (see the W3C XML 1.0 recommendation, + * section 2.10): non-validating parsers may also use this method + * if they are capable of parsing and using content models.</p> + * + * <p>SAX parsers may return all contiguous whitespace in a single + * chunk, or they may split it into several chunks; however, all of + * the characters in any single event must come from the same + * external entity, so that the Locator provides useful + * information.</p> + * + * <p>The application must not attempt to read from the array + * outside of the specified range.</p> + * + * @param ch The characters from the XML document. + * @param start The start position in the array. + * @param length The number of characters to read from the array. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see #characters + */ + public abstract void ignorableWhitespace (char ch[], int start, int length) + throws SAXException; + + + /** + * Receive notification of a processing instruction. + * + * <p>The Parser will invoke this method once for each processing + * instruction found: note that processing instructions may occur + * before or after the main document element.</p> + * + * <p>A SAX parser should never report an XML declaration (XML 1.0, + * section 2.8) or a text declaration (XML 1.0, section 4.3.1) + * using this method.</p> + * + * @param target The processing instruction target. + * @param data The processing instruction data, or null if + * none was supplied. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + */ + public abstract void processingInstruction (String target, String data) + throws SAXException; + +} + +// end of DocumentHandler.java diff --git a/xml/src/main/java/org/xml/sax/EntityResolver.java b/xml/src/main/java/org/xml/sax/EntityResolver.java new file mode 100644 index 0000000..06ac725 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/EntityResolver.java @@ -0,0 +1,119 @@ +// SAX entity resolver. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: EntityResolver.java,v 1.10 2002/01/30 21:13:44 dbrownell Exp $ + +package org.xml.sax; + +import java.io.IOException; + + +/** + * Basic interface for resolving entities. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>If a SAX application needs to implement customized handling + * for external entities, it must implement this interface and + * register an instance with the SAX driver using the + * {@link org.xml.sax.XMLReader#setEntityResolver setEntityResolver} + * method.</p> + * + * <p>The XML reader will then allow the application to intercept any + * external entities (including the external DTD subset and external + * parameter entities, if any) before including them.</p> + * + * <p>Many SAX applications will not need to implement this interface, + * but it will be especially useful for applications that build + * XML documents from databases or other specialised input sources, + * or for applications that use URI types other than URLs.</p> + * + * <p>The following resolver would provide the application + * with a special character stream for the entity with the system + * identifier "http://www.myhost.com/today":</p> + * + * <pre> + * import org.xml.sax.EntityResolver; + * import org.xml.sax.InputSource; + * + * public class MyResolver implements EntityResolver { + * public InputSource resolveEntity (String publicId, String systemId) + * { + * if (systemId.equals("http://www.myhost.com/today")) { + * // return a special input source + * MyReader reader = new MyReader(); + * return new InputSource(reader); + * } else { + * // use the default behaviour + * return null; + * } + * } + * } + * </pre> + * + * <p>The application can also use this interface to redirect system + * identifiers to local URIs or to look up replacements in a catalog + * (possibly by using the public identifier).</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.XMLReader#setEntityResolver + * @see org.xml.sax.InputSource + */ +public interface EntityResolver { + + + /** + * Allow the application to resolve external entities. + * + * <p>The parser will call this method before opening any external + * entity except the top-level document entity. Such entities include + * the external DTD subset and external parameter entities referenced + * within the DTD (in either case, only if the parser reads external + * parameter entities), and external general entities referenced + * within the document element (if the parser reads external general + * entities). The application may request that the parser locate + * the entity itself, that it use an alternative URI, or that it + * use data provided by the application (as a character or byte + * input stream).</p> + * + * <p>Application writers can use this method to redirect external + * system identifiers to secure and/or local URIs, to look up + * public identifiers in a catalogue, or to read an entity from a + * database or other input source (including, for example, a dialog + * box). Neither XML nor SAX specifies a preferred policy for using + * public or system IDs to resolve resources. However, SAX specifies + * how to interpret any InputSource returned by this method, and that + * if none is returned, then the system ID will be dereferenced as + * a URL. </p> + * + * <p>If the system identifier is a URL, the SAX parser must + * resolve it fully before reporting it to the application.</p> + * + * @param publicId The public identifier of the external entity + * being referenced, or null if none was supplied. + * @param systemId The system identifier of the external entity + * being referenced. + * @return An InputSource object describing the new input source, + * or null to request that the parser open a regular + * URI connection to the system identifier. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @exception java.io.IOException A Java-specific IO exception, + * possibly the result of creating a new InputStream + * or Reader for the InputSource. + * @see org.xml.sax.InputSource + */ + public abstract InputSource resolveEntity (String publicId, + String systemId) + throws SAXException, IOException; + +} + +// end of EntityResolver.java diff --git a/xml/src/main/java/org/xml/sax/ErrorHandler.java b/xml/src/main/java/org/xml/sax/ErrorHandler.java new file mode 100644 index 0000000..e4e4206 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ErrorHandler.java @@ -0,0 +1,139 @@ +// SAX error handler. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: ErrorHandler.java,v 1.10 2004/03/08 13:01:00 dmegginson Exp $ + +package org.xml.sax; + + +/** + * Basic interface for SAX error handlers. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>If a SAX application needs to implement customized error + * handling, it must implement this interface and then register an + * instance with the XML reader using the + * {@link org.xml.sax.XMLReader#setErrorHandler setErrorHandler} + * method. The parser will then report all errors and warnings + * through this interface.</p> + * + * <p><strong>WARNING:</strong> If an application does <em>not</em> + * register an ErrorHandler, XML parsing errors will go unreported, + * except that <em>SAXParseException</em>s will be thrown for fatal errors. + * In order to detect validity errors, an ErrorHandler that does something + * with {@link #error error()} calls must be registered.</p> + * + * <p>For XML processing errors, a SAX driver must use this interface + * in preference to throwing an exception: it is up to the application + * to decide whether to throw an exception for different types of + * errors and warnings. Note, however, that there is no requirement that + * the parser continue to report additional errors after a call to + * {@link #fatalError fatalError}. In other words, a SAX driver class + * may throw an exception after reporting any fatalError. + * Also parsers may throw appropriate exceptions for non-XML errors. + * For example, {@link XMLReader#parse XMLReader.parse()} would throw + * an IOException for errors accessing entities or the document.</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1+ (sax2r3pre1) + * @see org.xml.sax.XMLReader#setErrorHandler + * @see org.xml.sax.SAXParseException + */ +public interface ErrorHandler { + + + /** + * Receive notification of a warning. + * + * <p>SAX parsers will use this method to report conditions that + * are not errors or fatal errors as defined by the XML + * recommendation. The default behaviour is to take no + * action.</p> + * + * <p>The SAX parser must continue to provide normal parsing events + * after invoking this method: it should still be possible for the + * application to process the document through to the end.</p> + * + * <p>Filters may use this method to report other, non-XML warnings + * as well.</p> + * + * @param exception The warning information encapsulated in a + * SAX parse exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.SAXParseException + */ + public abstract void warning (SAXParseException exception) + throws SAXException; + + + /** + * Receive notification of a recoverable error. + * + * <p>This corresponds to the definition of "error" in section 1.2 + * of the W3C XML 1.0 Recommendation. For example, a validating + * parser would use this callback to report the violation of a + * validity constraint. The default behaviour is to take no + * action.</p> + * + * <p>The SAX parser must continue to provide normal parsing + * events after invoking this method: it should still be possible + * for the application to process the document through to the end. + * If the application cannot do so, then the parser should report + * a fatal error even if the XML recommendation does not require + * it to do so.</p> + * + * <p>Filters may use this method to report other, non-XML errors + * as well.</p> + * + * @param exception The error information encapsulated in a + * SAX parse exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.SAXParseException + */ + public abstract void error (SAXParseException exception) + throws SAXException; + + + /** + * Receive notification of a non-recoverable error. + * + * <p><strong>There is an apparent contradiction between the + * documentation for this method and the documentation for {@link + * org.xml.sax.ContentHandler#endDocument}. Until this ambiguity + * is resolved in a future major release, clients should make no + * assumptions about whether endDocument() will or will not be + * invoked when the parser has reported a fatalError() or thrown + * an exception.</strong></p> + * + * <p>This corresponds to the definition of "fatal error" in + * section 1.2 of the W3C XML 1.0 Recommendation. For example, a + * parser would use this callback to report the violation of a + * well-formedness constraint.</p> + * + * <p>The application must assume that the document is unusable + * after the parser has invoked this method, and should continue + * (if at all) only for the sake of collecting additional error + * messages: in fact, SAX parsers are free to stop reporting any + * other events once this method has been invoked.</p> + * + * @param exception The error information encapsulated in a + * SAX parse exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.SAXParseException + */ + public abstract void fatalError (SAXParseException exception) + throws SAXException; + +} + +// end of ErrorHandler.java diff --git a/xml/src/main/java/org/xml/sax/HandlerBase.java b/xml/src/main/java/org/xml/sax/HandlerBase.java new file mode 100644 index 0000000..15ea2d4 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/HandlerBase.java @@ -0,0 +1,369 @@ +// SAX default handler base class. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: HandlerBase.java,v 1.7 2004/04/26 17:34:34 dmegginson Exp $ + +package org.xml.sax; + +/** + * Default base class for handlers. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class implements the default behaviour for four SAX1 + * interfaces: EntityResolver, DTDHandler, DocumentHandler, + * and ErrorHandler. It is now obsolete, but is included in SAX2 to + * support legacy SAX1 applications. SAX2 applications should use + * the {@link org.xml.sax.helpers.DefaultHandler DefaultHandler} + * class instead.</p> + * + * <p>Application writers can extend this class when they need to + * implement only part of an interface; parser writers can + * instantiate this class to provide default handlers when the + * application has not supplied its own.</p> + * + * <p>Note that the use of this class is optional.</p> + * + * @deprecated This class works with the deprecated + * {@link org.xml.sax.DocumentHandler DocumentHandler} + * interface. It has been replaced by the SAX2 + * {@link org.xml.sax.helpers.DefaultHandler DefaultHandler} + * class. + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.EntityResolver + * @see org.xml.sax.DTDHandler + * @see org.xml.sax.DocumentHandler + * @see org.xml.sax.ErrorHandler + */ +public class HandlerBase + implements EntityResolver, DTDHandler, DocumentHandler, ErrorHandler +{ + + + //////////////////////////////////////////////////////////////////// + // Default implementation of the EntityResolver interface. + //////////////////////////////////////////////////////////////////// + + /** + * Resolve an external entity. + * + * <p>Always return null, so that the parser will use the system + * identifier provided in the XML document. This method implements + * the SAX default behaviour: application writers can override it + * in a subclass to do special translations such as catalog lookups + * or URI redirection.</p> + * + * @param publicId The public identifer, or null if none is + * available. + * @param systemId The system identifier provided in the XML + * document. + * @return The new input source, or null to require the + * default behaviour. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.EntityResolver#resolveEntity + */ + public InputSource resolveEntity (String publicId, String systemId) + throws SAXException + { + return null; + } + + + + //////////////////////////////////////////////////////////////////// + // Default implementation of DTDHandler interface. + //////////////////////////////////////////////////////////////////// + + + /** + * Receive notification of a notation declaration. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass if they wish to keep track of the notations + * declared in a document.</p> + * + * @param name The notation name. + * @param publicId The notation public identifier, or null if not + * available. + * @param systemId The notation system identifier. + * @see org.xml.sax.DTDHandler#notationDecl + */ + public void notationDecl (String name, String publicId, String systemId) + { + // no op + } + + + /** + * Receive notification of an unparsed entity declaration. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to keep track of the unparsed entities + * declared in a document.</p> + * + * @param name The entity name. + * @param publicId The entity public identifier, or null if not + * available. + * @param systemId The entity system identifier. + * @param notationName The name of the associated notation. + * @see org.xml.sax.DTDHandler#unparsedEntityDecl + */ + public void unparsedEntityDecl (String name, String publicId, + String systemId, String notationName) + { + // no op + } + + + + //////////////////////////////////////////////////////////////////// + // Default implementation of DocumentHandler interface. + //////////////////////////////////////////////////////////////////// + + + /** + * Receive a Locator object for document events. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass if they wish to store the locator for use + * with other document events.</p> + * + * @param locator A locator for all SAX document events. + * @see org.xml.sax.DocumentHandler#setDocumentLocator + * @see org.xml.sax.Locator + */ + public void setDocumentLocator (Locator locator) + { + // no op + } + + + /** + * Receive notification of the beginning of the document. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the beginning + * of a document (such as allocating the root node of a tree or + * creating an output file).</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#startDocument + */ + public void startDocument () + throws SAXException + { + // no op + } + + + /** + * Receive notification of the end of the document. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the beginning + * of a document (such as finalising a tree or closing an output + * file).</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#endDocument + */ + public void endDocument () + throws SAXException + { + // no op + } + + + /** + * Receive notification of the start of an element. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the start of + * each element (such as allocating a new tree node or writing + * output to a file).</p> + * + * @param name The element type name. + * @param attributes The specified or defaulted attributes. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#startElement + */ + public void startElement (String name, AttributeList attributes) + throws SAXException + { + // no op + } + + + /** + * Receive notification of the end of an element. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the end of + * each element (such as finalising a tree node or writing + * output to a file).</p> + * + * @param name the element name + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#endElement + */ + public void endElement (String name) + throws SAXException + { + // no op + } + + + /** + * Receive notification of character data inside an element. + * + * <p>By default, do nothing. Application writers may override this + * method to take specific actions for each chunk of character data + * (such as adding the data to a node or buffer, or printing it to + * a file).</p> + * + * @param ch The characters. + * @param start The start position in the character array. + * @param length The number of characters to use from the + * character array. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#characters + */ + public void characters (char ch[], int start, int length) + throws SAXException + { + // no op + } + + + /** + * Receive notification of ignorable whitespace in element content. + * + * <p>By default, do nothing. Application writers may override this + * method to take specific actions for each chunk of ignorable + * whitespace (such as adding data to a node or buffer, or printing + * it to a file).</p> + * + * @param ch The whitespace characters. + * @param start The start position in the character array. + * @param length The number of characters to use from the + * character array. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#ignorableWhitespace + */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException + { + // no op + } + + + /** + * Receive notification of a processing instruction. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions for each + * processing instruction, such as setting status variables or + * invoking other methods.</p> + * + * @param target The processing instruction target. + * @param data The processing instruction data, or null if + * none is supplied. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DocumentHandler#processingInstruction + */ + public void processingInstruction (String target, String data) + throws SAXException + { + // no op + } + + + + //////////////////////////////////////////////////////////////////// + // Default implementation of the ErrorHandler interface. + //////////////////////////////////////////////////////////////////// + + + /** + * Receive notification of a parser warning. + * + * <p>The default implementation does nothing. Application writers + * may override this method in a subclass to take specific actions + * for each warning, such as inserting the message in a log file or + * printing it to the console.</p> + * + * @param e The warning information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#warning + * @see org.xml.sax.SAXParseException + */ + public void warning (SAXParseException e) + throws SAXException + { + // no op + } + + + /** + * Receive notification of a recoverable parser error. + * + * <p>The default implementation does nothing. Application writers + * may override this method in a subclass to take specific actions + * for each error, such as inserting the message in a log file or + * printing it to the console.</p> + * + * @param e The warning information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#warning + * @see org.xml.sax.SAXParseException + */ + public void error (SAXParseException e) + throws SAXException + { + // no op + } + + + /** + * Report a fatal XML parsing error. + * + * <p>The default implementation throws a SAXParseException. + * Application writers may override this method in a subclass if + * they need to take specific actions for each fatal error (such as + * collecting all of the errors into a single report): in any case, + * the application must stop all regular processing when this + * method is invoked, since the document is no longer reliable, and + * the parser may no longer report parsing events.</p> + * + * @param e The error information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#fatalError + * @see org.xml.sax.SAXParseException + */ + public void fatalError (SAXParseException e) + throws SAXException + { + throw e; + } + +} + +// end of HandlerBase.java diff --git a/xml/src/main/java/org/xml/sax/InputSource.java b/xml/src/main/java/org/xml/sax/InputSource.java new file mode 100644 index 0000000..b1342ee --- /dev/null +++ b/xml/src/main/java/org/xml/sax/InputSource.java @@ -0,0 +1,337 @@ +// SAX input source. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: InputSource.java,v 1.9 2002/01/30 21:13:45 dbrownell Exp $ + +package org.xml.sax; + +import java.io.Reader; +import java.io.InputStream; + +/** + * A single input source for an XML entity. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class allows a SAX application to encapsulate information + * about an input source in a single object, which may include + * a public identifier, a system identifier, a byte stream (possibly + * with a specified encoding), and/or a character stream.</p> + * + * <p>There are two places that the application can deliver an + * input source to the parser: as the argument to the Parser.parse + * method, or as the return value of the EntityResolver.resolveEntity + * method.</p> + * + * <p>The SAX parser will use the InputSource object to determine how + * to read XML input. If there is a character stream available, the + * parser will read that stream directly, disregarding any text + * encoding declaration found in that stream. + * If there is no character stream, but there is + * a byte stream, the parser will use that byte stream, using the + * encoding specified in the InputSource or else (if no encoding is + * specified) autodetecting the character encoding using an algorithm + * such as the one in the XML specification. If neither a character + * stream nor a + * byte stream is available, the parser will attempt to open a URI + * connection to the resource identified by the system + * identifier.</p> + * + * <p>An InputSource object belongs to the application: the SAX parser + * shall never modify it in any way (it may modify a copy if + * necessary). However, standard processing of both byte and + * character streams is to close them on as part of end-of-parse cleanup, + * so applications should not attempt to re-use such streams after they + * have been handed to a parser. </p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource) + * @see org.xml.sax.EntityResolver#resolveEntity + * @see java.io.InputStream + * @see java.io.Reader + */ +public class InputSource { + + /** + * Zero-argument default constructor. + * + * @see #setPublicId + * @see #setSystemId + * @see #setByteStream + * @see #setCharacterStream + * @see #setEncoding + */ + public InputSource () + { + } + + + /** + * Create a new input source with a system identifier. + * + * <p>Applications may use setPublicId to include a + * public identifier as well, or setEncoding to specify + * the character encoding, if known.</p> + * + * <p>If the system identifier is a URL, it must be fully + * resolved (it may not be a relative URL).</p> + * + * @param systemId The system identifier (URI). + * @see #setPublicId + * @see #setSystemId + * @see #setByteStream + * @see #setEncoding + * @see #setCharacterStream + */ + public InputSource (String systemId) + { + setSystemId(systemId); + } + + + /** + * Create a new input source with a byte stream. + * + * <p>Application writers should use setSystemId() to provide a base + * for resolving relative URIs, may use setPublicId to include a + * public identifier, and may use setEncoding to specify the object's + * character encoding.</p> + * + * @param byteStream The raw byte stream containing the document. + * @see #setPublicId + * @see #setSystemId + * @see #setEncoding + * @see #setByteStream + * @see #setCharacterStream + */ + public InputSource (InputStream byteStream) + { + setByteStream(byteStream); + } + + + /** + * Create a new input source with a character stream. + * + * <p>Application writers should use setSystemId() to provide a base + * for resolving relative URIs, and may use setPublicId to include a + * public identifier.</p> + * + * <p>The character stream shall not include a byte order mark.</p> + * + * @param characterStream The raw character stream containing the document. + * @see #setPublicId + * @see #setSystemId + * @see #setByteStream + * @see #setCharacterStream + */ + public InputSource (Reader characterStream) + { + setCharacterStream(characterStream); + } + + + /** + * Set the public identifier for this input source. + * + * <p>The public identifier is always optional: if the application + * writer includes one, it will be provided as part of the + * location information.</p> + * + * @param publicId The public identifier as a string. + * @see #getPublicId + * @see org.xml.sax.Locator#getPublicId + * @see org.xml.sax.SAXParseException#getPublicId + */ + public void setPublicId (String publicId) + { + this.publicId = publicId; + } + + + /** + * Get the public identifier for this input source. + * + * @return The public identifier, or null if none was supplied. + * @see #setPublicId + */ + public String getPublicId () + { + return publicId; + } + + + /** + * Set the system identifier for this input source. + * + * <p>The system identifier is optional if there is a byte stream + * or a character stream, but it is still useful to provide one, + * since the application can use it to resolve relative URIs + * and can include it in error messages and warnings (the parser + * will attempt to open a connection to the URI only if + * there is no byte stream or character stream specified).</p> + * + * <p>If the application knows the character encoding of the + * object pointed to by the system identifier, it can register + * the encoding using the setEncoding method.</p> + * + * <p>If the system identifier is a URL, it must be fully + * resolved (it may not be a relative URL).</p> + * + * @param systemId The system identifier as a string. + * @see #setEncoding + * @see #getSystemId + * @see org.xml.sax.Locator#getSystemId + * @see org.xml.sax.SAXParseException#getSystemId + */ + public void setSystemId (String systemId) + { + this.systemId = systemId; + } + + + /** + * Get the system identifier for this input source. + * + * <p>The getEncoding method will return the character encoding + * of the object pointed to, or null if unknown.</p> + * + * <p>If the system ID is a URL, it will be fully resolved.</p> + * + * @return The system identifier, or null if none was supplied. + * @see #setSystemId + * @see #getEncoding + */ + public String getSystemId () + { + return systemId; + } + + + /** + * Set the byte stream for this input source. + * + * <p>The SAX parser will ignore this if there is also a character + * stream specified, but it will use a byte stream in preference + * to opening a URI connection itself.</p> + * + * <p>If the application knows the character encoding of the + * byte stream, it should set it with the setEncoding method.</p> + * + * @param byteStream A byte stream containing an XML document or + * other entity. + * @see #setEncoding + * @see #getByteStream + * @see #getEncoding + * @see java.io.InputStream + */ + public void setByteStream (InputStream byteStream) + { + this.byteStream = byteStream; + } + + + /** + * Get the byte stream for this input source. + * + * <p>The getEncoding method will return the character + * encoding for this byte stream, or null if unknown.</p> + * + * @return The byte stream, or null if none was supplied. + * @see #getEncoding + * @see #setByteStream + */ + public InputStream getByteStream () + { + return byteStream; + } + + + /** + * Set the character encoding, if known. + * + * <p>The encoding must be a string acceptable for an + * XML encoding declaration (see section 4.3.3 of the XML 1.0 + * recommendation).</p> + * + * <p>This method has no effect when the application provides a + * character stream.</p> + * + * @param encoding A string describing the character encoding. + * @see #setSystemId + * @see #setByteStream + * @see #getEncoding + */ + public void setEncoding (String encoding) + { + this.encoding = encoding; + } + + + /** + * Get the character encoding for a byte stream or URI. + * This value will be ignored when the application provides a + * character stream. + * + * @return The encoding, or null if none was supplied. + * @see #setByteStream + * @see #getSystemId + * @see #getByteStream + */ + public String getEncoding () + { + return encoding; + } + + + /** + * Set the character stream for this input source. + * + * <p>If there is a character stream specified, the SAX parser + * will ignore any byte stream and will not attempt to open + * a URI connection to the system identifier.</p> + * + * @param characterStream The character stream containing the + * XML document or other entity. + * @see #getCharacterStream + * @see java.io.Reader + */ + public void setCharacterStream (Reader characterStream) + { + this.characterStream = characterStream; + } + + + /** + * Get the character stream for this input source. + * + * @return The character stream, or null if none was supplied. + * @see #setCharacterStream + */ + public Reader getCharacterStream () + { + return characterStream; + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + private String publicId; + private String systemId; + private InputStream byteStream; + private String encoding; + private Reader characterStream; + +} + +// end of InputSource.java diff --git a/xml/src/main/java/org/xml/sax/Locator.java b/xml/src/main/java/org/xml/sax/Locator.java new file mode 100644 index 0000000..f8f3484 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/Locator.java @@ -0,0 +1,136 @@ +// SAX locator interface for document events. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: Locator.java,v 1.8 2002/01/30 21:13:47 dbrownell Exp $ + +package org.xml.sax; + + +/** + * Interface for associating a SAX event with a document location. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>If a SAX parser provides location information to the SAX + * application, it does so by implementing this interface and then + * passing an instance to the application using the content + * handler's {@link org.xml.sax.ContentHandler#setDocumentLocator + * setDocumentLocator} method. The application can use the + * object to obtain the location of any other SAX event + * in the XML source document.</p> + * + * <p>Note that the results returned by the object will be valid only + * during the scope of each callback method: the application + * will receive unpredictable results if it attempts to use the + * locator at any other time, or after parsing completes.</p> + * + * <p>SAX parsers are not required to supply a locator, but they are + * very strongly encouraged to do so. If the parser supplies a + * locator, it must do so before reporting any other document events. + * If no locator has been set by the time the application receives + * the {@link org.xml.sax.ContentHandler#startDocument startDocument} + * event, the application should assume that a locator is not + * available.</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ +public interface Locator { + + + /** + * Return the public identifier for the current document event. + * + * <p>The return value is the public identifier of the document + * entity or of the external parsed entity in which the markup + * triggering the event appears.</p> + * + * @return A string containing the public identifier, or + * null if none is available. + * @see #getSystemId + */ + public abstract String getPublicId (); + + + /** + * Return the system identifier for the current document event. + * + * <p>The return value is the system identifier of the document + * entity or of the external parsed entity in which the markup + * triggering the event appears.</p> + * + * <p>If the system identifier is a URL, the parser must resolve it + * fully before passing it to the application. For example, a file + * name must always be provided as a <em>file:...</em> URL, and other + * kinds of relative URI are also resolved against their bases.</p> + * + * @return A string containing the system identifier, or null + * if none is available. + * @see #getPublicId + */ + public abstract String getSystemId (); + + + /** + * Return the line number where the current document event ends. + * Lines are delimited by line ends, which are defined in + * the XML specification. + * + * <p><strong>Warning:</strong> The return value from the method + * is intended only as an approximation for the sake of diagnostics; + * it is not intended to provide sufficient information + * to edit the character content of the original XML document. + * In some cases, these "line" numbers match what would be displayed + * as columns, and in others they may not match the source text + * due to internal entity expansion. </p> + * + * <p>The return value is an approximation of the line number + * in the document entity or external parsed entity where the + * markup triggering the event appears.</p> + * + * <p>If possible, the SAX driver should provide the line position + * of the first character after the text associated with the document + * event. The first line is line 1.</p> + * + * @return The line number, or -1 if none is available. + * @see #getColumnNumber + */ + public abstract int getLineNumber (); + + + /** + * Return the column number where the current document event ends. + * This is one-based number of Java <code>char</code> values since + * the last line end. + * + * <p><strong>Warning:</strong> The return value from the method + * is intended only as an approximation for the sake of diagnostics; + * it is not intended to provide sufficient information + * to edit the character content of the original XML document. + * For example, when lines contain combining character sequences, wide + * characters, surrogate pairs, or bi-directional text, the value may + * not correspond to the column in a text editor's display. </p> + * + * <p>The return value is an approximation of the column number + * in the document entity or external parsed entity where the + * markup triggering the event appears.</p> + * + * <p>If possible, the SAX driver should provide the line position + * of the first character after the text associated with the document + * event. The first column in each line is column 1.</p> + * + * @return The column number, or -1 if none is available. + * @see #getLineNumber + */ + public abstract int getColumnNumber (); + +} + +// end of Locator.java diff --git a/xml/src/main/java/org/xml/sax/Parser.java b/xml/src/main/java/org/xml/sax/Parser.java new file mode 100644 index 0000000..67a5512 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/Parser.java @@ -0,0 +1,209 @@ +// SAX parser interface. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: Parser.java,v 1.6 2002/01/30 21:13:47 dbrownell Exp $ + +package org.xml.sax; + +import java.io.IOException; +import java.util.Locale; + + +/** + * Basic interface for SAX (Simple API for XML) parsers. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This was the main event supplier interface for SAX1; it has + * been replaced in SAX2 by {@link org.xml.sax.XMLReader XMLReader}, + * which includes Namespace support and sophisticated configurability + * and extensibility.</p> + * + * <p>All SAX1 parsers must implement this basic interface: it allows + * applications to register handlers for different types of events + * and to initiate a parse from a URI, or a character stream.</p> + * + * <p>All SAX1 parsers must also implement a zero-argument constructor + * (though other constructors are also allowed).</p> + * + * <p>SAX1 parsers are reusable but not re-entrant: the application + * may reuse a parser object (possibly with a different input source) + * once the first parse has completed successfully, but it may not + * invoke the parse() methods recursively within a parse.</p> + * + * @deprecated This interface has been replaced by the SAX2 + * {@link org.xml.sax.XMLReader XMLReader} + * interface, which includes Namespace support. + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.EntityResolver + * @see org.xml.sax.DTDHandler + * @see org.xml.sax.DocumentHandler + * @see org.xml.sax.ErrorHandler + * @see org.xml.sax.HandlerBase + * @see org.xml.sax.InputSource + */ +public interface Parser +{ + + /** + * Allow an application to request a locale for errors and warnings. + * + * <p>SAX parsers are not required to provide localisation for errors + * and warnings; if they cannot support the requested locale, + * however, they must throw a SAX exception. Applications may + * not request a locale change in the middle of a parse.</p> + * + * @param locale A Java Locale object. + * @exception org.xml.sax.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 abstract void setLocale (Locale locale) + throws SAXException; + + + /** + * Allow an application to register a custom entity resolver. + * + * <p>If the application does not register an entity resolver, the + * SAX parser will resolve system identifiers and open connections + * to entities itself (this is the default behaviour implemented in + * HandlerBase).</p> + * + * <p>Applications may register a new or different entity resolver + * in the middle of a parse, and the SAX parser must begin using + * the new resolver immediately.</p> + * + * @param resolver The object for resolving entities. + * @see EntityResolver + * @see HandlerBase + */ + public abstract void setEntityResolver (EntityResolver resolver); + + + /** + * Allow an application to register a DTD event handler. + * + * <p>If the application does not register a DTD handler, all DTD + * events reported by the SAX parser will be silently + * ignored (this is the default behaviour implemented by + * HandlerBase).</p> + * + * <p>Applications may register a new or different + * handler in the middle of a parse, and the SAX parser must + * begin using the new handler immediately.</p> + * + * @param handler The DTD handler. + * @see DTDHandler + * @see HandlerBase + */ + public abstract void setDTDHandler (DTDHandler handler); + + + /** + * Allow an application to register a document event handler. + * + * <p>If the application does not register a document handler, all + * document events reported by the SAX parser will be silently + * ignored (this is the default behaviour implemented by + * HandlerBase).</p> + * + * <p>Applications may register a new or different handler in the + * middle of a parse, and the SAX parser must begin using the new + * handler immediately.</p> + * + * @param handler The document handler. + * @see DocumentHandler + * @see HandlerBase + */ + public abstract void setDocumentHandler (DocumentHandler handler); + + + /** + * Allow an application to register an error event handler. + * + * <p>If the application does not register an error event handler, + * all error events reported by the SAX parser will be silently + * ignored, except for fatalError, which will throw a SAXException + * (this is the default behaviour implemented by HandlerBase).</p> + * + * <p>Applications may register a new or different handler in the + * middle of a parse, and the SAX parser must begin using the new + * handler immediately.</p> + * + * @param handler The error handler. + * @see ErrorHandler + * @see SAXException + * @see HandlerBase + */ + public abstract void setErrorHandler (ErrorHandler handler); + + + /** + * Parse an XML document. + * + * <p>The application can use this method to instruct the SAX parser + * 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 + * additional XML document). Once a parse is complete, an + * application may reuse the same Parser object, possibly with a + * different input source.</p> + * + * @param source 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 #parse(java.lang.String) + * @see #setEntityResolver + * @see #setDTDHandler + * @see #setDocumentHandler + * @see #setErrorHandler + */ + public abstract void parse (InputSource source) + throws SAXException, IOException; + + + /** + * Parse an XML document from a system identifier (URI). + * + * <p>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:</p> + * + * <pre> + * parse(new InputSource(systemId)); + * </pre> + * + * <p>If the system identifier is a URL, it must be fully resolved + * by the application before it is passed to the parser.</p> + * + * @param systemId The system identifier (URI). + * @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 #parse(org.xml.sax.InputSource) + */ + public abstract void parse (String systemId) + throws SAXException, IOException; + +} + +// end of Parser.java diff --git a/xml/src/main/java/org/xml/sax/SAXException.java b/xml/src/main/java/org/xml/sax/SAXException.java new file mode 100644 index 0000000..2e5b4cd --- /dev/null +++ b/xml/src/main/java/org/xml/sax/SAXException.java @@ -0,0 +1,153 @@ +// SAX exception class. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: SAXException.java,v 1.7 2002/01/30 21:13:48 dbrownell Exp $ + +package org.xml.sax; + +/** + * Encapsulate a general SAX error or warning. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class can contain basic error or warning information from + * either the XML parser or the application: a parser writer or + * application writer can subclass it to provide additional + * functionality. SAX handlers may throw this exception or + * any exception subclassed from it.</p> + * + * <p>If the application needs to pass through other types of + * exceptions, it must wrap those exceptions in a SAXException + * or an exception derived from a SAXException.</p> + * + * <p>If the parser or application needs to include information about a + * specific location in an XML document, it should use the + * {@link org.xml.sax.SAXParseException SAXParseException} subclass.</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.SAXParseException + */ +public class SAXException extends Exception { + + + /** + * Create a new SAXException. + */ + public SAXException () + { + super(); + this.exception = null; + } + + + /** + * Create a new SAXException. + * + * @param message The error or warning message. + */ + public SAXException (String message) { + super(message); + this.exception = null; + } + + + /** + * Create a new SAXException wrapping an existing exception. + * + * <p>The existing exception will be embedded in the new + * one, and its message will become the default message for + * the SAXException.</p> + * + * @param e The exception to be wrapped in a SAXException. + */ + public SAXException (Exception e) + { + super(); + this.exception = e; + } + + + /** + * Create a new SAXException from an existing exception. + * + * <p>The existing exception will be embedded in the new + * one, but the new exception will have its own message.</p> + * + * @param message The detail message. + * @param e The exception to be wrapped in a SAXException. + */ + public SAXException (String message, Exception e) + { + super(message); + this.exception = e; + } + + + /** + * Return a detail message for this exception. + * + * <p>If there is an embedded exception, and if the SAXException + * has no detail message of its own, this method will return + * the detail message from the embedded exception.</p> + * + * @return The error or warning message. + */ + public String getMessage () + { + String message = super.getMessage(); + + if (message == null && exception != null) { + return exception.getMessage(); + } else { + return message; + } + } + + + /** + * Return the embedded exception, if any. + * + * @return The embedded exception, or null if there is none. + */ + public Exception getException () + { + return exception; + } + + + /** + * Override toString to pick up any embedded exception. + * + * @return A string representation of this exception. + */ + public String toString () + { + if (exception != null) { + return exception.toString(); + } else { + return super.toString(); + } + } + + + + ////////////////////////////////////////////////////////////////////// + // Internal state. + ////////////////////////////////////////////////////////////////////// + + + /** + * @serial The embedded exception if tunnelling, or null. + */ + private Exception exception; + +} + +// end of SAXException.java diff --git a/xml/src/main/java/org/xml/sax/SAXNotRecognizedException.java b/xml/src/main/java/org/xml/sax/SAXNotRecognizedException.java new file mode 100644 index 0000000..69ba807 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/SAXNotRecognizedException.java @@ -0,0 +1,53 @@ +// SAXNotRecognizedException.java - unrecognized feature or value. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the Public Domain. +// $Id: SAXNotRecognizedException.java,v 1.7 2002/01/30 21:13:48 dbrownell Exp $ + +package org.xml.sax; + + +/** + * Exception class for an unrecognized identifier. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>An XMLReader will throw this exception when it finds an + * unrecognized feature or property identifier; SAX applications and + * extensions may use this class for other, similar purposes.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.SAXNotSupportedException + */ +public class SAXNotRecognizedException extends SAXException +{ + + /** + * Default constructor. + */ + public SAXNotRecognizedException () + { + super(); + } + + + /** + * Construct a new exception with the given message. + * + * @param message The text message of the exception. + */ + public SAXNotRecognizedException (String message) + { + super(message); + } + +} + +// end of SAXNotRecognizedException.java diff --git a/xml/src/main/java/org/xml/sax/SAXNotSupportedException.java b/xml/src/main/java/org/xml/sax/SAXNotSupportedException.java new file mode 100644 index 0000000..bd5b239 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/SAXNotSupportedException.java @@ -0,0 +1,53 @@ +// SAXNotSupportedException.java - unsupported feature or value. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the Public Domain. +// $Id: SAXNotSupportedException.java,v 1.7 2002/01/30 21:13:48 dbrownell Exp $ + +package org.xml.sax; + +/** + * Exception class for an unsupported operation. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>An XMLReader will throw this exception when it recognizes a + * feature or property identifier, but cannot perform the requested + * operation (setting a state or value). Other SAX2 applications and + * extensions may use this class for similar purposes.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.SAXNotRecognizedException + */ +public class SAXNotSupportedException extends SAXException +{ + + /** + * Construct a new exception with no message. + */ + public SAXNotSupportedException () + { + super(); + } + + + /** + * Construct a new exception with the given message. + * + * @param message The text message of the exception. + */ + public SAXNotSupportedException (String message) + { + super(message); + } + +} + +// end of SAXNotSupportedException.java diff --git a/xml/src/main/java/org/xml/sax/SAXParseException.java b/xml/src/main/java/org/xml/sax/SAXParseException.java new file mode 100644 index 0000000..a6a93e9 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/SAXParseException.java @@ -0,0 +1,269 @@ +// SAX exception class. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: SAXParseException.java,v 1.11 2004/04/21 13:05:02 dmegginson Exp $ + +package org.xml.sax; + +/** + * Encapsulate an XML parse error or warning. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This exception may include information for locating the error + * in the original XML document, as if it came from a {@link Locator} + * object. Note that although the application + * will receive a SAXParseException as the argument to the handlers + * in the {@link org.xml.sax.ErrorHandler ErrorHandler} interface, + * the application is not actually required to throw the exception; + * instead, it can simply read the information in it and take a + * different action.</p> + * + * <p>Since this exception is a subclass of {@link org.xml.sax.SAXException + * SAXException}, it inherits the ability to wrap another exception.</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.SAXException + * @see org.xml.sax.Locator + * @see org.xml.sax.ErrorHandler + */ +public class SAXParseException extends SAXException { + + + ////////////////////////////////////////////////////////////////////// + // Constructors. + ////////////////////////////////////////////////////////////////////// + + + /** + * Create a new SAXParseException from a message and a Locator. + * + * <p>This constructor is especially useful when an application is + * creating its own exception from within a {@link org.xml.sax.ContentHandler + * ContentHandler} callback.</p> + * + * @param message The error or warning message. + * @param locator The locator object for the error or warning (may be + * null). + * @see org.xml.sax.Locator + */ + public SAXParseException (String message, Locator locator) { + super(message); + if (locator != null) { + init(locator.getPublicId(), locator.getSystemId(), + locator.getLineNumber(), locator.getColumnNumber()); + } else { + init(null, null, -1, -1); + } + } + + + /** + * Wrap an existing exception in a SAXParseException. + * + * <p>This constructor is especially useful when an application is + * creating its own exception from within a {@link org.xml.sax.ContentHandler + * ContentHandler} callback, and needs to wrap an existing exception that is not a + * subclass of {@link org.xml.sax.SAXException SAXException}.</p> + * + * @param message The error or warning message, or null to + * use the message from the embedded exception. + * @param locator The locator object for the error or warning (may be + * null). + * @param e Any exception. + * @see org.xml.sax.Locator + */ + public SAXParseException (String message, Locator locator, + Exception e) { + super(message, e); + if (locator != null) { + init(locator.getPublicId(), locator.getSystemId(), + locator.getLineNumber(), locator.getColumnNumber()); + } else { + init(null, null, -1, -1); + } + } + + + /** + * Create a new SAXParseException. + * + * <p>This constructor is most useful for parser writers.</p> + * + * <p>All parameters except the message are as if + * they were provided by a {@link Locator}. For example, if the + * system identifier is a URL (including relative filename), the + * caller must resolve it fully before creating the exception.</p> + * + * + * @param message The error or warning message. + * @param publicId The public identifier of the entity that generated + * the error or warning. + * @param systemId The system identifier of the entity that generated + * the error or warning. + * @param lineNumber The line number of the end of the text that + * caused the error or warning. + * @param columnNumber The column number of the end of the text that + * cause the error or warning. + */ + public SAXParseException (String message, String publicId, String systemId, + int lineNumber, int columnNumber) + { + super(message); + init(publicId, systemId, lineNumber, columnNumber); + } + + + /** + * Create a new SAXParseException with an embedded exception. + * + * <p>This constructor is most useful for parser writers who + * need to wrap an exception that is not a subclass of + * {@link org.xml.sax.SAXException SAXException}.</p> + * + * <p>All parameters except the message and exception are as if + * they were provided by a {@link Locator}. For example, if the + * system identifier is a URL (including relative filename), the + * caller must resolve it fully before creating the exception.</p> + * + * @param message The error or warning message, or null to use + * the message from the embedded exception. + * @param publicId The public identifier of the entity that generated + * the error or warning. + * @param systemId The system identifier of the entity that generated + * the error or warning. + * @param lineNumber The line number of the end of the text that + * caused the error or warning. + * @param columnNumber The column number of the end of the text that + * cause the error or warning. + * @param e Another exception to embed in this one. + */ + public SAXParseException (String message, String publicId, String systemId, + int lineNumber, int columnNumber, Exception e) + { + super(message, e); + init(publicId, systemId, lineNumber, columnNumber); + } + + + /** + * Internal initialization method. + * + * @param publicId The public identifier of the entity which generated the exception, + * or null. + * @param systemId The system identifier of the entity which generated the exception, + * or null. + * @param lineNumber The line number of the error, or -1. + * @param columnNumber The column number of the error, or -1. + */ + private void init (String publicId, String systemId, + int lineNumber, int columnNumber) + { + this.publicId = publicId; + this.systemId = systemId; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + + /** + * Get the public identifier of the entity where the exception occurred. + * + * @return A string containing the public identifier, or null + * if none is available. + * @see org.xml.sax.Locator#getPublicId + */ + public String getPublicId () + { + return this.publicId; + } + + + /** + * Get the system identifier of the entity where the exception occurred. + * + * <p>If the system identifier is a URL, it will have been resolved + * fully.</p> + * + * @return A string containing the system identifier, or null + * if none is available. + * @see org.xml.sax.Locator#getSystemId + */ + public String getSystemId () + { + return this.systemId; + } + + + /** + * The line number of the end of the text where the exception occurred. + * + * <p>The first line is line 1.</p> + * + * @return An integer representing the line number, or -1 + * if none is available. + * @see org.xml.sax.Locator#getLineNumber + */ + public int getLineNumber () + { + return this.lineNumber; + } + + + /** + * The column number of the end of the text where the exception occurred. + * + * <p>The first column in a line is position 1.</p> + * + * @return An integer representing the column number, or -1 + * if none is available. + * @see org.xml.sax.Locator#getColumnNumber + */ + public int getColumnNumber () + { + return this.columnNumber; + } + + + ////////////////////////////////////////////////////////////////////// + // Internal state. + ////////////////////////////////////////////////////////////////////// + + + /** + * @serial The public identifier, or null. + * @see #getPublicId + */ + private String publicId; + + + /** + * @serial The system identifier, or null. + * @see #getSystemId + */ + private String systemId; + + + /** + * @serial The line number, or -1. + * @see #getLineNumber + */ + private int lineNumber; + + + /** + * @serial The column number, or -1. + * @see #getColumnNumber + */ + private int columnNumber; + +} + +// end of SAXParseException.java diff --git a/xml/src/main/java/org/xml/sax/XMLFilter.java b/xml/src/main/java/org/xml/sax/XMLFilter.java new file mode 100644 index 0000000..5a399fa --- /dev/null +++ b/xml/src/main/java/org/xml/sax/XMLFilter.java @@ -0,0 +1,65 @@ +// XMLFilter.java - filter SAX2 events. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the Public Domain. +// $Id: XMLFilter.java,v 1.6 2002/01/30 21:13:48 dbrownell Exp $ + +package org.xml.sax; + + +/** + * Interface for an XML filter. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>An XML filter is like an XML reader, except that it obtains its + * events from another XML reader rather than a primary source like + * an XML document or database. Filters can modify a stream of + * events as they pass on to the final application.</p> + * + * <p>The XMLFilterImpl helper class provides a convenient base + * for creating SAX2 filters, by passing on all {@link org.xml.sax.EntityResolver + * EntityResolver}, {@link org.xml.sax.DTDHandler DTDHandler}, + * {@link org.xml.sax.ContentHandler ContentHandler} and {@link org.xml.sax.ErrorHandler + * ErrorHandler} events automatically.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.helpers.XMLFilterImpl + */ +public interface XMLFilter extends XMLReader +{ + + /** + * Set the parent reader. + * + * <p>This method allows the application to link the filter to + * a parent reader (which may be another filter). The argument + * may not be null.</p> + * + * @param parent The parent reader. + */ + public abstract void setParent (XMLReader parent); + + + /** + * Get the parent reader. + * + * <p>This method allows the application to query the parent + * reader (which may be another filter). It is generally a + * bad idea to perform any operations on the parent reader + * directly: they should all pass through this filter.</p> + * + * @return The parent filter, or null if none has been set. + */ + public abstract XMLReader getParent (); + +} + +// end of XMLFilter.java diff --git a/xml/src/main/java/org/xml/sax/XMLReader.java b/xml/src/main/java/org/xml/sax/XMLReader.java new file mode 100644 index 0000000..d58a4bd --- /dev/null +++ b/xml/src/main/java/org/xml/sax/XMLReader.java @@ -0,0 +1,404 @@ +// XMLReader.java - read an XML document. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the Public Domain. +// $Id: XMLReader.java,v 1.9 2004/04/26 17:34:34 dmegginson Exp $ + +package org.xml.sax; + +import java.io.IOException; + + +/** + * Interface for reading an XML document using callbacks. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p><strong>Note:</strong> despite its name, this interface does + * <em>not</em> extend the standard Java {@link java.io.Reader Reader} + * interface, because reading XML is a fundamentally different activity + * than reading character data.</p> + * + * <p>XMLReader is the interface that an XML parser's SAX2 driver must + * implement. This interface allows an application to set and + * query features and properties in the parser, to register + * event handlers for document processing, and to initiate + * a document parse.</p> + * + * <p>All SAX interfaces are assumed to be synchronous: the + * {@link #parse parse} methods must not return until parsing + * is complete, and readers must wait for an event-handler callback + * to return before reporting the next event.</p> + * + * <p>This interface replaces the (now deprecated) SAX 1.0 {@link + * org.xml.sax.Parser Parser} interface. The XMLReader interface + * contains two important enhancements over the old Parser + * interface (as well as some minor ones):</p> + * + * <ol> + * <li>it adds a standard way to query and set features and + * properties; and</li> + * <li>it adds Namespace support, which is required for many + * higher-level XML standards.</li> + * </ol> + * + * <p>There are adapters available to convert a SAX1 Parser to + * a SAX2 XMLReader and vice-versa.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1+ (sax2r3pre1) + * @see org.xml.sax.XMLFilter + * @see org.xml.sax.helpers.ParserAdapter + * @see org.xml.sax.helpers.XMLReaderAdapter + */ +public interface XMLReader +{ + + + //////////////////////////////////////////////////////////////////// + // Configuration. + //////////////////////////////////////////////////////////////////// + + + /** + * Look up the value of a feature flag. + * + * <p>The feature name is any fully-qualified URI. It is + * possible for an XMLReader to recognize a feature name but + * temporarily be unable to return its value. + * Some feature values may be available only in specific + * contexts, such as before, during, or after a parse. + * Also, some feature values may not be programmatically accessible. + * (In the case of an adapter for SAX1 {@link Parser}, there is no + * implementation-independent way to expose whether the underlying + * parser is performing validation, expanding external entities, + * and so forth.) </p> + * + * <p>All XMLReaders are required to recognize the + * http://xml.org/sax/features/namespaces and the + * http://xml.org/sax/features/namespace-prefixes feature names.</p> + * + * <p>Typical usage is something like this:</p> + * + * <pre> + * XMLReader r = new MySAXDriver(); + * + * // try to activate validation + * try { + * r.setFeature("http://xml.org/sax/features/validation", true); + * } catch (SAXException e) { + * System.err.println("Cannot activate validation."); + * } + * + * // register event handlers + * r.setContentHandler(new MyContentHandler()); + * r.setErrorHandler(new MyErrorHandler()); + * + * // parse the first document + * try { + * r.parse("http://www.foo.com/mydoc.xml"); + * } catch (IOException e) { + * System.err.println("I/O exception reading XML document"); + * } catch (SAXException e) { + * System.err.println("XML exception reading document."); + * } + * </pre> + * + * <p>Implementors are free (and encouraged) to invent their own features, + * using names built on their own URIs.</p> + * + * @param name The feature name, which is a fully-qualified URI. + * @return The current value of the feature (true or false). + * @exception org.xml.sax.SAXNotRecognizedException If the feature + * value can't be assigned or retrieved. + * @exception org.xml.sax.SAXNotSupportedException When the + * XMLReader recognizes the feature name but + * cannot determine its value at this time. + * @see #setFeature + */ + public boolean getFeature (String name) + throws SAXNotRecognizedException, SAXNotSupportedException; + + + /** + * Set the value of a feature flag. + * + * <p>The feature name is any fully-qualified URI. It is + * possible for an XMLReader to expose a feature value but + * to be unable to change the current value. + * Some feature values may be immutable or mutable only + * in specific contexts, such as before, during, or after + * a parse.</p> + * + * <p>All XMLReaders are required to support setting + * http://xml.org/sax/features/namespaces to true and + * http://xml.org/sax/features/namespace-prefixes to false.</p> + * + * @param name The feature name, which is a fully-qualified URI. + * @param value The requested value of the feature (true or false). + * @exception org.xml.sax.SAXNotRecognizedException If the feature + * value can't be assigned or retrieved. + * @exception org.xml.sax.SAXNotSupportedException When the + * XMLReader recognizes the feature name but + * cannot set the requested value. + * @see #getFeature + */ + public void setFeature (String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException; + + + /** + * Look up the value of a property. + * + * <p>The property name is any fully-qualified URI. It is + * possible for an XMLReader to recognize a property name but + * temporarily be unable to return its value. + * Some property values may be available only in specific + * contexts, such as before, during, or after a parse.</p> + * + * <p>XMLReaders are not required to recognize any specific + * property names, though an initial core set is documented for + * SAX2.</p> + * + * <p>Implementors are free (and encouraged) to invent their own properties, + * using names built on their own URIs.</p> + * + * @param name The property name, which is a fully-qualified URI. + * @return The current value of the property. + * @exception org.xml.sax.SAXNotRecognizedException If the property + * value can't be assigned or retrieved. + * @exception org.xml.sax.SAXNotSupportedException When the + * XMLReader recognizes the property name but + * cannot determine its value at this time. + * @see #setProperty + */ + public Object getProperty (String name) + throws SAXNotRecognizedException, SAXNotSupportedException; + + + /** + * Set the value of a property. + * + * <p>The property name is any fully-qualified URI. It is + * possible for an XMLReader to recognize a property name but + * to be unable to change the current value. + * Some property values may be immutable or mutable only + * in specific contexts, such as before, during, or after + * a parse.</p> + * + * <p>XMLReaders are not required to recognize setting + * any specific property names, though a core set is defined by + * SAX2.</p> + * + * <p>This method is also the standard mechanism for setting + * extended handlers.</p> + * + * @param name The property name, which is a fully-qualified URI. + * @param value The requested value for the property. + * @exception org.xml.sax.SAXNotRecognizedException If the property + * value can't be assigned or retrieved. + * @exception org.xml.sax.SAXNotSupportedException When the + * XMLReader recognizes the property name but + * cannot set the requested value. + */ + public void setProperty (String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException; + + + + //////////////////////////////////////////////////////////////////// + // Event handlers. + //////////////////////////////////////////////////////////////////// + + + /** + * Allow an application to register an entity resolver. + * + * <p>If the application does not register an entity resolver, + * the XMLReader will perform its own default resolution.</p> + * + * <p>Applications may register a new or different resolver in the + * middle of a parse, and the SAX parser must begin using the new + * resolver immediately.</p> + * + * @param resolver The entity resolver. + * @see #getEntityResolver + */ + public void setEntityResolver (EntityResolver resolver); + + + /** + * Return the current entity resolver. + * + * @return The current entity resolver, or null if none + * has been registered. + * @see #setEntityResolver + */ + public EntityResolver getEntityResolver (); + + + /** + * Allow an application to register a DTD event handler. + * + * <p>If the application does not register a DTD handler, all DTD + * events reported by the SAX parser will be silently ignored.</p> + * + * <p>Applications may register a new or different handler in the + * middle of a parse, and the SAX parser must begin using the new + * handler immediately.</p> + * + * @param handler The DTD handler. + * @see #getDTDHandler + */ + public void setDTDHandler (DTDHandler handler); + + + /** + * Return the current DTD handler. + * + * @return The current DTD handler, or null if none + * has been registered. + * @see #setDTDHandler + */ + public DTDHandler getDTDHandler (); + + + /** + * Allow an application to register a content event handler. + * + * <p>If the application does not register a content handler, all + * content events reported by the SAX parser will be silently + * ignored.</p> + * + * <p>Applications may register a new or different handler in the + * middle of a parse, and the SAX parser must begin using the new + * handler immediately.</p> + * + * @param handler The content handler. + * @see #getContentHandler + */ + public void setContentHandler (ContentHandler handler); + + + /** + * Return the current content handler. + * + * @return The current content handler, or null if none + * has been registered. + * @see #setContentHandler + */ + public ContentHandler getContentHandler (); + + + /** + * Allow an application to register an error event handler. + * + * <p>If the application does not register an error handler, all + * error events reported by the SAX parser will be silently + * ignored; however, normal processing may not continue. It is + * highly recommended that all SAX applications implement an + * error handler to avoid unexpected bugs.</p> + * + * <p>Applications may register a new or different handler in the + * middle of a parse, and the SAX parser must begin using the new + * handler immediately.</p> + * + * @param handler The error handler. + * @see #getErrorHandler + */ + public void setErrorHandler (ErrorHandler handler); + + + /** + * Return the current error handler. + * + * @return The current error handler, or null if none + * has been registered. + * @see #setErrorHandler + */ + public ErrorHandler getErrorHandler (); + + + + //////////////////////////////////////////////////////////////////// + // Parsing. + //////////////////////////////////////////////////////////////////// + + /** + * 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 XMLReader instead for each + * nested XML document). Once a parse is complete, an + * application may reuse the same XMLReader object, possibly with a + * different input source. + * Configuration of the XMLReader object (such as handler bindings and + * values established for feature flags and properties) is unchanged + * by completion of a parse, unless the definition of that aspect of + * the configuration explicitly specifies other behavior. + * (For example, feature flags or properties exposing + * characteristics of the document being parsed.) + * </p> + * + * <p>During the parse, the XMLReader will provide information + * about the XML document through the registered event + * handlers.</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 input 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 #parse(java.lang.String) + * @see #setEntityResolver + * @see #setDTDHandler + * @see #setContentHandler + * @see #setErrorHandler + */ + public void parse (InputSource input) + throws IOException, SAXException; + + + /** + * Parse an XML document from a system identifier (URI). + * + * <p>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:</p> + * + * <pre> + * parse(new InputSource(systemId)); + * </pre> + * + * <p>If the system identifier is a URL, it must be fully resolved + * by the application before it is passed to the parser.</p> + * + * @param systemId The system identifier (URI). + * @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 #parse(org.xml.sax.InputSource) + */ + public void parse (String systemId) + throws IOException, SAXException; + +} diff --git a/xml/src/main/java/org/xml/sax/ext/Attributes2.java b/xml/src/main/java/org/xml/sax/ext/Attributes2.java new file mode 100644 index 0000000..45cd551 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/Attributes2.java @@ -0,0 +1,132 @@ +// Attributes2.java - extended Attributes +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: Attributes2.java,v 1.6 2004/03/08 13:01:00 dmegginson Exp $ + +package org.xml.sax.ext; + +import org.xml.sax.Attributes; + + +/** + * SAX2 extension to augment the per-attribute information + * provided though {@link Attributes}. + * If an implementation supports this extension, the attributes + * provided in {@link org.xml.sax.ContentHandler#startElement + * ContentHandler.startElement() } will implement this interface, + * and the <em>http://xml.org/sax/features/use-attributes2</em> + * feature flag will have the value <em>true</em>. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * </blockquote> + * + * <p> XMLReader implementations are not required to support this + * information, and it is not part of core-only SAX2 distributions.</p> + * + * <p>Note that if an attribute was defaulted (<em>!isSpecified()</em>) + * it will of necessity also have been declared (<em>isDeclared()</em>) + * in the DTD. + * Similarly if an attribute's type is anything except CDATA, then it + * must have been declared. + * </p> + * + * @since SAX 2.0 (extensions 1.1 alpha) + * @author David Brownell + * @version TBS + */ +public interface Attributes2 extends Attributes +{ + /** + * Returns false unless the attribute was declared in the DTD. + * This helps distinguish two kinds of attributes that SAX reports + * as CDATA: ones that were declared (and hence are usually valid), + * and those that were not (and which are never valid). + * + * @param index The attribute index (zero-based). + * @return true if the attribute was declared in the DTD, + * false otherwise. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not identify an attribute. + */ + public boolean isDeclared (int index); + + /** + * Returns false unless the attribute was declared in the DTD. + * This helps distinguish two kinds of attributes that SAX reports + * as CDATA: ones that were declared (and hence are usually valid), + * and those that were not (and which are never valid). + * + * @param qName The XML qualified (prefixed) name. + * @return true if the attribute was declared in the DTD, + * false otherwise. + * @exception java.lang.IllegalArgumentException When the + * supplied name does not identify an attribute. + */ + public boolean isDeclared (String qName); + + /** + * Returns false unless the attribute was declared in the DTD. + * This helps distinguish two kinds of attributes that SAX reports + * as CDATA: ones that were declared (and hence are usually valid), + * and those that were not (and which are never valid). + * + * <p>Remember that since DTDs do not "understand" namespaces, the + * namespace URI associated with an attribute may not have come from + * the DTD. The declaration will have applied to the attribute's + * <em>qName</em>. + * + * @param uri The Namespace URI, or the empty string if + * the name has no Namespace URI. + * @param localName The attribute's local name. + * @return true if the attribute was declared in the DTD, + * false otherwise. + * @exception java.lang.IllegalArgumentException When the + * supplied names do not identify an attribute. + */ + public boolean isDeclared (String uri, String localName); + + /** + * Returns true unless the attribute value was provided + * by DTD defaulting. + * + * @param index The attribute index (zero-based). + * @return true if the value was found in the XML text, + * false if the value was provided by DTD defaulting. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not identify an attribute. + */ + public boolean isSpecified (int index); + + /** + * Returns true unless the attribute value was provided + * by DTD defaulting. + * + * <p>Remember that since DTDs do not "understand" namespaces, the + * namespace URI associated with an attribute may not have come from + * the DTD. The declaration will have applied to the attribute's + * <em>qName</em>. + * + * @param uri The Namespace URI, or the empty string if + * the name has no Namespace URI. + * @param localName The attribute's local name. + * @return true if the value was found in the XML text, + * false if the value was provided by DTD defaulting. + * @exception java.lang.IllegalArgumentException When the + * supplied names do not identify an attribute. + */ + public boolean isSpecified (String uri, String localName); + + /** + * Returns true unless the attribute value was provided + * by DTD defaulting. + * + * @param qName The XML qualified (prefixed) name. + * @return true if the value was found in the XML text, + * false if the value was provided by DTD defaulting. + * @exception java.lang.IllegalArgumentException When the + * supplied name does not identify an attribute. + */ + public boolean isSpecified (String qName); +} diff --git a/xml/src/main/java/org/xml/sax/ext/Attributes2Impl.java b/xml/src/main/java/org/xml/sax/ext/Attributes2Impl.java new file mode 100644 index 0000000..81b9b93 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/Attributes2Impl.java @@ -0,0 +1,313 @@ +// Attributes2Impl.java - extended AttributesImpl +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: Attributes2Impl.java,v 1.5 2004/03/08 13:01:01 dmegginson Exp $ + +package org.xml.sax.ext; + +import org.xml.sax.Attributes; +import org.xml.sax.helpers.AttributesImpl; + + +/** + * SAX2 extension helper for additional Attributes information, + * implementing the {@link Attributes2} interface. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * </blockquote> + * + * <p>This is not part of core-only SAX2 distributions.</p> + * + * <p>The <em>specified</em> flag for each attribute will always + * be true, unless it has been set to false in the copy constructor + * or using {@link #setSpecified}. + * Similarly, the <em>declared</em> flag for each attribute will + * always be false, except for defaulted attributes (<em>specified</em> + * is false), non-CDATA attributes, or when it is set to true using + * {@link #setDeclared}. + * If you change an attribute's type by hand, you may need to modify + * its <em>declared</em> flag to match. + * </p> + * + * @since SAX 2.0 (extensions 1.1 alpha) + * @author David Brownell + * @version TBS + */ +public class Attributes2Impl extends AttributesImpl implements Attributes2 +{ + private boolean declared []; + private boolean specified []; + + + /** + * Construct a new, empty Attributes2Impl object. + */ + public Attributes2Impl () { } + + + /** + * Copy an existing Attributes or Attributes2 object. + * If the object implements Attributes2, values of the + * <em>specified</em> and <em>declared</em> flags for each + * attribute are copied. + * Otherwise the flag values are defaulted to assume no DTD was used, + * unless there is evidence to the contrary (such as attributes with + * type other than CDATA, which must have been <em>declared</em>). + * + * <p>This constructor is especially useful inside a + * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p> + * + * @param atts The existing Attributes object. + */ + public Attributes2Impl (Attributes atts) + { + super (atts); + } + + + //////////////////////////////////////////////////////////////////// + // Implementation of Attributes2 + //////////////////////////////////////////////////////////////////// + + + /* + * Returns the current value of the attribute's "declared" flag. + */ + // javadoc mostly from interface + public boolean isDeclared (int index) + { + if (index < 0 || index >= getLength ()) + throw new ArrayIndexOutOfBoundsException ( + "No attribute at index: " + index); + return declared [index]; + } + + + /* + * Returns the current value of the attribute's "declared" flag. + */ + // javadoc mostly from interface + public boolean isDeclared (String uri, String localName) + { + int index = getIndex (uri, localName); + + if (index < 0) + throw new IllegalArgumentException ( + "No such attribute: local=" + localName + + ", namespace=" + uri); + return declared [index]; + } + + + /* + * Returns the current value of the attribute's "declared" flag. + */ + // javadoc mostly from interface + public boolean isDeclared (String qName) + { + int index = getIndex (qName); + + if (index < 0) + throw new IllegalArgumentException ( + "No such attribute: " + qName); + return declared [index]; + } + + + /** + * Returns the current value of an attribute's "specified" flag. + * + * @param index The attribute index (zero-based). + * @return current flag value + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not identify an attribute. + */ + public boolean isSpecified (int index) + { + if (index < 0 || index >= getLength ()) + throw new ArrayIndexOutOfBoundsException ( + "No attribute at index: " + index); + return specified [index]; + } + + + /** + * Returns the current value of an attribute's "specified" flag. + * + * @param uri The Namespace URI, or the empty string if + * the name has no Namespace URI. + * @param localName The attribute's local name. + * @return current flag value + * @exception java.lang.IllegalArgumentException When the + * supplied names do not identify an attribute. + */ + public boolean isSpecified (String uri, String localName) + { + int index = getIndex (uri, localName); + + if (index < 0) + throw new IllegalArgumentException ( + "No such attribute: local=" + localName + + ", namespace=" + uri); + return specified [index]; + } + + + /** + * Returns the current value of an attribute's "specified" flag. + * + * @param qName The XML qualified (prefixed) name. + * @return current flag value + * @exception java.lang.IllegalArgumentException When the + * supplied name does not identify an attribute. + */ + public boolean isSpecified (String qName) + { + int index = getIndex (qName); + + if (index < 0) + throw new IllegalArgumentException ( + "No such attribute: " + qName); + return specified [index]; + } + + + //////////////////////////////////////////////////////////////////// + // Manipulators + //////////////////////////////////////////////////////////////////// + + + /** + * Copy an entire Attributes object. The "specified" flags are + * assigned as true, and "declared" flags as false (except when + * an attribute's type is not CDATA), + * unless the object is an Attributes2 object. + * In that case those flag values are all copied. + * + * @param atts The attributes to copy. + * + * @see AttributesImpl#setAttributes + */ + public void setAttributes (Attributes atts) + { + int length = atts.getLength (); + + super.setAttributes (atts); + declared = new boolean [length]; + specified = new boolean [length]; + + if (atts instanceof Attributes2) { + Attributes2 a2 = (Attributes2) atts; + for (int i = 0; i < length; i++) { + declared [i] = a2.isDeclared (i); + specified [i] = a2.isSpecified (i); + } + } else { + for (int i = 0; i < length; i++) { + declared [i] = !"CDATA".equals (atts.getType (i)); + specified [i] = true; + } + } + } + + + /** + * Add an attribute to the end of the list, setting its + * "specified" flag to true. To set that flag's value + * to false, use {@link #setSpecified}. + * + * <p>Unless the attribute <em>type</em> is CDATA, this attribute + * is marked as being declared in the DTD. To set that flag's value + * to true for CDATA attributes, use {@link #setDeclared}. + * + * @param uri The Namespace URI, or the empty string if + * none is available or Namespace processing is not + * being performed. + * @param localName The local name, or the empty string if + * Namespace processing is not being performed. + * @param qName The qualified (prefixed) name, or the empty string + * if qualified names are not available. + * @param type The attribute type as a string. + * @param value The attribute value. + * + * @see AttributesImpl#addAttribute + */ + public void addAttribute (String uri, String localName, String qName, + String type, String value) + { + super.addAttribute (uri, localName, qName, type, value); + + int length = getLength (); + + if (length < specified.length) { + boolean newFlags []; + + newFlags = new boolean [length]; + System.arraycopy (declared, 0, newFlags, 0, declared.length); + declared = newFlags; + + newFlags = new boolean [length]; + System.arraycopy (specified, 0, newFlags, 0, specified.length); + specified = newFlags; + } + + specified [length - 1] = true; + declared [length - 1] = !"CDATA".equals (type); + } + + + // javadoc entirely from superclass + public void removeAttribute (int index) + { + int origMax = getLength () - 1; + + super.removeAttribute (index); + if (index != origMax) { + System.arraycopy (declared, index + 1, declared, index, + origMax - index); + System.arraycopy (specified, index + 1, specified, index, + origMax - index); + } + } + + + /** + * Assign a value to the "declared" flag of a specific attribute. + * This is normally needed only for attributes of type CDATA, + * including attributes whose type is changed to or from CDATA. + * + * @param index The index of the attribute (zero-based). + * @param value The desired flag value. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not identify an attribute. + * @see #setType + */ + public void setDeclared (int index, boolean value) + { + if (index < 0 || index >= getLength ()) + throw new ArrayIndexOutOfBoundsException ( + "No attribute at index: " + index); + declared [index] = value; + } + + + /** + * Assign a value to the "specified" flag of a specific attribute. + * This is the only way this flag can be cleared, except clearing + * by initialization with the copy constructor. + * + * @param index The index of the attribute (zero-based). + * @param value The desired flag value. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not identify an attribute. + */ + public void setSpecified (int index, boolean value) + { + if (index < 0 || index >= getLength ()) + throw new ArrayIndexOutOfBoundsException ( + "No attribute at index: " + index); + specified [index] = value; + } +} diff --git a/xml/src/main/java/org/xml/sax/ext/DeclHandler.java b/xml/src/main/java/org/xml/sax/ext/DeclHandler.java new file mode 100644 index 0000000..26471f3 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/DeclHandler.java @@ -0,0 +1,146 @@ +// DeclHandler.java - Optional handler for DTD declaration events. +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: DeclHandler.java,v 1.6 2004/04/22 13:28:49 dmegginson Exp $ + +package org.xml.sax.ext; + +import org.xml.sax.SAXException; + + +/** + * SAX2 extension handler for DTD declaration events. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This is an optional extension handler for SAX2 to provide more + * complete information about DTD declarations in an XML document. + * XML readers are not required to recognize this handler, and it + * is not part of core-only SAX2 distributions.</p> + * + * <p>Note that data-related DTD declarations (unparsed entities and + * notations) are already reported through the {@link + * org.xml.sax.DTDHandler DTDHandler} interface.</p> + * + * <p>If you are using the declaration handler together with a lexical + * handler, all of the events will occur between the + * {@link org.xml.sax.ext.LexicalHandler#startDTD startDTD} and the + * {@link org.xml.sax.ext.LexicalHandler#endDTD endDTD} events.</p> + * + * <p>To set the DeclHandler for an XML reader, use the + * {@link org.xml.sax.XMLReader#setProperty setProperty} method + * with the property name + * <code>http://xml.org/sax/properties/declaration-handler</code> + * and an object implementing this interface (or null) as the value. + * If the reader does not report declaration events, it will throw a + * {@link org.xml.sax.SAXNotRecognizedException SAXNotRecognizedException} + * when you attempt to register the handler.</p> + * + * @since SAX 2.0 (extensions 1.0) + * @author David Megginson + * @version 2.0.1 (sax2r2) + */ +public interface DeclHandler +{ + + /** + * Report an element type declaration. + * + * <p>The content model will consist of the string "EMPTY", the + * string "ANY", or a parenthesised group, optionally followed + * by an occurrence indicator. The model will be normalized so + * that all parameter entities are fully resolved and all whitespace + * is removed,and will include the enclosing parentheses. Other + * normalization (such as removing redundant parentheses or + * simplifying occurrence indicators) is at the discretion of the + * parser.</p> + * + * @param name The element type name. + * @param model The content model as a normalized string. + * @exception SAXException The application may raise an exception. + */ + public abstract void elementDecl (String name, String model) + throws SAXException; + + + /** + * Report an attribute type declaration. + * + * <p>Only the effective (first) declaration for an attribute will + * be reported. The type will be one of the strings "CDATA", + * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", + * "ENTITIES", a parenthesized token group with + * the separator "|" and all whitespace removed, or the word + * "NOTATION" followed by a space followed by a parenthesized + * token group with all whitespace removed.</p> + * + * <p>The value will be the value as reported to applications, + * appropriately normalized and with entity and character + * references expanded. </p> + * + * @param eName The name of the associated element. + * @param aName The name of the attribute. + * @param type A string representing the attribute type. + * @param mode A string representing the attribute defaulting mode + * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if + * none of these applies. + * @param value A string representing the attribute's default value, + * or null if there is none. + * @exception SAXException The application may raise an exception. + */ + public abstract void attributeDecl (String eName, + String aName, + String type, + String mode, + String value) + throws SAXException; + + + /** + * Report an internal entity declaration. + * + * <p>Only the effective (first) declaration for each entity + * will be reported. All parameter entities in the value + * will be expanded, but general entities will not.</p> + * + * @param name The name of the entity. If it is a parameter + * entity, the name will begin with '%'. + * @param value The replacement text of the entity. + * @exception SAXException The application may raise an exception. + * @see #externalEntityDecl + * @see org.xml.sax.DTDHandler#unparsedEntityDecl + */ + public abstract void internalEntityDecl (String name, String value) + throws SAXException; + + + /** + * Report a parsed external entity declaration. + * + * <p>Only the effective (first) declaration for each entity + * will be reported.</p> + * + * <p>If the system identifier is a URL, the parser must resolve it + * fully before passing it to the application.</p> + * + * @param name The name of the entity. If it is a parameter + * entity, the name will begin with '%'. + * @param publicId The entity's public identifier, or null if none + * was given. + * @param systemId The entity's system identifier. + * @exception SAXException The application may raise an exception. + * @see #internalEntityDecl + * @see org.xml.sax.DTDHandler#unparsedEntityDecl + */ + public abstract void externalEntityDecl (String name, String publicId, + String systemId) + throws SAXException; + +} + +// end of DeclHandler.java diff --git a/xml/src/main/java/org/xml/sax/ext/DefaultHandler2.java b/xml/src/main/java/org/xml/sax/ext/DefaultHandler2.java new file mode 100644 index 0000000..f26564f --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/DefaultHandler2.java @@ -0,0 +1,185 @@ +// DefaultHandler2.java - extended DefaultHandler +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: DefaultHandler2.java,v 1.3 2002/01/12 19:04:19 dbrownell Exp $ + +package org.xml.sax.ext; + +import java.io.IOException; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + + +/** + * This class extends the SAX2 base handler class to support the + * SAX2 {@link LexicalHandler}, {@link DeclHandler}, and + * {@link EntityResolver2} extensions. Except for overriding the + * original SAX1 {@link DefaultHandler#resolveEntity resolveEntity()} + * method the added handler methods just return. Subclassers may + * override everything on a method-by-method basis. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * </blockquote> + * + * <p> <em>Note:</em> this class might yet learn that the + * <em>ContentHandler.setDocumentLocator()</em> call might be passed a + * {@link Locator2} object, and that the + * <em>ContentHandler.startElement()</em> call might be passed a + * {@link Attributes2} object. + * + * @since SAX 2.0 (extensions 1.1 alpha) + * @author David Brownell + * @version TBS + */ +public class DefaultHandler2 extends DefaultHandler + implements LexicalHandler, DeclHandler, EntityResolver2 +{ + /** Constructs a handler which ignores all parsing events. */ + public DefaultHandler2 () { } + + + // SAX2 ext-1.0 LexicalHandler + + public void startCDATA () + throws SAXException + {} + + public void endCDATA () + throws SAXException + {} + + public void startDTD (String name, String publicId, String systemId) + throws SAXException + {} + + public void endDTD () + throws SAXException + {} + + public void startEntity (String name) + throws SAXException + {} + + public void endEntity (String name) + throws SAXException + {} + + public void comment (char ch [], int start, int length) + throws SAXException + { } + + + // SAX2 ext-1.0 DeclHandler + + public void attributeDecl (String eName, String aName, + String type, String mode, String value) + throws SAXException + {} + + public void elementDecl (String name, String model) + throws SAXException + {} + + public void externalEntityDecl (String name, + String publicId, String systemId) + throws SAXException + {} + + public void internalEntityDecl (String name, String value) + throws SAXException + {} + + // SAX2 ext-1.1 EntityResolver2 + + /** + * Tells the parser that if no external subset has been declared + * in the document text, none should be used. + * + * @param name Identifies the document root element. This name comes + * from a DOCTYPE declaration (where available) or from the actual + * root element. The parameter is ignored. + * @param baseURI The document's base URI, serving as an additional + * hint for selecting the external subset. This is always an absolute + * URI, unless it is null because the XMLReader was given an InputSource + * without one. The parameter is ignored. + * + * @return null (always). + * + * @exception SAXException Any SAX exception, possibly wrapping + * another exception. + * @exception IOException Probably indicating a failure to create + * a new InputStream or Reader, or an illegal URL. + */ + public InputSource getExternalSubset (String name, String baseURI) + throws SAXException, IOException + { return null; } + + /** + * Tells the parser to resolve the systemId against the baseURI + * and read the entity text from that resulting absolute URI. + * Note that because the older + * {@link DefaultHandler#resolveEntity DefaultHandler.resolveEntity()}, + * method is overridden to call this one, this method may sometimes + * be invoked with null <em>name</em> and <em>baseURI</em>, and + * with the <em>systemId</em> already absolutized. + * + * @param name Identifies the external entity being resolved. + * Either "[dtd]" for the external subset, or a name starting + * with "%" to indicate a parameter entity, or else the name of + * a general entity. This is never null when invoked by a SAX2 + * parser. + * @param publicId The public identifier of the external entity being + * referenced (normalized as required by the XML specification), or + * null if none was supplied. + * @param baseURI The URI with respect to which relative systemIDs + * are interpreted. This is always an absolute URI, unless it is + * null (likely because the XMLReader was given an InputSource without + * one). This URI is defined by the XML specification to be the one + * associated with the "<" starting the relevant declaration. + * @param systemId The system identifier of the external entity + * being referenced; either a relative or absolute URI. + * This is never null when invoked by a SAX2 parser; only declared + * entities, and any external subset, are resolved by such parsers. + * + * @return An InputSource object describing the new input source. + * + * @exception SAXException Any SAX exception, possibly wrapping + * another exception. + * @exception IOException Probably indicating a failure to create + * a new InputStream or Reader, or an illegal URL. + */ + public InputSource resolveEntity (String name, String publicId, + String baseURI, String systemId) + throws SAXException, IOException + { return null; } + + // SAX1 EntityResolver + + /** + * Invokes + * {@link EntityResolver2#resolveEntity EntityResolver2.resolveEntity()} + * with null entity name and base URI. + * You only need to override that method to use this class. + * + * @param publicId The public identifier of the external entity being + * referenced (normalized as required by the XML specification), or + * null if none was supplied. + * @param systemId The system identifier of the external entity + * being referenced; either a relative or absolute URI. + * This is never null when invoked by a SAX2 parser; only declared + * entities, and any external subset, are resolved by such parsers. + * + * @return An InputSource object describing the new input source. + * + * @exception SAXException Any SAX exception, possibly wrapping + * another exception. + * @exception IOException Probably indicating a failure to create + * a new InputStream or Reader, or an illegal URL. + */ + public InputSource resolveEntity (String publicId, String systemId) + throws SAXException, IOException + { return resolveEntity (null, publicId, null, systemId); } +} diff --git a/xml/src/main/java/org/xml/sax/ext/EntityResolver2.java b/xml/src/main/java/org/xml/sax/ext/EntityResolver2.java new file mode 100644 index 0000000..5437a9b --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/EntityResolver2.java @@ -0,0 +1,197 @@ +// EntityResolver2.java - Extended SAX entity resolver. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: EntityResolver2.java,v 1.2 2002/01/12 19:20:08 dbrownell Exp $ + +package org.xml.sax.ext; + +import java.io.IOException; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.SAXException; + + +/** + * Extended interface for mapping external entity references to input + * sources, or providing a missing external subset. The + * {@link XMLReader#setEntityResolver XMLReader.setEntityResolver()} method + * is used to provide implementations of this interface to parsers. + * When a parser uses the methods in this interface, the + * {@link EntityResolver2#resolveEntity EntityResolver2.resolveEntity()} + * method (in this interface) is used <em>instead of</em> the older (SAX 1.0) + * {@link EntityResolver#resolveEntity EntityResolver.resolveEntity()} method. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * </blockquote> + * + * <p>If a SAX application requires the customized handling which this + * interface defines for external entities, it must ensure that it uses + * an XMLReader with the + * <em>http://xml.org/sax/features/use-entity-resolver2</em> feature flag + * set to <em>true</em> (which is its default value when the feature is + * recognized). If that flag is unrecognized, or its value is false, + * or the resolver does not implement this interface, then only the + * {@link EntityResolver} method will be used. + * </p> + * + * <p>That supports three categories of application that modify entity + * resolution. <em>Old Style</em> applications won't know about this interface; + * they will provide an EntityResolver. + * <em>Transitional Mode</em> provide an EntityResolver2 and automatically + * get the benefit of its methods in any systems (parsers or other tools) + * supporting it, due to polymorphism. + * Both <em>Old Style</em> and <em>Transitional Mode</em> applications will + * work with any SAX2 parser. + * <em>New style</em> applications will fail to run except on SAX2 parsers + * that support this particular feature. + * They will insist that feature flag have a value of "true", and the + * EntityResolver2 implementation they provide might throw an exception + * if the original SAX 1.0 style entity resolution method is invoked. + * </p> + * + * @see org.xml.sax.XMLReader#setEntityResolver + * + * @since SAX 2.0 (extensions 1.1 alpha) + * @author David Brownell + * @version TBD + */ +public interface EntityResolver2 extends EntityResolver +{ + /** + * Allows applications to provide an external subset for documents + * that don't explicitly define one. Documents with DOCTYPE declarations + * that omit an external subset can thus augment the declarations + * available for validation, entity processing, and attribute processing + * (normalization, defaulting, and reporting types including ID). + * This augmentation is reported + * through the {@link LexicalHandler#startDTD startDTD()} method as if + * the document text had originally included the external subset; + * this callback is made before any internal subset data or errors + * are reported.</p> + * + * <p>This method can also be used with documents that have no DOCTYPE + * declaration. When the root element is encountered, + * but no DOCTYPE declaration has been seen, this method is + * invoked. If it returns a value for the external subset, that root + * element is declared to be the root element, giving the effect of + * splicing a DOCTYPE declaration at the end the prolog of a document + * that could not otherwise be valid. The sequence of parser callbacks + * in that case logically resembles this:</p> + * + * <pre> + * ... comments and PIs from the prolog (as usual) + * startDTD ("rootName", source.getPublicId (), source.getSystemId ()); + * startEntity ("[dtd]"); + * ... declarations, comments, and PIs from the external subset + * endEntity ("[dtd]"); + * endDTD (); + * ... then the rest of the document (as usual) + * startElement (..., "rootName", ...); + * </pre> + * + * <p>Note that the InputSource gets no further resolution. + * Implementations of this method may wish to invoke + * {@link #resolveEntity resolveEntity()} to gain benefits such as use + * of local caches of DTD entities. Also, this method will never be + * used by a (non-validating) processor that is not including external + * parameter entities. </p> + * + * <p>Uses for this method include facilitating data validation when + * interoperating with XML processors that would always require + * undesirable network accesses for external entities, or which for + * other reasons adopt a "no DTDs" policy. + * Non-validation motives include forcing documents to include DTDs so + * that attributes are handled consistently. + * For example, an XPath processor needs to know which attibutes have + * type "ID" before it can process a widely used type of reference.</p> + * + * <p><strong>Warning:</strong> Returning an external subset modifies + * the input document. By providing definitions for general entities, + * it can make a malformed document appear to be well formed. + * </p> + * + * @param name Identifies the document root element. This name comes + * from a DOCTYPE declaration (where available) or from the actual + * root element. + * @param baseURI The document's base URI, serving as an additional + * hint for selecting the external subset. This is always an absolute + * URI, unless it is null because the XMLReader was given an InputSource + * without one. + * + * @return An InputSource object describing the new external subset + * to be used by the parser, or null to indicate that no external + * subset is provided. + * + * @exception SAXException Any SAX exception, possibly wrapping + * another exception. + * @exception IOException Probably indicating a failure to create + * a new InputStream or Reader, or an illegal URL. + */ + public InputSource getExternalSubset (String name, String baseURI) + throws SAXException, IOException; + + /** + * Allows applications to map references to external entities into input + * sources, or tell the parser it should use conventional URI resolution. + * This method is only called for external entities which have been + * properly declared. + * This method provides more flexibility than the {@link EntityResolver} + * interface, supporting implementations of more complex catalogue + * schemes such as the one defined by the <a href= + "http://www.oasis-open.org/committees/entity/spec-2001-08-06.html" + >OASIS XML Catalogs</a> specification.</p> + * + * <p>Parsers configured to use this resolver method will call it + * to determine the input source to use for any external entity + * being included because of a reference in the XML text. + * That excludes the document entity, and any external entity returned + * by {@link #getExternalSubset getExternalSubset()}. + * When a (non-validating) processor is configured not to include + * a class of entities (parameter or general) through use of feature + * flags, this method is not invoked for such entities. </p> + * + * <p>Note that the entity naming scheme used here is the same one + * used in the {@link LexicalHandler}, or in the {@link + org.xml.sax.ContentHandler#skippedEntity + ContentHandler.skippedEntity()} + * method. </p> + * + * @param name Identifies the external entity being resolved. + * Either "[dtd]" for the external subset, or a name starting + * with "%" to indicate a parameter entity, or else the name of + * a general entity. This is never null when invoked by a SAX2 + * parser. + * @param publicId The public identifier of the external entity being + * referenced (normalized as required by the XML specification), or + * null if none was supplied. + * @param baseURI The URI with respect to which relative systemIDs + * are interpreted. This is always an absolute URI, unless it is + * null (likely because the XMLReader was given an InputSource without + * one). This URI is defined by the XML specification to be the one + * associated with the "<" starting the relevant declaration. + * @param systemId The system identifier of the external entity + * being referenced; either a relative or absolute URI. + * This is never null when invoked by a SAX2 parser; only declared + * entities, and any external subset, are resolved by such parsers. + * + * @return An InputSource object describing the new input source to + * be used by the parser. Returning null directs the parser to + * resolve the system ID against the base URI and open a connection + * to resulting URI. + * + * @exception SAXException Any SAX exception, possibly wrapping + * another exception. + * @exception IOException Probably indicating a failure to create + * a new InputStream or Reader, or an illegal URL. + */ + public InputSource resolveEntity ( + String name, + String publicId, + String baseURI, + String systemId + ) throws SAXException, IOException; +} diff --git a/xml/src/main/java/org/xml/sax/ext/LexicalHandler.java b/xml/src/main/java/org/xml/sax/ext/LexicalHandler.java new file mode 100644 index 0000000..e551b61 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/LexicalHandler.java @@ -0,0 +1,212 @@ +// LexicalHandler.java - optional handler for lexical parse events. +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: LexicalHandler.java,v 1.5 2002/01/30 21:00:44 dbrownell Exp $ + +package org.xml.sax.ext; + +import org.xml.sax.SAXException; + +/** + * SAX2 extension handler for lexical events. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This is an optional extension handler for SAX2 to provide + * lexical information about an XML document, such as comments + * and CDATA section boundaries. + * XML readers are not required to recognize this handler, and it + * is not part of core-only SAX2 distributions.</p> + * + * <p>The events in the lexical handler apply to the entire document, + * not just to the document element, and all lexical handler events + * must appear between the content handler's startDocument and + * endDocument events.</p> + * + * <p>To set the LexicalHandler for an XML reader, use the + * {@link org.xml.sax.XMLReader#setProperty setProperty} method + * with the property name + * <code>http://xml.org/sax/properties/lexical-handler</code> + * and an object implementing this interface (or null) as the value. + * If the reader does not report lexical events, it will throw a + * {@link org.xml.sax.SAXNotRecognizedException SAXNotRecognizedException} + * when you attempt to register the handler.</p> + * + * @since SAX 2.0 (extensions 1.0) + * @author David Megginson + * @version 2.0.1 (sax2r2) + */ +public interface LexicalHandler +{ + + /** + * Report the start of DTD declarations, if any. + * + * <p>This method is intended to report the beginning of the + * DOCTYPE declaration; if the document has no DOCTYPE declaration, + * this method will not be invoked.</p> + * + * <p>All declarations reported through + * {@link org.xml.sax.DTDHandler DTDHandler} or + * {@link org.xml.sax.ext.DeclHandler DeclHandler} events must appear + * between the startDTD and {@link #endDTD endDTD} events. + * Declarations are assumed to belong to the internal DTD subset + * unless they appear between {@link #startEntity startEntity} + * and {@link #endEntity endEntity} events. Comments and + * processing instructions from the DTD should also be reported + * between the startDTD and endDTD events, in their original + * order of (logical) occurrence; they are not required to + * appear in their correct locations relative to DTDHandler + * or DeclHandler events, however.</p> + * + * <p>Note that the start/endDTD events will appear within + * the start/endDocument events from ContentHandler and + * before the first + * {@link org.xml.sax.ContentHandler#startElement startElement} + * event.</p> + * + * @param name The document type name. + * @param publicId The declared public identifier for the + * external DTD subset, or null if none was declared. + * @param systemId The declared system identifier for the + * external DTD subset, or null if none was declared. + * (Note that this is not resolved against the document + * base URI.) + * @exception SAXException The application may raise an + * exception. + * @see #endDTD + * @see #startEntity + */ + public abstract void startDTD (String name, String publicId, + String systemId) + throws SAXException; + + + /** + * Report the end of DTD declarations. + * + * <p>This method is intended to report the end of the + * DOCTYPE declaration; if the document has no DOCTYPE declaration, + * this method will not be invoked.</p> + * + * @exception SAXException The application may raise an exception. + * @see #startDTD + */ + public abstract void endDTD () + throws SAXException; + + + /** + * Report the beginning of some internal and external XML entities. + * + * <p>The reporting of parameter entities (including + * the external DTD subset) is optional, and SAX2 drivers that + * report LexicalHandler events may not implement it; you can use the + * <code + * >http://xml.org/sax/features/lexical-handler/parameter-entities</code> + * feature to query or control the reporting of parameter entities.</p> + * + * <p>General entities are reported with their regular names, + * parameter entities have '%' prepended to their names, and + * the external DTD subset has the pseudo-entity name "[dtd]".</p> + * + * <p>When a SAX2 driver is providing these events, all other + * events must be properly nested within start/end entity + * events. There is no additional requirement that events from + * {@link org.xml.sax.ext.DeclHandler DeclHandler} or + * {@link org.xml.sax.DTDHandler DTDHandler} be properly ordered.</p> + * + * <p>Note that skipped entities will be reported through the + * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity} + * event, which is part of the ContentHandler interface.</p> + * + * <p>Because of the streaming event model that SAX uses, some + * entity boundaries cannot be reported under any + * circumstances:</p> + * + * <ul> + * <li>general entities within attribute values</li> + * <li>parameter entities within declarations</li> + * </ul> + * + * <p>These will be silently expanded, with no indication of where + * the original entity boundaries were.</p> + * + * <p>Note also that the boundaries of character references (which + * are not really entities anyway) are not reported.</p> + * + * <p>All start/endEntity events must be properly nested. + * + * @param name The name of the entity. If it is a parameter + * entity, the name will begin with '%', and if it is the + * external DTD subset, it will be "[dtd]". + * @exception SAXException The application may raise an exception. + * @see #endEntity + * @see org.xml.sax.ext.DeclHandler#internalEntityDecl + * @see org.xml.sax.ext.DeclHandler#externalEntityDecl + */ + public abstract void startEntity (String name) + throws SAXException; + + + /** + * Report the end of an entity. + * + * @param name The name of the entity that is ending. + * @exception SAXException The application may raise an exception. + * @see #startEntity + */ + public abstract void endEntity (String name) + throws SAXException; + + + /** + * Report the start of a CDATA section. + * + * <p>The contents of the CDATA section will be reported through + * the regular {@link org.xml.sax.ContentHandler#characters + * characters} event; this event is intended only to report + * the boundary.</p> + * + * @exception SAXException The application may raise an exception. + * @see #endCDATA + */ + public abstract void startCDATA () + throws SAXException; + + + /** + * Report the end of a CDATA section. + * + * @exception SAXException The application may raise an exception. + * @see #startCDATA + */ + public abstract void endCDATA () + throws SAXException; + + + /** + * Report an XML comment anywhere in the document. + * + * <p>This callback will be used for comments inside or outside the + * document element, including comments in the external DTD + * subset (if read). Comments in the DTD must be properly + * nested inside start/endDTD and start/endEntity events (if + * used).</p> + * + * @param ch An array holding the characters in the comment. + * @param start The starting position in the array. + * @param length The number of characters to use from the array. + * @exception SAXException The application may raise an exception. + */ + public abstract void comment (char ch[], int start, int length) + throws SAXException; + +} + +// end of LexicalHandler.java diff --git a/xml/src/main/java/org/xml/sax/ext/Locator2.java b/xml/src/main/java/org/xml/sax/ext/Locator2.java new file mode 100644 index 0000000..6de9a16 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/Locator2.java @@ -0,0 +1,75 @@ +// Locator2.java - extended Locator +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: Locator2.java,v 1.5 2004/03/17 14:30:10 dmegginson Exp $ + +package org.xml.sax.ext; + +import org.xml.sax.Locator; + + +/** + * SAX2 extension to augment the entity information provided + * though a {@link Locator}. + * If an implementation supports this extension, the Locator + * provided in {@link org.xml.sax.ContentHandler#setDocumentLocator + * ContentHandler.setDocumentLocator() } will implement this + * interface, and the + * <em>http://xml.org/sax/features/use-locator2</em> feature + * flag will have the value <em>true</em>. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * </blockquote> + * + * <p> XMLReader implementations are not required to support this + * information, and it is not part of core-only SAX2 distributions.</p> + * + * @since SAX 2.0 (extensions 1.1 alpha) + * @author David Brownell + * @version TBS + */ +public interface Locator2 extends Locator +{ + /** + * Returns the version of XML used for the entity. This will + * normally be the identifier from the current entity's + * <em><?xml version='...' ...?></em> declaration, + * or be defaulted by the parser. + * + * @return Identifier for the XML version being used to interpret + * the entity's text, or null if that information is not yet + * available in the current parsing state. + */ + public String getXMLVersion (); + + /** + * Returns the name of the character encoding for the entity. + * If the encoding was declared externally (for example, in a MIME + * Content-Type header), that will be the name returned. Else if there + * was an <em><?xml ...encoding='...'?></em> declaration at + * the start of the document, that encoding name will be returned. + * Otherwise the encoding will been inferred (normally to be UTF-8, or + * some UTF-16 variant), and that inferred name will be returned. + * + * <p>When an {@link org.xml.sax.InputSource InputSource} is used + * to provide an entity's character stream, this method returns the + * encoding provided in that input stream. + * + * <p> Note that some recent W3C specifications require that text + * in some encodings be normalized, using Unicode Normalization + * Form C, before processing. Such normalization must be performed + * by applications, and would normally be triggered based on the + * value returned by this method. + * + * <p> Encoding names may be those used by the underlying JVM, + * and comparisons should be case-insensitive. + * + * @return Name of the character encoding being used to interpret + * * the entity's text, or null if this was not provided for a * + * character stream passed through an InputSource or is otherwise + * not yet available in the current parsing state. + */ + public String getEncoding (); +} diff --git a/xml/src/main/java/org/xml/sax/ext/Locator2Impl.java b/xml/src/main/java/org/xml/sax/ext/Locator2Impl.java new file mode 100644 index 0000000..91f912a --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/Locator2Impl.java @@ -0,0 +1,105 @@ +// Locator2Impl.java - extended LocatorImpl +// http://www.saxproject.org +// Public Domain: no warranty. +// $Id: Locator2Impl.java,v 1.3 2004/04/26 17:34:35 dmegginson Exp $ + +package org.xml.sax.ext; + +import org.xml.sax.Locator; +import org.xml.sax.helpers.LocatorImpl; + + +/** + * SAX2 extension helper for holding additional Entity information, + * implementing the {@link Locator2} interface. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * </blockquote> + * + * <p> This is not part of core-only SAX2 distributions.</p> + * + * @since SAX 2.0.2 + * @author David Brownell + * @version TBS + */ +public class Locator2Impl extends LocatorImpl implements Locator2 +{ + private String encoding; + private String version; + + + /** + * Construct a new, empty Locator2Impl object. + * This will not normally be useful, since the main purpose + * of this class is to make a snapshot of an existing Locator. + */ + public Locator2Impl () { } + + /** + * Copy an existing Locator or Locator2 object. + * If the object implements Locator2, values of the + * <em>encoding</em> and <em>version</em>strings are copied, + * otherwise they set to <em>null</em>. + * + * @param locator The existing Locator object. + */ + public Locator2Impl (Locator locator) + { + super (locator); + if (locator instanceof Locator2) { + Locator2 l2 = (Locator2) locator; + + version = l2.getXMLVersion (); + encoding = l2.getEncoding (); + } + } + + //////////////////////////////////////////////////////////////////// + // Locator2 method implementations + //////////////////////////////////////////////////////////////////// + + /** + * Returns the current value of the version property. + * + * @return the current value of the version property. + * + * @see #setXMLVersion + */ + public String getXMLVersion () + { return version; } + + /** + * Returns the current value of the encoding property. + * + * @return the current value of the encoding property. + * + * @see #setEncoding + */ + public String getEncoding () + { return encoding; } + + + //////////////////////////////////////////////////////////////////// + // Setters + //////////////////////////////////////////////////////////////////// + + /** + * Assigns the current value of the version property. + * + * @param version the new "version" value + * @see #getXMLVersion + */ + public void setXMLVersion (String version) + { this.version = version; } + + /** + * Assigns the current value of the encoding property. + * + * @param encoding the new "encoding" value + * @see #getEncoding + */ + public void setEncoding (String encoding) + { this.encoding = encoding; } +} diff --git a/xml/src/main/java/org/xml/sax/ext/package.html b/xml/src/main/java/org/xml/sax/ext/package.html new file mode 100644 index 0000000..9b79d77 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/ext/package.html @@ -0,0 +1,48 @@ +<HTML><HEAD> +<!-- $Id: package.html,v 1.8 2002/01/30 21:00:44 dbrownell Exp $ --> +</HEAD><BODY> + +<p> +This package contains interfaces to SAX2 facilities that +conformant SAX drivers won't necessarily support. + +<p>See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> +for more information about SAX.</p> + +<p> This package is independent of the SAX2 core, though the functionality +exposed generally needs to be implemented within a parser core. +That independence has several consequences:</p> + +<ul> + +<li>SAX2 drivers are <em>not</em> required to recognize these handlers. +</li> + +<li>You cannot assume that the class files will be present in every SAX2 +installation.</li> + +<li>This package may be updated independently of SAX2 (i.e. new +handlers and classes may be added without updating SAX2 itself).</li> + +<li>The new handlers are not implemented by the SAX2 +<code>org.xml.sax.helpers.DefaultHandler</code> or +<code>org.xml.sax.helpers.XMLFilterImpl</code> classes. +You can subclass these if you need such behavior, or +use the helper classes found here.</li> + +<li>The handlers need to be registered differently than core SAX2 +handlers.</li> + +</ul> + +<p>This package, SAX2-ext, is a standardized extension to SAX2. It is +designed both to allow SAX parsers to pass certain types of information +to applications, and to serve as a simple model for other SAX2 parser +extension packages. Not all such extension packages should need to +be recognized directly by parsers, however. +As an example, most validation systems can be cleanly layered on top +of parsers supporting the standardized SAX2 interfaces. </p> + +@since Android 1.0 + +</BODY></HTML> diff --git a/xml/src/main/java/org/xml/sax/helpers/AttributeListImpl.java b/xml/src/main/java/org/xml/sax/helpers/AttributeListImpl.java new file mode 100644 index 0000000..fb4aee3 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/AttributeListImpl.java @@ -0,0 +1,314 @@ +// SAX default implementation for AttributeList. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: AttributeListImpl.java,v 1.6 2002/01/30 20:52:22 dbrownell Exp $ + +package org.xml.sax.helpers; + +import org.xml.sax.AttributeList; + +import java.util.Vector; + + +/** + * Default implementation for AttributeList. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>AttributeList implements the deprecated SAX1 {@link + * org.xml.sax.AttributeList AttributeList} interface, and has been + * replaced by the new SAX2 {@link org.xml.sax.helpers.AttributesImpl + * AttributesImpl} interface.</p> + * + * <p>This class provides a convenience implementation of the SAX + * {@link org.xml.sax.AttributeList AttributeList} interface. This + * implementation is useful both for SAX parser writers, who can use + * it to provide attributes to the application, and for SAX application + * writers, who can use it to create a persistent copy of an element's + * attribute specifications:</p> + * + * <pre> + * private AttributeList myatts; + * + * public void startElement (String name, AttributeList atts) + * { + * // create a persistent copy of the attribute list + * // for use outside this method + * myatts = new AttributeListImpl(atts); + * [...] + * } + * </pre> + * + * <p>Please note that SAX parsers are not required to use this + * class to provide an implementation of AttributeList; it is + * supplied only as an optional convenience. In particular, + * parser writers are encouraged to invent more efficient + * implementations.</p> + * + * @deprecated This class implements a deprecated interface, + * {@link org.xml.sax.AttributeList AttributeList}; + * that interface has been replaced by + * {@link org.xml.sax.Attributes Attributes}, + * which is implemented in the + * {@link org.xml.sax.helpers.AttributesImpl + * AttributesImpl} helper class. + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.AttributeList + * @see org.xml.sax.DocumentHandler#startElement + */ +public class AttributeListImpl implements AttributeList +{ + + /** + * Create an empty attribute list. + * + * <p>This constructor is most useful for parser writers, who + * will use it to create a single, reusable attribute list that + * can be reset with the clear method between elements.</p> + * + * @see #addAttribute + * @see #clear + */ + public AttributeListImpl () + { + } + + + /** + * Construct a persistent copy of an existing attribute list. + * + * <p>This constructor is most useful for application writers, + * who will use it to create a persistent copy of an existing + * attribute list.</p> + * + * @param atts The attribute list to copy + * @see org.xml.sax.DocumentHandler#startElement + */ + public AttributeListImpl (AttributeList atts) + { + setAttributeList(atts); + } + + + + //////////////////////////////////////////////////////////////////// + // Methods specific to this class. + //////////////////////////////////////////////////////////////////// + + + /** + * Set the attribute list, discarding previous contents. + * + * <p>This method allows an application writer to reuse an + * attribute list easily.</p> + * + * @param atts The attribute list to copy. + */ + public void setAttributeList (AttributeList atts) + { + int count = atts.getLength(); + + clear(); + + for (int i = 0; i < count; i++) { + addAttribute(atts.getName(i), atts.getType(i), atts.getValue(i)); + } + } + + + /** + * Add an attribute to an attribute list. + * + * <p>This method is provided for SAX parser writers, to allow them + * to build up an attribute list incrementally before delivering + * it to the application.</p> + * + * @param name The attribute name. + * @param type The attribute type ("NMTOKEN" for an enumeration). + * @param value The attribute value (must not be null). + * @see #removeAttribute + * @see org.xml.sax.DocumentHandler#startElement + */ + public void addAttribute (String name, String type, String value) + { + names.addElement(name); + types.addElement(type); + values.addElement(value); + } + + + /** + * Remove an attribute from the list. + * + * <p>SAX application writers can use this method to filter an + * attribute out of an AttributeList. Note that invoking this + * method will change the length of the attribute list and + * some of the attribute's indices.</p> + * + * <p>If the requested attribute is not in the list, this is + * a no-op.</p> + * + * @param name The attribute name. + * @see #addAttribute + */ + public void removeAttribute (String name) + { + int i = names.indexOf(name); + + if (i >= 0) { + names.removeElementAt(i); + types.removeElementAt(i); + values.removeElementAt(i); + } + } + + + /** + * Clear the attribute list. + * + * <p>SAX parser writers can use this method to reset the attribute + * list between DocumentHandler.startElement events. Normally, + * it will make sense to reuse the same AttributeListImpl object + * rather than allocating a new one each time.</p> + * + * @see org.xml.sax.DocumentHandler#startElement + */ + public void clear () + { + names.removeAllElements(); + types.removeAllElements(); + values.removeAllElements(); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.AttributeList + //////////////////////////////////////////////////////////////////// + + + /** + * Return the number of attributes in the list. + * + * @return The number of attributes in the list. + * @see org.xml.sax.AttributeList#getLength + */ + public int getLength () + { + return names.size(); + } + + + /** + * Get the name of an attribute (by position). + * + * @param i The position of the attribute in the list. + * @return The attribute name as a string, or null if there + * is no attribute at that position. + * @see org.xml.sax.AttributeList#getName(int) + */ + public String getName (int i) + { + if (i < 0) { + return null; + } + try { + return (String)names.elementAt(i); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + + + /** + * Get the type of an attribute (by position). + * + * @param i The position of the attribute in the list. + * @return The attribute type as a string ("NMTOKEN" for an + * enumeration, and "CDATA" if no declaration was + * read), or null if there is no attribute at + * that position. + * @see org.xml.sax.AttributeList#getType(int) + */ + public String getType (int i) + { + if (i < 0) { + return null; + } + try { + return (String)types.elementAt(i); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + + + /** + * Get the value of an attribute (by position). + * + * @param i The position of the attribute in the list. + * @return The attribute value as a string, or null if + * there is no attribute at that position. + * @see org.xml.sax.AttributeList#getValue(int) + */ + public String getValue (int i) + { + if (i < 0) { + return null; + } + try { + return (String)values.elementAt(i); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + + + /** + * Get the type of an attribute (by name). + * + * @param name The attribute name. + * @return The attribute type as a string ("NMTOKEN" for an + * enumeration, and "CDATA" if no declaration was + * read). + * @see org.xml.sax.AttributeList#getType(java.lang.String) + */ + public String getType (String name) + { + return getType(names.indexOf(name)); + } + + + /** + * Get the value of an attribute (by name). + * + * @param name The attribute name. + * @return the named attribute's value or null, if the attribute does not + * exist. + * @see org.xml.sax.AttributeList#getValue(java.lang.String) + */ + public String getValue (String name) + { + return getValue(names.indexOf(name)); + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + Vector names = new Vector(); + Vector types = new Vector(); + Vector values = new Vector(); + +} + +// end of AttributeListImpl.java diff --git a/xml/src/main/java/org/xml/sax/helpers/AttributesImpl.java b/xml/src/main/java/org/xml/sax/helpers/AttributesImpl.java new file mode 100644 index 0000000..b740e23 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/AttributesImpl.java @@ -0,0 +1,618 @@ +// AttributesImpl.java - default implementation of Attributes. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the public domain. +// $Id: AttributesImpl.java,v 1.9 2002/01/30 20:52:24 dbrownell Exp $ + +package org.xml.sax.helpers; + +import org.xml.sax.Attributes; + + +/** + * Default implementation of the Attributes interface. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class provides a default implementation of the SAX2 + * {@link org.xml.sax.Attributes Attributes} interface, with the + * addition of manipulators so that the list can be modified or + * reused.</p> + * + * <p>There are two typical uses of this class:</p> + * + * <ol> + * <li>to take a persistent snapshot of an Attributes object + * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li> + * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li> + * </ol> + * + * <p>This class replaces the now-deprecated SAX1 {@link + * org.xml.sax.helpers.AttributeListImpl AttributeListImpl} + * class; in addition to supporting the updated Attributes + * interface rather than the deprecated {@link org.xml.sax.AttributeList + * AttributeList} interface, it also includes a much more efficient + * implementation using a single array rather than a set of Vectors.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + */ +public class AttributesImpl implements Attributes +{ + + + //////////////////////////////////////////////////////////////////// + // Constructors. + //////////////////////////////////////////////////////////////////// + + + /** + * Construct a new, empty AttributesImpl object. + */ + public AttributesImpl () + { + length = 0; + data = null; + } + + + /** + * Copy an existing Attributes object. + * + * <p>This constructor is especially useful inside a + * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p> + * + * @param atts The existing Attributes object. + */ + public AttributesImpl (Attributes atts) + { + setAttributes(atts); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.Attributes. + //////////////////////////////////////////////////////////////////// + + + /** + * Return the number of attributes in the list. + * + * @return The number of attributes in the list. + * @see org.xml.sax.Attributes#getLength + */ + public int getLength () + { + return length; + } + + + /** + * Return an attribute's Namespace URI. + * + * @param index The attribute's index (zero-based). + * @return The Namespace URI, the empty string if none is + * available, or null if the index is out of range. + * @see org.xml.sax.Attributes#getURI + */ + public String getURI (int index) + { + if (index >= 0 && index < length) { + return data[index*5]; + } else { + return null; + } + } + + + /** + * Return an attribute's local name. + * + * @param index The attribute's index (zero-based). + * @return The attribute's local name, the empty string if + * none is available, or null if the index if out of range. + * @see org.xml.sax.Attributes#getLocalName + */ + public String getLocalName (int index) + { + if (index >= 0 && index < length) { + return data[index*5+1]; + } else { + return null; + } + } + + + /** + * Return an attribute's qualified (prefixed) name. + * + * @param index The attribute's index (zero-based). + * @return The attribute's qualified name, the empty string if + * none is available, or null if the index is out of bounds. + * @see org.xml.sax.Attributes#getQName + */ + public String getQName (int index) + { + if (index >= 0 && index < length) { + return data[index*5+2]; + } else { + return null; + } + } + + + /** + * Return an attribute's type by index. + * + * @param index The attribute's index (zero-based). + * @return The attribute's type, "CDATA" if the type is unknown, or null + * if the index is out of bounds. + * @see org.xml.sax.Attributes#getType(int) + */ + public String getType (int index) + { + if (index >= 0 && index < length) { + return data[index*5+3]; + } else { + return null; + } + } + + + /** + * Return an attribute's value by index. + * + * @param index The attribute's index (zero-based). + * @return The attribute's value or null if the index is out of bounds. + * @see org.xml.sax.Attributes#getValue(int) + */ + public String getValue (int index) + { + if (index >= 0 && index < length) { + return data[index*5+4]; + } else { + return null; + } + } + + + /** + * Look up an attribute's index by Namespace name. + * + * <p>In many cases, it will be more efficient to look up the name once and + * use the index query methods rather than using the name query methods + * repeatedly.</p> + * + * @param uri The attribute's Namespace URI, or the empty + * string if none is available. + * @param localName The attribute's local name. + * @return The attribute's index, or -1 if none matches. + * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String) + */ + public int getIndex (String uri, String localName) + { + int max = length * 5; + for (int i = 0; i < max; i += 5) { + if (data[i].equals(uri) && data[i+1].equals(localName)) { + return i / 5; + } + } + return -1; + } + + + /** + * Look up an attribute's index by qualified (prefixed) name. + * + * @param qName The qualified name. + * @return The attribute's index, or -1 if none matches. + * @see org.xml.sax.Attributes#getIndex(java.lang.String) + */ + public int getIndex (String qName) + { + int max = length * 5; + for (int i = 0; i < max; i += 5) { + if (data[i+2].equals(qName)) { + return i / 5; + } + } + return -1; + } + + + /** + * Look up an attribute's type by Namespace-qualified name. + * + * @param uri The Namespace URI, or the empty string for a name + * with no explicit Namespace URI. + * @param localName The local name. + * @return The attribute's type, or null if there is no + * matching attribute. + * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String) + */ + public String getType (String uri, String localName) + { + int max = length * 5; + for (int i = 0; i < max; i += 5) { + if (data[i].equals(uri) && data[i+1].equals(localName)) { + return data[i+3]; + } + } + return null; + } + + + /** + * Look up an attribute's type by qualified (prefixed) name. + * + * @param qName The qualified name. + * @return The attribute's type, or null if there is no + * matching attribute. + * @see org.xml.sax.Attributes#getType(java.lang.String) + */ + public String getType (String qName) + { + int max = length * 5; + for (int i = 0; i < max; i += 5) { + if (data[i+2].equals(qName)) { + return data[i+3]; + } + } + return null; + } + + + /** + * Look up an attribute's value by Namespace-qualified name. + * + * @param uri The Namespace URI, or the empty string for a name + * with no explicit Namespace URI. + * @param localName The local name. + * @return The attribute's value, or null if there is no + * matching attribute. + * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String) + */ + public String getValue (String uri, String localName) + { + int max = length * 5; + for (int i = 0; i < max; i += 5) { + if (data[i].equals(uri) && data[i+1].equals(localName)) { + return data[i+4]; + } + } + return null; + } + + + /** + * Look up an attribute's value by qualified (prefixed) name. + * + * @param qName The qualified name. + * @return The attribute's value, or null if there is no + * matching attribute. + * @see org.xml.sax.Attributes#getValue(java.lang.String) + */ + public String getValue (String qName) + { + int max = length * 5; + for (int i = 0; i < max; i += 5) { + if (data[i+2].equals(qName)) { + return data[i+4]; + } + } + return null; + } + + + + //////////////////////////////////////////////////////////////////// + // Manipulators. + //////////////////////////////////////////////////////////////////// + + + /** + * Clear the attribute list for reuse. + * + * <p>Note that little memory is freed by this call: + * the current array is kept so it can be + * reused.</p> + */ + public void clear () + { + if (data != null) { + for (int i = 0; i < (length * 5); i++) + data [i] = null; + } + length = 0; + } + + + /** + * Copy an entire Attributes object. + * + * <p>It may be more efficient to reuse an existing object + * rather than constantly allocating new ones.</p> + * + * @param atts The attributes to copy. + */ + public void setAttributes (Attributes atts) + { + clear(); + length = atts.getLength(); + if (length > 0) { + data = new String[length*5]; + for (int i = 0; i < length; i++) { + data[i*5] = atts.getURI(i); + data[i*5+1] = atts.getLocalName(i); + data[i*5+2] = atts.getQName(i); + data[i*5+3] = atts.getType(i); + data[i*5+4] = atts.getValue(i); + } + } + } + + + /** + * Add an attribute to the end of the list. + * + * <p>For the sake of speed, this method does no checking + * to see if the attribute is already in the list: that is + * the responsibility of the application.</p> + * + * @param uri The Namespace URI, or the empty string if + * none is available or Namespace processing is not + * being performed. + * @param localName The local name, or the empty string if + * Namespace processing is not being performed. + * @param qName The qualified (prefixed) name, or the empty string + * if qualified names are not available. + * @param type The attribute type as a string. + * @param value The attribute value. + */ + public void addAttribute (String uri, String localName, String qName, + String type, String value) + { + ensureCapacity(length+1); + data[length*5] = uri; + data[length*5+1] = localName; + data[length*5+2] = qName; + data[length*5+3] = type; + data[length*5+4] = value; + length++; + } + + + /** + * Set an attribute in the list. + * + * <p>For the sake of speed, this method does no checking + * for name conflicts or well-formedness: such checks are the + * responsibility of the application.</p> + * + * @param index The index of the attribute (zero-based). + * @param uri The Namespace URI, or the empty string if + * none is available or Namespace processing is not + * being performed. + * @param localName The local name, or the empty string if + * Namespace processing is not being performed. + * @param qName The qualified name, or the empty string + * if qualified names are not available. + * @param type The attribute type as a string. + * @param value The attribute value. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void setAttribute (int index, String uri, String localName, + String qName, String type, String value) + { + if (index >= 0 && index < length) { + data[index*5] = uri; + data[index*5+1] = localName; + data[index*5+2] = qName; + data[index*5+3] = type; + data[index*5+4] = value; + } else { + badIndex(index); + } + } + + + /** + * Remove an attribute from the list. + * + * @param index The index of the attribute (zero-based). + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void removeAttribute (int index) + { + if (index >= 0 && index < length) { + if (index < length - 1) { + System.arraycopy(data, (index+1)*5, data, index*5, + (length-index-1)*5); + } + index = (length - 1) * 5; + data [index++] = null; + data [index++] = null; + data [index++] = null; + data [index++] = null; + data [index] = null; + length--; + } else { + badIndex(index); + } + } + + + /** + * Set the Namespace URI of a specific attribute. + * + * @param index The index of the attribute (zero-based). + * @param uri The attribute's Namespace URI, or the empty + * string for none. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void setURI (int index, String uri) + { + if (index >= 0 && index < length) { + data[index*5] = uri; + } else { + badIndex(index); + } + } + + + /** + * Set the local name of a specific attribute. + * + * @param index The index of the attribute (zero-based). + * @param localName The attribute's local name, or the empty + * string for none. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void setLocalName (int index, String localName) + { + if (index >= 0 && index < length) { + data[index*5+1] = localName; + } else { + badIndex(index); + } + } + + + /** + * Set the qualified name of a specific attribute. + * + * @param index The index of the attribute (zero-based). + * @param qName The attribute's qualified name, or the empty + * string for none. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void setQName (int index, String qName) + { + if (index >= 0 && index < length) { + data[index*5+2] = qName; + } else { + badIndex(index); + } + } + + + /** + * Set the type of a specific attribute. + * + * @param index The index of the attribute (zero-based). + * @param type The attribute's type. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void setType (int index, String type) + { + if (index >= 0 && index < length) { + data[index*5+3] = type; + } else { + badIndex(index); + } + } + + + /** + * Set the value of a specific attribute. + * + * @param index The index of the attribute (zero-based). + * @param value The attribute's value. + * @exception java.lang.ArrayIndexOutOfBoundsException When the + * supplied index does not point to an attribute + * in the list. + */ + public void setValue (int index, String value) + { + if (index >= 0 && index < length) { + data[index*5+4] = value; + } else { + badIndex(index); + } + } + + + + //////////////////////////////////////////////////////////////////// + // Internal methods. + //////////////////////////////////////////////////////////////////// + + + /** + * Ensure the internal array's capacity. + * + * @param n The minimum number of attributes that the array must + * be able to hold. + */ + private void ensureCapacity (int n) { + if (n <= 0) { + return; + } + int max; + if (data == null || data.length == 0) { + max = 25; + } + else if (data.length >= n * 5) { + return; + } + else { + max = data.length; + } + while (max < n * 5) { + max *= 2; + } + + String newData[] = new String[max]; + if (length > 0) { + System.arraycopy(data, 0, newData, 0, length*5); + } + data = newData; + } + + + /** + * Report a bad array index in a manipulator. + * + * @param index The index to report. + * @exception java.lang.ArrayIndexOutOfBoundsException Always. + */ + private void badIndex (int index) + throws ArrayIndexOutOfBoundsException + { + String msg = + "Attempt to modify attribute at illegal index: " + index; + throw new ArrayIndexOutOfBoundsException(msg); + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + int length; + String data []; + +} + +// end of AttributesImpl.java + diff --git a/xml/src/main/java/org/xml/sax/helpers/DefaultHandler.java b/xml/src/main/java/org/xml/sax/helpers/DefaultHandler.java new file mode 100644 index 0000000..8308be7 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/DefaultHandler.java @@ -0,0 +1,467 @@ +// DefaultHandler.java - default implementation of the core handlers. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the public domain. +// $Id: DefaultHandler.java,v 1.9 2004/04/26 17:34:35 dmegginson Exp $ + +package org.xml.sax.helpers; + +import java.io.IOException; + +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.Attributes; +import org.xml.sax.EntityResolver; +import org.xml.sax.DTDHandler; +import org.xml.sax.ContentHandler; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + + +/** + * Default base class for SAX2 event handlers. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class is available as a convenience base class for SAX2 + * applications: it provides default implementations for all of the + * callbacks in the four core SAX2 handler classes:</p> + * + * <ul> + * <li>{@link org.xml.sax.EntityResolver EntityResolver}</li> + * <li>{@link org.xml.sax.DTDHandler DTDHandler}</li> + * <li>{@link org.xml.sax.ContentHandler ContentHandler}</li> + * <li>{@link org.xml.sax.ErrorHandler ErrorHandler}</li> + * </ul> + * + * <p>Application writers can extend this class when they need to + * implement only part of an interface; parser writers can + * instantiate this class to provide default handlers when the + * application has not supplied its own.</p> + * + * <p>This class replaces the deprecated SAX1 + * {@link org.xml.sax.HandlerBase HandlerBase} class.</p> + * + * @since SAX 2.0 + * @author David Megginson, + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.EntityResolver + * @see org.xml.sax.DTDHandler + * @see org.xml.sax.ContentHandler + * @see org.xml.sax.ErrorHandler + */ +public class DefaultHandler + implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler +{ + + + //////////////////////////////////////////////////////////////////// + // Default implementation of the EntityResolver interface. + //////////////////////////////////////////////////////////////////// + + /** + * Resolve an external entity. + * + * <p>Always return null, so that the parser will use the system + * identifier provided in the XML document. This method implements + * the SAX default behaviour: application writers can override it + * in a subclass to do special translations such as catalog lookups + * or URI redirection.</p> + * + * @param publicId The public identifer, or null if none is + * available. + * @param systemId The system identifier provided in the XML + * document. + * @return The new input source, or null to require the + * default behaviour. + * @exception java.io.IOException If there is an error setting + * up the new input source. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.EntityResolver#resolveEntity + */ + public InputSource resolveEntity (String publicId, String systemId) + throws IOException, SAXException + { + return null; + } + + + + //////////////////////////////////////////////////////////////////// + // Default implementation of DTDHandler interface. + //////////////////////////////////////////////////////////////////// + + + /** + * Receive notification of a notation declaration. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass if they wish to keep track of the notations + * declared in a document.</p> + * + * @param name The notation name. + * @param publicId The notation public identifier, or null if not + * available. + * @param systemId The notation system identifier. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DTDHandler#notationDecl + */ + public void notationDecl (String name, String publicId, String systemId) + throws SAXException + { + // no op + } + + + /** + * Receive notification of an unparsed entity declaration. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to keep track of the unparsed entities + * declared in a document.</p> + * + * @param name The entity name. + * @param publicId The entity public identifier, or null if not + * available. + * @param systemId The entity system identifier. + * @param notationName The name of the associated notation. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.DTDHandler#unparsedEntityDecl + */ + public void unparsedEntityDecl (String name, String publicId, + String systemId, String notationName) + throws SAXException + { + // no op + } + + + + //////////////////////////////////////////////////////////////////// + // Default implementation of ContentHandler interface. + //////////////////////////////////////////////////////////////////// + + + /** + * Receive a Locator object for document events. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass if they wish to store the locator for use + * with other document events.</p> + * + * @param locator A locator for all SAX document events. + * @see org.xml.sax.ContentHandler#setDocumentLocator + * @see org.xml.sax.Locator + */ + public void setDocumentLocator (Locator locator) + { + // no op + } + + + /** + * Receive notification of the beginning of the document. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the beginning + * of a document (such as allocating the root node of a tree or + * creating an output file).</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#startDocument + */ + public void startDocument () + throws SAXException + { + // no op + } + + + /** + * Receive notification of the end of the document. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the end + * of a document (such as finalising a tree or closing an output + * file).</p> + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#endDocument + */ + public void endDocument () + throws SAXException + { + // no op + } + + + /** + * Receive notification of the start of a Namespace mapping. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the start of + * each Namespace prefix scope (such as storing the prefix mapping).</p> + * + * @param prefix The Namespace prefix being declared. + * @param uri The Namespace URI mapped to the prefix. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#startPrefixMapping + */ + public void startPrefixMapping (String prefix, String uri) + throws SAXException + { + // no op + } + + + /** + * Receive notification of the end of a Namespace mapping. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the end of + * each prefix mapping.</p> + * + * @param prefix The Namespace prefix being declared. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#endPrefixMapping + */ + public void endPrefixMapping (String prefix) + throws SAXException + { + // no op + } + + + /** + * Receive notification of the start of an element. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the start of + * each element (such as allocating a new tree node or writing + * output to a file).</p> + * + * @param uri The Namespace URI, or the empty string if the + * element has no Namespace URI or if Namespace + * processing is not being performed. + * @param localName The local name (without prefix), or the + * empty string if Namespace processing is not being + * performed. + * @param qName The qualified name (with prefix), or the + * empty string if qualified names are not available. + * @param attributes The attributes attached to the element. If + * there are no attributes, it shall be an empty + * Attributes object. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#startElement + */ + public void startElement (String uri, String localName, + String qName, Attributes attributes) + throws SAXException + { + // no op + } + + + /** + * Receive notification of the end of an element. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions at the end of + * each element (such as finalising a tree node or writing + * output to a file).</p> + * + * @param uri The Namespace URI, or the empty string if the + * element has no Namespace URI or if Namespace + * processing is not being performed. + * @param localName The local name (without prefix), or the + * empty string if Namespace processing is not being + * performed. + * @param qName The qualified name (with prefix), or the + * empty string if qualified names are not available. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#endElement + */ + public void endElement (String uri, String localName, String qName) + throws SAXException + { + // no op + } + + + /** + * Receive notification of character data inside an element. + * + * <p>By default, do nothing. Application writers may override this + * method to take specific actions for each chunk of character data + * (such as adding the data to a node or buffer, or printing it to + * a file).</p> + * + * @param ch The characters. + * @param start The start position in the character array. + * @param length The number of characters to use from the + * character array. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#characters + */ + public void characters (char ch[], int start, int length) + throws SAXException + { + // no op + } + + + /** + * Receive notification of ignorable whitespace in element content. + * + * <p>By default, do nothing. Application writers may override this + * method to take specific actions for each chunk of ignorable + * whitespace (such as adding data to a node or buffer, or printing + * it to a file).</p> + * + * @param ch The whitespace characters. + * @param start The start position in the character array. + * @param length The number of characters to use from the + * character array. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#ignorableWhitespace + */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException + { + // no op + } + + + /** + * Receive notification of a processing instruction. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions for each + * processing instruction, such as setting status variables or + * invoking other methods.</p> + * + * @param target The processing instruction target. + * @param data The processing instruction data, or null if + * none is supplied. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#processingInstruction + */ + public void processingInstruction (String target, String data) + throws SAXException + { + // no op + } + + + /** + * Receive notification of a skipped entity. + * + * <p>By default, do nothing. Application writers may override this + * method in a subclass to take specific actions for each + * processing instruction, such as setting status variables or + * invoking other methods.</p> + * + * @param name The name of the skipped entity. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#processingInstruction + */ + public void skippedEntity (String name) + throws SAXException + { + // no op + } + + + + //////////////////////////////////////////////////////////////////// + // Default implementation of the ErrorHandler interface. + //////////////////////////////////////////////////////////////////// + + + /** + * Receive notification of a parser warning. + * + * <p>The default implementation does nothing. Application writers + * may override this method in a subclass to take specific actions + * for each warning, such as inserting the message in a log file or + * printing it to the console.</p> + * + * @param e The warning information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#warning + * @see org.xml.sax.SAXParseException + */ + public void warning (SAXParseException e) + throws SAXException + { + // no op + } + + + /** + * Receive notification of a recoverable parser error. + * + * <p>The default implementation does nothing. Application writers + * may override this method in a subclass to take specific actions + * for each error, such as inserting the message in a log file or + * printing it to the console.</p> + * + * @param e The warning information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#warning + * @see org.xml.sax.SAXParseException + */ + public void error (SAXParseException e) + throws SAXException + { + // no op + } + + + /** + * Report a fatal XML parsing error. + * + * <p>The default implementation throws a SAXParseException. + * Application writers may override this method in a subclass if + * they need to take specific actions for each fatal error (such as + * collecting all of the errors into a single report): in any case, + * the application must stop all regular processing when this + * method is invoked, since the document is no longer reliable, and + * the parser may no longer report parsing events.</p> + * + * @param e The error information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#fatalError + * @see org.xml.sax.SAXParseException + */ + public void fatalError (SAXParseException e) + throws SAXException + { + throw e; + } + +} + +// end of DefaultHandler.java diff --git a/xml/src/main/java/org/xml/sax/helpers/LocatorImpl.java b/xml/src/main/java/org/xml/sax/helpers/LocatorImpl.java new file mode 100644 index 0000000..18445bc --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/LocatorImpl.java @@ -0,0 +1,214 @@ +// SAX default implementation for Locator. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: LocatorImpl.java,v 1.6 2002/01/30 20:52:27 dbrownell Exp $ + +package org.xml.sax.helpers; + +import org.xml.sax.Locator; + + +/** + * Provide an optional convenience implementation of Locator. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class is available mainly for application writers, who + * can use it to make a persistent snapshot of a locator at any + * point during a document parse:</p> + * + * <pre> + * Locator locator; + * Locator startloc; + * + * public void setLocator (Locator locator) + * { + * // note the locator + * this.locator = locator; + * } + * + * public void startDocument () + * { + * // save the location of the start of the document + * // for future use. + * Locator startloc = new LocatorImpl(locator); + * } + *</pre> + * + * <p>Normally, parser writers will not use this class, since it + * is more efficient to provide location information only when + * requested, rather than constantly updating a Locator object.</p> + * + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.Locator Locator + */ +public class LocatorImpl implements Locator +{ + + + /** + * Zero-argument constructor. + * + * <p>This will not normally be useful, since the main purpose + * of this class is to make a snapshot of an existing Locator.</p> + */ + public LocatorImpl () + { + } + + + /** + * Copy constructor. + * + * <p>Create a persistent copy of the current state of a locator. + * When the original locator changes, this copy will still keep + * the original values (and it can be used outside the scope of + * DocumentHandler methods).</p> + * + * @param locator The locator to copy. + */ + public LocatorImpl (Locator locator) + { + setPublicId(locator.getPublicId()); + setSystemId(locator.getSystemId()); + setLineNumber(locator.getLineNumber()); + setColumnNumber(locator.getColumnNumber()); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.Locator + //////////////////////////////////////////////////////////////////// + + + /** + * Return the saved public identifier. + * + * @return The public identifier as a string, or null if none + * is available. + * @see org.xml.sax.Locator#getPublicId + * @see #setPublicId + */ + public String getPublicId () + { + return publicId; + } + + + /** + * Return the saved system identifier. + * + * @return The system identifier as a string, or null if none + * is available. + * @see org.xml.sax.Locator#getSystemId + * @see #setSystemId + */ + public String getSystemId () + { + return systemId; + } + + + /** + * Return the saved line number (1-based). + * + * @return The line number as an integer, or -1 if none is available. + * @see org.xml.sax.Locator#getLineNumber + * @see #setLineNumber + */ + public int getLineNumber () + { + return lineNumber; + } + + + /** + * Return the saved column number (1-based). + * + * @return The column number as an integer, or -1 if none is available. + * @see org.xml.sax.Locator#getColumnNumber + * @see #setColumnNumber + */ + public int getColumnNumber () + { + return columnNumber; + } + + + + //////////////////////////////////////////////////////////////////// + // Setters for the properties (not in org.xml.sax.Locator) + //////////////////////////////////////////////////////////////////// + + + /** + * Set the public identifier for this locator. + * + * @param publicId The new public identifier, or null + * if none is available. + * @see #getPublicId + */ + public void setPublicId (String publicId) + { + this.publicId = publicId; + } + + + /** + * Set the system identifier for this locator. + * + * @param systemId The new system identifier, or null + * if none is available. + * @see #getSystemId + */ + public void setSystemId (String systemId) + { + this.systemId = systemId; + } + + + /** + * Set the line number for this locator (1-based). + * + * @param lineNumber The line number, or -1 if none is available. + * @see #getLineNumber + */ + public void setLineNumber (int lineNumber) + { + this.lineNumber = lineNumber; + } + + + /** + * Set the column number for this locator (1-based). + * + * @param columnNumber The column number, or -1 if none is available. + * @see #getColumnNumber + */ + public void setColumnNumber (int columnNumber) + { + this.columnNumber = columnNumber; + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + private String publicId; + private String systemId; + private int lineNumber; + private int columnNumber; + +} + +// end of LocatorImpl.java diff --git a/xml/src/main/java/org/xml/sax/helpers/NamespaceSupport.java b/xml/src/main/java/org/xml/sax/helpers/NamespaceSupport.java new file mode 100644 index 0000000..a737d1d --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/NamespaceSupport.java @@ -0,0 +1,840 @@ +// NamespaceSupport.java - generic Namespace support for SAX. +// http://www.saxproject.org +// Written by David Megginson +// This class is in the Public Domain. NO WARRANTY! +// $Id: NamespaceSupport.java,v 1.15 2004/04/26 17:34:35 dmegginson Exp $ + +package org.xml.sax.helpers; + +import java.util.EmptyStackException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + + +/** + * Encapsulate Namespace logic for use by applications using SAX, + * or internally by SAX drivers. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class encapsulates the logic of Namespace processing: it + * tracks the declarations currently in force for each context and + * automatically processes qualified XML names into their Namespace + * parts; it can also be used in reverse for generating XML qnames + * from Namespaces.</p> + * + * <p>Namespace support objects are reusable, but the reset method + * must be invoked between each session.</p> + * + * <p>Here is a simple session:</p> + * + * <pre> + * String parts[] = new String[3]; + * NamespaceSupport support = new NamespaceSupport(); + * + * support.pushContext(); + * support.declarePrefix("", "http://www.w3.org/1999/xhtml"); + * support.declarePrefix("dc", "http://www.purl.org/dc#"); + * + * parts = support.processName("p", parts, false); + * System.out.println("Namespace URI: " + parts[0]); + * System.out.println("Local name: " + parts[1]); + * System.out.println("Raw name: " + parts[2]); + * + * parts = support.processName("dc:title", parts, false); + * System.out.println("Namespace URI: " + parts[0]); + * System.out.println("Local name: " + parts[1]); + * System.out.println("Raw name: " + parts[2]); + * + * support.popContext(); + * </pre> + * + * <p>Note that this class is optimized for the use case where most + * elements do not contain Namespace declarations: if the same + * prefix/URI mapping is repeated for each context (for example), this + * class will be somewhat less efficient.</p> + * + * <p>Although SAX drivers (parsers) may choose to use this class to + * implement namespace handling, they are not required to do so. + * Applications must track namespace information themselves if they + * want to use namespace information. + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + */ +public class NamespaceSupport +{ + + + //////////////////////////////////////////////////////////////////// + // Constants. + //////////////////////////////////////////////////////////////////// + + + /** + * The XML Namespace URI as a constant. + * The value is <code>http://www.w3.org/XML/1998/namespace</code> + * as defined in the "Namespaces in XML" * recommendation. + * + * <p>This is the Namespace URI that is automatically mapped + * to the "xml" prefix.</p> + */ + public final static String XMLNS = + "http://www.w3.org/XML/1998/namespace"; + + + /** + * The namespace declaration URI as a constant. + * The value is <code>http://www.w3.org/xmlns/2000/</code>, as defined + * in a backwards-incompatible erratum to the "Namespaces in XML" + * recommendation. Because that erratum postdated SAX2, SAX2 defaults + * to the original recommendation, and does not normally use this URI. + * + * + * <p>This is the Namespace URI that is optionally applied to + * <em>xmlns</em> and <em>xmlns:*</em> attributes, which are used to + * declare namespaces. </p> + * + * @since SAX 2.1alpha + * @see #setNamespaceDeclUris + * @see #isNamespaceDeclUris + */ + public final static String NSDECL = + "http://www.w3.org/xmlns/2000/"; + + + /** + * An empty enumeration. + */ + private final static Enumeration EMPTY_ENUMERATION = + new Vector().elements(); + + + //////////////////////////////////////////////////////////////////// + // Constructor. + //////////////////////////////////////////////////////////////////// + + + /** + * Create a new Namespace support object. + */ + public NamespaceSupport () + { + reset(); + } + + + + //////////////////////////////////////////////////////////////////// + // Context management. + //////////////////////////////////////////////////////////////////// + + + /** + * Reset this Namespace support object for reuse. + * + * <p>It is necessary to invoke this method before reusing the + * Namespace support object for a new session. If namespace + * declaration URIs are to be supported, that flag must also + * be set to a non-default value. + * </p> + * + * @see #setNamespaceDeclUris + */ + public void reset () + { + contexts = new Context[32]; + namespaceDeclUris = false; + contextPos = 0; + contexts[contextPos] = currentContext = new Context(); + currentContext.declarePrefix("xml", XMLNS); + } + + + /** + * Start a new Namespace context. + * The new context will automatically inherit + * the declarations of its parent context, but it will also keep + * track of which declarations were made within this context. + * + * <p>Event callback code should start a new context once per element. + * This means being ready to call this in either of two places. + * For elements that don't include namespace declarations, the + * <em>ContentHandler.startElement()</em> callback is the right place. + * For elements with such a declaration, it'd done in the first + * <em>ContentHandler.startPrefixMapping()</em> callback. + * A boolean flag can be used to + * track whether a context has been started yet. When either of + * those methods is called, it checks the flag to see if a new context + * needs to be started. If so, it starts the context and sets the + * flag. After <em>ContentHandler.startElement()</em> + * does that, it always clears the flag. + * + * <p>Normally, SAX drivers would push a new context at the beginning + * of each XML element. Then they perform a first pass over the + * attributes to process all namespace declarations, making + * <em>ContentHandler.startPrefixMapping()</em> callbacks. + * Then a second pass is made, to determine the namespace-qualified + * names for all attributes and for the element name. + * Finally all the information for the + * <em>ContentHandler.startElement()</em> callback is available, + * so it can then be made. + * + * <p>The Namespace support object always starts with a base context + * already in force: in this context, only the "xml" prefix is + * declared.</p> + * + * @see org.xml.sax.ContentHandler + * @see #popContext + */ + public void pushContext () + { + int max = contexts.length; + + contexts [contextPos].declsOK = false; + contextPos++; + + // Extend the array if necessary + if (contextPos >= max) { + Context newContexts[] = new Context[max*2]; + System.arraycopy(contexts, 0, newContexts, 0, max); + max *= 2; + contexts = newContexts; + } + + // Allocate the context if necessary. + currentContext = contexts[contextPos]; + if (currentContext == null) { + contexts[contextPos] = currentContext = new Context(); + } + + // Set the parent, if any. + if (contextPos > 0) { + currentContext.setParent(contexts[contextPos - 1]); + } + } + + + /** + * Revert to the previous Namespace context. + * + * <p>Normally, you should pop the context at the end of each + * XML element. After popping the context, all Namespace prefix + * mappings that were previously in force are restored.</p> + * + * <p>You must not attempt to declare additional Namespace + * prefixes after popping a context, unless you push another + * context first.</p> + * + * @see #pushContext + */ + public void popContext () + { + contexts[contextPos].clear(); + contextPos--; + if (contextPos < 0) { + throw new EmptyStackException(); + } + currentContext = contexts[contextPos]; + } + + + + //////////////////////////////////////////////////////////////////// + // Operations within a context. + //////////////////////////////////////////////////////////////////// + + + /** + * Declare a Namespace prefix. All prefixes must be declared + * before they are referenced. For example, a SAX driver (parser) + * would scan an element's attributes + * in two passes: first for namespace declarations, + * then a second pass using {@link #processName processName()} to + * interpret prefixes against (potentially redefined) prefixes. + * + * <p>This method declares a prefix in the current Namespace + * context; the prefix will remain in force until this context + * is popped, unless it is shadowed in a descendant context.</p> + * + * <p>To declare the default element Namespace, use the empty string as + * the prefix.</p> + * + * <p>Note that you must <em>not</em> declare a prefix after + * you've pushed and popped another Namespace context, or + * treated the declarations phase as complete by processing + * a prefixed name.</p> + * + * <p>Note that there is an asymmetry in this library: {@link + * #getPrefix getPrefix} will not return the "" prefix, + * even if you have declared a default element namespace. + * To check for a default namespace, + * you have to look it up explicitly using {@link #getURI getURI}. + * This asymmetry exists to make it easier to look up prefixes + * for attribute names, where the default prefix is not allowed.</p> + * + * @param prefix The prefix to declare, or the empty string to + * indicate the default element namespace. This may never have + * the value "xml" or "xmlns". + * @param uri The Namespace URI to associate with the prefix. + * @return true if the prefix was legal, false otherwise + * + * @see #processName + * @see #getURI + * @see #getPrefix + */ + public boolean declarePrefix (String prefix, String uri) + { + if (prefix.equals("xml") || prefix.equals("xmlns")) { + return false; + } else { + currentContext.declarePrefix(prefix, uri); + return true; + } + } + + + /** + * Process a raw XML qualified name, after all declarations in the + * current context have been handled by {@link #declarePrefix + * declarePrefix()}. + * + * <p>This method processes a raw XML qualified name in the + * current context by removing the prefix and looking it up among + * the prefixes currently declared. The return value will be the + * array supplied by the caller, filled in as follows:</p> + * + * <dl> + * <dt>parts[0]</dt> + * <dd>The Namespace URI, or an empty string if none is + * in use.</dd> + * <dt>parts[1]</dt> + * <dd>The local name (without prefix).</dd> + * <dt>parts[2]</dt> + * <dd>The original raw name.</dd> + * </dl> + * + * <p>All of the strings in the array will be internalized. If + * the raw name has a prefix that has not been declared, then + * the return value will be null.</p> + * + * <p>Note that attribute names are processed differently than + * element names: an unprefixed element name will receive the + * default Namespace (if any), while an unprefixed attribute name + * will not.</p> + * + * @param qName The XML qualified name to be processed. + * @param parts An array supplied by the caller, capable of + * holding at least three members. + * @param isAttribute A flag indicating whether this is an + * attribute name (true) or an element name (false). + * @return The supplied array holding three internalized strings + * representing the Namespace URI (or empty string), the + * local name, and the XML qualified name; or null if there + * is an undeclared prefix. + * @see #declarePrefix + * @see java.lang.String#intern */ + public String [] processName (String qName, String parts[], + boolean isAttribute) + { + String myParts[] = currentContext.processName(qName, isAttribute); + if (myParts == null) { + return null; + } else { + parts[0] = myParts[0]; + parts[1] = myParts[1]; + parts[2] = myParts[2]; + return parts; + } + } + + + /** + * Look up a prefix and get the currently-mapped Namespace URI. + * + * <p>This method looks up the prefix in the current context. + * Use the empty string ("") for the default Namespace.</p> + * + * @param prefix The prefix to look up. + * @return The associated Namespace URI, or null if the prefix + * is undeclared in this context. + * @see #getPrefix + * @see #getPrefixes + */ + public String getURI (String prefix) + { + return currentContext.getURI(prefix); + } + + + /** + * Return an enumeration of all prefixes whose declarations are + * active in the current context. + * This includes declarations from parent contexts that have + * not been overridden. + * + * <p><strong>Note:</strong> if there is a default prefix, it will not be + * returned in this enumeration; check for the default prefix + * using the {@link #getURI getURI} with an argument of "".</p> + * + * @return An enumeration of prefixes (never empty). + * @see #getDeclaredPrefixes + * @see #getURI + */ + public Enumeration getPrefixes () + { + return currentContext.getPrefixes(); + } + + + /** + * Return one of the prefixes mapped to a Namespace URI. + * + * <p>If more than one prefix is currently mapped to the same + * URI, this method will make an arbitrary selection; if you + * want all of the prefixes, use the {@link #getPrefixes} + * method instead.</p> + * + * <p><strong>Note:</strong> this will never return the empty (default) prefix; + * to check for a default prefix, use the {@link #getURI getURI} + * method with an argument of "".</p> + * + * @param uri the namespace URI + * @return one of the prefixes currently mapped to the URI supplied, + * or null if none is mapped or if the URI is assigned to + * the default namespace + * @see #getPrefixes(java.lang.String) + * @see #getURI + */ + public String getPrefix (String uri) + { + return currentContext.getPrefix(uri); + } + + + /** + * Return an enumeration of all prefixes for a given URI whose + * declarations are active in the current context. + * This includes declarations from parent contexts that have + * not been overridden. + * + * <p>This method returns prefixes mapped to a specific Namespace + * URI. The xml: prefix will be included. If you want only one + * prefix that's mapped to the Namespace URI, and you don't care + * which one you get, use the {@link #getPrefix getPrefix} + * method instead.</p> + * + * <p><strong>Note:</strong> the empty (default) prefix is <em>never</em> included + * in this enumeration; to check for the presence of a default + * Namespace, use the {@link #getURI getURI} method with an + * argument of "".</p> + * + * @param uri The Namespace URI. + * @return An enumeration of prefixes (never empty). + * @see #getPrefix + * @see #getDeclaredPrefixes + * @see #getURI + */ + public Enumeration getPrefixes (String uri) + { + Vector prefixes = new Vector(); + Enumeration allPrefixes = getPrefixes(); + while (allPrefixes.hasMoreElements()) { + String prefix = (String)allPrefixes.nextElement(); + if (uri.equals(getURI(prefix))) { + prefixes.addElement(prefix); + } + } + return prefixes.elements(); + } + + + /** + * Return an enumeration of all prefixes declared in this context. + * + * <p>The empty (default) prefix will be included in this + * enumeration; note that this behaviour differs from that of + * {@link #getPrefix} and {@link #getPrefixes}.</p> + * + * @return An enumeration of all prefixes declared in this + * context. + * @see #getPrefixes + * @see #getURI + */ + public Enumeration getDeclaredPrefixes () + { + return currentContext.getDeclaredPrefixes(); + } + + /** + * Controls whether namespace declaration attributes are placed + * into the {@link #NSDECL NSDECL} namespace + * by {@link #processName processName()}. This may only be + * changed before any contexts have been pushed. + * + * @param value the namespace declaration attribute state. A value of true + * enables this feature, a value of false disables it. + * + * @since SAX 2.1alpha + * + * @exception IllegalStateException when attempting to set this + * after any context has been pushed. + */ + public void setNamespaceDeclUris (boolean value) + { + if (contextPos != 0) + throw new IllegalStateException (); + if (value == namespaceDeclUris) + return; + namespaceDeclUris = value; + if (value) + currentContext.declarePrefix ("xmlns", NSDECL); + else { + contexts[contextPos] = currentContext = new Context(); + currentContext.declarePrefix("xml", XMLNS); + } + } + + /** + * Returns true if namespace declaration attributes are placed into + * a namespace. This behavior is not the default. + * + * @return true if namespace declaration attributes are enabled, false + * otherwise. + * @since SAX 2.1alpha + */ + public boolean isNamespaceDeclUris () + { return namespaceDeclUris; } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + private Context contexts[]; + private Context currentContext; + private int contextPos; + private boolean namespaceDeclUris; + + + //////////////////////////////////////////////////////////////////// + // Internal classes. + //////////////////////////////////////////////////////////////////// + + /** + * Internal class for a single Namespace context. + * + * <p>This module caches and reuses Namespace contexts, + * so the number allocated + * will be equal to the element depth of the document, not to the total + * number of elements (i.e. 5-10 rather than tens of thousands). + * Also, data structures used to represent contexts are shared when + * possible (child contexts without declarations) to further reduce + * the amount of memory that's consumed. + * </p> + */ + final class Context { + + /** + * Create the root-level Namespace context. + */ + Context () + { + copyTables(); + } + + + /** + * (Re)set the parent of this Namespace context. + * The context must either have been freshly constructed, + * or must have been cleared. + * + * @param context The parent Namespace context object. + */ + void setParent (Context parent) + { + this.parent = parent; + declarations = null; + prefixTable = parent.prefixTable; + uriTable = parent.uriTable; + elementNameTable = parent.elementNameTable; + attributeNameTable = parent.attributeNameTable; + defaultNS = parent.defaultNS; + declSeen = false; + declsOK = true; + } + + /** + * Makes associated state become collectible, + * invalidating this context. + * {@link #setParent} must be called before + * this context may be used again. + */ + void clear () + { + parent = null; + prefixTable = null; + uriTable = null; + elementNameTable = null; + attributeNameTable = null; + defaultNS = null; + } + + + /** + * Declare a Namespace prefix for this context. + * + * @param prefix The prefix to declare. + * @param uri The associated Namespace URI. + * @see org.xml.sax.helpers.NamespaceSupport#declarePrefix + */ + void declarePrefix (String prefix, String uri) + { + // Lazy processing... + if (!declsOK) + throw new IllegalStateException ( + "can't declare any more prefixes in this context"); + if (!declSeen) { + copyTables(); + } + if (declarations == null) { + declarations = new Vector(); + } + + prefix = prefix.intern(); + uri = uri.intern(); + if ("".equals(prefix)) { + if ("".equals(uri)) { + defaultNS = null; + } else { + defaultNS = uri; + } + } else { + prefixTable.put(prefix, uri); + uriTable.put(uri, prefix); // may wipe out another prefix + } + declarations.addElement(prefix); + } + + + /** + * Process an XML qualified name in this context. + * + * @param qName The XML qualified name. + * @param isAttribute true if this is an attribute name. + * @return An array of three strings containing the + * URI part (or empty string), the local part, + * and the raw name, all internalized, or null + * if there is an undeclared prefix. + * @see org.xml.sax.helpers.NamespaceSupport#processName + */ + String [] processName (String qName, boolean isAttribute) + { + String name[]; + Hashtable table; + + // detect errors in call sequence + declsOK = false; + + // Select the appropriate table. + if (isAttribute) { + table = attributeNameTable; + } else { + table = elementNameTable; + } + + // Start by looking in the cache, and + // return immediately if the name + // is already known in this content + name = (String[])table.get(qName); + if (name != null) { + return name; + } + + // We haven't seen this name in this + // context before. Maybe in the parent + // context, but we can't assume prefix + // bindings are the same. + name = new String[3]; + name[2] = qName.intern(); + int index = qName.indexOf(':'); + + + // No prefix. + if (index == -1) { + if (isAttribute) { + if (qName == "xmlns" && namespaceDeclUris) + name[0] = NSDECL; + else + name[0] = ""; + } else if (defaultNS == null) { + name[0] = ""; + } else { + name[0] = defaultNS; + } + name[1] = name[2]; + } + + // Prefix + else { + String prefix = qName.substring(0, index); + String local = qName.substring(index+1); + String uri; + if ("".equals(prefix)) { + uri = defaultNS; + } else { + uri = (String)prefixTable.get(prefix); + } + if (uri == null + || (!isAttribute && "xmlns".equals (prefix))) { + return null; + } + name[0] = uri; + name[1] = local.intern(); + } + + // Save in the cache for future use. + // (Could be shared with parent context...) + table.put(name[2], name); + return name; + } + + + /** + * Look up the URI associated with a prefix in this context. + * + * @param prefix The prefix to look up. + * @return The associated Namespace URI, or null if none is + * declared. + * @see org.xml.sax.helpers.NamespaceSupport#getURI + */ + String getURI (String prefix) + { + if ("".equals(prefix)) { + return defaultNS; + } else if (prefixTable == null) { + return null; + } else { + return (String)prefixTable.get(prefix); + } + } + + + /** + * Look up one of the prefixes associated with a URI in this context. + * + * <p>Since many prefixes may be mapped to the same URI, + * the return value may be unreliable.</p> + * + * @param uri The URI to look up. + * @return The associated prefix, or null if none is declared. + * @see org.xml.sax.helpers.NamespaceSupport#getPrefix + */ + String getPrefix (String uri) + { + if (uriTable == null) { + return null; + } else { + return (String)uriTable.get(uri); + } + } + + + /** + * Return an enumeration of prefixes declared in this context. + * + * @return An enumeration of prefixes (possibly empty). + * @see org.xml.sax.helpers.NamespaceSupport#getDeclaredPrefixes + */ + Enumeration getDeclaredPrefixes () + { + if (declarations == null) { + return EMPTY_ENUMERATION; + } else { + return declarations.elements(); + } + } + + + /** + * Return an enumeration of all prefixes currently in force. + * + * <p>The default prefix, if in force, is <em>not</em> + * returned, and will have to be checked for separately.</p> + * + * @return An enumeration of prefixes (never empty). + * @see org.xml.sax.helpers.NamespaceSupport#getPrefixes + */ + Enumeration getPrefixes () + { + if (prefixTable == null) { + return EMPTY_ENUMERATION; + } else { + return prefixTable.keys(); + } + } + + + + //////////////////////////////////////////////////////////////// + // Internal methods. + //////////////////////////////////////////////////////////////// + + + /** + * Copy on write for the internal tables in this context. + * + * <p>This class is optimized for the normal case where most + * elements do not contain Namespace declarations.</p> + */ + private void copyTables () + { + if (prefixTable != null) { + prefixTable = (Hashtable)prefixTable.clone(); + } else { + prefixTable = new Hashtable(); + } + if (uriTable != null) { + uriTable = (Hashtable)uriTable.clone(); + } else { + uriTable = new Hashtable(); + } + elementNameTable = new Hashtable(); + attributeNameTable = new Hashtable(); + declSeen = true; + } + + + + //////////////////////////////////////////////////////////////// + // Protected state. + //////////////////////////////////////////////////////////////// + + Hashtable prefixTable; + Hashtable uriTable; + Hashtable elementNameTable; + Hashtable attributeNameTable; + String defaultNS = null; + boolean declsOK = true; + + + + //////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////// + + private Vector declarations = null; + private boolean declSeen = false; + private Context parent = null; + } +} + +// end of NamespaceSupport.java diff --git a/xml/src/main/java/org/xml/sax/helpers/NewInstance.java b/xml/src/main/java/org/xml/sax/helpers/NewInstance.java new file mode 100644 index 0000000..e590192 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/NewInstance.java @@ -0,0 +1,79 @@ +// NewInstance.java - create a new instance of a class by name. +// http://www.saxproject.org +// Written by Edwin Goei, edwingo@apache.org +// and by David Brownell, dbrownell@users.sourceforge.net +// NO WARRANTY! This class is in the Public Domain. +// $Id: NewInstance.java,v 1.4 2002/01/30 20:52:27 dbrownell Exp $ + +package org.xml.sax.helpers; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +/** + * Create a new instance of a class by name. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class contains a static method for creating an instance of a + * class from an explicit class name. It tries to use the thread's context + * ClassLoader if possible and falls back to using + * Class.forName(String).</p> + * + * <p>This code is designed to compile and run on JDK version 1.1 and later + * including versions of Java 2.</p> + * + * @author Edwin Goei, David Brownell + * @version 2.0.1 (sax2r2) + */ +class NewInstance { + + /** + * Creates a new instance of the specified class name + * + * Package private so this code is not exposed at the API level. + */ + static Object newInstance (ClassLoader classLoader, String className) + throws ClassNotFoundException, IllegalAccessException, + InstantiationException + { + Class driverClass; + if (classLoader == null) { + driverClass = Class.forName(className); + } else { + driverClass = classLoader.loadClass(className); + } + return driverClass.newInstance(); + } + + /** + * Figure out which ClassLoader to use. For JDK 1.2 and later use + * the context ClassLoader. + */ + static ClassLoader getClassLoader () + { + Method m = null; + + try { + m = Thread.class.getMethod("getContextClassLoader"); + } catch (NoSuchMethodException e) { + // Assume that we are running JDK 1.1, use the current ClassLoader + return NewInstance.class.getClassLoader(); + } + + try { + return (ClassLoader) m.invoke(Thread.currentThread()); + } catch (IllegalAccessException e) { + // assert(false) + throw new UnknownError(e.getMessage()); + } catch (InvocationTargetException e) { + // assert(e.getTargetException() instanceof SecurityException) + throw new UnknownError(e.getMessage()); + } + } +} diff --git a/xml/src/main/java/org/xml/sax/helpers/ParserAdapter.java b/xml/src/main/java/org/xml/sax/helpers/ParserAdapter.java new file mode 100644 index 0000000..7fc3813 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/ParserAdapter.java @@ -0,0 +1,1046 @@ +// ParserAdapter.java - adapt a SAX1 Parser to a SAX2 XMLReader. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the public domain. +// $Id: ParserAdapter.java,v 1.16 2004/04/26 17:34:35 dmegginson Exp $ + +package org.xml.sax.helpers; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import org.xml.sax.Parser; // deprecated +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.AttributeList; // deprecated +import org.xml.sax.EntityResolver; +import org.xml.sax.DTDHandler; +import org.xml.sax.DocumentHandler; // deprecated +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import org.xml.sax.XMLReader; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; + + +/** + * Adapt a SAX1 Parser as a SAX2 XMLReader. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class wraps a SAX1 {@link org.xml.sax.Parser Parser} + * and makes it act as a SAX2 {@link org.xml.sax.XMLReader XMLReader}, + * with feature, property, and Namespace support. Note + * that it is not possible to report {@link org.xml.sax.ContentHandler#skippedEntity + * skippedEntity} events, since SAX1 does not make that information available.</p> + * + * <p>This adapter does not test for duplicate Namespace-qualified + * attribute names.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.helpers.XMLReaderAdapter + * @see org.xml.sax.XMLReader + * @see org.xml.sax.Parser + */ +public class ParserAdapter implements XMLReader, DocumentHandler +{ + + + //////////////////////////////////////////////////////////////////// + // Constructors. + //////////////////////////////////////////////////////////////////// + + + /** + * Construct a new parser adapter. + * + * <p>Use the "org.xml.sax.parser" property to locate the + * embedded SAX1 driver.</p> + * + * @exception SAXException If the embedded driver + * cannot be instantiated or if the + * org.xml.sax.parser property is not specified. + */ + public ParserAdapter () + throws SAXException + { + super(); + + String driver = System.getProperty("org.xml.sax.parser"); + + try { + setup(ParserFactory.makeParser()); + } catch (ClassNotFoundException e1) { + throw new + SAXException("Cannot find SAX1 driver class " + + driver, e1); + } catch (IllegalAccessException e2) { + throw new + SAXException("SAX1 driver class " + + driver + + " found but cannot be loaded", e2); + } catch (InstantiationException e3) { + throw new + SAXException("SAX1 driver class " + + driver + + " loaded but cannot be instantiated", e3); + } catch (ClassCastException e4) { + throw new + SAXException("SAX1 driver class " + + driver + + " does not implement org.xml.sax.Parser"); + } catch (NullPointerException e5) { + throw new + SAXException("System property org.xml.sax.parser not specified"); + } + } + + + /** + * Construct a new parser adapter. + * + * <p>Note that the embedded parser cannot be changed once the + * adapter is created; to embed a different parser, allocate + * a new ParserAdapter.</p> + * + * @param parser The SAX1 parser to embed. + * @exception java.lang.NullPointerException If the parser parameter + * is null. + */ + public ParserAdapter (Parser parser) + { + super(); + setup(parser); + } + + + /** + * Internal setup method. + * + * @param parser The embedded parser. + * @exception java.lang.NullPointerException If the parser parameter + * is null. + */ + private void setup (Parser parser) + { + if (parser == null) { + throw new + NullPointerException("Parser argument must not be null"); + } + this.parser = parser; + atts = new AttributesImpl(); + nsSupport = new NamespaceSupport(); + attAdapter = new AttributeListAdapter(); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.XMLReader. + //////////////////////////////////////////////////////////////////// + + + // + // Internal constants for the sake of convenience. + // + private final static String FEATURES = "http://xml.org/sax/features/"; + private final static String NAMESPACES = FEATURES + "namespaces"; + private final static String NAMESPACE_PREFIXES = FEATURES + "namespace-prefixes"; + private final static String XMLNS_URIs = FEATURES + "xmlns-uris"; + + + /** + * Set a feature flag for the parser. + * + * <p>The only features recognized are namespaces and + * namespace-prefixes.</p> + * + * @param name The feature name, as a complete URI. + * @param value The requested feature value. + * @exception SAXNotRecognizedException If the feature + * can't be assigned or retrieved. + * @exception SAXNotSupportedException If the feature + * can't be assigned that value. + * @see org.xml.sax.XMLReader#setFeature + */ + public void setFeature (String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (name.equals(NAMESPACES)) { + checkNotParsing("feature", name); + namespaces = value; + if (!namespaces && !prefixes) { + prefixes = true; + } + } else if (name.equals(NAMESPACE_PREFIXES)) { + checkNotParsing("feature", name); + prefixes = value; + if (!prefixes && !namespaces) { + namespaces = true; + } + } else if (name.equals(XMLNS_URIs)) { + checkNotParsing("feature", name); + uris = value; + } else { + throw new SAXNotRecognizedException("Feature: " + name); + } + } + + + /** + * Check a parser feature flag. + * + * <p>The only features recognized are namespaces and + * namespace-prefixes.</p> + * + * @param name The feature name, as a complete URI. + * @return The current feature value. + * @exception SAXNotRecognizedException If the feature + * value can't be assigned or retrieved. + * @exception SAXNotSupportedException If the + * feature is not currently readable. + * @see org.xml.sax.XMLReader#setFeature + */ + public boolean getFeature (String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (name.equals(NAMESPACES)) { + return namespaces; + } else if (name.equals(NAMESPACE_PREFIXES)) { + return prefixes; + } else if (name.equals(XMLNS_URIs)) { + return uris; + } else { + throw new SAXNotRecognizedException("Feature: " + name); + } + } + + + /** + * Set a parser property. + * + * <p>No properties are currently recognized.</p> + * + * @param name The property name. + * @param value The property value. + * @exception SAXNotRecognizedException If the property + * value can't be assigned or retrieved. + * @exception SAXNotSupportedException If the property + * can't be assigned that value. + * @see org.xml.sax.XMLReader#setProperty + */ + public void setProperty (String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + throw new SAXNotRecognizedException("Property: " + name); + } + + + /** + * Get a parser property. + * + * <p>No properties are currently recognized.</p> + * + * @param name The property name. + * @return The property value. + * @exception SAXNotRecognizedException If the property + * value can't be assigned or retrieved. + * @exception SAXNotSupportedException If the property + * value is not currently readable. + * @see org.xml.sax.XMLReader#getProperty + */ + public Object getProperty (String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + throw new SAXNotRecognizedException("Property: " + name); + } + + + /** + * Set the entity resolver. + * + * @param resolver The new entity resolver. + * @see org.xml.sax.XMLReader#setEntityResolver + */ + public void setEntityResolver (EntityResolver resolver) + { + entityResolver = resolver; + } + + + /** + * Return the current entity resolver. + * + * @return The current entity resolver, or null if none was supplied. + * @see org.xml.sax.XMLReader#getEntityResolver + */ + public EntityResolver getEntityResolver () + { + return entityResolver; + } + + + /** + * Set the DTD handler. + * + * @param handler the new DTD handler + * @see org.xml.sax.XMLReader#setEntityResolver + */ + public void setDTDHandler (DTDHandler handler) + { + dtdHandler = handler; + } + + + /** + * Return the current DTD handler. + * + * @return the current DTD handler, or null if none was supplied + * @see org.xml.sax.XMLReader#getEntityResolver + */ + public DTDHandler getDTDHandler () + { + return dtdHandler; + } + + + /** + * Set the content handler. + * + * @param handler the new content handler + * @see org.xml.sax.XMLReader#setEntityResolver + */ + public void setContentHandler (ContentHandler handler) + { + contentHandler = handler; + } + + + /** + * Return the current content handler. + * + * @return The current content handler, or null if none was supplied. + * @see org.xml.sax.XMLReader#getEntityResolver + */ + public ContentHandler getContentHandler () + { + return contentHandler; + } + + + /** + * Set the error handler. + * + * @param handler The new error handler. + * @see org.xml.sax.XMLReader#setEntityResolver + */ + public void setErrorHandler (ErrorHandler handler) + { + errorHandler = handler; + } + + + /** + * Return the current error handler. + * + * @return The current error handler, or null if none was supplied. + * @see org.xml.sax.XMLReader#getEntityResolver + */ + public ErrorHandler getErrorHandler () + { + return errorHandler; + } + + + /** + * Parse an XML document. + * + * @param systemId The absolute URL of the document. + * @exception java.io.IOException If there is a problem reading + * the raw content of the document. + * @exception SAXException If there is a problem + * processing the document. + * @see #parse(org.xml.sax.InputSource) + * @see org.xml.sax.Parser#parse(java.lang.String) + */ + public void parse (String systemId) + throws IOException, SAXException + { + parse(new InputSource(systemId)); + } + + + /** + * Parse an XML document. + * + * @param input An input source for the document. + * @exception java.io.IOException If there is a problem reading + * the raw content of the document. + * @exception SAXException If there is a problem + * processing the document. + * @see #parse(java.lang.String) + * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource) + */ + public void parse (InputSource input) + throws IOException, SAXException + { + if (parsing) { + throw new SAXException("Parser is already in use"); + } + setupParser(); + parsing = true; + try { + parser.parse(input); + } finally { + parsing = false; + } + parsing = false; + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.DocumentHandler. + //////////////////////////////////////////////////////////////////// + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 document locator event. + * + * @param locator A document locator. + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ + public void setDocumentLocator (Locator locator) + { + this.locator = locator; + if (contentHandler != null) { + contentHandler.setDocumentLocator(locator); + } + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 start document event. + * + * @exception SAXException The client may raise a + * processing exception. + * @see org.xml.sax.DocumentHandler#startDocument + */ + public void startDocument () + throws SAXException + { + if (contentHandler != null) { + contentHandler.startDocument(); + } + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 end document event. + * + * @exception SAXException The client may raise a + * processing exception. + * @see org.xml.sax.DocumentHandler#endDocument + */ + public void endDocument () + throws SAXException + { + if (contentHandler != null) { + contentHandler.endDocument(); + } + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 startElement event. + * + * <p>If necessary, perform Namespace processing.</p> + * + * @param qName The qualified (prefixed) name. + * @param qAtts The XML attribute list (with qnames). + * @exception SAXException The client may raise a + * processing exception. + */ + public void startElement (String qName, AttributeList qAtts) + throws SAXException + { + // These are exceptions from the + // first pass; they should be + // ignored if there's a second pass, + // but reported otherwise. + Vector exceptions = null; + + // If we're not doing Namespace + // processing, dispatch this quickly. + if (!namespaces) { + if (contentHandler != null) { + attAdapter.setAttributeList(qAtts); + contentHandler.startElement("", "", qName.intern(), + attAdapter); + } + return; + } + + + // OK, we're doing Namespace processing. + nsSupport.pushContext(); + int length = qAtts.getLength(); + + // First pass: handle NS decls + for (int i = 0; i < length; i++) { + String attQName = qAtts.getName(i); + + if (!attQName.startsWith("xmlns")) + continue; + // Could be a declaration... + String prefix; + int n = attQName.indexOf(':'); + + // xmlns=... + if (n == -1 && attQName.length () == 5) { + prefix = ""; + } else if (n != 5) { + // XML namespaces spec doesn't discuss "xmlnsf:oo" + // (and similarly named) attributes ... at most, warn + continue; + } else // xmlns:foo=... + prefix = attQName.substring(n+1); + + String value = qAtts.getValue(i); + if (!nsSupport.declarePrefix(prefix, value)) { + reportError("Illegal Namespace prefix: " + prefix); + continue; + } + if (contentHandler != null) + contentHandler.startPrefixMapping(prefix, value); + } + + // Second pass: copy all relevant + // attributes into the SAX2 AttributeList + // using updated prefix bindings + atts.clear(); + for (int i = 0; i < length; i++) { + String attQName = qAtts.getName(i); + String type = qAtts.getType(i); + String value = qAtts.getValue(i); + + // Declaration? + if (attQName.startsWith("xmlns")) { + String prefix; + int n = attQName.indexOf(':'); + + if (n == -1 && attQName.length () == 5) { + prefix = ""; + } else if (n != 5) { + // XML namespaces spec doesn't discuss "xmlnsf:oo" + // (and similarly named) attributes ... ignore + prefix = null; + } else { + prefix = attQName.substring(6); + } + // Yes, decl: report or prune + if (prefix != null) { + if (prefixes) { + if (uris) + // note funky case: localname can be null + // when declaring the default prefix, and + // yet the uri isn't null. + atts.addAttribute (nsSupport.XMLNS, prefix, + attQName.intern(), type, value); + else + atts.addAttribute ("", "", + attQName.intern(), type, value); + } + continue; + } + } + + // Not a declaration -- report + try { + String attName[] = processName(attQName, true, true); + atts.addAttribute(attName[0], attName[1], attName[2], + type, value); + } catch (SAXException e) { + if (exceptions == null) + exceptions = new Vector(); + exceptions.addElement(e); + atts.addAttribute("", attQName, attQName, type, value); + } + } + + // now handle the deferred exception reports + if (exceptions != null && errorHandler != null) { + for (int i = 0; i < exceptions.size(); i++) + errorHandler.error((SAXParseException) + (exceptions.elementAt(i))); + } + + // OK, finally report the event. + if (contentHandler != null) { + String name[] = processName(qName, false, false); + contentHandler.startElement(name[0], name[1], name[2], atts); + } + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 end element event. + * + * @param qName The qualified (prefixed) name. + * @exception SAXException The client may raise a + * processing exception. + * @see org.xml.sax.DocumentHandler#endElement + */ + public void endElement (String qName) + throws SAXException + { + // If we're not doing Namespace + // processing, dispatch this quickly. + if (!namespaces) { + if (contentHandler != null) { + contentHandler.endElement("", "", qName.intern()); + } + return; + } + + // Split the name. + String names[] = processName(qName, false, false); + if (contentHandler != null) { + contentHandler.endElement(names[0], names[1], names[2]); + Enumeration prefixes = nsSupport.getDeclaredPrefixes(); + while (prefixes.hasMoreElements()) { + String prefix = (String)prefixes.nextElement(); + contentHandler.endPrefixMapping(prefix); + } + } + nsSupport.popContext(); + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 characters event. + * + * @param ch An array of characters. + * @param start The starting position in the array. + * @param length The number of characters to use. + * @exception SAXException The client may raise a + * processing exception. + * @see org.xml.sax.DocumentHandler#characters + */ + public void characters (char ch[], int start, int length) + throws SAXException + { + if (contentHandler != null) { + contentHandler.characters(ch, start, length); + } + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 ignorable whitespace event. + * + * @param ch An array of characters. + * @param start The starting position in the array. + * @param length The number of characters to use. + * @exception SAXException The client may raise a + * processing exception. + * @see org.xml.sax.DocumentHandler#ignorableWhitespace + */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException + { + if (contentHandler != null) { + contentHandler.ignorableWhitespace(ch, start, length); + } + } + + + /** + * Adapter implementation method; do not call. + * Adapt a SAX1 processing instruction event. + * + * @param target The processing instruction target. + * @param data The remainder of the processing instruction + * @exception SAXException The client may raise a + * processing exception. + * @see org.xml.sax.DocumentHandler#processingInstruction + */ + public void processingInstruction (String target, String data) + throws SAXException + { + if (contentHandler != null) { + contentHandler.processingInstruction(target, data); + } + } + + + + //////////////////////////////////////////////////////////////////// + // Internal utility methods. + //////////////////////////////////////////////////////////////////// + + + /** + * Initialize the parser before each run. + */ + private void setupParser () + { + // catch an illegal "nonsense" state. + if (!prefixes && !namespaces) + throw new IllegalStateException (); + + nsSupport.reset(); + if (uris) + nsSupport.setNamespaceDeclUris (true); + + if (entityResolver != null) { + parser.setEntityResolver(entityResolver); + } + if (dtdHandler != null) { + parser.setDTDHandler(dtdHandler); + } + if (errorHandler != null) { + parser.setErrorHandler(errorHandler); + } + parser.setDocumentHandler(this); + locator = null; + } + + + /** + * Process a qualified (prefixed) name. + * + * <p>If the name has an undeclared prefix, use only the qname + * and make an ErrorHandler.error callback in case the app is + * interested.</p> + * + * @param qName The qualified (prefixed) name. + * @param isAttribute true if this is an attribute name. + * @return The name split into three parts. + * @exception SAXException The client may throw + * an exception if there is an error callback. + */ + private String [] processName (String qName, boolean isAttribute, + boolean useException) + throws SAXException + { + String parts[] = nsSupport.processName(qName, nameParts, + isAttribute); + if (parts == null) { + if (useException) + throw makeException("Undeclared prefix: " + qName); + reportError("Undeclared prefix: " + qName); + parts = new String[3]; + parts[0] = parts[1] = ""; + parts[2] = qName.intern(); + } + return parts; + } + + + /** + * Report a non-fatal error. + * + * @param message The error message. + * @exception SAXException The client may throw + * an exception. + */ + void reportError (String message) + throws SAXException + { + if (errorHandler != null) + errorHandler.error(makeException(message)); + } + + + /** + * Construct an exception for the current context. + * + * @param message The error message. + */ + private SAXParseException makeException (String message) + { + if (locator != null) { + return new SAXParseException(message, locator); + } else { + return new SAXParseException(message, null, null, -1, -1); + } + } + + + /** + * Throw an exception if we are parsing. + * + * <p>Use this method to detect illegal feature or + * property changes.</p> + * + * @param type The type of thing (feature or property). + * @param name The feature or property name. + * @exception SAXNotSupportedException If a + * document is currently being parsed. + */ + private void checkNotParsing (String type, String name) + throws SAXNotSupportedException + { + if (parsing) { + throw new SAXNotSupportedException("Cannot change " + + type + ' ' + + name + " while parsing"); + + } + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + private NamespaceSupport nsSupport; + private AttributeListAdapter attAdapter; + + private boolean parsing = false; + private String nameParts[] = new String[3]; + + private Parser parser = null; + + private AttributesImpl atts = null; + + // Features + private boolean namespaces = true; + private boolean prefixes = false; + private boolean uris = false; + + // Properties + + // Handlers + Locator locator; + + EntityResolver entityResolver = null; + DTDHandler dtdHandler = null; + ContentHandler contentHandler = null; + ErrorHandler errorHandler = null; + + + + //////////////////////////////////////////////////////////////////// + // Inner class to wrap an AttributeList when not doing NS proc. + //////////////////////////////////////////////////////////////////// + + + /** + * Adapt a SAX1 AttributeList as a SAX2 Attributes object. + * + * <p>This class is in the Public Domain, and comes with NO + * WARRANTY of any kind.</p> + * + * <p>This wrapper class is used only when Namespace support + * is disabled -- it provides pretty much a direct mapping + * from SAX1 to SAX2, except that names and types are + * interned whenever requested.</p> + */ + final class AttributeListAdapter implements Attributes + { + + /** + * Construct a new adapter. + */ + AttributeListAdapter () + { + } + + + /** + * Set the embedded AttributeList. + * + * <p>This method must be invoked before any of the others + * can be used.</p> + * + * @param The SAX1 attribute list (with qnames). + */ + void setAttributeList (AttributeList qAtts) + { + this.qAtts = qAtts; + } + + + /** + * Return the length of the attribute list. + * + * @return The number of attributes in the list. + * @see org.xml.sax.Attributes#getLength + */ + public int getLength () + { + return qAtts.getLength(); + } + + + /** + * Return the Namespace URI of the specified attribute. + * + * @param The attribute's index. + * @return Always the empty string. + * @see org.xml.sax.Attributes#getURI + */ + public String getURI (int i) + { + return ""; + } + + + /** + * Return the local name of the specified attribute. + * + * @param The attribute's index. + * @return Always the empty string. + * @see org.xml.sax.Attributes#getLocalName + */ + public String getLocalName (int i) + { + return ""; + } + + + /** + * Return the qualified (prefixed) name of the specified attribute. + * + * @param The attribute's index. + * @return The attribute's qualified name, internalized. + */ + public String getQName (int i) + { + return qAtts.getName(i).intern(); + } + + + /** + * Return the type of the specified attribute. + * + * @param The attribute's index. + * @return The attribute's type as an internalized string. + */ + public String getType (int i) + { + return qAtts.getType(i).intern(); + } + + + /** + * Return the value of the specified attribute. + * + * @param The attribute's index. + * @return The attribute's value. + */ + public String getValue (int i) + { + return qAtts.getValue(i); + } + + + /** + * Look up an attribute index by Namespace name. + * + * @param uri The Namespace URI or the empty string. + * @param localName The local name. + * @return The attributes index, or -1 if none was found. + * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String) + */ + public int getIndex (String uri, String localName) + { + return -1; + } + + + /** + * Look up an attribute index by qualified (prefixed) name. + * + * @param qName The qualified name. + * @return The attributes index, or -1 if none was found. + * @see org.xml.sax.Attributes#getIndex(java.lang.String) + */ + public int getIndex (String qName) + { + int max = atts.getLength(); + for (int i = 0; i < max; i++) { + if (qAtts.getName(i).equals(qName)) { + return i; + } + } + return -1; + } + + + /** + * Look up the type of an attribute by Namespace name. + * + * @param uri The Namespace URI + * @param localName The local name. + * @return The attribute's type as an internalized string. + */ + public String getType (String uri, String localName) + { + return null; + } + + + /** + * Look up the type of an attribute by qualified (prefixed) name. + * + * @param qName The qualified name. + * @return The attribute's type as an internalized string. + */ + public String getType (String qName) + { + return qAtts.getType(qName).intern(); + } + + + /** + * Look up the value of an attribute by Namespace name. + * + * @param uri The Namespace URI + * @param localName The local name. + * @return The attribute's value. + */ + public String getValue (String uri, String localName) + { + return null; + } + + + /** + * Look up the value of an attribute by qualified (prefixed) name. + * + * @param qName The qualified name. + * @return The attribute's value. + */ + public String getValue (String qName) + { + return qAtts.getValue(qName); + } + + private AttributeList qAtts; + } +} + +// end of ParserAdapter.java diff --git a/xml/src/main/java/org/xml/sax/helpers/ParserFactory.java b/xml/src/main/java/org/xml/sax/helpers/ParserFactory.java new file mode 100644 index 0000000..ffe2a60 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/ParserFactory.java @@ -0,0 +1,133 @@ +// SAX parser factory. +// http://www.saxproject.org +// No warranty; no copyright -- use this as you will. +// $Id: ParserFactory.java,v 1.7 2002/01/30 20:52:36 dbrownell Exp $ + +package org.xml.sax.helpers; + +import java.lang.ClassNotFoundException; +import java.lang.IllegalAccessException; +import java.lang.InstantiationException; +import java.lang.SecurityException; +import java.lang.ClassCastException; + +import org.xml.sax.Parser; + + +/** + * Java-specific class for dynamically loading SAX parsers. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p><strong>Note:</strong> This class is designed to work with the now-deprecated + * SAX1 {@link org.xml.sax.Parser Parser} class. SAX2 applications should use + * {@link org.xml.sax.helpers.XMLReaderFactory XMLReaderFactory} instead.</p> + * + * <p>ParserFactory is not part of the platform-independent definition + * of SAX; it is an additional convenience class designed + * specifically for Java XML application writers. SAX applications + * can use the static methods in this class to allocate a SAX parser + * dynamically at run-time based either on the value of the + * `org.xml.sax.parser' system property or on a string containing the class + * name.</p> + * + * <p>Note that the application still requires an XML parser that + * implements SAX1.</p> + * + * @deprecated This class works with the deprecated + * {@link org.xml.sax.Parser Parser} + * interface. + * @since SAX 1.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + */ +public class ParserFactory { + + + /** + * Private null constructor. + */ + private ParserFactory () + { + } + + + /** + * Create a new SAX parser using the `org.xml.sax.parser' system property. + * + * <p>The named class must exist and must implement the + * {@link org.xml.sax.Parser Parser} interface.</p> + * + * @return the newly created parser. + * + * @exception java.lang.NullPointerException There is no value + * for the `org.xml.sax.parser' system property. + * @exception java.lang.ClassNotFoundException The SAX parser + * class was not found (check your CLASSPATH). + * @exception IllegalAccessException The SAX parser class was + * found, but you do not have permission to load + * it. + * @exception InstantiationException The SAX parser class was + * found but could not be instantiated. + * @exception java.lang.ClassCastException The SAX parser class + * was found and instantiated, but does not implement + * org.xml.sax.Parser. + * @see #makeParser(java.lang.String) + * @see org.xml.sax.Parser + */ + public static Parser makeParser () + throws ClassNotFoundException, + IllegalAccessException, + InstantiationException, + NullPointerException, + ClassCastException + { + String className = System.getProperty("org.xml.sax.parser"); + if (className == null) { + throw new NullPointerException("No value for sax.parser property"); + } else { + return makeParser(className); + } + } + + + /** + * Create a new SAX parser object using the class name provided. + * + * <p>The named class must exist and must implement the + * {@link org.xml.sax.Parser Parser} interface.</p> + * + * @return the newly created parser. + * + * @param className A string containing the name of the + * SAX parser class. + * @exception java.lang.ClassNotFoundException The SAX parser + * class was not found (check your CLASSPATH). + * @exception IllegalAccessException The SAX parser class was + * found, but you do not have permission to load + * it. + * @exception InstantiationException The SAX parser class was + * found but could not be instantiated. + * @exception java.lang.ClassCastException The SAX parser class + * was found and instantiated, but does not implement + * org.xml.sax.Parser. + * @see #makeParser() + * @see org.xml.sax.Parser + */ + public static Parser makeParser (String className) + throws ClassNotFoundException, + IllegalAccessException, + InstantiationException, + ClassCastException + { + return (Parser) NewInstance.newInstance ( + NewInstance.getClassLoader (), className); + } + +} + diff --git a/xml/src/main/java/org/xml/sax/helpers/XMLFilterImpl.java b/xml/src/main/java/org/xml/sax/helpers/XMLFilterImpl.java new file mode 100644 index 0000000..68e2579 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/XMLFilterImpl.java @@ -0,0 +1,715 @@ +// XMLFilterImpl.java - base SAX2 filter implementation. +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the Public Domain. +// $Id: XMLFilterImpl.java,v 1.9 2004/04/26 17:34:35 dmegginson Exp $ + +package org.xml.sax.helpers; + +import java.io.IOException; + +import org.xml.sax.XMLReader; +import org.xml.sax.XMLFilter; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.Attributes; +import org.xml.sax.EntityResolver; +import org.xml.sax.DTDHandler; +import org.xml.sax.ContentHandler; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXNotRecognizedException; + + +/** + * Base class for deriving an XML filter. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class is designed to sit between an {@link org.xml.sax.XMLReader + * XMLReader} and the client application's event handlers. By default, it + * does nothing but pass requests up to the reader and events + * on to the handlers unmodified, but subclasses can override + * specific methods to modify the event stream or the configuration + * requests as they pass through.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.XMLFilter + * @see org.xml.sax.XMLReader + * @see org.xml.sax.EntityResolver + * @see org.xml.sax.DTDHandler + * @see org.xml.sax.ContentHandler + * @see org.xml.sax.ErrorHandler + */ +public class XMLFilterImpl + implements XMLFilter, EntityResolver, DTDHandler, ContentHandler, ErrorHandler +{ + + + //////////////////////////////////////////////////////////////////// + // Constructors. + //////////////////////////////////////////////////////////////////// + + + /** + * Construct an empty XML filter, with no parent. + * + * <p>This filter will have no parent: you must assign a parent + * before you start a parse or do any configuration with + * setFeature or setProperty, unless you use this as a pure event + * consumer rather than as an {@link XMLReader}.</p> + * + * @see org.xml.sax.XMLReader#setFeature + * @see org.xml.sax.XMLReader#setProperty + * @see #setParent + */ + public XMLFilterImpl () + { + super(); + } + + + /** + * Construct an XML filter with the specified parent. + * + * @param parent the XML reader from which this filter receives its events. + * + * @see #setParent + * @see #getParent + */ + public XMLFilterImpl (XMLReader parent) + { + super(); + setParent(parent); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.XMLFilter. + //////////////////////////////////////////////////////////////////// + + + /** + * Set the parent reader. + * + * <p>This is the {@link org.xml.sax.XMLReader XMLReader} from which + * this filter will obtain its events and to which it will pass its + * configuration requests. The parent may itself be another filter.</p> + * + * <p>If there is no parent reader set, any attempt to parse + * or to set or get a feature or property will fail.</p> + * + * @param parent The parent XML reader. + * @see #getParent + */ + public void setParent (XMLReader parent) + { + this.parent = parent; + } + + + /** + * Get the parent reader. + * + * @return The parent XML reader, or null if none is set. + * @see #setParent + */ + public XMLReader getParent () + { + return parent; + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.XMLReader. + //////////////////////////////////////////////////////////////////// + + + /** + * Set the value of a feature. + * + * <p>This will always fail if the parent is null.</p> + * + * @param name The feature name. + * @param value The requested feature value. + * @exception org.xml.sax.SAXNotRecognizedException If the feature + * value can't be assigned or retrieved from the parent. + * @exception org.xml.sax.SAXNotSupportedException When the + * parent recognizes the feature name but + * cannot set the requested value. + */ + public void setFeature (String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (parent != null) { + parent.setFeature(name, value); + } else { + throw new SAXNotRecognizedException("Feature: " + name); + } + } + + + /** + * Look up the value of a feature. + * + * <p>This will always fail if the parent is null.</p> + * + * @param name The feature name. + * @return The current value of the feature. + * @exception org.xml.sax.SAXNotRecognizedException If the feature + * value can't be assigned or retrieved from the parent. + * @exception org.xml.sax.SAXNotSupportedException When the + * parent recognizes the feature name but + * cannot determine its value at this time. + */ + public boolean getFeature (String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (parent != null) { + return parent.getFeature(name); + } else { + throw new SAXNotRecognizedException("Feature: " + name); + } + } + + + /** + * Set the value of a property. + * + * <p>This will always fail if the parent is null.</p> + * + * @param name The property name. + * @param value The requested property value. + * @exception org.xml.sax.SAXNotRecognizedException If the property + * value can't be assigned or retrieved from the parent. + * @exception org.xml.sax.SAXNotSupportedException When the + * parent recognizes the property name but + * cannot set the requested value. + */ + public void setProperty (String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (parent != null) { + parent.setProperty(name, value); + } else { + throw new SAXNotRecognizedException("Property: " + name); + } + } + + + /** + * Look up the value of a property. + * + * @param name The property name. + * @return The current value of the property. + * @exception org.xml.sax.SAXNotRecognizedException If the property + * value can't be assigned or retrieved from the parent. + * @exception org.xml.sax.SAXNotSupportedException When the + * parent recognizes the property name but + * cannot determine its value at this time. + */ + public Object getProperty (String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (parent != null) { + return parent.getProperty(name); + } else { + throw new SAXNotRecognizedException("Property: " + name); + } + } + + + /** + * Set the entity resolver. + * + * @param resolver The new entity resolver. + */ + public void setEntityResolver (EntityResolver resolver) + { + entityResolver = resolver; + } + + + /** + * Get the current entity resolver. + * + * @return The current entity resolver, or null if none was set. + */ + public EntityResolver getEntityResolver () + { + return entityResolver; + } + + + /** + * Set the DTD event handler. + * + * @param handler the new DTD handler + */ + public void setDTDHandler (DTDHandler handler) + { + dtdHandler = handler; + } + + + /** + * Get the current DTD event handler. + * + * @return The current DTD handler, or null if none was set. + */ + public DTDHandler getDTDHandler () + { + return dtdHandler; + } + + + /** + * Set the content event handler. + * + * @param handler the new content handler + */ + public void setContentHandler (ContentHandler handler) + { + contentHandler = handler; + } + + + /** + * Get the content event handler. + * + * @return The current content handler, or null if none was set. + */ + public ContentHandler getContentHandler () + { + return contentHandler; + } + + + /** + * Set the error event handler. + * + * @param handler the new error handler + */ + public void setErrorHandler (ErrorHandler handler) + { + errorHandler = handler; + } + + + /** + * Get the current error event handler. + * + * @return The current error handler, or null if none was set. + */ + public ErrorHandler getErrorHandler () + { + return errorHandler; + } + + + /** + * Parse a document. + * + * @param input The input source for the document entity. + * @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. + */ + public void parse (InputSource input) + throws SAXException, IOException + { + setupParse(); + parent.parse(input); + } + + + /** + * Parse a document. + * + * @param systemId The system identifier as a fully-qualified URI. + * @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. + */ + public void parse (String systemId) + throws SAXException, IOException + { + parse(new InputSource(systemId)); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.EntityResolver. + //////////////////////////////////////////////////////////////////// + + + /** + * Filter an external entity resolution. + * + * @param publicId The entity's public identifier, or null. + * @param systemId The entity's system identifier. + * @return A new InputSource or null for the default. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + * @exception java.io.IOException The client may throw an + * I/O-related exception while obtaining the + * new InputSource. + */ + public InputSource resolveEntity (String publicId, String systemId) + throws SAXException, IOException + { + if (entityResolver != null) { + return entityResolver.resolveEntity(publicId, systemId); + } else { + return null; + } + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.DTDHandler. + //////////////////////////////////////////////////////////////////// + + + /** + * Filter a notation declaration event. + * + * @param name The notation name. + * @param publicId The notation's public identifier, or null. + * @param systemId The notation's system identifier, or null. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void notationDecl (String name, String publicId, String systemId) + throws SAXException + { + if (dtdHandler != null) { + dtdHandler.notationDecl(name, publicId, systemId); + } + } + + + /** + * Filter an unparsed entity declaration event. + * + * @param name The entity name. + * @param publicId The entity's public identifier, or null. + * @param systemId The entity's system identifier, or null. + * @param notationName The name of the associated notation. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void unparsedEntityDecl (String name, String publicId, + String systemId, String notationName) + throws SAXException + { + if (dtdHandler != null) { + dtdHandler.unparsedEntityDecl(name, publicId, systemId, + notationName); + } + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.ContentHandler. + //////////////////////////////////////////////////////////////////// + + + /** + * Filter a new document locator event. + * + * @param locator The document locator. + */ + public void setDocumentLocator (Locator locator) + { + this.locator = locator; + if (contentHandler != null) { + contentHandler.setDocumentLocator(locator); + } + } + + + /** + * Filter a start document event. + * + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void startDocument () + throws SAXException + { + if (contentHandler != null) { + contentHandler.startDocument(); + } + } + + + /** + * Filter an end document event. + * + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void endDocument () + throws SAXException + { + if (contentHandler != null) { + contentHandler.endDocument(); + } + } + + + /** + * Filter a start Namespace prefix mapping event. + * + * @param prefix The Namespace prefix. + * @param uri The Namespace URI. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void startPrefixMapping (String prefix, String uri) + throws SAXException + { + if (contentHandler != null) { + contentHandler.startPrefixMapping(prefix, uri); + } + } + + + /** + * Filter an end Namespace prefix mapping event. + * + * @param prefix The Namespace prefix. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void endPrefixMapping (String prefix) + throws SAXException + { + if (contentHandler != null) { + contentHandler.endPrefixMapping(prefix); + } + } + + + /** + * Filter a start element event. + * + * @param uri The element's Namespace URI, or the empty string. + * @param localName The element's local name, or the empty string. + * @param qName The element's qualified (prefixed) name, or the empty + * string. + * @param atts The element's attributes. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void startElement (String uri, String localName, String qName, + Attributes atts) + throws SAXException + { + if (contentHandler != null) { + contentHandler.startElement(uri, localName, qName, atts); + } + } + + + /** + * Filter an end element event. + * + * @param uri The element's Namespace URI, or the empty string. + * @param localName The element's local name, or the empty string. + * @param qName The element's qualified (prefixed) name, or the empty + * string. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void endElement (String uri, String localName, String qName) + throws SAXException + { + if (contentHandler != null) { + contentHandler.endElement(uri, localName, qName); + } + } + + + /** + * Filter a character data event. + * + * @param ch An array of characters. + * @param start The starting position in the array. + * @param length The number of characters to use from the array. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void characters (char ch[], int start, int length) + throws SAXException + { + if (contentHandler != null) { + contentHandler.characters(ch, start, length); + } + } + + + /** + * Filter an ignorable whitespace event. + * + * @param ch An array of characters. + * @param start The starting position in the array. + * @param length The number of characters to use from the array. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException + { + if (contentHandler != null) { + contentHandler.ignorableWhitespace(ch, start, length); + } + } + + + /** + * Filter a processing instruction event. + * + * @param target The processing instruction target. + * @param data The text following the target. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void processingInstruction (String target, String data) + throws SAXException + { + if (contentHandler != null) { + contentHandler.processingInstruction(target, data); + } + } + + + /** + * Filter a skipped entity event. + * + * @param name The name of the skipped entity. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void skippedEntity (String name) + throws SAXException + { + if (contentHandler != null) { + contentHandler.skippedEntity(name); + } + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.ErrorHandler. + //////////////////////////////////////////////////////////////////// + + + /** + * Filter a warning event. + * + * @param e The warning as an exception. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void warning (SAXParseException e) + throws SAXException + { + if (errorHandler != null) { + errorHandler.warning(e); + } + } + + + /** + * Filter an error event. + * + * @param e The error as an exception. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void error (SAXParseException e) + throws SAXException + { + if (errorHandler != null) { + errorHandler.error(e); + } + } + + + /** + * Filter a fatal error event. + * + * @param e The error as an exception. + * @exception org.xml.sax.SAXException The client may throw + * an exception during processing. + */ + public void fatalError (SAXParseException e) + throws SAXException + { + if (errorHandler != null) { + errorHandler.fatalError(e); + } + } + + + + //////////////////////////////////////////////////////////////////// + // Internal methods. + //////////////////////////////////////////////////////////////////// + + + /** + * Set up before a parse. + * + * <p>Before every parse, check whether the parent is + * non-null, and re-register the filter for all of the + * events.</p> + */ + private void setupParse () + { + if (parent == null) { + throw new NullPointerException("No parent for filter"); + } + parent.setEntityResolver(this); + parent.setDTDHandler(this); + parent.setContentHandler(this); + parent.setErrorHandler(this); + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + private XMLReader parent = null; + private Locator locator = null; + private EntityResolver entityResolver = null; + private DTDHandler dtdHandler = null; + private ContentHandler contentHandler = null; + private ErrorHandler errorHandler = null; + +} + +// end of XMLFilterImpl.java diff --git a/xml/src/main/java/org/xml/sax/helpers/XMLReaderAdapter.java b/xml/src/main/java/org/xml/sax/helpers/XMLReaderAdapter.java new file mode 100644 index 0000000..cc8f431 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/XMLReaderAdapter.java @@ -0,0 +1,538 @@ +// XMLReaderAdapter.java - adapt an SAX2 XMLReader to a SAX1 Parser +// http://www.saxproject.org +// Written by David Megginson +// NO WARRANTY! This class is in the public domain. +// $Id: XMLReaderAdapter.java,v 1.9 2004/04/26 17:34:35 dmegginson Exp $ + +package org.xml.sax.helpers; + +import java.io.IOException; +import java.util.Locale; + +import org.xml.sax.Parser; // deprecated +import org.xml.sax.Locator; +import org.xml.sax.InputSource; +import org.xml.sax.AttributeList; // deprecated +import org.xml.sax.EntityResolver; +import org.xml.sax.DTDHandler; +import org.xml.sax.DocumentHandler; // deprecated +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; + +import org.xml.sax.XMLReader; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXNotSupportedException; + + +/** + * Adapt a SAX2 XMLReader as a SAX1 Parser. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class wraps a SAX2 {@link org.xml.sax.XMLReader XMLReader} + * and makes it act as a SAX1 {@link org.xml.sax.Parser Parser}. The XMLReader + * must support a true value for the + * http://xml.org/sax/features/namespace-prefixes property or parsing will fail + * with a {@link org.xml.sax.SAXException SAXException}; if the XMLReader + * supports a false value for the http://xml.org/sax/features/namespaces + * property, that will also be used to improve efficiency.</p> + * + * @since SAX 2.0 + * @author David Megginson + * @version 2.0.1 (sax2r2) + * @see org.xml.sax.Parser + * @see org.xml.sax.XMLReader + */ +public class XMLReaderAdapter implements Parser, ContentHandler +{ + + + //////////////////////////////////////////////////////////////////// + // Constructor. + //////////////////////////////////////////////////////////////////// + + + /** + * Create a new adapter. + * + * <p>Use the "org.xml.sax.driver" property to locate the SAX2 + * driver to embed.</p> + * + * @exception org.xml.sax.SAXException If the embedded driver + * cannot be instantiated or if the + * org.xml.sax.driver property is not specified. + */ + public XMLReaderAdapter () + throws SAXException + { + setup(XMLReaderFactory.createXMLReader()); + } + + + /** + * Create a new adapter. + * + * <p>Create a new adapter, wrapped around a SAX2 XMLReader. + * The adapter will make the XMLReader act like a SAX1 + * Parser.</p> + * + * @param xmlReader The SAX2 XMLReader to wrap. + * @exception java.lang.NullPointerException If the argument is null. + */ + public XMLReaderAdapter (XMLReader xmlReader) + { + setup(xmlReader); + } + + + + /** + * Internal setup. + * + * @param xmlReader The embedded XMLReader. + */ + private void setup (XMLReader xmlReader) + { + if (xmlReader == null) { + throw new NullPointerException("XMLReader must not be null"); + } + this.xmlReader = xmlReader; + qAtts = new AttributesAdapter(); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.Parser. + //////////////////////////////////////////////////////////////////// + + + /** + * Set the locale for error reporting. + * + * <p>This is not supported in SAX2, and will always throw + * an exception.</p> + * + * @param locale the locale for error reporting. + * @see org.xml.sax.Parser#setLocale + * @exception org.xml.sax.SAXException Thrown unless overridden. + */ + public void setLocale (Locale locale) + throws SAXException + { + throw new SAXNotSupportedException("setLocale not supported"); + } + + + /** + * Register the entity resolver. + * + * @param resolver The new resolver. + * @see org.xml.sax.Parser#setEntityResolver + */ + public void setEntityResolver (EntityResolver resolver) + { + xmlReader.setEntityResolver(resolver); + } + + + /** + * Register the DTD event handler. + * + * @param handler The new DTD event handler. + * @see org.xml.sax.Parser#setDTDHandler + */ + public void setDTDHandler (DTDHandler handler) + { + xmlReader.setDTDHandler(handler); + } + + + /** + * Register the SAX1 document event handler. + * + * <p>Note that the SAX1 document handler has no Namespace + * support.</p> + * + * @param handler The new SAX1 document event handler. + * @see org.xml.sax.Parser#setDocumentHandler + */ + public void setDocumentHandler (DocumentHandler handler) + { + documentHandler = handler; + } + + + /** + * Register the error event handler. + * + * @param handler The new error event handler. + * @see org.xml.sax.Parser#setErrorHandler + */ + public void setErrorHandler (ErrorHandler handler) + { + xmlReader.setErrorHandler(handler); + } + + + /** + * Parse the document. + * + * <p>This method will throw an exception if the embedded + * XMLReader does not support the + * http://xml.org/sax/features/namespace-prefixes property.</p> + * + * @param systemId The absolute URL of the document. + * @exception java.io.IOException If there is a problem reading + * the raw content of the document. + * @exception org.xml.sax.SAXException If there is a problem + * processing the document. + * @see #parse(org.xml.sax.InputSource) + * @see org.xml.sax.Parser#parse(java.lang.String) + */ + public void parse (String systemId) + throws IOException, SAXException + { + parse(new InputSource(systemId)); + } + + + /** + * Parse the document. + * + * <p>This method will throw an exception if the embedded + * XMLReader does not support the + * http://xml.org/sax/features/namespace-prefixes property.</p> + * + * @param input An input source for the document. + * @exception java.io.IOException If there is a problem reading + * the raw content of the document. + * @exception org.xml.sax.SAXException If there is a problem + * processing the document. + * @see #parse(java.lang.String) + * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource) + */ + public void parse (InputSource input) + throws IOException, SAXException + { + setupXMLReader(); + xmlReader.parse(input); + } + + + /** + * Set up the XML reader. + */ + private void setupXMLReader () + throws SAXException + { + xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); + try { + xmlReader.setFeature("http://xml.org/sax/features/namespaces", + false); + } catch (SAXException e) { + // NO OP: it's just extra information, and we can ignore it + } + xmlReader.setContentHandler(this); + } + + + + //////////////////////////////////////////////////////////////////// + // Implementation of org.xml.sax.ContentHandler. + //////////////////////////////////////////////////////////////////// + + + /** + * Set a document locator. + * + * @param locator The document locator. + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ + public void setDocumentLocator (Locator locator) + { + if (documentHandler != null) + documentHandler.setDocumentLocator(locator); + } + + + /** + * Start document event. + * + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#startDocument + */ + public void startDocument () + throws SAXException + { + if (documentHandler != null) + documentHandler.startDocument(); + } + + + /** + * End document event. + * + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#endDocument + */ + public void endDocument () + throws SAXException + { + if (documentHandler != null) + documentHandler.endDocument(); + } + + + /** + * Adapt a SAX2 start prefix mapping event. + * + * @param prefix The prefix being mapped. + * @param uri The Namespace URI being mapped to. + * @see org.xml.sax.ContentHandler#startPrefixMapping + */ + public void startPrefixMapping (String prefix, String uri) + { + } + + + /** + * Adapt a SAX2 end prefix mapping event. + * + * @param prefix The prefix being mapped. + * @see org.xml.sax.ContentHandler#endPrefixMapping + */ + public void endPrefixMapping (String prefix) + { + } + + + /** + * Adapt a SAX2 start element event. + * + * @param uri The Namespace URI. + * @param localName The Namespace local name. + * @param qName The qualified (prefixed) name. + * @param atts The SAX2 attributes. + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#endDocument + */ + public void startElement (String uri, String localName, + String qName, Attributes atts) + throws SAXException + { + if (documentHandler != null) { + qAtts.setAttributes(atts); + documentHandler.startElement(qName, qAtts); + } + } + + + /** + * Adapt a SAX2 end element event. + * + * @param uri The Namespace URI. + * @param localName The Namespace local name. + * @param qName The qualified (prefixed) name. + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#endElement + */ + public void endElement (String uri, String localName, + String qName) + throws SAXException + { + if (documentHandler != null) + documentHandler.endElement(qName); + } + + + /** + * Adapt a SAX2 characters event. + * + * @param ch An array of characters. + * @param start The starting position in the array. + * @param length The number of characters to use. + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#characters + */ + public void characters (char ch[], int start, int length) + throws SAXException + { + if (documentHandler != null) + documentHandler.characters(ch, start, length); + } + + + /** + * Adapt a SAX2 ignorable whitespace event. + * + * @param ch An array of characters. + * @param start The starting position in the array. + * @param length The number of characters to use. + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#ignorableWhitespace + */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException + { + if (documentHandler != null) + documentHandler.ignorableWhitespace(ch, start, length); + } + + + /** + * Adapt a SAX2 processing instruction event. + * + * @param target The processing instruction target. + * @param data The remainder of the processing instruction + * @exception org.xml.sax.SAXException The client may raise a + * processing exception. + * @see org.xml.sax.ContentHandler#processingInstruction + */ + public void processingInstruction (String target, String data) + throws SAXException + { + if (documentHandler != null) + documentHandler.processingInstruction(target, data); + } + + + /** + * Adapt a SAX2 skipped entity event. + * + * @param name The name of the skipped entity. + * @see org.xml.sax.ContentHandler#skippedEntity + * @exception org.xml.sax.SAXException Throwable by subclasses. + */ + public void skippedEntity (String name) + throws SAXException + { + } + + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + XMLReader xmlReader; + DocumentHandler documentHandler; + AttributesAdapter qAtts; + + + + //////////////////////////////////////////////////////////////////// + // Internal class. + //////////////////////////////////////////////////////////////////// + + + /** + * Internal class to wrap a SAX2 Attributes object for SAX1. + */ + final class AttributesAdapter implements AttributeList + { + AttributesAdapter () + { + } + + + /** + * Set the embedded Attributes object. + * + * @param The embedded SAX2 Attributes. + */ + void setAttributes (Attributes attributes) + { + this.attributes = attributes; + } + + + /** + * Return the number of attributes. + * + * @return The length of the attribute list. + * @see org.xml.sax.AttributeList#getLength + */ + public int getLength () + { + return attributes.getLength(); + } + + + /** + * Return the qualified (prefixed) name of an attribute by position. + * + * @return The qualified name. + * @see org.xml.sax.AttributeList#getName + */ + public String getName (int i) + { + return attributes.getQName(i); + } + + + /** + * Return the type of an attribute by position. + * + * @return The type. + * @see org.xml.sax.AttributeList#getType(int) + */ + public String getType (int i) + { + return attributes.getType(i); + } + + + /** + * Return the value of an attribute by position. + * + * @return The value. + * @see org.xml.sax.AttributeList#getValue(int) + */ + public String getValue (int i) + { + return attributes.getValue(i); + } + + + /** + * Return the type of an attribute by qualified (prefixed) name. + * + * @return The type. + * @see org.xml.sax.AttributeList#getType(java.lang.String) + */ + public String getType (String qName) + { + return attributes.getType(qName); + } + + + /** + * Return the value of an attribute by qualified (prefixed) name. + * + * @return The value. + * @see org.xml.sax.AttributeList#getValue(java.lang.String) + */ + public String getValue (String qName) + { + return attributes.getValue(qName); + } + + private Attributes attributes; + } + +} + +// end of XMLReaderAdapter.java diff --git a/xml/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java b/xml/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java new file mode 100644 index 0000000..96151a1 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java @@ -0,0 +1,206 @@ +// XMLReaderFactory.java - factory for creating a new reader. +// http://www.saxproject.org +// Written by David Megginson +// and by David Brownell +// NO WARRANTY! This class is in the Public Domain. +// $Id: XMLReaderFactory.java,v 1.10 2002/04/22 01:00:13 dbrownell Exp $ + +package org.xml.sax.helpers; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import org.xml.sax.XMLReader; +import org.xml.sax.SAXException; + + +/** + * Factory for creating an XML reader. + * + * <blockquote> + * <em>This module, both source code and documentation, is in the + * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> + * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> + * for further information. + * </blockquote> + * + * <p>This class contains static methods for creating an XML reader + * from an explicit class name, or based on runtime defaults:</p> + * + * <pre> + * try { + * XMLReader myReader = XMLReaderFactory.createXMLReader(); + * } catch (SAXException e) { + * System.err.println(e.getMessage()); + * } + * </pre> + * + * <p><strong>Note to Distributions bundled with parsers:</strong> + * You should modify the implementation of the no-arguments + * <em>createXMLReader</em> to handle cases where the external + * configuration mechanisms aren't set up. That method should do its + * best to return a parser when one is in the class path, even when + * nothing bound its class name to <code>org.xml.sax.driver</code> so + * those configuration mechanisms would see it.</p> + * + * @since SAX 2.0 + * @author David Megginson, David Brownell + * @version 2.0.1 (sax2r2) + */ +final public class XMLReaderFactory +{ + /** + * Private constructor. + * + * <p>This constructor prevents the class from being instantiated.</p> + */ + private XMLReaderFactory () + { + } + + private static final String property = "org.xml.sax.driver"; + + /** + * Attempt to create an XMLReader from system defaults. + * In environments which can support it, the name of the XMLReader + * class is determined by trying each these options in order, and + * using the first one which succeeds:</p> <ul> + * + * <li>If the system property <code>org.xml.sax.driver</code> + * has a value, that is used as an XMLReader class name. </li> + * + * <li>The JAR "Services API" is used to look for a class name + * in the <em>META-INF/services/org.xml.sax.driver</em> file in + * jarfiles available to the runtime.</li> + * + * <li> SAX parser distributions are strongly encouraged to provide + * a default XMLReader class name that will take effect only when + * previous options (on this list) are not successful.</li> + * + * <li>Finally, if {@link ParserFactory#makeParser()} can + * return a system default SAX1 parser, that parser is wrapped in + * a {@link ParserAdapter}. (This is a migration aid for SAX1 + * environments, where the <code>org.xml.sax.parser</code> system + * property will often be usable.) </li> + * + * </ul> + * + * <p> In environments such as small embedded systems, which can not + * support that flexibility, other mechanisms to determine the default + * may be used. </p> + * + * <p>Note that many Java environments allow system properties to be + * initialized on a command line. This means that <em>in most cases</em> + * setting a good value for that property ensures that calls to this + * method will succeed, except when security policies intervene. + * This will also maximize application portability to older SAX + * environments, with less robust implementations of this method. + * </p> + * + * @return A new XMLReader. + * @exception org.xml.sax.SAXException If no default XMLReader class + * can be identified and instantiated. + * @see #createXMLReader(java.lang.String) + */ + public static XMLReader createXMLReader () + throws SAXException + { + String className = null; + ClassLoader loader = NewInstance.getClassLoader (); + + // 1. try the JVM-instance-wide system property + try { className = System.getProperty (property); } + catch (RuntimeException e) { /* normally fails for applets */ } + + // 2. if that fails, try META-INF/services/ + if (className == null) { + try { + String service = "META-INF/services/" + property; + InputStream in; + BufferedReader reader; + + if (loader == null) + in = ClassLoader.getSystemResourceAsStream (service); + else + in = loader.getResourceAsStream (service); + + if (in != null) { + // BEGIN android-modified + reader = new BufferedReader ( + new InputStreamReader (in, "UTF8"), 8192); + // END android-modified + className = reader.readLine (); + in.close (); + } + } catch (Exception e) { + } + } + + // 3. Distro-specific fallback + if (className == null) { +// BEGIN DISTRIBUTION-SPECIFIC + + // EXAMPLE: + // className = "com.example.sax.XmlReader"; + // or a $JAVA_HOME/jre/lib/*properties setting... + +// END DISTRIBUTION-SPECIFIC + } + + // do we know the XMLReader implementation class yet? + if (className != null) + return loadClass (loader, className); + + // 4. panic -- adapt any SAX1 parser + try { + return new ParserAdapter (ParserFactory.makeParser ()); + } catch (Exception e) { + throw new SAXException ("Can't create default XMLReader; " + + "is system property org.xml.sax.driver set?"); + } + } + + + /** + * Attempt to create an XML reader from a class name. + * + * <p>Given a class name, this method attempts to load + * and instantiate the class as an XML reader.</p> + * + * @param className the name of the class that should be instantiated. + * + * <p>Note that this method will not be usable in environments where + * the caller (perhaps an applet) is not permitted to load classes + * dynamically.</p> + * + * @return A new XML reader. + * @exception org.xml.sax.SAXException If the class cannot be + * loaded, instantiated, and cast to XMLReader. + * @see #createXMLReader() + */ + public static XMLReader createXMLReader (String className) + throws SAXException + { + return loadClass (NewInstance.getClassLoader (), className); + } + + private static XMLReader loadClass (ClassLoader loader, String className) + throws SAXException + { + try { + return (XMLReader) NewInstance.newInstance (loader, className); + } catch (ClassNotFoundException e1) { + throw new SAXException("SAX2 driver class " + className + + " not found", e1); + } catch (IllegalAccessException e2) { + throw new SAXException("SAX2 driver class " + className + + " found but cannot be loaded", e2); + } catch (InstantiationException e3) { + throw new SAXException("SAX2 driver class " + className + + " loaded but cannot be instantiated (no empty public constructor?)", + e3); + } catch (ClassCastException e4) { + throw new SAXException("SAX2 driver class " + className + + " does not implement XMLReader", e4); + } + } +} diff --git a/xml/src/main/java/org/xml/sax/helpers/package.html b/xml/src/main/java/org/xml/sax/helpers/package.html new file mode 100644 index 0000000..3a265fd --- /dev/null +++ b/xml/src/main/java/org/xml/sax/helpers/package.html @@ -0,0 +1,13 @@ +<HTML><HEAD> +<!-- $Id: package.html,v 1.6 2002/01/30 20:52:39 dbrownell Exp $ --> +</HEAD><BODY> + +<p>This package contains "helper" classes, including +support for bootstrapping SAX-based applications. + +<p>See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> +for more information about SAX.</p> + +@since Android 1.0 + +</BODY></HTML> diff --git a/xml/src/main/java/org/xml/sax/package.html b/xml/src/main/java/org/xml/sax/package.html new file mode 100644 index 0000000..fbe7108 --- /dev/null +++ b/xml/src/main/java/org/xml/sax/package.html @@ -0,0 +1,299 @@ +<html><head> +<!-- $Id: package.html,v 1.18 2004/04/21 13:06:01 dmegginson Exp $ --> +</head><body> + +<p> This package provides the core SAX APIs. +Some SAX1 APIs are deprecated to encourage integration of +namespace-awareness into designs of new applications +and into maintenance of existing infrastructure. </p> + +<p>See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> +for more information about SAX.</p> + + +<h2> SAX2 Standard Feature Flags </h2> + +<p> One of the essential characteristics of SAX2 is that it added +feature flags which can be used to examine and perhaps modify +parser modes, in particular modes such as validation. +Since features are identified by (absolute) URIs, anyone +can define such features. +Currently defined standard feature URIs have the prefix +<code>http://xml.org/sax/features/</code> before an identifier such as +<code>validation</code>. Turn features on or off using +<em>setFeature</em>. Those standard identifiers are: </p> + + +<table border="1" cellpadding="3" cellspacing="0" width="100%"> + <tr align="center" bgcolor="#ccccff"> + <th>Feature ID</th> + <th>Access</th> + <th>Default</th> + <th>Description</th> + </tr> + + <tr> + <td>external-general-entities</td> + <td><em>read/write</em></td> + <td><em>unspecified</em></td> + <td> Reports whether this parser processes external + general entities; always true if validating. + </td> + </tr> + + <tr> + <td>external-parameter-entities</td> + <td><em>read/write</em></td> + <td><em>unspecified</em></td> + <td> Reports whether this parser processes external + parameter entities; always true if validating. + </td> + </tr> + + <tr> + <td>is-standalone</td> + <td>(parsing) <em>read-only</em>, (not parsing) <em>none</em></td> + <td>not applicable</td> + <td> May be examined only during a parse, after the + <em>startDocument()</em> callback has been completed; read-only. + The value is true if the document specified standalone="yes" in + its XML declaration, and otherwise is false. + </td> + </tr> + + <tr> + <td>lexical-handler/parameter-entities</td> + <td><em>read/write</em></td> + <td><em>unspecified</em></td> + <td> A value of "true" indicates that the LexicalHandler will report + the beginning and end of parameter entities. + </td> + </tr> + + <tr> + <td>namespaces</td> + <td><em>read/write</em></td> + <td>true</td> + <td> A value of "true" indicates namespace URIs and unprefixed local names + for element and attribute names will be available. + </td> + </tr> + + <tr> + <td>namespace-prefixes</td> + <td><em>read/write</em></td> + <td>false</td> + <td> A value of "true" indicates that XML qualified names (with prefixes) and + attributes (including <em>xmlns*</em> attributes) will be available. + </td> + </tr> + + <tr> + <td>resolve-dtd-uris</td> + <td><em>read/write</em></td> + <td><em>true</em></td> + <td> A value of "true" indicates that system IDs in declarations will + be absolutized (relative to their base URIs) before reporting. + (That is the default behavior for all SAX2 XML parsers.) + A value of "false" indicates those IDs will not be absolutized; + parsers will provide the base URI from + <em>Locator.getSystemId()</em>. + This applies to system IDs passed in <ul> + <li><em>DTDHandler.notationDecl()</em>, + <li><em>DTDHandler.unparsedEntityDecl()</em>, and + <li><em>DeclHandler.externalEntityDecl()</em>. + </ul> + It does not apply to <em>EntityResolver.resolveEntity()</em>, + which is not used to report declarations, or to + <em>LexicalHandler.startDTD()</em>, which already provides + the non-absolutized URI. + </td> + </tr> + + <tr> + <td>string-interning</td> + <td><em>read/write</em></td> + <td><em>unspecified</em></td> + <td> Has a value of "true" if all XML names (for elements, prefixes, + attributes, entities, notations, and local names), + as well as Namespace URIs, will have been interned + using <em>java.lang.String.intern</em>. This supports fast + testing of equality/inequality against string constants, + rather than forcing slower calls to <em>String.equals()</em>. + </td> + </tr> + + <tr> + <td>unicode-normalization-checking</td> + <td><em>read/write</em></td> + <td><em>false</em></td> + <td> Controls whether the parser reports Unicode normalization + errors as described in section 2.13 and Appendix B of the + XML 1.1 Recommendation. If true, Unicode normalization + errors are reported using the ErrorHandler.error() callback. + Such errors are not fatal in themselves (though, obviously, + other Unicode-related encoding errors may be). + </td> + </tr> + + <tr> + <td>use-attributes2</td> + <td><em>read-only</em></td> + <td>not applicable</td> + <td> Returns "true" if the <em>Attributes</em> objects passed by + this parser in <em>ContentHandler.startElement()</em> + implement the <a href="ext/Attributes2.html" + ><em>org.xml.sax.ext.Attributes2</em></a> interface. + That interface exposes additional DTD-related information, + such as whether the attribute was specified in the + source text rather than defaulted. + </td> + </tr> + + <tr> + <td>use-locator2</td> + <td><em>read-only</em></td> + <td>not applicable</td> + <td> Returns "true" if the <em>Locator</em> objects passed by + this parser in <em>ContentHandler.setDocumentLocator()</em> + implement the <a href="ext/Locator2.html" + ><em>org.xml.sax.ext.Locator2</em></a> interface. + That interface exposes additional entity information, + such as the character encoding and XML version used. + </td> + </tr> + + <tr> + <td>use-entity-resolver2</td> + <td><em>read/write</em></td> + <td><em>true</em></td> + <td> Returns "true" if, when <em>setEntityResolver</em> is given + an object implementing the <a href="ext/EntityResolver2.html" + ><em>org.xml.sax.ext.EntityResolver2</em></a> interface, + those new methods will be used. + Returns "false" to indicate that those methods will not be used. + </td> + </tr> + + <tr> + <td>validation</td> + <td><em>read/write</em></td> + <td><em>unspecified</em></td> + <td> Controls whether the parser is reporting all validity + errors; if true, all external entities will be read. + </td> + </tr> + + <tr> + <td>xmlns-uris</td> + <td><em>read/write</em></td> + <td><em>false</em></td> + <td> Controls whether, when the <em>namespace-prefixes</em> feature + is set, the parser treats namespace declaration attributes as + being in the <em>http://www.w3.org/2000/xmlns/</em> namespace. + By default, SAX2 conforms to the original "Namespaces in XML" + Recommendation, which explicitly states that such attributes are + not in any namespace. + Setting this optional flag to "true" makes the SAX2 events conform to + a later backwards-incompatible revision of that recommendation, + placing those attributes in a namespace. + </td> + </tr> + + <tr> + <td>xml-1.1</td> + <td><em>read-only</em></td> + <td>not applicable</td> + <td> Returns "true" if the parser supports both XML 1.1 and XML 1.0. + Returns "false" if the parser supports only XML 1.0. + </td> + </tr> + +</table> + +<p> Support for the default values of the +<em>namespaces</em> and <em>namespace-prefixes</em> +properties is required. +Support for any other feature flags is entirely optional. +</p> + +<p> For default values not specified by SAX2, +each XMLReader implementation specifies its default, +or may choose not to expose the feature flag. +Unless otherwise specified here, +implementations may support changing current values +of these standard feature flags, but not while parsing. +</p> + +<h2> SAX2 Standard Handler and Property IDs </h2> + +<p> For parser interface characteristics that are described +as objects, a separate namespace is defined. The +objects in this namespace are again identified by URI, and +the standard property URIs have the prefix +<code>http://xml.org/sax/properties/</code> before an identifier such as +<code>lexical-handler</code> or +<code>dom-node</code>. Manage those properties using +<em>setProperty()</em>. Those identifiers are: </p> + +<table border="1" cellpadding="3" cellspacing="0" width="100%"> + <tr align="center" bgcolor="#ccccff"> + <th>Property ID</th> + <th>Description</th> + </tr> + + <tr> + <td>declaration-handler</td> + <td> Used to see most DTD declarations except those treated + as lexical ("document element name is ...") or which are + mandatory for all SAX parsers (<em>DTDHandler</em>). + The Object must implement <a href="ext/DeclHandler.html" + ><em>org.xml.sax.ext.DeclHandler</em></a>. + </td> + </tr> + + <tr> + <td>document-xml-version</td> + <td> May be examined only during a parse, after the startDocument() + callback has been completed; read-only. This property is a + literal string describing the actual XML version of the document, + such as "1.0" or "1.1". + </td> + </tr> + + <tr> + <td>dom-node</td> + <td> For "DOM Walker" style parsers, which ignore their + <em>parser.parse()</em> parameters, this is used to + specify the DOM (sub)tree being walked by the parser. + The Object must implement the + <em>org.w3c.dom.Node</em> interface. + </td> + </tr> + + <tr> + <td>lexical-handler</td> + <td> Used to see some syntax events that are essential in some + applications: comments, CDATA delimiters, selected general + entity inclusions, and the start and end of the DTD + (and declaration of document element name). + The Object must implement <a href="ext/LexicalHandler.html" + ><em>org.xml.sax.ext.LexicalHandler</em></a>. + </td> + </tr> + + <tr> + <td>xml-string</td> + <td> Readable only during a parser callback, this exposes a <b>TBS</b> + chunk of characters responsible for the current event. </td> + </tr> + +</table> + +<p> All of these standard properties are optional; +XMLReader implementations need not support them. +</p> + +@since Android 1.0 + +</body></html>
\ No newline at end of file diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java new file mode 100644 index 0000000..2c2946f --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java @@ -0,0 +1,1116 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1; + +import java.io.InputStream; +import java.io.IOException; +import java.io.Reader; + +/** + * XML Pull Parser is an interface that defines parsing functionlity provided + * in <a href="http://www.xmlpull.org/">XMLPULL V1 API</a> (visit this website to + * learn more about API and its implementations). + * + * <p>There are following different + * kinds of parser depending on which features are set:<ul> + * <li><b>non-validating</b> parser as defined in XML 1.0 spec when + * FEATURE_PROCESS_DOCDECL is set to true + * <li><b>validating parser</b> as defined in XML 1.0 spec when + * FEATURE_VALIDATION is true (and that implies that FEATURE_PROCESS_DOCDECL is true) + * <li>when FEATURE_PROCESS_DOCDECL is false (this is default and + * if different value is required necessary must be changed before parsing is started) + * then parser behaves like XML 1.0 compliant non-validating parser under condition that + * <em>no DOCDECL is present</em> in XML documents + * (internal entites can still be defined with defineEntityReplacementText()). + * This mode of operation is intened <b>for operation in constrained environments</b> such as J2ME. + * </ul> + * + * + * <p>There are two key methods: next() and nextToken(). While next() provides + * access to high level parsing events, nextToken() allows access to lower + * level tokens. + * + * <p>The current event state of the parser + * can be determined by calling the + * <a href="#getEventType()">getEventType()</a> method. + * Initially, the parser is in the <a href="#START_DOCUMENT">START_DOCUMENT</a> + * state. + * + * <p>The method <a href="#next()">next()</a> advances the parser to the + * next event. The int value returned from next determines the current parser + * state and is identical to the value returned from following calls to + * getEventType (). + * + * <p>Th following event types are seen by next()<dl> + * <dt><a href="#START_TAG">START_TAG</a><dd> An XML start tag was read. + * <dt><a href="#TEXT">TEXT</a><dd> Text content was read; + * the text content can be retreived using the getText() method. + * (when in validating mode next() will not report ignorable whitespaces, use nextToken() instead) + * <dt><a href="#END_TAG">END_TAG</a><dd> An end tag was read + * <dt><a href="#END_DOCUMENT">END_DOCUMENT</a><dd> No more events are available + * </dl> + * + * <p>after first next() or nextToken() (or any other next*() method) + * is called user application can obtain + * XML version, standalone and encoding from XML declaration + * in following ways:<ul> + * <li><b>version</b>: + * getProperty("<a href="http://xmlpull.org/v1/doc/properties.html#xmldecl-version">http://xmlpull.org/v1/doc/properties.html#xmldecl-version</a>") + * returns String ("1.0") or null if XMLDecl was not read or if property is not supported + * <li><b>standalone</b>: + * getProperty("<a href="http://xmlpull.org/v1/doc/features.html#xmldecl-standalone">http://xmlpull.org/v1/doc/features.html#xmldecl-standalone</a>") + * returns Boolean: null if there was no standalone declaration + * or if property is not supported + * otherwise returns Boolean(true) if standalon="yes" and Boolean(false) when standalone="no" + * <li><b>encoding</b>: obtained from getInputEncoding() + * null if stream had unknown encoding (not set in setInputStream) + * and it was not declared in XMLDecl + * </ul> + * + * A minimal example for using this API may look as follows: + * <pre> + * import java.io.IOException; + * import java.io.StringReader; + * + * import org.xmlpull.v1.XmlPullParser; + * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException.html</a>; + * import org.xmlpull.v1.<a href="XmlPullParserFactory.html">XmlPullParserFactory</a>; + * + * public class SimpleXmlPullApp + * { + * + * public static void main (String args[]) + * throws XmlPullParserException, IOException + * { + * XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + * factory.setNamespaceAware(true); + * XmlPullParser xpp = factory.newPullParser(); + * + * xpp.<a href="#setInput">setInput</a>( new StringReader ( "<foo>Hello World!</foo>" ) ); + * int eventType = xpp.getEventType(); + * while (eventType != XmlPullParser.END_DOCUMENT) { + * if(eventType == XmlPullParser.START_DOCUMENT) { + * System.out.println("Start document"); + * } else if(eventType == XmlPullParser.END_DOCUMENT) { + * System.out.println("End document"); + * } else if(eventType == XmlPullParser.START_TAG) { + * System.out.println("Start tag "+xpp.<a href="#getName()">getName()</a>); + * } else if(eventType == XmlPullParser.END_TAG) { + * System.out.println("End tag "+xpp.getName()); + * } else if(eventType == XmlPullParser.TEXT) { + * System.out.println("Text "+xpp.<a href="#getText()">getText()</a>); + * } + * eventType = xpp.next(); + * } + * } + * } + * </pre> + * + * <p>The above example will generate the following output: + * <pre> + * Start document + * Start tag foo + * Text Hello World! + * End tag foo + * </pre> + * + * <p>For more details on API usage, please refer to the + * quick Introduction available at <a href="http://www.xmlpull.org">http://www.xmlpull.org</a> + * + * @see XmlPullParserFactory + * @see #defineEntityReplacementText + * @see #getName + * @see #getNamespace + * @see #getText + * @see #next + * @see #nextToken + * @see #setInput + * @see #FEATURE_PROCESS_DOCDECL + * @see #FEATURE_VALIDATION + * @see #START_DOCUMENT + * @see #START_TAG + * @see #TEXT + * @see #END_TAG + * @see #END_DOCUMENT + * + * @author <a href="http://www-ai.cs.uni-dortmund.de/PERSONAL/haustein.html">Stefan Haustein</a> + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + */ + +public interface XmlPullParser { + + /** This constant represents the default namespace (empty string "") */ + String NO_NAMESPACE = ""; + + // ---------------------------------------------------------------------------- + // EVENT TYPES as reported by next() + + /** + * Signalize that parser is at the very beginning of the document + * and nothing was read yet. + * This event type can only be observed by calling getEvent() + * before the first call to next(), nextToken, or nextTag()</a>). + * + * @see #next + * @see #nextToken + */ + int START_DOCUMENT = 0; + + /** + * Logical end of the xml document. Returned from getEventType, next() + * and nextToken() + * when the end of the input document has been reached. + * <p><strong>NOTE:</strong> calling again + * <a href="#next()">next()</a> or <a href="#nextToken()">nextToken()</a> + * will result in exception being thrown. + * + * @see #next + * @see #nextToken + */ + int END_DOCUMENT = 1; + + /** + * Returned from getEventType(), + * <a href="#next()">next()</a>, <a href="#nextToken()">nextToken()</a> when + * a start tag was read. + * The name of start tag is available from getName(), its namespace and prefix are + * available from getNamespace() and getPrefix() + * if <a href='#FEATURE_PROCESS_NAMESPACES'>namespaces are enabled</a>. + * See getAttribute* methods to retrieve element attributes. + * See getNamespace* methods to retrieve newly declared namespaces. + * + * @see #next + * @see #nextToken + * @see #getName + * @see #getPrefix + * @see #getNamespace + * @see #getAttributeCount + * @see #getDepth + * @see #getNamespaceCount + * @see #getNamespace + * @see #FEATURE_PROCESS_NAMESPACES + */ + int START_TAG = 2; + + /** + * Returned from getEventType(), <a href="#next()">next()</a>, or + * <a href="#nextToken()">nextToken()</a> when an end tag was read. + * The name of start tag is available from getName(), its + * namespace and prefix are + * available from getNamespace() and getPrefix(). + * + * @see #next + * @see #nextToken + * @see #getName + * @see #getPrefix + * @see #getNamespace + * @see #FEATURE_PROCESS_NAMESPACES + */ + int END_TAG = 3; + + + /** + * Character data was read and will is available by calling getText(). + * <p><strong>Please note:</strong> <a href="#next()">next()</a> will + * accumulate multiple + * events into one TEXT event, skipping IGNORABLE_WHITESPACE, + * PROCESSING_INSTRUCTION and COMMENT events, + * In contrast, <a href="#nextToken()">nextToken()</a> will stop reading + * text when any other event is observed. + * Also, when the state was reached by calling next(), the text value will + * be normalized, whereas getText() will + * return unnormalized content in the case of nextToken(). This allows + * an exact roundtrip without chnanging line ends when examining low + * level events, whereas for high level applications the text is + * normalized apropriately. + * + * @see #next + * @see #nextToken + * @see #getText + */ + int TEXT = 4; + + // ---------------------------------------------------------------------------- + // additional events exposed by lower level nextToken() + + /** + * A CDATA sections was just read; + * this token is available only from calls to <a href="#nextToken()">nextToken()</a>. + * A call to next() will accumulate various text events into a single event + * of type TEXT. The text contained in the CDATA section is available + * by callling getText(). + * + * @see #nextToken + * @see #getText + */ + int CDSECT = 5; + + /** + * An entity reference was just read; + * this token is available from <a href="#nextToken()">nextToken()</a> + * only. The entity name is available by calling getName(). If available, + * the replacement text can be obtained by calling getTextt(); otherwise, + * the user is responsibile for resolving the entity reference. + * This event type is never returned from next(); next() will + * accumulate the replacement text and other text + * events to a single TEXT event. + * + * @see #nextToken + * @see #getText + */ + int ENTITY_REF = 6; + + /** + * Ignorable whitespace was just read. + * This token is available only from <a href="#nextToken()">nextToken()</a>). + * For non-validating + * parsers, this event is only reported by nextToken() when outside + * the root element. + * Validating parsers may be able to detect ignorable whitespace at + * other locations. + * The ignorable whitespace string is available by calling getText() + * + * <p><strong>NOTE:</strong> this is different from calling the + * isWhitespace() method, since text content + * may be whitespace but not ignorable. + * + * Ignorable whitespace is skipped by next() automatically; this event + * type is never returned from next(). + * + * @see #nextToken + * @see #getText + */ + int IGNORABLE_WHITESPACE = 7; + + /** + * An XML processing instruction declaration was just read. This + * event type is available only via <a href="#nextToken()">nextToken()</a>. + * getText() will return text that is inside the processing instruction. + * Calls to next() will skip processing instructions automatically. + * @see #nextToken + * @see #getText + */ + int PROCESSING_INSTRUCTION = 8; + + /** + * An XML comment was just read. This event type is this token is + * available via <a href="#nextToken()">nextToken()</a> only; + * calls to next() will skip comments automatically. + * The content of the comment can be accessed using the getText() + * method. + * + * @see #nextToken + * @see #getText + */ + int COMMENT = 9; + + /** + * An XML document type declaration was just read. This token is + * available from <a href="#nextToken()">nextToken()</a> only. + * The unparsed text inside the doctype is available via + * the getText() method. + * + * @see #nextToken + * @see #getText + */ + int DOCDECL = 10; + + /** + * This array can be used to convert the event type integer constants + * such as START_TAG or TEXT to + * to a string. For example, the value of TYPES[START_TAG] is + * the string "START_TAG". + * + * This array is intended for diagnostic output only. Relying + * on the contents of the array may be dangerous since malicous + * applications may alter the array, although it is final, due + * to limitations of the Java language. + */ + String [] TYPES = { + "START_DOCUMENT", + "END_DOCUMENT", + "START_TAG", + "END_TAG", + "TEXT", + "CDSECT", + "ENTITY_REF", + "IGNORABLE_WHITESPACE", + "PROCESSING_INSTRUCTION", + "COMMENT", + "DOCDECL" + }; + + + // ---------------------------------------------------------------------------- + // namespace related features + + /** + * This feature determines whether the parser processes + * namespaces. As for all features, the default value is false. + * <p><strong>NOTE:</strong> The value can not be changed during + * parsing an must be set before parsing. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_PROCESS_NAMESPACES = + "http://xmlpull.org/v1/doc/features.html#process-namespaces"; + + /** + * This feature determines whether namespace attributes are + * exposed via the attribute access methods. Like all features, + * the default value is false. This feature cannot be changed + * during parsing. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = + "http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes"; + + /** + * This feature determines whether the document declaration + * is processed. If set to false, + * the DOCDECL event type is reported by nextToken() + * and ignored by next(). + * + * If this featue is activated, then the document declaration + * must be processed by the parser. + * + * <p><strong>Please note:</strong> If the document type declaration + * was ignored, entity references may cause exceptions + * later in the parsing process. + * The default value of this feature is false. It cannot be changed + * during parsing. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_PROCESS_DOCDECL = + "http://xmlpull.org/v1/doc/features.html#process-docdecl"; + + /** + * If this feature is activated, all validation errors as + * defined in the XML 1.0 sepcification are reported. + * This implies that FEATURE_PROCESS_DOCDECL is true and both, the + * internal and external document type declaration will be processed. + * <p><strong>Please Note:</strong> This feature can not be changed + * during parsing. The default value is false. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_VALIDATION = + "http://xmlpull.org/v1/doc/features.html#validation"; + + /** + * Use this call to change the general behaviour of the parser, + * such as namespace processing or doctype declaration handling. + * This method must be called before the first call to next or + * nextToken. Otherwise, an exception is thrown. + * <p>Example: call setFeature(FEATURE_PROCESS_NAMESPACES, true) in order + * to switch on namespace processing. The initial settings correspond + * to the properties requested from the XML Pull Parser factory. + * If none were requested, all feautures are deactivated by default. + * + * @exception XmlPullParserException If the feature is not supported or can not be set + * @exception IllegalArgumentException If string with the feature name is null + */ + void setFeature(String name, + boolean state) throws XmlPullParserException; + + /** + * Returns the current value of the given feature. + * <p><strong>Please note:</strong> unknown features are + * <strong>always</strong> returned as false. + * + * @param name The name of feature to be retrieved. + * @return The value of the feature. + * @exception IllegalArgumentException if string the feature name is null + */ + + boolean getFeature(String name); + + /** + * Set the value of a property. + * + * The property name is any fully-qualified URI. + * + * @exception XmlPullParserException If the property is not supported or can not be set + * @exception IllegalArgumentException If string with the property name is null + */ + void setProperty(String name, + Object value) throws XmlPullParserException; + + /** + * Look up the value of a property. + * + * The property name is any fully-qualified URI. + * <p><strong>NOTE:</strong> unknown properties are <strong>always</strong> + * returned as null. + * + * @param name The name of property to be retrieved. + * @return The value of named property. + */ + Object getProperty(String name); + + + /** + * Set the input source for parser to the given reader and + * resets the parser. The event type is set to the initial value + * START_DOCUMENT. + * Setting the reader to null will just stop parsing and + * reset parser state, + * allowing the parser to free internal resources + * such as parsing buffers. + */ + void setInput(Reader in) throws XmlPullParserException; + + + /** + * Sets the input stream the parser is going to process. + * This call resets the parser state and sets the event type + * to the initial value START_DOCUMENT. + * + * <p><strong>NOTE:</strong> If an input encoding string is passed, + * it MUST be used. Otherwise, + * if inputEncoding is null, the parser SHOULD try to determine + * input encoding following XML 1.0 specification (see below). + * If encoding detection is supported then following feature + * <a href="http://xmlpull.org/v1/doc/features.html#detect-encoding">http://xmlpull.org/v1/doc/features.html#detect-encoding</a> + * MUST be true amd otherwise it must be false + * + * @param inputStream contains a raw byte input stream of possibly + * unknown encoding (when inputEncoding is null). + * + * @param inputEncoding if not null it MUST be used as encoding for inputStream + */ + void setInput(InputStream inputStream, String inputEncoding) + throws XmlPullParserException; + + /** + * Returns the input encoding if known, null otherwise. + * If setInput(InputStream, inputEncoding) was called with an inputEncoding + * value other than null, this value must be returned + * from this method. Otherwise, if inputEncoding is null and + * the parser suppports the encoding detection feature + * (http://xmlpull.org/v1/doc/features.html#detect-encoding), + * it must return the detected encoding. + * If setInput(Reader) was called, null is returned. + * After first call to next if XML declaration was present this method + * will return encoding declared. + */ + String getInputEncoding(); + + /** + * Set new value for entity replacement text as defined in + * <a href="http://www.w3.org/TR/REC-xml#intern-replacement">XML 1.0 Section 4.5 + * Construction of Internal Entity Replacement Text</a>. + * If FEATURE_PROCESS_DOCDECL or FEATURE_VALIDATION are set, calling this + * function will result in an exception -- when processing of DOCDECL is + * enabled, there is no need to the entity replacement text manually. + * + * <p>The motivation for this function is to allow very small + * implementations of XMLPULL that will work in J2ME environments. + * Though these implementations may not be able to process the document type + * declaration, they still can work with known DTDs by using this function. + * + * <p><b>Please notes:</b> The given value is used literally as replacement text + * and it corresponds to declaring entity in DTD that has all special characters + * escaped: left angle bracket is replaced with &lt;, ampersnad with &amp; + * and so on. + * + * <p><b>Note:</b> The given value is the literal replacement text and must not + * contain any other entity reference (if it contains any entity reference + * there will be no further replacement). + * + * <p><b>Note:</b> The list of pre-defined entity names will + * always contain standard XML entities such as + * amp (&amp;), lt (&lt;), gt (&gt;), quot (&quot;), and apos (&apos;). + * Those cannot be redefined by this method! + * + * @see #setInput + * @see #FEATURE_PROCESS_DOCDECL + * @see #FEATURE_VALIDATION + */ + void defineEntityReplacementText( String entityName, + String replacementText ) throws XmlPullParserException; + + /** + * Returns the numbers of elements in the namespace stack for the given + * depth. + * If namespaces are not enabled, 0 is returned. + * + * <p><b>NOTE:</b> when parser is on END_TAG then it is allowed to call + * this function with getDepth()+1 argument to retrieve position of namespace + * prefixes and URIs that were declared on corresponding START_TAG. + * <p><b>NOTE:</b> to retrieve lsit of namespaces declared in current element:<pre> + * XmlPullParser pp = ... + * int nsStart = pp.getNamespaceCount(pp.getDepth()-1); + * int nsEnd = pp.getNamespaceCount(pp.getDepth()); + * for (int i = nsStart; i < nsEnd; i++) { + * String prefix = pp.getNamespacePrefix(i); + * String ns = pp.getNamespaceUri(i); + * // ... + * } + * </pre> + * + * @see #getNamespacePrefix + * @see #getNamespaceUri + * @see #getNamespace() + * @see #getNamespace(String) + */ + int getNamespaceCount(int depth) throws XmlPullParserException; + + /** + * Returns the namespace prefixe for the given position + * in the namespace stack. + * Default namespace declaration (xmlns='...') will have null as prefix. + * If the given index is out of range, an exception is thrown. + * <p><b>Please note:</b> when the parser is on an END_TAG, + * namespace prefixes that were declared + * in the corresponding START_TAG are still accessible + * although they are no longer in scope. + */ + String getNamespacePrefix(int pos) throws XmlPullParserException; + + /** + * Returns the namespace URI for the given position in the + * namespace stack + * If the position is out of range, an exception is thrown. + * <p><b>NOTE:</b> when parser is on END_TAG then namespace prefixes that were declared + * in corresponding START_TAG are still accessible even though they are not in scope + */ + String getNamespaceUri(int pos) throws XmlPullParserException; + + /** + * Returns the URI corresponding to the given prefix, + * depending on current state of the parser. + * + * <p>If the prefix was not declared in the current scope, + * null is returned. The default namespace is included + * in the namespace table and is available via + * getNamespace (null). + * + * <p>This method is a convenience method for + * + * <pre> + * for (int i = getNamespaceCount(getDepth ())-1; i >= 0; i--) { + * if (getNamespacePrefix(i).equals( prefix )) { + * return getNamespaceUri(i); + * } + * } + * return null; + * </pre> + * + * <p><strong>Please note:</strong> parser implementations + * may provide more efifcient lookup, e.g. using a Hashtable. + * The 'xml' prefix is bound to "http://www.w3.org/XML/1998/namespace", as + * defined in the + * <a href="http://www.w3.org/TR/REC-xml-names/#ns-using">Namespaces in XML</a> + * specification. Analogous, the 'xmlns' prefix is resolved to + * <a href="http://www.w3.org/2000/xmlns/">http://www.w3.org/2000/xmlns/</a> + * + * @see #getNamespaceCount + * @see #getNamespacePrefix + * @see #getNamespaceUri + */ + String getNamespace (String prefix); + + + // -------------------------------------------------------------------------- + // miscellaneous reporting methods + + /** + * Returns the current depth of the element. + * Outside the root element, the depth is 0. The + * depth is incremented by 1 when a start tag is reached. + * The depth is decremented AFTER the end tag + * event was observed. + * + * <pre> + * <!-- outside --> 0 + * <root> 1 + * sometext 1 + * <foobar> 2 + * </foobar> 2 + * </root> 1 + * <!-- outside --> 0 + * </pre> + */ + int getDepth(); + + /** + * Returns a short text describing the current parser state, including + * the position, a + * description of the current event and the data source if known. + * This method is especially useful to provide meaningful + * error messages and for debugging purposes. + */ + String getPositionDescription (); + + + /** + * Returns the current line number, starting from 1. + * When the parser does not know the current line number + * or can not determine it, -1 is returned (e.g. for WBXML). + * + * @return current line number or -1 if unknown. + */ + int getLineNumber(); + + /** + * Returns the current column number, starting from 0. + * When the parser does not know the current column number + * or can not determine it, -1 is returned (e.g. for WBXML). + * + * @return current column number or -1 if unknown. + */ + int getColumnNumber(); + + + // -------------------------------------------------------------------------- + // TEXT related methods + + /** + * Checks whether the current TEXT event contains only whitespace + * characters. + * For IGNORABLE_WHITESPACE, this is always true. + * For TEXT and CDSECT, false is returned when the current event text + * contains at least one non-white space character. For any other + * event type an exception is thrown. + * + * <p><b>Please note:</b> non-validating parsers are not + * able to distinguish whitespace and ignorable whitespace, + * except from whitespace outside the root element. Ignorable + * whitespace is reported as separate event, which is exposed + * via nextToken only. + * + */ + boolean isWhitespace() throws XmlPullParserException; + + /** + * Returns the text content of the current event as String. + * The value returned depends on current event type, + * for example for TEXT event it is element content + * (this is typical case when next() is used). + * + * See description of nextToken() for detailed description of + * possible returned values for different types of events. + * + * <p><strong>NOTE:</strong> in case of ENTITY_REF, this method returns + * the entity replacement text (or null if not available). This is + * the only case where + * getText() and getTextCharacters() return different values. + * + * @see #getEventType + * @see #next + * @see #nextToken + */ + String getText (); + + + /** + * Returns the buffer that contains the text of the current event, + * as well as the start offset and length relevant for the current + * event. See getText(), next() and nextToken() for description of possible returned values. + * + * <p><strong>Please note:</strong> this buffer must not + * be modified and its content MAY change after a call to + * next() or nextToken(). This method will always return the + * same value as getText(), except for ENTITY_REF. In the case + * of ENTITY ref, getText() returns the replacement text and + * this method returns the actual input buffer containing the + * entity name. + * If getText() returns null, this method returns null as well and + * the values returned in the holder array MUST be -1 (both start + * and length). + * + * @see #getText + * @see #next + * @see #nextToken + * + * @param holderForStartAndLength Must hold an 2-element int array + * into which the start offset and length values will be written. + * @return char buffer that contains the text of the current event + * (null if the current event has no text associated). + */ + char[] getTextCharacters(int [] holderForStartAndLength); + + // -------------------------------------------------------------------------- + // START_TAG / END_TAG shared methods + + /** + * Returns the namespace URI of the current element. + * The default namespace is represented + * as empty string. + * If namespaces are not enabled, an empty String ("") is always returned. + * The current event must be START_TAG or END_TAG; otherwise, + * null is returned. + */ + String getNamespace (); + + /** + * For START_TAG or END_TAG events, the (local) name of the current + * element is returned when namespaces are enabled. When namespace + * processing is disabled, the raw name is returned. + * For ENTITY_REF events, the entity name is returned. + * If the current event is not START_TAG, END_TAG, or ENTITY_REF, + * null is returned. + * <p><b>Please note:</b> To reconstruct the raw element name + * when namespaces are enabled and the prefix is not null, + * you will need to add the prefix and a colon to localName.. + * + */ + String getName(); + + /** + * Returns the prefix of the current element. + * If the element is in the default namespace (has no prefix), + * null is returned. + * If namespaces are not enabled, or the current event + * is not START_TAG or END_TAG, null is returned. + */ + String getPrefix(); + + /** + * Returns true if the current event is START_TAG and the tag + * is degenerated + * (e.g. <foobar/>). + * <p><b>NOTE:</b> if the parser is not on START_TAG, an exception + * will be thrown. + */ + boolean isEmptyElementTag() throws XmlPullParserException; + + // -------------------------------------------------------------------------- + // START_TAG Attributes retrieval methods + + /** + * Returns the number of attributes of the current start tag, or + * -1 if the current event type is not START_TAG + * + * @see #getAttributeNamespace + * @see #getAttributeName + * @see #getAttributePrefix + * @see #getAttributeValue + */ + int getAttributeCount(); + + /** + * Returns the namespace URI of the attribute + * with the given index (starts from 0). + * Returns an empty string ("") if namespaces are not enabled + * or the attribute has no namespace. + * Throws an IndexOutOfBoundsException if the index is out of range + * or the current event type is not START_TAG. + * + * <p><strong>NOTE:</strong> if FEATURE_REPORT_NAMESPACE_ATTRIBUTES is set + * then namespace attributes (xmlns:ns='...') must be reported + * with namespace + * <a href="http://www.w3.org/2000/xmlns/">http://www.w3.org/2000/xmlns/</a> + * (visit this URL for description!). + * The default namespace attribute (xmlns="...") will be reported with empty namespace. + * <p><strong>NOTE:</strong>The xml prefix is bound as defined in + * <a href="http://www.w3.org/TR/REC-xml-names/#ns-using">Namespaces in XML</a> + * specification to "http://www.w3.org/XML/1998/namespace". + * + * @param index zero-based index of attribute + * @return attribute namespace, + * empty string ("") is returned if namesapces processing is not enabled or + * namespaces processing is enabled but attribute has no namespace (it has no prefix). + */ + String getAttributeNamespace (int index); + + /** + * Returns the local name of the specified attribute + * if namespaces are enabled or just attribute name if namespaces are disabled. + * Throws an IndexOutOfBoundsException if the index is out of range + * or current event type is not START_TAG. + * + * @param index zero-based index of attribute + * @return attribute name (null is never returned) + */ + String getAttributeName (int index); + + /** + * Returns the prefix of the specified attribute + * Returns null if the element has no prefix. + * If namespaces are disabled it will always return null. + * Throws an IndexOutOfBoundsException if the index is out of range + * or current event type is not START_TAG. + * + * @param index zero-based index of attribute + * @return attribute prefix or null if namespaces processing is not enabled. + */ + String getAttributePrefix(int index); + + /** + * Returns the type of the specified attribute + * If parser is non-validating it MUST return CDATA. + * + * @param index zero-based index of attribute + * @return attribute type (null is never returned) + */ + String getAttributeType(int index); + + /** + * Returns if the specified attribute was not in input was declared in XML. + * If parser is non-validating it MUST always return false. + * This information is part of XML infoset: + * + * @param index zero-based index of attribute + * @return false if attribute was in input + */ + boolean isAttributeDefault(int index); + + /** + * Returns the given attributes value. + * Throws an IndexOutOfBoundsException if the index is out of range + * or current event type is not START_TAG. + * + * <p><strong>NOTE:</strong> attribute value must be normalized + * (including entity replacement text if PROCESS_DOCDECL is false) as described in + * <a href="http://www.w3.org/TR/REC-xml#AVNormalize">XML 1.0 section + * 3.3.3 Attribute-Value Normalization</a> + * + * @see #defineEntityReplacementText + * + * @param index zero-based index of attribute + * @return value of attribute (null is never returned) + */ + String getAttributeValue(int index); + + /** + * Returns the attributes value identified by namespace URI and namespace localName. + * If namespaces are disabled namespace must be null. + * If current event type is not START_TAG then IndexOutOfBoundsException will be thrown. + * + * <p><strong>NOTE:</strong> attribute value must be normalized + * (including entity replacement text if PROCESS_DOCDECL is false) as described in + * <a href="http://www.w3.org/TR/REC-xml#AVNormalize">XML 1.0 section + * 3.3.3 Attribute-Value Normalization</a> + * + * @see #defineEntityReplacementText + * + * @param namespace Namespace of the attribute if namespaces are enabled otherwise must be null + * @param name If namespaces enabled local name of attribute otherwise just attribute name + * @return value of attribute or null if attribute with given name does not exist + */ + String getAttributeValue(String namespace, + String name); + + // -------------------------------------------------------------------------- + // actual parsing methods + + /** + * Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.) + * + * @see #next() + * @see #nextToken() + */ + int getEventType() + throws XmlPullParserException; + + /** + * Get next parsing event - element content wil be coalesced and only one + * TEXT event must be returned for whole element content + * (comments and processing instructions will be ignored and emtity references + * must be expanded or exception mus be thrown if entity reerence can not be exapnded). + * If element content is empty (content is "") then no TEXT event will be reported. + * + * <p><b>NOTE:</b> empty element (such as <tag/>) will be reported + * with two separate events: START_TAG, END_TAG - it must be so to preserve + * parsing equivalency of empty element to <tag></tag>. + * (see isEmptyElementTag ()) + * + * @see #isEmptyElementTag + * @see #START_TAG + * @see #TEXT + * @see #END_TAG + * @see #END_DOCUMENT + */ + + int next() + throws XmlPullParserException, IOException; + + + /** + * This method works similarly to next() but will expose + * additional event types (COMMENT, CDSECT, DOCDECL, ENTITY_REF, PROCESSING_INSTRUCTION, or + * IGNORABLE_WHITESPACE) if they are available in input. + * + * <p>If special feature + * <a href="http://xmlpull.org/v1/doc/features.html#xml-roundtrip">FEATURE_XML_ROUNDTRIP</a> + * (identified by URI: http://xmlpull.org/v1/doc/features.html#xml-roundtrip) + * is enabled it is possible to do XML document round trip ie. reproduce + * exectly on output the XML input using getText(): + * returned content is always unnormalized (exactly as in input). + * Otherwise returned content is end-of-line normalized as described + * <a href="http://www.w3.org/TR/REC-xml#sec-line-ends">XML 1.0 End-of-Line Handling</a> + * and. Also when this feature is enabled exact content of START_TAG, END_TAG, + * DOCDECL and PROCESSING_INSTRUCTION is available. + * + * <p>Here is the list of tokens that can be returned from nextToken() + * and what getText() and getTextCharacters() returns:<dl> + * <dt>START_DOCUMENT<dd>null + * <dt>END_DOCUMENT<dd>null + * <dt>START_TAG<dd>null unless FEATURE_XML_ROUNDTRIP + * enabled and then returns XML tag, ex: <tag attr='val'> + * <dt>END_TAG<dd>null unless FEATURE_XML_ROUNDTRIP + * id enabled and then returns XML tag, ex: </tag> + * <dt>TEXT<dd>return element content. + * <br>Note: that element content may be delivered in multiple consecutive TEXT events. + * <dt>IGNORABLE_WHITESPACE<dd>return characters that are determined to be ignorable white + * space. If the FEATURE_XML_ROUNDTRIP is enabled all whitespace content outside root + * element will always reported as IGNORABLE_WHITESPACE otherise rteporting is optional. + * <br>Note: that element content may be delevered in multiple consecutive IGNORABLE_WHITESPACE events. + * <dt>CDSECT<dd> + * return text <em>inside</em> CDATA + * (ex. 'fo<o' from <!CDATA[fo<o]]>) + * <dt>PROCESSING_INSTRUCTION<dd> + * if FEATURE_XML_ROUNDTRIP is true + * return exact PI content ex: 'pi foo' from <?pi foo?> + * otherwise it may be exact PI content or concatenation of PI target, + * space and data so for example for + * <?target data?> string "target data" may + * be returned if FEATURE_XML_ROUNDTRIP is false. + * <dt>COMMENT<dd>return comment content ex. 'foo bar' from <!--foo bar--> + * <dt>ENTITY_REF<dd>getText() MUST return entity replacement text if PROCESS_DOCDECL is false + * otherwise getText() MAY return null, + * additionally getTextCharacters() MUST return entity name + * (for example 'entity_name' for &entity_name;). + * <br><b>NOTE:</b> this is the only place where value returned from getText() and + * getTextCharacters() <b>are different</b> + * <br><b>NOTE:</b> it is user responsibility to resolve entity reference + * if PROCESS_DOCDECL is false and there is no entity replacement text set in + * defineEntityReplacementText() method (getText() will be null) + * <br><b>NOTE:</b> character entities (ex. &#32;) and standard entities such as + * &amp; &lt; &gt; &quot; &apos; are reported as well + * and are <b>not</b> reported as TEXT tokens but as ENTITY_REF tokens! + * This requirement is added to allow to do roundtrip of XML documents! + * <dt>DOCDECL<dd> + * if FEATURE_XML_ROUNDTRIP is true or PROCESS_DOCDECL is false + * then return what is inside of DOCDECL for example it returns:<pre> + * " titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd" + * [<!ENTITY % active.links "INCLUDE">]"</pre> + * <p>for input document that contained:<pre> + * <!DOCTYPE titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd" + * [<!ENTITY % active.links "INCLUDE">]></pre> + * otherwise if FEATURE_XML_ROUNDTRIP is false and PROCESS_DOCDECL is true + * then what is returned is undefined (it may be even null) + * </dd> + * </dl> + * + * <p><strong>NOTE:</strong> there is no gurantee that there will only one TEXT or + * IGNORABLE_WHITESPACE event from nextToken() as parser may chose to deliver element content in + * multiple tokens (dividing element content into chunks) + * + * <p><strong>NOTE:</strong> whether returned text of token is end-of-line normalized + * is depending on FEATURE_XML_ROUNDTRIP. + * + * <p><strong>NOTE:</strong> XMLDecl (<?xml ...?>) is not reported but its content + * is available through optional properties (see class description above). + * + * @see #next + * @see #START_TAG + * @see #TEXT + * @see #END_TAG + * @see #END_DOCUMENT + * @see #COMMENT + * @see #DOCDECL + * @see #PROCESSING_INSTRUCTION + * @see #ENTITY_REF + * @see #IGNORABLE_WHITESPACE + */ + int nextToken() + throws XmlPullParserException, IOException; + + //----------------------------------------------------------------------------- + // utility methods to mak XML parsing easier ... + + /** + * Test if the current event is of the given type and if the + * namespace and name do match. null will match any namespace + * and any name. If the test is not passed, an exception is + * thrown. The exception text indicates the parser position, + * the expected event and the current event that is not meeting the + * requirement. + * + * <p>Essentially it does this + * <pre> + * if (type != getEventType() + * || (namespace != null && !namespace.equals( getNamespace () ) ) + * || (name != null && !name.equals( getName() ) ) ) + * throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription()); + * </pre> + */ + void require(int type, String namespace, String name) + throws XmlPullParserException, IOException; + + /** + * If current event is START_TAG then if next element is TEXT then element content is returned + * or if next event is END_TAG then empty string is returned, otherwise exception is thrown. + * After calling this function successfully parser will be positioned on END_TAG. + * + * <p>The motivation for this function is to allow to parse consistently both + * empty elements and elements that has non empty content, for example for input: <ol> + * <li><tag>foo</tag> + * <li><tag></tag> (which is equivalent to <tag/> + * both input can be parsed with the same code: + * <pre> + * p.nextTag() + * p.requireEvent(p.START_TAG, "", "tag"); + * String content = p.nextText(); + * p.requireEvent(p.END_TAG, "", "tag"); + * </pre> + * This function together with nextTag make it very easy to parse XML that has + * no mixed content. + * + * + * <p>Essentially it does this + * <pre> + * if(getEventType() != START_TAG) { + * throw new XmlPullParserException( + * "parser must be on START_TAG to read next text", this, null); + * } + * int eventType = next(); + * if(eventType == TEXT) { + * String result = getText(); + * eventType = next(); + * if(eventType != END_TAG) { + * throw new XmlPullParserException( + * "event TEXT it must be immediately followed by END_TAG", this, null); + * } + * return result; + * } else if(eventType == END_TAG) { + * return ""; + * } else { + * throw new XmlPullParserException( + * "parser must be on START_TAG or TEXT to read text", this, null); + * } + * </pre> + */ + String nextText() throws XmlPullParserException, IOException; + + /** + * Call next() and return event if it is START_TAG or END_TAG + * otherwise throw an exception. + * It will skip whitespace TEXT before actual tag if any. + * + * <p>essentially it does this + * <pre> + * int eventType = next(); + * if(eventType == TEXT && isWhitespace()) { // skip whitespace + * eventType = next(); + * } + * if (eventType != START_TAG && eventType != END_TAG) { + * throw new XmlPullParserException("expected start or end tag", this, null); + * } + * return eventType; + * </pre> + */ + int nextTag() throws XmlPullParserException, IOException; + +} + diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java new file mode 100644 index 0000000..b4b4b71 --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java @@ -0,0 +1,76 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1; + +/** + * This exception is thrown to signal XML Pull Parser related faults. + * + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + */ +public class XmlPullParserException extends Exception { + protected Throwable detail; + protected int row = -1; + protected int column = -1; + + /* public XmlPullParserException() { + }*/ + + public XmlPullParserException(String s) { + super(s); + } + + /* + public XmlPullParserException(String s, Throwable thrwble) { + super(s); + this.detail = thrwble; + } + + public XmlPullParserException(String s, int row, int column) { + super(s); + this.row = row; + this.column = column; + } + */ + + public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) { + super ((msg == null ? "" : msg+" ") + + (parser == null ? "" : "(position:"+parser.getPositionDescription()+") ") + + (chain == null ? "" : "caused by: "+chain)); + + if (parser != null) { + this.row = parser.getLineNumber(); + this.column = parser.getColumnNumber(); + } + this.detail = chain; + } + + public Throwable getDetail() { return detail; } + // public void setDetail(Throwable cause) { this.detail = cause; } + public int getLineNumber() { return row; } + public int getColumnNumber() { return column; } + + /* + public String getMessage() { + if(detail == null) + return super.getMessage(); + else + return super.getMessage() + "; nested exception is: \n\t" + + detail.getMessage(); + } + */ + + //NOTE: code that prints this and detail is difficult in J2ME + public void printStackTrace() { + if (detail == null) { + super.printStackTrace(); + } else { + synchronized(System.err) { + System.err.println(super.getMessage() + "; nested exception is:"); + detail.printStackTrace(); + } + } + } + +} + diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java new file mode 100644 index 0000000..7b786f6 --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java @@ -0,0 +1,349 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * This class is used to create implementations of XML Pull Parser defined in XMPULL V1 API. + * The name of actual factory class will be determined based on several parameters. + * It works similar to JAXP but tailored to work in J2ME environments + * (no access to system properties or file system) so name of parser class factory to use + * and its class used for loading (no class loader - on J2ME no access to context class loaders) + * must be passed explicitly. If no name of parser factory was passed (or is null) + * it will try to find name by searching in CLASSPATH for + * META-INF/services/org.xmlpull.v1.XmlPullParserFactory resource that should contain + * a comma separated list of class names of factories or parsers to try (in order from + * left to the right). If none found, it will throw an exception. + * + * <br /><strong>NOTE:</strong>In J2SE or J2EE environments, you may want to use + * <code>newInstance(property, classLoaderCtx)</code> + * where first argument is + * <code>System.getProperty(XmlPullParserFactory.PROPERTY_NAME)</code> + * and second is <code>Thread.getContextClassLoader().getClass()</code> . + * + * @see XmlPullParser + * + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + * @author Stefan Haustein + */ + +public class XmlPullParserFactory { + /** used as default class to server as context class in newInstance() */ + final static Class referenceContextClass; + + static { + XmlPullParserFactory f = new XmlPullParserFactory(); + referenceContextClass = f.getClass(); + } + + /** Name of the system or midlet property that should be used for + a system property containing a comma separated list of factory + or parser class names (value: + org.xmlpull.v1.XmlPullParserFactory). */ + + + public static final String PROPERTY_NAME = + "org.xmlpull.v1.XmlPullParserFactory"; + + private static final String RESOURCE_NAME = + "/META-INF/services/" + PROPERTY_NAME; + + + // public static final String DEFAULT_PROPERTY = + // "org.xmlpull.xpp3.XmlPullParser,org.kxml2.io.KXmlParser"; + + + protected ArrayList parserClasses; + protected String classNamesLocation; + + protected ArrayList serializerClasses; + + + // features are kept there + protected HashMap features = new HashMap(); + + + /** + * Protected constructor to be called by factory implementations. + */ + + protected XmlPullParserFactory() { + } + + + + /** + * Set the features to be set when XML Pull Parser is created by this factory. + * <p><b>NOTE:</b> factory features are not used for XML Serializer. + * + * @param name string with URI identifying feature + * @param state if true feature will be set; if false will be ignored + */ + + public void setFeature(String name, + boolean state) throws XmlPullParserException { + + features.put(name, new Boolean(state)); + } + + + /** + * Return the current value of the feature with given name. + * <p><b>NOTE:</b> factory features are not used for XML Serializer. + * + * @param name The name of feature to be retrieved. + * @return The value of named feature. + * Unknown features are <string>always</strong> returned as false + */ + + public boolean getFeature (String name) { + Boolean value = (Boolean) features.get(name); + return value != null ? value.booleanValue() : false; + } + + /** + * Specifies that the parser produced by this factory will provide + * support for XML namespaces. + * By default the value of this is set to false. + * + * @param awareness true if the parser produced by this code + * will provide support for XML namespaces; false otherwise. + */ + + public void setNamespaceAware(boolean awareness) { + features.put (XmlPullParser.FEATURE_PROCESS_NAMESPACES, new Boolean (awareness)); + } + + /** + * Indicates whether or not the factory is configured to produce + * parsers which are namespace aware + * (it simply set feature XmlPullParser.FEATURE_PROCESS_NAMESPACES to true or false). + * + * @return true if the factory is configured to produce parsers + * which are namespace aware; false otherwise. + */ + + public boolean isNamespaceAware() { + return getFeature (XmlPullParser.FEATURE_PROCESS_NAMESPACES); + } + + + /** + * Specifies that the parser produced by this factory will be validating + * (it simply set feature XmlPullParser.FEATURE_VALIDATION to true or false). + * + * By default the value of this is set to false. + * + * @param validating - if true the parsers created by this factory must be validating. + */ + + public void setValidating(boolean validating) { + features.put (XmlPullParser.FEATURE_VALIDATION, new Boolean (validating)); + } + + /** + * Indicates whether or not the factory is configured to produce parsers + * which validate the XML content during parse. + * + * @return true if the factory is configured to produce parsers + * which validate the XML content during parse; false otherwise. + */ + + public boolean isValidating() { + return getFeature (XmlPullParser.FEATURE_VALIDATION); + } + + /** + * Creates a new instance of a XML Pull Parser + * using the currently configured factory features. + * + * @return A new instance of a XML Pull Parser. + * @throws XmlPullParserException if a parser cannot be created which satisfies the + * requested configuration. + */ + + public XmlPullParser newPullParser() throws XmlPullParserException { + + if (parserClasses == null) throw new XmlPullParserException + ("Factory initialization was incomplete - has not tried "+classNamesLocation); + + if (parserClasses.size() == 0) throw new XmlPullParserException + ("No valid parser classes found in "+classNamesLocation); + + final StringBuffer issues = new StringBuffer (); + + for (int i = 0; i < parserClasses.size(); i++) { + final Class ppClass = (Class) parserClasses.get(i); + try { + final XmlPullParser pp = (XmlPullParser) ppClass.newInstance(); + + for (Iterator iter = features.keySet().iterator(); iter.hasNext(); ) { + final String key = (String) iter.next(); + final Boolean value = (Boolean) features.get(key); + if(value != null && value.booleanValue()) { + pp.setFeature(key, true); + } + } + return pp; + + } catch(Exception ex) { + issues.append (ppClass.getName () + ": "+ ex.toString ()+"; "); + } + } + + throw new XmlPullParserException ("could not create parser: "+issues); + } + + + /** + * Creates a new instance of a XML Serializer. + * + * <p><b>NOTE:</b> factory features are not used for XML Serializer. + * + * @return A new instance of a XML Serializer. + * @throws XmlPullParserException if a parser cannot be created which satisfies the + * requested configuration. + */ + + public XmlSerializer newSerializer() throws XmlPullParserException { + + if (serializerClasses == null) { + throw new XmlPullParserException + ("Factory initialization incomplete - has not tried "+classNamesLocation); + } + if(serializerClasses.size() == 0) { + throw new XmlPullParserException + ("No valid serializer classes found in "+classNamesLocation); + } + + final StringBuffer issues = new StringBuffer (); + + for (int i = 0; i < serializerClasses.size (); i++) { + final Class ppClass = (Class) serializerClasses.get(i); + try { + final XmlSerializer ser = (XmlSerializer) ppClass.newInstance(); + + return ser; + + } catch(Exception ex) { + issues.append (ppClass.getName () + ": "+ ex.toString ()+"; "); + } + } + + throw new XmlPullParserException ("could not create serializer: "+issues); + } + + /** + * Create a new instance of a PullParserFactory that can be used + * to create XML pull parsers (see class description for more + * details). + * + * @return a new instance of a PullParserFactory, as returned by newInstance (null, null); + */ + public static XmlPullParserFactory newInstance () throws XmlPullParserException { + return newInstance(null, null); + } + + public static XmlPullParserFactory newInstance (String classNames, Class context) + throws XmlPullParserException { + + if (context == null) { + //NOTE: make sure context uses the same class loader as API classes + // this is the best we can do without having access to context classloader in J2ME + // if API is in the same classloader as implementation then this will work + context = referenceContextClass; + } + + /* + String classNamesLocation = null; + + if (classNames == null || classNames.length() == 0 || "DEFAULT".equals(classNames)) { + try { + InputStream is = context.getResourceAsStream (RESOURCE_NAME); + + if (is == null) throw new XmlPullParserException + ("resource not found: "+RESOURCE_NAME + +" make sure that parser implementing XmlPull API is available"); + final StringBuffer sb = new StringBuffer(); + + while (true) { + final int ch = is.read(); + if (ch < 0) break; + else if (ch > ' ') + sb.append((char) ch); + } + is.close (); + + classNames = sb.toString (); + } + catch (Exception e) { + throw new XmlPullParserException (null, null, e); + } + classNamesLocation = "resource "+RESOURCE_NAME+" that contained '"+classNames+"'"; + } else { + classNamesLocation = + "parameter classNames to newInstance() that contained '"+classNames+"'"; + } + */ + classNames = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer"; + + XmlPullParserFactory factory = null; + final ArrayList parserClasses = new ArrayList(); + final ArrayList serializerClasses = new ArrayList(); + int pos = 0; + + while (pos < classNames.length ()) { + int cut = classNames.indexOf (',', pos); + + if (cut == -1) cut = classNames.length (); + final String name = classNames.substring (pos, cut); + + Class candidate = null; + Object instance = null; + + try { + candidate = Class.forName (name); + // necessary because of J2ME .class issue + instance = candidate.newInstance (); + } + catch (Exception e) {} + + if (candidate != null) { + boolean recognized = false; + if (instance instanceof XmlPullParser) { + parserClasses.add(candidate); + recognized = true; + } + if (instance instanceof XmlSerializer) { + serializerClasses.add(candidate); + recognized = true; + } + if (instance instanceof XmlPullParserFactory) { + if (factory == null) { + factory = (XmlPullParserFactory) instance; + } + recognized = true; + } + if (!recognized) { + throw new XmlPullParserException ("incompatible class: "+name); + } + } + pos = cut + 1; + } + + if (factory == null) { + factory = new XmlPullParserFactory (); + } + factory.parserClasses = parserClasses; + factory.serializerClasses = serializerClasses; + factory.classNamesLocation = "org.kxml2.io.kXmlParser,org.kxml2.io.KXmlSerializer"; + return factory; + } +} + + diff --git a/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java b/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java new file mode 100644 index 0000000..8e85e2f --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java @@ -0,0 +1,326 @@ +package org.xmlpull.v1; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; + +/** + * Define an interface to serialziation of XML Infoset. + * This interface abstracts away if serialized XML is XML 1.0 comaptible text or + * other formats of XML 1.0 serializations (such as binary XML for example with WBXML). + * + * <p><b>PLEASE NOTE:</b> This interface will be part of XmlPull 1.2 API. + * It is included as basis for discussion. It may change in any way. + * + * <p>Exceptions that may be thrown are: IOException or runtime exception + * (more runtime exceptions can be thrown but are not declared and as such + * have no semantics defined for this interface): + * <ul> + * <li><em>IllegalArgumentException</em> - for almost all methods to signal that + * argument is illegal + * <li><em>IllegalStateException</em> - to signal that call has good arguments but + * is not expected here (violation of contract) and for features/properties + * when requesting setting unimplemented feature/property + * (UnsupportedOperationException would be better but it is not in MIDP) + * </ul> + * + * <p><b>NOTE:</b> writing CDSECT, ENTITY_REF, IGNORABLE_WHITESPACE, + * PROCESSING_INSTRUCTION, COMMENT, and DOCDECL in some implementations + * may not be supported (for example when serializing to WBXML). + * In such case IllegalStateException will be thrown and it is recommened + * to use an optional feature to signal that implementation is not + * supporting this kind of output. + */ + +public interface XmlSerializer { + + /** + * Set feature identified by name (recommended to be URI for uniqueness). + * Some well known optional features are defined in + * <a href="http://www.xmlpull.org/v1/doc/features.html"> + * http://www.xmlpull.org/v1/doc/features.html</a>. + * + * If feature is not recocgnized or can not be set + * then IllegalStateException MUST be thrown. + * + * @exception IllegalStateException If the feature is not supported or can not be set + */ + void setFeature(String name, + boolean state) + throws IllegalArgumentException, IllegalStateException; + + + /** + * Return the current value of the feature with given name. + * <p><strong>NOTE:</strong> unknown properties are <strong>always</strong> returned as null + * + * @param name The name of feature to be retrieved. + * @return The value of named feature. + * @exception IllegalArgumentException if feature string is null + */ + boolean getFeature(String name); + + + /** + * Set the value of a property. + * (the property name is recommened to be URI for uniqueness). + * Some well known optional properties are defined in + * <a href="http://www.xmlpull.org/v1/doc/properties.html"> + * http://www.xmlpull.org/v1/doc/properties.html</a>. + * + * If property is not recocgnized or can not be set + * then IllegalStateException MUST be thrown. + * + * @exception IllegalStateException if the property is not supported or can not be set + */ + void setProperty(String name, + Object value) + throws IllegalArgumentException, IllegalStateException; + + /** + * Look up the value of a property. + * + * The property name is any fully-qualified URI. I + * <p><strong>NOTE:</strong> unknown properties are <string>always</strong> returned as null + * + * @param name The name of property to be retrieved. + * @return The value of named property. + */ + Object getProperty(String name); + + /** + * Set to use binary output stream with given encoding. + */ + void setOutput (OutputStream os, String encoding) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Set the output to the given writer. + * <p><b>WARNING</b> no information about encoding is available! + */ + void setOutput (Writer writer) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write <?xml declaration with encoding (if encoding not null) + * and standalone flag (if standalone not null) + * This method can only be called just after setOutput. + */ + void startDocument (String encoding, Boolean standalone) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Finish writing. All unclosed start tags will be closed and output + * will be flushed. After calling this method no more output can be + * serialized until next call to setOutput() + */ + void endDocument () + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Binds the given prefix to the given namespace. + * This call is valid for the next element including child elements. + * The prefix and namespace MUST be always declared even if prefix + * is not used in element (startTag() or attribute()) - for XML 1.0 + * it must result in declaring <code>xmlns:prefix='namespace'</code> + * (or <code>xmlns:prefix="namespace"</code> depending what character is used + * to quote attribute value). + * + * <p><b>NOTE:</b> this method MUST be called directly before startTag() + * and if anything but startTag() or setPrefix() is called next there will be exception. + * <p><b>NOTE:</b> prefixes "xml" and "xmlns" are already bound + * and can not be redefined see: + * <a href="http://www.w3.org/XML/xml-names-19990114-errata#NE05">Namespaces in XML Errata</a>. + * <p><b>NOTE:</b> to set default namespace use as prefix empty string. + * + * @param prefix must be not null (or IllegalArgumentException is thrown) + * @param namespace must be not null + */ + void setPrefix (String prefix, String namespace) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Return namespace that corresponds to given prefix + * If there is no prefix bound to this namespace return null + * but if generatePrefix is false then return generated prefix. + * + * <p><b>NOTE:</b> if the prefix is empty string "" and defualt namespace is bound + * to this prefix then empty string ("") is returned. + * + * <p><b>NOTE:</b> prefixes "xml" and "xmlns" are already bound + * will have values as defined + * <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in XML specification</a> + */ + String getPrefix (String namespace, boolean generatePrefix) + throws IllegalArgumentException; + + /** + * Returns the current depth of the element. + * Outside the root element, the depth is 0. The + * depth is incremented by 1 when startTag() is called. + * The depth is decremented after the call to endTag() + * event was observed. + * + * <pre> + * <!-- outside --> 0 + * <root> 1 + * sometext 1 + * <foobar> 2 + * </foobar> 2 + * </root> 1 + * <!-- outside --> 0 + * </pre> + */ + int getDepth(); + + /** + * Returns the namespace URI of the current element as set by startTag(). + * + * <p><b>NOTE:</b> that measn in particaulr that: <ul> + * <li>if there was startTag("", ...) then getNamespace() returns "" + * <li>if there was startTag(null, ...) then getNamespace() returns null + * </ul> + * + * @return namespace set by startTag() that is currently in scope + */ + String getNamespace (); + + /** + * Returns the name of the current element as set by startTag(). + * It can only be null before first call to startTag() + * or when last endTag() is called to close first startTag(). + * + * @return namespace set by startTag() that is currently in scope + */ + String getName(); + + /** + * Writes a start tag with the given namespace and name. + * If there is no prefix defined for the given namespace, + * a prefix will be defined automatically. + * The explicit prefixes for namespaces can be established by calling setPrefix() + * immediately before this method. + * If namespace is null no namespace prefix is printed but just name. + * If namespace is empty string then serialzier will make sure that + * default empty namespace is declared (in XML 1.0 xmlns='') + * or throw IllegalStateException if default namespace is already bound + * to non-empty string. + */ + XmlSerializer startTag (String namespace, String name) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write an attribute. Calls to attribute() MUST follow a call to + * startTag() immediately. If there is no prefix defined for the + * given namespace, a prefix will be defined automatically. + * If namespace is null or empty string + * no namespace prefix is printed but just name. + */ + XmlSerializer attribute (String namespace, String name, String value) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write end tag. Repetition of namespace and name is just for avoiding errors. + * <p><b>Background:</b> in kXML endTag had no arguments, and non matching tags were + * very difficult to find... + * If namespace is null no namespace prefix is printed but just name. + * If namespace is empty string then serialzier will make sure that + * default empty namespace is declared (in XML 1.0 xmlns=''). + */ + XmlSerializer endTag (String namespace, String name) + throws IOException, IllegalArgumentException, IllegalStateException; + + + // /** + // * Writes a start tag with the given namespace and name. + // * <br />If there is no prefix defined (prefix == null) for the given namespace, + // * a prefix will be defined automatically. + // * <br />If explicit prefixes is passed (prefix != null) then it will be used + // *and namespace declared if not already declared or + // * throw IllegalStateException the same prefix was already set on this + // * element (setPrefix()) and was bound to different namespace. + // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown. + // * <br />If namespace is null then no namespace prefix is printed but just name. + // * <br />If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns='') + // * or throw IllegalStateException if default namespace is already bound + // * to non-empty string. + // */ + // XmlSerializer startTag (String prefix, String namespace, String name) + // throws IOException, IllegalArgumentException, IllegalStateException; + // + // /** + // * Write an attribute. Calls to attribute() MUST follow a call to + // * startTag() immediately. + // * <br />If there is no prefix defined (prefix == null) for the given namespace, + // * a prefix will be defined automatically. + // * <br />If explicit prefixes is passed (prefix != null) then it will be used + // * and namespace declared if not already declared or + // * throw IllegalStateException the same prefix was already set on this + // * element (setPrefix()) and was bound to different namespace. + // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown. + // * <br />If namespace is null then no namespace prefix is printed but just name. + // * <br />If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns='') + // * or throw IllegalStateException if default namespace is already bound + // * to non-empty string. + // */ + // XmlSerializer attribute (String prefix, String namespace, String name, String value) + // throws IOException, IllegalArgumentException, IllegalStateException; + // + // /** + // * Write end tag. Repetition of namespace, prefix, and name is just for avoiding errors. + // * <br />If namespace or name arguments are different from corresponding startTag call + // * then IllegalArgumentException is thrown, if prefix argument is not null and is different + // * from corresponding starTag then IllegalArgumentException is thrown. + // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown. + // * <br />If namespace is null then no namespace prefix is printed but just name. + // * <br />If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns=''). + // * <p><b>Background:</b> in kXML endTag had no arguments, and non matching tags were + // * very difficult to find...</p> + // */ + // ALEK: This is really optional as prefix in end tag MUST correspond to start tag but good for error checking + // XmlSerializer endTag (String prefix, String namespace, String name) + // throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Writes text, where special XML chars are escaped automatically + */ + XmlSerializer text (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Writes text, where special XML chars are escaped automatically + */ + XmlSerializer text (char [] buf, int start, int len) + throws IOException, IllegalArgumentException, IllegalStateException; + + void cdsect (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void entityRef (String text) throws IOException, + IllegalArgumentException, IllegalStateException; + void processingInstruction (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void comment (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void docdecl (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void ignorableWhitespace (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write all pending output to the stream. + * If method startTag() or attribute() was called then start tag is closed (final >) + * before flush() is called on underlying output stream. + * + * <p><b>NOTE:</b> if there is need to close start tag + * (so no more attribute() calls are allowed) but without flushinging output + * call method text() with empty string (text("")). + * + */ + void flush () + throws IOException; + +} + diff --git a/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java b/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java new file mode 100644 index 0000000..0bd2d4f --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java @@ -0,0 +1,469 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1.sax2; + +import java.io.InputStream; +import java.io.IOException; +import java.io.Reader; + +// not J2ME classes -- remove if you want to run in MIDP devices +import java.net.URL; +import java.net.MalformedURLException; + + +// not J2ME classes +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import org.xml.sax.Attributes; +import org.xml.sax.DTDHandler; +import org.xml.sax.ContentHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +/** + * SAX2 Driver that pulls events from XmlPullParser + * and comverts them into SAX2 callbacks. + * + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + */ + +public class Driver implements Locator, XMLReader, Attributes +{ + + protected static final String DECLARATION_HANDLER_PROPERTY = + "http://xml.org/sax/properties/declaration-handler"; + + protected static final String LEXICAL_HANDLER_PROPERTY = + "http://xml.org/sax/properties/lexical-handler"; + + protected static final String NAMESPACES_FEATURE = + "http://xml.org/sax/features/namespaces"; + + protected static final String NAMESPACE_PREFIXES_FEATURE = + "http://xml.org/sax/features/namespace-prefixes"; + + protected static final String VALIDATION_FEATURE = + "http://xml.org/sax/features/validation"; + + protected static final String APACHE_SCHEMA_VALIDATION_FEATURE = + "http://apache.org/xml/features/validation/schema"; + + protected static final String APACHE_DYNAMIC_VALIDATION_FEATURE = + "http://apache.org/xml/features/validation/dynamic"; + + protected ContentHandler contentHandler = new DefaultHandler(); + protected ErrorHandler errorHandler = new DefaultHandler();; + + protected String systemId; + + protected XmlPullParser pp; + + //private final static boolean DEBUG = false; + + /** + */ + public Driver() throws XmlPullParserException { + final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + pp = factory.newPullParser(); + } + + public Driver(XmlPullParser pp) throws XmlPullParserException { + this.pp = pp; + } + + // -- Attributes interface + + public int getLength() { return pp.getAttributeCount(); } + public String getURI(int index) { return pp.getAttributeNamespace(index); } + public String getLocalName(int index) { return pp.getAttributeName(index); } + public String getQName(int index) { + final String prefix = pp.getAttributePrefix(index); + if(prefix != null) { + return prefix+':'+pp.getAttributeName(index); + } else { + return pp.getAttributeName(index); + } + } + public String getType(int index) { return pp.getAttributeType(index); } + public String getValue(int index) { return pp.getAttributeValue(index); } + + public int getIndex(String uri, String localName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeNamespace(i).equals(uri) + && pp.getAttributeName(i).equals(localName)) + { + return i; + } + + } + return -1; + } + + public int getIndex(String qName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeName(i).equals(qName)) + { + return i; + } + + } + return -1; + } + + public String getType(String uri, String localName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeNamespace(i).equals(uri) + && pp.getAttributeName(i).equals(localName)) + { + return pp.getAttributeType(i); + } + + } + return null; + } + public String getType(String qName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeName(i).equals(qName)) + { + return pp.getAttributeType(i); + } + + } + return null; + } + public String getValue(String uri, String localName) { + return pp.getAttributeValue(uri, localName); + } + public String getValue(String qName) { + return pp.getAttributeValue(null, qName); + } + + // -- Locator interface + + public String getPublicId() { return null; } + public String getSystemId() { return systemId; } + public int getLineNumber() { return pp.getLineNumber(); } + public int getColumnNumber() { return pp.getColumnNumber(); } + + // --- XMLReader interface + + public boolean getFeature(String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if(NAMESPACES_FEATURE.equals(name)) { + return pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES); + } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) { + return pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES); + } else if(VALIDATION_FEATURE.equals(name)) { + return pp.getFeature(XmlPullParser.FEATURE_VALIDATION); + // } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) { + // return false; //TODO + // } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) { + // return false; //TODO + } else { + return pp.getFeature(name); + //throw new SAXNotRecognizedException("unrecognized feature "+name); + } + } + + public void setFeature (String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + try { + if(NAMESPACES_FEATURE.equals(name)) { + pp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, value); + } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) { + if(pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES) != value) { + pp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, value); + } + } else if(VALIDATION_FEATURE.equals(name)) { + pp.setFeature(XmlPullParser.FEATURE_VALIDATION, value); + // } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) { + // // can ignore as validation must be false ... + // // if(true == value) { + // // throw new SAXNotSupportedException("schema validation is not supported"); + // // } + // } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) { + // if(true == value) { + // throw new SAXNotSupportedException("dynamic validation is not supported"); + // } + } else { + pp.setFeature(name, value); + //throw new SAXNotRecognizedException("unrecognized feature "+name); + } + } catch(XmlPullParserException ex) { + // throw new SAXNotSupportedException("problem with setting feature "+name+": "+ex); + } + } + + public Object getProperty (String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if(DECLARATION_HANDLER_PROPERTY.equals(name)) { + return null; + } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) { + return null; + } else { + return pp.getProperty(name); + //throw new SAXNotRecognizedException("not recognized get property "+name); + } + } + + public void setProperty (String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + // + if(DECLARATION_HANDLER_PROPERTY.equals(name)) { + throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value); + } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) { + throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value); + } else { + try { + pp.setProperty(name, value); + } catch(XmlPullParserException ex) { + throw new SAXNotSupportedException("not supported set property "+name+": "+ ex); + } + //throw new SAXNotRecognizedException("not recognized set property "+name); + } + } + + public void setEntityResolver (EntityResolver resolver) {} + + public EntityResolver getEntityResolver () { return null; } + + public void setDTDHandler (DTDHandler handler) {} + + public DTDHandler getDTDHandler () { return null; } + + public void setContentHandler (ContentHandler handler) + { + this.contentHandler = handler; + } + + public ContentHandler getContentHandler() { return contentHandler; } + + public void setErrorHandler(ErrorHandler handler) { + this.errorHandler = handler; + } + + public ErrorHandler getErrorHandler() { return errorHandler; } + + public void parse(InputSource source) throws SAXException, IOException + { + + systemId = source.getSystemId(); + contentHandler.setDocumentLocator(this); + + final Reader reader = source.getCharacterStream(); + try { + if (reader == null) { + InputStream stream = source.getByteStream(); + final String encoding = source.getEncoding(); + + if (stream == null) { + systemId = source.getSystemId(); + if(systemId == null) { + SAXParseException saxException = new SAXParseException( + "null source systemId" , this); + errorHandler.fatalError(saxException); + return; + } + // NOTE: replace with Connection to run in J2ME environment + try { + final URL url = new URL(systemId); + stream = url.openStream(); + } catch (MalformedURLException nue) { + try { + stream = new FileInputStream(systemId); + } catch (FileNotFoundException fnfe) { + final SAXParseException saxException = new SAXParseException( + "could not open file with systemId "+systemId, this, fnfe); + errorHandler.fatalError(saxException); + return; + } + } + } + pp.setInput(stream, encoding); + } else { + pp.setInput(reader); + } + } catch (XmlPullParserException ex) { + final SAXParseException saxException = new SAXParseException( + "parsing initialization error: "+ex, this, ex); + //if(DEBUG) ex.printStackTrace(); + errorHandler.fatalError(saxException); + return; + } + + // start parsing - move to first start tag + try { + contentHandler.startDocument(); + // get first event + pp.next(); + // it should be start tag... + if(pp.getEventType() != XmlPullParser.START_TAG) { + final SAXParseException saxException = new SAXParseException( + "expected start tag not"+pp.getPositionDescription(), this); + //throw saxException; + errorHandler.fatalError(saxException); + return; + } + } catch (XmlPullParserException ex) { + final SAXParseException saxException = new SAXParseException( + "parsing initialization error: "+ex, this, ex); + //ex.printStackTrace(); + errorHandler.fatalError(saxException); + return; + } + + // now real parsing can start! + + parseSubTree(pp); + + // and finished ... + + contentHandler.endDocument(); + } + + public void parse(String systemId) throws SAXException, IOException { + parse(new InputSource(systemId)); + } + + + public void parseSubTree(XmlPullParser pp) throws SAXException, IOException { + this.pp = pp; + final boolean namespaceAware = pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES); + try { + if(pp.getEventType() != XmlPullParser.START_TAG) { + throw new SAXException( + "start tag must be read before skiping subtree"+pp.getPositionDescription()); + } + final int[] holderForStartAndLength = new int[2]; + final StringBuffer rawName = new StringBuffer(16); + String prefix = null; + String name = null; + int level = pp.getDepth() - 1; + int type = XmlPullParser.START_TAG; + + LOOP: + do { + switch(type) { + case XmlPullParser.START_TAG: + if(namespaceAware) { + final int depth = pp.getDepth() - 1; + final int countPrev = + (level > depth) ? pp.getNamespaceCount(depth) : 0; + //int countPrev = pp.getNamespaceCount(pp.getDepth() - 1); + final int count = pp.getNamespaceCount(depth + 1); + for (int i = countPrev; i < count; i++) + { + contentHandler.startPrefixMapping( + pp.getNamespacePrefix(i), + pp.getNamespaceUri(i) + ); + } + name = pp.getName(); + prefix = pp.getPrefix(); + if(prefix != null) { + rawName.setLength(0); + rawName.append(prefix); + rawName.append(':'); + rawName.append(name); + } + startElement(pp.getNamespace(), + name, + // TODO Fixed this. Was "not equals". + prefix == null ? name : rawName.toString()); + } else { + startElement(pp.getNamespace(), + pp.getName(), + pp.getName()); + } + //++level; + + break; + case XmlPullParser.TEXT: + final char[] chars = pp.getTextCharacters(holderForStartAndLength); + contentHandler.characters(chars, + holderForStartAndLength[0], //start + holderForStartAndLength[1] //len + ); + break; + case XmlPullParser.END_TAG: + //--level; + if(namespaceAware) { + name = pp.getName(); + prefix = pp.getPrefix(); + if(prefix != null) { + rawName.setLength(0); + rawName.append(prefix); + rawName.append(':'); + rawName.append(name); + } + contentHandler.endElement(pp.getNamespace(), + name, + prefix != null ? name : rawName.toString() + ); + // when entering show prefixes for all levels!!!! + final int depth = pp.getDepth(); + final int countPrev = + (level > depth) ? pp.getNamespaceCount(pp.getDepth()) : 0; + int count = pp.getNamespaceCount(pp.getDepth() - 1); + // undeclare them in reverse order + for (int i = count - 1; i >= countPrev; i--) + { + contentHandler.endPrefixMapping( + pp.getNamespacePrefix(i) + ); + } + } else { + contentHandler.endElement(pp.getNamespace(), + pp.getName(), + pp.getName() + ); + + } + break; + case XmlPullParser.END_DOCUMENT: + break LOOP; + } + type = pp.next(); + } while(pp.getDepth() > level); + } catch (XmlPullParserException ex) { + final SAXParseException saxException = new SAXParseException("parsing error: "+ex, this, ex); + ex.printStackTrace(); + errorHandler.fatalError(saxException); + } + } + + /** + * Calls {@link ContentHandler#startElement(String, String, String, Attributes) startElement} + * on the <code>ContentHandler</code> with <code>this</code> driver object as the + * {@link Attributes} implementation. In default implementation + * {@link Attributes} object is valid only during this method call and may not + * be stored. Sub-classes can overwrite this method to cache attributes. + */ + protected void startElement(String namespace, String localName, String qName) throws SAXException { + contentHandler.startElement(namespace, localName, qName, this); + } + +} |