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