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