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 {
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) {
} 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 );
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 );
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);
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;