001: /*
002: * $Id: Element.java,v 1.43 2007/09/18 11:21:02 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.common.xml;
008:
009: import java.util.ArrayList;
010: import java.util.Collections;
011: import java.util.Iterator;
012: import java.util.List;
013: import java.util.Map;
014:
015: import org.xins.common.Log;
016: import org.xins.common.MandatoryArgumentChecker;
017: import org.xins.common.Utils;
018: import org.xins.common.collections.ChainedMap;
019: import org.xins.common.collections.ProtectedList;
020: import org.xins.common.text.ParseException;
021:
022: /**
023: * XML Element.
024: *
025: * <p>Note that this class is not thread-safe. It should not be used from
026: * different threads at the same time. This applies even to read operations.
027: *
028: * <p>Note that the namespace URIs and local names are not checked for
029: * validity in this class.
030: *
031: * <p>Instances of this class cannot be created directly, using a constructor.
032: * Instead, use {@link ElementBuilder} to build an XML element, or
033: * {@link ElementParser} to parse an XML string.
034: *
035: * @version $Revision: 1.43 $ $Date: 2007/09/18 11:21:02 $
036: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
037: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
038: *
039: * @since XINS 1.1.0
040: */
041: public class Element implements Cloneable {
042:
043: /**
044: * Fully-qualified name of this class.
045: */
046: private static final String CLASSNAME = Element.class.getName();
047:
048: /**
049: * The secret key to use to add child elements.
050: */
051: private static final Object SECRET_KEY = new Object();
052:
053: /**
054: * The namespace prefix. This field can be <code>null</code>, but it can never
055: * be an empty string.
056: */
057: private String _namespacePrefix;
058:
059: /**
060: * The namespace URI. This field can be <code>null</code>, but it can never
061: * be an empty string.
062: */
063: private String _namespaceURI;
064:
065: /**
066: * The local name. This field is never <code>null</code>.
067: */
068: private String _localName;
069:
070: /**
071: * The child elements. This field is lazily initialized is initially
072: * <code>null</code>.
073: */
074: private ArrayList _children;
075:
076: /**
077: * The attributes. This field is lazily initialized and is initially
078: * <code>null</code>.
079: */
080: private ChainedMap _attributes;
081:
082: /**
083: * The character content for this element. Can be <code>null</code>.
084: */
085: private String _text;
086:
087: /**
088: * Creates a new <code>Element</code> with no namespace.
089: *
090: * @param localName
091: * the local name of the element, cannot be <code>null</code>.
092: *
093: * @throws IllegalArgumentException
094: * if <code>localName == null</code>.
095: *
096: * @since XINS 2.0.
097: */
098: public Element(String localName) throws IllegalArgumentException {
099: this (null, null, localName);
100: }
101:
102: /**
103: * Creates a new <code>Element</code>.
104: *
105: * @param namespaceURI
106: * the namespace URI for the element, can be <code>null</code>; an empty
107: * string is equivalent to <code>null</code>.
108: *
109: * @param localName
110: * the local name of the element, cannot be <code>null</code>.
111: *
112: * @throws IllegalArgumentException
113: * if <code>localName == null</code>.
114: *
115: * @since XINS 2.0.
116: */
117: public Element(String namespaceURI, String localName)
118: throws IllegalArgumentException {
119: this (null, namespaceURI, localName);
120: }
121:
122: /**
123: * Creates a new <code>Element</code>.
124: *
125: * @param namespacePrefix
126: * the namespace prefix for the element, can be <code>null</code>; an empty
127: * string is equivalent to <code>null</code>.
128: *
129: * @param namespaceURI
130: * the namespace URI for the element, can be <code>null</code>; an empty
131: * string is equivalent to <code>null</code>.
132: *
133: * @param localName
134: * the local name of the element, cannot be <code>null</code>.
135: *
136: * @throws IllegalArgumentException
137: * if <code>localName == null</code>.
138: *
139: * @since XINS 2.1.
140: */
141: public Element(String namespacePrefix, String namespaceURI,
142: String localName) throws IllegalArgumentException {
143:
144: // Check preconditions
145: MandatoryArgumentChecker.check("localName", localName);
146:
147: // An empty namespace prefix is equivalent to null
148: if (namespacePrefix != null && namespacePrefix.length() < 1) {
149: _namespacePrefix = null;
150: } else {
151: _namespacePrefix = namespacePrefix;
152: }
153:
154: // An empty namespace URI is equivalent to null
155: if (namespaceURI != null && namespaceURI.length() < 1) {
156: _namespaceURI = null;
157: } else {
158: _namespaceURI = namespaceURI;
159: }
160:
161: _localName = localName;
162: }
163:
164: /**
165: * Sets the namespace prefix.
166: *
167: * @param namespacePrefix
168: * the namespace prefix of this element, can be <code>null</code>.
169: * If the value if <code>null</code> or an empty string, the element is
170: * consider to have no namespace prefix.
171: *
172: * @throws IllegalArgumentException
173: * if <code>namespacePrefix == null</code>.
174: *
175: * @since XINS 2.1.
176: */
177: public void setNamespacePrefix(String namespacePrefix)
178: throws IllegalArgumentException {
179: if (namespacePrefix != null && namespacePrefix.length() == 0) {
180: _namespacePrefix = null;
181: } else {
182: _namespacePrefix = namespacePrefix;
183: }
184: }
185:
186: /**
187: * Gets the namespace prefix.
188: *
189: * @return
190: * the namespace prefix for this element, or <code>null</code> if there is
191: * none, but never an empty string.
192: *
193: * @since XINS 2.1.
194: */
195: public String getNamespacePrefix() {
196: return _namespacePrefix;
197: }
198:
199: /**
200: * Sets the namespace URI.
201: *
202: * @param namespaceURI
203: * the namespace URI of this element, can be <code>null</code>.
204: * If the value if <code>null</code> or an empty string, the element is
205: * consider to have no namespace URI.
206: *
207: * @throws IllegalArgumentException
208: * if <code>namespaceURI == null</code>.
209: *
210: * @since XINS 2.1.
211: */
212: public void setNamespaceURI(String namespaceURI)
213: throws IllegalArgumentException {
214: if (namespaceURI != null && namespaceURI.length() == 0) {
215: _namespaceURI = null;
216: } else {
217: _namespaceURI = namespaceURI;
218: }
219: }
220:
221: /**
222: * Gets the namespace URI.
223: *
224: * @return
225: * the namespace URI for this element, or <code>null</code> if there is
226: * none, but never an empty string.
227: */
228: public String getNamespaceURI() {
229: return _namespaceURI;
230: }
231:
232: /**
233: * Gets the local name.
234: *
235: * @return
236: * the local name of this element, cannot be <code>null</code>.
237: */
238: public String getLocalName() {
239: return _localName;
240: }
241:
242: /**
243: * Sets the local name.
244: *
245: * @param localName
246: * the local name of this element, cannot be <code>null</code>.
247: *
248: * @throws IllegalArgumentException
249: * if <code>localName == null</code>.
250: *
251: * @since XINS 2.0.
252: */
253: public void setLocalName(String localName)
254: throws IllegalArgumentException {
255: MandatoryArgumentChecker.check("localName", localName);
256: _localName = localName;
257: }
258:
259: /**
260: * Sets the specified attribute. If the value for the specified
261: * attribute is already set, then the previous value is replaced.
262: *
263: * @param localName
264: * the local name for the attribute, cannot be <code>null</code>.
265: *
266: * @param value
267: * the value for the attribute, can be <code>null</code>.
268: *
269: * @throws IllegalArgumentException
270: * if <code>localName == null</code>.
271: *
272: * @since XINS 2.0.
273: */
274: public void setAttribute(String localName, String value)
275: throws IllegalArgumentException {
276: setAttribute(null, null, localName, value);
277: }
278:
279: /**
280: * Sets the specified attribute. If the value for the specified
281: * attribute is already set, then the previous value is replaced.
282: *
283: * @param namespaceURI
284: * the namespace URI for the attribute, can be <code>null</code>; an
285: * empty string is equivalent to <code>null</code>.
286: *
287: * @param localName
288: * the local name for the attribute, cannot be <code>null</code>.
289: *
290: * @param value
291: * the value for the attribute, can be <code>null</code>.
292: *
293: * @throws IllegalArgumentException
294: * if <code>localName == null</code>.
295: *
296: * @since XINS 2.0.
297: */
298: public void setAttribute(String namespaceURI, String localName,
299: String value) throws IllegalArgumentException {
300: setAttribute(null, namespaceURI, localName, value);
301: }
302:
303: /**
304: * Sets the specified attribute. If the value for the specified
305: * attribute is already set, then the previous value is replaced.
306: *
307: * @param namespacePrefix
308: * the namespace prefix for the attribute, can be <code>null</code>; an
309: * empty string is equivalent to <code>null</code>.
310: *
311: * @param namespaceURI
312: * the namespace URI for the attribute, can be <code>null</code>; an
313: * empty string is equivalent to <code>null</code>.
314: *
315: * @param localName
316: * the local name for the attribute, cannot be <code>null</code>.
317: *
318: * @param value
319: * the value for the attribute, can be <code>null</code>.
320: *
321: * @throws IllegalArgumentException
322: * if <code>localName == null</code>.
323: *
324: * @since XINS 2.1.
325: */
326: public void setAttribute(String namespacePrefix,
327: String namespaceURI, String localName, String value)
328: throws IllegalArgumentException {
329:
330: // Construct a QualifiedName object. This will check the preconditions.
331: QualifiedName qn = new QualifiedName(namespacePrefix,
332: namespaceURI, localName);
333:
334: if (_attributes == null) {
335: if (value == null) {
336: return;
337: }
338:
339: // Lazily initialize
340: _attributes = new ChainedMap();
341: }
342:
343: // Set or reset the attribute
344: _attributes.put(qn, value);
345: }
346:
347: /**
348: * Removes the specified attribute. If no attribute with the specified name
349: * exists, nothing happens.
350: *
351: * @param localName
352: * the local name for the attribute, cannot be <code>null</code>.
353: *
354: * @throws IllegalArgumentException
355: * if <code>localName == null</code>.
356: *
357: * @since XINS 2.0.
358: */
359: public void removeAttribute(String localName)
360: throws IllegalArgumentException {
361: removeAttribute(null, localName);
362: }
363:
364: /**
365: * Removes the specified attribute. If no attribute with the specified name
366: * exists, nothing happens.
367: *
368: * @param namespaceURI
369: * the namespace URI for the attribute, can be <code>null</code>; an
370: * empty string is equivalent to <code>null</code>.
371: *
372: * @param localName
373: * the local name for the attribute, cannot be <code>null</code>.
374: *
375: * @throws IllegalArgumentException
376: * if <code>localName == null</code>.
377: *
378: * @since XINS 2.0.
379: */
380: public void removeAttribute(String namespaceURI, String localName)
381: throws IllegalArgumentException {
382:
383: // Construct a QualifiedName object. This will check the preconditions.
384: QualifiedName qn = new QualifiedName(namespaceURI, localName);
385:
386: if (_attributes != null) {
387: _attributes.remove(qn);
388: }
389:
390: }
391:
392: /**
393: * Gets the attributes of this element.
394: *
395: * @return
396: * a {@link Map} (never <code>null</code>) which contains the attributes;
397: * each key in the <code>Map</code> is a {@link QualifiedName} instance
398: * (not <code>null</code>) and each value in it is a <code>String</code>
399: * instance (not <code>null</code>).
400: */
401: public Map getAttributeMap() {
402: if (_attributes == null) {
403: _attributes = new ChainedMap();
404: }
405: return _attributes;
406: }
407:
408: /**
409: * Gets the value of the attribute with the qualified name. If the
410: * qualified name does not specify a namespace, then only an attribute that
411: * does not have a namespace will match.
412: *
413: * @param qn
414: * a combination of an optional namespace and a mandatory local name, or
415: * <code>null</code>.
416: *
417: * @return
418: * the value of the attribute that matches the specified namespace and
419: * local name, or <code>null</code> if such an attribute is either not
420: * set or set to <code>null</code>.
421: *
422: * @throws IllegalArgumentException
423: * if <code>qn == null</code>.
424: */
425: public String getAttribute(QualifiedName qn)
426: throws IllegalArgumentException {
427:
428: // Check preconditions
429: MandatoryArgumentChecker.check("qn", qn);
430:
431: if (_attributes == null) {
432: return null;
433: } else {
434: return (String) _attributes.get(qn);
435: }
436: }
437:
438: /**
439: * Gets the value of the attribute with the specified namespace and local
440: * name. The namespace is optional. If the namespace is not given, then only
441: * an attribute that does not have a namespace will match.
442: *
443: * @param namespaceURI
444: * the namespace URI for the attribute, can be <code>null</code>; an
445: * empty string is equivalent to <code>null</code>; if specified this
446: * string must be a valid namespace URI.
447: *
448: * @param localName
449: * the local name of the attribute, cannot be <code>null</code>.
450: *
451: * @return
452: * the value of the attribute that matches the specified namespace and
453: * local name, or <code>null</code> if such an attribute is either not
454: * set or set to <code>null</code>.
455: *
456: * @throws IllegalArgumentException
457: * if <code>localName == null</code>.
458: */
459: public String getAttribute(String namespaceURI, String localName)
460: throws IllegalArgumentException {
461: QualifiedName qn = new QualifiedName(namespaceURI, localName);
462: return getAttribute(qn);
463: }
464:
465: /**
466: * Gets the value of an attribute that has no namespace.
467: *
468: * @param localName
469: * the local name of the attribute, cannot be <code>null</code>.
470: *
471: * @return
472: * the value of the attribute that matches the specified local name and
473: * has no namespace defined, or <code>null</code> if the attribute is
474: * either not set or set to <code>null</code>.
475: *
476: * @throws IllegalArgumentException
477: * if <code>localName == null</code>.
478: */
479: public String getAttribute(String localName)
480: throws IllegalArgumentException {
481: return getAttribute(null, localName);
482: }
483:
484: /**
485: * Adds a new child element.
486: *
487: * @param child
488: * the new child to add to this element, cannot be <code>null</code>.
489: *
490: * @throws IllegalArgumentException
491: * if <code>child == null || child == <em>this</em></code>.
492: *
493: * @since XINS 2.0.
494: */
495: public void addChild(Element child) throws IllegalArgumentException {
496:
497: final String METHODNAME = "addChild(Element)";
498:
499: // Check preconditions
500: MandatoryArgumentChecker.check("child", child);
501: if (child == this ) {
502: String message = "child == this";
503: Log.log_1050(CLASSNAME, METHODNAME,
504: Utils.getCallingClass(), Utils.getCallingMethod(),
505: message);
506: throw new IllegalArgumentException(message);
507: }
508:
509: // Lazily initialize
510: if (_children == null) {
511: _children = new ArrayList();
512: }
513:
514: _children.add(child);
515: }
516:
517: /**
518: * Removes a child element. If the child is not found, nothing is removed.
519: *
520: * @param child
521: * the child to be removed to this element, cannot be <code>null</code>.
522: *
523: * @throws IllegalArgumentException
524: * if <code>child == null || child == <em>this</em></code>.
525: *
526: * @since XINS 2.0.
527: */
528: public void removeChild(Element child)
529: throws IllegalArgumentException {
530:
531: final String METHODNAME = "removeChild(Element)";
532:
533: // Check preconditions
534: MandatoryArgumentChecker.check("child", child);
535: if (child == this ) {
536: String message = "child == this";
537: Log.log_1050(CLASSNAME, METHODNAME,
538: Utils.getCallingClass(), Utils.getCallingMethod(),
539: message);
540: throw new IllegalArgumentException(message);
541: }
542:
543: // Lazily initialize
544: if (_children == null) {
545: return;
546: }
547:
548: _children.remove(child);
549: }
550:
551: /**
552: * Gets the list of all child elements.
553: *
554: * @return
555: * the {@link List} containing all child elements; each
556: * element in the list is another <code>Element</code> instance;
557: * never <code>null</code>.
558: */
559: public List getChildElements() {
560:
561: if (_children == null) {
562: _children = new ArrayList();
563: }
564:
565: return _children;
566: }
567:
568: /**
569: * Gets the list of child elements that match the specified name.
570: *
571: * @param name
572: * the name for the child elements to match, cannot be
573: * <code>null</code>.
574: *
575: * @return
576: * a {@link List} containing each child element that matches the
577: * specified name as another <code>Element</code> instance;
578: * never <code>null</code>. The list cannot be modified.
579: *
580: * @throws IllegalArgumentException
581: * if <code>name == null</code>.
582: */
583: public List getChildElements(String name)
584: throws IllegalArgumentException {
585:
586: // TODO: Support namespaces
587:
588: // Check preconditions
589: MandatoryArgumentChecker.check("name", name);
590:
591: // If there are no children, then return null
592: if (_children == null || _children.size() == 0) {
593: return Collections.EMPTY_LIST;
594:
595: // There are children, find all matching ones
596: } else {
597: ProtectedList matches = new ProtectedList(SECRET_KEY);
598: Iterator it = _children.iterator();
599: while (it.hasNext()) {
600: Element child = (Element) it.next();
601: if (name.equals(child.getLocalName())) {
602: matches.add(SECRET_KEY, child);
603: }
604: }
605:
606: // If there are no matching children, then return null
607: if (matches.size() == 0) {
608: return Collections.EMPTY_LIST;
609:
610: // Otherwise return an immutable list with all matches
611: } else {
612: return matches;
613: }
614: }
615: }
616:
617: /**
618: * Sets the character content. The existing character content, if any, is
619: * replaced
620: *
621: * @param text
622: * the character content for this element, or <code>null</code>.
623: *
624: * @since XINS 2.0.
625: */
626: public void setText(String text) {
627: _text = text;
628: }
629:
630: /**
631: * Gets the character content, if any.
632: *
633: * @return
634: * the character content of this element, or <code>null</code> if no
635: * text has been specified for this element.
636: */
637: public String getText() {
638: return _text;
639: }
640:
641: /**
642: * Gets the unique child of this element.
643: *
644: * @param elementName
645: * the name of the child element to get, or <code>null</code> if the
646: * parent have a unique child.
647: *
648: * @return
649: * the sub-element of this element, never <code>null</code>.
650: *
651: * @throws ParseException
652: * if no child was found or more than one child was found.
653: *
654: * @since XINS 1.4.0
655: */
656: public Element getUniqueChildElement(String elementName)
657: throws ParseException {
658:
659: List childList;
660: if (elementName == null) {
661: childList = getChildElements();
662: } else {
663: childList = getChildElements(elementName);
664: }
665:
666: if (childList.size() == 0) {
667: throw new ParseException("No \"" + elementName
668: + "\" children found in the \"" + getLocalName()
669: + "\" element.");
670: } else if (childList.size() > 1) {
671: throw new ParseException("More than one \"" + elementName
672: + "\" children found in the \"" + getLocalName()
673: + "\" element.");
674: }
675: return (Element) childList.get(0);
676: }
677:
678: public int hashCode() {
679: int hashCode = _localName.hashCode();
680: if (_namespaceURI != null) {
681: hashCode += _namespaceURI.hashCode();
682: }
683: if (_attributes != null) {
684: hashCode += _attributes.hashCode();
685: }
686: if (_children != null) {
687: hashCode += _children.hashCode();
688: }
689: if (_text != null) {
690: hashCode += _text.hashCode();
691: }
692: return hashCode;
693: }
694:
695: public boolean equals(Object obj) {
696: if (obj == null || !(obj instanceof Element)) {
697: return false;
698: }
699: Element other = (Element) obj;
700: if (!_localName.equals(other.getLocalName())
701: || (_namespaceURI != null && !_namespaceURI
702: .equals(other._namespaceURI))
703: || (_namespaceURI == null && other._namespaceURI != null)) {
704: return false;
705: }
706: if ((_attributes != null && !_attributes
707: .equals(other._attributes))
708: || (_attributes == null && other._attributes != null)) {
709: return false;
710: }
711: // XXX This should be changed as the order of the children should no matter
712: if ((_children != null && !_children.equals(other._children))
713: || (_children == null && other._children != null)) {
714: return false;
715: }
716: if ((_text != null && !_text.equals(other._text))
717: || (_text == null && other._text != null)) {
718: return false;
719: }
720: return true;
721: }
722:
723: /**
724: * Clones this object. The clone will have the same namespace URI and local
725: * name and equivalent attributes, children and character content.
726: *
727: * @return
728: * a new clone of this object, never <code>null</code>.
729: */
730: public Object clone() {
731:
732: // Construct a new Element, copy all field values (shallow copy)
733: Element clone;
734: try {
735: clone = (Element) super .clone();
736: } catch (CloneNotSupportedException exception) {
737: throw Utils.logProgrammingError(exception);
738: }
739:
740: // Deep copy the children
741: if (_children != null) {
742: clone._children = (ArrayList) _children.clone();
743: }
744:
745: // Deep copy the attributes
746: if (_attributes != null) {
747: clone._attributes = (ChainedMap) _attributes.clone();
748: }
749:
750: return clone;
751: }
752:
753: /**
754: * Overrides the {@link Object#toString()} method to return
755: * the element as its XML representation.
756: *
757: * @return
758: * the XML representation of this element without the XML declaration,
759: * never <code>null</code>.
760: */
761: public String toString() {
762: ElementSerializer serializer = new ElementSerializer();
763: return serializer.serialize(this );
764: }
765:
766: /**
767: * Qualified name for an element or attribute. This is a combination of an
768: * optional namespace URI and a mandatory local name.
769: *
770: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
771: *
772: * @since XINS 1.1.0
773: */
774: public static final class QualifiedName {
775:
776: /**
777: * The hash code for this object.
778: */
779: private final int _hashCode;
780:
781: /**
782: * The namespace prefix. Can be <code>null</code>.
783: */
784: private final String _namespacePrefix;
785:
786: /**
787: * The namespace URI. Can be <code>null</code>.
788: */
789: private final String _namespaceURI;
790:
791: /**
792: * The local name. Cannot be <code>null</code>.
793: */
794: private final String _localName;
795:
796: /**
797: * Constructs a new <code>QualifiedName</code> with the specified
798: * namespace and local name.
799: *
800: * @param namespaceURI
801: * the namespace URI for the element, can be <code>null</code>; an
802: * empty string is equivalent to <code>null</code>.
803: *
804: * @param localName
805: * the local name of the element, cannot be <code>null</code>.
806: *
807: * @throws IllegalArgumentException
808: * if <code>localName == null</code>.
809: */
810: public QualifiedName(String namespaceURI, String localName)
811: throws IllegalArgumentException {
812: this (null, namespaceURI, localName);
813: }
814:
815: /**
816: * Constructs a new <code>QualifiedName</code> with the specified
817: * namespace and local name.
818: *
819: * @param namespacePrefix
820: * the namespace prefix for the element, can be <code>null</code>; an
821: * empty string is equivalent to <code>null</code>.
822: *
823: * @param namespaceURI
824: * the namespace URI for the element, can be <code>null</code>; an
825: * empty string is equivalent to <code>null</code>.
826: *
827: * @param localName
828: * the local name of the element, cannot be <code>null</code>.
829: *
830: * @throws IllegalArgumentException
831: * if <code>localName == null</code>.
832: *
833: * @since XINS 2.1.
834: */
835: public QualifiedName(String namespacePrefix,
836: String namespaceURI, String localName)
837: throws IllegalArgumentException {
838:
839: // Check preconditions
840: MandatoryArgumentChecker.check("localName", localName);
841:
842: // An empty namespace prefix is equivalent to null
843: if (namespacePrefix != null && namespacePrefix.length() < 1) {
844: _namespacePrefix = null;
845: } else {
846: _namespacePrefix = namespacePrefix;
847: }
848:
849: // An empty namespace URI is equivalent to null
850: if (namespaceURI != null && namespaceURI.length() < 1) {
851: _namespaceURI = null;
852: } else {
853: _namespaceURI = namespaceURI;
854: }
855:
856: // Initialize fields
857: _hashCode = localName.hashCode();
858: _localName = localName;
859: }
860:
861: /**
862: * Returns the hash code value for this object.
863: *
864: * @return
865: * the hash code value.
866: */
867: public int hashCode() {
868: return _hashCode;
869: }
870:
871: /**
872: * Compares this object with the specified object for equality.
873: *
874: * @param obj
875: * the object to compare with, or <code>null</code>.
876: *
877: * @return
878: * <code>true</code> if this object and the argument are considered
879: * equal, <code>false</code> otherwise.
880: */
881: public boolean equals(Object obj) {
882:
883: if (!(obj instanceof QualifiedName)) {
884: return false;
885: }
886:
887: QualifiedName qn = (QualifiedName) obj;
888: return ((_namespaceURI == null && qn._namespaceURI == null) || (_namespaceURI != null && _namespaceURI
889: .equals(qn._namespaceURI)))
890: && _localName.equals(qn._localName);
891: }
892:
893: /**
894: * Gets the namespace prefix.
895: *
896: * @return
897: * the namespace prefix, can be <code>null</code>.
898: *
899: * @since XINS 2.1.
900: */
901: public String getNamespacePrefix() {
902: return _namespacePrefix;
903: }
904:
905: /**
906: * Gets the namespace URI.
907: *
908: * @return
909: * the namespace URI, can be <code>null</code>.
910: */
911: public String getNamespaceURI() {
912: return _namespaceURI;
913: }
914:
915: /**
916: * Gets the local name.
917: *
918: * @return
919: * the local name, never <code>null</code>.
920: */
921: public String getLocalName() {
922: return _localName;
923: }
924: }
925: }
|