1 /*
2  * Copyright (c) 2005, 2012, 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.buffer.sax;
27 
28 import com.sun.xml.internal.stream.buffer.AbstractProcessor;
29 import com.sun.xml.internal.stream.buffer.AttributesHolder;
30 import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
31 import org.xml.sax.ContentHandler;
32 import org.xml.sax.DTDHandler;
33 import org.xml.sax.EntityResolver;
34 import org.xml.sax.ErrorHandler;
35 import org.xml.sax.InputSource;
36 import org.xml.sax.SAXException;
37 import org.xml.sax.SAXNotRecognizedException;
38 import org.xml.sax.SAXNotSupportedException;
39 import org.xml.sax.SAXParseException;
40 import org.xml.sax.XMLReader;
41 import org.xml.sax.ext.LexicalHandler;
42 import org.xml.sax.helpers.LocatorImpl;
43 
44 import javax.xml.XMLConstants;
45 import java.io.IOException;
46 import java.util.Collections;
47 import java.util.HashSet;
48 import java.util.Map;
49 import java.util.Set;
50 
51 /**
52  * A processor of a {@link XMLStreamBuffer} that that reads the XML infoset as
53  * {@link XMLReader}.
54  */
55 public class SAXBufferProcessor extends AbstractProcessor implements XMLReader {
56     /**
57      * Reference to entity resolver.
58      */
59     protected EntityResolver _entityResolver = DEFAULT_LEXICAL_HANDLER;
60 
61     /**
62      * Reference to dtd handler.
63      */
64     protected DTDHandler _dtdHandler = DEFAULT_LEXICAL_HANDLER;
65 
66     /**
67      * Reference to content handler.
68      */
69     protected ContentHandler _contentHandler = DEFAULT_LEXICAL_HANDLER;
70 
71     /**
72      * Reference to error handler.
73      */
74     protected ErrorHandler _errorHandler = DEFAULT_LEXICAL_HANDLER;
75 
76     /**
77      * Reference to lexical handler.
78      */
79     protected LexicalHandler _lexicalHandler = DEFAULT_LEXICAL_HANDLER;
80 
81     /**
82      * SAX Namespace attributes features
83      */
84     protected boolean _namespacePrefixesFeature = false;
85 
86     protected AttributesHolder _attributes = new AttributesHolder();
87 
88     protected String[] _namespacePrefixes = new String[16];
89     protected int _namespacePrefixesIndex;
90 
91     protected int[] _namespaceAttributesStartingStack = new int[16];
92     protected int[] _namespaceAttributesStack = new int[16];
93     protected int _namespaceAttributesStackIndex;
94 
SAXBufferProcessor()95     public SAXBufferProcessor() {
96     }
97 
98     /**
99      * @deprecated
100      *      Use {@link #SAXBufferProcessor(XMLStreamBuffer, boolean)}
101      */
SAXBufferProcessor(XMLStreamBuffer buffer)102     public SAXBufferProcessor(XMLStreamBuffer buffer) {
103         setXMLStreamBuffer(buffer);
104     }
105 
106     /**
107      * @param produceFragmentEvent
108      *      True to generate fragment SAX events without start/endDocument.
109      *      False to generate a full document SAX events.
110      */
SAXBufferProcessor(XMLStreamBuffer buffer, boolean produceFragmentEvent)111     public SAXBufferProcessor(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
112         setXMLStreamBuffer(buffer,produceFragmentEvent);
113     }
114 
getFeature(String name)115     public boolean getFeature(String name)
116             throws SAXNotRecognizedException, SAXNotSupportedException {
117         if (name.equals(Features.NAMESPACES_FEATURE)) {
118             return true;
119         } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
120             return _namespacePrefixesFeature;
121         } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
122             return true;
123         } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
124             return true;
125         } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
126             return _stringInterningFeature;
127         } else {
128             throw new SAXNotRecognizedException(
129                     "Feature not supported: " + name);
130         }
131     }
132 
setFeature(String name, boolean value)133     public void setFeature(String name, boolean value)
134             throws SAXNotRecognizedException, SAXNotSupportedException {
135         if (name.equals(Features.NAMESPACES_FEATURE)) {
136             if (!value) {
137                 throw new SAXNotSupportedException(name + ":" + value);
138             }
139         } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
140             _namespacePrefixesFeature = value;
141         } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
142             // ignore
143         } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
144             // ignore
145         } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
146             if (value != _stringInterningFeature) {
147                 throw new SAXNotSupportedException(name + ":" + value);
148             }
149         } else {
150             throw new SAXNotRecognizedException(
151                     "Feature not supported: " + name);
152         }
153     }
154 
getProperty(String name)155     public Object getProperty(String name)
156             throws SAXNotRecognizedException, SAXNotSupportedException {
157         if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
158             return getLexicalHandler();
159         } else {
160             throw new SAXNotRecognizedException("Property not recognized: " + name);
161         }
162     }
163 
setProperty(String name, Object value)164     public void setProperty(String name, Object value)
165             throws SAXNotRecognizedException, SAXNotSupportedException {
166         if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
167             if (value instanceof LexicalHandler) {
168                 setLexicalHandler((LexicalHandler)value);
169             } else {
170                 throw new SAXNotSupportedException(Properties.LEXICAL_HANDLER_PROPERTY);
171             }
172         } else {
173             throw new SAXNotRecognizedException("Property not recognized: " + name);
174         }
175     }
176 
setEntityResolver(EntityResolver resolver)177     public void setEntityResolver(EntityResolver resolver) {
178         _entityResolver = resolver;
179     }
180 
getEntityResolver()181     public EntityResolver getEntityResolver() {
182         return _entityResolver;
183     }
184 
setDTDHandler(DTDHandler handler)185     public void setDTDHandler(DTDHandler handler) {
186         _dtdHandler = handler;
187     }
188 
getDTDHandler()189     public DTDHandler getDTDHandler() {
190         return _dtdHandler;
191     }
192 
setContentHandler(ContentHandler handler)193     public void setContentHandler(ContentHandler handler) {
194         _contentHandler = handler;
195     }
196 
getContentHandler()197     public ContentHandler getContentHandler() {
198         return _contentHandler;
199     }
200 
setErrorHandler(ErrorHandler handler)201     public void setErrorHandler(ErrorHandler handler) {
202         _errorHandler = handler;
203     }
204 
getErrorHandler()205     public ErrorHandler getErrorHandler() {
206         return _errorHandler;
207     }
208 
setLexicalHandler(LexicalHandler handler)209     public void setLexicalHandler(LexicalHandler handler) {
210         _lexicalHandler = handler;
211     }
212 
getLexicalHandler()213     public LexicalHandler getLexicalHandler() {
214         return _lexicalHandler;
215     }
216 
parse(InputSource input)217     public void parse(InputSource input) throws IOException, SAXException {
218         // InputSource is ignored
219         process();
220     }
221 
parse(String systemId)222     public void parse(String systemId) throws IOException, SAXException {
223         // systemId is ignored
224         process();
225     }
226 
227     /**
228      * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer)} then {@link #process()}.
229      *
230      * @deprecated
231      *      Use {@link #process(XMLStreamBuffer, boolean)}
232      */
process(XMLStreamBuffer buffer)233     public final void process(XMLStreamBuffer buffer) throws SAXException {
234         setXMLStreamBuffer(buffer);
235         process();
236     }
237 
238     /**
239      * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer,boolean)} then {@link #process()}.
240      *
241      * @param produceFragmentEvent
242      *      True to generate fragment SAX events without start/endDocument.
243      *      False to generate a full document SAX events.
244      */
process(XMLStreamBuffer buffer, boolean produceFragmentEvent)245     public final void process(XMLStreamBuffer buffer, boolean produceFragmentEvent) throws SAXException {
246         setXMLStreamBuffer(buffer);
247         process();
248     }
249 
250     /**
251      * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
252      *
253      * @deprecated
254      *      Use {@link #setXMLStreamBuffer(XMLStreamBuffer, boolean)}.
255      */
setXMLStreamBuffer(XMLStreamBuffer buffer)256     public void setXMLStreamBuffer(XMLStreamBuffer buffer) {
257         setBuffer(buffer);
258     }
259 
260     /**
261      * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
262      *
263      * @param produceFragmentEvent
264      *      True to generate fragment SAX events without start/endDocument.
265      *      False to generate a full document SAX events.
266      */
setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent)267     public void setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
268         if(!produceFragmentEvent && _treeCount>1)
269             throw new IllegalStateException("Can't write a forest to a full XML infoset");
270         setBuffer(buffer,produceFragmentEvent);
271     }
272 
273     /**
274      * Parse the sub-tree (or a whole document) that {@link XMLStreamBuffer}
275      * points to, and sends events to handlers.
276      *
277      * <p>
278      * TODO:
279      * We probably need two modes for a sub-tree event generation. One for
280      * firing a sub-tree as if it's a whole document (in which case start/endDocument
281      * and appropriate additional namespace bindings are necessary), and the other
282      * mode for firing a subtree as a subtree, like it does today.
283      * A stream buffer SAX feature could be used to specify this.
284      *
285      * @throws SAXException
286      *      Follow the same semantics as {@link XMLReader#parse(InputSource)}.
287      */
process()288     public final void process() throws SAXException {
289         if(!_fragmentMode) {
290             LocatorImpl nullLocator = new LocatorImpl();
291             nullLocator.setSystemId(_buffer.getSystemId());
292             nullLocator.setLineNumber(-1);
293             nullLocator.setColumnNumber(-1);
294             _contentHandler.setDocumentLocator(nullLocator);
295 
296             _contentHandler.startDocument();
297             // TODO: if we are writing a fragment stream buffer as a full XML document,
298             // we need to declare in-scope namespaces as if they are on the root element.
299         }
300 
301         while (_treeCount>0) {
302             final int item = readEiiState();
303             switch(item) {
304                 case STATE_DOCUMENT:
305                     processDocument();
306                     _treeCount--;
307                     break;
308                 case STATE_END:
309                     // Empty buffer
310                     return;
311                 case STATE_ELEMENT_U_LN_QN:
312                     processElement(readStructureString(), readStructureString(), readStructureString(), isInscope());
313                     _treeCount--;
314                     break;
315                 case STATE_ELEMENT_P_U_LN:
316                 {
317                     final String prefix = readStructureString();
318                     final String uri = readStructureString();
319                     final String localName = readStructureString();
320                     processElement(uri, localName, getQName(prefix, localName),isInscope());
321                     _treeCount--;
322                     break;
323                 }
324                 case STATE_ELEMENT_U_LN: {
325                     final String uri = readStructureString();
326                     final String localName = readStructureString();
327                     processElement(uri, localName, localName,isInscope());
328                     _treeCount--;
329                     break;
330                 }
331                 case STATE_ELEMENT_LN:
332                 {
333                     final String localName = readStructureString();
334                     processElement("", localName, localName,isInscope());
335                     _treeCount--;
336                     break;
337                 }
338                 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
339                     processCommentAsCharArraySmall();
340                     break;
341                 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
342                     processCommentAsCharArrayMedium();
343                     break;
344                 case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
345                     processCommentAsCharArrayCopy();
346                     break;
347                 case STATE_COMMENT_AS_STRING:
348                     processComment(readContentString());
349                     break;
350                 case STATE_PROCESSING_INSTRUCTION:
351                     processProcessingInstruction(readStructureString(), readStructureString());
352                     break;
353                 default:
354                     throw reportFatalError("Illegal state for DIIs: "+item);
355             }
356         }
357 
358         if(!_fragmentMode)
359             _contentHandler.endDocument();
360     }
361 
processCommentAsCharArraySmall()362     private void processCommentAsCharArraySmall() throws SAXException {
363         final int length = readStructure();
364         final int start = readContentCharactersBuffer(length);
365         processComment(_contentCharactersBuffer, start, length);
366     }
367 
368     /**
369      * Report a fatal error and abort.
370      *
371      * This is necessary to follow the SAX semantics of error handling.
372      */
reportFatalError(String msg)373     private SAXParseException reportFatalError(String msg) throws SAXException {
374         SAXParseException spe = new SAXParseException(msg, null);
375         if(_errorHandler!=null)
376             _errorHandler.fatalError(spe);
377         return spe;
378     }
379 
isInscope()380     private boolean isInscope() {
381         return _buffer.getInscopeNamespaces().size() > 0;
382     }
383 
processDocument()384     private void processDocument() throws SAXException {
385         while(true) {
386             int item = readEiiState();
387             switch(item) {
388                 case STATE_ELEMENT_U_LN_QN:
389                     processElement(readStructureString(), readStructureString(), readStructureString(),isInscope());
390                     break;
391                 case STATE_ELEMENT_P_U_LN:
392                 {
393                     final String prefix = readStructureString();
394                     final String uri = readStructureString();
395                     final String localName = readStructureString();
396                     processElement(uri, localName, getQName(prefix, localName),isInscope());
397                     break;
398                 }
399                 case STATE_ELEMENT_U_LN: {
400                     final String uri = readStructureString();
401                     final String localName = readStructureString();
402                     processElement(uri, localName, localName,isInscope());
403                     break;
404                 }
405                 case STATE_ELEMENT_LN:
406                 {
407                     final String localName = readStructureString();
408                     processElement("", localName, localName,isInscope());
409                     break;
410                 }
411                 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
412                     processCommentAsCharArraySmall();
413                     break;
414                 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
415                     processCommentAsCharArrayMedium();
416                     break;
417                 case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
418                     processCommentAsCharArrayCopy();
419                     break;
420                 case STATE_COMMENT_AS_STRING:
421                     processComment(readContentString());
422                     break;
423                 case STATE_PROCESSING_INSTRUCTION:
424                     processProcessingInstruction(readStructureString(), readStructureString());
425                     break;
426                 case STATE_END:
427                     return;
428                 default:
429                     throw reportFatalError("Illegal state for child of DII: "+item);
430             }
431         }
432     }
433 
processElement(String uri, String localName, String qName, boolean inscope)434     protected void processElement(String uri, String localName, String qName, boolean inscope) throws SAXException {
435         boolean hasAttributes = false;
436         boolean hasNamespaceAttributes = false;
437         int item = peekStructure();
438         Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet();
439         if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
440             cacheNamespacePrefixStartingIndex();
441             hasNamespaceAttributes = true;
442             item = processNamespaceAttributes(item, inscope, prefixSet);
443         }
444         if (inscope) {
445             readInscopeNamespaces(prefixSet);
446         }
447 
448         if ((item & TYPE_MASK) == T_ATTRIBUTE) {
449             hasAttributes = true;
450             processAttributes(item);
451         }
452 
453         _contentHandler.startElement(uri, localName, qName, _attributes);
454 
455         if (hasAttributes) {
456             _attributes.clear();
457         }
458 
459         do {
460             item = readEiiState();
461             switch(item) {
462                 case STATE_ELEMENT_U_LN_QN:
463                     processElement(readStructureString(), readStructureString(), readStructureString(), false);
464                     break;
465                 case STATE_ELEMENT_P_U_LN:
466                 {
467                     final String p = readStructureString();
468                     final String u = readStructureString();
469                     final String ln = readStructureString();
470                     processElement(u, ln, getQName(p, ln),false);
471                     break;
472                 }
473                 case STATE_ELEMENT_U_LN: {
474                     final String u = readStructureString();
475                     final String ln = readStructureString();
476                     processElement(u, ln, ln,false);
477                     break;
478                 }
479                 case STATE_ELEMENT_LN: {
480                     final String ln = readStructureString();
481                     processElement("", ln, ln,false);
482                     break;
483                 }
484                 case STATE_TEXT_AS_CHAR_ARRAY_SMALL:
485                 {
486                     final int length = readStructure();
487                     int start = readContentCharactersBuffer(length);
488                     _contentHandler.characters(_contentCharactersBuffer, start, length);
489                     break;
490                 }
491                 case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM:
492                 {
493                     final int length = readStructure16();
494                     int start = readContentCharactersBuffer(length);
495                     _contentHandler.characters(_contentCharactersBuffer, start, length);
496                     break;
497                 }
498                 case STATE_TEXT_AS_CHAR_ARRAY_COPY:
499                 {
500                     final char[] ch = readContentCharactersCopy();
501 
502                     _contentHandler.characters(ch, 0, ch.length);
503                     break;
504                 }
505                 case STATE_TEXT_AS_STRING:
506                 {
507                     final String s = readContentString();
508                     _contentHandler.characters(s.toCharArray(), 0, s.length());
509                     break;
510                 }
511                 case STATE_TEXT_AS_OBJECT:
512                 {
513                     final CharSequence c = (CharSequence)readContentObject();
514                     final String s = c.toString();
515                     _contentHandler.characters(s.toCharArray(), 0, s.length());
516                     break;
517                 }
518                 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
519                     processCommentAsCharArraySmall();
520                     break;
521                 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
522                     processCommentAsCharArrayMedium();
523                     break;
524                 case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
525                     processCommentAsCharArrayCopy();
526                     break;
527                 case T_COMMENT_AS_STRING:
528                     processComment(readContentString());
529                     break;
530                 case STATE_PROCESSING_INSTRUCTION:
531                     processProcessingInstruction(readStructureString(), readStructureString());
532                     break;
533                 case STATE_END:
534                     break;
535                 default:
536                     throw reportFatalError("Illegal state for child of EII: "+item);
537             }
538         } while(item != STATE_END);
539 
540         _contentHandler.endElement(uri, localName, qName);
541 
542         if (hasNamespaceAttributes) {
543             processEndPrefixMapping();
544         }
545     }
546 
readInscopeNamespaces(Set<String> prefixSet)547     private void readInscopeNamespaces(Set<String> prefixSet) throws SAXException {
548         for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) {
549             String key = fixNull(e.getKey());
550             // If the prefix is already written, do not write the prefix
551             if (!prefixSet.contains(key)) {
552                 processNamespaceAttribute(key,e.getValue());
553             }
554         }
555 
556     }
557 
fixNull(String s)558      private static String fixNull(String s) {
559         if (s == null) return "";
560         else return s;
561     }
processCommentAsCharArrayCopy()562     private void processCommentAsCharArrayCopy() throws SAXException {
563         final char[] ch = readContentCharactersCopy();
564         processComment(ch, 0, ch.length);
565     }
566 
processCommentAsCharArrayMedium()567     private void processCommentAsCharArrayMedium() throws SAXException {
568         final int length = readStructure16();
569         final int start = readContentCharactersBuffer(length);
570         processComment(_contentCharactersBuffer, start, length);
571     }
572 
processEndPrefixMapping()573     private void processEndPrefixMapping() throws SAXException {
574         final int end = _namespaceAttributesStack[--_namespaceAttributesStackIndex];
575 //      final int start = (_namespaceAttributesStackIndex > 0) ? _namespaceAttributesStack[_namespaceAttributesStackIndex] : 0;
576         final int start = (_namespaceAttributesStackIndex >= 0) ? _namespaceAttributesStartingStack[_namespaceAttributesStackIndex] : 0;
577 
578         for (int i = end - 1; i >= start; i--) {
579             _contentHandler.endPrefixMapping(_namespacePrefixes[i]);
580         }
581         _namespacePrefixesIndex = start;
582     }
583 
processNamespaceAttributes(int item,boolean collectPrefixes, Set<String> prefixSet)584     private int processNamespaceAttributes(int item,boolean collectPrefixes, Set<String> prefixSet) throws SAXException {
585         do {
586             String prefix;
587             switch(getNIIState(item)) {
588                 case STATE_NAMESPACE_ATTRIBUTE:
589                     // Undeclaration of default namespace
590                     processNamespaceAttribute("", "");
591                     if(collectPrefixes) {
592                         prefixSet.add("");
593                     }
594                     break;
595                 case STATE_NAMESPACE_ATTRIBUTE_P:
596                     // Undeclaration of namespace
597                     prefix = readStructureString();
598                     processNamespaceAttribute(prefix, "");
599                     if(collectPrefixes) {
600                         prefixSet.add(prefix);
601                     }
602                     break;
603                 case STATE_NAMESPACE_ATTRIBUTE_P_U:
604                     // Declaration with prefix
605                    prefix = readStructureString();
606                     processNamespaceAttribute(prefix, readStructureString());
607                     if(collectPrefixes) {
608                         prefixSet.add(prefix);
609                     }
610                     break;
611                 case STATE_NAMESPACE_ATTRIBUTE_U:
612                     // Default declaration
613                     processNamespaceAttribute("", readStructureString());
614                     if(collectPrefixes) {
615                         prefixSet.add("");
616                     }
617                     break;
618                 default:
619                     throw reportFatalError("Illegal state: "+item);
620             }
621             readStructure();
622 
623             item = peekStructure();
624         } while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE);
625 
626 
627         cacheNamespacePrefixIndex();
628 
629         return item;
630     }
631 
processAttributes(int item)632     private void processAttributes(int item) throws SAXException {
633         do {
634             switch(getAIIState(item)) {
635                 case STATE_ATTRIBUTE_U_LN_QN:
636                     _attributes.addAttributeWithQName(readStructureString(), readStructureString(), readStructureString(), readStructureString(), readContentString());
637                     break;
638                 case STATE_ATTRIBUTE_P_U_LN:
639                 {
640                     final String p = readStructureString();
641                     final String u = readStructureString();
642                     final String ln = readStructureString();
643                     _attributes.addAttributeWithQName(u, ln, getQName(p, ln), readStructureString(), readContentString());
644                     break;
645                 }
646                 case STATE_ATTRIBUTE_U_LN: {
647                     final String u = readStructureString();
648                     final String ln = readStructureString();
649                     _attributes.addAttributeWithQName(u, ln, ln, readStructureString(), readContentString());
650                     break;
651                 }
652                 case STATE_ATTRIBUTE_LN: {
653                     final String ln = readStructureString();
654                     _attributes.addAttributeWithQName("", ln, ln, readStructureString(), readContentString());
655                     break;
656                 }
657                 default:
658                     throw reportFatalError("Illegal state: "+item);
659             }
660             readStructure();
661 
662             item = peekStructure();
663         } while((item & TYPE_MASK) == T_ATTRIBUTE);
664     }
665 
processNamespaceAttribute(String prefix, String uri)666     private void processNamespaceAttribute(String prefix, String uri) throws SAXException {
667         _contentHandler.startPrefixMapping(prefix, uri);
668 
669         if (_namespacePrefixesFeature) {
670             // Add the namespace delcaration as an attribute
671             if (prefix != "") {
672                 _attributes.addAttributeWithQName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix,
673                         getQName(XMLConstants.XMLNS_ATTRIBUTE, prefix),
674                         "CDATA", uri);
675             } else {
676                 _attributes.addAttributeWithQName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE,
677                         XMLConstants.XMLNS_ATTRIBUTE,
678                         "CDATA", uri);
679             }
680         }
681 
682         cacheNamespacePrefix(prefix);
683     }
684 
cacheNamespacePrefix(String prefix)685     private void cacheNamespacePrefix(String prefix) {
686         if (_namespacePrefixesIndex == _namespacePrefixes.length) {
687             final String[] namespaceAttributes = new String[_namespacePrefixesIndex * 3 / 2 + 1];
688             System.arraycopy(_namespacePrefixes, 0, namespaceAttributes, 0, _namespacePrefixesIndex);
689             _namespacePrefixes = namespaceAttributes;
690         }
691 
692         _namespacePrefixes[_namespacePrefixesIndex++] = prefix;
693     }
694 
cacheNamespacePrefixIndex()695     private void cacheNamespacePrefixIndex() {
696         if (_namespaceAttributesStackIndex == _namespaceAttributesStack.length) {
697             final int[] namespaceAttributesStack = new int[_namespaceAttributesStackIndex * 3 /2 + 1];
698             System.arraycopy(_namespaceAttributesStack, 0, namespaceAttributesStack, 0, _namespaceAttributesStackIndex);
699             _namespaceAttributesStack = namespaceAttributesStack;
700         }
701 
702         _namespaceAttributesStack[_namespaceAttributesStackIndex++] = _namespacePrefixesIndex;
703     }
704 
cacheNamespacePrefixStartingIndex()705     private void cacheNamespacePrefixStartingIndex() {
706         if (_namespaceAttributesStackIndex == _namespaceAttributesStartingStack.length) {
707             final int[] namespaceAttributesStart = new int[_namespaceAttributesStackIndex * 3 /2 + 1];
708             System.arraycopy(_namespaceAttributesStartingStack, 0, namespaceAttributesStart, 0, _namespaceAttributesStackIndex);
709             _namespaceAttributesStartingStack = namespaceAttributesStart;
710         }
711         _namespaceAttributesStartingStack[_namespaceAttributesStackIndex] = _namespacePrefixesIndex;
712     }
713 
processComment(String s)714     private void processComment(String s)  throws SAXException {
715         processComment(s.toCharArray(), 0, s.length());
716     }
717 
processComment(char[] ch, int start, int length)718     private void processComment(char[] ch, int start, int length) throws SAXException {
719         _lexicalHandler.comment(ch, start, length);
720     }
721 
processProcessingInstruction(String target, String data)722     private void processProcessingInstruction(String target, String data) throws SAXException {
723         _contentHandler.processingInstruction(target, data);
724     }
725 
726     private static final DefaultWithLexicalHandler DEFAULT_LEXICAL_HANDLER = new DefaultWithLexicalHandler();
727 }
728