1 /*
   2  * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.stream.writers;
  27 
  28 import com.sun.org.apache.xerces.internal.impl.Constants;
  29 import com.sun.org.apache.xerces.internal.impl.PropertyManager;
  30 import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
  31 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  32 import com.sun.org.apache.xerces.internal.xni.QName;
  33 import com.sun.xml.internal.stream.util.ReadOnlyIterator;
  34 import java.io.FileOutputStream;
  35 import java.io.IOException;
  36 import java.io.OutputStream;
  37 import java.io.OutputStreamWriter;
  38 import java.io.Writer;
  39 import java.nio.charset.Charset;
  40 import java.nio.charset.CharsetEncoder;
  41 import java.util.AbstractMap;
  42 import java.util.ArrayList;
  43 import java.util.HashMap;
  44 import java.util.Iterator;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Objects;
  48 import java.util.Random;
  49 import java.util.Set;
  50 import javax.xml.XMLConstants;
  51 import javax.xml.namespace.NamespaceContext;
  52 import javax.xml.stream.XMLOutputFactory;
  53 import javax.xml.stream.XMLStreamConstants;
  54 import javax.xml.stream.XMLStreamException;
  55 import javax.xml.transform.stream.StreamResult;
  56 import jdk.xml.internal.SecuritySupport;
  57 
  58 /**
  59  * This class implements a StAX XMLStreamWriter. It extends
  60  * <code>AbstractMap</code> in order to support a getter for
  61  * implementation-specific properties. For example, you can get
  62  * the underlying <code>OutputStream</code> by casting an instance
  63  * of this class to <code>Map</code> and calling
  64  * <code>getProperty(OUTPUTSTREAM_PROPERTY)</code>.
  65  *
  66  * @author Neeraj Bajaj
  67  * @author K.Venugopal
  68  * @author Santiago Pericas-Geertsen
  69  * @author Sunitha Reddy
  70  */
  71 public final class XMLStreamWriterImpl extends AbstractMap<Object, Object>
  72         implements XMLStreamWriterBase {
  73 
  74     public static final String START_COMMENT = "<!--";
  75     public static final String END_COMMENT = "-->";
  76     public static final String DEFAULT_ENCODING = " encoding=\"utf-8\"";
  77     public static final String DEFAULT_XMLDECL = "<?xml version=\"1.0\" ?>";
  78     public static final String DEFAULT_XML_VERSION = "1.0";
  79     public static final char CLOSE_START_TAG = '>';
  80     public static final char OPEN_START_TAG = '<';
  81     public static final String OPEN_END_TAG = "</";
  82     public static final char CLOSE_END_TAG = '>';
  83     public static final String START_CDATA = "<![CDATA[";
  84     public static final String END_CDATA = "]]>";
  85     public static final String CLOSE_EMPTY_ELEMENT = "/>";
  86     public static final String SPACE = " ";
  87     public static final String UTF_8 = "UTF-8";
  88 
  89     public static final String OUTPUTSTREAM_PROPERTY = "sjsxp-outputstream";
  90 
  91     /**
  92      * This flag can be used to turn escaping off for content. It does
  93      * not apply to attribute content.
  94      */
  95     boolean fEscapeCharacters = true;
  96 
  97     /**
  98      * Flag for the value of repairNamespace property
  99      */
 100     private boolean fIsRepairingNamespace = false;
 101 
 102     /**
 103      * Underlying Writer to which characters are written.
 104      */
 105     private Writer fWriter;
 106 
 107     /**
 108      * Underlying OutputStream to which <code>fWriter</code>
 109      * writes to. May be null if unknown.
 110      */
 111     private OutputStream fOutputStream = null;
 112 
 113     /**
 114      * Collects attributes when the writer is in reparing mode.
 115      */
 116     private List<Attribute> fAttributeCache;
 117 
 118     /**
 119      * Collects namespace declarations when the writer is in reparing mode.
 120      */
 121     private List<QName> fNamespaceDecls;
 122 
 123     /**
 124      * Namespace context encapsulating user specified context
 125      * and context built by the writer
 126      */
 127     private NamespaceContextImpl fNamespaceContext = null;
 128 
 129     private NamespaceSupport fInternalNamespaceContext = null;
 130 
 131     private Random fPrefixGen = null;
 132 
 133     /**
 134      * Reference to PropertyManager
 135      */
 136     private PropertyManager fPropertyManager = null;
 137 
 138     /**
 139      * Flag to track if start tag is opened
 140      */
 141     private boolean fStartTagOpened = false;
 142 
 143     /**
 144      * Boolean flag  to indicate, if instance can be reused
 145      */
 146     private boolean fReuse;
 147 
 148     private SymbolTable fSymbolTable = new SymbolTable();
 149 
 150     private ElementStack fElementStack = new ElementStack(); //Change this .-Venu
 151 
 152     final private String DEFAULT_PREFIX = fSymbolTable.addSymbol("");
 153 
 154     private final ReadOnlyIterator<String> fReadOnlyIterator = new ReadOnlyIterator<>();
 155 
 156     /**
 157      * In some cases, this charset encoder is used to determine if a char is
 158      * encodable by underlying writer. For example, an 8-bit char from the
 159      * extended ASCII set is not encodable by 7-bit ASCII encoder. Unencodable
 160      * chars are escaped using XML numeric entities.
 161      */
 162     private CharsetEncoder fEncoder = null;
 163 
 164      /**
 165      * This is used to hold the namespace for attributes which happen to have
 166      * the same uri as the default namespace; It's added to avoid changing the
 167      * current impl. which has many redundant code for the repair mode
 168      */
 169     Map<String, String> fAttrNamespace = null;
 170 
 171     /**
 172      * Creates a new instance of XMLStreamWriterImpl. Uses platform's default
 173      * encoding.
 174      *
 175      * @param outputStream Underlying stream to write the bytes to
 176      * @param props        Properties used by this writer
 177      */
 178     public XMLStreamWriterImpl(OutputStream outputStream, PropertyManager props)
 179         throws IOException {
 180 
 181         // cannot call this(outputStream, null, props); for constructor,
 182         // OutputStreamWriter charsetName cannot be null
 183 
 184         // use default encoding
 185         this(new OutputStreamWriter(outputStream), props);
 186     }
 187 
 188     /**
 189      * Creates a new instance of XMLStreamWriterImpl.
 190      *
 191      * @param outputStream Underlying stream to write the bytes
 192      * @param encoding     Encoding used to convert chars into bytes
 193      * @param props        Properties used by this writer
 194      */
 195     public XMLStreamWriterImpl(OutputStream outputStream, String encoding,
 196         PropertyManager props) throws java.io.IOException {
 197         this(new StreamResult(outputStream), encoding, props);
 198     }
 199 
 200     /**
 201      * Creates a new instance of XMLStreamWriterImpl using a Writer.
 202      *
 203      * @param writer  Underlying writer to which chars are written
 204      * @param props   Properties used by this writer
 205      */
 206     public XMLStreamWriterImpl(Writer writer, PropertyManager props)
 207         throws java.io.IOException {
 208         this(new StreamResult(writer), null, props);
 209     }
 210 
 211     /**
 212      * Creates a new instance of XMLStreamWriterImpl using a StreamResult.
 213      * A StreamResult encasupates an OutputStream, a Writer or a SystemId.
 214      *
 215      * @param writer  Underlying writer to which chars are written
 216      * @param props   Properties used by this writer
 217      */
 218     public XMLStreamWriterImpl(StreamResult sr, String encoding,
 219         PropertyManager props) throws java.io.IOException {
 220         setOutput(sr, encoding);
 221         fPropertyManager = props;
 222         init();
 223     }
 224 
 225     /**
 226      * Initialize an instance of this XMLStreamWriter. Allocate new instances
 227      * for all the data structures. Set internal flags based on property values.
 228      */
 229     private void init() {
 230         fReuse = false;
 231         fNamespaceDecls = new ArrayList<>();
 232         fPrefixGen = new Random();
 233         fAttributeCache = new ArrayList<>();
 234         fInternalNamespaceContext = new NamespaceSupport();
 235         fInternalNamespaceContext.reset();
 236         fNamespaceContext = new NamespaceContextImpl();
 237         fNamespaceContext.internalContext = fInternalNamespaceContext;
 238 
 239         // Set internal state based on property values
 240         Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
 241         fIsRepairingNamespace = ob;
 242         ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS);
 243         setEscapeCharacters(ob);
 244     }
 245 
 246     /**
 247      * Reset this instance so that it can be re-used. Do not read properties
 248      * again. The method <code>setOutput(StreamResult, encoding)</code> must
 249      * be called after this one.
 250      */
 251     public void reset() {
 252         reset(false);
 253     }
 254 
 255     /**
 256      * Reset this instance so that it can be re-used. Clears but does not
 257      * re-allocate internal data structures.
 258      *
 259      * @param resetProperties Indicates if properties should be read again
 260      */
 261     void reset(boolean resetProperties) {
 262         if (!fReuse) {
 263             throw new java.lang.IllegalStateException(
 264                 "close() Must be called before calling reset()");
 265         }
 266 
 267         fReuse = false;
 268         fNamespaceDecls.clear();
 269         fAttributeCache.clear();
 270 
 271         // reset Element/NamespaceContext stacks
 272         fElementStack.clear();
 273         fInternalNamespaceContext.reset();
 274 
 275         fStartTagOpened = false;
 276         fNamespaceContext.userContext = null;
 277 
 278         if (resetProperties) {
 279             Boolean ob = (Boolean) fPropertyManager.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
 280             fIsRepairingNamespace = ob;
 281             ob = (Boolean) fPropertyManager.getProperty(Constants.ESCAPE_CHARACTERS);
 282             setEscapeCharacters(ob);
 283         }
 284     }
 285 
 286     /**
 287      * Use a StreamResult to initialize the output for this XMLStreamWriter. Check
 288      * for OutputStream, Writer and then systemId, in that order.
 289      *
 290      * @param sr        StreamResult encapsulating output information
 291      * @param encoding  Encoding to be used except when a Writer is available
 292      */
 293     public void setOutput(StreamResult sr, String encoding)
 294         throws IOException {
 295 
 296         if (sr.getOutputStream() != null) {
 297             setOutputUsingStream(sr.getOutputStream(), encoding);
 298         }
 299         else if (sr.getWriter() != null) {
 300             setOutputUsingWriter(sr.getWriter());
 301         }
 302         else if (sr.getSystemId() != null) {
 303             setOutputUsingStream(new FileOutputStream(sr.getSystemId()),
 304                 encoding);
 305         }
 306     }
 307 
 308      private void setOutputUsingWriter(Writer writer)
 309         throws IOException
 310      {
 311          fWriter = writer;
 312 
 313          if (writer instanceof OutputStreamWriter) {
 314              String charset = ((OutputStreamWriter) writer).getEncoding();
 315              if (charset != null && !charset.equalsIgnoreCase("utf-8")) {
 316                  fEncoder = Charset.forName(charset).newEncoder();
 317              }
 318          }
 319      }
 320 
 321     /**
 322      * Utility method to create a writer when passed an OutputStream. Make
 323      * sure to wrap an <code>OutputStreamWriter</code> using an
 324      * <code>XMLWriter</code> for performance reasons.
 325      *
 326      * @param os        Underlying OutputStream
 327      * @param encoding  Encoding used to convert chars into bytes
 328      */
 329     private void setOutputUsingStream(OutputStream os, String encoding)
 330         throws IOException {
 331         fOutputStream = os;
 332 
 333         if (encoding != null) {
 334             if (encoding.equalsIgnoreCase("utf-8")) {
 335                 fWriter = new UTF8OutputStreamWriter(os);
 336             }
 337             else {
 338                 fWriter = new XMLWriter(new OutputStreamWriter(os, encoding));
 339                 fEncoder = Charset.forName(encoding).newEncoder();
 340             }
 341         } else {
 342             encoding = SecuritySupport.getSystemProperty("file.encoding");
 343             if (encoding != null && encoding.equalsIgnoreCase("utf-8")) {
 344                 fWriter = new UTF8OutputStreamWriter(os);
 345             } else {
 346                 fWriter = new XMLWriter(new OutputStreamWriter(os));
 347             }
 348         }
 349     }
 350 
 351     /** Can this instance be reused
 352      *
 353      * @return boolean boolean value to indicate if this instance can be reused or not
 354      */
 355     public boolean canReuse() {
 356         return fReuse;
 357     }
 358 
 359     public void setEscapeCharacters(boolean escape) {
 360         fEscapeCharacters = escape;
 361     }
 362 
 363     public boolean getEscapeCharacters() {
 364         return fEscapeCharacters;
 365     }
 366 
 367     /**
 368      * Close this XMLStreamWriter by closing underlying writer.
 369      */
 370     @Override
 371     public void close() throws XMLStreamException {
 372         if (fWriter != null) {
 373             try {
 374                 //fWriter.close();
 375                 fWriter.flush();
 376             } catch (IOException e) {
 377                 throw new XMLStreamException(e);
 378             }
 379         }
 380         fWriter = null;
 381         fOutputStream = null;
 382         fNamespaceDecls.clear();
 383         fAttributeCache.clear();
 384         fElementStack.clear();
 385         fInternalNamespaceContext.reset();
 386         fReuse = true;
 387         fStartTagOpened = false;
 388         fNamespaceContext.userContext = null;
 389     }
 390 
 391     /**
 392      * Flush this XMLStreamWriter by flushin underlying writer.
 393      */
 394     @Override
 395     public void flush() throws XMLStreamException {
 396         try {
 397             fWriter.flush();
 398         } catch (IOException e) {
 399             throw new XMLStreamException(e);
 400         }
 401     }
 402 
 403     /**
 404      * Return <code>NamespaceContext</code> being used by the writer.
 405      *
 406      * @return NamespaceContext
 407      */
 408     @Override
 409     public NamespaceContext getNamespaceContext() {
 410         return fNamespaceContext;
 411     }
 412 
 413     /**
 414      * Return a prefix associated with specified uri, or null if the
 415      * uri is unknown.
 416      *
 417      * @param  uri The namespace uri
 418      * @throws XMLStreamException if uri specified is "" or null
 419      */
 420     @Override
 421     public String getPrefix(String uri) throws XMLStreamException {
 422         return fNamespaceContext.getPrefix(uri);
 423     }
 424 
 425     /**
 426      * Returns value associated with the specified property name.
 427      *
 428      * @param  str Property name
 429      * @throws IllegalArgumentException if the specified property is not supported
 430      * @return value associated with the specified property.
 431      */
 432     @Override
 433     public Object getProperty(String str)
 434         throws IllegalArgumentException {
 435         if (str == null) {
 436             throw new NullPointerException();
 437         }
 438 
 439         if (!fPropertyManager.containsProperty(str)) {
 440             throw new IllegalArgumentException("Property '" + str +
 441                 "' is not supported");
 442         }
 443 
 444         return fPropertyManager.getProperty(str);
 445     }
 446 
 447     /**
 448      * Set the specified URI as default namespace in the current namespace context.
 449      *
 450      * @param uri Namespace URI
 451      */
 452     @Override
 453     public void setDefaultNamespace(String uri) throws XMLStreamException {
 454         if (uri != null) {
 455             uri = fSymbolTable.addSymbol(uri);
 456         }
 457 
 458         if (fIsRepairingNamespace) {
 459             if (isDefaultNamespace(uri)) {
 460                 return;
 461             }
 462 
 463             QName qname = new QName();
 464             qname.setValues(DEFAULT_PREFIX, "xmlns", null, uri);
 465             fNamespaceDecls.add(qname);
 466         } else {
 467             fInternalNamespaceContext.declarePrefix(DEFAULT_PREFIX, uri);
 468         }
 469     }
 470 
 471     /**
 472      * Sets the current <code>NamespaceContext</code> for prefix and uri bindings.
 473      * This context becomes the root namespace context for writing and
 474      * will replace the current root namespace context. Subsequent calls
 475      * to setPrefix and setDefaultNamespace will bind namespaces using
 476      * the context passed to the method as the root context for resolving
 477      * namespaces. This method may only be called once at the start of the
 478      * document. It does not cause the namespaces to be declared. If a
 479      * namespace URI to prefix mapping is found in the namespace context
 480      * it is treated as declared and the prefix may be used by the
 481      * <code>XMLStreamWriter</code>.
 482      *
 483      * @param namespaceContext the namespace context to use for this writer, may not be null
 484      * @throws XMLStreamException
 485      */
 486     @Override
 487     public void setNamespaceContext(NamespaceContext namespaceContext)
 488         throws XMLStreamException {
 489         fNamespaceContext.userContext = namespaceContext;
 490     }
 491 
 492     /**
 493      * Sets the prefix the uri is bound to. This prefix is bound in the scope of
 494      * the current START_ELEMENT / END_ELEMENT pair. If this method is called before
 495      * a START_ELEMENT has been written the prefix is bound in the root scope.
 496      *
 497      * @param prefix
 498      * @param uri
 499      * @throws XMLStreamException
 500      */
 501     @Override
 502     public void setPrefix(String prefix, String uri) throws XMLStreamException {
 503 
 504         if (prefix == null) {
 505             throw new XMLStreamException("Prefix cannot be null");
 506         }
 507 
 508         if (uri == null) {
 509             throw new XMLStreamException("URI cannot be null");
 510         }
 511 
 512         prefix = fSymbolTable.addSymbol(prefix);
 513         uri = fSymbolTable.addSymbol(uri);
 514 
 515         if (fIsRepairingNamespace) {
 516             String tmpURI = fInternalNamespaceContext.getURI(prefix);
 517 
 518             if ((tmpURI != null) && (tmpURI == uri)) {
 519                 return;
 520             }
 521 
 522             if(checkUserNamespaceContext(prefix,uri))
 523                 return;
 524             QName qname = new QName();
 525             qname.setValues(prefix,XMLConstants.XMLNS_ATTRIBUTE, null,uri);
 526             fNamespaceDecls.add(qname);
 527 
 528             return;
 529         }
 530 
 531         fInternalNamespaceContext.declarePrefix(prefix, uri);
 532     }
 533 
 534     @Override
 535     public void writeAttribute(String localName, String value)
 536         throws XMLStreamException {
 537         try {
 538             if (!fStartTagOpened) {
 539                 throw new XMLStreamException(
 540                     "Attribute not associated with any element");
 541             }
 542 
 543             if (fIsRepairingNamespace) {
 544                 Attribute attr = new Attribute(value); // Revisit:Dont create new one's. Reuse.-Venu
 545                 attr.setValues(null, localName, null, null);
 546                 fAttributeCache.add(attr);
 547 
 548                 return;
 549             }
 550 
 551             fWriter.write(" ");
 552             fWriter.write(localName);
 553             fWriter.write("=\"");
 554             writeXMLContent(
 555                     value,
 556                     true,   // true = escapeChars
 557                     true);  // true = escapeDoubleQuotes
 558             fWriter.write("\"");
 559         } catch (IOException e) {
 560             throw new XMLStreamException(e);
 561         }
 562     }
 563 
 564     @Override
 565     public void writeAttribute(String namespaceURI, String localName,
 566         String value) throws XMLStreamException {
 567         try {
 568             if (!fStartTagOpened) {
 569                 throw new XMLStreamException(
 570                     "Attribute not associated with any element");
 571             }
 572 
 573             if (namespaceURI == null) {
 574                 throw new XMLStreamException("NamespaceURI cannot be null");
 575             }
 576 
 577             namespaceURI = fSymbolTable.addSymbol(namespaceURI);
 578 
 579             String prefix = fInternalNamespaceContext.getPrefix(namespaceURI);
 580 
 581             if (!fIsRepairingNamespace) {
 582                 if (prefix == null) {
 583                     throw new XMLStreamException("Prefix cannot be null");
 584                 }
 585 
 586                 writeAttributeWithPrefix(prefix, localName, value);
 587             } else {
 588                 Attribute attr = new Attribute(value);
 589                 attr.setValues(null, localName, null, namespaceURI);
 590                 fAttributeCache.add(attr);
 591             }
 592         } catch (IOException e) {
 593             throw new XMLStreamException(e);
 594         }
 595     }
 596 
 597     private void writeAttributeWithPrefix(String prefix, String localName,
 598         String value) throws IOException {
 599         fWriter.write(SPACE);
 600 
 601         if ((prefix != null) && (!prefix.equals(XMLConstants.DEFAULT_NS_PREFIX))) {
 602             fWriter.write(prefix);
 603             fWriter.write(":");
 604         }
 605 
 606         fWriter.write(localName);
 607         fWriter.write("=\"");
 608         writeXMLContent(value,
 609                 true,   // true = escapeChars
 610                 true);  // true = escapeDoubleQuotes
 611         fWriter.write("\"");
 612     }
 613 
 614     @Override
 615     public void writeAttribute(String prefix, String namespaceURI,
 616         String localName, String value) throws XMLStreamException {
 617         try {
 618             if (!fStartTagOpened) {
 619                 throw new XMLStreamException(
 620                     "Attribute not associated with any element");
 621             }
 622 
 623             if (namespaceURI == null) {
 624                 throw new XMLStreamException("NamespaceURI cannot be null");
 625             }
 626 
 627             if (localName == null) {
 628                 throw new XMLStreamException("Local name cannot be null");
 629             }
 630 
 631             if (!fIsRepairingNamespace) {
 632                 if (prefix == null || prefix.isEmpty()){
 633                     if (!namespaceURI.isEmpty()) {
 634                         throw new XMLStreamException("prefix cannot be null or empty");
 635                     } else {
 636                         writeAttributeWithPrefix(null, localName, value);
 637                         return;
 638                     }
 639                 }
 640 
 641                 if (!prefix.equals(XMLConstants.XML_NS_PREFIX) ||
 642                         !namespaceURI.equals(XMLConstants.XML_NS_URI)) {
 643 
 644                     prefix = fSymbolTable.addSymbol(prefix);
 645                     namespaceURI = fSymbolTable.addSymbol(namespaceURI);
 646 
 647                     if (fInternalNamespaceContext.containsPrefixInCurrentContext(prefix)){
 648 
 649                         String tmpURI = fInternalNamespaceContext.getURI(prefix);
 650 
 651                         if (tmpURI != null && tmpURI != namespaceURI){
 652                             throw new XMLStreamException("Prefix "+prefix+" is " +
 653                                     "already bound to "+tmpURI+
 654                                     ". Trying to rebind it to "+namespaceURI+" is an error.");
 655                         }
 656                     }
 657                     fInternalNamespaceContext.declarePrefix(prefix, namespaceURI);
 658                 }
 659                 writeAttributeWithPrefix(prefix, localName, value);
 660             } else {
 661                 if (prefix != null) {
 662                     prefix = fSymbolTable.addSymbol(prefix);
 663                 }
 664 
 665                 namespaceURI = fSymbolTable.addSymbol(namespaceURI);
 666 
 667                 Attribute attr = new Attribute(value);
 668                 attr.setValues(prefix, localName, null, namespaceURI);
 669                 fAttributeCache.add(attr);
 670             }
 671         } catch (IOException e) {
 672             throw new XMLStreamException(e);
 673         }
 674     }
 675 
 676     @Override
 677     public void writeCData(String cdata) throws XMLStreamException {
 678         try {
 679             if (cdata == null) {
 680                 throw new XMLStreamException("cdata cannot be null");
 681             }
 682 
 683             if (fStartTagOpened) {
 684                 closeStartTag();
 685             }
 686 
 687             fWriter.write(START_CDATA);
 688             fWriter.write(cdata);
 689             fWriter.write(END_CDATA);
 690         } catch (IOException e) {
 691             throw new XMLStreamException(e);
 692         }
 693     }
 694 
 695     @Override
 696     public void writeCharacters(String data) throws XMLStreamException {
 697         try {
 698             if (fStartTagOpened) {
 699                 closeStartTag();
 700             }
 701 
 702             writeXMLContent(data);
 703         } catch (IOException e) {
 704             throw new XMLStreamException(e);
 705         }
 706     }
 707 
 708     @Override
 709     public void writeCharacters(char[] data, int start, int len)
 710         throws XMLStreamException {
 711         try {
 712             if (fStartTagOpened) {
 713                 closeStartTag();
 714             }
 715 
 716             writeXMLContent(data, start, len, fEscapeCharacters);
 717         } catch (IOException e) {
 718             throw new XMLStreamException(e);
 719         }
 720     }
 721 
 722     @Override
 723     public void writeComment(String comment) throws XMLStreamException {
 724         try {
 725             if (fStartTagOpened) {
 726                 closeStartTag();
 727             }
 728 
 729             fWriter.write(START_COMMENT);
 730 
 731             if (comment != null) {
 732                 fWriter.write(comment);
 733             }
 734 
 735             fWriter.write(END_COMMENT);
 736         } catch (IOException e) {
 737             throw new XMLStreamException(e);
 738         }
 739     }
 740 
 741     @Override
 742     public void writeDTD(String dtd) throws XMLStreamException {
 743         try {
 744             if (fStartTagOpened) {
 745                 closeStartTag();
 746             }
 747 
 748             fWriter.write(dtd);
 749         } catch (IOException e) {
 750             throw new XMLStreamException(e);
 751         }
 752     }
 753 
 754     /*
 755      * Write default Namespace.
 756      *
 757      * If namespaceURI == null,
 758      * then it is assumed to be equivilent to {@link XMLConstants.NULL_NS_URI},
 759      * i.e. there is no Namespace.
 760      *
 761      * @param namespaceURI NamespaceURI to declare.
 762      *
 763      * @throws XMLStreamException
 764      *
 765      * @see <a href="http://www.w3.org/TR/REC-xml-names/#defaulting">
 766      *   Namespaces in XML, 5.2 Namespace Defaulting</a>
 767      */
 768     @Override
 769     public void writeDefaultNamespace(String namespaceURI)
 770         throws XMLStreamException {
 771 
 772         // normalize namespaceURI
 773         String namespaceURINormalized;
 774         if (namespaceURI == null) {
 775             namespaceURINormalized = ""; // XMLConstants.NULL_NS_URI
 776         } else {
 777             namespaceURINormalized = namespaceURI;
 778         }
 779 
 780         try {
 781             if (!fStartTagOpened) {
 782                 throw new IllegalStateException(
 783                     "Namespace Attribute not associated with any element");
 784             }
 785 
 786             if (fIsRepairingNamespace) {
 787                 QName qname = new QName();
 788                 qname.setValues(XMLConstants.DEFAULT_NS_PREFIX,
 789                     XMLConstants.XMLNS_ATTRIBUTE, null, namespaceURINormalized);
 790                 fNamespaceDecls.add(qname);
 791 
 792                 return;
 793             }
 794 
 795             namespaceURINormalized = fSymbolTable.addSymbol(namespaceURINormalized);
 796 
 797             if (fInternalNamespaceContext.containsPrefixInCurrentContext("")){
 798 
 799                 String tmp = fInternalNamespaceContext.getURI("");
 800 
 801                 if (tmp != null && !tmp.equals(namespaceURINormalized)) {
 802                         throw new XMLStreamException(
 803                                 "xmlns has been already bound to " +tmp +
 804                                 ". Rebinding it to "+ namespaceURINormalized +
 805                                 " is an error");
 806                     }
 807             }
 808             fInternalNamespaceContext.declarePrefix("", namespaceURINormalized);
 809 
 810             // use common namespace code with a prefix == null for xmlns="..."
 811             writenamespace(null, namespaceURINormalized);
 812         } catch (IOException e) {
 813             throw new XMLStreamException(e);
 814         }
 815     }
 816 
 817     @Override
 818     public void writeEmptyElement(String localName) throws XMLStreamException {
 819         try {
 820             if (fStartTagOpened) {
 821                 closeStartTag();
 822             }
 823 
 824             openStartTag();
 825             fElementStack.push(null, localName, null, null, true);
 826             fInternalNamespaceContext.pushContext();
 827 
 828             if (!fIsRepairingNamespace) {
 829                 fWriter.write(localName);
 830             }
 831         } catch (IOException e) {
 832             throw new XMLStreamException(e);
 833         }
 834     }
 835 
 836     @Override
 837     public void writeEmptyElement(String namespaceURI, String localName)
 838         throws XMLStreamException {
 839         if (namespaceURI == null) {
 840             throw new XMLStreamException("NamespaceURI cannot be null");
 841         }
 842 
 843         namespaceURI = fSymbolTable.addSymbol(namespaceURI);
 844 
 845         String prefix = fNamespaceContext.getPrefix(namespaceURI);
 846         writeEmptyElement(prefix, localName, namespaceURI);
 847     }
 848 
 849     @Override
 850     public void writeEmptyElement(String prefix, String localName,
 851         String namespaceURI) throws XMLStreamException {
 852         try {
 853             if (localName == null) {
 854                 throw new XMLStreamException("Local Name cannot be null");
 855             }
 856 
 857             if (namespaceURI == null) {
 858                 throw new XMLStreamException("NamespaceURI cannot be null");
 859             }
 860 
 861             if (prefix != null) {
 862                 prefix = fSymbolTable.addSymbol(prefix);
 863             }
 864 
 865             namespaceURI = fSymbolTable.addSymbol(namespaceURI);
 866 
 867             if (fStartTagOpened) {
 868                 closeStartTag();
 869             }
 870 
 871             openStartTag();
 872 
 873             fElementStack.push(prefix, localName, null, namespaceURI, true);
 874             fInternalNamespaceContext.pushContext();
 875 
 876             if (!fIsRepairingNamespace) {
 877                 if (prefix == null) {
 878                     throw new XMLStreamException("NamespaceURI " +
 879                         namespaceURI + " has not been bound to any prefix");
 880                 }
 881             } else {
 882                 return;
 883             }
 884 
 885             if ((prefix != null) && (!prefix.equals(XMLConstants.DEFAULT_NS_PREFIX))) {
 886                 fWriter.write(prefix);
 887                 fWriter.write(":");
 888             }
 889 
 890             fWriter.write(localName);
 891         } catch (IOException e) {
 892             throw new XMLStreamException(e);
 893         }
 894     }
 895 
 896     @Override
 897     public void writeEndDocument() throws XMLStreamException {
 898         try {
 899             if (fStartTagOpened) {
 900                 closeStartTag();
 901             }
 902 
 903             while (!fElementStack.empty()) {
 904                 ElementState elem = fElementStack.pop();
 905                 fInternalNamespaceContext.popContext();
 906 
 907                 if (elem.isEmpty) {
 908                     //fWriter.write(CLOSE_EMPTY_ELEMENT);
 909                 } else {
 910                     fWriter.write(OPEN_END_TAG);
 911 
 912                     if ((elem.prefix != null) && !(elem.prefix).isEmpty()) {
 913                         fWriter.write(elem.prefix);
 914                         fWriter.write(":");
 915                     }
 916 
 917                     fWriter.write(elem.localpart);
 918                     fWriter.write(CLOSE_END_TAG);
 919                 }
 920             }
 921         } catch (IOException e) {
 922             throw new XMLStreamException(e);
 923         } catch (ArrayIndexOutOfBoundsException e) {
 924             throw new XMLStreamException("No more elements to write");
 925         }
 926     }
 927 
 928     @Override
 929     public void writeEndElement() throws XMLStreamException {
 930         try {
 931             if (fStartTagOpened) {
 932                 closeStartTag();
 933             }
 934 
 935             ElementState currentElement = fElementStack.pop();
 936 
 937             if (currentElement == null) {
 938                 throw new XMLStreamException("No element was found to write");
 939             }
 940 
 941             if (currentElement.isEmpty) {
 942                 //fWriter.write(CLOSE_EMPTY_ELEMENT);
 943                 return;
 944             }
 945 
 946             fWriter.write(OPEN_END_TAG);
 947 
 948             if ((currentElement.prefix != null) &&
 949                     !(currentElement.prefix).isEmpty()) {
 950                 fWriter.write(currentElement.prefix);
 951                 fWriter.write(":");
 952             }
 953 
 954             fWriter.write(currentElement.localpart);
 955             fWriter.write(CLOSE_END_TAG);
 956             fInternalNamespaceContext.popContext();
 957         } catch (IOException e) {
 958             throw new XMLStreamException(e);
 959         } catch (ArrayIndexOutOfBoundsException e) {
 960             throw new XMLStreamException(
 961                     "No element was found to write: "
 962                     + e.toString(), e);
 963         }
 964     }
 965 
 966     @Override
 967     public void writeEntityRef(String refName) throws XMLStreamException {
 968         try {
 969             if (fStartTagOpened) {
 970                 closeStartTag();
 971             }
 972 
 973             fWriter.write('&');
 974             fWriter.write(refName);
 975             fWriter.write(';');
 976         } catch (IOException e) {
 977             throw new XMLStreamException(e);
 978         }
 979     }
 980 
 981     /**
 982      * Write a Namespace declaration.
 983      *
 984      * If namespaceURI == null,
 985      * then it is assumed to be equivilent to {@link XMLConstants.NULL_NS_URI},
 986      * i.e. there is no Namespace.
 987      *
 988      * @param prefix Prefix to bind.
 989      * @param namespaceURI NamespaceURI to declare.
 990      *
 991      * @throws XMLStreamException
 992      *
 993      * @see <a href="http://www.w3.org/TR/REC-xml-names/#defaulting">
 994      *   Namespaces in XML, 5.2 Namespace Defaulting</a>
 995      */
 996     @Override
 997     public void writeNamespace(String prefix, String namespaceURI)
 998         throws XMLStreamException {
 999 
1000         // normalize namespaceURI
1001         String namespaceURINormalized;
1002         if (namespaceURI == null) {
1003             namespaceURINormalized = ""; // XMLConstants.NULL_NS_URI
1004         } else {
1005             namespaceURINormalized = namespaceURI;
1006         }
1007 
1008         try {
1009             QName qname;
1010 
1011             if (!fStartTagOpened) {
1012                 throw new IllegalStateException(
1013                         "Invalid state: start tag is not opened at writeNamespace("
1014                         + prefix
1015                         + ", "
1016                         + namespaceURINormalized
1017                         + ")");
1018             }
1019 
1020             // is this the default Namespace?
1021             if (prefix == null
1022                     || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)
1023                     || prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
1024                 writeDefaultNamespace(namespaceURINormalized);
1025                 return;
1026             }
1027 
1028             if (prefix.equals(XMLConstants.XML_NS_PREFIX) && namespaceURINormalized.equals(XMLConstants.XML_NS_URI))
1029                 return;
1030 
1031             prefix = fSymbolTable.addSymbol(prefix);
1032             namespaceURINormalized = fSymbolTable.addSymbol(namespaceURINormalized);
1033 
1034             if (fIsRepairingNamespace) {
1035                 String tmpURI = fInternalNamespaceContext.getURI(prefix);
1036 
1037                 if ((tmpURI != null) && (tmpURI.equals(namespaceURINormalized))) {
1038                     return;
1039                 }
1040 
1041                 qname = new QName();
1042                 qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null,
1043                     namespaceURINormalized);
1044                 fNamespaceDecls.add(qname);
1045 
1046                 return;
1047             }
1048 
1049 
1050             if (fInternalNamespaceContext.containsPrefixInCurrentContext(prefix)){
1051 
1052                 String tmp = fInternalNamespaceContext.getURI(prefix);
1053 
1054                 if (tmp != null && !tmp.equals(namespaceURINormalized)) {
1055 
1056                        throw new XMLStreamException("prefix "+prefix+
1057                             " has been already bound to " +tmp +
1058                             ". Rebinding it to "+ namespaceURINormalized+
1059                             " is an error");
1060                 }
1061             }
1062 
1063             fInternalNamespaceContext.declarePrefix(prefix, namespaceURINormalized);
1064             writenamespace(prefix, namespaceURINormalized);
1065 
1066         } catch (IOException e) {
1067             throw new XMLStreamException(e);
1068         }
1069     }
1070 
1071     private void writenamespace(String prefix, String namespaceURI)
1072         throws IOException {
1073         fWriter.write(" xmlns");
1074 
1075         if ((prefix != null) && (!prefix.equals(XMLConstants.DEFAULT_NS_PREFIX))) {
1076             fWriter.write(":");
1077             fWriter.write(prefix);
1078         }
1079 
1080         fWriter.write("=\"");
1081         writeXMLContent(
1082                 namespaceURI,
1083                 true,   // true = escapeChars
1084                 true);  // true = escapeDoubleQuotes
1085         fWriter.write("\"");
1086     }
1087 
1088     @Override
1089     public void writeProcessingInstruction(String target)
1090         throws XMLStreamException {
1091         try {
1092             if (fStartTagOpened) {
1093                 closeStartTag();
1094             }
1095 
1096             if (target != null) {
1097                 fWriter.write("<?");
1098                 fWriter.write(target);
1099                 fWriter.write("?>");
1100 
1101                 return;
1102             }
1103         } catch (IOException e) {
1104             throw new XMLStreamException(e);
1105         }
1106 
1107         throw new XMLStreamException("PI target cannot be null");
1108     }
1109 
1110     /**
1111      * @param target
1112      * @param data
1113      * @throws XMLStreamException
1114      */
1115     @Override
1116     public void writeProcessingInstruction(String target, String data)
1117         throws XMLStreamException {
1118         try {
1119             if (fStartTagOpened) {
1120                 closeStartTag();
1121             }
1122 
1123             if ((target == null) || (data == null)) {
1124                 throw new XMLStreamException("PI target cannot be null");
1125             }
1126 
1127             fWriter.write("<?");
1128             fWriter.write(target);
1129             fWriter.write(SPACE);
1130             fWriter.write(data);
1131             fWriter.write("?>");
1132         } catch (IOException e) {
1133             throw new XMLStreamException(e);
1134         }
1135     }
1136 
1137     /**
1138      * Writes the XML declaration.
1139      *
1140      * @throws XMLStreamException in case of an IOException
1141      */
1142     @Override
1143     public void writeStartDocument() throws XMLStreamException {
1144         writeStartDocument(null, null, false, false);
1145     }
1146 
1147     /**
1148      * Writes the XML declaration.
1149      *
1150      * @param version the specified version
1151      * @throws XMLStreamException in case of an IOException
1152      */
1153     @Override
1154     public void writeStartDocument(String version) throws XMLStreamException {
1155         writeStartDocument(null, version, false, false);
1156     }
1157 
1158     /**
1159      * Writes the XML declaration.
1160      *
1161      * @param encoding the specified encoding
1162      * @param version the specified version
1163      * @throws XMLStreamException in case of an IOException
1164      */
1165     @Override
1166     public void writeStartDocument(String encoding, String version)
1167         throws XMLStreamException {
1168         writeStartDocument(encoding, version, false, false);
1169     }
1170 
1171     public void writeStartDocument(String encoding, String version,
1172             boolean standalone, boolean standaloneSet)
1173         throws XMLStreamException {
1174 
1175         try {
1176             if ((encoding == null || encoding.length() == 0)
1177                     && (version == null || version.length() == 0)
1178                     && (!standaloneSet)) {
1179                 fWriter.write(DEFAULT_XMLDECL);
1180                 return;
1181             }
1182 
1183             // Verify the encoding before writing anything
1184             if (encoding != null && !encoding.isEmpty()) {
1185                 verifyEncoding(encoding);
1186             }
1187 
1188             fWriter.write("<?xml version=\"");
1189 
1190             if ((version == null) || version.isEmpty()) {
1191                 fWriter.write(DEFAULT_XML_VERSION);
1192             } else {
1193                 fWriter.write(version);
1194             }
1195 
1196             if (encoding != null && !encoding.isEmpty()) {
1197                 fWriter.write("\" encoding=\"");
1198                 fWriter.write(encoding);
1199             }
1200 
1201             if (standaloneSet) {
1202                 fWriter.write("\" standalone=\"");
1203                 if (standalone) {
1204                     fWriter.write("yes");
1205                 } else {
1206                     fWriter.write("no");
1207                 }
1208             }
1209 
1210             fWriter.write("\"?>");
1211         } catch (IOException ex) {
1212             throw new XMLStreamException(ex);
1213         }
1214     }
1215 
1216     /**
1217      * Verifies that the encoding is consistent between the underlying encoding
1218      * and that specified.
1219      *
1220      * @param encoding the specified encoding
1221      * @throws XMLStreamException if they do not match
1222      */
1223     private void verifyEncoding(String encoding) throws XMLStreamException {
1224 
1225         String streamEncoding = null;
1226         if (fWriter instanceof OutputStreamWriter) {
1227             streamEncoding = ((OutputStreamWriter) fWriter).getEncoding();
1228         }
1229         else if (fWriter instanceof UTF8OutputStreamWriter) {
1230             streamEncoding = ((UTF8OutputStreamWriter) fWriter).getEncoding();
1231         }
1232         else if (fWriter instanceof XMLWriter) {
1233             streamEncoding = ((OutputStreamWriter) ((XMLWriter)fWriter).getWriter()).getEncoding();
1234         }
1235 
1236         if (streamEncoding != null && !streamEncoding.equalsIgnoreCase(encoding)) {
1237             // If the equality check failed, check for charset encoding aliases
1238             boolean foundAlias = false;
1239             Set<String> aliases = Charset.forName(encoding).aliases();
1240             for (Iterator<String> it = aliases.iterator(); !foundAlias && it.hasNext(); ) {
1241                 if (streamEncoding.equalsIgnoreCase(it.next())) {
1242                     foundAlias = true;
1243                 }
1244             }
1245             // If no alias matches the encoding name, then report error
1246             if (!foundAlias) {
1247                 throw new XMLStreamException("Underlying stream encoding '"
1248                         + streamEncoding
1249                         + "' and input paramter for writeStartDocument() method '"
1250                         + encoding + "' do not match.");
1251             }
1252         }
1253     }
1254 
1255     /**
1256      * @param localName
1257      * @throws XMLStreamException
1258      */
1259     @Override
1260     public void writeStartElement(String localName) throws XMLStreamException {
1261         try {
1262             if (localName == null) {
1263                 throw new XMLStreamException("Local Name cannot be null");
1264             }
1265 
1266             if (fStartTagOpened) {
1267                 closeStartTag();
1268             }
1269 
1270             openStartTag();
1271             fElementStack.push(null, localName, null, null, false);
1272             fInternalNamespaceContext.pushContext();
1273 
1274             if (fIsRepairingNamespace) {
1275                 return;
1276             }
1277 
1278             fWriter.write(localName);
1279         } catch (IOException ex) {
1280             throw new XMLStreamException(ex);
1281         }
1282     }
1283 
1284     /**
1285      * @param namespaceURI
1286      * @param localName
1287      * @throws XMLStreamException
1288      */
1289     @Override
1290     public void writeStartElement(String namespaceURI, String localName)
1291         throws XMLStreamException {
1292         if (localName == null) {
1293             throw new XMLStreamException("Local Name cannot be null");
1294         }
1295 
1296         if (namespaceURI == null) {
1297             throw new XMLStreamException("NamespaceURI cannot be null");
1298         }
1299 
1300         namespaceURI = fSymbolTable.addSymbol(namespaceURI);
1301 
1302         String prefix = null;
1303 
1304         if (!fIsRepairingNamespace) {
1305             prefix = fNamespaceContext.getPrefix(namespaceURI);
1306 
1307             if (prefix != null) {
1308                 prefix = fSymbolTable.addSymbol(prefix);
1309             }
1310         }
1311 
1312         writeStartElement(prefix, localName, namespaceURI);
1313     }
1314 
1315     /**
1316      * @param prefix
1317      * @param localName
1318      * @param namespaceURI
1319      * @throws XMLStreamException
1320      */
1321     @Override
1322     public void writeStartElement(String prefix, String localName,
1323         String namespaceURI) throws XMLStreamException {
1324         try {
1325             if (localName == null) {
1326                 throw new XMLStreamException("Local Name cannot be null");
1327             }
1328 
1329             if (namespaceURI == null) {
1330                 throw new XMLStreamException("NamespaceURI cannot be null");
1331             }
1332 
1333             if (!fIsRepairingNamespace) {
1334                 if (prefix == null) {
1335                     throw new XMLStreamException("Prefix cannot be null");
1336                 }
1337             }
1338 
1339             if (fStartTagOpened) {
1340                 closeStartTag();
1341             }
1342 
1343             openStartTag();
1344             namespaceURI = fSymbolTable.addSymbol(namespaceURI);
1345 
1346             if (prefix != null) {
1347                 prefix = fSymbolTable.addSymbol(prefix);
1348             }
1349 
1350             fElementStack.push(prefix, localName, null, namespaceURI, false);
1351             fInternalNamespaceContext.pushContext();
1352 
1353             String tmpPrefix = fNamespaceContext.getPrefix(namespaceURI);
1354 
1355 
1356             if ((prefix != null) &&
1357                     ((tmpPrefix == null) || !prefix.equals(tmpPrefix))) {
1358                 fInternalNamespaceContext.declarePrefix(prefix, namespaceURI);
1359 
1360             }
1361 
1362             if (fIsRepairingNamespace) {
1363                 if ((prefix == null) ||
1364                         ((tmpPrefix != null) && prefix.equals(tmpPrefix))) {
1365                     return;
1366                 }
1367 
1368                 QName qname = new QName();
1369                 qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null,
1370                     namespaceURI);
1371                 fNamespaceDecls.add(qname);
1372 
1373                 return;
1374             }
1375 
1376             if ((prefix != null) && (prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
1377                 fWriter.write(prefix);
1378                 fWriter.write(":");
1379             }
1380 
1381             fWriter.write(localName);
1382 
1383         } catch (IOException ex) {
1384             throw new XMLStreamException(ex);
1385         }
1386     }
1387 
1388     /**
1389      * Writes character reference in hex format.
1390      */
1391     private void writeCharRef(int codePoint) throws IOException {
1392         fWriter.write( "&#x" );
1393         fWriter.write( Integer.toHexString(codePoint) );
1394         fWriter.write( ';' );
1395     }
1396 
1397     /**
1398      * Writes XML content to underlying writer. Escapes characters unless
1399      * escaping character feature is turned off.
1400      */
1401     private void writeXMLContent(char[] content, int start, int length,
1402         boolean escapeChars) throws IOException {
1403         if (!escapeChars) {
1404             fWriter.write(content, start, length);
1405 
1406             return;
1407         }
1408 
1409         // Index of the next char to be written
1410         int startWritePos = start;
1411 
1412         final int end = start + length;
1413 
1414         for (int index = start; index < end; index++) {
1415             char ch = content[index];
1416 
1417             if (fEncoder != null && !fEncoder.canEncode(ch)){
1418                 fWriter.write(content, startWritePos, index - startWritePos );
1419 
1420                 // Check if current and next characters forms a surrogate pair
1421                 // and escape it to avoid generation of invalid xml content
1422                 if ( index != end - 1 && Character.isSurrogatePair(ch, content[index+1])) {
1423                     writeCharRef(Character.toCodePoint(ch, content[index+1]));
1424                     index++;
1425                 } else {
1426                     writeCharRef(ch);
1427                 }
1428                 startWritePos = index + 1;
1429                 continue;
1430             }
1431 
1432             switch (ch) {
1433             case '<':
1434                 fWriter.write(content, startWritePos, index - startWritePos);
1435                 fWriter.write("&lt;");
1436                 startWritePos = index + 1;
1437 
1438                 break;
1439 
1440             case '&':
1441                 fWriter.write(content, startWritePos, index - startWritePos);
1442                 fWriter.write("&amp;");
1443                 startWritePos = index + 1;
1444 
1445                 break;
1446 
1447             case '>':
1448                 fWriter.write(content, startWritePos, index - startWritePos);
1449                 fWriter.write("&gt;");
1450                 startWritePos = index + 1;
1451 
1452                 break;
1453             }
1454         }
1455 
1456         // Write any pending data
1457         fWriter.write(content, startWritePos, end - startWritePos);
1458     }
1459 
1460     private void writeXMLContent(String content) throws IOException {
1461         if ((content != null) && (content.length() > 0)) {
1462             writeXMLContent(content,
1463                     fEscapeCharacters,  // boolean = escapeChars
1464                     false);             // false = escapeDoubleQuotes
1465         }
1466     }
1467 
1468     /**
1469      * Writes XML content to underlying writer. Escapes characters unless
1470      * escaping character feature is turned off.
1471      */
1472     private void writeXMLContent(
1473             String content,
1474             boolean escapeChars,
1475             boolean escapeDoubleQuotes)
1476         throws IOException {
1477 
1478         if (!escapeChars) {
1479             fWriter.write(content);
1480 
1481             return;
1482         }
1483 
1484         // Index of the next char to be written
1485         int startWritePos = 0;
1486 
1487         final int end = content.length();
1488 
1489         for (int index = 0; index < end; index++) {
1490             char ch = content.charAt(index);
1491 
1492             if (fEncoder != null && !fEncoder.canEncode(ch)){
1493                 fWriter.write(content, startWritePos, index - startWritePos );
1494 
1495                 // Check if current and next characters forms a surrogate pair
1496                 // and escape it to avoid generation of invalid xml content
1497                 if ( index != end - 1 && Character.isSurrogatePair(ch, content.charAt(index+1))) {
1498                     writeCharRef(Character.toCodePoint(ch, content.charAt(index+1)));
1499                     index++;
1500                 } else {
1501                     writeCharRef(ch);
1502                 }
1503 
1504                 startWritePos = index + 1;
1505                 continue;
1506             }
1507 
1508             switch (ch) {
1509             case '<':
1510                 fWriter.write(content, startWritePos, index - startWritePos);
1511                 fWriter.write("&lt;");
1512                 startWritePos = index + 1;
1513 
1514                 break;
1515 
1516             case '&':
1517                 fWriter.write(content, startWritePos, index - startWritePos);
1518                 fWriter.write("&amp;");
1519                 startWritePos = index + 1;
1520 
1521                 break;
1522 
1523             case '>':
1524                 fWriter.write(content, startWritePos, index - startWritePos);
1525                 fWriter.write("&gt;");
1526                 startWritePos = index + 1;
1527 
1528                 break;
1529 
1530             case '"':
1531                 fWriter.write(content, startWritePos, index - startWritePos);
1532                 if (escapeDoubleQuotes) {
1533                     fWriter.write("&quot;");
1534                 } else {
1535                     fWriter.write('"');
1536                 }
1537                 startWritePos = index + 1;
1538 
1539                 break;
1540             }
1541         }
1542 
1543         // Write any pending data
1544         fWriter.write(content, startWritePos, end - startWritePos);
1545     }
1546 
1547     /**
1548      * marks close of start tag and writes the same into the writer.
1549      */
1550     private void closeStartTag() throws XMLStreamException {
1551         try {
1552             ElementState currentElement = fElementStack.peek();
1553 
1554             if (fIsRepairingNamespace) {
1555                 repair();
1556                 correctPrefix(currentElement, XMLStreamConstants.START_ELEMENT);
1557 
1558                 if ((currentElement.prefix != null) &&
1559                         (currentElement.prefix != XMLConstants.DEFAULT_NS_PREFIX)) {
1560                     fWriter.write(currentElement.prefix);
1561                     fWriter.write(":");
1562                 }
1563 
1564                 fWriter.write(currentElement.localpart);
1565 
1566                 int len = fNamespaceDecls.size();
1567                 QName qname;
1568 
1569                 for (int i = 0; i < len; i++) {
1570                     qname = fNamespaceDecls.get(i);
1571 
1572                     if (qname != null) {
1573                         if (fInternalNamespaceContext.declarePrefix(qname.prefix,
1574                             qname.uri)) {
1575                             writenamespace(qname.prefix, qname.uri);
1576                         }
1577                     }
1578                 }
1579 
1580                 fNamespaceDecls.clear();
1581 
1582                 Attribute attr;
1583 
1584                 for (int j = 0; j < fAttributeCache.size(); j++) {
1585                     attr = fAttributeCache.get(j);
1586 
1587                     if ((attr.prefix != null) && (attr.uri != null)) {
1588                         if (!attr.prefix.isEmpty() && !attr.uri.isEmpty() ) {
1589                             String tmp = fInternalNamespaceContext.getPrefix(attr.uri);
1590 
1591                             if ((tmp == null) || (!tmp.equals(attr.prefix))) {
1592                                 tmp = getAttrPrefix(attr.uri);
1593                                 if (tmp == null) {
1594                                     if (fInternalNamespaceContext.declarePrefix(attr.prefix,
1595                                         attr.uri)) {
1596                                         writenamespace(attr.prefix, attr.uri);
1597                                     }
1598                                 } else {
1599                                     writenamespace(attr.prefix, attr.uri);
1600                                 }
1601                             }
1602                         }
1603                     }
1604 
1605                     writeAttributeWithPrefix(attr.prefix, attr.localpart,
1606                         attr.value);
1607                 }
1608                 fAttrNamespace = null;
1609                 fAttributeCache.clear();
1610             }
1611 
1612             if (currentElement.isEmpty) {
1613                 fElementStack.pop();
1614                 fInternalNamespaceContext.popContext();
1615                 fWriter.write(CLOSE_EMPTY_ELEMENT);
1616             } else {
1617                 fWriter.write(CLOSE_START_TAG);
1618             }
1619 
1620             fStartTagOpened = false;
1621         } catch (IOException ex) {
1622             fStartTagOpened = false;
1623             throw new XMLStreamException(ex);
1624         }
1625     }
1626 
1627     /**
1628      * marks open of start tag and writes the same into the writer.
1629      */
1630     private void openStartTag() throws IOException {
1631         fStartTagOpened = true;
1632         fWriter.write(OPEN_START_TAG);
1633     }
1634 
1635     /**
1636      *
1637      * @param uri
1638      * @return
1639      */
1640     private void correctPrefix(QName attr, int type) {
1641         String tmpPrefix;
1642         String prefix;
1643         String uri;
1644         prefix = attr.prefix;
1645         uri = attr.uri;
1646         boolean isSpecialCaseURI = false;
1647 
1648         if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
1649             if (uri == null) {
1650                 return;
1651             }
1652 
1653             if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix) && uri.equals(XMLConstants.DEFAULT_NS_PREFIX))
1654                 return;
1655 
1656             uri = fSymbolTable.addSymbol(uri);
1657 
1658             QName decl;
1659 
1660             for (int i = 0; i < fNamespaceDecls.size(); i++) {
1661                 decl = fNamespaceDecls.get(i);
1662 
1663                 if ((decl != null) && (decl.uri.equals(attr.uri))) {
1664                     attr.prefix = decl.prefix;
1665 
1666                     return;
1667                 }
1668             }
1669 
1670             tmpPrefix = fNamespaceContext.getPrefix(uri);
1671 
1672             if (XMLConstants.DEFAULT_NS_PREFIX.equals(tmpPrefix)) {
1673                 if (type == XMLStreamConstants.START_ELEMENT) {
1674                     return;
1675                 }
1676                 else if (type == XMLStreamConstants.ATTRIBUTE) {
1677                     //the uri happens to be the same as that of the default namespace
1678                     tmpPrefix = getAttrPrefix(uri);
1679                     isSpecialCaseURI = true;
1680                 }
1681             }
1682 
1683             if (tmpPrefix == null) {
1684                 StringBuilder genPrefix = new StringBuilder("zdef");
1685 
1686                 for (int i = 0; i < 1; i++) {
1687                     genPrefix.append(fPrefixGen.nextInt());
1688                 }
1689 
1690                 prefix = genPrefix.toString();
1691                 prefix = fSymbolTable.addSymbol(prefix);
1692             } else {
1693                 prefix = fSymbolTable.addSymbol(tmpPrefix);
1694             }
1695 
1696             if (tmpPrefix == null) {
1697                 if (isSpecialCaseURI) {
1698                     addAttrNamespace(prefix, uri);
1699                 } else {
1700                     QName qname = new QName();
1701                     qname.setValues(prefix, XMLConstants.XMLNS_ATTRIBUTE, null, uri);
1702                     fNamespaceDecls.add(qname);
1703                     fInternalNamespaceContext.declarePrefix(fSymbolTable.addSymbol(
1704                         prefix), uri);
1705                 }
1706             }
1707         }
1708 
1709         attr.prefix = prefix;
1710     }
1711 
1712     /**
1713      * return the prefix if the attribute has an uri the same as that of the default namespace
1714      */
1715     private String getAttrPrefix(String uri) {
1716         if (fAttrNamespace != null) {
1717             return fAttrNamespace.get(uri);
1718         }
1719         return null;
1720     }
1721     private void addAttrNamespace(String prefix, String uri) {
1722         if (fAttrNamespace == null) {
1723             fAttrNamespace = new HashMap<>();
1724         }
1725         fAttrNamespace.put(prefix, uri);
1726     }
1727     /**
1728      * @param uri
1729      * @return
1730      */
1731     private boolean isDefaultNamespace(String uri) {
1732         String defaultNamespace = fInternalNamespaceContext.getURI(DEFAULT_PREFIX);
1733         return Objects.equals(uri, defaultNamespace);
1734     }
1735 
1736     /**
1737      * @param prefix
1738      * @param uri
1739      * @return
1740      */
1741     private boolean checkUserNamespaceContext(String prefix, String uri) {
1742         if (fNamespaceContext.userContext != null) {
1743             String tmpURI = fNamespaceContext.userContext.getNamespaceURI(prefix);
1744 
1745             if ((tmpURI != null) && tmpURI.equals(uri)) {
1746                 return true;
1747             }
1748         }
1749 
1750         return false;
1751     }
1752 
1753     /**
1754      * Correct's namespaces  as per requirements of isReparisingNamespace property.
1755      */
1756     protected void repair() {
1757         Attribute attr;
1758         Attribute attr2;
1759         ElementState currentElement = fElementStack.peek();
1760         removeDuplicateDecls();
1761 
1762         for(int i=0 ; i< fAttributeCache.size();i++){
1763             attr = fAttributeCache.get(i);
1764             if((attr.prefix != null && !attr.prefix.isEmpty()) || (attr.uri != null && !attr.uri.isEmpty())) {
1765                 correctPrefix(currentElement,attr);
1766             }
1767         }
1768 
1769         if (!isDeclared(currentElement)) {
1770             if ((currentElement.prefix != null) &&
1771                     (currentElement.uri != null)) {
1772                 if ((!currentElement.prefix.isEmpty()) && (!currentElement.uri.isEmpty())) {
1773                     fNamespaceDecls.add(currentElement);
1774                 }
1775             }
1776         }
1777 
1778         for(int i=0 ; i< fAttributeCache.size();i++){
1779             attr = fAttributeCache.get(i);
1780             for(int j=i+1;j<fAttributeCache.size();j++){
1781                 attr2 = fAttributeCache.get(j);
1782                 if(!"".equals(attr.prefix)&& !"".equals(attr2.prefix)){
1783                     correctPrefix(attr,attr2);
1784                 }
1785             }
1786         }
1787 
1788         repairNamespaceDecl(currentElement);
1789 
1790         int i;
1791 
1792         for (i = 0; i < fAttributeCache.size(); i++) {
1793             attr = fAttributeCache.get(i);
1794             /* If 'attr' is an attribute and it is in no namespace(which means that prefix="", uri=""), attr's
1795                namespace should not be redinded. See [http://www.w3.org/TR/REC-xml-names/#defaulting].
1796              */
1797             if (attr.prefix != null && attr.prefix.isEmpty() && attr.uri != null && attr.uri.isEmpty()){
1798                 repairNamespaceDecl(attr);
1799             }
1800         }
1801 
1802         QName qname = null;
1803 
1804         for (i = 0; i < fNamespaceDecls.size(); i++) {
1805             qname = fNamespaceDecls.get(i);
1806 
1807             if (qname != null) {
1808                 fInternalNamespaceContext.declarePrefix(qname.prefix, qname.uri);
1809             }
1810         }
1811 
1812         for (i = 0; i < fAttributeCache.size(); i++) {
1813             attr = fAttributeCache.get(i);
1814             correctPrefix(attr, XMLStreamConstants.ATTRIBUTE);
1815         }
1816     }
1817 
1818     /*
1819      *If element and/or attribute names in the same start or empty-element tag
1820      *are bound to different namespace URIs and are using the same prefix then
1821      *the element or the first occurring attribute retains the original prefix
1822      *and the following attributes have their prefixes replaced with a new prefix
1823      *that is bound to the namespace URIs of those attributes.
1824      */
1825     void correctPrefix(QName attr1, QName attr2) {
1826         String tmpPrefix;
1827         QName decl;
1828 
1829         checkForNull(attr1);
1830         checkForNull(attr2);
1831 
1832         if(attr1.prefix.equals(attr2.prefix) && !(attr1.uri.equals(attr2.uri))){
1833 
1834             tmpPrefix = fNamespaceContext.getPrefix(attr2.uri);
1835 
1836             if (tmpPrefix != null) {
1837                 attr2.prefix = fSymbolTable.addSymbol(tmpPrefix);
1838             } else {
1839                 for (int n=0; n<fNamespaceDecls.size(); n++) {
1840                     decl = fNamespaceDecls.get(n);
1841                     if(decl != null && (decl.uri.equals(attr2.uri))){
1842                         attr2.prefix = decl.prefix;
1843 
1844                         return;
1845                     }
1846                 }
1847 
1848                 //No namespace mapping found , so declare prefix.
1849                 StringBuilder genPrefix = new StringBuilder("zdef");
1850 
1851                 for (int k = 0; k < 1; k++) {
1852                     genPrefix.append(fPrefixGen.nextInt());
1853                 }
1854 
1855                 tmpPrefix = genPrefix.toString();
1856                 tmpPrefix = fSymbolTable.addSymbol(tmpPrefix);
1857                 attr2.prefix = tmpPrefix;
1858 
1859                 QName qname = new QName();
1860                 qname.setValues(tmpPrefix, XMLConstants.XMLNS_ATTRIBUTE, null,
1861                     attr2.uri);
1862                 fNamespaceDecls.add(qname);
1863             }
1864         }
1865     }
1866 
1867     void checkForNull(QName attr) {
1868         if (attr.prefix == null) attr.prefix = XMLConstants.DEFAULT_NS_PREFIX;
1869         if (attr.uri == null) attr.uri = XMLConstants.DEFAULT_NS_PREFIX;
1870     }
1871 
1872     void removeDuplicateDecls(){
1873         QName decl1,decl2;
1874         for(int i =0; i<fNamespaceDecls.size(); i++) {
1875             decl1 = fNamespaceDecls.get(i);
1876             if(decl1!=null) {
1877                 for(int j=i+1;j<fNamespaceDecls.size();j++){
1878                     decl2 = fNamespaceDecls.get(j);
1879                     // QName.equals relies on identity equality, so we can't use it,
1880                     // because prefixes aren't interned
1881                     if(decl2!=null && decl1.prefix.equals(decl2.prefix) && decl1.uri.equals(decl2.uri))
1882                         fNamespaceDecls.remove(j);
1883                 }
1884             }
1885         }
1886     }
1887 
1888     /*
1889      *If an element or attribute name is bound to a prefix and there is a namespace
1890      *declaration that binds that prefix to a different URI then that namespace declaration
1891      *is either removed if the correct mapping is inherited from the parent context of that element,
1892      *or changed to the namespace URI of the element or attribute using that prefix.
1893      *
1894      */
1895     void repairNamespaceDecl(QName attr) {
1896         QName decl;
1897         String tmpURI;
1898 
1899         //check for null prefix.
1900         for (int j = 0; j < fNamespaceDecls.size(); j++) {
1901             decl = fNamespaceDecls.get(j);
1902 
1903             if (decl != null) {
1904                 if ((attr.prefix != null) &&
1905                         (attr.prefix.equals(decl.prefix) &&
1906                         !(attr.uri.equals(decl.uri)))) {
1907                     tmpURI = fNamespaceContext.getNamespaceURI(attr.prefix);
1908 
1909                     //see if you need to add to symbole table.
1910                     if (tmpURI != null) {
1911                         if (tmpURI.equals(attr.uri)) {
1912                             fNamespaceDecls.set(j, null);
1913                         } else {
1914                             decl.uri = attr.uri;
1915                         }
1916                     }
1917                 }
1918             }
1919         }
1920     }
1921 
1922     boolean isDeclared(QName attr) {
1923         QName decl;
1924 
1925         for (int n = 0; n < fNamespaceDecls.size(); n++) {
1926             decl = fNamespaceDecls.get(n);
1927 
1928             if ((attr.prefix != null) &&
1929                     ((attr.prefix.equals(decl.prefix)) && (decl.uri.equals(attr.uri)))) {
1930                 return true;
1931             }
1932         }
1933 
1934         if (attr.uri != null) {
1935             if (fNamespaceContext.getPrefix(attr.uri) != null) {
1936                 return true;
1937             }
1938         }
1939 
1940         return false;
1941     }
1942 
1943     /*
1944      * Start of Internal classes.
1945      *
1946      */
1947     protected class ElementStack {
1948         /** The stack data. */
1949         protected ElementState[] fElements;
1950 
1951         /** The size of the stack. */
1952         protected short fDepth;
1953 
1954         /** Default constructor. */
1955         public ElementStack() {
1956             fElements = new ElementState[10];
1957 
1958             for (int i = 0; i < fElements.length; i++) {
1959                 fElements[i] = new ElementState();
1960             }
1961         }
1962 
1963         /**
1964          * Pushes an element on the stack.
1965          * <p>
1966          * <strong>Note:</strong> The QName values are copied into the
1967          * stack. In other words, the caller does <em>not</em> orphan
1968          * the element to the stack. Also, the QName object returned
1969          * is <em>not</em> orphaned to the caller. It should be
1970          * considered read-only.
1971          *
1972          * @param element The element to push onto the stack.
1973          *
1974          * @return Returns the actual QName object that stores the
1975          */
1976         public ElementState push(ElementState element) {
1977             if (fDepth == fElements.length) {
1978                 ElementState[] array = new ElementState[fElements.length * 2];
1979                 System.arraycopy(fElements, 0, array, 0, fDepth);
1980                 fElements = array;
1981 
1982                 for (int i = fDepth; i < fElements.length; i++) {
1983                     fElements[i] = new ElementState();
1984                 }
1985             }
1986 
1987             fElements[fDepth].setValues(element);
1988 
1989             return fElements[fDepth++];
1990         }
1991 
1992         /**
1993          *
1994          * @param prefix
1995          * @param localpart
1996          * @param rawname
1997          * @param uri
1998          * @param isEmpty
1999          * @return
2000          */
2001         public ElementState push(String prefix, String localpart,
2002             String rawname, String uri, boolean isEmpty) {
2003             if (fDepth == fElements.length) {
2004                 ElementState[] array = new ElementState[fElements.length * 2];
2005                 System.arraycopy(fElements, 0, array, 0, fDepth);
2006                 fElements = array;
2007 
2008                 for (int i = fDepth; i < fElements.length; i++) {
2009                     fElements[i] = new ElementState();
2010                 }
2011             }
2012 
2013             fElements[fDepth].setValues(prefix, localpart, rawname, uri, isEmpty);
2014 
2015             return fElements[fDepth++];
2016         }
2017 
2018         /**
2019          * Pops an element off of the stack by setting the values of
2020          * the specified QName.
2021          * <p>
2022          * <strong>Note:</strong> The object returned is <em>not</em>
2023          * orphaned to the caller. Therefore, the caller should consider
2024          * the object to be read-only.
2025          */
2026         public ElementState pop() {
2027             return fElements[--fDepth];
2028         }
2029 
2030         /** Clears the stack without throwing away existing QName objects. */
2031         public void clear() {
2032             fDepth = 0;
2033         }
2034 
2035         /**
2036          * This function is as a result of optimization done for endElement --
2037          * we dont need to set the value for every end element we encouter.
2038          * For Well formedness checks we can have the same QName object that was pushed.
2039          * the values will be set only if application need to know about the endElement
2040          */
2041         public ElementState peek() {
2042             return fElements[fDepth - 1];
2043         }
2044 
2045         /**
2046          *
2047          * @return
2048          */
2049         public boolean empty() {
2050             return (fDepth > 0) ? false : true;
2051         }
2052     }
2053 
2054     /**
2055      * Maintains element state . localName for now.
2056      */
2057     class ElementState extends QName {
2058         public boolean isEmpty = false;
2059 
2060         public ElementState() {}
2061 
2062         public ElementState(String prefix, String localpart, String rawname,
2063             String uri) {
2064             super(prefix, localpart, rawname, uri);
2065         }
2066 
2067         public void setValues(String prefix, String localpart, String rawname,
2068             String uri, boolean isEmpty) {
2069             super.setValues(prefix, localpart, rawname, uri);
2070             this.isEmpty = isEmpty;
2071         }
2072     }
2073 
2074     /**
2075      * Attributes
2076      */
2077     class Attribute extends QName {
2078         String value;
2079 
2080         Attribute(String value) {
2081             super();
2082             this.value = value;
2083         }
2084     }
2085 
2086     /**
2087      * Implementation of NamespaceContext .
2088      *
2089      */
2090     class NamespaceContextImpl implements NamespaceContext {
2091         //root namespace context set by user.
2092         NamespaceContext userContext = null;
2093 
2094         //context built by the writer.
2095         NamespaceSupport internalContext = null;
2096 
2097         public String getNamespaceURI(String prefix) {
2098             String uri = null;
2099 
2100             if (prefix != null) {
2101                 prefix = fSymbolTable.addSymbol(prefix);
2102             }
2103 
2104             if (internalContext != null) {
2105                 uri = internalContext.getURI(prefix);
2106 
2107                 if (uri != null) {
2108                     return uri;
2109                 }
2110             }
2111 
2112             if (userContext != null) {
2113                 uri = userContext.getNamespaceURI(prefix);
2114 
2115                 return uri;
2116             }
2117 
2118             return null;
2119         }
2120 
2121         public String getPrefix(String uri) {
2122             String prefix = null;
2123 
2124             if (uri != null) {
2125                 uri = fSymbolTable.addSymbol(uri);
2126             }
2127 
2128             if (internalContext != null) {
2129                 prefix = internalContext.getPrefix(uri);
2130 
2131                 if (prefix != null) {
2132                     return prefix;
2133                 }
2134             }
2135 
2136             if (userContext != null) {
2137                 return userContext.getPrefix(uri);
2138             }
2139 
2140             return null;
2141         }
2142 
2143         //Cleanup note: leaving these warnings to a xerces.internal.util cleanup
2144         public Iterator<String> getPrefixes(String uri) {
2145             List<String> prefixes = null;
2146             Iterator<String> itr = null;
2147 
2148             if (uri != null) {
2149                 uri = fSymbolTable.addSymbol(uri);
2150             }
2151 
2152             if (userContext != null) {
2153                 itr = userContext.getPrefixes(uri);
2154             }
2155 
2156             if (internalContext != null) {
2157                 prefixes = internalContext.getPrefixes(uri);
2158             }
2159 
2160             if ((prefixes == null) && (itr != null)) {
2161                 return itr;
2162             } else if ((prefixes != null) && (itr == null)) {
2163                 return new ReadOnlyIterator<>(prefixes.iterator());
2164             } else if ((prefixes != null) && (itr != null)) {
2165                 String ob = null;
2166 
2167                 while (itr.hasNext()) {
2168                     ob = itr.next();
2169 
2170                     if (ob != null) {
2171                         ob = fSymbolTable.addSymbol(ob);
2172                     }
2173 
2174                     if (!prefixes.contains(ob)) {
2175                         prefixes.add(ob);
2176                     }
2177                 }
2178 
2179                 return new ReadOnlyIterator<>(prefixes.iterator());
2180             }
2181 
2182             return fReadOnlyIterator;
2183         }
2184     }
2185 
2186     // -- Map Interface --------------------------------------------------
2187 
2188     @Override
2189     public int size() {
2190         return 1;
2191     }
2192 
2193     @Override
2194     public boolean isEmpty() {
2195         return false;
2196     }
2197 
2198     @Override
2199     public boolean containsKey(Object key) {
2200         return key.equals(OUTPUTSTREAM_PROPERTY);
2201     }
2202 
2203     /**
2204      * Returns the value associated to an implementation-specific
2205      * property.
2206      */
2207     @Override
2208     public Object get(Object key) {
2209         if (key.equals(OUTPUTSTREAM_PROPERTY)) {
2210             return fOutputStream;
2211         }
2212         return null;
2213     }
2214 
2215     @Override
2216     public Set<Entry<Object,Object>> entrySet() {
2217         throw new UnsupportedOperationException();
2218     }
2219 
2220     /**
2221      * Overrides the method defined in AbstractMap which is
2222      * not completely implemented. Calling toString() in
2223      * AbstractMap would cause an unsupported exection to
2224      * be thrown.
2225      */
2226     @Override
2227     public String toString() {
2228         return getClass().getName() + "@" + Integer.toHexString(hashCode());
2229     }
2230 
2231     /**
2232      * Overrides the method defined in AbstractMap
2233      * This is required by the toString() method
2234      */
2235     @Override
2236     public int hashCode() {
2237         return fElementStack.hashCode();
2238     }
2239     /**
2240      * Overrides the method defined in AbstractMap
2241      * This is required to satisfy the contract for hashCode.
2242      */
2243     @Override
2244     public boolean equals(Object obj) {
2245         return (this == obj);
2246     }
2247 }