KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xmlpull > mxp1_serializer > MXSerializer


1 package org.xmlpull.mxp1_serializer;
2
3 import java.io.IOException JavaDoc;
4 import java.io.OutputStream JavaDoc;
5 import java.io.OutputStreamWriter JavaDoc;
6 import java.io.Writer JavaDoc;
7
8 import org.xmlpull.v1.XmlSerializer;
9
10 /**
11  * Implementation of XmlSerializer interface from XmlPull V1 API.
12  * This implementation is optimzied for performance and low memory footprint.
13  *
14  * <p>Implemented features:<ul>
15  * <li> FEATURE_NAMES_INTERNED - when enabled all returned names
16  * (namespaces, prefixes) will be interned and it is required that
17  * all names passed as arguments MUST be interned
18  * <li> FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE
19  * </ul>
20  * <p>Implemented properties:<ul>
21  * <li> PROPERTY_SERIALIZER_INDENTATION
22  * <li> PROPERTY_SERIALIZER_LINE_SEPARATOR
23  * </ul>
24  *
25  */

26 public class MXSerializer implements XmlSerializer {
27     protected final static String JavaDoc XML_URI = "http://www.w3.org/XML/1998/namespace";
28     protected final static String JavaDoc XMLNS_URI = "http://www.w3.org/2000/xmlns/";
29     private static final boolean TRACE_SIZING = false;
30
31     protected final String JavaDoc FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE =
32         "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
33     protected final String JavaDoc FEATURE_NAMES_INTERNED =
34         "http://xmlpull.org/v1/doc/properties.html#names-interned";
35     protected final String JavaDoc PROPERTY_SERIALIZER_INDENTATION =
36         "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
37     protected final String JavaDoc PROPERTY_SERIALIZER_LINE_SEPARATOR =
38         "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
39
40     // properties/features
41
protected boolean namesInterned;
42     protected boolean attributeUseApostrophe;
43     protected String JavaDoc indentationString = null; //" ";
44
protected String JavaDoc lineSeparator = null; //"\n";
45

46     protected Writer JavaDoc out;
47
48     protected int autoDeclaredPrefixes;
49
50     protected int depth = 0;
51
52     // element stack
53
protected String JavaDoc elNamespace[] = new String JavaDoc[ 2 ];
54     protected String JavaDoc elName[] = new String JavaDoc[ elNamespace.length ];
55     protected int elNamespaceCount[] = new int[ elNamespace.length ];
56
57     //namespace stack
58
protected int namespaceEnd = 0;
59     protected String JavaDoc namespacePrefix[] = new String JavaDoc[ 8 ];
60     protected String JavaDoc namespaceUri[] = new String JavaDoc[ namespacePrefix.length ];
61
62     protected boolean finished;
63     protected boolean pastRoot;
64     protected boolean setPrefixCalled;
65     protected boolean startTagIncomplete;
66
67     protected static final String JavaDoc precomputedPrefixes[];
68     static {
69         precomputedPrefixes = new String JavaDoc[10]; //arbitrary number ...
70
for (int i = 0; i < precomputedPrefixes.length; i++)
71         {
72             precomputedPrefixes[i] = ("n"+i).intern();
73         }
74     }
75
76     protected void reset() {
77         out = null;
78         autoDeclaredPrefixes = 0;
79         depth = 0;
80
81         // nullify references on all levels to allow it to be GCed
82
for (int i = 0; i < elNamespaceCount.length; i++)
83         {
84             elName[ i ] = null;
85             elNamespace[ i ] = null;
86             elNamespaceCount[ i ] = 2;
87         }
88
89
90         namespaceEnd = 0;
91
92
93         //NOTE: no need to intern() as all literal strings and string-valued constant expressions
94
//are interned. String literals are defined in §3.10.5 of the Java Language Specification
95
// just checking ...
96
//assert "xmlns" == "xmlns".intern();
97
//assert XMLNS_URI == XMLNS_URI.intern();
98

99         //TODO: how to prevent from reporting this namespace?
100
// this is special namespace declared for consistensy with XML infoset
101
namespacePrefix[ namespaceEnd ] = "xmlns";
102         namespaceUri[ namespaceEnd ] = XMLNS_URI;
103         ++namespaceEnd;
104
105         namespacePrefix[ namespaceEnd ] = "xml";
106         namespaceUri[ namespaceEnd ] = XML_URI;
107         ++namespaceEnd;
108
109         finished = false;
110         pastRoot = false;
111         setPrefixCalled = false;
112         startTagIncomplete = false;
113     }
114
115
116     protected void ensureElementsCapacity() {
117         int elStackSize = elName.length;
118         //assert (depth + 1) >= elName.length;
119
// we add at least one extra slot ...
120
int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25
121
if(TRACE_SIZING) {
122             System.err.println("elStackSize "+elStackSize+" ==> "+newSize);
123         }
124         boolean needsCopying = elStackSize > 0;
125         String JavaDoc[] arr = null;
126         arr = new String JavaDoc[newSize];
127         if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize);
128         elName = arr;
129         arr = new String JavaDoc[newSize];
130         if(needsCopying) System.arraycopy(elNamespace, 0, arr, 0, elStackSize);
131         elNamespace = arr;
132
133         int[] iarr = new int[newSize];
134         if(needsCopying) {
135             System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize);
136         } else {
137             // special initialization
138
iarr[0] = 0;
139         }
140         elNamespaceCount = iarr;
141     }
142
143     protected void ensureNamespacesCapacity() { //int size) {
144
//int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0;
145
//assert (namespaceEnd >= namespacePrefix.length);
146

147         //if(size >= namespaceSize) {
148
//int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25
149
int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8;
150         if(TRACE_SIZING) {
151             System.err.println("namespaceSize "+namespacePrefix.length+" ==> "+newSize);
152         }
153         String JavaDoc[] newNamespacePrefix = new String JavaDoc[newSize];
154         String JavaDoc[] newNamespaceUri = new String JavaDoc[newSize];
155         if(namespacePrefix != null) {
156             System.arraycopy(
157                 namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd);
158             System.arraycopy(
159                 namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
160         }
161         namespacePrefix = newNamespacePrefix;
162         namespaceUri = newNamespaceUri;
163
164         // TODO use hashes for quick namespace->prefix lookups
165
// if( ! allStringsInterned ) {
166
// int[] newNamespacePrefixHash = new int[newSize];
167
// if(namespacePrefixHash != null) {
168
// System.arraycopy(
169
// namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd);
170
// }
171
// namespacePrefixHash = newNamespacePrefixHash;
172
// }
173
//prefixesSize = newSize;
174
// ////assert nsPrefixes.length > size && nsPrefixes.length == newSize
175
//}
176
}
177
178
179     public void setFeature(String JavaDoc name,
180                            boolean state) throws IllegalArgumentException JavaDoc, IllegalStateException JavaDoc
181     {
182         if(name == null) {
183             throw new IllegalArgumentException JavaDoc("feature name can not be null");
184         }
185         if(FEATURE_NAMES_INTERNED.equals(name)) {
186             namesInterned = state;
187         } else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) {
188             attributeUseApostrophe = state;
189         } else {
190             throw new IllegalStateException JavaDoc("unsupported feature "+name);
191         }
192     }
193
194     public boolean getFeature(String JavaDoc name) throws IllegalArgumentException JavaDoc
195     {
196         if(name == null) {
197             throw new IllegalArgumentException JavaDoc("feature name can not be null");
198         }
199         if(FEATURE_NAMES_INTERNED.equals(name)) {
200             return namesInterned;
201         } else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) {
202             return attributeUseApostrophe;
203         } else {
204             return false;
205         }
206     }
207
208     protected boolean writeLineSepartor;
209     protected boolean writeIndentation;
210
211     protected boolean seenTag;
212     protected int indentLevel;
213
214     protected boolean doIndent;
215     protected int offsetNewLine;
216     protected int indentationJump;
217     protected char[] indentationBuf;
218     protected int maxIndentLevel;
219
220     protected void rebuildIndentationBuf() {
221         final int maxIndent = 65;
222         int bufSize = 0;
223         offsetNewLine = 0;
224         if(writeLineSepartor) {
225             offsetNewLine = lineSeparator.length();
226             bufSize += offsetNewLine;
227         }
228         maxIndentLevel = 0;
229         if(writeIndentation) {
230             indentationJump = indentationString.length();
231             maxIndentLevel = maxIndent / indentationJump;
232             bufSize += maxIndentLevel * indentationJump;
233         }
234         if(indentationBuf == null || indentationBuf.length < bufSize) {
235             indentationBuf = new char[bufSize + 8];
236         }
237         int bufPos = 0;
238         if(writeLineSepartor) {
239             for (int i = 0; i < lineSeparator.length(); i++)
240             {
241                 indentationBuf[ bufPos++ ] = lineSeparator.charAt(i);
242             }
243         }
244         if(writeIndentation) {
245             for (int i = 0; i < maxIndentLevel; i++)
246             {
247                 for (int j = 0; j < indentationString.length(); j++)
248                 {
249                     indentationBuf[ bufPos++ ] = indentationString.charAt(j);
250                 }
251             }
252         }
253     }
254
255     // if(doIndent) writeIndent();
256
protected void writeIndent() throws IOException JavaDoc {
257         int start = writeLineSepartor ? 0 : offsetNewLine;
258         int level = (indentLevel > maxIndentLevel) ? maxIndentLevel : indentLevel;
259         out.write( indentationBuf, start, (level * indentationJump) - start);
260     }
261
262     public void setProperty(String JavaDoc name,
263                             Object JavaDoc value) throws IllegalArgumentException JavaDoc, IllegalStateException JavaDoc
264     {
265         if(name == null) {
266             throw new IllegalArgumentException JavaDoc("property name can not be null");
267         }
268 // if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) {
269
// indentationString = (String)value;
270
// } else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) {
271
// lineSeparator = (String)value;
272
// } else {
273
throw new IllegalStateException JavaDoc("unsupported property "+name);
274 // }
275
// writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0;
276
// writeIndentation = indentationString != null && indentationString.length() > 0;
277
// // optimize - do not write when nothing to write ...
278
// doIndent = indentationString != null && (writeLineSepartor || writeIndentation);
279
// //NOTE: when indentationString == null there is no indentation
280
// // (even though writeLineSeparator may be true ...)
281
// rebuildIndentationBuf();
282
}
283
284     public Object JavaDoc getProperty(String JavaDoc name) throws IllegalArgumentException JavaDoc
285     {
286         if(name == null) {
287             throw new IllegalArgumentException JavaDoc("property name can not be null");
288         }
289 // if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) {
290
// return indentationString;
291
// } else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) {
292
// return lineSeparator;
293
// } else {
294
return null;
295 // }
296
}
297
298
299     public void setOutput (Writer JavaDoc writer)
300     {
301         reset();
302         out = writer;
303     }
304
305     public void setOutput (OutputStream JavaDoc os, String JavaDoc encoding) throws IOException JavaDoc
306     {
307         if(os == null) throw new IllegalArgumentException JavaDoc("output stream can not be null");
308         reset();
309         if(encoding != null) {
310             out = new OutputStreamWriter JavaDoc(os, encoding);
311         } else {
312             out = new OutputStreamWriter JavaDoc(os);
313         }
314     }
315
316     public void startDocument (String JavaDoc encoding, Boolean JavaDoc standalone) throws IOException JavaDoc
317     {
318         out.write("<?xml version=\"1.0\"");
319         if(encoding != null) {
320             out.write(" encoding='");
321             out.write(encoding);
322             out.write("'");
323         }
324         if(standalone != null) {
325             if(standalone.booleanValue()) {
326                 out.write(" standalone='yes'");
327             } else {
328                 out.write(" standalone='no'");
329             }
330         }
331         out.write("?>");
332     }
333
334     public void endDocument () throws IOException JavaDoc
335     {
336         // close all unclosed tag;
337
while(depth > 0) {
338             endTag(elNamespace[ depth ], elName[ depth ]);
339         }
340         //assert depth == 0;
341
//assert startTagIncomplete == false;
342
finished = pastRoot = startTagIncomplete = true;
343         out.flush();
344     }
345
346     public void setPrefix (String JavaDoc prefix, String JavaDoc namespace) throws IOException JavaDoc
347     {
348         if(startTagIncomplete) closeStartTag();
349         //assert prefix != null;
350
//assert namespace != null;
351
prefix = prefix.intern();
352
353         //check that prefix is not duplicated ...
354
int start = elNamespaceCount[ depth ];
355         for (int i = start; i < namespaceEnd; i++)
356         {
357             if(prefix == namespacePrefix[ i ]) {
358                 throw new IllegalStateException JavaDoc("duplicated prefix "+printable(prefix));
359             }
360         }
361         namespace = namespace.intern();
362
363         if(namespaceEnd >= namespacePrefix.length) {
364             ensureNamespacesCapacity();
365         }
366         namespacePrefix[ namespaceEnd ] = prefix;
367         namespaceUri[ namespaceEnd ] = namespace;
368         ++namespaceEnd;
369         setPrefixCalled = true;
370     }
371
372     protected String JavaDoc lookupOrDeclarePrefix( String JavaDoc namespace ) {
373         return getPrefix(namespace, true);
374     }
375
376     public String JavaDoc getPrefix (String JavaDoc namespace, boolean generatePrefix)
377
378     {
379         //assert namespace != null;
380
if(namesInterned) {
381             //assert namespace != namespace.intern();
382
// if(namespace != namespace.intern()) {
383
// throw new IllegalArgumentException(
384
// "namespace msut be interned when NAMES INTERNED feature is enabled");
385
// }
386
} else {
387             // whenString is interned we can do much faster namespace stack lookups ...
388
namespace = namespace.intern();
389         }
390         // first check if namespace is already in scope
391
for (int i = namespaceEnd - 1; i >= 0 ; --i)
392         {
393             if(namespace == namespaceUri[ i ]) {
394                 String JavaDoc prefix = namespacePrefix[ i ];
395                 // now check that prefix is still in scope
396
for (int p = namespaceEnd - 1; p > i ; --p)
397                 {
398                     if(prefix == namespacePrefix[ p ])
399                         continue; // too bad - prefix is redeclared with different namespace
400
}
401                 return prefix;
402             }
403         }
404
405         // so not found it ...
406
if(!generatePrefix) {
407             return null;
408         }
409         return generatePrefix(namespace);
410     }
411
412     private String JavaDoc generatePrefix(String JavaDoc namespace) {
413         //assert namespace == namespace.intern();
414
while(true) {
415             ++autoDeclaredPrefixes;
416             //fast lookup uses table that was pre-initialized in static{} ....
417
String JavaDoc prefix = autoDeclaredPrefixes < precomputedPrefixes.length
418                 ? precomputedPrefixes[autoDeclaredPrefixes] : ("n"+autoDeclaredPrefixes).intern();
419             // make sure this prefix is not declared in any scope (avoid hiding in-scope prefixes)!
420
for (int i = namespaceEnd - 1; i >= 0 ; --i)
421             {
422                 if(prefix == namespacePrefix[ i ]) {
423                     continue; // prefix is already declared - generate new and try again
424
}
425             }
426             // declare prefix
427

428             if(namespaceEnd >= namespacePrefix.length) {
429                 ensureNamespacesCapacity();
430             }
431             namespacePrefix[ namespaceEnd ] = prefix;
432             namespaceUri[ namespaceEnd ] = namespace;
433             ++namespaceEnd;
434
435             return prefix;
436         }
437     }
438
439     public int getDepth()
440     {
441         return depth;
442     }
443
444     public String JavaDoc getNamespace ()
445     {
446         return elNamespace[depth];
447     }
448
449     public String JavaDoc getName()
450     {
451         return elName[depth];
452     }
453
454     public XmlSerializer startTag (String JavaDoc namespace, String JavaDoc name) throws IOException JavaDoc
455     {
456         if(startTagIncomplete) closeStartTag();
457         setPrefixCalled = false;
458         startTagIncomplete = true;
459         ++depth;
460         if( (depth + 1) >= elName.length) {
461             ensureElementsCapacity();
462         }
463         ////assert namespace != null;
464
elNamespace[ depth ] = namespace;
465         //assert name != null;
466
elName[ depth ] = name;
467         out.write('<');
468         if(namespace != null) {
469             if(namespace.length() > 0) {
470                 String JavaDoc prefix = lookupOrDeclarePrefix( namespace );
471                 //assert prefix != null;
472
// make sure that default ("") namespace to not print ":"
473
if(prefix.length() > 0) {
474                     out.write(prefix);
475                     out.write(':');
476                 }
477             } else {
478                 // make sure that default namespace can be declared
479
for (int i = namespaceEnd - 1; i >= 0 ; --i)
480                 {
481                     if(namespacePrefix[ i ] == "") {
482                         String JavaDoc uri = namespaceUri[ i ];
483                         if(uri == null) {
484                             // declare default namespace
485
setPrefix("", "");
486                         } else if(uri.length() > 0) {
487                             throw new IllegalStateException JavaDoc(
488                                 "start tag can not be writtwen in empty default namespace "+
489                                     "as default namespace is currently bound to '"+uri+"'");
490                         }
491                         break;
492                     }
493                 }
494             }
495
496         }
497         out.write(name);
498         return this;
499     }
500
501     public XmlSerializer attribute (String JavaDoc namespace, String JavaDoc name,
502                                     String JavaDoc value) throws IOException JavaDoc
503     {
504         if(!startTagIncomplete) {
505             throw new IllegalArgumentException JavaDoc("startTag() must be called before attribute()");
506         }
507         //assert setPrefixCalled == false;
508
out.write(' ');
509         ////assert namespace != null;
510
if(namespace != null && namespace.length() > 0) {
511             namespace = namespace.intern();
512             String JavaDoc prefix = lookupOrDeclarePrefix( namespace );
513             //assert( prefix != null);
514
if(prefix.length() == 0) {
515                 // needs to declare prefix to hold default namespace
516
prefix = generatePrefix(namespace);
517             }
518             out.write(prefix);
519             out.write(':');
520         }
521         //assert name != null;
522
out.write(name);
523         out.write('=');
524         //assert value != null;
525
out.write( attributeUseApostrophe ? '\'' : '"');
526         writeAttributeValue(value, out);
527         out.write( attributeUseApostrophe ? '\'' : '"');
528         return this;
529     }
530
531     protected void closeStartTag() throws IOException JavaDoc {
532         if(finished) {
533             throw new IllegalArgumentException JavaDoc("trying to write past already finished output");
534         }
535         if(setPrefixCalled) {
536             throw new IllegalArgumentException JavaDoc(
537                 "startTag() must be called immediately after setPrefix()");
538         }
539         if(!startTagIncomplete) {
540             throw new IllegalArgumentException JavaDoc("trying to close start tag that is not declared");
541         }
542
543         // write all namespace delcarations!
544
writeNamespaceDeclarations();
545         out.write('>');
546         elNamespaceCount[ depth ] = namespaceEnd;
547         startTagIncomplete = false;
548     }
549
550     private void writeNamespaceDeclarations() throws IOException JavaDoc
551     {
552         int start = elNamespaceCount[ depth - 1 ];
553         for (int i = start; i < namespaceEnd; i++)
554         {
555             if(namespacePrefix[ i ] != "") {
556                 out.write(" xmlns:");
557                 out.write(namespacePrefix[ i ]);
558                 out.write('=');
559             } else {
560                 out.write(" xmlns=");
561             }
562             out.write( attributeUseApostrophe ? '\'' : '"');
563
564             //NOTE: escaping of namespace value the same way as attributes!!!!
565
writeAttributeValue(namespaceUri[ i ], out);
566
567             out.write( attributeUseApostrophe ? '\'' : '"');
568         }
569     }
570
571     public XmlSerializer endTag (String JavaDoc namespace, String JavaDoc name) throws IOException JavaDoc
572     {
573         // check that level is valid
574
////assert namespace != null;
575
if(namespace != null) {
576             namespace = namespace.intern();
577         }
578         if(namespace != elNamespace[ depth ])
579         {
580             throw new IllegalArgumentException JavaDoc(
581                 "expected namespace "+printable(elNamespace[ depth ])
582                     +" and not "+printable(namespace));
583         }
584         if((name != null && !name.equals(elName[ depth ]))
585            || name != elName[ depth ])
586         {
587             throw new IllegalArgumentException JavaDoc(
588                 "expected element name "+printable(elName[ depth ])+" and not '"+printable(name));
589         }
590         if(startTagIncomplete) {
591             writeNamespaceDeclarations();
592             out.write("/>");
593         } else {
594             out.write("</");
595             if(namespace != null && namespace.length() > 0) {
596                 String JavaDoc prefix = lookupOrDeclarePrefix( namespace );
597                 //assert( prefix != null);
598
if(prefix.length() > 0) {
599                     out.write(prefix);
600                     out.write(':');
601                 }
602             }
603             out.write(name);
604             out.write('>');
605
606         }
607         --depth;
608         namespaceEnd = elNamespaceCount[ depth ];
609         startTagIncomplete = false;
610         return this;
611     }
612
613     public XmlSerializer text (String JavaDoc text) throws IOException JavaDoc
614     {
615         //assert text != null;
616
if(startTagIncomplete || setPrefixCalled) closeStartTag();
617         writeElementContent(text, out);
618         return this;
619     }
620
621     public XmlSerializer text (char [] buf, int start, int len) throws IOException JavaDoc
622     {
623         if(startTagIncomplete || setPrefixCalled) closeStartTag();
624         writeElementContent(buf, start, len, out);
625         return this;
626     }
627
628     public void cdsect (String JavaDoc text) throws IOException JavaDoc
629     {
630         if(startTagIncomplete || setPrefixCalled) closeStartTag();
631         out.write("<![CDATA[");
632         out.write(text); //escape?
633
out.write("]]>");
634     }
635
636     public void entityRef (String JavaDoc text) throws IOException JavaDoc
637     {
638         if(startTagIncomplete || setPrefixCalled) closeStartTag();
639         out.write('&');
640         out.write(text); //escape?
641
out.write(';');
642     }
643
644     public void processingInstruction (String JavaDoc text) throws IOException JavaDoc
645     {
646         if(startTagIncomplete || setPrefixCalled) closeStartTag();
647         out.write("<?");
648         out.write(text); //escape?
649
out.write("?>");
650     }
651
652     public void comment (String JavaDoc text) throws IOException JavaDoc
653     {
654         if(startTagIncomplete || setPrefixCalled) closeStartTag();
655         out.write("<!--");
656         out.write(text); //escape?
657
out.write("-->");
658     }
659
660     public void docdecl (String JavaDoc text) throws IOException JavaDoc
661     {
662         if(startTagIncomplete || setPrefixCalled) closeStartTag();
663         out.write("<!DOCTYPE");
664         out.write(text); //escape?
665
out.write(">");
666     }
667
668     public void ignorableWhitespace (String JavaDoc text) throws IOException JavaDoc
669     {
670         if(startTagIncomplete || setPrefixCalled) closeStartTag();
671         out.write(text); //no escape?
672
}
673
674     public void flush () throws IOException JavaDoc
675     {
676         if(startTagIncomplete) closeStartTag();
677         out.flush();
678     }
679
680     // --- utility methods
681

682     protected void writeAttributeValue(String JavaDoc value, Writer JavaDoc out) throws IOException JavaDoc
683     {
684         //.[apostrophe and <, & escaped],
685
char quot = attributeUseApostrophe ? '\'' : '"';
686         String JavaDoc quotEntity = attributeUseApostrophe ? "&apos;" : "&quot;";
687         int posLt = value.indexOf('<');
688         int posAmp = value.indexOf('&');
689         int posQuot = value.indexOf(quot);
690         // painful loop ...
691
int pos = 0;
692         while(true) {
693             if(posLt == -1 && posAmp == -1 && posQuot == -1) {
694                 if(pos > 0) {
695                     out.write(value.substring(pos));
696                 } else {
697                     out.write(value); // this is shortcut to the most common case
698
}
699                 break;
700             } else if(posQuot != -1 //TODO check dead expr optimization
701
&& (posAmp == -1 || (posAmp != -1 && posQuot < posAmp))
702                       && (posLt == -1 || (posLt != -1 && posQuot < posLt))
703                      )
704             {
705                 if(pos < posQuot) out.write(value.substring(pos, posQuot));
706                 out.write(quotEntity);
707                 pos = posQuot + 1;
708                 posQuot = value.indexOf(quot, pos);
709             } else if(posAmp != -1
710                       && (posQuot == -1 || (posQuot != -1 && posAmp < posQuot))
711                       && (posLt == -1 || (posLt != -1 && posAmp < posLt))
712                      )
713             {
714                 if(pos < posAmp) out.write(value.substring(pos, posAmp));
715                 out.write("&amp;");
716                 pos = posAmp + 1;
717                 posAmp = value.indexOf('&', pos);
718             } else if(posLt != -1
719                       && (posQuot == -1 || (posQuot != -1 && posLt < posQuot))
720                       && (posAmp == -1 || (posAmp != -1 && posLt < posAmp))
721                      )
722             {
723                 if(pos < posLt) out.write(value.substring(pos, posLt));
724                 out.write("&lt;");
725                 pos = posLt + 1;
726                 posLt = value.indexOf('<', pos);
727             } else {
728                 throw new IllegalStateException JavaDoc("wrong state #1 posLt="+posLt+" posAmp="+posAmp
729                                                     +" posQuot="+posQuot+" for "+value);
730             }
731         }
732     }
733
734     protected void writeElementContent(String JavaDoc text, Writer JavaDoc out) throws IOException JavaDoc
735     {
736         //<, & esccaped]
737
//out.write(text);
738
int posLt = text.indexOf('<');
739         int posAmp = text.indexOf('&');
740         // painful loop ...
741
int pos = 0;
742         while(true) {
743             if(posLt == -1 && posAmp == -1) { // this is shortcut
744
out.write(text.substring(pos));
745                 break;
746             } else if(posLt == -1
747                       || (posLt != -1 && posAmp != -1 && posAmp < posLt))
748             {
749                 if(pos < posAmp) out.write(text.substring(pos, posAmp));
750                 out.write("&amp;");
751                 pos = posAmp + 1;
752                 posAmp = text.indexOf('&', pos);
753             } else if(posAmp == -1
754                       || (posLt != -1 && posAmp != -1 && posLt < posAmp))
755             {
756                 if(pos < posLt) out.write(text.substring(pos, posLt));
757                 out.write("&lt;");
758                 pos = posLt + 1;
759                 posLt = text.indexOf('<', pos);
760             } else {
761                 throw new IllegalStateException JavaDoc("wrong state posLt="+posLt
762                                                     +" posAmp="+posAmp+" for "+text);
763             }
764         }
765     }
766
767     protected void writeElementContent(char[] buf, int off, int len, Writer JavaDoc out) throws IOException JavaDoc
768     {
769         // esccape '<', '&'
770
int end = off + len;
771         int pos = off;
772         for (int i = off; i < end; i++)
773         {
774             char ch = buf[i];
775             if(ch == '&') {
776                 if(i > pos) {
777                     out.write(buf, pos, i - pos);
778                 }
779                 out.write("&amp;");
780                 pos = i + 1;
781             } else if(ch == '<') {
782                 if(i > pos) {
783                     out.write(buf, pos, i - pos);
784                 }
785                 out.write("&lt;");
786                 pos = i + 1;
787             }
788         }
789         if(end > pos) {
790             out.write(buf, pos, end - pos);
791         }
792     }
793
794     /** simple utility method -- good for debugging */
795     protected static final String JavaDoc printable(String JavaDoc s) {
796         if(s == null) return "null";
797         StringBuffer JavaDoc retval = new StringBuffer JavaDoc(s.length() + 16);
798         retval.append("'");
799         char ch;
800         for (int i = 0; i < s.length(); i++) {
801             addPrintable(retval, s.charAt(i));
802         }
803         retval.append("'");
804         return retval.toString();
805     }
806
807     protected static final String JavaDoc printable(char ch) {
808         StringBuffer JavaDoc retval = new StringBuffer JavaDoc();
809         addPrintable(retval, ch);
810         return retval.toString();
811     }
812
813     private static void addPrintable(StringBuffer JavaDoc retval, char ch)
814     {
815         switch (ch)
816         {
817             case '\b':
818                 retval.append("\\b");
819                 break;
820             case '\t':
821                 retval.append("\\t");
822                 break;
823             case '\n':
824                 retval.append("\\n");
825                 break;
826             case '\f':
827                 retval.append("\\f");
828                 break;
829             case '\r':
830                 retval.append("\\r");
831                 break;
832             case '\"':
833                 retval.append("\\\"");
834                 break;
835             case '\'':
836                 retval.append("\\\'");
837                 break;
838             case '\\':
839                 retval.append("\\\\");
840                 break;
841             default:
842                 if (ch < 0x20 || ch > 0x7e) {
843                     String JavaDoc ss = "0000" + Integer.toString(ch, 16);
844                     retval.append("\\u" + ss.substring(ss.length() - 4, ss.length()));
845                 } else {
846                     retval.append(ch);
847                 }
848         }
849     }
850
851 }
852
853
Popular Tags