001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl;
019:
020: import org.apache.xerces.impl.msg.XMLMessageFormatter;
021: import org.apache.xerces.util.SymbolTable;
022: import org.apache.xerces.util.XMLSymbols;
023: import org.apache.xerces.xni.Augmentations;
024: import org.apache.xerces.xni.NamespaceContext;
025: import org.apache.xerces.xni.QName;
026: import org.apache.xerces.xni.XMLAttributes;
027: import org.apache.xerces.xni.XMLDocumentHandler;
028: import org.apache.xerces.xni.XMLLocator;
029: import org.apache.xerces.xni.XMLResourceIdentifier;
030: import org.apache.xerces.xni.XMLString;
031: import org.apache.xerces.xni.XNIException;
032: import org.apache.xerces.xni.parser.XMLComponent;
033: import org.apache.xerces.xni.parser.XMLComponentManager;
034: import org.apache.xerces.xni.parser.XMLConfigurationException;
035: import org.apache.xerces.xni.parser.XMLDocumentFilter;
036: import org.apache.xerces.xni.parser.XMLDocumentSource;
037:
038: /**
039: * This class performs namespace binding on the startElement and endElement
040: * method calls and passes all other methods through to the registered
041: * document handler. This class can be configured to only pass the
042: * start and end prefix mappings (start/endPrefixMapping).
043: * <p>
044: * This component requires the following features and properties from the
045: * component manager that uses it:
046: * <ul>
047: * <li>http://xml.org/sax/features/namespaces</li>
048: * <li>http://apache.org/xml/properties/internal/symbol-table</li>
049: * <li>http://apache.org/xml/properties/internal/error-reporter</li>
050: * </ul>
051: *
052: * @xerces.internal
053: *
054: * @author Andy Clark, IBM
055: *
056: * @version $Id: XMLNamespaceBinder.java 572055 2007-09-02 17:55:43Z mrglavas $
057: */
058: public class XMLNamespaceBinder implements XMLComponent,
059: XMLDocumentFilter {
060:
061: //
062: // Constants
063: //
064:
065: // feature identifiers
066:
067: /** Feature identifier: namespaces. */
068: protected static final String NAMESPACES = Constants.SAX_FEATURE_PREFIX
069: + Constants.NAMESPACES_FEATURE;
070:
071: // property identifiers
072:
073: /** Property identifier: symbol table. */
074: protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX
075: + Constants.SYMBOL_TABLE_PROPERTY;
076:
077: /** Property identifier: error reporter. */
078: protected static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX
079: + Constants.ERROR_REPORTER_PROPERTY;
080:
081: // recognized features and properties
082:
083: /** Recognized features. */
084: private static final String[] RECOGNIZED_FEATURES = { NAMESPACES, };
085:
086: /** Feature defaults. */
087: private static final Boolean[] FEATURE_DEFAULTS = { null, };
088:
089: /** Recognized properties. */
090: private static final String[] RECOGNIZED_PROPERTIES = {
091: SYMBOL_TABLE, ERROR_REPORTER, };
092:
093: /** Property defaults. */
094: private static final Object[] PROPERTY_DEFAULTS = { null, null, };
095:
096: //
097: // Data
098: //
099:
100: // features
101:
102: /** Namespaces. */
103: protected boolean fNamespaces;
104:
105: // properties
106:
107: /** Symbol table. */
108: protected SymbolTable fSymbolTable;
109:
110: /** Error reporter. */
111: protected XMLErrorReporter fErrorReporter;
112:
113: // handlers
114:
115: /** Document handler. */
116: protected XMLDocumentHandler fDocumentHandler;
117:
118: protected XMLDocumentSource fDocumentSource;
119:
120: // settings
121:
122: /** Only pass start and end prefix mapping events. */
123: protected boolean fOnlyPassPrefixMappingEvents;
124:
125: // shared context
126:
127: /** Namespace context. */
128: private NamespaceContext fNamespaceContext;
129:
130: // temp vars
131:
132: /** Attribute QName. */
133: private final QName fAttributeQName = new QName();
134:
135: //
136: // Constructors
137: //
138:
139: /** Default constructor. */
140: public XMLNamespaceBinder() {
141: } // <init>()
142:
143: //
144: // Public methods
145: //
146:
147: // settings
148:
149: /**
150: * Sets whether the namespace binder only passes the prefix mapping
151: * events to the registered document handler or passes all document
152: * events.
153: *
154: * @param onlyPassPrefixMappingEvents True to pass only the prefix
155: * mapping events; false to pass
156: * all events.
157: */
158: public void setOnlyPassPrefixMappingEvents(
159: boolean onlyPassPrefixMappingEvents) {
160: fOnlyPassPrefixMappingEvents = onlyPassPrefixMappingEvents;
161: } // setOnlyPassPrefixMappingEvents(boolean)
162:
163: /**
164: * Returns true if the namespace binder only passes the prefix mapping
165: * events to the registered document handler; false if the namespace
166: * binder passes all document events.
167: */
168: public boolean getOnlyPassPrefixMappingEvents() {
169: return fOnlyPassPrefixMappingEvents;
170: } // getOnlyPassPrefixMappingEvents():boolean
171:
172: //
173: // XMLComponent methods
174: //
175:
176: /**
177: * Resets the component. The component can query the component manager
178: * about any features and properties that affect the operation of the
179: * component.
180: *
181: * @param componentManager The component manager.
182: *
183: * @throws SAXException Thrown by component on initialization error.
184: * For example, if a feature or property is
185: * required for the operation of the component, the
186: * component manager may throw a
187: * SAXNotRecognizedException or a
188: * SAXNotSupportedException.
189: */
190: public void reset(XMLComponentManager componentManager)
191: throws XNIException {
192:
193: // features
194: try {
195: fNamespaces = componentManager.getFeature(NAMESPACES);
196: } catch (XMLConfigurationException e) {
197: fNamespaces = true;
198: }
199:
200: // Xerces properties
201: fSymbolTable = (SymbolTable) componentManager
202: .getProperty(SYMBOL_TABLE);
203: fErrorReporter = (XMLErrorReporter) componentManager
204: .getProperty(ERROR_REPORTER);
205:
206: } // reset(XMLComponentManager)
207:
208: /**
209: * Returns a list of feature identifiers that are recognized by
210: * this component. This method may return null if no features
211: * are recognized by this component.
212: */
213: public String[] getRecognizedFeatures() {
214: return (String[]) (RECOGNIZED_FEATURES.clone());
215: } // getRecognizedFeatures():String[]
216:
217: /**
218: * Sets the state of a feature. This method is called by the component
219: * manager any time after reset when a feature changes state.
220: * <p>
221: * <strong>Note:</strong> Components should silently ignore features
222: * that do not affect the operation of the component.
223: *
224: * @param featureId The feature identifier.
225: * @param state The state of the feature.
226: *
227: * @throws SAXNotRecognizedException The component should not throw
228: * this exception.
229: * @throws SAXNotSupportedException The component should not throw
230: * this exception.
231: */
232: public void setFeature(String featureId, boolean state)
233: throws XMLConfigurationException {
234: } // setFeature(String,boolean)
235:
236: /**
237: * Returns a list of property identifiers that are recognized by
238: * this component. This method may return null if no properties
239: * are recognized by this component.
240: */
241: public String[] getRecognizedProperties() {
242: return (String[]) (RECOGNIZED_PROPERTIES.clone());
243: } // getRecognizedProperties():String[]
244:
245: /**
246: * Sets the value of a property during parsing.
247: *
248: * @param propertyId
249: * @param value
250: */
251: public void setProperty(String propertyId, Object value)
252: throws XMLConfigurationException {
253:
254: // Xerces properties
255: if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
256: final int suffixLength = propertyId.length()
257: - Constants.XERCES_PROPERTY_PREFIX.length();
258:
259: if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY
260: .length()
261: && propertyId
262: .endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
263: fSymbolTable = (SymbolTable) value;
264: } else if (suffixLength == Constants.ERROR_REPORTER_PROPERTY
265: .length()
266: && propertyId
267: .endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
268: fErrorReporter = (XMLErrorReporter) value;
269: }
270: return;
271: }
272:
273: } // setProperty(String,Object)
274:
275: /**
276: * Returns the default state for a feature, or null if this
277: * component does not want to report a default value for this
278: * feature.
279: *
280: * @param featureId The feature identifier.
281: *
282: * @since Xerces 2.2.0
283: */
284: public Boolean getFeatureDefault(String featureId) {
285: for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
286: if (RECOGNIZED_FEATURES[i].equals(featureId)) {
287: return FEATURE_DEFAULTS[i];
288: }
289: }
290: return null;
291: } // getFeatureDefault(String):Boolean
292:
293: /**
294: * Returns the default state for a property, or null if this
295: * component does not want to report a default value for this
296: * property.
297: *
298: * @param propertyId The property identifier.
299: *
300: * @since Xerces 2.2.0
301: */
302: public Object getPropertyDefault(String propertyId) {
303: for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
304: if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
305: return PROPERTY_DEFAULTS[i];
306: }
307: }
308: return null;
309: } // getPropertyDefault(String):Object
310:
311: //
312: // XMLDocumentSource methods
313: //
314:
315: /** Sets the document handler to receive information about the document. */
316: public void setDocumentHandler(XMLDocumentHandler documentHandler) {
317: fDocumentHandler = documentHandler;
318: } // setDocumentHandler(XMLDocumentHandler)
319:
320: /** Returns the document handler */
321: public XMLDocumentHandler getDocumentHandler() {
322: return fDocumentHandler;
323: } // setDocumentHandler(XMLDocumentHandler)
324:
325: //
326: // XMLDocumentHandler methods
327: //
328:
329: /** Sets the document source */
330: public void setDocumentSource(XMLDocumentSource source) {
331: fDocumentSource = source;
332: } // setDocumentSource
333:
334: /** Returns the document source */
335: public XMLDocumentSource getDocumentSource() {
336: return fDocumentSource;
337: } // getDocumentSource
338:
339: /**
340: * This method notifies the start of a general entity.
341: * <p>
342: * <strong>Note:</strong> This method is not called for entity references
343: * appearing as part of attribute values.
344: *
345: * @param name The name of the general entity.
346: * @param identifier The resource identifier.
347: * @param encoding The auto-detected IANA encoding name of the entity
348: * stream. This value will be null in those situations
349: * where the entity encoding is not auto-detected (e.g.
350: * internal entities or a document entity that is
351: * parsed from a java.io.Reader).
352: * @param augs Additional information that may include infoset augmentations
353: *
354: * @exception XNIException Thrown by handler to signal an error.
355: */
356: public void startGeneralEntity(String name,
357: XMLResourceIdentifier identifier, String encoding,
358: Augmentations augs) throws XNIException {
359: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
360: fDocumentHandler.startGeneralEntity(name, identifier,
361: encoding, augs);
362: }
363: } // startEntity(String,String,String,String,String)
364:
365: /**
366: * Notifies of the presence of a TextDecl line in an entity. If present,
367: * this method will be called immediately following the startEntity call.
368: * <p>
369: * <strong>Note:</strong> This method will never be called for the
370: * document entity; it is only called for external general entities
371: * referenced in document content.
372: * <p>
373: * <strong>Note:</strong> This method is not called for entity references
374: * appearing as part of attribute values.
375: *
376: * @param version The XML version, or null if not specified.
377: * @param encoding The IANA encoding name of the entity.
378: * @param augs Additional information that may include infoset augmentations
379: *
380: * @throws XNIException Thrown by handler to signal an error.
381: */
382: public void textDecl(String version, String encoding,
383: Augmentations augs) throws XNIException {
384: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
385: fDocumentHandler.textDecl(version, encoding, augs);
386: }
387: } // textDecl(String,String)
388:
389: /**
390: * The start of the document.
391: *
392: * @param locator The system identifier of the entity if the entity
393: * is external, null otherwise.
394: * @param encoding The auto-detected IANA encoding name of the entity
395: * stream. This value will be null in those situations
396: * where the entity encoding is not auto-detected (e.g.
397: * internal entities or a document entity that is
398: * parsed from a java.io.Reader).
399: * @param namespaceContext
400: * The namespace context in effect at the
401: * start of this document.
402: * This object represents the current context.
403: * Implementors of this class are responsible
404: * for copying the namespace bindings from the
405: * the current context (and its parent contexts)
406: * if that information is important.
407: * @param augs Additional information that may include infoset augmentations
408: *
409: * @throws XNIException Thrown by handler to signal an error.
410: */
411: public void startDocument(XMLLocator locator, String encoding,
412: NamespaceContext namespaceContext, Augmentations augs)
413: throws XNIException {
414: fNamespaceContext = namespaceContext;
415:
416: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
417: fDocumentHandler.startDocument(locator, encoding,
418: namespaceContext, augs);
419: }
420: } // startDocument(XMLLocator,String)
421:
422: /**
423: * Notifies of the presence of an XMLDecl line in the document. If
424: * present, this method will be called immediately following the
425: * startDocument call.
426: *
427: * @param version The XML version.
428: * @param encoding The IANA encoding name of the document, or null if
429: * not specified.
430: * @param standalone The standalone value, or null if not specified.
431: * @param augs Additional information that may include infoset augmentations
432: *
433: * @throws XNIException Thrown by handler to signal an error.
434: */
435: public void xmlDecl(String version, String encoding,
436: String standalone, Augmentations augs) throws XNIException {
437: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
438: fDocumentHandler.xmlDecl(version, encoding, standalone,
439: augs);
440: }
441: } // xmlDecl(String,String,String)
442:
443: /**
444: * Notifies of the presence of the DOCTYPE line in the document.
445: *
446: * @param rootElement The name of the root element.
447: * @param publicId The public identifier if an external DTD or null
448: * if the external DTD is specified using SYSTEM.
449: * @param systemId The system identifier if an external DTD, null
450: * otherwise.
451: * @param augs Additional information that may include infoset augmentations
452: *
453: * @throws XNIException Thrown by handler to signal an error.
454: */
455: public void doctypeDecl(String rootElement, String publicId,
456: String systemId, Augmentations augs) throws XNIException {
457: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
458: fDocumentHandler.doctypeDecl(rootElement, publicId,
459: systemId, augs);
460: }
461: } // doctypeDecl(String,String,String)
462:
463: /**
464: * A comment.
465: *
466: * @param text The text in the comment.
467: * @param augs Additional information that may include infoset augmentations
468: *
469: * @throws XNIException Thrown by application to signal an error.
470: */
471: public void comment(XMLString text, Augmentations augs)
472: throws XNIException {
473: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
474: fDocumentHandler.comment(text, augs);
475: }
476: } // comment(XMLString)
477:
478: /**
479: * A processing instruction. Processing instructions consist of a
480: * target name and, optionally, text data. The data is only meaningful
481: * to the application.
482: * <p>
483: * Typically, a processing instruction's data will contain a series
484: * of pseudo-attributes. These pseudo-attributes follow the form of
485: * element attributes but are <strong>not</strong> parsed or presented
486: * to the application as anything other than text. The application is
487: * responsible for parsing the data.
488: *
489: * @param target The target.
490: * @param data The data or null if none specified.
491: * @param augs Additional information that may include infoset augmentations
492: *
493: * @throws XNIException Thrown by handler to signal an error.
494: */
495: public void processingInstruction(String target, XMLString data,
496: Augmentations augs) throws XNIException {
497: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
498: fDocumentHandler.processingInstruction(target, data, augs);
499: }
500: } // processingInstruction(String,XMLString)
501:
502: /**
503: * Binds the namespaces. This method will handle calling the
504: * document handler to start the prefix mappings.
505: * <p>
506: * <strong>Note:</strong> This method makes use of the
507: * fAttributeQName variable. Any contents of the variable will
508: * be destroyed. Caller should copy the values out of this
509: * temporary variable before calling this method.
510: *
511: * @param element The name of the element.
512: * @param attributes The element attributes.
513: * @param augs Additional information that may include infoset augmentations
514: *
515: * @throws XNIException Thrown by handler to signal an error.
516: */
517: public void startElement(QName element, XMLAttributes attributes,
518: Augmentations augs) throws XNIException {
519:
520: if (fNamespaces) {
521: handleStartElement(element, attributes, augs, false);
522: } else if (fDocumentHandler != null) {
523: fDocumentHandler.startElement(element, attributes, augs);
524: }
525:
526: } // startElement(QName,XMLAttributes)
527:
528: /**
529: * An empty element.
530: *
531: * @param element The name of the element.
532: * @param attributes The element attributes.
533: * @param augs Additional information that may include infoset augmentations
534: *
535: * @throws XNIException Thrown by handler to signal an error.
536: */
537: public void emptyElement(QName element, XMLAttributes attributes,
538: Augmentations augs) throws XNIException {
539:
540: if (fNamespaces) {
541: handleStartElement(element, attributes, augs, true);
542: handleEndElement(element, augs, true);
543: } else if (fDocumentHandler != null) {
544: fDocumentHandler.emptyElement(element, attributes, augs);
545: }
546:
547: } // emptyElement(QName,XMLAttributes)
548:
549: /**
550: * Character content.
551: *
552: * @param text The content.
553: * @param augs Additional information that may include infoset augmentations
554: *
555: * @throws XNIException Thrown by handler to signal an error.
556: */
557: public void characters(XMLString text, Augmentations augs)
558: throws XNIException {
559: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
560: fDocumentHandler.characters(text, augs);
561: }
562: } // characters(XMLString)
563:
564: /**
565: * Ignorable whitespace. For this method to be called, the document
566: * source must have some way of determining that the text containing
567: * only whitespace characters should be considered ignorable. For
568: * example, the validator can determine if a length of whitespace
569: * characters in the document are ignorable based on the element
570: * content model.
571: *
572: * @param text The ignorable whitespace.
573: * @param augs Additional information that may include infoset augmentations
574: *
575: * @throws XNIException Thrown by handler to signal an error.
576: */
577: public void ignorableWhitespace(XMLString text, Augmentations augs)
578: throws XNIException {
579: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
580: fDocumentHandler.ignorableWhitespace(text, augs);
581: }
582: } // ignorableWhitespace(XMLString)
583:
584: /**
585: * The end of an element.
586: *
587: * @param element The name of the element.
588: * @param augs Additional information that may include infoset augmentations
589: *
590: * @throws XNIException Thrown by handler to signal an error.
591: */
592: public void endElement(QName element, Augmentations augs)
593: throws XNIException {
594:
595: if (fNamespaces) {
596: handleEndElement(element, augs, false);
597: } else if (fDocumentHandler != null) {
598: fDocumentHandler.endElement(element, augs);
599: }
600:
601: } // endElement(QName)
602:
603: /**
604: * The start of a CDATA section.
605: * @param augs Additional information that may include infoset augmentations
606: *
607: * @throws XNIException Thrown by handler to signal an error.
608: */
609: public void startCDATA(Augmentations augs) throws XNIException {
610: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
611: fDocumentHandler.startCDATA(augs);
612: }
613: } // startCDATA()
614:
615: /**
616: * The end of a CDATA section.
617: * @param augs Additional information that may include infoset augmentations
618: *
619: * @throws XNIException Thrown by handler to signal an error.
620: */
621: public void endCDATA(Augmentations augs) throws XNIException {
622: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
623: fDocumentHandler.endCDATA(augs);
624: }
625: } // endCDATA()
626:
627: /**
628: * The end of the document.
629: * @param augs Additional information that may include infoset augmentations
630: *
631: * @throws XNIException Thrown by handler to signal an error.
632: */
633: public void endDocument(Augmentations augs) throws XNIException {
634: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
635: fDocumentHandler.endDocument(augs);
636: }
637: } // endDocument()
638:
639: /**
640: * This method notifies the end of a general entity.
641: * <p>
642: * <strong>Note:</strong> This method is not called for entity references
643: * appearing as part of attribute values.
644: *
645: * @param name The name of the entity.
646: * @param augs Additional information that may include infoset augmentations
647: *
648: * @exception XNIException
649: * Thrown by handler to signal an error.
650: */
651: public void endGeneralEntity(String name, Augmentations augs)
652: throws XNIException {
653: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
654: fDocumentHandler.endGeneralEntity(name, augs);
655: }
656: } // endEntity(String)
657:
658: //
659: // Protected methods
660: //
661:
662: /** Handles start element. */
663: protected void handleStartElement(QName element,
664: XMLAttributes attributes, Augmentations augs,
665: boolean isEmpty) throws XNIException {
666:
667: // add new namespace context
668: fNamespaceContext.pushContext();
669:
670: if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
671: fErrorReporter.reportError(
672: XMLMessageFormatter.XMLNS_DOMAIN,
673: "ElementXMLNSPrefix",
674: new Object[] { element.rawname },
675: XMLErrorReporter.SEVERITY_FATAL_ERROR);
676: }
677:
678: // search for new namespace bindings
679: int length = attributes.getLength();
680: for (int i = 0; i < length; i++) {
681: String localpart = attributes.getLocalName(i);
682: String prefix = attributes.getPrefix(i);
683: // when it's of form xmlns="..." or xmlns:prefix="...",
684: // it's a namespace declaration. but prefix:xmlns="..." isn't.
685: if (prefix == XMLSymbols.PREFIX_XMLNS
686: || prefix == XMLSymbols.EMPTY_STRING
687: && localpart == XMLSymbols.PREFIX_XMLNS) {
688:
689: // get the internalized value of this attribute
690: String uri = fSymbolTable.addSymbol(attributes
691: .getValue(i));
692:
693: // 1. "xmlns" can't be bound to any namespace
694: if (prefix == XMLSymbols.PREFIX_XMLNS
695: && localpart == XMLSymbols.PREFIX_XMLNS) {
696: fErrorReporter.reportError(
697: XMLMessageFormatter.XMLNS_DOMAIN,
698: "CantBindXMLNS", new Object[] { attributes
699: .getQName(i) },
700: XMLErrorReporter.SEVERITY_FATAL_ERROR);
701: }
702:
703: // 2. the namespace for "xmlns" can't be bound to any prefix
704: if (uri == NamespaceContext.XMLNS_URI) {
705: fErrorReporter.reportError(
706: XMLMessageFormatter.XMLNS_DOMAIN,
707: "CantBindXMLNS", new Object[] { attributes
708: .getQName(i) },
709: XMLErrorReporter.SEVERITY_FATAL_ERROR);
710: }
711:
712: // 3. "xml" can't be bound to any other namespace than it's own
713: if (localpart == XMLSymbols.PREFIX_XML) {
714: if (uri != NamespaceContext.XML_URI) {
715: fErrorReporter
716: .reportError(
717: XMLMessageFormatter.XMLNS_DOMAIN,
718: "CantBindXML",
719: new Object[] { attributes
720: .getQName(i) },
721: XMLErrorReporter.SEVERITY_FATAL_ERROR);
722: }
723: }
724: // 4. the namespace for "xml" can't be bound to any other prefix
725: else {
726: if (uri == NamespaceContext.XML_URI) {
727: fErrorReporter
728: .reportError(
729: XMLMessageFormatter.XMLNS_DOMAIN,
730: "CantBindXML",
731: new Object[] { attributes
732: .getQName(i) },
733: XMLErrorReporter.SEVERITY_FATAL_ERROR);
734: }
735: }
736:
737: prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart
738: : XMLSymbols.EMPTY_STRING;
739:
740: // http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix
741: // We should only report an error if there is a prefix,
742: // that is, the local part is not "xmlns". -SG
743: // Since this is an error condition in XML 1.0,
744: // and should be relatively uncommon in XML 1.1,
745: // making this test into a method call to reuse code
746: // should be acceptable. - NG
747: if (prefixBoundToNullURI(uri, localpart)) {
748: fErrorReporter.reportError(
749: XMLMessageFormatter.XMLNS_DOMAIN,
750: "EmptyPrefixedAttName",
751: new Object[] { attributes.getQName(i) },
752: XMLErrorReporter.SEVERITY_FATAL_ERROR);
753: continue;
754: }
755:
756: // declare prefix in context
757: fNamespaceContext.declarePrefix(prefix,
758: uri.length() != 0 ? uri : null);
759:
760: }
761: }
762:
763: // bind the element
764: String prefix = element.prefix != null ? element.prefix
765: : XMLSymbols.EMPTY_STRING;
766: element.uri = fNamespaceContext.getURI(prefix);
767: if (element.prefix == null && element.uri != null) {
768: element.prefix = XMLSymbols.EMPTY_STRING;
769: }
770: if (element.prefix != null && element.uri == null) {
771: fErrorReporter.reportError(
772: XMLMessageFormatter.XMLNS_DOMAIN,
773: "ElementPrefixUnbound", new Object[] {
774: element.prefix, element.rawname },
775: XMLErrorReporter.SEVERITY_FATAL_ERROR);
776: }
777:
778: // bind the attributes
779: for (int i = 0; i < length; i++) {
780: attributes.getName(i, fAttributeQName);
781: String aprefix = fAttributeQName.prefix != null ? fAttributeQName.prefix
782: : XMLSymbols.EMPTY_STRING;
783: String arawname = fAttributeQName.rawname;
784: if (arawname == XMLSymbols.PREFIX_XMLNS) {
785: fAttributeQName.uri = fNamespaceContext
786: .getURI(XMLSymbols.PREFIX_XMLNS);
787: attributes.setName(i, fAttributeQName);
788: } else if (aprefix != XMLSymbols.EMPTY_STRING) {
789: fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
790: if (fAttributeQName.uri == null) {
791: fErrorReporter.reportError(
792: XMLMessageFormatter.XMLNS_DOMAIN,
793: "AttributePrefixUnbound",
794: new Object[] { element.rawname, arawname,
795: aprefix },
796: XMLErrorReporter.SEVERITY_FATAL_ERROR);
797: }
798: attributes.setName(i, fAttributeQName);
799: }
800: }
801:
802: // verify that duplicate attributes don't exist
803: // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
804: int attrCount = attributes.getLength();
805: for (int i = 0; i < attrCount - 1; i++) {
806: String auri = attributes.getURI(i);
807: if (auri == null || auri == NamespaceContext.XMLNS_URI) {
808: continue;
809: }
810: String alocalpart = attributes.getLocalName(i);
811: for (int j = i + 1; j < attrCount; j++) {
812: String blocalpart = attributes.getLocalName(j);
813: String buri = attributes.getURI(j);
814: if (alocalpart == blocalpart && auri == buri) {
815: fErrorReporter.reportError(
816: XMLMessageFormatter.XMLNS_DOMAIN,
817: "AttributeNSNotUnique",
818: new Object[] { element.rawname, alocalpart,
819: auri },
820: XMLErrorReporter.SEVERITY_FATAL_ERROR);
821: }
822: }
823: }
824:
825: // call handler
826: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
827: if (isEmpty) {
828: fDocumentHandler
829: .emptyElement(element, attributes, augs);
830: } else {
831: fDocumentHandler
832: .startElement(element, attributes, augs);
833: }
834: }
835:
836: } // handleStartElement(QName,XMLAttributes,boolean)
837:
838: /** Handles end element. */
839: protected void handleEndElement(QName element, Augmentations augs,
840: boolean isEmpty) throws XNIException {
841:
842: // bind element
843: String eprefix = element.prefix != null ? element.prefix
844: : XMLSymbols.EMPTY_STRING;
845: element.uri = fNamespaceContext.getURI(eprefix);
846: if (element.uri != null) {
847: element.prefix = eprefix;
848: }
849:
850: // call handlers
851: if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
852: if (!isEmpty) {
853: fDocumentHandler.endElement(element, augs);
854: }
855: }
856:
857: // pop context
858: fNamespaceContext.popContext();
859:
860: } // handleEndElement(QName,boolean)
861:
862: // returns true iff the given prefix is bound to "" *and*
863: // this is disallowed by the version of XML namespaces in use.
864: protected boolean prefixBoundToNullURI(String uri, String localpart) {
865: return (uri == XMLSymbols.EMPTY_STRING && localpart != XMLSymbols.PREFIX_XMLNS);
866: } // prefixBoundToNullURI(String, String): boolean
867:
868: } // class XMLNamespaceBinder
|