001: package net.sf.saxon.om;
002:
003: import net.sf.saxon.style.StandardNames;
004: import net.sf.saxon.trans.DynamicError;
005: import net.sf.saxon.type.TypeHierarchy;
006:
007: import java.io.Serializable;
008: import java.util.HashMap;
009: import java.util.StringTokenizer;
010:
011: /**
012: * An object representing a collection of XML names, each containing a Namespace URI,
013: * a Namespace prefix, and a local name; plus a collection of namespaces, each
014: * consisting of a prefix/URI pair. <br>
015: * <p/>
016: * <p>The equivalence betweem names depends only on the URI and the local name.
017: * The prefix is retained for documentary purposes only: it is useful when
018: * reconstructing a document to use prefixes that the user is familiar with.</p>
019: * <p/>
020: * <p>The NamePool eliminates duplicate names if they have the same prefix, uri,
021: * and local part. It retains duplicates if they have different prefixes</p>
022: *
023: * @author Michael H. Kay
024: */
025:
026: public class NamePool implements Serializable {
027:
028: // The NamePool holds two kinds of entry: name entries, representing
029: // expanded names (local name + prefix + URI), identified by a name code,
030: // and namespace entries (prefix + URI) identified by a namespace code.
031: //
032: // The data structure of the name table is as follows.
033: //
034: // There is a fixed size hash table; names are allocated to slots in this
035: // table by hashing on the local name. Each entry in the table is the head of
036: // a chain of NameEntry objects representing names that have the same hash code.
037: //
038: // Each NameEntry represents a distinct name (same URI and local name). It contains
039: // The local name as a string, plus a short integer representing the URI (as an
040: // offset into the array uris[]).
041: //
042: // The fingerprint of a name consists of the hash slot number (in the bottom 10 bits)
043: // concatenated with the depth of the entry down the chain of hash synonyms (in the
044: // next 10 bits). Fingerprints with depth 0 (i.e., in the range 0-1023) are reserved
045: // for predefined names (names of XSLT elements and attributes, and of built-in types).
046: // These names are not stored in the name pool, but are accessible as if they were.
047: //
048: // A nameCode contains the fingerprint in the bottom 20 bits. It also contains
049: // an 8-bit prefix index. This distinguishes the prefix used, among all the
050: // prefixes that have been used with this namespace URI. If the prefix index is
051: // zero, the prefix is null. Otherwise, it indexes an space-separated list of
052: // prefix Strings associated with the namespace URI.
053:
054: /**
055: * FP_MASK is a mask used to obtain a fingerprint from a nameCode. Given a
056: * nameCode nc, the fingerprint is <code>nc & NamePool.FP_MASK</code>.
057: * (In practice, Saxon code often uses the literal constant 0xfffff.)
058: * The difference between a nameCode is that a nameCode contains information
059: * about the prefix of a name, the fingerprint depends only on the namespace
060: * URI and local name. Note that the "null" nameCode (-1) does not produce
061: * the "null" fingerprint (also -1) when this mask is applied.
062: */
063:
064: public static final int FP_MASK = 0xfffff;
065:
066: // The default singular instance, used unless the user deliberately wants to
067: // manage name pools himself
068:
069: private static NamePool defaultNamePool = new NamePool();
070:
071: /**
072: * Get the singular default NamePool
073: */
074:
075: public static NamePool getDefaultNamePool() {
076: return defaultNamePool;
077: }
078:
079: /**
080: * Set the default NamePool
081: * (used after loading a compiled stylesheet)
082: */
083:
084: public static void setDefaultNamePool(NamePool pool) {
085: defaultNamePool = pool;
086: }
087:
088: private static class NameEntry implements Serializable {
089: String localName;
090: short uriCode;
091: NameEntry nextEntry; // next NameEntry with the same hashcode
092:
093: public NameEntry(short uriCode, String localName) {
094: this .uriCode = uriCode;
095: this .localName = localName.intern();
096: this .nextEntry = null;
097: }
098:
099: }
100:
101: NameEntry[] hashslots = new NameEntry[1024];
102:
103: String[] prefixes = new String[100];
104: short prefixesUsed = 0;
105: String[] uris = new String[100];
106: String[] prefixesForUri = new String[100];
107: short urisUsed = 0;
108:
109: // General purpose cache for data held by clients of the namePool
110:
111: private HashMap clientData;
112:
113: private transient TypeHierarchy typeHierarchy;
114:
115: public NamePool() {
116:
117: prefixes[NamespaceConstant.NULL_CODE] = "";
118: uris[NamespaceConstant.NULL_CODE] = NamespaceConstant.NULL;
119: prefixesForUri[NamespaceConstant.NULL_CODE] = "";
120:
121: prefixes[NamespaceConstant.XML_CODE] = "xml";
122: uris[NamespaceConstant.XML_CODE] = NamespaceConstant.XML;
123: prefixesForUri[NamespaceConstant.XML_CODE] = "xml ";
124:
125: prefixes[NamespaceConstant.XSLT_CODE] = "xsl";
126: uris[NamespaceConstant.XSLT_CODE] = NamespaceConstant.XSLT;
127: prefixesForUri[NamespaceConstant.XSLT_CODE] = "xsl ";
128:
129: prefixes[NamespaceConstant.SAXON_CODE] = "saxon";
130: uris[NamespaceConstant.SAXON_CODE] = NamespaceConstant.SAXON;
131: prefixesForUri[NamespaceConstant.SAXON_CODE] = "saxon ";
132:
133: prefixes[NamespaceConstant.SCHEMA_CODE] = "xs";
134: uris[NamespaceConstant.SCHEMA_CODE] = NamespaceConstant.SCHEMA;
135: prefixesForUri[NamespaceConstant.SCHEMA_CODE] = "xs ";
136:
137: prefixes[NamespaceConstant.XDT_CODE] = "xdt";
138: uris[NamespaceConstant.XDT_CODE] = NamespaceConstant.XDT;
139: prefixesForUri[NamespaceConstant.XDT_CODE] = "xdt ";
140:
141: prefixes[NamespaceConstant.XSI_CODE] = "xsi";
142: uris[NamespaceConstant.XSI_CODE] = NamespaceConstant.SCHEMA_INSTANCE;
143: prefixesForUri[NamespaceConstant.XSI_CODE] = "xsi ";
144:
145: prefixesUsed = 7;
146: urisUsed = 7;
147:
148: }
149:
150: /**
151: * Get the TypeHierarchy
152: */
153:
154: public final TypeHierarchy getTypeHierarchy() {
155: if (typeHierarchy == null) {
156: typeHierarchy = new TypeHierarchy();
157: }
158: return typeHierarchy;
159: }
160:
161: /**
162: * Get a name entry corresponding to a given name code
163: *
164: * @return null if there is none.
165: */
166:
167: private NameEntry getNameEntry(int nameCode) {
168: int hash = nameCode & 0x3ff;
169: int depth = (nameCode >> 10) & 0x3ff;
170: NameEntry entry = hashslots[hash];
171:
172: for (int i = 1; i < depth; i++) {
173: if (entry == null) {
174: return null;
175: }
176: entry = entry.nextEntry;
177: }
178: return entry;
179: }
180:
181: /**
182: * Allocate the namespace code for a namespace prefix/URI pair.
183: * Create it if not already present
184: *
185: * @param prefix the namespace prefix
186: * @param uri the namespace URI
187: * @return an integer code identifying the namespace. The namespace code
188: * identifies both the prefix and the URI.
189: */
190:
191: public/**/synchronized/**/int allocateNamespaceCode(
192: String prefix, String uri) {
193: // System.err.println("allocate nscode for " + prefix + " = " + uri);
194:
195: int prefixCode = allocateCodeForPrefix(prefix);
196: int uriCode = allocateCodeForURI(uri);
197:
198: if (prefixCode != 0) {
199: // ensure the prefix is in the list of prefixes used with this URI
200: String key = prefix + ' ';
201: if (prefixesForUri[uriCode].indexOf(key) < 0) {
202: prefixesForUri[uriCode] += key;
203: }
204: }
205:
206: return (prefixCode << 16) + uriCode;
207: }
208:
209: /**
210: * Get the existing namespace code for a namespace prefix/URI pair.
211: *
212: * @return -1 if there is none present
213: */
214:
215: public int getNamespaceCode(String prefix, String uri) {
216: //System.err.println("get nscode for " + prefix + " = " + uri);
217: int prefixCode = getCodeForPrefix(prefix);
218: if (prefixCode < 0) {
219: return -1;
220: }
221: int uriCode = getCodeForURI(uri);
222: if (uriCode < 0) {
223: return -1;
224: }
225:
226: if (prefixCode != 0) {
227: // ensure the prefix is in the list of prefixes used with this URI
228: String key = prefix + ' ';
229: if (prefixesForUri[uriCode].indexOf(key) < 0) {
230: return -1;
231: }
232: }
233:
234: return (prefixCode << 16) + uriCode;
235: }
236:
237: /**
238: * Allocate a namespace code for a given namecode
239: *
240: * @param namecode a code identifying an expanded QName, e.g. of an element or attribute
241: * @return a code identifying the namespace used in the given name. The namespace code
242: * identifies both the prefix and the URI. Return -1 if no namespace code has been allocated
243: * (in this case the caller should call allocateNamespaceCode() to get one).
244: */
245:
246: public int getNamespaceCode(int namecode) {
247: short uriCode;
248: int fp = namecode & 0xfffff;
249: if ((fp & 0xffc00) == 0) {
250: uriCode = StandardNames.getURICode(fp);
251: } else {
252: NameEntry entry = getNameEntry(namecode);
253: if (entry == null) {
254: return -1;
255: } else {
256: uriCode = entry.uriCode;
257: }
258: }
259: int prefixIndex = (namecode >> 20) & 0xff;
260: String prefix = getPrefixWithIndex(uriCode, prefixIndex);
261: if (prefix == null) {
262: return -1;
263: }
264: int prefixCode = getCodeForPrefix(prefix);
265: if (prefixCode == -1) {
266: return -1;
267: }
268: return (prefixCode << 16) + uriCode;
269: }
270:
271: /**
272: * Allocate the uri code for a given URI;
273: * create one if not found
274: */
275:
276: public/**/synchronized/**/short allocateCodeForURI(String uri) {
277: //System.err.println("allocate code for URI " + uri);
278: for (short j = 0; j < urisUsed; j++) {
279: if (uris[j].equals(uri)) {
280: return j;
281: }
282: }
283: if (urisUsed >= uris.length) {
284: if (urisUsed > 32000) {
285: throw new NamePoolLimitException(
286: "Too many namespace URIs");
287: }
288: String[] p = new String[urisUsed * 2];
289: String[] u = new String[urisUsed * 2];
290: System.arraycopy(prefixesForUri, 0, p, 0, urisUsed);
291: System.arraycopy(uris, 0, u, 0, urisUsed);
292: prefixesForUri = p;
293: uris = u;
294: }
295: uris[urisUsed] = uri;
296: prefixesForUri[urisUsed] = "";
297: return urisUsed++;
298: }
299:
300: /**
301: * Get the uri code for a given URI
302: *
303: * @return -1 if not present in the name pool
304: */
305:
306: public short getCodeForURI(String uri) {
307: for (short j = 0; j < urisUsed; j++) {
308: if (uris[j].equals(uri)) {
309: return j;
310: }
311: }
312: return -1;
313: }
314:
315: /**
316: * Allocate the prefix code for a given Prefix; create one if not found
317: *
318: * @param prefix the namespace prefix whose code is to be allocated or returned
319: * @return the numeric code for this prefix
320: */
321:
322: private short allocateCodeForPrefix(String prefix) {
323: // Not synchronized, because it's always called from a synchronized method
324:
325: // exploit knowledge of the standard prefixes to shorten the search
326: short start = 1;
327: if (prefix.equals("")) {
328: return NamespaceConstant.NULL_CODE;
329: }
330: if (prefix.charAt(0) != 'x') {
331: if (prefix.equals("saxon")) {
332: return NamespaceConstant.SAXON_CODE;
333: }
334: start = NamespaceConstant.XSI_CODE + 1;
335: }
336:
337: for (short i = start; i < prefixesUsed; i++) {
338: if (prefixes[i].equals(prefix)) {
339: return i;
340: }
341: }
342: if (prefixesUsed >= prefixes.length) {
343: if (prefixesUsed > 32000) {
344: throw new NamePoolLimitException(
345: "Too many namespace prefixes");
346: }
347: String[] p = new String[prefixesUsed * 2];
348: System.arraycopy(prefixes, 0, p, 0, prefixesUsed);
349: prefixes = p;
350: }
351: prefixes[prefixesUsed] = prefix;
352: return prefixesUsed++;
353: }
354:
355: /**
356: * Get the prefix code for a given Prefix
357: *
358: * @return -1 if not found
359: */
360:
361: public short getCodeForPrefix(String prefix) {
362: for (short i = 0; i < prefixesUsed; i++) {
363: if (prefixes[i].equals(prefix)) {
364: return i;
365: }
366: }
367: return -1;
368: }
369:
370: /**
371: * Suggest a prefix for a given URI. If there are several, it's undefined which one is returned.
372: * If there are no prefixes registered for this URI, return null.
373: */
374:
375: public String suggestPrefixForURI(String URI) {
376: short uriCode = getCodeForURI(URI);
377: if (uriCode == -1) {
378: return null;
379: }
380: StringTokenizer tok = new StringTokenizer(
381: prefixesForUri[uriCode]);
382: if (tok.hasMoreElements()) {
383: return (String) tok.nextElement();
384: }
385: return null;
386: }
387:
388: /**
389: * Get the index of a prefix among all the prefixes used with a given URI
390: *
391: * @return -1 if not found
392: */
393:
394: private int getPrefixIndex(short uriCode, String prefix) {
395:
396: // look for quick wins
397: if (prefix.equals("")) {
398: return 0;
399: }
400: if (prefixesForUri[uriCode].equals(prefix + ' ')) {
401: return 1;
402: }
403:
404: // search for the prefix in the list
405: int i = 1;
406: StringTokenizer tok = new StringTokenizer(
407: prefixesForUri[uriCode]);
408: while (tok.hasMoreElements()) {
409: if (prefix.equals(tok.nextElement())) {
410: return i;
411: }
412: if (i++ == 255) {
413: throw new NamePoolLimitException(
414: "Too many prefixes for one namespace URI");
415: }
416: }
417: return -1;
418: }
419:
420: /**
421: * Get a prefix among all the prefixes used with a given URI, given its index
422: *
423: * @return null if not found
424: */
425:
426: public String getPrefixWithIndex(short uriCode, int index) {
427: if (index == 0) {
428: return "";
429: }
430: StringTokenizer tok = new StringTokenizer(
431: prefixesForUri[uriCode]);
432: int i = 1;
433: while (tok.hasMoreElements()) {
434: String prefix = (String) tok.nextElement();
435: if (i++ == index) {
436: return prefix;
437: }
438: }
439: return null;
440: }
441:
442: /**
443: * Allocate a name from the pool, or a new Name if there is not a matching one there
444: *
445: * @param prefix
446: * @param uri - the namespace URI. The null URI is represented as an empty string.
447: * @param localName
448: * @return an integer (the "namecode") identifying the name within the namepool.
449: * The Name itself may be retrieved using the getName(int) method
450: */
451:
452: public synchronized int allocate(String prefix, String uri,
453: String localName) {
454: if (NamespaceConstant.isReserved(uri)
455: || uri.equals(NamespaceConstant.SAXON)) {
456: int fp = StandardNames.getFingerprint(uri, localName);
457: if (fp != -1) {
458: short uriCode = StandardNames.getURICode(fp);
459: int prefixIndex = getPrefixIndex(uriCode, prefix);
460:
461: if (prefixIndex < 0) {
462: prefixesForUri[uriCode] += (prefix + ' ');
463: prefixIndex = getPrefixIndex(uriCode, prefix);
464: }
465:
466: return (prefixIndex << 20) + fp;
467: }
468: }
469: // otherwise register the name in this NamePool
470: short uriCode = allocateCodeForURI(uri);
471: return allocateInternal(prefix, uriCode, localName);
472: }
473:
474: /**
475: * Allocate a name from the pool, or a new Name if there is not a matching one there
476: *
477: * @param prefix - the namespace prefix
478: * @param uriCode - the code of the URI
479: * @param localName - the local part of the QName
480: * @return an integer (the "namecode") identifying the name within the namepool.
481: */
482:
483: public synchronized int allocate(String prefix, short uriCode,
484: String localName) {
485: // System.err.println("Allocate " + prefix + " : " + uriCode + " : " + localName);
486: if (NamespaceConstant.isSpecialURICode(uriCode)) {
487: return allocate(prefix, getURIFromURICode(uriCode),
488: localName);
489: } else {
490: return allocateInternal(prefix, uriCode, localName);
491: }
492: }
493:
494: private int allocateInternal(String prefix, short uriCode,
495: String localName) {
496: int hash = (localName.hashCode() & 0x7fffffff) % 1023;
497: int depth = 1;
498: int prefixIndex = getPrefixIndex(uriCode, prefix);
499:
500: if (prefixIndex < 0) {
501: prefixesForUri[uriCode] += (prefix + ' ');
502: prefixIndex = getPrefixIndex(uriCode, prefix);
503: }
504: NameEntry entry;
505:
506: if (hashslots[hash] == null) {
507: entry = new NameEntry(uriCode, localName);
508: hashslots[hash] = entry;
509: } else {
510: entry = hashslots[hash];
511: while (true) {
512: boolean sameLocalName = (entry.localName
513: .equals(localName));
514: boolean sameURI = (entry.uriCode == uriCode);
515:
516: if (sameLocalName && sameURI) {
517: // may need to add a new prefix to the entry
518: break;
519: } else {
520: NameEntry next = entry.nextEntry;
521: depth++;
522: if (depth >= 1024) {
523: throw new NamePoolLimitException(
524: "Saxon name pool is full");
525: }
526: if (next == null) {
527: NameEntry newentry = new NameEntry(uriCode,
528: localName);
529: entry.nextEntry = newentry;
530: break;
531: } else {
532: entry = next;
533: }
534: }
535: }
536: }
537: // System.err.println("name code = " + prefixIndex + "/" + depth + "/" + hash);
538: return ((prefixIndex << 20) + (depth << 10) + hash);
539: }
540:
541: /**
542: * Allocate a namespace code for the prefix/URI of a given namecode
543: *
544: * @param namecode a code identifying an expanded QName, e.g. of an element or attribute
545: * @return a code identifying the namespace used in the given name. The namespace code
546: * identifies both the prefix and the URI.
547: */
548:
549: public/**/synchronized/**/int allocateNamespaceCode(int namecode) {
550: short uriCode;
551: int fp = namecode & 0xfffff;
552: if ((fp & 0xffc00) == 0) {
553: uriCode = StandardNames.getURICode(fp);
554: } else {
555: NameEntry entry = getNameEntry(namecode);
556: if (entry == null) {
557: unknownNameCode(namecode);
558: return -1; // to keep the compiler happy
559: } else {
560: uriCode = entry.uriCode;
561: }
562: }
563: int prefixIndex = (namecode >> 20) & 0xff;
564: String prefix = getPrefixWithIndex(uriCode, prefixIndex);
565: int prefixCode = allocateCodeForPrefix(prefix);
566: return (prefixCode << 16) + uriCode;
567: }
568:
569: /**
570: * Get the namespace-URI of a name, given its name code or fingerprint
571: */
572:
573: public String getURI(int nameCode) {
574: if ((nameCode & 0xffc00) == 0) {
575: return StandardNames.getURI(nameCode & 0xfffff);
576: }
577: NameEntry entry = getNameEntry(nameCode);
578: if (entry == null) {
579: unknownNameCode(nameCode);
580: return null; // to keep the compiler happy
581: }
582: return uris[entry.uriCode];
583: }
584:
585: /**
586: * Get the URI code of a name, given its name code or fingerprint
587: */
588:
589: public short getURICode(int nameCode) {
590: if ((nameCode & 0xffc00) == 0) {
591: return StandardNames.getURICode(nameCode & 0xfffff);
592: }
593: NameEntry entry = getNameEntry(nameCode);
594: if (entry == null) {
595: unknownNameCode(nameCode);
596: return -1;
597: }
598: return entry.uriCode;
599: }
600:
601: /**
602: * Get the local part of a name, given its name code or fingerprint
603: */
604:
605: public String getLocalName(int nameCode) {
606: if ((nameCode & 0xffc00) == 0) {
607: return StandardNames.getLocalName(nameCode & 0xfffff);
608: }
609: NameEntry entry = getNameEntry(nameCode);
610: if (entry == null) {
611: unknownNameCode(nameCode);
612: return null;
613: }
614: return entry.localName;
615: }
616:
617: /**
618: * Get the prefix part of a name, given its name code or fingerprint
619: */
620:
621: public String getPrefix(int nameCode) {
622: if ((nameCode & 0xffc00) == 0) {
623: return StandardNames.getPrefix(nameCode & 0xfffff);
624: }
625: short uriCode = getURICode(nameCode);
626: int prefixIndex = (nameCode >> 20) & 0xff;
627: return getPrefixWithIndex(uriCode, prefixIndex);
628: }
629:
630: /**
631: * Get the display form of a name (the QName), given its name code or fingerprint
632: */
633:
634: public String getDisplayName(int nameCode) {
635: if ((nameCode & 0xffc00) == 0) {
636: // This indicates a standard name known to the system (but it might have a non-standard prefix)
637: int prefixIndex = (nameCode >> 20) & 0xff;
638: short uriCode = getURICode(nameCode);
639: String prefix = getPrefixWithIndex(uriCode, prefixIndex);
640: if (prefix.equals("")) {
641: return StandardNames.getLocalName(nameCode & 0xfffff);
642: } else {
643: return prefix
644: + ':'
645: + StandardNames
646: .getLocalName(nameCode & 0xfffff);
647: }
648: }
649:
650: NameEntry entry = getNameEntry(nameCode);
651: if (entry == null) {
652: unknownNameCode(nameCode);
653: return null;
654: }
655: int prefixIndex = (nameCode >> 20) & 0xff;
656: String prefix = getPrefixWithIndex(entry.uriCode, prefixIndex);
657: if (prefix == null || prefix.equals("")) {
658: return entry.localName;
659: } else {
660: return prefix + ':' + entry.localName;
661: }
662: }
663:
664: /**
665: * Get the Clark form of a name, given its name code or fingerprint
666: *
667: * @return the local name if the name is in the null namespace, or "{uri}local"
668: * otherwise. The name is always interned.
669: */
670:
671: public String getClarkName(int nameCode) {
672: if ((nameCode & 0xffc00) == 0) {
673: return StandardNames.getClarkName(nameCode & 0xfffff);
674: }
675: NameEntry entry = getNameEntry(nameCode);
676: if (entry == null) {
677: unknownNameCode(nameCode);
678: return null;
679: }
680: if (entry.uriCode == 0) {
681: return entry.localName;
682: } else {
683: String n = '{' + getURIFromURICode(entry.uriCode) + '}'
684: + entry.localName;
685: return n.intern();
686: }
687: }
688:
689: /**
690: * Allocate a fingerprint given a Clark Name
691: */
692:
693: public int allocateClarkName(String expandedName) {
694: String namespace;
695: String localName;
696: if (expandedName.charAt(0) == '{') {
697: int closeBrace = expandedName.indexOf('}');
698: if (closeBrace < 0) {
699: throw new IllegalArgumentException(
700: "No closing '}' in Clark name");
701: }
702: namespace = expandedName.substring(1, closeBrace);
703: if (closeBrace == expandedName.length()) {
704: throw new IllegalArgumentException(
705: "Missing local part in Clark name");
706: }
707: localName = expandedName.substring(closeBrace + 1);
708: } else {
709: namespace = "";
710: localName = expandedName;
711: }
712:
713: return allocate("", namespace, localName);
714: }
715:
716: /**
717: * Parse a Clark-format expanded name, returning the URI and local name
718: */
719:
720: public static String[] parseClarkName(String expandedName) {
721: String namespace;
722: String localName;
723: if (expandedName.charAt(0) == '{') {
724: int closeBrace = expandedName.indexOf('}');
725: if (closeBrace < 0) {
726: throw new IllegalArgumentException(
727: "No closing '}' in Clark name");
728: }
729: namespace = expandedName.substring(1, closeBrace);
730: if (closeBrace == expandedName.length()) {
731: throw new IllegalArgumentException(
732: "Missing local part in Clark name");
733: }
734: localName = expandedName.substring(closeBrace + 1);
735: } else {
736: namespace = "";
737: localName = expandedName;
738: }
739: String[] result = { namespace, localName };
740: return result;
741: }
742:
743: /**
744: * Internal error: name not found in namepool
745: * (Usual cause is allocating a name code from one name pool and trying to
746: * find it in another)
747: */
748:
749: private void unknownNameCode(int nameCode) {
750: //System.err.println("Unknown name code " + nameCode);
751: //diagnosticDump();
752: //(new IllegalArgumentException("Unknown name")).printStackTrace();
753: throw new IllegalArgumentException("Unknown name code "
754: + nameCode);
755: }
756:
757: /**
758: * Get a fingerprint for the name with a given uri and local name.
759: * These must be present in the NamePool.
760: * The fingerprint has the property that if two fingerprint are the same, the names
761: * are the same (ie. same local name and same URI).
762: *
763: * @return -1 if not found
764: */
765:
766: public int getFingerprint(String uri, String localName) {
767: // A read-only version of allocate()
768:
769: short uriCode;
770: if (uri.equals("")) {
771: uriCode = 0;
772: } else {
773: if (NamespaceConstant.isReserved(uri)
774: || uri.equals(NamespaceConstant.SAXON)) {
775: int fp = StandardNames.getFingerprint(uri, localName);
776: if (fp != -1) {
777: return fp;
778: // otherwise, look for the name in this namepool
779: }
780: }
781: uriCode = -1;
782: for (short j = 0; j < urisUsed; j++) {
783: if (uris[j].equals(uri)) {
784: uriCode = j;
785: break;
786: }
787: }
788: if (uriCode == -1) {
789: return -1;
790: }
791: }
792:
793: int hash = (localName.hashCode() & 0x7fffffff) % 1023;
794: int depth = 1;
795:
796: NameEntry entry;
797:
798: if (hashslots[hash] == null) {
799: return -1;
800: }
801:
802: entry = hashslots[hash];
803: while (true) {
804: if (entry.uriCode == uriCode
805: && entry.localName.equals(localName)) {
806: break;
807: } else {
808: NameEntry next = entry.nextEntry;
809: depth++;
810: if (next == null) {
811: return -1;
812: } else {
813: entry = next;
814: }
815: }
816: }
817: return (depth << 10) + hash;
818: }
819:
820: /**
821: * Get the namespace URI from a namespace code.
822: */
823:
824: public String getURIFromNamespaceCode(int code) {
825: return uris[code & 0xffff];
826: }
827:
828: /**
829: * Get the namespace URI from a URI code.
830: */
831:
832: public String getURIFromURICode(short code) {
833: return uris[code];
834: }
835:
836: /**
837: * Get the namespace prefix from a namespace code.
838: */
839:
840: public String getPrefixFromNamespaceCode(int code) {
841: // System.err.println("get prefix for " + code);
842: return prefixes[code >> 16];
843: }
844:
845: /**
846: * Get the nameCode for a lexical QName, given a namespace resolver.
847: * @param qname the lexical QName.
848: * @param useDefault if true, an absent prefix is resolved by the NamespaceResolver
849: * to the namespace URI assigned to the prefix "". If false, an absent prefix is
850: * interpreted as meaning the name is in no namespace.
851: * @param checker NameChecker used to check names against the XML 1.0 or 1.1 specification
852: * @return the corresponding nameCode
853: * @throws net.sf.saxon.trans.DynamicError if the string is not a valid lexical QName or
854: * if the namespace prefix has not been declared*
855: */
856:
857: public int allocateLexicalQName(CharSequence qname,
858: boolean useDefault, NamespaceResolver resolver,
859: NameChecker checker) throws DynamicError {
860: try {
861: String[] parts = checker.getQNameParts(qname);
862: String uri = resolver.getURIForPrefix(parts[0], useDefault);
863: if (uri == null) {
864: throw new DynamicError("Namespace prefix '" + parts[0]
865: + "' has not been declared");
866: }
867: return allocate(parts[0], uri, parts[1]);
868: } catch (QNameException e) {
869: throw new DynamicError(e.getMessage());
870: }
871: }
872:
873: /**
874: * Get fingerprint for expanded name in {uri}local format
875: */
876:
877: public int getFingerprintForExpandedName(String expandedName) {
878:
879: String localName;
880: String namespace;
881:
882: if (expandedName.charAt(0) == '{') {
883: int closeBrace = expandedName.indexOf('}');
884: if (closeBrace < 0) {
885: throw new IllegalArgumentException(
886: "No closing '}' in parameter name");
887: }
888: namespace = expandedName.substring(1, closeBrace);
889: if (closeBrace == expandedName.length()) {
890: throw new IllegalArgumentException(
891: "Missing local part in parameter name");
892: }
893: localName = expandedName.substring(closeBrace + 1);
894: } else {
895: namespace = "";
896: localName = expandedName;
897: }
898:
899: return allocate("", namespace, localName);
900: }
901:
902: /**
903: * Save client data on behalf of a user of the namepool
904: */
905:
906: public void setClientData(Class key, Object value) {
907: if (clientData == null) {
908: clientData = new HashMap(10);
909: }
910: clientData.put(key, value);
911: }
912:
913: /**
914: * Retrieve client data on behalf of a user of the namepool
915: */
916:
917: public Object getClientData(Class key) {
918: if (clientData == null) {
919: return null;
920: }
921: return clientData.get(key);
922: }
923:
924: /**
925: * Diagnostic print of the namepool contents.
926: */
927:
928: public synchronized void diagnosticDump() {
929: System.err.println("Contents of NamePool " + this );
930: for (int i = 0; i < 1024; i++) {
931: NameEntry entry = hashslots[i];
932: int depth = 0;
933: while (entry != null) {
934: System.err.println("Fingerprint " + depth + '/' + i);
935: System.err.println(" local name = " + entry.localName
936: + " uri code = " + entry.uriCode);
937: entry = entry.nextEntry;
938: depth++;
939: }
940: }
941:
942: for (int p = 0; p < prefixesUsed; p++) {
943: System.err.println("Prefix " + p + " = " + prefixes[p]);
944: }
945: for (int u = 0; u < urisUsed; u++) {
946: System.err.println("URI " + u + " = " + uris[u]);
947: System.err.println("Prefixes for URI " + u + " = "
948: + prefixesForUri[u]);
949: }
950: }
951:
952: /**
953: * Statistics summarizing the namepool contents.
954: * This method outputs summary statistical information to System.err
955: */
956:
957: public synchronized void statistics() {
958: int slots = 0;
959: int entries = 0;
960: for (int i = 0; i < 1024; i++) {
961: NameEntry entry = hashslots[i];
962: if (entry != null)
963: slots++;
964: while (entry != null) {
965: entry = entry.nextEntry;
966: entries++;
967: }
968: }
969: System.err.println("NamePool contents: " + entries
970: + " entries in " + slots + " chains. " + +prefixesUsed
971: + " prefixes, " + urisUsed + " URIs");
972: }
973:
974: public static class NamePoolLimitException extends RuntimeException {
975:
976: public NamePoolLimitException(String message) {
977: super (message);
978: }
979: }
980:
981: }
982:
983: //
984: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
985: // you may not use this file except in compliance with the License. You may obtain a copy of the
986: // License at http://www.mozilla.org/MPL/
987: //
988: // Software distributed under the License is distributed on an "AS IS" basis,
989: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
990: // See the License for the specific language governing rights and limitations under the License.
991: //
992: // The Original Code is: all this file.
993: //
994: // The Initial Developer of the Original Code is Michael H. Kay.
995: //
996: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
997: //
998: // Contributor(s): none.
999: //
|