001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: QName.java,v 1.18 2005/03/23 17:54:05 ytalwar Exp $
018: */
019: package org.apache.xml.utils;
020:
021: import java.util.Stack;
022: import java.util.StringTokenizer;
023:
024: import org.apache.xml.res.XMLErrorResources;
025: import org.apache.xml.res.XMLMessages;
026:
027: import org.w3c.dom.Element;
028:
029: /**
030: * Class to represent a qualified name: "The name of an internal XSLT object,
031: * specifically a named template (see [7 Named Templates]), a mode (see [6.7 Modes]),
032: * an attribute set (see [8.1.4 Named Attribute Sets]), a key (see [14.2 Keys]),
033: * a locale (see [14.3 Number Formatting]), a variable or a parameter (see
034: * [12 Variables and Parameters]) is specified as a QName. If it has a prefix,
035: * then the prefix is expanded into a URI reference using the namespace declarations
036: * in effect on the attribute in which the name occurs. The expanded name
037: * consisting of the local part of the name and the possibly null URI reference
038: * is used as the name of the object. The default namespace is not used for
039: * unprefixed names."
040: * @xsl.usage general
041: */
042: public class QName implements java.io.Serializable {
043: static final long serialVersionUID = 467434581652829920L;
044:
045: /**
046: * The local name.
047: * @serial
048: */
049: protected String _localName;
050:
051: /**
052: * The namespace URI.
053: * @serial
054: */
055: protected String _namespaceURI;
056:
057: /**
058: * The namespace prefix.
059: * @serial
060: */
061: protected String _prefix;
062:
063: /**
064: * The XML namespace.
065: */
066: public static final String S_XMLNAMESPACEURI = "http://www.w3.org/XML/1998/namespace";
067:
068: /**
069: * The cached hashcode, which is calculated at construction time.
070: * @serial
071: */
072: private int m_hashCode;
073:
074: /**
075: * Constructs an empty QName.
076: * 20001019: Try making this public, to support Serializable? -- JKESS
077: */
078: public QName() {
079: }
080:
081: /**
082: * Constructs a new QName with the specified namespace URI and
083: * local name.
084: *
085: * @param namespaceURI The namespace URI if known, or null
086: * @param localName The local name
087: */
088: public QName(String namespaceURI, String localName) {
089: this (namespaceURI, localName, false);
090: }
091:
092: /**
093: * Constructs a new QName with the specified namespace URI and
094: * local name.
095: *
096: * @param namespaceURI The namespace URI if known, or null
097: * @param localName The local name
098: * @param validate If true the new QName will be validated and an IllegalArgumentException will
099: * be thrown if it is invalid.
100: */
101: public QName(String namespaceURI, String localName, boolean validate) {
102:
103: // This check was already here. So, for now, I will not add it to the validation
104: // that is done when the validate parameter is true.
105: if (localName == null)
106: throw new IllegalArgumentException(XMLMessages
107: .createXMLMessage(
108: XMLErrorResources.ER_ARG_LOCALNAME_NULL,
109: null)); //"Argument 'localName' is null");
110:
111: if (validate) {
112: if (!XML11Char.isXML11ValidNCName(localName)) {
113: throw new IllegalArgumentException(
114: XMLMessages
115: .createXMLMessage(
116: XMLErrorResources.ER_ARG_LOCALNAME_INVALID,
117: null)); //"Argument 'localName' not a valid NCName");
118: }
119: }
120:
121: _namespaceURI = namespaceURI;
122: _localName = localName;
123: m_hashCode = toString().hashCode();
124: }
125:
126: /**
127: * Constructs a new QName with the specified namespace URI, prefix
128: * and local name.
129: *
130: * @param namespaceURI The namespace URI if known, or null
131: * @param prefix The namespace prefix is known, or null
132: * @param localName The local name
133: *
134: */
135: public QName(String namespaceURI, String prefix, String localName) {
136: this (namespaceURI, prefix, localName, false);
137: }
138:
139: /**
140: * Constructs a new QName with the specified namespace URI, prefix
141: * and local name.
142: *
143: * @param namespaceURI The namespace URI if known, or null
144: * @param prefix The namespace prefix is known, or null
145: * @param localName The local name
146: * @param validate If true the new QName will be validated and an IllegalArgumentException will
147: * be thrown if it is invalid.
148: */
149: public QName(String namespaceURI, String prefix, String localName,
150: boolean validate) {
151:
152: // This check was already here. So, for now, I will not add it to the validation
153: // that is done when the validate parameter is true.
154: if (localName == null)
155: throw new IllegalArgumentException(XMLMessages
156: .createXMLMessage(
157: XMLErrorResources.ER_ARG_LOCALNAME_NULL,
158: null)); //"Argument 'localName' is null");
159:
160: if (validate) {
161: if (!XML11Char.isXML11ValidNCName(localName)) {
162: throw new IllegalArgumentException(
163: XMLMessages
164: .createXMLMessage(
165: XMLErrorResources.ER_ARG_LOCALNAME_INVALID,
166: null)); //"Argument 'localName' not a valid NCName");
167: }
168:
169: if ((null != prefix)
170: && (!XML11Char.isXML11ValidNCName(prefix))) {
171: throw new IllegalArgumentException(
172: XMLMessages
173: .createXMLMessage(
174: XMLErrorResources.ER_ARG_PREFIX_INVALID,
175: null)); //"Argument 'prefix' not a valid NCName");
176: }
177:
178: }
179: _namespaceURI = namespaceURI;
180: _prefix = prefix;
181: _localName = localName;
182: m_hashCode = toString().hashCode();
183: }
184:
185: /**
186: * Construct a QName from a string, without namespace resolution. Good
187: * for a few odd cases.
188: *
189: * @param localName Local part of qualified name
190: *
191: */
192: public QName(String localName) {
193: this (localName, false);
194: }
195:
196: /**
197: * Construct a QName from a string, without namespace resolution. Good
198: * for a few odd cases.
199: *
200: * @param localName Local part of qualified name
201: * @param validate If true the new QName will be validated and an IllegalArgumentException will
202: * be thrown if it is invalid.
203: */
204: public QName(String localName, boolean validate) {
205:
206: // This check was already here. So, for now, I will not add it to the validation
207: // that is done when the validate parameter is true.
208: if (localName == null)
209: throw new IllegalArgumentException(XMLMessages
210: .createXMLMessage(
211: XMLErrorResources.ER_ARG_LOCALNAME_NULL,
212: null)); //"Argument 'localName' is null");
213:
214: if (validate) {
215: if (!XML11Char.isXML11ValidNCName(localName)) {
216: throw new IllegalArgumentException(
217: XMLMessages
218: .createXMLMessage(
219: XMLErrorResources.ER_ARG_LOCALNAME_INVALID,
220: null)); //"Argument 'localName' not a valid NCName");
221: }
222: }
223: _namespaceURI = null;
224: _localName = localName;
225: m_hashCode = toString().hashCode();
226: }
227:
228: /**
229: * Construct a QName from a string, resolving the prefix
230: * using the given namespace stack. The default namespace is
231: * not resolved.
232: *
233: * @param qname Qualified name to resolve
234: * @param namespaces Namespace stack to use to resolve namespace
235: */
236: public QName(String qname, Stack namespaces) {
237: this (qname, namespaces, false);
238: }
239:
240: /**
241: * Construct a QName from a string, resolving the prefix
242: * using the given namespace stack. The default namespace is
243: * not resolved.
244: *
245: * @param qname Qualified name to resolve
246: * @param namespaces Namespace stack to use to resolve namespace
247: * @param validate If true the new QName will be validated and an IllegalArgumentException will
248: * be thrown if it is invalid.
249: */
250: public QName(String qname, Stack namespaces, boolean validate) {
251:
252: String namespace = null;
253: String prefix = null;
254: int indexOfNSSep = qname.indexOf(':');
255:
256: if (indexOfNSSep > 0) {
257: prefix = qname.substring(0, indexOfNSSep);
258:
259: if (prefix.equals("xml")) {
260: namespace = S_XMLNAMESPACEURI;
261: }
262: // Do we want this?
263: else if (prefix.equals("xmlns")) {
264: return;
265: } else {
266: int depth = namespaces.size();
267:
268: for (int i = depth - 1; i >= 0; i--) {
269: NameSpace ns = (NameSpace) namespaces.elementAt(i);
270:
271: while (null != ns) {
272: if ((null != ns.m_prefix)
273: && prefix.equals(ns.m_prefix)) {
274: namespace = ns.m_uri;
275: i = -1;
276:
277: break;
278: }
279:
280: ns = ns.m_next;
281: }
282: }
283: }
284:
285: if (null == namespace) {
286: throw new RuntimeException(
287: XMLMessages
288: .createXMLMessage(
289: XMLErrorResources.ER_PREFIX_MUST_RESOLVE,
290: new Object[] { prefix })); //"Prefix must resolve to a namespace: "+prefix);
291: }
292: }
293:
294: _localName = (indexOfNSSep < 0) ? qname : qname
295: .substring(indexOfNSSep + 1);
296:
297: if (validate) {
298: if ((_localName == null)
299: || (!XML11Char.isXML11ValidNCName(_localName))) {
300: throw new IllegalArgumentException(
301: XMLMessages
302: .createXMLMessage(
303: XMLErrorResources.ER_ARG_LOCALNAME_INVALID,
304: null)); //"Argument 'localName' not a valid NCName");
305: }
306: }
307: _namespaceURI = namespace;
308: _prefix = prefix;
309: m_hashCode = toString().hashCode();
310: }
311:
312: /**
313: * Construct a QName from a string, resolving the prefix
314: * using the given namespace context and prefix resolver.
315: * The default namespace is not resolved.
316: *
317: * @param qname Qualified name to resolve
318: * @param namespaceContext Namespace Context to use
319: * @param resolver Prefix resolver for this context
320: */
321: public QName(String qname, Element namespaceContext,
322: PrefixResolver resolver) {
323: this (qname, namespaceContext, resolver, false);
324: }
325:
326: /**
327: * Construct a QName from a string, resolving the prefix
328: * using the given namespace context and prefix resolver.
329: * The default namespace is not resolved.
330: *
331: * @param qname Qualified name to resolve
332: * @param namespaceContext Namespace Context to use
333: * @param resolver Prefix resolver for this context
334: * @param validate If true the new QName will be validated and an IllegalArgumentException will
335: * be thrown if it is invalid.
336: */
337: public QName(String qname, Element namespaceContext,
338: PrefixResolver resolver, boolean validate) {
339:
340: _namespaceURI = null;
341:
342: int indexOfNSSep = qname.indexOf(':');
343:
344: if (indexOfNSSep > 0) {
345: if (null != namespaceContext) {
346: String prefix = qname.substring(0, indexOfNSSep);
347:
348: _prefix = prefix;
349:
350: if (prefix.equals("xml")) {
351: _namespaceURI = S_XMLNAMESPACEURI;
352: }
353:
354: // Do we want this?
355: else if (prefix.equals("xmlns")) {
356: return;
357: } else {
358: _namespaceURI = resolver.getNamespaceForPrefix(
359: prefix, namespaceContext);
360: }
361:
362: if (null == _namespaceURI) {
363: throw new RuntimeException(
364: XMLMessages
365: .createXMLMessage(
366: XMLErrorResources.ER_PREFIX_MUST_RESOLVE,
367: new Object[] { prefix })); //"Prefix must resolve to a namespace: "+prefix);
368: }
369: } else {
370:
371: // TODO: error or warning...
372: }
373: }
374:
375: _localName = (indexOfNSSep < 0) ? qname : qname
376: .substring(indexOfNSSep + 1);
377:
378: if (validate) {
379: if ((_localName == null)
380: || (!XML11Char.isXML11ValidNCName(_localName))) {
381: throw new IllegalArgumentException(
382: XMLMessages
383: .createXMLMessage(
384: XMLErrorResources.ER_ARG_LOCALNAME_INVALID,
385: null)); //"Argument 'localName' not a valid NCName");
386: }
387: }
388:
389: m_hashCode = toString().hashCode();
390: }
391:
392: /**
393: * Construct a QName from a string, resolving the prefix
394: * using the given namespace stack. The default namespace is
395: * not resolved.
396: *
397: * @param qname Qualified name to resolve
398: * @param resolver Prefix resolver for this context
399: */
400: public QName(String qname, PrefixResolver resolver) {
401: this (qname, resolver, false);
402: }
403:
404: /**
405: * Construct a QName from a string, resolving the prefix
406: * using the given namespace stack. The default namespace is
407: * not resolved.
408: *
409: * @param qname Qualified name to resolve
410: * @param resolver Prefix resolver for this context
411: * @param validate If true the new QName will be validated and an IllegalArgumentException will
412: * be thrown if it is invalid.
413: */
414: public QName(String qname, PrefixResolver resolver, boolean validate) {
415:
416: String prefix = null;
417: _namespaceURI = null;
418:
419: int indexOfNSSep = qname.indexOf(':');
420:
421: if (indexOfNSSep > 0) {
422: prefix = qname.substring(0, indexOfNSSep);
423:
424: if (prefix.equals("xml")) {
425: _namespaceURI = S_XMLNAMESPACEURI;
426: } else {
427: _namespaceURI = resolver.getNamespaceForPrefix(prefix);
428: }
429:
430: if (null == _namespaceURI) {
431: throw new RuntimeException(
432: XMLMessages
433: .createXMLMessage(
434: XMLErrorResources.ER_PREFIX_MUST_RESOLVE,
435: new Object[] { prefix })); //"Prefix must resolve to a namespace: "+prefix);
436: }
437: _localName = qname.substring(indexOfNSSep + 1);
438: } else if (indexOfNSSep == 0) {
439: throw new RuntimeException(XMLMessages.createXMLMessage(
440: XMLErrorResources.ER_NAME_CANT_START_WITH_COLON,
441: null));
442: } else {
443: _localName = qname;
444: }
445:
446: if (validate) {
447: if ((_localName == null)
448: || (!XML11Char.isXML11ValidNCName(_localName))) {
449: throw new IllegalArgumentException(
450: XMLMessages
451: .createXMLMessage(
452: XMLErrorResources.ER_ARG_LOCALNAME_INVALID,
453: null)); //"Argument 'localName' not a valid NCName");
454: }
455: }
456:
457: m_hashCode = toString().hashCode();
458: _prefix = prefix;
459: }
460:
461: /**
462: * Returns the namespace URI. Returns null if the namespace URI
463: * is not known.
464: *
465: * @return The namespace URI, or null
466: */
467: public String getNamespaceURI() {
468: return _namespaceURI;
469: }
470:
471: /**
472: * Returns the namespace prefix. Returns null if the namespace
473: * prefix is not known.
474: *
475: * @return The namespace prefix, or null
476: */
477: public String getPrefix() {
478: return _prefix;
479: }
480:
481: /**
482: * Returns the local part of the qualified name.
483: *
484: * @return The local part of the qualified name
485: */
486: public String getLocalName() {
487: return _localName;
488: }
489:
490: /**
491: * Return the string representation of the qualified name, using the
492: * prefix if available, or the '{ns}foo' notation if not. Performs
493: * string concatenation, so beware of performance issues.
494: *
495: * @return the string representation of the namespace
496: */
497: public String toString() {
498:
499: return _prefix != null ? (_prefix + ":" + _localName)
500: : (_namespaceURI != null ? ("{" + _namespaceURI + "}" + _localName)
501: : _localName);
502: }
503:
504: /**
505: * Return the string representation of the qualified name using the
506: * the '{ns}foo' notation. Performs
507: * string concatenation, so beware of performance issues.
508: *
509: * @return the string representation of the namespace
510: */
511: public String toNamespacedString() {
512:
513: return (_namespaceURI != null ? ("{" + _namespaceURI + "}" + _localName)
514: : _localName);
515: }
516:
517: /**
518: * Get the namespace of the qualified name.
519: *
520: * @return the namespace URI of the qualified name
521: */
522: public String getNamespace() {
523: return getNamespaceURI();
524: }
525:
526: /**
527: * Get the local part of the qualified name.
528: *
529: * @return the local part of the qualified name
530: */
531: public String getLocalPart() {
532: return getLocalName();
533: }
534:
535: /**
536: * Return the cached hashcode of the qualified name.
537: *
538: * @return the cached hashcode of the qualified name
539: */
540: public int hashCode() {
541: return m_hashCode;
542: }
543:
544: /**
545: * Override equals and agree that we're equal if
546: * the passed object is a string and it matches
547: * the name of the arg.
548: *
549: * @param ns Namespace URI to compare to
550: * @param localPart Local part of qualified name to compare to
551: *
552: * @return True if the local name and uri match
553: */
554: public boolean equals(String ns, String localPart) {
555:
556: String this namespace = getNamespaceURI();
557:
558: return getLocalName().equals(localPart)
559: && (((null != this namespace) && (null != ns)) ? this namespace
560: .equals(ns)
561: : ((null == this namespace) && (null == ns)));
562: }
563:
564: /**
565: * Override equals and agree that we're equal if
566: * the passed object is a QName and it matches
567: * the name of the arg.
568: *
569: * @return True if the qualified names are equal
570: */
571: public boolean equals(Object object) {
572:
573: if (object == this )
574: return true;
575:
576: if (object instanceof QName) {
577: QName qname = (QName) object;
578: String this namespace = getNamespaceURI();
579: String thatnamespace = qname.getNamespaceURI();
580:
581: return getLocalName().equals(qname.getLocalName())
582: && (((null != this namespace) && (null != thatnamespace)) ? this namespace
583: .equals(thatnamespace)
584: : ((null == this namespace) && (null == thatnamespace)));
585: } else
586: return false;
587: }
588:
589: /**
590: * Given a string, create and return a QName object
591: *
592: *
593: * @param name String to use to create QName
594: *
595: * @return a QName object
596: */
597: public static QName getQNameFromString(String name) {
598:
599: StringTokenizer tokenizer = new StringTokenizer(name, "{}",
600: false);
601: QName qname;
602: String s1 = tokenizer.nextToken();
603: String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken()
604: : null;
605:
606: if (null == s2)
607: qname = new QName(null, s1);
608: else
609: qname = new QName(s1, s2);
610:
611: return qname;
612: }
613:
614: /**
615: * This function tells if a raw attribute name is a
616: * xmlns attribute.
617: *
618: * @param attRawName Raw name of attribute
619: *
620: * @return True if the attribute starts with or is equal to xmlns
621: */
622: public static boolean isXMLNSDecl(String attRawName) {
623:
624: return (attRawName.startsWith("xmlns") && (attRawName
625: .equals("xmlns") || attRawName.startsWith("xmlns:")));
626: }
627:
628: /**
629: * This function tells if a raw attribute name is a
630: * xmlns attribute.
631: *
632: * @param attRawName Raw name of attribute
633: *
634: * @return Prefix of attribute
635: */
636: public static String getPrefixFromXMLNSDecl(String attRawName) {
637:
638: int index = attRawName.indexOf(':');
639:
640: return (index >= 0) ? attRawName.substring(index + 1) : "";
641: }
642:
643: /**
644: * Returns the local name of the given node.
645: *
646: * @param qname Input name
647: *
648: * @return Local part of the name if prefixed, or the given name if not
649: */
650: public static String getLocalPart(String qname) {
651:
652: int index = qname.indexOf(':');
653:
654: return (index < 0) ? qname : qname.substring(index + 1);
655: }
656:
657: /**
658: * Returns the local name of the given node.
659: *
660: * @param qname Input name
661: *
662: * @return Prefix of name or empty string if none there
663: */
664: public static String getPrefixPart(String qname) {
665:
666: int index = qname.indexOf(':');
667:
668: return (index >= 0) ? qname.substring(0, index) : "";
669: }
670: }
|