|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.xml.internal.stream.writers; |
|
|
|
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.xni.QName; |
|
import com.sun.xml.internal.stream.util.ReadOnlyIterator; |
|
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.List; |
|
import java.util.Map; |
|
import java.util.Objects; |
|
import java.util.Random; |
|
import java.util.Set; |
|
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.transform.stream.StreamResult; |
|
import jdk.xml.internal.SecuritySupport; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class XMLStreamWriterImpl extends AbstractMap<Object, Object> |
|
implements XMLStreamWriterBase { |
|
|
|
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"; |
|
|
|
|
|
|
|
|
|
*/ |
|
boolean fEscapeCharacters = true; |
|
|
|
|
|
|
|
*/ |
|
private boolean fIsRepairingNamespace = false; |
|
|
|
|
|
|
|
*/ |
|
private Writer fWriter; |
|
|
|
|
|
|
|
|
|
*/ |
|
private OutputStream fOutputStream = null; |
|
|
|
|
|
|
|
*/ |
|
private List<Attribute> fAttributeCache; |
|
|
|
|
|
|
|
*/ |
|
private List<QName> fNamespaceDecls; |
|
|
|
|
|
|
|
|
|
*/ |
|
private NamespaceContextImpl fNamespaceContext = null; |
|
|
|
private NamespaceSupport fInternalNamespaceContext = null; |
|
|
|
private Random fPrefixGen = null; |
|
|
|
|
|
|
|
*/ |
|
private PropertyManager fPropertyManager = null; |
|
|
|
|
|
|
|
*/ |
|
private boolean fStartTagOpened = false; |
|
|
|
|
|
|
|
*/ |
|
private boolean fReuse; |
|
|
|
private SymbolTable fSymbolTable = new SymbolTable(); |
|
|
|
private ElementStack fElementStack = new ElementStack(); |
|
|
|
final private String DEFAULT_PREFIX = fSymbolTable.addSymbol(""); |
|
|
|
private final ReadOnlyIterator<String> fReadOnlyIterator = new ReadOnlyIterator<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private CharsetEncoder fEncoder = null; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Map<String, String> fAttrNamespace = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public XMLStreamWriterImpl(OutputStream outputStream, PropertyManager props) |
|
throws IOException { |
|
|
|
// cannot call this(outputStream, null, props); for constructor, |
|
// OutputStreamWriter charsetName cannot be null |
|
|
|
|
|
this(new OutputStreamWriter(outputStream), props); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public XMLStreamWriterImpl(OutputStream outputStream, String encoding, |
|
PropertyManager props) throws java.io.IOException { |
|
this(new StreamResult(outputStream), encoding, props); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public XMLStreamWriterImpl(Writer writer, PropertyManager props) |
|
throws java.io.IOException { |
|
this(new StreamResult(writer), null, props); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public XMLStreamWriterImpl(StreamResult sr, String encoding, |
|
PropertyManager props) throws java.io.IOException { |
|
setOutput(sr, encoding); |
|
fPropertyManager = props; |
|
init(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); |
|
fIsRepairingNamespace = ob; |
|
ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS); |
|
setEscapeCharacters(ob); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void reset() { |
|
reset(false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void reset(boolean resetProperties) { |
|
if (!fReuse) { |
|
throw new java.lang.IllegalStateException( |
|
"close() Must be called before calling reset()"); |
|
} |
|
|
|
fReuse = false; |
|
fNamespaceDecls.clear(); |
|
fAttributeCache.clear(); |
|
|
|
|
|
fElementStack.clear(); |
|
fInternalNamespaceContext.reset(); |
|
|
|
fStartTagOpened = false; |
|
fNamespaceContext.userContext = null; |
|
|
|
if (resetProperties) { |
|
Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); |
|
fIsRepairingNamespace = ob; |
|
ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS); |
|
setEscapeCharacters(ob); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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)); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean canReuse() { |
|
return fReuse; |
|
} |
|
|
|
public void setEscapeCharacters(boolean escape) { |
|
fEscapeCharacters = escape; |
|
} |
|
|
|
public boolean getEscapeCharacters() { |
|
return fEscapeCharacters; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void close() throws XMLStreamException { |
|
if (fWriter != null) { |
|
try { |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void flush() throws XMLStreamException { |
|
try { |
|
fWriter.flush(); |
|
} catch (IOException e) { |
|
throw new XMLStreamException(e); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public NamespaceContext getNamespaceContext() { |
|
return fNamespaceContext; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String getPrefix(String uri) throws XMLStreamException { |
|
return fNamespaceContext.getPrefix(uri); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void setNamespaceContext(NamespaceContext namespaceContext) |
|
throws XMLStreamException { |
|
fNamespaceContext.userContext = namespaceContext; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
|
|
@Override |
|
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); |
|
attr.setValues(null, localName, null, null); |
|
fAttributeCache.add(attr); |
|
|
|
return; |
|
} |
|
|
|
fWriter.write(" "); |
|
fWriter.write(localName); |
|
fWriter.write("=\""); |
|
writeXMLContent( |
|
value, |
|
true, |
|
true); |
|
fWriter.write("\""); |
|
} catch (IOException e) { |
|
throw new XMLStreamException(e); |
|
} |
|
} |
|
|
|
@Override |
|
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.equals(XMLConstants.DEFAULT_NS_PREFIX))) { |
|
fWriter.write(prefix); |
|
fWriter.write(":"); |
|
} |
|
|
|
fWriter.write(localName); |
|
fWriter.write("=\""); |
|
writeXMLContent(value, |
|
true, |
|
true); |
|
fWriter.write("\""); |
|
} |
|
|
|
@Override |
|
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.isEmpty()){ |
|
if (!namespaceURI.isEmpty()) { |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
public void writeCharacters(String data) throws XMLStreamException { |
|
try { |
|
if (fStartTagOpened) { |
|
closeStartTag(); |
|
} |
|
|
|
writeXMLContent(data); |
|
} catch (IOException e) { |
|
throw new XMLStreamException(e); |
|
} |
|
} |
|
|
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
public void writeDTD(String dtd) throws XMLStreamException { |
|
try { |
|
if (fStartTagOpened) { |
|
closeStartTag(); |
|
} |
|
|
|
fWriter.write(dtd); |
|
} catch (IOException e) { |
|
throw new XMLStreamException(e); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void writeDefaultNamespace(String namespaceURI) |
|
throws XMLStreamException { |
|
|
|
|
|
String namespaceURINormalized; |
|
if (namespaceURI == null) { |
|
namespaceURINormalized = ""; |
|
} 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.equals(namespaceURINormalized)) { |
|
throw new XMLStreamException( |
|
"xmlns has been already bound to " +tmp + |
|
". Rebinding it to "+ namespaceURINormalized + |
|
" is an error"); |
|
} |
|
} |
|
fInternalNamespaceContext.declarePrefix("", namespaceURINormalized); |
|
|
|
|
|
writenamespace(null, namespaceURINormalized); |
|
} catch (IOException e) { |
|
throw new XMLStreamException(e); |
|
} |
|
} |
|
|
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
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); |
|
} |
|
|
|
@Override |
|
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.equals(XMLConstants.DEFAULT_NS_PREFIX))) { |
|
fWriter.write(prefix); |
|
fWriter.write(":"); |
|
} |
|
|
|
fWriter.write(localName); |
|
} catch (IOException e) { |
|
throw new XMLStreamException(e); |
|
} |
|
} |
|
|
|
@Override |
|
public void writeEndDocument() throws XMLStreamException { |
|
try { |
|
if (fStartTagOpened) { |
|
closeStartTag(); |
|
} |
|
|
|
while (!fElementStack.empty()) { |
|
ElementState elem = fElementStack.pop(); |
|
fInternalNamespaceContext.popContext(); |
|
|
|
if (elem.isEmpty) { |
|
//fWriter.write(CLOSE_EMPTY_ELEMENT); |
|
} else { |
|
fWriter.write(OPEN_END_TAG); |
|
|
|
if ((elem.prefix != null) && !(elem.prefix).isEmpty()) { |
|
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"); |
|
} |
|
} |
|
|
|
@Override |
|
public void writeEndElement() throws XMLStreamException { |
|
try { |
|
if (fStartTagOpened) { |
|
closeStartTag(); |
|
} |
|
|
|
ElementState currentElement = fElementStack.pop(); |
|
|
|
if (currentElement == null) { |
|
throw new XMLStreamException("No element was found to write"); |
|
} |
|
|
|
if (currentElement.isEmpty) { |
|
|
|
return; |
|
} |
|
|
|
fWriter.write(OPEN_END_TAG); |
|
|
|
if ((currentElement.prefix != null) && |
|
!(currentElement.prefix).isEmpty()) { |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void writeNamespace(String prefix, String namespaceURI) |
|
throws XMLStreamException { |
|
|
|
|
|
String namespaceURINormalized; |
|
if (namespaceURI == null) { |
|
namespaceURINormalized = ""; |
|
} else { |
|
namespaceURINormalized = namespaceURI; |
|
} |
|
|
|
try { |
|
QName qname; |
|
|
|
if (!fStartTagOpened) { |
|
throw new IllegalStateException( |
|
"Invalid state: start tag is not opened at writeNamespace(" |
|
+ prefix |
|
+ ", " |
|
+ namespaceURINormalized |
|
+ ")"); |
|
} |
|
|
|
|
|
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.equals(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.equals(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.equals(XMLConstants.DEFAULT_NS_PREFIX))) { |
|
fWriter.write(":"); |
|
fWriter.write(prefix); |
|
} |
|
|
|
fWriter.write("=\""); |
|
writeXMLContent( |
|
namespaceURI, |
|
true, |
|
true); |
|
fWriter.write("\""); |
|
} |
|
|
|
@Override |
|
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"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void writeStartDocument() throws XMLStreamException { |
|
writeStartDocument(null, null, false, false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void writeStartDocument(String version) throws XMLStreamException { |
|
writeStartDocument(null, version, false, false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void writeStartDocument(String encoding, String version) |
|
throws XMLStreamException { |
|
writeStartDocument(encoding, version, false, false); |
|
} |
|
|
|
public void writeStartDocument(String encoding, String version, |
|
boolean standalone, boolean standaloneSet) |
|
throws XMLStreamException { |
|
|
|
try { |
|
if ((encoding == null || encoding.length() == 0) |
|
&& (version == null || version.length() == 0) |
|
&& (!standaloneSet)) { |
|
fWriter.write(DEFAULT_XMLDECL); |
|
return; |
|
} |
|
|
|
|
|
if (encoding != null && !encoding.isEmpty()) { |
|
verifyEncoding(encoding); |
|
} |
|
|
|
fWriter.write("<?xml version=\""); |
|
|
|
if ((version == null) || version.isEmpty()) { |
|
fWriter.write(DEFAULT_XML_VERSION); |
|
} else { |
|
fWriter.write(version); |
|
} |
|
|
|
if (encoding != null && !encoding.isEmpty()) { |
|
fWriter.write("\" encoding=\""); |
|
fWriter.write(encoding); |
|
} |
|
|
|
if (standaloneSet) { |
|
fWriter.write("\" standalone=\""); |
|
if (standalone) { |
|
fWriter.write("yes"); |
|
} else { |
|
fWriter.write("no"); |
|
} |
|
} |
|
|
|
fWriter.write("\"?>"); |
|
} catch (IOException ex) { |
|
throw new XMLStreamException(ex); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void verifyEncoding(String encoding) throws XMLStreamException { |
|
|
|
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)) { |
|
|
|
boolean foundAlias = false; |
|
Set<String> aliases = Charset.forName(encoding).aliases(); |
|
for (Iterator<String> it = aliases.iterator(); !foundAlias && it.hasNext(); ) { |
|
if (streamEncoding.equalsIgnoreCase(it.next())) { |
|
foundAlias = true; |
|
} |
|
} |
|
|
|
if (!foundAlias) { |
|
throw new XMLStreamException("Underlying stream encoding '" |
|
+ streamEncoding |
|
+ "' and input paramter for writeStartDocument() method '" |
|
+ encoding + "' do not match."); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void writeCharRef(int codePoint) throws IOException { |
|
fWriter.write( "&#x" ); |
|
fWriter.write( Integer.toHexString(codePoint) ); |
|
fWriter.write( ';' ); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeXMLContent(char[] content, int start, int length, |
|
boolean escapeChars) throws IOException { |
|
if (!escapeChars) { |
|
fWriter.write(content, start, length); |
|
|
|
return; |
|
} |
|
|
|
|
|
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 |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
fWriter.write(content, startWritePos, end - startWritePos); |
|
} |
|
|
|
private void writeXMLContent(String content) throws IOException { |
|
if ((content != null) && (content.length() > 0)) { |
|
writeXMLContent(content, |
|
fEscapeCharacters, |
|
false); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeXMLContent( |
|
String content, |
|
boolean escapeChars, |
|
boolean escapeDoubleQuotes) |
|
throws IOException { |
|
|
|
if (!escapeChars) { |
|
fWriter.write(content); |
|
|
|
return; |
|
} |
|
|
|
|
|
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 |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
fWriter.write(content, startWritePos, end - startWritePos); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
for (int i = 0; i < len; i++) { |
|
qname = fNamespaceDecls.get(i); |
|
|
|
if (qname != null) { |
|
if (fInternalNamespaceContext.declarePrefix(qname.prefix, |
|
qname.uri)) { |
|
writenamespace(qname.prefix, qname.uri); |
|
} |
|
} |
|
} |
|
|
|
fNamespaceDecls.clear(); |
|
|
|
Attribute attr; |
|
|
|
for (int j = 0; j < fAttributeCache.size(); j++) { |
|
attr = fAttributeCache.get(j); |
|
|
|
if ((attr.prefix != null) && (attr.uri != null)) { |
|
if (!attr.prefix.isEmpty() && !attr.uri.isEmpty() ) { |
|
String tmp = fInternalNamespaceContext.getPrefix(attr.uri); |
|
|
|
if ((tmp == null) || (!tmp.equals(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); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void openStartTag() throws IOException { |
|
fStartTagOpened = true; |
|
fWriter.write(OPEN_START_TAG); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void correctPrefix(QName attr, int type) { |
|
String tmpPrefix; |
|
String prefix; |
|
String uri; |
|
prefix = attr.prefix; |
|
uri = attr.uri; |
|
boolean isSpecialCaseURI = false; |
|
|
|
if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) { |
|
if (uri == null) { |
|
return; |
|
} |
|
|
|
if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix) && uri.equals(XMLConstants.DEFAULT_NS_PREFIX)) |
|
return; |
|
|
|
uri = fSymbolTable.addSymbol(uri); |
|
|
|
QName decl; |
|
|
|
for (int i = 0; i < fNamespaceDecls.size(); i++) { |
|
decl = fNamespaceDecls.get(i); |
|
|
|
if ((decl != null) && (decl.uri.equals(attr.uri))) { |
|
attr.prefix = decl.prefix; |
|
|
|
return; |
|
} |
|
} |
|
|
|
tmpPrefix = fNamespaceContext.getPrefix(uri); |
|
|
|
if (XMLConstants.DEFAULT_NS_PREFIX.equals(tmpPrefix)) { |
|
if (type == XMLStreamConstants.START_ELEMENT) { |
|
return; |
|
} |
|
else if (type == XMLStreamConstants.ATTRIBUTE) { |
|
|
|
tmpPrefix = getAttrPrefix(uri); |
|
isSpecialCaseURI = true; |
|
} |
|
} |
|
|
|
if (tmpPrefix == null) { |
|
StringBuilder genPrefix = new StringBuilder("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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private String getAttrPrefix(String uri) { |
|
if (fAttrNamespace != null) { |
|
return fAttrNamespace.get(uri); |
|
} |
|
return null; |
|
} |
|
private void addAttrNamespace(String prefix, String uri) { |
|
if (fAttrNamespace == null) { |
|
fAttrNamespace = new HashMap<>(); |
|
} |
|
fAttrNamespace.put(prefix, uri); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private boolean isDefaultNamespace(String uri) { |
|
String defaultNamespace = fInternalNamespaceContext.getURI(DEFAULT_PREFIX); |
|
return Objects.equals(uri, defaultNamespace); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
protected void repair() { |
|
Attribute attr; |
|
Attribute attr2; |
|
ElementState currentElement = fElementStack.peek(); |
|
removeDuplicateDecls(); |
|
|
|
for(int i=0 ; i< fAttributeCache.size();i++){ |
|
attr = fAttributeCache.get(i); |
|
if((attr.prefix != null && !attr.prefix.isEmpty()) || (attr.uri != null && !attr.uri.isEmpty())) { |
|
correctPrefix(currentElement,attr); |
|
} |
|
} |
|
|
|
if (!isDeclared(currentElement)) { |
|
if ((currentElement.prefix != null) && |
|
(currentElement.uri != null)) { |
|
if ((!currentElement.prefix.isEmpty()) && (!currentElement.uri.isEmpty())) { |
|
fNamespaceDecls.add(currentElement); |
|
} |
|
} |
|
} |
|
|
|
for(int i=0 ; i< fAttributeCache.size();i++){ |
|
attr = fAttributeCache.get(i); |
|
for(int j=i+1;j<fAttributeCache.size();j++){ |
|
attr2 = fAttributeCache.get(j); |
|
if(!"".equals(attr.prefix)&& !"".equals(attr2.prefix)){ |
|
correctPrefix(attr,attr2); |
|
} |
|
} |
|
} |
|
|
|
repairNamespaceDecl(currentElement); |
|
|
|
int i; |
|
|
|
for (i = 0; i < fAttributeCache.size(); i++) { |
|
attr = fAttributeCache.get(i); |
|
|
|
|
|
*/ |
|
if (attr.prefix != null && attr.prefix.isEmpty() && attr.uri != null && attr.uri.isEmpty()){ |
|
repairNamespaceDecl(attr); |
|
} |
|
} |
|
|
|
QName qname = null; |
|
|
|
for (i = 0; i < fNamespaceDecls.size(); i++) { |
|
qname = fNamespaceDecls.get(i); |
|
|
|
if (qname != null) { |
|
fInternalNamespaceContext.declarePrefix(qname.prefix, qname.uri); |
|
} |
|
} |
|
|
|
for (i = 0; i < fAttributeCache.size(); i++) { |
|
attr = fAttributeCache.get(i); |
|
correctPrefix(attr, XMLStreamConstants.ATTRIBUTE); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void correctPrefix(QName attr1, QName attr2) { |
|
String tmpPrefix; |
|
QName decl; |
|
|
|
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 { |
|
for (int n=0; n<fNamespaceDecls.size(); n++) { |
|
decl = fNamespaceDecls.get(n); |
|
if(decl != null && (decl.uri.equals(attr2.uri))){ |
|
attr2.prefix = decl.prefix; |
|
|
|
return; |
|
} |
|
} |
|
|
|
|
|
StringBuilder genPrefix = new StringBuilder("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 = fNamespaceDecls.get(i); |
|
if(decl1!=null) { |
|
for(int j=i+1;j<fNamespaceDecls.size();j++){ |
|
decl2 = fNamespaceDecls.get(j); |
|
// QName.equals relies on identity equality, so we can't use it, |
|
|
|
if(decl2!=null && decl1.prefix.equals(decl2.prefix) && decl1.uri.equals(decl2.uri)) |
|
fNamespaceDecls.remove(j); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void repairNamespaceDecl(QName attr) { |
|
QName decl; |
|
String tmpURI; |
|
|
|
|
|
for (int j = 0; j < fNamespaceDecls.size(); j++) { |
|
decl = 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); |
|
|
|
|
|
if (tmpURI != null) { |
|
if (tmpURI.equals(attr.uri)) { |
|
fNamespaceDecls.set(j, null); |
|
} else { |
|
decl.uri = attr.uri; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
boolean isDeclared(QName attr) { |
|
QName decl; |
|
|
|
for (int n = 0; n < fNamespaceDecls.size(); n++) { |
|
decl = fNamespaceDecls.get(n); |
|
|
|
if ((attr.prefix != null) && |
|
((attr.prefix.equals(decl.prefix)) && (decl.uri.equals(attr.uri)))) { |
|
return true; |
|
} |
|
} |
|
|
|
if (attr.uri != null) { |
|
if (fNamespaceContext.getPrefix(attr.uri) != null) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected class ElementStack { |
|
|
|
protected ElementState[] fElements; |
|
|
|
|
|
protected short fDepth; |
|
|
|
|
|
public ElementStack() { |
|
fElements = new ElementState[10]; |
|
|
|
for (int i = 0; i < fElements.length; i++) { |
|
fElements[i] = new ElementState(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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++]; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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++]; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public ElementState pop() { |
|
return fElements[--fDepth]; |
|
} |
|
|
|
|
|
public void clear() { |
|
fDepth = 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public ElementState peek() { |
|
return fElements[fDepth - 1]; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean empty() { |
|
return (fDepth > 0) ? false : true; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
class Attribute extends QName { |
|
String value; |
|
|
|
Attribute(String value) { |
|
super(); |
|
this.value = value; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
class NamespaceContextImpl implements NamespaceContext { |
|
|
|
NamespaceContext userContext = null; |
|
|
|
|
|
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 Iterator<String> getPrefixes(String uri) { |
|
List<String> prefixes = null; |
|
Iterator<String> 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 = 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 -------------------------------------------------- |
|
|
|
@Override |
|
public int size() { |
|
return 1; |
|
} |
|
|
|
@Override |
|
public boolean isEmpty() { |
|
return false; |
|
} |
|
|
|
@Override |
|
public boolean containsKey(Object key) { |
|
return key.equals(OUTPUTSTREAM_PROPERTY); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public Object get(Object key) { |
|
if (key.equals(OUTPUTSTREAM_PROPERTY)) { |
|
return fOutputStream; |
|
} |
|
return null; |
|
} |
|
|
|
@Override |
|
public Set<Entry<Object,Object>> entrySet() { |
|
throw new UnsupportedOperationException(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String toString() { |
|
return getClass().getName() + "@" + Integer.toHexString(hashCode()); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int hashCode() { |
|
return fElementStack.hashCode(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean equals(Object obj) { |
|
return (this == obj); |
|
} |
|
} |