001: /*
002: * $Id: XMLNSDocumentScannerImpl.java,v 1.5 2006/11/29 22:01:30 spericas Exp $
003: */
004:
005: /*
006: * The contents of this file are subject to the terms
007: * of the Common Development and Distribution License
008: * (the License). You may not use this file except in
009: * compliance with the License.
010: *
011: * You can obtain a copy of the license at
012: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
013: * See the License for the specific language governing
014: * permissions and limitations under the License.
015: *
016: * When distributing Covered Code, include this CDDL
017: * Header Notice in each file and include the License file
018: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
019: * If applicable, add the following below the CDDL Header,
020: * with the fields enclosed by brackets [] replaced by
021: * you own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * [Name of File] [ver.__] [Date]
025: *
026: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
027: */
028:
029: /*
030: * The Apache Software License, Version 1.1
031: *
032: *
033: * Copyright (c) 1999-2002 The Apache Software Foundation.
034: * All rights reserved.
035: *
036: * Redistribution and use in source and binary forms, with or without
037: * modification, are permitted provided that the following conditions
038: * are met:
039: *
040: * 1. Redistributions of source code must retain the above copyright
041: * notice, this list of conditions and the following disclaimer.
042: *
043: * 2. Redistributions in binary form must reproduce the above copyright
044: * notice, this list of conditions and the following disclaimer in
045: * the documentation and/or other materials provided with the
046: * distribution.
047: *
048: * 3. The end-user documentation included with the redistribution,
049: * if any, must include the following acknowledgment:
050: * "This product includes software developed by the
051: * Apache Software Foundation (http://www.apache.org/)."
052: * Alternately, this acknowledgment may appear in the software itself,
053: * if and wherever such third-party acknowledgments normally appear.
054: *
055: * 4. The names "Xerces" and "Apache Software Foundation" must
056: * not be used to endorse or promote products derived from this
057: * software without prior written permission. For written
058: * permission, please contact apache@apache.org.
059: *
060: * 5. Products derived from this software may not be called "Apache",
061: * nor may "Apache" appear in their name, without prior written
062: * permission of the Apache Software Foundation.
063: *
064: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
065: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
066: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
067: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
068: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
069: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
070: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
071: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
072: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
073: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
074: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
075: * SUCH DAMAGE.
076: * ====================================================================
077: *
078: * This software consists of voluntary contributions made by many
079: * individuals on behalf of the Apache Software Foundation and was
080: * originally based on software copyright (c) 2002, International
081: * Business Machines, Inc., http://www.apache.org. For more
082: * information on the Apache Software Foundation, please see
083: * <http://www.apache.org/>.
084: */
085:
086: package com.sun.xml.stream;
087:
088: import java.io.IOException;
089: import javax.xml.stream.events.XMLEvent;
090: import com.sun.xml.stream.xerces.xni.XMLString; //import com.sun.xml.stream.xerces.impl.dtd.XMLDTDValidatorFilter;
091: import com.sun.xml.stream.xerces.impl.msg.XMLMessageFormatter;
092: import com.sun.xml.stream.xerces.util.XMLAttributesImpl;
093: import com.sun.xml.stream.xerces.util.XMLStringBuffer;
094: import com.sun.xml.stream.xerces.util.XMLSymbols;
095: import com.sun.xml.stream.xerces.xni.NamespaceContext;
096: import com.sun.xml.stream.xerces.xni.QName;
097: import com.sun.xml.stream.xerces.xni.XNIException;
098: import com.sun.xml.stream.xerces.xni.parser.XMLComponentManager;
099: import com.sun.xml.stream.xerces.xni.parser.XMLConfigurationException;
100:
101: /**
102: * This class adds the functionality of namespace processing.
103: *
104: * This class has been modified as per the new design which is more suited to
105: * efficiently build pull parser. Lot of improvements have been done and
106: * the code has been added to support stax functionality/features.
107: *
108: *
109: * This class scans an XML document, checks if document has a DTD, and if
110: * DTD is not found the scanner will remove the DTD Validator from the pipeline and perform
111: * namespace binding.
112: *
113: *
114: * @author Neeraj Bajaj, Sun Microsystems
115: * @author Venugopal Rao K, Sun Microsystems
116: * @author Elena Litani, IBM
117: * @version $Id: XMLNSDocumentScannerImpl.java,v 1.5 2006/11/29 22:01:30 spericas Exp $
118: */
119: public class XMLNSDocumentScannerImpl extends XMLDocumentScannerImpl {
120:
121: /** If validating parser, make sure we report an error in the
122: * scanner if DTD grammar is missing.*/
123: protected boolean fPerformValidation;
124: private boolean fEmptyElement = false;
125: private XMLBufferListenerImpl listener = new XMLBufferListenerImpl();
126: // private boolean fClearAttributes = true;
127:
128: private boolean fXmlnsDeclared = false;
129:
130: /** Resets the fields of this scanner.
131: */
132: public void reset(PropertyManager propertyManager) {
133: setPropertyManager(propertyManager);
134: super .reset(propertyManager);
135: try {
136: if (!fAttributeCacheInitDone) {
137: for (int i = 0; i < initialCacheCount; i++) {
138: attributeValueCache.add(new XMLString());
139: stringBufferCache.add(new XMLStringBuffer());
140: }
141: fAttributeCacheInitDone = true;
142: }
143: fStringBufferIndex = 0;
144: fAttributeCacheUsedCount = 0;
145: fEntityScanner.registerListener(listener);
146: dtdGrammarUtil = null;
147: //fClearAttributes = true;
148: } catch (RuntimeException ex) {
149: }
150: }
151:
152: // private data
153: //
154:
155: /** DTD validator */
156: //private XMLDTDValidatorFilter fDTDValidator;
157: /**
158: * Return the information about the element -- If it is EndELement, QName values
159: * are computed dynamically.
160: *
161: * @returns com.sun.xml.stream.xerces.xni.QName
162: */
163:
164: public com.sun.xml.stream.xerces.xni.QName getElementQName() {
165: if (fScannerLastState == XMLEvent.END_ELEMENT) {
166: fElementQName.setValues(fElementStack
167: .getLastPoppedElement());
168: }
169: return fElementQName;
170: }
171:
172: /**
173: * Scans a start element. This method will handle the binding of
174: * namespace information and notifying the handler of the start
175: * of the element.
176: * <p>
177: * <pre>
178: * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
179: * [40] STag ::= '<' Name (S Attribute)* S? '>'
180: * </pre>
181: * <p>
182: * <strong>Note:</strong> This method assumes that the leading
183: * '<' character has been consumed.
184: * <p>
185: * <strong>Note:</strong> This method uses the fElementQName and
186: * fAttributes variables. The contents of these variables will be
187: * destroyed. The caller should copy important information out of
188: * these variables before calling this method.
189: *
190: * @return True if element is empty. (i.e. It matches
191: * production [44].
192: */
193: protected boolean scanStartElement() throws IOException,
194: XNIException {
195:
196: if (DEBUG_CONTENT_SCANNING)
197: System.out.println(">>> scanStartElement()");
198: //when skipping is true and no more elements should be added
199: if (fSkip && !fAdd) {
200: //get the stored element -- if everything goes right this should match the
201: //token in the buffer
202:
203: QName name = fElementStack.getNext();
204:
205: if (DEBUG_SKIP_ALGORITHM) {
206: System.out.println("Trying to skip String = "
207: + name.rawname);
208: }
209:
210: //Be conservative -- if skipping fails -- stop.
211: fSkip = fEntityScanner.skipString(name.characters); // skipQElement(name);
212:
213: if (fSkip) {
214: if (DEBUG_SKIP_ALGORITHM) {
215: System.out.println("Element SUCESSFULLY skipped = "
216: + name.rawname);
217: }
218: fElementStack.push();
219: fElementQName = name;
220: } else {
221: //if skipping fails reposition the stack or fallback to normal way of processing
222: fElementStack.reposition();
223: if (DEBUG_SKIP_ALGORITHM) {
224: System.out
225: .println("Element was NOT skipped, REPOSITIONING stack");
226: }
227: }
228: }
229:
230: //we are still at the stage of adding elements
231: //the elements were not matched or
232: //fSkip is not set to true
233: if (!fSkip || fAdd) {
234: //get the next element from the stack
235: fElementQName = fElementStack.nextElement();
236: // name
237: if (fBindNamespaces) {
238: fEntityScanner.scanQName(fElementQName);
239: } else {
240: String name = fEntityScanner.scanName();
241: fElementQName.setValues(null, name, name, null);
242: //XXX: THIS IS UGLY -- THIS SHOULD BE CHANGED.
243: //WE SHOULD DO IT AS PART OF QNAME -- NB.
244: fElementQName.characters = fEntityScanner.scannedName;
245: }
246:
247: if (DEBUG_SKIP_ALGORITHM) {
248: if (fAdd) {
249: System.out
250: .println("Elements are being ADDED -- elemet added is = "
251: + fElementQName.rawname
252: + " at count = "
253: + fElementStack.fCount);
254: }
255: }
256:
257: }
258:
259: //when the elements are being added , we need to check if we are set for skipping the elements
260: if (fAdd) {
261: //this sets the value of fAdd variable
262: fElementStack.matchElement(fElementQName);
263: }
264:
265: //xxx: We dont need another pointer, fCurrentElement, we can use fElementQName
266: fCurrentElement = fElementQName;
267:
268: String rawname = fElementQName.rawname;
269: if (fBindNamespaces) {
270: fNamespaceContext.pushContext();
271: if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
272: if (fPerformValidation) {
273: fErrorReporter.reportError(
274: XMLMessageFormatter.XML_DOMAIN,
275: "MSG_GRAMMAR_NOT_FOUND",
276: new Object[] { rawname },
277: XMLErrorReporter.SEVERITY_ERROR);
278:
279: if (fDoctypeName == null
280: || !fDoctypeName.equals(rawname)) {
281: fErrorReporter.reportError(
282: XMLMessageFormatter.XML_DOMAIN,
283: "RootElementTypeMustMatchDoctypedecl",
284: new Object[] { fDoctypeName, rawname },
285: XMLErrorReporter.SEVERITY_ERROR);
286: }
287: }
288: }
289: }
290:
291: fEmptyElement = false;
292: // attributes must be always cleared
293: //if(fClearAttributes)
294: fAttributes.removeAllAttributes();
295: if (!seekCloseOfStartTag()) {
296: fReadingAttributes = true;
297: fAttributeCacheUsedCount = 0;
298: fStringBufferIndex = 0;
299: //fClearAttributes=true;
300: fAddDefaultAttr = true;
301: fXmlnsDeclared = false;
302:
303: do {
304: scanAttribute(fAttributes);
305: } while (!seekCloseOfStartTag());
306: fReadingAttributes = false;
307: } /*else{
308: fClearAttributes=false;
309: }*/
310:
311: if (fBindNamespaces) {
312: // REVISIT: is it required? forbit xmlns prefix for element
313: if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
314: fErrorReporter.reportError(
315: XMLMessageFormatter.XMLNS_DOMAIN,
316: "ElementXMLNSPrefix",
317: new Object[] { fElementQName.rawname },
318: XMLErrorReporter.SEVERITY_FATAL_ERROR);
319: }
320:
321: // bind the element
322: String prefix = fElementQName.prefix != null ? fElementQName.prefix
323: : XMLSymbols.EMPTY_STRING;
324: // assign uri to the element
325: fElementQName.uri = fNamespaceContext.getURI(prefix);
326: // make sure that object in the element stack is updated as well
327: fCurrentElement.uri = fElementQName.uri;
328:
329: if (fElementQName.prefix == null
330: && fElementQName.uri != null) {
331: fElementQName.prefix = XMLSymbols.EMPTY_STRING;
332: }
333: if (fElementQName.prefix != null
334: && fElementQName.uri == null) {
335: fErrorReporter.reportError(
336: XMLMessageFormatter.XMLNS_DOMAIN,
337: "ElementPrefixUnbound", new Object[] {
338: fElementQName.prefix,
339: fElementQName.rawname },
340: XMLErrorReporter.SEVERITY_FATAL_ERROR);
341: }
342:
343: // bind attributes (xmlns are already bound bellow)
344: int length = fAttributes.getLength();
345: // fLength = 0; //initialize structure
346: for (int i = 0; i < length; i++) {
347: fAttributes.getName(i, fAttributeQName);
348:
349: String aprefix = fAttributeQName.prefix != null ? fAttributeQName.prefix
350: : XMLSymbols.EMPTY_STRING;
351: String uri = fNamespaceContext.getURI(aprefix);
352: // REVISIT: try removing the first "if" and see if it is faster.
353: //
354: if (fAttributeQName.uri != null
355: && fAttributeQName.uri == uri) {
356: // checkDuplicates(fAttributeQName, fAttributes);
357: continue;
358: }
359: if (aprefix != XMLSymbols.EMPTY_STRING) {
360: fAttributeQName.uri = uri;
361: if (uri == null) {
362: fErrorReporter.reportError(
363: XMLMessageFormatter.XMLNS_DOMAIN,
364: "AttributePrefixUnbound", new Object[] {
365: fElementQName.rawname,
366: fAttributeQName.rawname,
367: aprefix },
368: XMLErrorReporter.SEVERITY_FATAL_ERROR);
369: }
370: fAttributes.setURI(i, uri);
371: // checkDuplicates(fAttributeQName, fAttributes);
372: }
373: }
374:
375: if (length > 1) {
376: QName name = fAttributes.checkDuplicatesNS();
377: if (name != null) {
378: if (name.uri != null) {
379: fErrorReporter.reportError(
380: XMLMessageFormatter.XMLNS_DOMAIN,
381: "AttributeNSNotUnique", new Object[] {
382: fElementQName.rawname,
383: name.localpart, name.uri },
384: XMLErrorReporter.SEVERITY_FATAL_ERROR);
385: } else {
386: fErrorReporter.reportError(
387: XMLMessageFormatter.XMLNS_DOMAIN,
388: "AttributeNotUnique", new Object[] {
389: fElementQName.rawname,
390: name.rawname },
391: XMLErrorReporter.SEVERITY_FATAL_ERROR);
392: }
393: }
394: }
395: }
396:
397: if (fEmptyElement) {
398: //decrease the markup depth..
399: fMarkupDepth--;
400:
401: // check that this element was opened in the same entity
402: if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
403: reportFatalError("ElementEntityMismatch",
404: new Object[] { fCurrentElement.rawname });
405: }
406: // call handler
407: if (fDocumentHandler != null) {
408: fDocumentHandler.emptyElement(fElementQName,
409: fAttributes, null);
410: }
411:
412: //We should not be popping out the context here in endELement becaause the namespace context is still
413: //valid when parser is at the endElement state.
414: fScanEndElement = true;
415: //if (fBindNamespaces) {
416: // fNamespaceContext.popContext();
417: //}
418:
419: //pop the element off the stack..
420: fElementStack.popElement();
421:
422: } else {
423: if (dtdGrammarUtil != null) {
424: dtdGrammarUtil.startElement(fElementQName, fAttributes);
425: }
426: if (fDocumentHandler != null) {
427: //fDocumentHandler.startElement(fElementQName, fAttributes, null);
428: }
429: }
430:
431: if (DEBUG_CONTENT_SCANNING)
432: System.out.println("<<< scanStartElement(): "
433: + fEmptyElement);
434: return fEmptyElement;
435:
436: } // scanStartElement():boolean
437:
438: /**
439: * Looks for the close of start tag, i.e. if it finds '>' or '/>'
440: * Characters are consumed.
441: */
442: private boolean seekCloseOfStartTag() throws IOException,
443: XNIException {
444: // spaces
445: boolean sawSpace = fEntityScanner.skipSpaces();
446:
447: // end tag?
448: final int c = fEntityScanner.peekChar();
449: if (c == '>') {
450: fEntityScanner.scanChar();
451: return true;
452: } else if (c == '/') {
453: fEntityScanner.scanChar();
454: if (!fEntityScanner.skipChar('>')) {
455: reportFatalError("ElementUnterminated",
456: new Object[] { fElementQName.rawname });
457: }
458: fEmptyElement = true;
459: return true;
460: } else if (!isValidNameStartChar(c) || !sawSpace) {
461: reportFatalError("ElementUnterminated",
462: new Object[] { fElementQName.rawname });
463: }
464:
465: return false;
466: }
467:
468: /**
469: * Scans an attribute.
470: * <p>
471: * <pre>
472: * [41] Attribute ::= Name Eq AttValue
473: * </pre>
474: * <p>
475: * <strong>Note:</strong> This method assumes that the next
476: * character on the stream is the first character of the attribute
477: * name.
478: * <p>
479: * <strong>Note:</strong> This method uses the fAttributeQName and
480: * fQName variables. The contents of these variables will be
481: * destroyed.
482: *
483: * @param attributes The attributes list for the scanned attribute.
484: */
485: protected void scanAttribute(XMLAttributesImpl attributes)
486: throws IOException, XNIException {
487: if (DEBUG_CONTENT_SCANNING)
488: System.out.println(">>> scanAttribute()");
489:
490: // name
491: fEntityScanner.scanQName(fAttributeQName);
492:
493: // equals
494: fEntityScanner.skipSpaces();
495: if (!fEntityScanner.skipChar('=')) {
496: reportFatalError("EqRequiredInAttribute", new Object[] {
497: fCurrentElement.rawname, fAttributeQName.rawname });
498: }
499: fEntityScanner.skipSpaces();
500:
501: // content
502: int attrIndex = 0;
503:
504: //REVISIT: one more case needs to be included: external PE and standalone is no
505: boolean isVC = fHasExternalDTD && !fStandalone;
506:
507: // REVISIT: it seems that this function should not take attributes, and length
508: //fTempString would store attribute value
509: ///fTempString2 would store attribute non-normalized value
510:
511: //this function doesn't use 'attIndex'. We are adding the attribute later
512: //after we have figured out that current attribute is not namespace declaration
513: //since scanAttributeValue doesn't use attIndex parameter therefore we
514: //can safely add the attribute later..
515: XMLString tmpStr = getString();
516: scanAttributeValue(tmpStr, fTempString2,
517: fAttributeQName.rawname, attributes, attrIndex, isVC);
518:
519: String value = null;
520: //fTempString.toString();
521:
522: // record namespace declarations if any.
523: if (fBindNamespaces) {
524:
525: String localpart = fAttributeQName.localpart;
526: String prefix = fAttributeQName.prefix != null ? fAttributeQName.prefix
527: : XMLSymbols.EMPTY_STRING;
528:
529: // when it's of form xmlns="..." or xmlns:prefix="...",
530: // it's a namespace declaration. but prefix:xmlns="..." isn't.
531: if (prefix == XMLSymbols.PREFIX_XMLNS
532: || prefix == XMLSymbols.EMPTY_STRING
533: && localpart == XMLSymbols.PREFIX_XMLNS) {
534:
535: // get the internalized value of this attribute
536: String uri = fSymbolTable.addSymbol(tmpStr.ch,
537: tmpStr.offset, tmpStr.length);
538: value = uri;
539: // 1. "xmlns" can't be bound to any namespace
540: if (prefix == XMLSymbols.PREFIX_XMLNS
541: && localpart == XMLSymbols.PREFIX_XMLNS) {
542: fErrorReporter.reportError(
543: XMLMessageFormatter.XMLNS_DOMAIN,
544: "CantBindXMLNS",
545: new Object[] { fAttributeQName },
546: XMLErrorReporter.SEVERITY_FATAL_ERROR);
547: }
548:
549: // 2. the namespace for "xmlns" can't be bound to any prefix
550: if (uri == NamespaceContext.XMLNS_URI) {
551: fErrorReporter.reportError(
552: XMLMessageFormatter.XMLNS_DOMAIN,
553: "CantBindXMLNS",
554: new Object[] { fAttributeQName },
555: XMLErrorReporter.SEVERITY_FATAL_ERROR);
556: }
557:
558: // 3. "xml" can't be bound to any other namespace than it's own
559: if (localpart == XMLSymbols.PREFIX_XML) {
560: if (uri != NamespaceContext.XML_URI) {
561: fErrorReporter.reportError(
562: XMLMessageFormatter.XMLNS_DOMAIN,
563: "CantBindXML",
564: new Object[] { fAttributeQName },
565: XMLErrorReporter.SEVERITY_FATAL_ERROR);
566: }
567: }
568: // 4. the namespace for "xml" can't be bound to any other prefix
569: else {
570: if (uri == NamespaceContext.XML_URI) {
571: fErrorReporter.reportError(
572: XMLMessageFormatter.XMLNS_DOMAIN,
573: "CantBindXML",
574: new Object[] { fAttributeQName },
575: XMLErrorReporter.SEVERITY_FATAL_ERROR);
576: }
577: }
578:
579: prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart
580: : XMLSymbols.EMPTY_STRING;
581:
582: // http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix
583: // We should only report an error if there is a prefix,
584: // that is, the local part is not "xmlns". -SG
585: if (uri == XMLSymbols.EMPTY_STRING
586: && localpart != XMLSymbols.PREFIX_XMLNS) {
587: fErrorReporter.reportError(
588: XMLMessageFormatter.XMLNS_DOMAIN,
589: "EmptyPrefixedAttName",
590: new Object[] { fAttributeQName },
591: XMLErrorReporter.SEVERITY_FATAL_ERROR);
592: }
593:
594: // check for duplicate prefix bindings
595: if (((com.sun.xml.stream.xerces.util.NamespaceSupport) fNamespaceContext)
596: .containsPrefixInCurrentContext(prefix)) {
597: reportFatalError("AttributeNotUnique",
598: new Object[] { fCurrentElement.rawname,
599: fAttributeQName.rawname });
600: }
601:
602: // declare prefix in context
603: boolean declared = fNamespaceContext.declarePrefix(
604: prefix, uri.length() != 0 ? uri : null);
605:
606: // check for duplicate xmlns declarations
607: if (!declared) { // by convention, prefix == "xmlns" | "xml"
608: // error if duplicate declaration
609: if (fXmlnsDeclared) {
610: reportFatalError("AttributeNotUnique",
611: new Object[] { fCurrentElement.rawname,
612: fAttributeQName.rawname });
613: }
614:
615: // xmlns declared
616: fXmlnsDeclared = true;
617: }
618:
619: //we are not adding namespace declarations in the XMLAttributes.
620: //Namespace declarations are available from fNamespaceContext
621: return;
622: // bind namespace attribute to a namespace
623: //we are not adding namespace declarations tothe list of attribute
624: //attributes.setURI(attrIndex, fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS));
625:
626: }
627:
628: }
629:
630: //add the attributes to the list of attributes
631: if (fBindNamespaces) {
632: attrIndex = attributes.getLength();
633: attributes.addAttributeNS(fAttributeQName,
634: XMLSymbols.fCDATASymbol, null);
635: } else {
636: int oldLen = attributes.getLength();
637: attrIndex = attributes.addAttribute(fAttributeQName,
638: XMLSymbols.fCDATASymbol, null);
639:
640: // WFC: Unique Att Spec
641: if (oldLen == attributes.getLength()) {
642: reportFatalError("AttributeNotUnique", new Object[] {
643: fCurrentElement.rawname,
644: fAttributeQName.rawname });
645: }
646: }
647:
648: attributes.setValue(attrIndex, value, tmpStr);
649: //attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
650: //removing as we are not using non-normalized values . -Venu
651: attributes.setSpecified(attrIndex, true);
652:
653: // attempt to bind attribute
654: if (fAttributeQName.prefix != null) {
655: attributes.setURI(attrIndex, fNamespaceContext
656: .getURI(fAttributeQName.prefix));
657: }
658:
659: if (DEBUG_CONTENT_SCANNING)
660: System.out.println("<<< scanAttribute()");
661: } // scanAttribute(XMLAttributes)
662:
663: /**
664: * Scans an end element.
665: * <p>
666: * <pre>
667: * [42] ETag ::= '</' Name S? '>'
668: * </pre>
669: * <p>
670: * <strong>Note:</strong> This method uses the fElementQName variable.
671: * The contents of this variable will be destroyed. The caller should
672: * copy the needed information out of this variable before calling
673: * this method.
674: *
675: * @return The element depth.
676: */
677: protected int scanEndElement() throws IOException, XNIException {
678: if (DEBUG_CONTENT_SCANNING)
679: System.out.println(">>> scanEndElement()");
680:
681: // pop context
682: QName endElementName = fElementStack.popElement();
683:
684: String rawname = endElementName.rawname;
685:
686: // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
687: //In scanners most of the time is consumed on checks done for XML characters, we can
688: // optimize on it and avoid the checks done for endElement,
689: //we will also avoid symbol table lookup - neeraj.bajaj@sun.com
690:
691: // this should work both for namespace processing true or false...
692:
693: //REVISIT: if the string is not the same as expected.. we need to do better error handling..
694: //We can skip this for now... In any case if the string doesn't match -- document is not well formed.
695:
696: //Pass characters instead of string , this gives better performance than strings.
697: //Use the character array for rawname present in the symboltable. -Venu
698: if (!fEntityScanner.skipString(endElementName.characters)) {
699: reportFatalError("ETagRequired", new Object[] { rawname });
700: }
701:
702: // end
703: fEntityScanner.skipSpaces();
704: if (!fEntityScanner.skipChar('>')) {
705: reportFatalError("ETagUnterminated",
706: new Object[] { rawname });
707: }
708: fMarkupDepth--;
709:
710: //we have increased the depth for two markup "<" characters
711: fMarkupDepth--;
712:
713: // check that this element was opened in the same entity
714: if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
715: reportFatalError("ElementEntityMismatch",
716: new Object[] { rawname });
717: }
718:
719: //We should not be popping out the context here in endELement becaause the namespace context is still
720: //valid when parser is at the endElement state.
721:
722: //if (fBindNamespaces) {
723: // fNamespaceContext.popContext();
724: //}
725:
726: // call handler
727: if (fDocumentHandler != null) {
728: //xxx: Commenting this now since we are not passing any information
729: //along the pipeline. However, we do need to set the values if we
730: //need to pass the values along the pipeline.
731:
732: //fDocumentHandler.endElement(fElementQName, null);
733: }
734: if (dtdGrammarUtil != null)
735: dtdGrammarUtil.endElement(endElementName);
736:
737: fScanEndElement = true;
738: return fMarkupDepth;
739:
740: } // scanEndElement():int
741:
742: //getNamespaceContext
743: public NamespaceContext getNamespaceContext() {
744: return fNamespaceContext;
745: }
746:
747: public void reset(XMLComponentManager componentManager)
748: throws XMLConfigurationException {
749:
750: super .reset(componentManager);
751: fPerformValidation = false;
752: fBindNamespaces = false;
753: }
754:
755: /** Creates a content driver. */
756: protected Driver createContentDriver() {
757: return new NSContentDriver();
758: } // createContentDriver():Driver
759:
760: /**
761: * Driver to handle content scanning.
762: */
763: protected final class NSContentDriver extends ContentDriver {
764: /**
765: * Scan for root element hook. This method is a hook for
766: * subclasses to add code that handles scanning for the root
767: * element. This method will also attempt to remove DTD validator
768: * from the pipeline, if there is no DTD grammar. If DTD validator
769: * is no longer in the pipeline bind namespaces in the scanner.
770: *
771: *
772: * @return True if the caller should stop and return true which
773: * allows the scanner to switch to a new scanning
774: * driver. A return value of false indicates that
775: * the content driver should continue as normal.
776: */
777: protected boolean scanRootElementHook() throws IOException,
778: XNIException {
779:
780: if (scanStartElement()) {
781: setScannerState(SCANNER_STATE_TRAILING_MISC);
782: setDriver(fTrailingMiscDriver);
783: return true;
784: }
785: return false;
786:
787: } // scanRootElementHook():boolean
788: }
789:
790: XMLString getString() {
791: if (fAttributeCacheUsedCount < initialCacheCount
792: || fAttributeCacheUsedCount < attributeValueCache
793: .size()) {
794: return (XMLString) attributeValueCache
795: .get(fAttributeCacheUsedCount++);
796: } else {
797: XMLString str = new XMLString();
798: fAttributeCacheUsedCount++;
799: attributeValueCache.add(str);
800: return str;
801: }
802: }
803:
804: public XMLStringBuffer getDTDDecl() {
805: Entity entity = fEntityScanner.getCurrentEntity();
806: fDTDDecl.append(((Entity.ScannedEntity) entity).ch, fStartPos,
807: fEndPos - fStartPos);
808: if (fSeenInternalSubset)
809: fDTDDecl.append("]>");
810: return fDTDDecl;
811: }
812:
813: public String getCharacterEncodingScheme() {
814: return fDeclaredEncoding;
815: }
816:
817: } // class XMLNSDocumentScannerImpl
|