2207 lines
70 KiB
Java
2207 lines
70 KiB
Java
/*
|
|
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package com.sun.xml.internal.stream.writers;
|
|
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.io.OutputStreamWriter;
|
|
import java.io.Writer;
|
|
import java.nio.charset.Charset;
|
|
import java.nio.charset.CharsetEncoder;
|
|
import java.util.AbstractMap;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Random;
|
|
import java.util.Vector;
|
|
import java.util.Set;
|
|
import java.util.Iterator;
|
|
|
|
import javax.xml.XMLConstants;
|
|
import javax.xml.namespace.NamespaceContext;
|
|
import javax.xml.stream.XMLOutputFactory;
|
|
import javax.xml.stream.XMLStreamConstants;
|
|
import javax.xml.stream.XMLStreamException;
|
|
import javax.xml.stream.XMLStreamWriter;
|
|
import javax.xml.transform.stream.StreamResult;
|
|
|
|
import com.sun.org.apache.xerces.internal.impl.Constants;
|
|
import com.sun.org.apache.xerces.internal.impl.PropertyManager;
|
|
import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
|
|
import com.sun.org.apache.xerces.internal.util.SymbolTable;
|
|
import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
|
|
import com.sun.org.apache.xerces.internal.xni.QName;
|
|
|
|
import com.sun.xml.internal.stream.util.ReadOnlyIterator;
|
|
|
|
/**
|
|
* This class implements a StAX XMLStreamWriter. It extends
|
|
* <code>AbstractMap</code> in order to support a getter for
|
|
* implementation-specific properties. For example, you can get
|
|
* the underlying <code>OutputStream</code> by casting an instance
|
|
* of this class to <code>Map</code> and calling
|
|
* <code>getProperty(OUTPUTSTREAM_PROPERTY)</code>.
|
|
*
|
|
* @author Neeraj Bajaj
|
|
* @author K.Venugopal
|
|
* @author Santiago.Pericas-Geertsen@sun.com
|
|
* @author Sunitha.Reddy@sun.com
|
|
*/
|
|
public final class XMLStreamWriterImpl extends AbstractMap implements XMLStreamWriter {
|
|
|
|
public static final String START_COMMENT = "<!--";
|
|
public static final String END_COMMENT = "-->";
|
|
public static final String DEFAULT_ENCODING = " encoding=\"utf-8\"";
|
|
public static final String DEFAULT_XMLDECL = "<?xml version=\"1.0\" ?>";
|
|
public static final String DEFAULT_XML_VERSION = "1.0";
|
|
public static final char CLOSE_START_TAG = '>';
|
|
public static final char OPEN_START_TAG = '<';
|
|
public static final String OPEN_END_TAG = "</";
|
|
public static final char CLOSE_END_TAG = '>';
|
|
public static final String START_CDATA = "<![CDATA[";
|
|
public static final String END_CDATA = "]]>";
|
|
public static final String CLOSE_EMPTY_ELEMENT = "/>";
|
|
public static final String SPACE = " ";
|
|
public static final String UTF_8 = "UTF-8";
|
|
|
|
public static final String OUTPUTSTREAM_PROPERTY = "sjsxp-outputstream";
|
|
|
|
/**
|
|
* This flag can be used to turn escaping off for content. It does
|
|
* not apply to attribute content.
|
|
*/
|
|
boolean fEscapeCharacters = true;
|
|
|
|
/**
|
|
* Flag for the value of repairNamespace property
|
|
*/
|
|
private boolean fIsRepairingNamespace = false;
|
|
|
|
/**
|
|
* Underlying Writer to which characters are written.
|
|
*/
|
|
private Writer fWriter;
|
|
|
|
/**
|
|
* Underlying OutputStream to which <code>fWriter</code>
|
|
* writes to. May be null if unknown.
|
|
*/
|
|
private OutputStream fOutputStream = null;
|
|
|
|
/**
|
|
* Collects attributes when the writer is in reparing mode.
|
|
*/
|
|
private ArrayList fAttributeCache;
|
|
|
|
/**
|
|
* Collects namespace declarations when the writer is in reparing mode.
|
|
*/
|
|
private ArrayList fNamespaceDecls;
|
|
|
|
/**
|
|
* Namespace context encapsulating user specified context
|
|
* and context built by the writer
|
|
*/
|
|
private NamespaceContextImpl fNamespaceContext = null;
|
|
|
|
private NamespaceSupport fInternalNamespaceContext = null;
|
|
|
|
private Random fPrefixGen = null;
|
|
|
|
/**
|
|
* Reference to PropertyManager
|
|
*/
|
|
private PropertyManager fPropertyManager = null;
|
|
|
|
/**
|
|
* Flag to track if start tag is opened
|
|
*/
|
|
private boolean fStartTagOpened = false;
|
|
|
|
/**
|
|
* Boolean flag to indicate, if instance can be reused
|
|
*/
|
|
private boolean fReuse;
|
|
|
|
private SymbolTable fSymbolTable = new SymbolTable();
|
|
|
|
private ElementStack fElementStack = new ElementStack(); //Change this .-Venu
|
|
|
|
final private String DEFAULT_PREFIX = fSymbolTable.addSymbol("");
|
|
|
|
private final ReadOnlyIterator fReadOnlyIterator = new ReadOnlyIterator();
|
|
|
|
/**
|
|
* In some cases, this charset encoder is used to determine if a char is
|
|
* encodable by underlying writer. For example, an 8-bit char from the
|
|
* extended ASCII set is not encodable by 7-bit ASCII encoder. Unencodable
|
|
* chars are escaped using XML numeric entities.
|
|
*/
|
|
private CharsetEncoder fEncoder = null;
|
|
|
|
/**
|
|
* This is used to hold the namespace for attributes which happen to have
|
|
* the same uri as the default namespace; It's added to avoid changing the
|
|
* current impl. which has many redundant code for the repair mode
|
|
*/
|
|
HashMap fAttrNamespace = null;
|
|
|
|
/**
|
|
* Creates a new instance of XMLStreamWriterImpl. Uses platform's default
|
|
* encoding.
|
|
*
|
|
* @param outputStream Underlying stream to write the bytes to
|
|
* @param props Properties used by this writer
|
|
*/
|
|
public XMLStreamWriterImpl(OutputStream outputStream, PropertyManager props)
|
|
throws IOException {
|
|
|
|
// cannot call this(outputStream, null, props); for constructor,
|
|
// OutputStreamWriter charsetName cannot be null
|
|
|
|
// use default encoding
|
|
this(new OutputStreamWriter(outputStream), props);
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance of XMLStreamWriterImpl.
|
|
*
|
|
* @param outputStream Underlying stream to write the bytes
|
|
* @param encoding Encoding used to convert chars into bytes
|
|
* @param props Properties used by this writer
|
|
*/
|
|
public XMLStreamWriterImpl(OutputStream outputStream, String encoding,
|
|
PropertyManager props) throws java.io.IOException {
|
|
this(new StreamResult(outputStream), encoding, props);
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance of XMLStreamWriterImpl using a Writer.
|
|
*
|
|
* @param writer Underlying writer to which chars are written
|
|
* @param props Properties used by this writer
|
|
*/
|
|
public XMLStreamWriterImpl(Writer writer, PropertyManager props)
|
|
throws java.io.IOException {
|
|
this(new StreamResult(writer), null, props);
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance of XMLStreamWriterImpl using a StreamResult.
|
|
* A StreamResult encasupates an OutputStream, a Writer or a SystemId.
|
|
*
|
|
* @param writer Underlying writer to which chars are written
|
|
* @param props Properties used by this writer
|
|
*/
|
|
public XMLStreamWriterImpl(StreamResult sr, String encoding,
|
|
PropertyManager props) throws java.io.IOException {
|
|
setOutput(sr, encoding);
|
|
fPropertyManager = props;
|
|
init();
|
|
}
|
|
|
|
/**
|
|
* Initialize an instance of this XMLStreamWriter. Allocate new instances
|
|
* for all the data structures. Set internal flags based on property values.
|
|
*/
|
|
private void init() {
|
|
fReuse = false;
|
|
fNamespaceDecls = new ArrayList();
|
|
fPrefixGen = new Random();
|
|
fAttributeCache = new ArrayList();
|
|
fInternalNamespaceContext = new NamespaceSupport();
|
|
fInternalNamespaceContext.reset();
|
|
fNamespaceContext = new NamespaceContextImpl();
|
|
fNamespaceContext.internalContext = fInternalNamespaceContext;
|
|
|
|
// Set internal state based on property values
|
|
Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
|
|
fIsRepairingNamespace = ob.booleanValue();
|
|
ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS);
|
|
setEscapeCharacters(ob.booleanValue());
|
|
}
|
|
|
|
/**
|
|
* Reset this instance so that it can be re-used. Do not read properties
|
|
* again. The method <code>setOutput(StreamResult, encoding)</code> must
|
|
* be called after this one.
|
|
*/
|
|
public void reset() {
|
|
reset(false);
|
|
}
|
|
|
|
/**
|
|
* Reset this instance so that it can be re-used. Clears but does not
|
|
* re-allocate internal data structures.
|
|
*
|
|
* @param resetProperties Indicates if properties should be read again
|
|
*/
|
|
void reset(boolean resetProperties) {
|
|
if (!fReuse) {
|
|
throw new java.lang.IllegalStateException(
|
|
"close() Must be called before calling reset()");
|
|
}
|
|
|
|
fReuse = false;
|
|
fNamespaceDecls.clear();
|
|
fAttributeCache.clear();
|
|
|
|
// reset Element/NamespaceContext stacks
|
|
fElementStack.clear();
|
|
fInternalNamespaceContext.reset();
|
|
|
|
fStartTagOpened = false;
|
|
fNamespaceContext.userContext = null;
|
|
|
|
if (resetProperties) {
|
|
Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
|
|
fIsRepairingNamespace = ob.booleanValue();
|
|
ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS);
|
|
setEscapeCharacters(ob.booleanValue());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Use a StreamResult to initialize the output for this XMLStreamWriter. Check
|
|
* for OutputStream, Writer and then systemId, in that order.
|
|
*
|
|
* @param sr StreamResult encapsulating output information
|
|
* @param encoding Encoding to be used except when a Writer is available
|
|
*/
|
|
public void setOutput(StreamResult sr, String encoding)
|
|
throws IOException {
|
|
|
|
if (sr.getOutputStream() != null) {
|
|
setOutputUsingStream(sr.getOutputStream(), encoding);
|
|
}
|
|
else if (sr.getWriter() != null) {
|
|
setOutputUsingWriter(sr.getWriter());
|
|
}
|
|
else if (sr.getSystemId() != null) {
|
|
setOutputUsingStream(new FileOutputStream(sr.getSystemId()),
|
|
encoding);
|
|
}
|
|
}
|
|
|
|
private void setOutputUsingWriter(Writer writer)
|
|
throws IOException
|
|
{
|
|
fWriter = writer;
|
|
|
|
if (writer instanceof OutputStreamWriter) {
|
|
String charset = ((OutputStreamWriter) writer).getEncoding();
|
|
if (charset != null && !charset.equalsIgnoreCase("utf-8")) {
|
|
fEncoder = Charset.forName(charset).newEncoder();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility method to create a writer when passed an OutputStream. Make
|
|
* sure to wrap an <code>OutputStreamWriter</code> using an
|
|
* <code>XMLWriter</code> for performance reasons.
|
|
*
|
|
* @param os Underlying OutputStream
|
|
* @param encoding Encoding used to convert chars into bytes
|
|
*/
|
|
private void setOutputUsingStream(OutputStream os, String encoding)
|
|
throws IOException {
|
|
fOutputStream = os;
|
|
|
|
if (encoding != null) {
|
|
if (encoding.equalsIgnoreCase("utf-8")) {
|
|
fWriter = new UTF8OutputStreamWriter(os);
|
|
}
|
|
else {
|
|
fWriter = new XMLWriter(new OutputStreamWriter(os, encoding));
|
|
fEncoder = Charset.forName(encoding).newEncoder();
|
|
}
|
|
} else {
|
|
encoding = SecuritySupport.getSystemProperty("file.encoding");
|
|
if (encoding != null && encoding.equalsIgnoreCase("utf-8")) {
|
|
fWriter = new UTF8OutputStreamWriter(os);
|
|
} else {
|
|
fWriter = new XMLWriter(new OutputStreamWriter(os));
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Can this instance be reused
|
|
*
|
|
* @return boolean boolean value to indicate if this instance can be reused or not
|
|
*/
|
|
public boolean canReuse() {
|
|
return fReuse;
|
|
}
|
|
|
|
public void setEscapeCharacters(boolean escape) {
|
|
fEscapeCharacters = escape;
|
|
}
|
|
|
|
public boolean getEscapeCharacters() {
|
|
return fEscapeCharacters;
|
|
}
|
|
|
|
/**
|
|
* Close this XMLStreamWriter by closing underlying writer.
|
|
*/
|
|
public void close() throws XMLStreamException {
|
|
if (fWriter != null) {
|
|
try {
|
|
//fWriter.close();
|
|
fWriter.flush();
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
fWriter = null;
|
|
fOutputStream = null;
|
|
fNamespaceDecls.clear();
|
|
fAttributeCache.clear();
|
|
fElementStack.clear();
|
|
fInternalNamespaceContext.reset();
|
|
fReuse = true;
|
|
fStartTagOpened = false;
|
|
fNamespaceContext.userContext = null;
|
|
}
|
|
|
|
/**
|
|
* Flush this XMLStreamWriter by flushin underlying writer.
|
|
*/
|
|
public void flush() throws XMLStreamException {
|
|
try {
|
|
fWriter.flush();
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return <code>NamespaceContext</code> being used by the writer.
|
|
*
|
|
* @return NamespaceContext
|
|
*/
|
|
public NamespaceContext getNamespaceContext() {
|
|
return fNamespaceContext;
|
|
}
|
|
|
|
/**
|
|
* Return a prefix associated with specified uri, or null if the
|
|
* uri is unknown.
|
|
*
|
|
* @param uri The namespace uri
|
|
* @throws XMLStreamException if uri specified is "" or null
|
|
*/
|
|
public String getPrefix(String uri) throws XMLStreamException {
|
|
return fNamespaceContext.getPrefix(uri);
|
|
}
|
|
|
|
/**
|
|
* Returns value associated with the specified property name.
|
|
*
|
|
* @param str Property name
|
|
* @throws IllegalArgumentException if the specified property is not supported
|
|
* @return value associated with the specified property.
|
|
*/
|
|
public Object getProperty(String str)
|
|
throws IllegalArgumentException {
|
|
if (str == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
|
|
if (!fPropertyManager.containsProperty(str)) {
|
|
throw new IllegalArgumentException("Property '" + str +
|
|
"' is not supported");
|
|
}
|
|
|
|
return fPropertyManager.getProperty(str);
|
|
}
|
|
|
|
/**
|
|
* Set the specified URI as default namespace in the current namespace context.
|
|
*
|
|
* @param uri Namespace URI
|
|
*/
|
|
public void setDefaultNamespace(String uri) throws XMLStreamException {
|
|
if (uri != null) {
|
|
uri = fSymbolTable.addSymbol(uri);
|
|
}
|
|
|
|
if (fIsRepairingNamespace) {
|
|
if (isDefaultNamespace(uri)) {
|
|
return;
|
|
}
|
|
|
|
QName qname = new QName();
|
|
qname.setValues(DEFAULT_PREFIX, "xmlns", null, uri);
|
|
fNamespaceDecls.add(qname);
|
|
} else {
|
|
fInternalNamespaceContext.declarePrefix(DEFAULT_PREFIX, uri);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the current <code>NamespaceContext</code> for prefix and uri bindings.
|
|
* This context becomes the root namespace context for writing and
|
|
* will replace the current root namespace context. Subsequent calls
|
|
* to setPrefix and setDefaultNamespace will bind namespaces using
|
|
* the context passed to the method as the root context for resolving
|
|
* namespaces. This method may only be called once at the start of the
|
|
* document. It does not cause the namespaces to be declared. If a
|
|
* namespace URI to prefix mapping is found in the namespace context
|
|
* it is treated as declared and the prefix may be used by the
|
|
* <code>XMLStreamWriter</code>.
|
|
*
|
|
* @param namespaceContext the namespace context to use for this writer, may not be null
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void setNamespaceContext(NamespaceContext namespaceContext)
|
|
throws XMLStreamException {
|
|
fNamespaceContext.userContext = namespaceContext;
|
|
}
|
|
|
|
/**
|
|
* Sets the prefix the uri is bound to. This prefix is bound in the scope of
|
|
* the current START_ELEMENT / END_ELEMENT pair. If this method is called before
|
|
* a START_ELEMENT has been written the prefix is bound in the root scope.
|
|
*
|
|
* @param prefix
|
|
* @param uri
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void setPrefix(String prefix, String uri) throws XMLStreamException {
|
|
|
|
if (prefix == null) {
|
|
throw new XMLStreamException("Prefix cannot be null");
|
|
}
|
|
|
|
if (uri == null) {
|
|
throw new XMLStreamException("URI cannot be null");
|
|
}
|
|
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
uri = fSymbolTable.addSymbol(uri);
|
|
|
|
if (fIsRepairingNamespace) {
|
|
String tmpURI = fInternalNamespaceContext.getURI(prefix);
|
|
|
|
if ((tmpURI != null) && (tmpURI == uri)) {
|
|
return;
|
|
}
|
|
|
|
if(checkUserNamespaceContext(prefix,uri))
|
|
return;
|
|
QName qname = new QName();
|
|
qname.setValues(prefix,XMLConstants.XMLNS_ATTRIBUTE, null,uri);
|
|
fNamespaceDecls.add(qname);
|
|
|
|
return;
|
|
}
|
|
|
|
fInternalNamespaceContext.declarePrefix(prefix, uri);
|
|
}
|
|
|
|
public void writeAttribute(String localName, String value)
|
|
throws XMLStreamException {
|
|
try {
|
|
if (!fStartTagOpened) {
|
|
throw new XMLStreamException(
|
|
"Attribute not associated with any element");
|
|
}
|
|
|
|
if (fIsRepairingNamespace) {
|
|
Attribute attr = new Attribute(value); // Revisit:Dont create new one's. Reuse.-Venu
|
|
attr.setValues(null, localName, null, null);
|
|
fAttributeCache.add(attr);
|
|
|
|
return;
|
|
}
|
|
|
|
fWriter.write(" ");
|
|
fWriter.write(localName);
|
|
fWriter.write("=\"");
|
|
writeXMLContent(
|
|
value,
|
|
true, // true = escapeChars
|
|
true); // true = escapeDoubleQuotes
|
|
fWriter.write("\"");
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeAttribute(String namespaceURI, String localName,
|
|
String value) throws XMLStreamException {
|
|
try {
|
|
if (!fStartTagOpened) {
|
|
throw new XMLStreamException(
|
|
"Attribute not associated with any element");
|
|
}
|
|
|
|
if (namespaceURI == null) {
|
|
throw new XMLStreamException("NamespaceURI cannot be null");
|
|
}
|
|
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
String prefix = fInternalNamespaceContext.getPrefix(namespaceURI);
|
|
|
|
if (!fIsRepairingNamespace) {
|
|
if (prefix == null) {
|
|
throw new XMLStreamException("Prefix cannot be null");
|
|
}
|
|
|
|
writeAttributeWithPrefix(prefix, localName, value);
|
|
} else {
|
|
Attribute attr = new Attribute(value);
|
|
attr.setValues(null, localName, null, namespaceURI);
|
|
fAttributeCache.add(attr);
|
|
}
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
private void writeAttributeWithPrefix(String prefix, String localName,
|
|
String value) throws IOException {
|
|
fWriter.write(SPACE);
|
|
|
|
if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
|
|
fWriter.write(prefix);
|
|
fWriter.write(":");
|
|
}
|
|
|
|
fWriter.write(localName);
|
|
fWriter.write("=\"");
|
|
writeXMLContent(value,
|
|
true, // true = escapeChars
|
|
true); // true = escapeDoubleQuotes
|
|
fWriter.write("\"");
|
|
}
|
|
|
|
public void writeAttribute(String prefix, String namespaceURI,
|
|
String localName, String value) throws XMLStreamException {
|
|
try {
|
|
if (!fStartTagOpened) {
|
|
throw new XMLStreamException(
|
|
"Attribute not associated with any element");
|
|
}
|
|
|
|
if (namespaceURI == null) {
|
|
throw new XMLStreamException("NamespaceURI cannot be null");
|
|
}
|
|
|
|
if (localName == null) {
|
|
throw new XMLStreamException("Local name cannot be null");
|
|
}
|
|
|
|
if (!fIsRepairingNamespace) {
|
|
if (prefix == null || prefix.equals("")){
|
|
if (!namespaceURI.equals("")) {
|
|
throw new XMLStreamException("prefix cannot be null or empty");
|
|
} else {
|
|
writeAttributeWithPrefix(null, localName, value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!prefix.equals(XMLConstants.XML_NS_PREFIX) || !namespaceURI.equals(XMLConstants.XML_NS_URI)) {
|
|
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
if (fInternalNamespaceContext.containsPrefixInCurrentContext(prefix)){
|
|
|
|
String tmpURI = fInternalNamespaceContext.getURI(prefix);
|
|
|
|
if (tmpURI != null && tmpURI != namespaceURI){
|
|
throw new XMLStreamException("Prefix "+prefix+" is " +
|
|
"already bound to "+tmpURI+
|
|
". Trying to rebind it to "+namespaceURI+" is an error.");
|
|
}
|
|
}
|
|
fInternalNamespaceContext.declarePrefix(prefix, namespaceURI);
|
|
}
|
|
writeAttributeWithPrefix(prefix, localName, value);
|
|
} else {
|
|
if (prefix != null) {
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
}
|
|
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
Attribute attr = new Attribute(value);
|
|
attr.setValues(prefix, localName, null, namespaceURI);
|
|
fAttributeCache.add(attr);
|
|
}
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeCData(String cdata) throws XMLStreamException {
|
|
try {
|
|
if (cdata == null) {
|
|
throw new XMLStreamException("cdata cannot be null");
|
|
}
|
|
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
fWriter.write(START_CDATA);
|
|
fWriter.write(cdata);
|
|
fWriter.write(END_CDATA);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeCharacters(String data) throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
writeXMLContent(data);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeCharacters(char[] data, int start, int len)
|
|
throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
writeXMLContent(data, start, len, fEscapeCharacters);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeComment(String comment) throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
fWriter.write(START_COMMENT);
|
|
|
|
if (comment != null) {
|
|
fWriter.write(comment);
|
|
}
|
|
|
|
fWriter.write(END_COMMENT);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeDTD(String dtd) throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
fWriter.write(dtd);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write default Namespace.
|
|
*
|
|
* If namespaceURI == null,
|
|
* then it is assumed to be equivilent to {@link XMLConstants.NULL_NS_URI},
|
|
* i.e. there is no Namespace.
|
|
*
|
|
* @param namespaceURI NamespaceURI to declare.
|
|
*
|
|
* @throws XMLStreamException
|
|
*
|
|
* @see <a href="http://www.w3.org/TR/REC-xml-names/#defaulting">
|
|
* Namespaces in XML, 5.2 Namespace Defaulting</a>
|
|
*/
|
|
public void writeDefaultNamespace(String namespaceURI)
|
|
throws XMLStreamException {
|
|
|
|
// normalize namespaceURI
|
|
String namespaceURINormalized = null;
|
|
if (namespaceURI == null) {
|
|
namespaceURINormalized = ""; // XMLConstants.NULL_NS_URI
|
|
} else {
|
|
namespaceURINormalized = namespaceURI;
|
|
}
|
|
|
|
try {
|
|
if (!fStartTagOpened) {
|
|
throw new IllegalStateException(
|
|
"Namespace Attribute not associated with any element");
|
|
}
|
|
|
|
if (fIsRepairingNamespace) {
|
|
QName qname = new QName();
|
|
qname.setValues(XMLConstants.DEFAULT_NS_PREFIX,
|
|
XMLConstants.XMLNS_ATTRIBUTE, null, namespaceURINormalized);
|
|
fNamespaceDecls.add(qname);
|
|
|
|
return;
|
|
}
|
|
|
|
namespaceURINormalized = fSymbolTable.addSymbol(namespaceURINormalized);
|
|
|
|
if (fInternalNamespaceContext.containsPrefixInCurrentContext("")){
|
|
|
|
String tmp = fInternalNamespaceContext.getURI("");
|
|
|
|
if (tmp != null && tmp != namespaceURINormalized) {
|
|
throw new XMLStreamException(
|
|
"xmlns has been already bound to " +tmp +
|
|
". Rebinding it to "+ namespaceURINormalized +
|
|
" is an error");
|
|
}
|
|
}
|
|
fInternalNamespaceContext.declarePrefix("", namespaceURINormalized);
|
|
|
|
// use common namespace code with a prefix == null for xmlns="..."
|
|
writenamespace(null, namespaceURINormalized);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeEmptyElement(String localName) throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
openStartTag();
|
|
fElementStack.push(null, localName, null, null, true);
|
|
fInternalNamespaceContext.pushContext();
|
|
|
|
if (!fIsRepairingNamespace) {
|
|
fWriter.write(localName);
|
|
}
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeEmptyElement(String namespaceURI, String localName)
|
|
throws XMLStreamException {
|
|
if (namespaceURI == null) {
|
|
throw new XMLStreamException("NamespaceURI cannot be null");
|
|
}
|
|
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
String prefix = fNamespaceContext.getPrefix(namespaceURI);
|
|
writeEmptyElement(prefix, localName, namespaceURI);
|
|
}
|
|
|
|
public void writeEmptyElement(String prefix, String localName,
|
|
String namespaceURI) throws XMLStreamException {
|
|
try {
|
|
if (localName == null) {
|
|
throw new XMLStreamException("Local Name cannot be null");
|
|
}
|
|
|
|
if (namespaceURI == null) {
|
|
throw new XMLStreamException("NamespaceURI cannot be null");
|
|
}
|
|
|
|
if (prefix != null) {
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
}
|
|
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
openStartTag();
|
|
|
|
fElementStack.push(prefix, localName, null, namespaceURI, true);
|
|
fInternalNamespaceContext.pushContext();
|
|
|
|
if (!fIsRepairingNamespace) {
|
|
if (prefix == null) {
|
|
throw new XMLStreamException("NamespaceURI " +
|
|
namespaceURI + " has not been bound to any prefix");
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
|
|
fWriter.write(prefix);
|
|
fWriter.write(":");
|
|
}
|
|
|
|
fWriter.write(localName);
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
public void writeEndDocument() throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
ElementState elem = null;
|
|
|
|
while (!fElementStack.empty()) {
|
|
elem = (ElementState) fElementStack.pop();
|
|
fInternalNamespaceContext.popContext();
|
|
|
|
if (elem.isEmpty) {
|
|
//fWriter.write(CLOSE_EMPTY_ELEMENT);
|
|
} else {
|
|
fWriter.write(OPEN_END_TAG);
|
|
|
|
if ((elem.prefix != null) && !(elem.prefix).equals("")) {
|
|
fWriter.write(elem.prefix);
|
|
fWriter.write(":");
|
|
}
|
|
|
|
fWriter.write(elem.localpart);
|
|
fWriter.write(CLOSE_END_TAG);
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
throw new XMLStreamException("No more elements to write");
|
|
}
|
|
}
|
|
|
|
public void writeEndElement() throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
ElementState currentElement = (ElementState) fElementStack.pop();
|
|
|
|
if (currentElement == null) {
|
|
throw new XMLStreamException("No element was found to write");
|
|
}
|
|
|
|
if (currentElement.isEmpty) {
|
|
//fWriter.write(CLOSE_EMPTY_ELEMENT);
|
|
return;
|
|
}
|
|
|
|
fWriter.write(OPEN_END_TAG);
|
|
|
|
if ((currentElement.prefix != null) &&
|
|
!(currentElement.prefix).equals("")) {
|
|
fWriter.write(currentElement.prefix);
|
|
fWriter.write(":");
|
|
}
|
|
|
|
fWriter.write(currentElement.localpart);
|
|
fWriter.write(CLOSE_END_TAG);
|
|
fInternalNamespaceContext.popContext();
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
throw new XMLStreamException(
|
|
"No element was found to write: "
|
|
+ e.toString(), e);
|
|
}
|
|
}
|
|
|
|
public void writeEntityRef(String refName) throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
fWriter.write('&');
|
|
fWriter.write(refName);
|
|
fWriter.write(';');
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write a Namespace declaration.
|
|
*
|
|
* If namespaceURI == null,
|
|
* then it is assumed to be equivilent to {@link XMLConstants.NULL_NS_URI},
|
|
* i.e. there is no Namespace.
|
|
*
|
|
* @param prefix Prefix to bind.
|
|
* @param namespaceURI NamespaceURI to declare.
|
|
*
|
|
* @throws XMLStreamException
|
|
*
|
|
* @see <a href="http://www.w3.org/TR/REC-xml-names/#defaulting">
|
|
* Namespaces in XML, 5.2 Namespace Defaulting</a>
|
|
*/
|
|
public void writeNamespace(String prefix, String namespaceURI)
|
|
throws XMLStreamException {
|
|
|
|
// normalize namespaceURI
|
|
String namespaceURINormalized = null;
|
|
if (namespaceURI == null) {
|
|
namespaceURINormalized = ""; // XMLConstants.NULL_NS_URI
|
|
} else {
|
|
namespaceURINormalized = namespaceURI;
|
|
}
|
|
|
|
try {
|
|
QName qname = null;
|
|
|
|
if (!fStartTagOpened) {
|
|
throw new IllegalStateException(
|
|
"Invalid state: start tag is not opened at writeNamespace("
|
|
+ prefix
|
|
+ ", "
|
|
+ namespaceURINormalized
|
|
+ ")");
|
|
}
|
|
|
|
// is this the default Namespace?
|
|
if (prefix == null
|
|
|| prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)
|
|
|| prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
|
|
writeDefaultNamespace(namespaceURINormalized);
|
|
return;
|
|
}
|
|
|
|
if (prefix.equals(XMLConstants.XML_NS_PREFIX) && namespaceURINormalized.equals(XMLConstants.XML_NS_URI))
|
|
return;
|
|
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
namespaceURINormalized = fSymbolTable.addSymbol(namespaceURINormalized);
|
|
|
|
if (fIsRepairingNamespace) {
|
|
String tmpURI = fInternalNamespaceContext.getURI(prefix);
|
|
|
|
if ((tmpURI != null) && (tmpURI == namespaceURINormalized)) {
|
|
return;
|
|
}
|
|
|
|
qname = new QName();
|
|
qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null,
|
|
namespaceURINormalized);
|
|
fNamespaceDecls.add(qname);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (fInternalNamespaceContext.containsPrefixInCurrentContext(prefix)){
|
|
|
|
String tmp = fInternalNamespaceContext.getURI(prefix);
|
|
|
|
if (tmp != null && tmp != namespaceURINormalized) {
|
|
|
|
throw new XMLStreamException("prefix "+prefix+
|
|
" has been already bound to " +tmp +
|
|
". Rebinding it to "+ namespaceURINormalized+
|
|
" is an error");
|
|
}
|
|
}
|
|
|
|
fInternalNamespaceContext.declarePrefix(prefix, namespaceURINormalized);
|
|
writenamespace(prefix, namespaceURINormalized);
|
|
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
private void writenamespace(String prefix, String namespaceURI)
|
|
throws IOException {
|
|
fWriter.write(" xmlns");
|
|
|
|
if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
|
|
fWriter.write(":");
|
|
fWriter.write(prefix);
|
|
}
|
|
|
|
fWriter.write("=\"");
|
|
writeXMLContent(
|
|
namespaceURI,
|
|
true, // true = escapeChars
|
|
true); // true = escapeDoubleQuotes
|
|
fWriter.write("\"");
|
|
}
|
|
|
|
public void writeProcessingInstruction(String target)
|
|
throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
if (target != null) {
|
|
fWriter.write("<?");
|
|
fWriter.write(target);
|
|
fWriter.write("?>");
|
|
|
|
return;
|
|
}
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
|
|
throw new XMLStreamException("PI target cannot be null");
|
|
}
|
|
|
|
/**
|
|
* @param target
|
|
* @param data
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeProcessingInstruction(String target, String data)
|
|
throws XMLStreamException {
|
|
try {
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
if ((target == null) || (data == null)) {
|
|
throw new XMLStreamException("PI target cannot be null");
|
|
}
|
|
|
|
fWriter.write("<?");
|
|
fWriter.write(target);
|
|
fWriter.write(SPACE);
|
|
fWriter.write(data);
|
|
fWriter.write("?>");
|
|
} catch (IOException e) {
|
|
throw new XMLStreamException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeStartDocument() throws XMLStreamException {
|
|
try {
|
|
fWriter.write(DEFAULT_XMLDECL);
|
|
} catch (IOException ex) {
|
|
throw new XMLStreamException(ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param version
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeStartDocument(String version) throws XMLStreamException {
|
|
try {
|
|
if ((version == null) || version.equals("")) {
|
|
writeStartDocument();
|
|
|
|
return;
|
|
}
|
|
|
|
fWriter.write("<?xml version=\"");
|
|
fWriter.write(version);
|
|
fWriter.write("\"");
|
|
|
|
//fWriter.write(DEFAULT_ENCODING);
|
|
fWriter.write("?>");
|
|
} catch (IOException ex) {
|
|
throw new XMLStreamException(ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param encoding
|
|
* @param version
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeStartDocument(String encoding, String version)
|
|
throws XMLStreamException {
|
|
//Revisit : What about standalone ?
|
|
try {
|
|
if ((encoding == null) && (version == null)) {
|
|
writeStartDocument();
|
|
|
|
return;
|
|
}
|
|
|
|
if (encoding == null) {
|
|
writeStartDocument(version);
|
|
|
|
return;
|
|
}
|
|
|
|
String streamEncoding = null;
|
|
if (fWriter instanceof OutputStreamWriter) {
|
|
streamEncoding = ((OutputStreamWriter) fWriter).getEncoding();
|
|
}
|
|
else if (fWriter instanceof UTF8OutputStreamWriter) {
|
|
streamEncoding = ((UTF8OutputStreamWriter) fWriter).getEncoding();
|
|
}
|
|
else if (fWriter instanceof XMLWriter) {
|
|
streamEncoding = ((OutputStreamWriter) ((XMLWriter)fWriter).getWriter()).getEncoding();
|
|
}
|
|
|
|
if (streamEncoding != null && !streamEncoding.equalsIgnoreCase(encoding)) {
|
|
// If the equality check failed, check for charset encoding aliases
|
|
boolean foundAlias = false;
|
|
Set aliases = Charset.forName(encoding).aliases();
|
|
for (Iterator it = aliases.iterator(); !foundAlias && it.hasNext(); ) {
|
|
if (streamEncoding.equalsIgnoreCase((String) it.next())) {
|
|
foundAlias = true;
|
|
}
|
|
}
|
|
// If no alias matches the encoding name, then report error
|
|
if (!foundAlias) {
|
|
throw new XMLStreamException("Underlying stream encoding '"
|
|
+ streamEncoding
|
|
+ "' and input paramter for writeStartDocument() method '"
|
|
+ encoding + "' do not match.");
|
|
}
|
|
}
|
|
|
|
|
|
fWriter.write("<?xml version=\"");
|
|
|
|
if ((version == null) || version.equals("")) {
|
|
fWriter.write(DEFAULT_XML_VERSION);
|
|
} else {
|
|
fWriter.write(version);
|
|
}
|
|
|
|
if (!encoding.equals("")) {
|
|
fWriter.write("\" encoding=\"");
|
|
fWriter.write(encoding);
|
|
}
|
|
|
|
fWriter.write("\"?>");
|
|
} catch (IOException ex) {
|
|
throw new XMLStreamException(ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param localName
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeStartElement(String localName) throws XMLStreamException {
|
|
try {
|
|
if (localName == null) {
|
|
throw new XMLStreamException("Local Name cannot be null");
|
|
}
|
|
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
openStartTag();
|
|
fElementStack.push(null, localName, null, null, false);
|
|
fInternalNamespaceContext.pushContext();
|
|
|
|
if (fIsRepairingNamespace) {
|
|
return;
|
|
}
|
|
|
|
fWriter.write(localName);
|
|
} catch (IOException ex) {
|
|
throw new XMLStreamException(ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param namespaceURI
|
|
* @param localName
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeStartElement(String namespaceURI, String localName)
|
|
throws XMLStreamException {
|
|
if (localName == null) {
|
|
throw new XMLStreamException("Local Name cannot be null");
|
|
}
|
|
|
|
if (namespaceURI == null) {
|
|
throw new XMLStreamException("NamespaceURI cannot be null");
|
|
}
|
|
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
String prefix = null;
|
|
|
|
if (!fIsRepairingNamespace) {
|
|
prefix = fNamespaceContext.getPrefix(namespaceURI);
|
|
|
|
if (prefix != null) {
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
}
|
|
}
|
|
|
|
writeStartElement(prefix, localName, namespaceURI);
|
|
}
|
|
|
|
/**
|
|
* @param prefix
|
|
* @param localName
|
|
* @param namespaceURI
|
|
* @throws XMLStreamException
|
|
*/
|
|
public void writeStartElement(String prefix, String localName,
|
|
String namespaceURI) throws XMLStreamException {
|
|
try {
|
|
if (localName == null) {
|
|
throw new XMLStreamException("Local Name cannot be null");
|
|
}
|
|
|
|
if (namespaceURI == null) {
|
|
throw new XMLStreamException("NamespaceURI cannot be null");
|
|
}
|
|
|
|
if (!fIsRepairingNamespace) {
|
|
if (prefix == null) {
|
|
throw new XMLStreamException("Prefix cannot be null");
|
|
}
|
|
}
|
|
|
|
if (fStartTagOpened) {
|
|
closeStartTag();
|
|
}
|
|
|
|
openStartTag();
|
|
namespaceURI = fSymbolTable.addSymbol(namespaceURI);
|
|
|
|
if (prefix != null) {
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
}
|
|
|
|
fElementStack.push(prefix, localName, null, namespaceURI, false);
|
|
fInternalNamespaceContext.pushContext();
|
|
|
|
String tmpPrefix = fNamespaceContext.getPrefix(namespaceURI);
|
|
|
|
|
|
if ((prefix != null) &&
|
|
((tmpPrefix == null) || !prefix.equals(tmpPrefix))) {
|
|
fInternalNamespaceContext.declarePrefix(prefix, namespaceURI);
|
|
|
|
}
|
|
|
|
if (fIsRepairingNamespace) {
|
|
if ((prefix == null) ||
|
|
((tmpPrefix != null) && prefix.equals(tmpPrefix))) {
|
|
return;
|
|
}
|
|
|
|
QName qname = new QName();
|
|
qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null,
|
|
namespaceURI);
|
|
fNamespaceDecls.add(qname);
|
|
|
|
return;
|
|
}
|
|
|
|
if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
|
|
fWriter.write(prefix);
|
|
fWriter.write(":");
|
|
}
|
|
|
|
fWriter.write(localName);
|
|
|
|
} catch (IOException ex) {
|
|
throw new XMLStreamException(ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes character reference in hex format.
|
|
*/
|
|
private void writeCharRef(int codePoint) throws IOException {
|
|
fWriter.write( "&#x" );
|
|
fWriter.write( Integer.toHexString(codePoint) );
|
|
fWriter.write( ';' );
|
|
}
|
|
|
|
/**
|
|
* Writes XML content to underlying writer. Escapes characters unless
|
|
* escaping character feature is turned off.
|
|
*/
|
|
private void writeXMLContent(char[] content, int start, int length,
|
|
boolean escapeChars) throws IOException {
|
|
if (!escapeChars) {
|
|
fWriter.write(content, start, length);
|
|
|
|
return;
|
|
}
|
|
|
|
// Index of the next char to be written
|
|
int startWritePos = start;
|
|
|
|
final int end = start + length;
|
|
|
|
for (int index = start; index < end; index++) {
|
|
char ch = content[index];
|
|
|
|
if (fEncoder != null && !fEncoder.canEncode(ch)){
|
|
fWriter.write(content, startWritePos, index - startWritePos );
|
|
|
|
// Check if current and next characters forms a surrogate pair
|
|
// and escape it to avoid generation of invalid xml content
|
|
if ( index != end - 1 && Character.isSurrogatePair(ch, content[index+1])) {
|
|
writeCharRef(Character.toCodePoint(ch, content[index+1]));
|
|
index++;
|
|
} else {
|
|
writeCharRef(ch);
|
|
}
|
|
startWritePos = index + 1;
|
|
continue;
|
|
}
|
|
|
|
switch (ch) {
|
|
case '<':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
fWriter.write("<");
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
|
|
case '&':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
fWriter.write("&");
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
|
|
case '>':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
fWriter.write(">");
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Write any pending data
|
|
fWriter.write(content, startWritePos, end - startWritePos);
|
|
}
|
|
|
|
private void writeXMLContent(String content) throws IOException {
|
|
if ((content != null) && (content.length() > 0)) {
|
|
writeXMLContent(content,
|
|
fEscapeCharacters, // boolean = escapeChars
|
|
false); // false = escapeDoubleQuotes
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes XML content to underlying writer. Escapes characters unless
|
|
* escaping character feature is turned off.
|
|
*/
|
|
private void writeXMLContent(
|
|
String content,
|
|
boolean escapeChars,
|
|
boolean escapeDoubleQuotes)
|
|
throws IOException {
|
|
|
|
if (!escapeChars) {
|
|
fWriter.write(content);
|
|
|
|
return;
|
|
}
|
|
|
|
// Index of the next char to be written
|
|
int startWritePos = 0;
|
|
|
|
final int end = content.length();
|
|
|
|
for (int index = 0; index < end; index++) {
|
|
char ch = content.charAt(index);
|
|
|
|
if (fEncoder != null && !fEncoder.canEncode(ch)){
|
|
fWriter.write(content, startWritePos, index - startWritePos );
|
|
|
|
// Check if current and next characters forms a surrogate pair
|
|
// and escape it to avoid generation of invalid xml content
|
|
if ( index != end - 1 && Character.isSurrogatePair(ch, content.charAt(index+1))) {
|
|
writeCharRef(Character.toCodePoint(ch, content.charAt(index+1)));
|
|
index++;
|
|
} else {
|
|
writeCharRef(ch);
|
|
}
|
|
|
|
startWritePos = index + 1;
|
|
continue;
|
|
}
|
|
|
|
switch (ch) {
|
|
case '<':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
fWriter.write("<");
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
|
|
case '&':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
fWriter.write("&");
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
|
|
case '>':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
fWriter.write(">");
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
fWriter.write(content, startWritePos, index - startWritePos);
|
|
if (escapeDoubleQuotes) {
|
|
fWriter.write(""");
|
|
} else {
|
|
fWriter.write('"');
|
|
}
|
|
startWritePos = index + 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Write any pending data
|
|
fWriter.write(content, startWritePos, end - startWritePos);
|
|
}
|
|
|
|
/**
|
|
* marks close of start tag and writes the same into the writer.
|
|
*/
|
|
private void closeStartTag() throws XMLStreamException {
|
|
try {
|
|
ElementState currentElement = fElementStack.peek();
|
|
|
|
if (fIsRepairingNamespace) {
|
|
repair();
|
|
correctPrefix(currentElement, XMLStreamConstants.START_ELEMENT);
|
|
|
|
if ((currentElement.prefix != null) &&
|
|
(currentElement.prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
|
|
fWriter.write(currentElement.prefix);
|
|
fWriter.write(":");
|
|
}
|
|
|
|
fWriter.write(currentElement.localpart);
|
|
|
|
int len = fNamespaceDecls.size();
|
|
QName qname = null;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
qname = (QName) fNamespaceDecls.get(i);
|
|
|
|
if (qname != null) {
|
|
if (fInternalNamespaceContext.declarePrefix(qname.prefix,
|
|
qname.uri)) {
|
|
writenamespace(qname.prefix, qname.uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
fNamespaceDecls.clear();
|
|
|
|
Attribute attr = null;
|
|
|
|
for (int j = 0; j < fAttributeCache.size(); j++) {
|
|
attr = (Attribute) fAttributeCache.get(j);
|
|
|
|
if ((attr.prefix != null) && (attr.uri != null)) {
|
|
if (!attr.prefix.equals("") && !attr.uri.equals("") ) {
|
|
String tmp = fInternalNamespaceContext.getPrefix(attr.uri);
|
|
|
|
if ((tmp == null) || (tmp != attr.prefix)) {
|
|
tmp = getAttrPrefix(attr.uri);
|
|
if (tmp == null) {
|
|
if (fInternalNamespaceContext.declarePrefix(attr.prefix,
|
|
attr.uri)) {
|
|
writenamespace(attr.prefix, attr.uri);
|
|
}
|
|
} else {
|
|
writenamespace(attr.prefix, attr.uri);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
writeAttributeWithPrefix(attr.prefix, attr.localpart,
|
|
attr.value);
|
|
}
|
|
fAttrNamespace = null;
|
|
fAttributeCache.clear();
|
|
}
|
|
|
|
if (currentElement.isEmpty) {
|
|
fElementStack.pop();
|
|
fInternalNamespaceContext.popContext();
|
|
fWriter.write(CLOSE_EMPTY_ELEMENT);
|
|
} else {
|
|
fWriter.write(CLOSE_START_TAG);
|
|
}
|
|
|
|
fStartTagOpened = false;
|
|
} catch (IOException ex) {
|
|
fStartTagOpened = false;
|
|
throw new XMLStreamException(ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* marks open of start tag and writes the same into the writer.
|
|
*/
|
|
private void openStartTag() throws IOException {
|
|
fStartTagOpened = true;
|
|
fWriter.write(OPEN_START_TAG);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param uri
|
|
* @return
|
|
*/
|
|
private void correctPrefix(QName attr, int type) {
|
|
String tmpPrefix = null;
|
|
String prefix;
|
|
String uri;
|
|
prefix = attr.prefix;
|
|
uri = attr.uri;
|
|
boolean isSpecialCaseURI = false;
|
|
|
|
if (prefix == null || prefix.equals("")) {
|
|
if (uri == null) {
|
|
return;
|
|
}
|
|
|
|
if (prefix == XMLConstants.DEFAULT_NS_PREFIX && uri == XMLConstants.DEFAULT_NS_PREFIX)
|
|
return;
|
|
|
|
uri = fSymbolTable.addSymbol(uri);
|
|
|
|
QName decl = null;
|
|
|
|
for (int i = 0; i < fNamespaceDecls.size(); i++) {
|
|
decl = (QName) fNamespaceDecls.get(i);
|
|
|
|
if ((decl != null) && (decl.uri == attr.uri)) {
|
|
attr.prefix = decl.prefix;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
tmpPrefix = fNamespaceContext.getPrefix(uri);
|
|
|
|
if (tmpPrefix == XMLConstants.DEFAULT_NS_PREFIX) {
|
|
if (type == XMLStreamConstants.START_ELEMENT) {
|
|
return;
|
|
}
|
|
else if (type == XMLStreamConstants.ATTRIBUTE) {
|
|
//the uri happens to be the same as that of the default namespace
|
|
tmpPrefix = getAttrPrefix(uri);
|
|
isSpecialCaseURI = true;
|
|
}
|
|
}
|
|
|
|
if (tmpPrefix == null) {
|
|
StringBuffer genPrefix = new StringBuffer("zdef");
|
|
|
|
for (int i = 0; i < 1; i++) {
|
|
genPrefix.append(fPrefixGen.nextInt());
|
|
}
|
|
|
|
prefix = genPrefix.toString();
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
} else {
|
|
prefix = fSymbolTable.addSymbol(tmpPrefix);
|
|
}
|
|
|
|
if (tmpPrefix == null) {
|
|
if (isSpecialCaseURI) {
|
|
addAttrNamespace(prefix, uri);
|
|
} else {
|
|
QName qname = new QName();
|
|
qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null, uri);
|
|
fNamespaceDecls.add(qname);
|
|
fInternalNamespaceContext.declarePrefix(fSymbolTable.addSymbol(
|
|
prefix), uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
attr.prefix = prefix;
|
|
}
|
|
|
|
/**
|
|
* return the prefix if the attribute has an uri the same as that of the default namespace
|
|
*/
|
|
private String getAttrPrefix(String uri) {
|
|
if (fAttrNamespace != null) {
|
|
return (String)fAttrNamespace.get(uri);
|
|
}
|
|
return null;
|
|
}
|
|
private void addAttrNamespace(String prefix, String uri) {
|
|
if (fAttrNamespace == null) {
|
|
fAttrNamespace = new HashMap();
|
|
}
|
|
fAttrNamespace.put(prefix, uri);
|
|
}
|
|
/**
|
|
* @param uri
|
|
* @return
|
|
*/
|
|
private boolean isDefaultNamespace(String uri) {
|
|
String defaultNamespace = fInternalNamespaceContext.getURI(DEFAULT_PREFIX);
|
|
|
|
if (uri == defaultNamespace) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param prefix
|
|
* @param uri
|
|
* @return
|
|
*/
|
|
private boolean checkUserNamespaceContext(String prefix, String uri) {
|
|
if (fNamespaceContext.userContext != null) {
|
|
String tmpURI = fNamespaceContext.userContext.getNamespaceURI(prefix);
|
|
|
|
if ((tmpURI != null) && tmpURI.equals(uri)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Correct's namespaces as per requirements of isReparisingNamespace property.
|
|
*/
|
|
protected void repair() {
|
|
Attribute attr = null;
|
|
Attribute attr2 = null;
|
|
ElementState currentElement = fElementStack.peek();
|
|
removeDuplicateDecls();
|
|
|
|
for(int i=0 ; i< fAttributeCache.size();i++){
|
|
attr = (Attribute)fAttributeCache.get(i);
|
|
if((attr.prefix != null && !attr.prefix.equals("")) || (attr.uri != null && !attr.uri.equals(""))) {
|
|
correctPrefix(currentElement,attr);
|
|
}
|
|
}
|
|
|
|
if (!isDeclared(currentElement)) {
|
|
if ((currentElement.prefix != null) &&
|
|
(currentElement.uri != null)) {
|
|
if ((!currentElement.prefix.equals("")) && (!currentElement.uri.equals(""))) {
|
|
fNamespaceDecls.add(currentElement);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int i=0 ; i< fAttributeCache.size();i++){
|
|
attr = (Attribute)fAttributeCache.get(i);
|
|
for(int j=i+1;j<fAttributeCache.size();j++){
|
|
attr2 = (Attribute)fAttributeCache.get(j);
|
|
if(!"".equals(attr.prefix)&& !"".equals(attr2.prefix)){
|
|
correctPrefix(attr,attr2);
|
|
}
|
|
}
|
|
}
|
|
|
|
repairNamespaceDecl(currentElement);
|
|
|
|
int i = 0;
|
|
|
|
for (i = 0; i < fAttributeCache.size(); i++) {
|
|
attr = (Attribute) fAttributeCache.get(i);
|
|
/* If 'attr' is an attribute and it is in no namespace(which means that prefix="", uri=""), attr's
|
|
namespace should not be redinded. See [http://www.w3.org/TR/REC-xml-names/#defaulting].
|
|
*/
|
|
if (attr.prefix != null && attr.prefix.equals("") && attr.uri != null && attr.uri.equals("")){
|
|
repairNamespaceDecl(attr);
|
|
}
|
|
}
|
|
|
|
QName qname = null;
|
|
|
|
for (i = 0; i < fNamespaceDecls.size(); i++) {
|
|
qname = (QName) fNamespaceDecls.get(i);
|
|
|
|
if (qname != null) {
|
|
fInternalNamespaceContext.declarePrefix(qname.prefix, qname.uri);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < fAttributeCache.size(); i++) {
|
|
attr = (Attribute) fAttributeCache.get(i);
|
|
correctPrefix(attr, XMLStreamConstants.ATTRIBUTE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
*If element and/or attribute names in the same start or empty-element tag
|
|
*are bound to different namespace URIs and are using the same prefix then
|
|
*the element or the first occurring attribute retains the original prefix
|
|
*and the following attributes have their prefixes replaced with a new prefix
|
|
*that is bound to the namespace URIs of those attributes.
|
|
*/
|
|
void correctPrefix(QName attr1, QName attr2) {
|
|
String tmpPrefix = null;
|
|
QName decl = null;
|
|
boolean done = false;
|
|
|
|
checkForNull(attr1);
|
|
checkForNull(attr2);
|
|
|
|
if(attr1.prefix.equals(attr2.prefix) && !(attr1.uri.equals(attr2.uri))){
|
|
|
|
tmpPrefix = fNamespaceContext.getPrefix(attr2.uri);
|
|
|
|
if (tmpPrefix != null) {
|
|
attr2.prefix = fSymbolTable.addSymbol(tmpPrefix);
|
|
} else {
|
|
decl = null;
|
|
for(int n=0;n<fNamespaceDecls.size();n++){
|
|
decl = (QName)fNamespaceDecls.get(n);
|
|
if(decl != null && (decl.uri == attr2.uri)){
|
|
attr2.prefix = decl.prefix;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//No namespace mapping found , so declare prefix.
|
|
StringBuffer genPrefix = new StringBuffer("zdef");
|
|
|
|
for (int k = 0; k < 1; k++) {
|
|
genPrefix.append(fPrefixGen.nextInt());
|
|
}
|
|
|
|
tmpPrefix = genPrefix.toString();
|
|
tmpPrefix = fSymbolTable.addSymbol(tmpPrefix);
|
|
attr2.prefix = tmpPrefix;
|
|
|
|
QName qname = new QName();
|
|
qname.setValues(tmpPrefix, XMLConstants.XMLNS_ATTRIBUTE, null,
|
|
attr2.uri);
|
|
fNamespaceDecls.add(qname);
|
|
}
|
|
}
|
|
}
|
|
|
|
void checkForNull(QName attr) {
|
|
if (attr.prefix == null) attr.prefix = XMLConstants.DEFAULT_NS_PREFIX;
|
|
if (attr.uri == null) attr.uri = XMLConstants.DEFAULT_NS_PREFIX;
|
|
}
|
|
|
|
void removeDuplicateDecls(){
|
|
QName decl1,decl2;
|
|
for(int i =0;i<fNamespaceDecls.size();i++){
|
|
decl1 = (QName)fNamespaceDecls.get(i);
|
|
if(decl1!=null) {
|
|
for(int j=i+1;j<fNamespaceDecls.size();j++){
|
|
decl2 = (QName)fNamespaceDecls.get(j);
|
|
// QName.equals relies on identity equality, so we can't use it,
|
|
// because prefixes aren't interned
|
|
if(decl2!=null && decl1.prefix.equals(decl2.prefix) && decl1.uri.equals(decl2.uri))
|
|
fNamespaceDecls.remove(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*If an element or attribute name is bound to a prefix and there is a namespace
|
|
*declaration that binds that prefix to a different URI then that namespace declaration
|
|
*is either removed if the correct mapping is inherited from the parent context of that element,
|
|
*or changed to the namespace URI of the element or attribute using that prefix.
|
|
*
|
|
*/
|
|
void repairNamespaceDecl(QName attr) {
|
|
QName decl = null;
|
|
String tmpURI;
|
|
|
|
//check for null prefix.
|
|
for (int j = 0; j < fNamespaceDecls.size(); j++) {
|
|
decl = (QName) fNamespaceDecls.get(j);
|
|
|
|
if (decl != null) {
|
|
if ((attr.prefix != null) &&
|
|
(attr.prefix.equals(decl.prefix) &&
|
|
!(attr.uri.equals(decl.uri)))) {
|
|
tmpURI = fNamespaceContext.getNamespaceURI(attr.prefix);
|
|
|
|
//see if you need to add to symbole table.
|
|
if (tmpURI != null) {
|
|
if (tmpURI.equals(attr.uri)) {
|
|
fNamespaceDecls.set(j, null);
|
|
} else {
|
|
decl.uri = attr.uri;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean isDeclared(QName attr) {
|
|
QName decl = null;
|
|
|
|
for (int n = 0; n < fNamespaceDecls.size(); n++) {
|
|
decl = (QName) fNamespaceDecls.get(n);
|
|
|
|
if ((attr.prefix != null) &&
|
|
((attr.prefix == decl.prefix) && (decl.uri == attr.uri))) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (attr.uri != null) {
|
|
if (fNamespaceContext.getPrefix(attr.uri) != null) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Start of Internal classes.
|
|
*
|
|
*/
|
|
protected class ElementStack {
|
|
/** The stack data. */
|
|
protected ElementState[] fElements;
|
|
|
|
/** The size of the stack. */
|
|
protected short fDepth;
|
|
|
|
/** Default constructor. */
|
|
public ElementStack() {
|
|
fElements = new ElementState[10];
|
|
|
|
for (int i = 0; i < fElements.length; i++) {
|
|
fElements[i] = new ElementState();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pushes an element on the stack.
|
|
* <p>
|
|
* <strong>Note:</strong> The QName values are copied into the
|
|
* stack. In other words, the caller does <em>not</em> orphan
|
|
* the element to the stack. Also, the QName object returned
|
|
* is <em>not</em> orphaned to the caller. It should be
|
|
* considered read-only.
|
|
*
|
|
* @param element The element to push onto the stack.
|
|
*
|
|
* @return Returns the actual QName object that stores the
|
|
*/
|
|
public ElementState push(ElementState element) {
|
|
if (fDepth == fElements.length) {
|
|
ElementState[] array = new ElementState[fElements.length * 2];
|
|
System.arraycopy(fElements, 0, array, 0, fDepth);
|
|
fElements = array;
|
|
|
|
for (int i = fDepth; i < fElements.length; i++) {
|
|
fElements[i] = new ElementState();
|
|
}
|
|
}
|
|
|
|
fElements[fDepth].setValues(element);
|
|
|
|
return fElements[fDepth++];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param prefix
|
|
* @param localpart
|
|
* @param rawname
|
|
* @param uri
|
|
* @param isEmpty
|
|
* @return
|
|
*/
|
|
public ElementState push(String prefix, String localpart,
|
|
String rawname, String uri, boolean isEmpty) {
|
|
if (fDepth == fElements.length) {
|
|
ElementState[] array = new ElementState[fElements.length * 2];
|
|
System.arraycopy(fElements, 0, array, 0, fDepth);
|
|
fElements = array;
|
|
|
|
for (int i = fDepth; i < fElements.length; i++) {
|
|
fElements[i] = new ElementState();
|
|
}
|
|
}
|
|
|
|
fElements[fDepth].setValues(prefix, localpart, rawname, uri, isEmpty);
|
|
|
|
return fElements[fDepth++];
|
|
}
|
|
|
|
/**
|
|
* Pops an element off of the stack by setting the values of
|
|
* the specified QName.
|
|
* <p>
|
|
* <strong>Note:</strong> The object returned is <em>not</em>
|
|
* orphaned to the caller. Therefore, the caller should consider
|
|
* the object to be read-only.
|
|
*/
|
|
public ElementState pop() {
|
|
return fElements[--fDepth];
|
|
}
|
|
|
|
/** Clears the stack without throwing away existing QName objects. */
|
|
public void clear() {
|
|
fDepth = 0;
|
|
}
|
|
|
|
/**
|
|
* This function is as a result of optimization done for endElement --
|
|
* we dont need to set the value for every end element we encouter.
|
|
* For Well formedness checks we can have the same QName object that was pushed.
|
|
* the values will be set only if application need to know about the endElement
|
|
* -- neeraj.bajaj@sun.com
|
|
*/
|
|
public ElementState peek() {
|
|
return fElements[fDepth - 1];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return
|
|
*/
|
|
public boolean empty() {
|
|
return (fDepth > 0) ? false : true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maintains element state . localName for now.
|
|
*/
|
|
class ElementState extends QName {
|
|
public boolean isEmpty = false;
|
|
|
|
public ElementState() {}
|
|
|
|
public ElementState(String prefix, String localpart, String rawname,
|
|
String uri) {
|
|
super(prefix, localpart, rawname, uri);
|
|
}
|
|
|
|
public void setValues(String prefix, String localpart, String rawname,
|
|
String uri, boolean isEmpty) {
|
|
super.setValues(prefix, localpart, rawname, uri);
|
|
this.isEmpty = isEmpty;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attributes
|
|
*/
|
|
class Attribute extends QName {
|
|
String value;
|
|
|
|
Attribute(String value) {
|
|
super();
|
|
this.value = value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of NamespaceContext .
|
|
*
|
|
*/
|
|
class NamespaceContextImpl implements NamespaceContext {
|
|
//root namespace context set by user.
|
|
NamespaceContext userContext = null;
|
|
|
|
//context built by the writer.
|
|
NamespaceSupport internalContext = null;
|
|
|
|
public String getNamespaceURI(String prefix) {
|
|
String uri = null;
|
|
|
|
if (prefix != null) {
|
|
prefix = fSymbolTable.addSymbol(prefix);
|
|
}
|
|
|
|
if (internalContext != null) {
|
|
uri = internalContext.getURI(prefix);
|
|
|
|
if (uri != null) {
|
|
return uri;
|
|
}
|
|
}
|
|
|
|
if (userContext != null) {
|
|
uri = userContext.getNamespaceURI(prefix);
|
|
|
|
return uri;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public String getPrefix(String uri) {
|
|
String prefix = null;
|
|
|
|
if (uri != null) {
|
|
uri = fSymbolTable.addSymbol(uri);
|
|
}
|
|
|
|
if (internalContext != null) {
|
|
prefix = internalContext.getPrefix(uri);
|
|
|
|
if (prefix != null) {
|
|
return prefix;
|
|
}
|
|
}
|
|
|
|
if (userContext != null) {
|
|
return userContext.getPrefix(uri);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public java.util.Iterator getPrefixes(String uri) {
|
|
Vector prefixes = null;
|
|
Iterator itr = null;
|
|
|
|
if (uri != null) {
|
|
uri = fSymbolTable.addSymbol(uri);
|
|
}
|
|
|
|
if (userContext != null) {
|
|
itr = userContext.getPrefixes(uri);
|
|
}
|
|
|
|
if (internalContext != null) {
|
|
prefixes = internalContext.getPrefixes(uri);
|
|
}
|
|
|
|
if ((prefixes == null) && (itr != null)) {
|
|
return itr;
|
|
} else if ((prefixes != null) && (itr == null)) {
|
|
return new ReadOnlyIterator(prefixes.iterator());
|
|
} else if ((prefixes != null) && (itr != null)) {
|
|
String ob = null;
|
|
|
|
while (itr.hasNext()) {
|
|
ob = (String) itr.next();
|
|
|
|
if (ob != null) {
|
|
ob = fSymbolTable.addSymbol(ob);
|
|
}
|
|
|
|
if (!prefixes.contains(ob)) {
|
|
prefixes.add(ob);
|
|
}
|
|
}
|
|
|
|
return new ReadOnlyIterator(prefixes.iterator());
|
|
}
|
|
|
|
return fReadOnlyIterator;
|
|
}
|
|
}
|
|
|
|
// -- Map Interface --------------------------------------------------
|
|
|
|
public int size() {
|
|
return 1;
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
return false;
|
|
}
|
|
|
|
public boolean containsKey(Object key) {
|
|
return key.equals(OUTPUTSTREAM_PROPERTY);
|
|
}
|
|
|
|
/**
|
|
* Returns the value associated to an implementation-specific
|
|
* property.
|
|
*/
|
|
public Object get(Object key) {
|
|
if (key.equals(OUTPUTSTREAM_PROPERTY)) {
|
|
return fOutputStream;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public java.util.Set entrySet() {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
/**
|
|
* Overrides the method defined in AbstractMap which is
|
|
* not completely implemented. Calling toString() in
|
|
* AbstractMap would cause an unsupported exection to
|
|
* be thrown.
|
|
*/
|
|
public String toString() {
|
|
return getClass().getName() + "@" + Integer.toHexString(hashCode());
|
|
}
|
|
|
|
/**
|
|
* Overrides the method defined in AbstractMap
|
|
* This is required by the toString() method
|
|
*/
|
|
public int hashCode() {
|
|
return fElementStack.hashCode();
|
|
}
|
|
/**
|
|
* Overrides the method defined in AbstractMap
|
|
* This is required to satisfy the contract for hashCode.
|
|
*/
|
|
public boolean equals(Object obj) {
|
|
return (this == obj);
|
|
}
|
|
}
|