Path: blob/aarch64-shenandoah-jdk8u272-b10/jaxp/src/com/sun/xml/internal/stream/writers/XMLStreamWriterImpl.java
48527 views
/*1* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.xml.internal.stream.writers;2627import java.io.FileOutputStream;28import java.io.IOException;29import java.io.OutputStream;30import java.io.OutputStreamWriter;31import java.io.Writer;32import java.nio.charset.Charset;33import java.nio.charset.CharsetEncoder;34import java.util.AbstractMap;35import java.util.ArrayList;36import java.util.HashMap;37import java.util.Iterator;38import java.util.Random;39import java.util.Vector;40import java.util.Set;41import java.util.Iterator;4243import javax.xml.XMLConstants;44import javax.xml.namespace.NamespaceContext;45import javax.xml.stream.XMLOutputFactory;46import javax.xml.stream.XMLStreamConstants;47import javax.xml.stream.XMLStreamException;48import javax.xml.stream.XMLStreamWriter;49import javax.xml.transform.stream.StreamResult;5051import com.sun.org.apache.xerces.internal.impl.Constants;52import com.sun.org.apache.xerces.internal.impl.PropertyManager;53import com.sun.org.apache.xerces.internal.util.NamespaceSupport;54import com.sun.org.apache.xerces.internal.util.SymbolTable;55import com.sun.org.apache.xerces.internal.utils.SecuritySupport;56import com.sun.org.apache.xerces.internal.xni.QName;5758import com.sun.xml.internal.stream.util.ReadOnlyIterator;5960/**61* This class implements a StAX XMLStreamWriter. It extends62* <code>AbstractMap</code> in order to support a getter for63* implementation-specific properties. For example, you can get64* the underlying <code>OutputStream</code> by casting an instance65* of this class to <code>Map</code> and calling66* <code>getProperty(OUTPUTSTREAM_PROPERTY)</code>.67*68* @author Neeraj Bajaj69* @author K.Venugopal70* @author [email protected]71* @author [email protected]72*/73public final class XMLStreamWriterImpl extends AbstractMap implements XMLStreamWriter {7475public static final String START_COMMENT = "<!--";76public static final String END_COMMENT = "-->";77public static final String DEFAULT_ENCODING = " encoding=\"utf-8\"";78public static final String DEFAULT_XMLDECL = "<?xml version=\"1.0\" ?>";79public static final String DEFAULT_XML_VERSION = "1.0";80public static final char CLOSE_START_TAG = '>';81public static final char OPEN_START_TAG = '<';82public static final String OPEN_END_TAG = "</";83public static final char CLOSE_END_TAG = '>';84public static final String START_CDATA = "<![CDATA[";85public static final String END_CDATA = "]]>";86public static final String CLOSE_EMPTY_ELEMENT = "/>";87public static final String SPACE = " ";88public static final String UTF_8 = "UTF-8";8990public static final String OUTPUTSTREAM_PROPERTY = "sjsxp-outputstream";9192/**93* This flag can be used to turn escaping off for content. It does94* not apply to attribute content.95*/96boolean fEscapeCharacters = true;9798/**99* Flag for the value of repairNamespace property100*/101private boolean fIsRepairingNamespace = false;102103/**104* Underlying Writer to which characters are written.105*/106private Writer fWriter;107108/**109* Underlying OutputStream to which <code>fWriter</code>110* writes to. May be null if unknown.111*/112private OutputStream fOutputStream = null;113114/**115* Collects attributes when the writer is in reparing mode.116*/117private ArrayList fAttributeCache;118119/**120* Collects namespace declarations when the writer is in reparing mode.121*/122private ArrayList fNamespaceDecls;123124/**125* Namespace context encapsulating user specified context126* and context built by the writer127*/128private NamespaceContextImpl fNamespaceContext = null;129130private NamespaceSupport fInternalNamespaceContext = null;131132private Random fPrefixGen = null;133134/**135* Reference to PropertyManager136*/137private PropertyManager fPropertyManager = null;138139/**140* Flag to track if start tag is opened141*/142private boolean fStartTagOpened = false;143144/**145* Boolean flag to indicate, if instance can be reused146*/147private boolean fReuse;148149private SymbolTable fSymbolTable = new SymbolTable();150151private ElementStack fElementStack = new ElementStack(); //Change this .-Venu152153final private String DEFAULT_PREFIX = fSymbolTable.addSymbol("");154155private final ReadOnlyIterator fReadOnlyIterator = new ReadOnlyIterator();156157/**158* In some cases, this charset encoder is used to determine if a char is159* encodable by underlying writer. For example, an 8-bit char from the160* extended ASCII set is not encodable by 7-bit ASCII encoder. Unencodable161* chars are escaped using XML numeric entities.162*/163private CharsetEncoder fEncoder = null;164165/**166* This is used to hold the namespace for attributes which happen to have167* the same uri as the default namespace; It's added to avoid changing the168* current impl. which has many redundant code for the repair mode169*/170HashMap fAttrNamespace = null;171172/**173* Creates a new instance of XMLStreamWriterImpl. Uses platform's default174* encoding.175*176* @param outputStream Underlying stream to write the bytes to177* @param props Properties used by this writer178*/179public XMLStreamWriterImpl(OutputStream outputStream, PropertyManager props)180throws IOException {181182// cannot call this(outputStream, null, props); for constructor,183// OutputStreamWriter charsetName cannot be null184185// use default encoding186this(new OutputStreamWriter(outputStream), props);187}188189/**190* Creates a new instance of XMLStreamWriterImpl.191*192* @param outputStream Underlying stream to write the bytes193* @param encoding Encoding used to convert chars into bytes194* @param props Properties used by this writer195*/196public XMLStreamWriterImpl(OutputStream outputStream, String encoding,197PropertyManager props) throws java.io.IOException {198this(new StreamResult(outputStream), encoding, props);199}200201/**202* Creates a new instance of XMLStreamWriterImpl using a Writer.203*204* @param writer Underlying writer to which chars are written205* @param props Properties used by this writer206*/207public XMLStreamWriterImpl(Writer writer, PropertyManager props)208throws java.io.IOException {209this(new StreamResult(writer), null, props);210}211212/**213* Creates a new instance of XMLStreamWriterImpl using a StreamResult.214* A StreamResult encasupates an OutputStream, a Writer or a SystemId.215*216* @param writer Underlying writer to which chars are written217* @param props Properties used by this writer218*/219public XMLStreamWriterImpl(StreamResult sr, String encoding,220PropertyManager props) throws java.io.IOException {221setOutput(sr, encoding);222fPropertyManager = props;223init();224}225226/**227* Initialize an instance of this XMLStreamWriter. Allocate new instances228* for all the data structures. Set internal flags based on property values.229*/230private void init() {231fReuse = false;232fNamespaceDecls = new ArrayList();233fPrefixGen = new Random();234fAttributeCache = new ArrayList();235fInternalNamespaceContext = new NamespaceSupport();236fInternalNamespaceContext.reset();237fNamespaceContext = new NamespaceContextImpl();238fNamespaceContext.internalContext = fInternalNamespaceContext;239240// Set internal state based on property values241Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);242fIsRepairingNamespace = ob.booleanValue();243ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS);244setEscapeCharacters(ob.booleanValue());245}246247/**248* Reset this instance so that it can be re-used. Do not read properties249* again. The method <code>setOutput(StreamResult, encoding)</code> must250* be called after this one.251*/252public void reset() {253reset(false);254}255256/**257* Reset this instance so that it can be re-used. Clears but does not258* re-allocate internal data structures.259*260* @param resetProperties Indicates if properties should be read again261*/262void reset(boolean resetProperties) {263if (!fReuse) {264throw new java.lang.IllegalStateException(265"close() Must be called before calling reset()");266}267268fReuse = false;269fNamespaceDecls.clear();270fAttributeCache.clear();271272// reset Element/NamespaceContext stacks273fElementStack.clear();274fInternalNamespaceContext.reset();275276fStartTagOpened = false;277fNamespaceContext.userContext = null;278279if (resetProperties) {280Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);281fIsRepairingNamespace = ob.booleanValue();282ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS);283setEscapeCharacters(ob.booleanValue());284}285}286287/**288* Use a StreamResult to initialize the output for this XMLStreamWriter. Check289* for OutputStream, Writer and then systemId, in that order.290*291* @param sr StreamResult encapsulating output information292* @param encoding Encoding to be used except when a Writer is available293*/294public void setOutput(StreamResult sr, String encoding)295throws IOException {296297if (sr.getOutputStream() != null) {298setOutputUsingStream(sr.getOutputStream(), encoding);299}300else if (sr.getWriter() != null) {301setOutputUsingWriter(sr.getWriter());302}303else if (sr.getSystemId() != null) {304setOutputUsingStream(new FileOutputStream(sr.getSystemId()),305encoding);306}307}308309private void setOutputUsingWriter(Writer writer)310throws IOException311{312fWriter = writer;313314if (writer instanceof OutputStreamWriter) {315String charset = ((OutputStreamWriter) writer).getEncoding();316if (charset != null && !charset.equalsIgnoreCase("utf-8")) {317fEncoder = Charset.forName(charset).newEncoder();318}319}320}321322/**323* Utility method to create a writer when passed an OutputStream. Make324* sure to wrap an <code>OutputStreamWriter</code> using an325* <code>XMLWriter</code> for performance reasons.326*327* @param os Underlying OutputStream328* @param encoding Encoding used to convert chars into bytes329*/330private void setOutputUsingStream(OutputStream os, String encoding)331throws IOException {332fOutputStream = os;333334if (encoding != null) {335if (encoding.equalsIgnoreCase("utf-8")) {336fWriter = new UTF8OutputStreamWriter(os);337}338else {339fWriter = new XMLWriter(new OutputStreamWriter(os, encoding));340fEncoder = Charset.forName(encoding).newEncoder();341}342} else {343encoding = SecuritySupport.getSystemProperty("file.encoding");344if (encoding != null && encoding.equalsIgnoreCase("utf-8")) {345fWriter = new UTF8OutputStreamWriter(os);346} else {347fWriter = new XMLWriter(new OutputStreamWriter(os));348}349}350}351352/** Can this instance be reused353*354* @return boolean boolean value to indicate if this instance can be reused or not355*/356public boolean canReuse() {357return fReuse;358}359360public void setEscapeCharacters(boolean escape) {361fEscapeCharacters = escape;362}363364public boolean getEscapeCharacters() {365return fEscapeCharacters;366}367368/**369* Close this XMLStreamWriter by closing underlying writer.370*/371public void close() throws XMLStreamException {372if (fWriter != null) {373try {374//fWriter.close();375fWriter.flush();376} catch (IOException e) {377throw new XMLStreamException(e);378}379}380fWriter = null;381fOutputStream = null;382fNamespaceDecls.clear();383fAttributeCache.clear();384fElementStack.clear();385fInternalNamespaceContext.reset();386fReuse = true;387fStartTagOpened = false;388fNamespaceContext.userContext = null;389}390391/**392* Flush this XMLStreamWriter by flushin underlying writer.393*/394public void flush() throws XMLStreamException {395try {396fWriter.flush();397} catch (IOException e) {398throw new XMLStreamException(e);399}400}401402/**403* Return <code>NamespaceContext</code> being used by the writer.404*405* @return NamespaceContext406*/407public NamespaceContext getNamespaceContext() {408return fNamespaceContext;409}410411/**412* Return a prefix associated with specified uri, or null if the413* uri is unknown.414*415* @param uri The namespace uri416* @throws XMLStreamException if uri specified is "" or null417*/418public String getPrefix(String uri) throws XMLStreamException {419return fNamespaceContext.getPrefix(uri);420}421422/**423* Returns value associated with the specified property name.424*425* @param str Property name426* @throws IllegalArgumentException if the specified property is not supported427* @return value associated with the specified property.428*/429public Object getProperty(String str)430throws IllegalArgumentException {431if (str == null) {432throw new NullPointerException();433}434435if (!fPropertyManager.containsProperty(str)) {436throw new IllegalArgumentException("Property '" + str +437"' is not supported");438}439440return fPropertyManager.getProperty(str);441}442443/**444* Set the specified URI as default namespace in the current namespace context.445*446* @param uri Namespace URI447*/448public void setDefaultNamespace(String uri) throws XMLStreamException {449if (uri != null) {450uri = fSymbolTable.addSymbol(uri);451}452453if (fIsRepairingNamespace) {454if (isDefaultNamespace(uri)) {455return;456}457458QName qname = new QName();459qname.setValues(DEFAULT_PREFIX, "xmlns", null, uri);460fNamespaceDecls.add(qname);461} else {462fInternalNamespaceContext.declarePrefix(DEFAULT_PREFIX, uri);463}464}465466/**467* Sets the current <code>NamespaceContext</code> for prefix and uri bindings.468* This context becomes the root namespace context for writing and469* will replace the current root namespace context. Subsequent calls470* to setPrefix and setDefaultNamespace will bind namespaces using471* the context passed to the method as the root context for resolving472* namespaces. This method may only be called once at the start of the473* document. It does not cause the namespaces to be declared. If a474* namespace URI to prefix mapping is found in the namespace context475* it is treated as declared and the prefix may be used by the476* <code>XMLStreamWriter</code>.477*478* @param namespaceContext the namespace context to use for this writer, may not be null479* @throws XMLStreamException480*/481public void setNamespaceContext(NamespaceContext namespaceContext)482throws XMLStreamException {483fNamespaceContext.userContext = namespaceContext;484}485486/**487* Sets the prefix the uri is bound to. This prefix is bound in the scope of488* the current START_ELEMENT / END_ELEMENT pair. If this method is called before489* a START_ELEMENT has been written the prefix is bound in the root scope.490*491* @param prefix492* @param uri493* @throws XMLStreamException494*/495public void setPrefix(String prefix, String uri) throws XMLStreamException {496497if (prefix == null) {498throw new XMLStreamException("Prefix cannot be null");499}500501if (uri == null) {502throw new XMLStreamException("URI cannot be null");503}504505prefix = fSymbolTable.addSymbol(prefix);506uri = fSymbolTable.addSymbol(uri);507508if (fIsRepairingNamespace) {509String tmpURI = fInternalNamespaceContext.getURI(prefix);510511if ((tmpURI != null) && (tmpURI == uri)) {512return;513}514515if(checkUserNamespaceContext(prefix,uri))516return;517QName qname = new QName();518qname.setValues(prefix,XMLConstants.XMLNS_ATTRIBUTE, null,uri);519fNamespaceDecls.add(qname);520521return;522}523524fInternalNamespaceContext.declarePrefix(prefix, uri);525}526527public void writeAttribute(String localName, String value)528throws XMLStreamException {529try {530if (!fStartTagOpened) {531throw new XMLStreamException(532"Attribute not associated with any element");533}534535if (fIsRepairingNamespace) {536Attribute attr = new Attribute(value); // Revisit:Dont create new one's. Reuse.-Venu537attr.setValues(null, localName, null, null);538fAttributeCache.add(attr);539540return;541}542543fWriter.write(" ");544fWriter.write(localName);545fWriter.write("=\"");546writeXMLContent(547value,548true, // true = escapeChars549true); // true = escapeDoubleQuotes550fWriter.write("\"");551} catch (IOException e) {552throw new XMLStreamException(e);553}554}555556public void writeAttribute(String namespaceURI, String localName,557String value) throws XMLStreamException {558try {559if (!fStartTagOpened) {560throw new XMLStreamException(561"Attribute not associated with any element");562}563564if (namespaceURI == null) {565throw new XMLStreamException("NamespaceURI cannot be null");566}567568namespaceURI = fSymbolTable.addSymbol(namespaceURI);569570String prefix = fInternalNamespaceContext.getPrefix(namespaceURI);571572if (!fIsRepairingNamespace) {573if (prefix == null) {574throw new XMLStreamException("Prefix cannot be null");575}576577writeAttributeWithPrefix(prefix, localName, value);578} else {579Attribute attr = new Attribute(value);580attr.setValues(null, localName, null, namespaceURI);581fAttributeCache.add(attr);582}583} catch (IOException e) {584throw new XMLStreamException(e);585}586}587588private void writeAttributeWithPrefix(String prefix, String localName,589String value) throws IOException {590fWriter.write(SPACE);591592if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {593fWriter.write(prefix);594fWriter.write(":");595}596597fWriter.write(localName);598fWriter.write("=\"");599writeXMLContent(value,600true, // true = escapeChars601true); // true = escapeDoubleQuotes602fWriter.write("\"");603}604605public void writeAttribute(String prefix, String namespaceURI,606String localName, String value) throws XMLStreamException {607try {608if (!fStartTagOpened) {609throw new XMLStreamException(610"Attribute not associated with any element");611}612613if (namespaceURI == null) {614throw new XMLStreamException("NamespaceURI cannot be null");615}616617if (localName == null) {618throw new XMLStreamException("Local name cannot be null");619}620621if (!fIsRepairingNamespace) {622if (prefix == null || prefix.equals("")){623if (!namespaceURI.equals("")) {624throw new XMLStreamException("prefix cannot be null or empty");625} else {626writeAttributeWithPrefix(null, localName, value);627return;628}629}630631if (!prefix.equals(XMLConstants.XML_NS_PREFIX) || !namespaceURI.equals(XMLConstants.XML_NS_URI)) {632633prefix = fSymbolTable.addSymbol(prefix);634namespaceURI = fSymbolTable.addSymbol(namespaceURI);635636if (fInternalNamespaceContext.containsPrefixInCurrentContext(prefix)){637638String tmpURI = fInternalNamespaceContext.getURI(prefix);639640if (tmpURI != null && tmpURI != namespaceURI){641throw new XMLStreamException("Prefix "+prefix+" is " +642"already bound to "+tmpURI+643". Trying to rebind it to "+namespaceURI+" is an error.");644}645}646fInternalNamespaceContext.declarePrefix(prefix, namespaceURI);647}648writeAttributeWithPrefix(prefix, localName, value);649} else {650if (prefix != null) {651prefix = fSymbolTable.addSymbol(prefix);652}653654namespaceURI = fSymbolTable.addSymbol(namespaceURI);655656Attribute attr = new Attribute(value);657attr.setValues(prefix, localName, null, namespaceURI);658fAttributeCache.add(attr);659}660} catch (IOException e) {661throw new XMLStreamException(e);662}663}664665public void writeCData(String cdata) throws XMLStreamException {666try {667if (cdata == null) {668throw new XMLStreamException("cdata cannot be null");669}670671if (fStartTagOpened) {672closeStartTag();673}674675fWriter.write(START_CDATA);676fWriter.write(cdata);677fWriter.write(END_CDATA);678} catch (IOException e) {679throw new XMLStreamException(e);680}681}682683public void writeCharacters(String data) throws XMLStreamException {684try {685if (fStartTagOpened) {686closeStartTag();687}688689writeXMLContent(data);690} catch (IOException e) {691throw new XMLStreamException(e);692}693}694695public void writeCharacters(char[] data, int start, int len)696throws XMLStreamException {697try {698if (fStartTagOpened) {699closeStartTag();700}701702writeXMLContent(data, start, len, fEscapeCharacters);703} catch (IOException e) {704throw new XMLStreamException(e);705}706}707708public void writeComment(String comment) throws XMLStreamException {709try {710if (fStartTagOpened) {711closeStartTag();712}713714fWriter.write(START_COMMENT);715716if (comment != null) {717fWriter.write(comment);718}719720fWriter.write(END_COMMENT);721} catch (IOException e) {722throw new XMLStreamException(e);723}724}725726public void writeDTD(String dtd) throws XMLStreamException {727try {728if (fStartTagOpened) {729closeStartTag();730}731732fWriter.write(dtd);733} catch (IOException e) {734throw new XMLStreamException(e);735}736}737738/*739* Write default Namespace.740*741* If namespaceURI == null,742* then it is assumed to be equivilent to {@link XMLConstants.NULL_NS_URI},743* i.e. there is no Namespace.744*745* @param namespaceURI NamespaceURI to declare.746*747* @throws XMLStreamException748*749* @see <a href="http://www.w3.org/TR/REC-xml-names/#defaulting">750* Namespaces in XML, 5.2 Namespace Defaulting</a>751*/752public void writeDefaultNamespace(String namespaceURI)753throws XMLStreamException {754755// normalize namespaceURI756String namespaceURINormalized = null;757if (namespaceURI == null) {758namespaceURINormalized = ""; // XMLConstants.NULL_NS_URI759} else {760namespaceURINormalized = namespaceURI;761}762763try {764if (!fStartTagOpened) {765throw new IllegalStateException(766"Namespace Attribute not associated with any element");767}768769if (fIsRepairingNamespace) {770QName qname = new QName();771qname.setValues(XMLConstants.DEFAULT_NS_PREFIX,772XMLConstants.XMLNS_ATTRIBUTE, null, namespaceURINormalized);773fNamespaceDecls.add(qname);774775return;776}777778namespaceURINormalized = fSymbolTable.addSymbol(namespaceURINormalized);779780if (fInternalNamespaceContext.containsPrefixInCurrentContext("")){781782String tmp = fInternalNamespaceContext.getURI("");783784if (tmp != null && tmp != namespaceURINormalized) {785throw new XMLStreamException(786"xmlns has been already bound to " +tmp +787". Rebinding it to "+ namespaceURINormalized +788" is an error");789}790}791fInternalNamespaceContext.declarePrefix("", namespaceURINormalized);792793// use common namespace code with a prefix == null for xmlns="..."794writenamespace(null, namespaceURINormalized);795} catch (IOException e) {796throw new XMLStreamException(e);797}798}799800public void writeEmptyElement(String localName) throws XMLStreamException {801try {802if (fStartTagOpened) {803closeStartTag();804}805806openStartTag();807fElementStack.push(null, localName, null, null, true);808fInternalNamespaceContext.pushContext();809810if (!fIsRepairingNamespace) {811fWriter.write(localName);812}813} catch (IOException e) {814throw new XMLStreamException(e);815}816}817818public void writeEmptyElement(String namespaceURI, String localName)819throws XMLStreamException {820if (namespaceURI == null) {821throw new XMLStreamException("NamespaceURI cannot be null");822}823824namespaceURI = fSymbolTable.addSymbol(namespaceURI);825826String prefix = fNamespaceContext.getPrefix(namespaceURI);827writeEmptyElement(prefix, localName, namespaceURI);828}829830public void writeEmptyElement(String prefix, String localName,831String namespaceURI) throws XMLStreamException {832try {833if (localName == null) {834throw new XMLStreamException("Local Name cannot be null");835}836837if (namespaceURI == null) {838throw new XMLStreamException("NamespaceURI cannot be null");839}840841if (prefix != null) {842prefix = fSymbolTable.addSymbol(prefix);843}844845namespaceURI = fSymbolTable.addSymbol(namespaceURI);846847if (fStartTagOpened) {848closeStartTag();849}850851openStartTag();852853fElementStack.push(prefix, localName, null, namespaceURI, true);854fInternalNamespaceContext.pushContext();855856if (!fIsRepairingNamespace) {857if (prefix == null) {858throw new XMLStreamException("NamespaceURI " +859namespaceURI + " has not been bound to any prefix");860}861} else {862return;863}864865if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {866fWriter.write(prefix);867fWriter.write(":");868}869870fWriter.write(localName);871} catch (IOException e) {872throw new XMLStreamException(e);873}874}875876public void writeEndDocument() throws XMLStreamException {877try {878if (fStartTagOpened) {879closeStartTag();880}881882ElementState elem = null;883884while (!fElementStack.empty()) {885elem = (ElementState) fElementStack.pop();886fInternalNamespaceContext.popContext();887888if (elem.isEmpty) {889//fWriter.write(CLOSE_EMPTY_ELEMENT);890} else {891fWriter.write(OPEN_END_TAG);892893if ((elem.prefix != null) && !(elem.prefix).equals("")) {894fWriter.write(elem.prefix);895fWriter.write(":");896}897898fWriter.write(elem.localpart);899fWriter.write(CLOSE_END_TAG);900}901}902} catch (IOException e) {903throw new XMLStreamException(e);904} catch (ArrayIndexOutOfBoundsException e) {905throw new XMLStreamException("No more elements to write");906}907}908909public void writeEndElement() throws XMLStreamException {910try {911if (fStartTagOpened) {912closeStartTag();913}914915ElementState currentElement = (ElementState) fElementStack.pop();916917if (currentElement == null) {918throw new XMLStreamException("No element was found to write");919}920921if (currentElement.isEmpty) {922//fWriter.write(CLOSE_EMPTY_ELEMENT);923return;924}925926fWriter.write(OPEN_END_TAG);927928if ((currentElement.prefix != null) &&929!(currentElement.prefix).equals("")) {930fWriter.write(currentElement.prefix);931fWriter.write(":");932}933934fWriter.write(currentElement.localpart);935fWriter.write(CLOSE_END_TAG);936fInternalNamespaceContext.popContext();937} catch (IOException e) {938throw new XMLStreamException(e);939} catch (ArrayIndexOutOfBoundsException e) {940throw new XMLStreamException(941"No element was found to write: "942+ e.toString(), e);943}944}945946public void writeEntityRef(String refName) throws XMLStreamException {947try {948if (fStartTagOpened) {949closeStartTag();950}951952fWriter.write('&');953fWriter.write(refName);954fWriter.write(';');955} catch (IOException e) {956throw new XMLStreamException(e);957}958}959960/**961* Write a Namespace declaration.962*963* If namespaceURI == null,964* then it is assumed to be equivilent to {@link XMLConstants.NULL_NS_URI},965* i.e. there is no Namespace.966*967* @param prefix Prefix to bind.968* @param namespaceURI NamespaceURI to declare.969*970* @throws XMLStreamException971*972* @see <a href="http://www.w3.org/TR/REC-xml-names/#defaulting">973* Namespaces in XML, 5.2 Namespace Defaulting</a>974*/975public void writeNamespace(String prefix, String namespaceURI)976throws XMLStreamException {977978// normalize namespaceURI979String namespaceURINormalized = null;980if (namespaceURI == null) {981namespaceURINormalized = ""; // XMLConstants.NULL_NS_URI982} else {983namespaceURINormalized = namespaceURI;984}985986try {987QName qname = null;988989if (!fStartTagOpened) {990throw new IllegalStateException(991"Invalid state: start tag is not opened at writeNamespace("992+ prefix993+ ", "994+ namespaceURINormalized995+ ")");996}997998// is this the default Namespace?999if (prefix == null1000|| prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)1001|| prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {1002writeDefaultNamespace(namespaceURINormalized);1003return;1004}10051006if (prefix.equals(XMLConstants.XML_NS_PREFIX) && namespaceURINormalized.equals(XMLConstants.XML_NS_URI))1007return;10081009prefix = fSymbolTable.addSymbol(prefix);1010namespaceURINormalized = fSymbolTable.addSymbol(namespaceURINormalized);10111012if (fIsRepairingNamespace) {1013String tmpURI = fInternalNamespaceContext.getURI(prefix);10141015if ((tmpURI != null) && (tmpURI == namespaceURINormalized)) {1016return;1017}10181019qname = new QName();1020qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null,1021namespaceURINormalized);1022fNamespaceDecls.add(qname);10231024return;1025}102610271028if (fInternalNamespaceContext.containsPrefixInCurrentContext(prefix)){10291030String tmp = fInternalNamespaceContext.getURI(prefix);10311032if (tmp != null && tmp != namespaceURINormalized) {10331034throw new XMLStreamException("prefix "+prefix+1035" has been already bound to " +tmp +1036". Rebinding it to "+ namespaceURINormalized+1037" is an error");1038}1039}10401041fInternalNamespaceContext.declarePrefix(prefix, namespaceURINormalized);1042writenamespace(prefix, namespaceURINormalized);10431044} catch (IOException e) {1045throw new XMLStreamException(e);1046}1047}10481049private void writenamespace(String prefix, String namespaceURI)1050throws IOException {1051fWriter.write(" xmlns");10521053if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {1054fWriter.write(":");1055fWriter.write(prefix);1056}10571058fWriter.write("=\"");1059writeXMLContent(1060namespaceURI,1061true, // true = escapeChars1062true); // true = escapeDoubleQuotes1063fWriter.write("\"");1064}10651066public void writeProcessingInstruction(String target)1067throws XMLStreamException {1068try {1069if (fStartTagOpened) {1070closeStartTag();1071}10721073if (target != null) {1074fWriter.write("<?");1075fWriter.write(target);1076fWriter.write("?>");10771078return;1079}1080} catch (IOException e) {1081throw new XMLStreamException(e);1082}10831084throw new XMLStreamException("PI target cannot be null");1085}10861087/**1088* @param target1089* @param data1090* @throws XMLStreamException1091*/1092public void writeProcessingInstruction(String target, String data)1093throws XMLStreamException {1094try {1095if (fStartTagOpened) {1096closeStartTag();1097}10981099if ((target == null) || (data == null)) {1100throw new XMLStreamException("PI target cannot be null");1101}11021103fWriter.write("<?");1104fWriter.write(target);1105fWriter.write(SPACE);1106fWriter.write(data);1107fWriter.write("?>");1108} catch (IOException e) {1109throw new XMLStreamException(e);1110}1111}11121113/**1114* @throws XMLStreamException1115*/1116public void writeStartDocument() throws XMLStreamException {1117try {1118fWriter.write(DEFAULT_XMLDECL);1119} catch (IOException ex) {1120throw new XMLStreamException(ex);1121}1122}11231124/**1125* @param version1126* @throws XMLStreamException1127*/1128public void writeStartDocument(String version) throws XMLStreamException {1129try {1130if ((version == null) || version.equals("")) {1131writeStartDocument();11321133return;1134}11351136fWriter.write("<?xml version=\"");1137fWriter.write(version);1138fWriter.write("\"");11391140//fWriter.write(DEFAULT_ENCODING);1141fWriter.write("?>");1142} catch (IOException ex) {1143throw new XMLStreamException(ex);1144}1145}11461147/**1148* @param encoding1149* @param version1150* @throws XMLStreamException1151*/1152public void writeStartDocument(String encoding, String version)1153throws XMLStreamException {1154//Revisit : What about standalone ?1155try {1156if ((encoding == null) && (version == null)) {1157writeStartDocument();11581159return;1160}11611162if (encoding == null) {1163writeStartDocument(version);11641165return;1166}11671168String streamEncoding = null;1169if (fWriter instanceof OutputStreamWriter) {1170streamEncoding = ((OutputStreamWriter) fWriter).getEncoding();1171}1172else if (fWriter instanceof UTF8OutputStreamWriter) {1173streamEncoding = ((UTF8OutputStreamWriter) fWriter).getEncoding();1174}1175else if (fWriter instanceof XMLWriter) {1176streamEncoding = ((OutputStreamWriter) ((XMLWriter)fWriter).getWriter()).getEncoding();1177}11781179if (streamEncoding != null && !streamEncoding.equalsIgnoreCase(encoding)) {1180// If the equality check failed, check for charset encoding aliases1181boolean foundAlias = false;1182Set aliases = Charset.forName(encoding).aliases();1183for (Iterator it = aliases.iterator(); !foundAlias && it.hasNext(); ) {1184if (streamEncoding.equalsIgnoreCase((String) it.next())) {1185foundAlias = true;1186}1187}1188// If no alias matches the encoding name, then report error1189if (!foundAlias) {1190throw new XMLStreamException("Underlying stream encoding '"1191+ streamEncoding1192+ "' and input paramter for writeStartDocument() method '"1193+ encoding + "' do not match.");1194}1195}119611971198fWriter.write("<?xml version=\"");11991200if ((version == null) || version.equals("")) {1201fWriter.write(DEFAULT_XML_VERSION);1202} else {1203fWriter.write(version);1204}12051206if (!encoding.equals("")) {1207fWriter.write("\" encoding=\"");1208fWriter.write(encoding);1209}12101211fWriter.write("\"?>");1212} catch (IOException ex) {1213throw new XMLStreamException(ex);1214}1215}12161217/**1218* @param localName1219* @throws XMLStreamException1220*/1221public void writeStartElement(String localName) throws XMLStreamException {1222try {1223if (localName == null) {1224throw new XMLStreamException("Local Name cannot be null");1225}12261227if (fStartTagOpened) {1228closeStartTag();1229}12301231openStartTag();1232fElementStack.push(null, localName, null, null, false);1233fInternalNamespaceContext.pushContext();12341235if (fIsRepairingNamespace) {1236return;1237}12381239fWriter.write(localName);1240} catch (IOException ex) {1241throw new XMLStreamException(ex);1242}1243}12441245/**1246* @param namespaceURI1247* @param localName1248* @throws XMLStreamException1249*/1250public void writeStartElement(String namespaceURI, String localName)1251throws XMLStreamException {1252if (localName == null) {1253throw new XMLStreamException("Local Name cannot be null");1254}12551256if (namespaceURI == null) {1257throw new XMLStreamException("NamespaceURI cannot be null");1258}12591260namespaceURI = fSymbolTable.addSymbol(namespaceURI);12611262String prefix = null;12631264if (!fIsRepairingNamespace) {1265prefix = fNamespaceContext.getPrefix(namespaceURI);12661267if (prefix != null) {1268prefix = fSymbolTable.addSymbol(prefix);1269}1270}12711272writeStartElement(prefix, localName, namespaceURI);1273}12741275/**1276* @param prefix1277* @param localName1278* @param namespaceURI1279* @throws XMLStreamException1280*/1281public void writeStartElement(String prefix, String localName,1282String namespaceURI) throws XMLStreamException {1283try {1284if (localName == null) {1285throw new XMLStreamException("Local Name cannot be null");1286}12871288if (namespaceURI == null) {1289throw new XMLStreamException("NamespaceURI cannot be null");1290}12911292if (!fIsRepairingNamespace) {1293if (prefix == null) {1294throw new XMLStreamException("Prefix cannot be null");1295}1296}12971298if (fStartTagOpened) {1299closeStartTag();1300}13011302openStartTag();1303namespaceURI = fSymbolTable.addSymbol(namespaceURI);13041305if (prefix != null) {1306prefix = fSymbolTable.addSymbol(prefix);1307}13081309fElementStack.push(prefix, localName, null, namespaceURI, false);1310fInternalNamespaceContext.pushContext();13111312String tmpPrefix = fNamespaceContext.getPrefix(namespaceURI);131313141315if ((prefix != null) &&1316((tmpPrefix == null) || !prefix.equals(tmpPrefix))) {1317fInternalNamespaceContext.declarePrefix(prefix, namespaceURI);13181319}13201321if (fIsRepairingNamespace) {1322if ((prefix == null) ||1323((tmpPrefix != null) && prefix.equals(tmpPrefix))) {1324return;1325}13261327QName qname = new QName();1328qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null,1329namespaceURI);1330fNamespaceDecls.add(qname);13311332return;1333}13341335if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {1336fWriter.write(prefix);1337fWriter.write(":");1338}13391340fWriter.write(localName);13411342} catch (IOException ex) {1343throw new XMLStreamException(ex);1344}1345}13461347/**1348* Writes character reference in hex format.1349*/1350private void writeCharRef(int codePoint) throws IOException {1351fWriter.write( "&#x" );1352fWriter.write( Integer.toHexString(codePoint) );1353fWriter.write( ';' );1354}13551356/**1357* Writes XML content to underlying writer. Escapes characters unless1358* escaping character feature is turned off.1359*/1360private void writeXMLContent(char[] content, int start, int length,1361boolean escapeChars) throws IOException {1362if (!escapeChars) {1363fWriter.write(content, start, length);13641365return;1366}13671368// Index of the next char to be written1369int startWritePos = start;13701371final int end = start + length;13721373for (int index = start; index < end; index++) {1374char ch = content[index];13751376if (fEncoder != null && !fEncoder.canEncode(ch)){1377fWriter.write(content, startWritePos, index - startWritePos );13781379// Check if current and next characters forms a surrogate pair1380// and escape it to avoid generation of invalid xml content1381if ( index != end - 1 && Character.isSurrogatePair(ch, content[index+1])) {1382writeCharRef(Character.toCodePoint(ch, content[index+1]));1383index++;1384} else {1385writeCharRef(ch);1386}1387startWritePos = index + 1;1388continue;1389}13901391switch (ch) {1392case '<':1393fWriter.write(content, startWritePos, index - startWritePos);1394fWriter.write("<");1395startWritePos = index + 1;13961397break;13981399case '&':1400fWriter.write(content, startWritePos, index - startWritePos);1401fWriter.write("&");1402startWritePos = index + 1;14031404break;14051406case '>':1407fWriter.write(content, startWritePos, index - startWritePos);1408fWriter.write(">");1409startWritePos = index + 1;14101411break;1412}1413}14141415// Write any pending data1416fWriter.write(content, startWritePos, end - startWritePos);1417}14181419private void writeXMLContent(String content) throws IOException {1420if ((content != null) && (content.length() > 0)) {1421writeXMLContent(content,1422fEscapeCharacters, // boolean = escapeChars1423false); // false = escapeDoubleQuotes1424}1425}14261427/**1428* Writes XML content to underlying writer. Escapes characters unless1429* escaping character feature is turned off.1430*/1431private void writeXMLContent(1432String content,1433boolean escapeChars,1434boolean escapeDoubleQuotes)1435throws IOException {14361437if (!escapeChars) {1438fWriter.write(content);14391440return;1441}14421443// Index of the next char to be written1444int startWritePos = 0;14451446final int end = content.length();14471448for (int index = 0; index < end; index++) {1449char ch = content.charAt(index);14501451if (fEncoder != null && !fEncoder.canEncode(ch)){1452fWriter.write(content, startWritePos, index - startWritePos );14531454// Check if current and next characters forms a surrogate pair1455// and escape it to avoid generation of invalid xml content1456if ( index != end - 1 && Character.isSurrogatePair(ch, content.charAt(index+1))) {1457writeCharRef(Character.toCodePoint(ch, content.charAt(index+1)));1458index++;1459} else {1460writeCharRef(ch);1461}14621463startWritePos = index + 1;1464continue;1465}14661467switch (ch) {1468case '<':1469fWriter.write(content, startWritePos, index - startWritePos);1470fWriter.write("<");1471startWritePos = index + 1;14721473break;14741475case '&':1476fWriter.write(content, startWritePos, index - startWritePos);1477fWriter.write("&");1478startWritePos = index + 1;14791480break;14811482case '>':1483fWriter.write(content, startWritePos, index - startWritePos);1484fWriter.write(">");1485startWritePos = index + 1;14861487break;14881489case '"':1490fWriter.write(content, startWritePos, index - startWritePos);1491if (escapeDoubleQuotes) {1492fWriter.write(""");1493} else {1494fWriter.write('"');1495}1496startWritePos = index + 1;14971498break;1499}1500}15011502// Write any pending data1503fWriter.write(content, startWritePos, end - startWritePos);1504}15051506/**1507* marks close of start tag and writes the same into the writer.1508*/1509private void closeStartTag() throws XMLStreamException {1510try {1511ElementState currentElement = fElementStack.peek();15121513if (fIsRepairingNamespace) {1514repair();1515correctPrefix(currentElement, XMLStreamConstants.START_ELEMENT);15161517if ((currentElement.prefix != null) &&1518(currentElement.prefix != XMLConstants.DEFAULT_NS_PREFIX)) {1519fWriter.write(currentElement.prefix);1520fWriter.write(":");1521}15221523fWriter.write(currentElement.localpart);15241525int len = fNamespaceDecls.size();1526QName qname = null;15271528for (int i = 0; i < len; i++) {1529qname = (QName) fNamespaceDecls.get(i);15301531if (qname != null) {1532if (fInternalNamespaceContext.declarePrefix(qname.prefix,1533qname.uri)) {1534writenamespace(qname.prefix, qname.uri);1535}1536}1537}15381539fNamespaceDecls.clear();15401541Attribute attr = null;15421543for (int j = 0; j < fAttributeCache.size(); j++) {1544attr = (Attribute) fAttributeCache.get(j);15451546if ((attr.prefix != null) && (attr.uri != null)) {1547if (!attr.prefix.equals("") && !attr.uri.equals("") ) {1548String tmp = fInternalNamespaceContext.getPrefix(attr.uri);15491550if ((tmp == null) || (tmp != attr.prefix)) {1551tmp = getAttrPrefix(attr.uri);1552if (tmp == null) {1553if (fInternalNamespaceContext.declarePrefix(attr.prefix,1554attr.uri)) {1555writenamespace(attr.prefix, attr.uri);1556}1557} else {1558writenamespace(attr.prefix, attr.uri);1559}1560}1561}1562}15631564writeAttributeWithPrefix(attr.prefix, attr.localpart,1565attr.value);1566}1567fAttrNamespace = null;1568fAttributeCache.clear();1569}15701571if (currentElement.isEmpty) {1572fElementStack.pop();1573fInternalNamespaceContext.popContext();1574fWriter.write(CLOSE_EMPTY_ELEMENT);1575} else {1576fWriter.write(CLOSE_START_TAG);1577}15781579fStartTagOpened = false;1580} catch (IOException ex) {1581fStartTagOpened = false;1582throw new XMLStreamException(ex);1583}1584}15851586/**1587* marks open of start tag and writes the same into the writer.1588*/1589private void openStartTag() throws IOException {1590fStartTagOpened = true;1591fWriter.write(OPEN_START_TAG);1592}15931594/**1595*1596* @param uri1597* @return1598*/1599private void correctPrefix(QName attr, int type) {1600String tmpPrefix = null;1601String prefix;1602String uri;1603prefix = attr.prefix;1604uri = attr.uri;1605boolean isSpecialCaseURI = false;16061607if (prefix == null || prefix.equals("")) {1608if (uri == null) {1609return;1610}16111612if (prefix == XMLConstants.DEFAULT_NS_PREFIX && uri == XMLConstants.DEFAULT_NS_PREFIX)1613return;16141615uri = fSymbolTable.addSymbol(uri);16161617QName decl = null;16181619for (int i = 0; i < fNamespaceDecls.size(); i++) {1620decl = (QName) fNamespaceDecls.get(i);16211622if ((decl != null) && (decl.uri == attr.uri)) {1623attr.prefix = decl.prefix;16241625return;1626}1627}16281629tmpPrefix = fNamespaceContext.getPrefix(uri);16301631if (tmpPrefix == XMLConstants.DEFAULT_NS_PREFIX) {1632if (type == XMLStreamConstants.START_ELEMENT) {1633return;1634}1635else if (type == XMLStreamConstants.ATTRIBUTE) {1636//the uri happens to be the same as that of the default namespace1637tmpPrefix = getAttrPrefix(uri);1638isSpecialCaseURI = true;1639}1640}16411642if (tmpPrefix == null) {1643StringBuffer genPrefix = new StringBuffer("zdef");16441645for (int i = 0; i < 1; i++) {1646genPrefix.append(fPrefixGen.nextInt());1647}16481649prefix = genPrefix.toString();1650prefix = fSymbolTable.addSymbol(prefix);1651} else {1652prefix = fSymbolTable.addSymbol(tmpPrefix);1653}16541655if (tmpPrefix == null) {1656if (isSpecialCaseURI) {1657addAttrNamespace(prefix, uri);1658} else {1659QName qname = new QName();1660qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null, uri);1661fNamespaceDecls.add(qname);1662fInternalNamespaceContext.declarePrefix(fSymbolTable.addSymbol(1663prefix), uri);1664}1665}1666}16671668attr.prefix = prefix;1669}16701671/**1672* return the prefix if the attribute has an uri the same as that of the default namespace1673*/1674private String getAttrPrefix(String uri) {1675if (fAttrNamespace != null) {1676return (String)fAttrNamespace.get(uri);1677}1678return null;1679}1680private void addAttrNamespace(String prefix, String uri) {1681if (fAttrNamespace == null) {1682fAttrNamespace = new HashMap();1683}1684fAttrNamespace.put(prefix, uri);1685}1686/**1687* @param uri1688* @return1689*/1690private boolean isDefaultNamespace(String uri) {1691String defaultNamespace = fInternalNamespaceContext.getURI(DEFAULT_PREFIX);16921693if (uri == defaultNamespace) {1694return true;1695}16961697return false;1698}16991700/**1701* @param prefix1702* @param uri1703* @return1704*/1705private boolean checkUserNamespaceContext(String prefix, String uri) {1706if (fNamespaceContext.userContext != null) {1707String tmpURI = fNamespaceContext.userContext.getNamespaceURI(prefix);17081709if ((tmpURI != null) && tmpURI.equals(uri)) {1710return true;1711}1712}17131714return false;1715}17161717/**1718* Correct's namespaces as per requirements of isReparisingNamespace property.1719*/1720protected void repair() {1721Attribute attr = null;1722Attribute attr2 = null;1723ElementState currentElement = fElementStack.peek();1724removeDuplicateDecls();17251726for(int i=0 ; i< fAttributeCache.size();i++){1727attr = (Attribute)fAttributeCache.get(i);1728if((attr.prefix != null && !attr.prefix.equals("")) || (attr.uri != null && !attr.uri.equals(""))) {1729correctPrefix(currentElement,attr);1730}1731}17321733if (!isDeclared(currentElement)) {1734if ((currentElement.prefix != null) &&1735(currentElement.uri != null)) {1736if ((!currentElement.prefix.equals("")) && (!currentElement.uri.equals(""))) {1737fNamespaceDecls.add(currentElement);1738}1739}1740}17411742for(int i=0 ; i< fAttributeCache.size();i++){1743attr = (Attribute)fAttributeCache.get(i);1744for(int j=i+1;j<fAttributeCache.size();j++){1745attr2 = (Attribute)fAttributeCache.get(j);1746if(!"".equals(attr.prefix)&& !"".equals(attr2.prefix)){1747correctPrefix(attr,attr2);1748}1749}1750}17511752repairNamespaceDecl(currentElement);17531754int i = 0;17551756for (i = 0; i < fAttributeCache.size(); i++) {1757attr = (Attribute) fAttributeCache.get(i);1758/* If 'attr' is an attribute and it is in no namespace(which means that prefix="", uri=""), attr's1759namespace should not be redinded. See [http://www.w3.org/TR/REC-xml-names/#defaulting].1760*/1761if (attr.prefix != null && attr.prefix.equals("") && attr.uri != null && attr.uri.equals("")){1762repairNamespaceDecl(attr);1763}1764}17651766QName qname = null;17671768for (i = 0; i < fNamespaceDecls.size(); i++) {1769qname = (QName) fNamespaceDecls.get(i);17701771if (qname != null) {1772fInternalNamespaceContext.declarePrefix(qname.prefix, qname.uri);1773}1774}17751776for (i = 0; i < fAttributeCache.size(); i++) {1777attr = (Attribute) fAttributeCache.get(i);1778correctPrefix(attr, XMLStreamConstants.ATTRIBUTE);1779}1780}17811782/*1783*If element and/or attribute names in the same start or empty-element tag1784*are bound to different namespace URIs and are using the same prefix then1785*the element or the first occurring attribute retains the original prefix1786*and the following attributes have their prefixes replaced with a new prefix1787*that is bound to the namespace URIs of those attributes.1788*/1789void correctPrefix(QName attr1, QName attr2) {1790String tmpPrefix = null;1791QName decl = null;1792boolean done = false;17931794checkForNull(attr1);1795checkForNull(attr2);17961797if(attr1.prefix.equals(attr2.prefix) && !(attr1.uri.equals(attr2.uri))){17981799tmpPrefix = fNamespaceContext.getPrefix(attr2.uri);18001801if (tmpPrefix != null) {1802attr2.prefix = fSymbolTable.addSymbol(tmpPrefix);1803} else {1804decl = null;1805for(int n=0;n<fNamespaceDecls.size();n++){1806decl = (QName)fNamespaceDecls.get(n);1807if(decl != null && (decl.uri == attr2.uri)){1808attr2.prefix = decl.prefix;18091810return;1811}1812}18131814//No namespace mapping found , so declare prefix.1815StringBuffer genPrefix = new StringBuffer("zdef");18161817for (int k = 0; k < 1; k++) {1818genPrefix.append(fPrefixGen.nextInt());1819}18201821tmpPrefix = genPrefix.toString();1822tmpPrefix = fSymbolTable.addSymbol(tmpPrefix);1823attr2.prefix = tmpPrefix;18241825QName qname = new QName();1826qname.setValues(tmpPrefix, XMLConstants.XMLNS_ATTRIBUTE, null,1827attr2.uri);1828fNamespaceDecls.add(qname);1829}1830}1831}18321833void checkForNull(QName attr) {1834if (attr.prefix == null) attr.prefix = XMLConstants.DEFAULT_NS_PREFIX;1835if (attr.uri == null) attr.uri = XMLConstants.DEFAULT_NS_PREFIX;1836}18371838void removeDuplicateDecls(){1839QName decl1,decl2;1840for(int i =0;i<fNamespaceDecls.size();i++){1841decl1 = (QName)fNamespaceDecls.get(i);1842if(decl1!=null) {1843for(int j=i+1;j<fNamespaceDecls.size();j++){1844decl2 = (QName)fNamespaceDecls.get(j);1845// QName.equals relies on identity equality, so we can't use it,1846// because prefixes aren't interned1847if(decl2!=null && decl1.prefix.equals(decl2.prefix) && decl1.uri.equals(decl2.uri))1848fNamespaceDecls.remove(j);1849}1850}1851}1852}18531854/*1855*If an element or attribute name is bound to a prefix and there is a namespace1856*declaration that binds that prefix to a different URI then that namespace declaration1857*is either removed if the correct mapping is inherited from the parent context of that element,1858*or changed to the namespace URI of the element or attribute using that prefix.1859*1860*/1861void repairNamespaceDecl(QName attr) {1862QName decl = null;1863String tmpURI;18641865//check for null prefix.1866for (int j = 0; j < fNamespaceDecls.size(); j++) {1867decl = (QName) fNamespaceDecls.get(j);18681869if (decl != null) {1870if ((attr.prefix != null) &&1871(attr.prefix.equals(decl.prefix) &&1872!(attr.uri.equals(decl.uri)))) {1873tmpURI = fNamespaceContext.getNamespaceURI(attr.prefix);18741875//see if you need to add to symbole table.1876if (tmpURI != null) {1877if (tmpURI.equals(attr.uri)) {1878fNamespaceDecls.set(j, null);1879} else {1880decl.uri = attr.uri;1881}1882}1883}1884}1885}1886}18871888boolean isDeclared(QName attr) {1889QName decl = null;18901891for (int n = 0; n < fNamespaceDecls.size(); n++) {1892decl = (QName) fNamespaceDecls.get(n);18931894if ((attr.prefix != null) &&1895((attr.prefix == decl.prefix) && (decl.uri == attr.uri))) {1896return true;1897}1898}18991900if (attr.uri != null) {1901if (fNamespaceContext.getPrefix(attr.uri) != null) {1902return true;1903}1904}19051906return false;1907}19081909/*1910* Start of Internal classes.1911*1912*/1913protected class ElementStack {1914/** The stack data. */1915protected ElementState[] fElements;19161917/** The size of the stack. */1918protected short fDepth;19191920/** Default constructor. */1921public ElementStack() {1922fElements = new ElementState[10];19231924for (int i = 0; i < fElements.length; i++) {1925fElements[i] = new ElementState();1926}1927}19281929/**1930* Pushes an element on the stack.1931* <p>1932* <strong>Note:</strong> The QName values are copied into the1933* stack. In other words, the caller does <em>not</em> orphan1934* the element to the stack. Also, the QName object returned1935* is <em>not</em> orphaned to the caller. It should be1936* considered read-only.1937*1938* @param element The element to push onto the stack.1939*1940* @return Returns the actual QName object that stores the1941*/1942public ElementState push(ElementState element) {1943if (fDepth == fElements.length) {1944ElementState[] array = new ElementState[fElements.length * 2];1945System.arraycopy(fElements, 0, array, 0, fDepth);1946fElements = array;19471948for (int i = fDepth; i < fElements.length; i++) {1949fElements[i] = new ElementState();1950}1951}19521953fElements[fDepth].setValues(element);19541955return fElements[fDepth++];1956}19571958/**1959*1960* @param prefix1961* @param localpart1962* @param rawname1963* @param uri1964* @param isEmpty1965* @return1966*/1967public ElementState push(String prefix, String localpart,1968String rawname, String uri, boolean isEmpty) {1969if (fDepth == fElements.length) {1970ElementState[] array = new ElementState[fElements.length * 2];1971System.arraycopy(fElements, 0, array, 0, fDepth);1972fElements = array;19731974for (int i = fDepth; i < fElements.length; i++) {1975fElements[i] = new ElementState();1976}1977}19781979fElements[fDepth].setValues(prefix, localpart, rawname, uri, isEmpty);19801981return fElements[fDepth++];1982}19831984/**1985* Pops an element off of the stack by setting the values of1986* the specified QName.1987* <p>1988* <strong>Note:</strong> The object returned is <em>not</em>1989* orphaned to the caller. Therefore, the caller should consider1990* the object to be read-only.1991*/1992public ElementState pop() {1993return fElements[--fDepth];1994}19951996/** Clears the stack without throwing away existing QName objects. */1997public void clear() {1998fDepth = 0;1999}20002001/**2002* This function is as a result of optimization done for endElement --2003* we dont need to set the value for every end element we encouter.2004* For Well formedness checks we can have the same QName object that was pushed.2005* the values will be set only if application need to know about the endElement2006* -- [email protected]2007*/2008public ElementState peek() {2009return fElements[fDepth - 1];2010}20112012/**2013*2014* @return2015*/2016public boolean empty() {2017return (fDepth > 0) ? false : true;2018}2019}20202021/**2022* Maintains element state . localName for now.2023*/2024class ElementState extends QName {2025public boolean isEmpty = false;20262027public ElementState() {}20282029public ElementState(String prefix, String localpart, String rawname,2030String uri) {2031super(prefix, localpart, rawname, uri);2032}20332034public void setValues(String prefix, String localpart, String rawname,2035String uri, boolean isEmpty) {2036super.setValues(prefix, localpart, rawname, uri);2037this.isEmpty = isEmpty;2038}2039}20402041/**2042* Attributes2043*/2044class Attribute extends QName {2045String value;20462047Attribute(String value) {2048super();2049this.value = value;2050}2051}20522053/**2054* Implementation of NamespaceContext .2055*2056*/2057class NamespaceContextImpl implements NamespaceContext {2058//root namespace context set by user.2059NamespaceContext userContext = null;20602061//context built by the writer.2062NamespaceSupport internalContext = null;20632064public String getNamespaceURI(String prefix) {2065String uri = null;20662067if (prefix != null) {2068prefix = fSymbolTable.addSymbol(prefix);2069}20702071if (internalContext != null) {2072uri = internalContext.getURI(prefix);20732074if (uri != null) {2075return uri;2076}2077}20782079if (userContext != null) {2080uri = userContext.getNamespaceURI(prefix);20812082return uri;2083}20842085return null;2086}20872088public String getPrefix(String uri) {2089String prefix = null;20902091if (uri != null) {2092uri = fSymbolTable.addSymbol(uri);2093}20942095if (internalContext != null) {2096prefix = internalContext.getPrefix(uri);20972098if (prefix != null) {2099return prefix;2100}2101}21022103if (userContext != null) {2104return userContext.getPrefix(uri);2105}21062107return null;2108}21092110public java.util.Iterator getPrefixes(String uri) {2111Vector prefixes = null;2112Iterator itr = null;21132114if (uri != null) {2115uri = fSymbolTable.addSymbol(uri);2116}21172118if (userContext != null) {2119itr = userContext.getPrefixes(uri);2120}21212122if (internalContext != null) {2123prefixes = internalContext.getPrefixes(uri);2124}21252126if ((prefixes == null) && (itr != null)) {2127return itr;2128} else if ((prefixes != null) && (itr == null)) {2129return new ReadOnlyIterator(prefixes.iterator());2130} else if ((prefixes != null) && (itr != null)) {2131String ob = null;21322133while (itr.hasNext()) {2134ob = (String) itr.next();21352136if (ob != null) {2137ob = fSymbolTable.addSymbol(ob);2138}21392140if (!prefixes.contains(ob)) {2141prefixes.add(ob);2142}2143}21442145return new ReadOnlyIterator(prefixes.iterator());2146}21472148return fReadOnlyIterator;2149}2150}21512152// -- Map Interface --------------------------------------------------21532154public int size() {2155return 1;2156}21572158public boolean isEmpty() {2159return false;2160}21612162public boolean containsKey(Object key) {2163return key.equals(OUTPUTSTREAM_PROPERTY);2164}21652166/**2167* Returns the value associated to an implementation-specific2168* property.2169*/2170public Object get(Object key) {2171if (key.equals(OUTPUTSTREAM_PROPERTY)) {2172return fOutputStream;2173}2174return null;2175}21762177public java.util.Set entrySet() {2178throw new UnsupportedOperationException();2179}21802181/**2182* Overrides the method defined in AbstractMap which is2183* not completely implemented. Calling toString() in2184* AbstractMap would cause an unsupported exection to2185* be thrown.2186*/2187public String toString() {2188return getClass().getName() + "@" + Integer.toHexString(hashCode());2189}21902191/**2192* Overrides the method defined in AbstractMap2193* This is required by the toString() method2194*/2195public int hashCode() {2196return fElementStack.hashCode();2197}2198/**2199* Overrides the method defined in AbstractMap2200* This is required to satisfy the contract for hashCode.2201*/2202public boolean equals(Object obj) {2203return (this == obj);2204}2205}220622072208