001: package net.sf.saxon.om;
002:
003: import net.sf.saxon.event.LocationProvider;
004: import net.sf.saxon.style.StandardNames;
005: import org.xml.sax.Attributes;
006:
007: /**
008: * AttributeCollectionImpl is an implementation of both the SAX2 interface Attributes
009: * and the Saxon equivalent AttributeCollection.
010: *
011: * <p>As well as providing the information required by the SAX2 interface, an
012: * AttributeCollection can hold type information (as needed to support the JAXP 1.3
013: * {@link javax.xml.validation.ValidatorHandler} interface), and location information
014: * for debugging. The location information is used in the case of attributes on a result
015: * tree to identify the location in the query or stylesheet from which they were
016: * generated.
017: */
018:
019: public final class AttributeCollectionImpl implements Attributes,
020: AttributeCollection {
021:
022: // Attribute values are maintained as an array of Strings. Everything else is maintained
023: // in the form of integers.
024:
025: private NamePool namePool;
026: private LocationProvider locationProvider;
027: private String[] values = null;
028: private int[] codes = null;
029: private int used = 0;
030:
031: // Empty attribute collection. The caller is trusted not to try and modify it.
032:
033: public static final AttributeCollection EMPTY_ATTRIBUTE_COLLECTION = new AttributeCollectionImpl(
034: null);
035:
036: // Layout of the integer array. There are RECSIZE integers for each attribute.
037:
038: private static final int RECSIZE = 4;
039:
040: private static final int NAMECODE = 0;
041: private static final int TYPECODE = 1;
042: private static final int LOCATIONID = 2;
043: private static final int PROPERTIES = 3;
044:
045: /**
046: * Create an empty attribute list.
047: */
048:
049: public AttributeCollectionImpl(NamePool pool) {
050: namePool = pool;
051: used = 0;
052: }
053:
054: /**
055: * Set the location provider. This must be set if the methods getSystemId() and getLineNumber()
056: * are to be used to get location information for an attribute.
057: */
058:
059: public void setLocationProvider(LocationProvider provider) {
060: this .locationProvider = provider;
061: }
062:
063: /**
064: * Add an attribute to an attribute list. The parameters correspond
065: * to the parameters of the {@link net.sf.saxon.event.Receiver#attribute(int, int, CharSequence, int, int)}
066: * method. There is no check that the name of the attribute is distinct from other attributes
067: * already in the collection: this check must be made by the caller.
068: *
069: * @param nameCode Integer representing the attribute name.
070: * @param typeCode The attribute type code
071: * @param value The attribute value (must not be null)
072: * @param locationId Identifies the attribtue location.
073: * @param properties Attribute properties
074: */
075:
076: public void addAttribute(int nameCode, int typeCode, String value,
077: int locationId, int properties) {
078: if (values == null) {
079: values = new String[5];
080: codes = new int[5 * RECSIZE];
081: used = 0;
082: }
083: if (values.length == used) {
084: int newsize = (used == 0 ? 5 : used * 2);
085: String[] v2 = new String[newsize];
086: int[] c2 = new int[newsize * RECSIZE];
087: System.arraycopy(values, 0, v2, 0, used);
088: System.arraycopy(codes, 0, c2, 0, used * RECSIZE);
089: values = v2;
090: codes = c2;
091: }
092: int n = used * RECSIZE;
093: codes[n + NAMECODE] = nameCode;
094: codes[n + TYPECODE] = typeCode;
095: codes[n + LOCATIONID] = locationId;
096: codes[n + PROPERTIES] = properties;
097: values[used++] = value;
098: }
099:
100: /**
101: * Set (overwrite) an attribute in the attribute list. The parameters correspond
102: * to the parameters of the {@link net.sf.saxon.event.Receiver#attribute(int, int, CharSequence, int, int)}
103: * method.
104: * @param index Identifies the entry to be replaced
105: * @param nameCode Integer representing the attribute name.
106: * @param typeCode The attribute type code
107: * @param value The attribute value (must not be null)
108: * @param locationId Identifies the attribtue location.
109: * @param properties Attribute properties
110: */
111:
112: public void setAttribute(int index, int nameCode, int typeCode,
113: String value, int locationId, int properties) {
114: int n = index * RECSIZE;
115: codes[n + NAMECODE] = nameCode;
116: codes[n + TYPECODE] = typeCode;
117: codes[n + LOCATIONID] = locationId;
118: codes[n + PROPERTIES] = properties;
119: values[index] = value;
120: }
121:
122: /**
123: * Clear the attribute list. This removes the values but doesn't free the memory used.
124: * free the memory, use clear() then compact().
125: */
126:
127: public void clear() {
128: used = 0;
129: }
130:
131: /**
132: * Compact the attribute list to avoid wasting memory
133: */
134:
135: public void compact() {
136: if (used == 0) {
137: codes = null;
138: values = null;
139: } else if (values.length > used) {
140: String[] v2 = new String[used];
141: int[] c2 = new int[used * RECSIZE];
142: System.arraycopy(values, 0, v2, 0, used);
143: System.arraycopy(codes, 0, c2, 0, used * RECSIZE);
144: values = v2;
145: codes = c2;
146: }
147: }
148:
149: /**
150: * Return the number of attributes in the list.
151: *
152: * @return The number of attributes in the list.
153: */
154:
155: public int getLength() {
156: return (values == null ? 0 : used);
157: }
158:
159: /**
160: * Get the namecode of an attribute (by position).
161: *
162: * @param index The position of the attribute in the list.
163: * @return The display name of the attribute as a string, or null if there
164: * is no attribute at that position.
165: */
166:
167: public int getNameCode(int index) {
168: if (codes == null) {
169: return -1;
170: }
171: if (index < 0 || index >= used) {
172: return -1;
173: }
174:
175: return codes[index * RECSIZE + NAMECODE];
176: }
177:
178: /**
179: * Get the namecode of an attribute (by position).
180: *
181: * @param index The position of the attribute in the list.
182: * @return The type annotation, as the fingerprint of the type name.
183: * The bit {@link net.sf.saxon.om.NodeInfo.IS_DTD_TYPE} represents a DTD-derived type.
184: */
185:
186: public int getTypeAnnotation(int index) {
187: if (codes == null) {
188: return StandardNames.XDT_UNTYPED_ATOMIC;
189: }
190: if (index < 0 || index >= used) {
191: return StandardNames.XDT_UNTYPED_ATOMIC;
192: }
193:
194: return codes[index * RECSIZE + TYPECODE];
195: }
196:
197: /**
198: * Get the locationID of an attribute (by position)
199: * @param index The position of the attribute in the list.
200: * @return The location identifier of the attribute. This can be supplied
201: * to a {@link net.sf.saxon.event.LocationProvider} in order to obtain the
202: * actual system identifier and line number of the relevant location
203: */
204:
205: public int getLocationId(int index) {
206: if (codes == null) {
207: return -1;
208: }
209: if (index < 0 || index >= used) {
210: return -1;
211: }
212:
213: return codes[index * RECSIZE + LOCATIONID];
214: }
215:
216: /**
217: * Get the systemId part of the location of an attribute, at a given index.
218: *
219: * <p>Attribute location information is not available from a SAX parser, so this method
220: * is not useful for getting the location of an attribute in a source document. However,
221: * in a Saxon result document, the location information represents the location in the
222: * stylesheet of the instruction used to generate this attribute, which is useful for
223: * debugging.</p>
224: * @param index the required attribute
225: * @return the systemId of the location of the attribute
226: */
227:
228: public String getSystemId(int index) {
229: return locationProvider.getSystemId(getLocationId(index));
230: }
231:
232: /**
233: * Get the line number part of the location of an attribute, at a given index.
234: *
235: * <p>Attribute location information is not available from a SAX parser, so this method
236: * is not useful for getting the location of an attribute in a source document. However,
237: * in a Saxon result document, the location information represents the location in the
238: * stylesheet of the instruction used to generate this attribute, which is useful for
239: * debugging.</p>
240: * @param index the required attribute
241: * @return the line number of the location of the attribute
242: */
243:
244: public int getLineNumber(int index) {
245: return locationProvider.getLineNumber(getLocationId(index));
246: }
247:
248: /**
249: * Get the properties of an attribute (by position)
250: * @param index The position of the attribute in the list.
251: * @return The properties of the attribute. This is a set
252: * of bit-settings defined in class {@link net.sf.saxon.event.ReceiverOptions}. The
253: * most interesting of these is {{@link net.sf.saxon.event.ReceiverOptions#DEFAULTED_ATTRIBUTE},
254: * which indicates an attribute that was added to an element as a result of schema validation.
255: */
256:
257: public int getProperties(int index) {
258: if (codes == null) {
259: return -1;
260: }
261: if (index < 0 || index >= used) {
262: return -1;
263: }
264:
265: return codes[index * RECSIZE + PROPERTIES];
266: }
267:
268: /**
269: * Get the prefix of the name of an attribute (by position).
270: *
271: * @param index The position of the attribute in the list.
272: * @return The prefix of the attribute name as a string, or null if there
273: * is no attribute at that position. Returns "" for an attribute that
274: * has no prefix.
275: */
276:
277: public String getPrefix(int index) {
278: if (codes == null) {
279: return null;
280: }
281: if (index < 0 || index >= used) {
282: return null;
283: }
284: return namePool.getPrefix(getNameCode(index));
285: }
286:
287: /**
288: * Get the lexical QName of an attribute (by position).
289: *
290: * @param index The position of the attribute in the list.
291: * @return The lexical QName of the attribute as a string, or null if there
292: * is no attribute at that position.
293: */
294:
295: public String getQName(int index) {
296: if (codes == null) {
297: return null;
298: }
299: if (index < 0 || index >= used) {
300: return null;
301: }
302: return namePool.getDisplayName(getNameCode(index));
303: }
304:
305: /**
306: * Get the local name of an attribute (by position).
307: *
308: * @param index The position of the attribute in the list.
309: * @return The local name of the attribute as a string, or null if there
310: * is no attribute at that position.
311: */
312:
313: public String getLocalName(int index) {
314: if (codes == null) {
315: return null;
316: }
317: if (index < 0 || index >= used) {
318: return null;
319: }
320: return namePool.getLocalName(getNameCode(index));
321: }
322:
323: /**
324: * Get the namespace URI of an attribute (by position).
325: *
326: * @param index The position of the attribute in the list.
327: * @return The local name of the attribute as a string, or null if there
328: * is no attribute at that position.
329: */
330:
331: public String getURI(int index) {
332: if (codes == null) {
333: return null;
334: }
335: if (index < 0 || index >= used) {
336: return null;
337: }
338: return namePool.getURI(getNameCode(index));
339: }
340:
341: /**
342: * Get the type of an attribute (by position). This is a SAX2 method,
343: * so it gets the type name as a DTD attribute type, mapped from the
344: * schema type code.
345: *
346: * @param index The position of the attribute in the list.
347: * @return The attribute type as a string ("NMTOKEN" for an
348: * enumeration, and "CDATA" if no declaration was
349: * read), or null if there is no attribute at
350: * that position.
351: */
352:
353: public String getType(int index) {
354: int typeCode = getTypeAnnotation(index) & NamePool.FP_MASK;
355: switch (typeCode) {
356: case StandardNames.XS_ID:
357: return "ID";
358: case StandardNames.XS_IDREF:
359: return "IDREF";
360: case StandardNames.XS_NMTOKEN:
361: return "NMTOKEN";
362: case StandardNames.XS_ENTITY:
363: return "ENTITY";
364: case StandardNames.XS_IDREFS:
365: return "IDREFS";
366: case StandardNames.XS_NMTOKENS:
367: return "NMTOKENS";
368: case StandardNames.XS_ENTITIES:
369: return "ENTITIES";
370: default:
371: return "CDATA";
372: }
373: }
374:
375: /**
376: * Get the type of an attribute (by name).
377: *
378: * @param uri The namespace uri of the attribute.
379: * @param localname The local name of the attribute.
380: * @return The index position of the attribute
381: */
382:
383: public String getType(String uri, String localname) {
384: int index = findByName(uri, localname);
385: return (index < 0 ? null : getType(index));
386: }
387:
388: /**
389: * Get the value of an attribute (by position).
390: *
391: * @param index The position of the attribute in the list.
392: * @return The attribute value as a string, or null if
393: * there is no attribute at that position.
394: */
395:
396: public String getValue(int index) {
397: if (values == null) {
398: return null;
399: }
400: if (index < 0 || index >= used) {
401: return null;
402: }
403: return values[index];
404: }
405:
406: /**
407: * Get the value of an attribute (by name).
408: *
409: * @param uri The namespace uri of the attribute.
410: * @param localname The local name of the attribute.
411: * @return The index position of the attribute
412: */
413:
414: public String getValue(String uri, String localname) {
415: int index = findByName(uri, localname);
416: return (index < 0 ? null : getValue(index));
417: }
418:
419: /**
420: * Get the attribute value using its fingerprint
421: */
422:
423: public String getValueByFingerprint(int fingerprint) {
424: int index = findByFingerprint(fingerprint);
425: return (index < 0 ? null : getValue(index));
426: }
427:
428: /**
429: * Get the index of an attribute, from its lexical QName
430: *
431: * @param qname The lexical QName of the attribute. The prefix must match.
432: * @return The index position of the attribute
433: */
434:
435: public int getIndex(String qname) {
436: if (codes == null) {
437: return -1;
438: }
439: if (qname.indexOf(':') < 0) {
440: return findByName("", qname);
441: }
442: // Searching using prefix+localname is not recommended, but SAX allows it...
443: String[] parts;
444: try {
445: parts = Name11Checker.getInstance().getQNameParts(qname);
446: } catch (QNameException err) {
447: return -1;
448: }
449: String prefix = parts[0];
450: if (prefix.equals("")) {
451: return findByName("", qname);
452: } else {
453: String localName = parts[1];
454: for (int i = 0; i < used; i++) {
455: String lname = namePool.getLocalName(getNameCode(i));
456: String ppref = namePool.getPrefix(getNameCode(i));
457: if (localName.equals(lname) && prefix.equals(ppref)) {
458: return i;
459: }
460: }
461: return -1;
462: }
463: }
464:
465: /**
466: * Get the index of an attribute (by name).
467: *
468: * @param uri The namespace uri of the attribute.
469: * @param localname The local name of the attribute.
470: * @return The index position of the attribute
471: */
472:
473: public int getIndex(String uri, String localname) {
474: return findByName(uri, localname);
475: }
476:
477: /**
478: * Get the index, given the fingerprint.
479: * Return -1 if not found.
480: */
481:
482: public int getIndexByFingerprint(int fingerprint) {
483: return findByFingerprint(fingerprint);
484: }
485:
486: /**
487: * Get the type of an attribute (by lexical QName).
488: *
489: * @param name The lexical QName of the attribute.
490: * @return The attribute type as a string (e.g. "NMTOKEN", or
491: * "CDATA" if no declaration was read).
492: */
493:
494: public String getType(String name) {
495: int index = getIndex(name);
496: return getType(index);
497: }
498:
499: /**
500: * Get the value of an attribute (by lexical QName).
501: *
502: * @param name The attribute name (a lexical QName).
503: * The prefix must match the prefix originally used. This method is defined in SAX, but is
504: * not recommended except where the prefix is null.
505: */
506:
507: public String getValue(String name) {
508: int index = getIndex(name);
509: return getValue(index);
510: }
511:
512: /**
513: * Find an attribute by expanded name
514: * @param uri the namespace uri
515: * @param localName the local name
516: * @return the index of the attribute, or -1 if absent
517: */
518:
519: private int findByName(String uri, String localName) {
520: if (namePool == null) {
521: return -1; // indicates an empty attribute set
522: }
523: int f = namePool.getFingerprint(uri, localName);
524: if (f == -1) {
525: return -1;
526: }
527: return findByFingerprint(f);
528: }
529:
530: /**
531: * Find an attribute by fingerprint
532: *
533: * @return the index of the attribute, or -1 if absent
534: */
535:
536: private int findByFingerprint(int fingerprint) {
537: if (codes == null) {
538: return -1;
539: }
540: for (int i = 0; i < used; i++) {
541: if (fingerprint == (codes[i * RECSIZE + NAMECODE] & NamePool.FP_MASK)) {
542: return i;
543: }
544: }
545: return -1;
546: }
547:
548: /**
549: * Determine whether a given attribute has the is-ID property set
550: */
551:
552: public boolean isId(int index) {
553: return getType(index).equals("ID")
554: || ((getNameCode(index) & NamePool.FP_MASK) == StandardNames.XML_ID);
555: }
556:
557: }
558:
559: //
560: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
561: // you may not use this file except in compliance with the License. You may obtain a copy of the
562: // License at http://www.mozilla.org/MPL/
563: //
564: // Software distributed under the License is distributed on an "AS IS" basis,
565: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
566: // See the License for the specific language governing rights and limitations under the License.
567: //
568: // The Original Code is: all this file.
569: //
570: // The Initial Developer of the Original Code is Michael H. Kay.
571: //
572: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
573: //
574: // Contributor(s): none.
575: //
|