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