001: package net.sf.saxon.pull;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.event.PipelineConfiguration;
005: import net.sf.saxon.expr.XPathContext;
006: import net.sf.saxon.instruct.DocumentInstr;
007: import net.sf.saxon.instruct.ElementCreator;
008: import net.sf.saxon.instruct.ParentNodeConstructor;
009: import net.sf.saxon.om.*;
010: import net.sf.saxon.trans.DynamicError;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.type.Type;
013: import net.sf.saxon.value.AtomicValue;
014:
015: import javax.xml.transform.SourceLocator;
016: import java.util.ArrayList;
017: import java.util.Stack;
018:
019: /**
020: * A virtual tree walker provides a sequence of pull events describing the structure and content of a tree
021: * that is conceptually being constructed by expressions in a query or stylesheet; in fact the tree is
022: * not necessarily constructed in memory, and exists only as this stream of pull events.
023: * <p>
024: * The tree is physically constructed if operations are requested that depend on the identity of the nodes
025: * in the tree, or that navigate within the tree. Operations such as copying or atomizing the tree can be done
026: * directly, without building it in memory. (Note however that if such operations are done more than once, the
027: * underlying instructions may be evaluated repeatedly.)
028: */
029:
030: public class VirtualTreeWalker implements PullProvider,
031: NamespaceDeclarations {
032:
033: private PipelineConfiguration pipe;
034: private int currentEvent = START_OF_INPUT;
035: private int nameCode;
036: private int nextNameCode;
037: private ParentNodeConstructor instruction;
038: private XPathContext context;
039: private Stack constructorStack = new Stack();
040: private Stack iteratorStack = new Stack();
041: private PullProvider subordinateTreeWalker = null;
042: // The subordinateTreeWalker is used if the tree construction expression pulls in references
043: // to document or element nodes in an existing source document. When this happens, tree walking
044: // events generated by walking the source document are copied into to the stream of events
045: // generated by this class.
046: private boolean alreadyRead = false;
047: private boolean allowAttributes = false;
048:
049: private int stripDepth = -1;
050: // If this is >0, it indicates that instructions on the constructor stack with depth < stripDepth
051: // specify validation=preserve, while those at greater depth effectively specify validation=strip
052: // (It doesn't matter if they actually say validation=preserve, the stripping takes priority)
053:
054: private AttributeCollectionImpl attributes;
055: private boolean foundAttributes;
056: private int[] activeNamespaces;
057: private ArrayList additionalNamespaces = new ArrayList(10); // array of namespace nodes
058:
059: /**
060: * Create a VirtualTreeWalker to navigate the tree constructed by evaluating a given instruction
061: * in a given dyamic context
062: * @param instruction the instruction (this will always be an instruction that creates element or
063: * document nodes)
064: * @param context the dynamic evaluation context
065: */
066:
067: public VirtualTreeWalker(ParentNodeConstructor instruction,
068: XPathContext context) {
069: this .instruction = instruction;
070: this .context = context;
071: }
072:
073: /**
074: * Set configuration information. This must only be called before any events
075: * have been read.
076: */
077:
078: public void setPipelineConfiguration(PipelineConfiguration pipe) {
079: this .pipe = pipe;
080: }
081:
082: /**
083: * Get configuration information.
084: */
085:
086: public PipelineConfiguration getPipelineConfiguration() {
087: return pipe;
088: }
089:
090: /**
091: * Get the namepool
092: */
093:
094: public NamePool getNamePool() {
095: return pipe.getConfiguration().getNamePool();
096: }
097:
098: /**
099: * Get the next event
100: *
101: * @return an integer code indicating the type of event. The code
102: * {@link #END_OF_INPUT} is returned at the end of the sequence.
103: */
104:
105: public int next() throws XPathException {
106:
107: try {
108:
109: // First see if we are currently walking some other tree that has been logically
110: // copied into this tree.
111:
112: if (subordinateTreeWalker != null) {
113: currentEvent = subordinateTreeWalker.next();
114: if (currentEvent == END_OF_INPUT) {
115: subordinateTreeWalker = null;
116: return next();
117: }
118: return currentEvent;
119: }
120:
121: // On the first call, produce a START_ELEMENT or START_DOCUMENT event depending on the instruction
122:
123: if (currentEvent == START_OF_INPUT) {
124: constructorStack.push(instruction);
125: if (stripDepth < 0
126: && instruction.getValidationAction() == Validation.STRIP) {
127: stripDepth = constructorStack.size();
128: }
129: SequenceIterator content = instruction
130: .getContentExpression().iterate(context);
131: iteratorStack.push(content);
132: if (instruction instanceof DocumentInstr) {
133: currentEvent = START_DOCUMENT;
134: nameCode = -1;
135: } else {
136: currentEvent = START_ELEMENT;
137: nameCode = ((ElementCreator) instruction)
138: .getNameCode(context);
139: allowAttributes = true;
140: // look ahead to generate all the attributes and namespaces of the element
141: processAttributesAndNamespaces(
142: (ElementCreator) instruction, content);
143: // remember that we've read one event too many
144: allowAttributes = false;
145: alreadyRead = true;
146: }
147: return currentEvent;
148: }
149:
150: if (iteratorStack.isEmpty()) {
151: // if we're at the top level, see if we've just started, or are about to finish
152: if (currentEvent == START_DOCUMENT
153: || currentEvent == START_ELEMENT) {
154: // we've just started: start processing the content of the instruction
155: SequenceIterator iter = instruction
156: .getContentExpression().iterate(context);
157: constructorStack.push(instruction);
158: if (stripDepth < 0
159: && instruction.getValidationAction() == Validation.STRIP) {
160: stripDepth = constructorStack.size();
161: }
162: iteratorStack.push(iter);
163: } else if (currentEvent == END_DOCUMENT
164: || currentEvent == END_ELEMENT) {
165: // we're about to finish
166: currentEvent = END_OF_INPUT;
167: return currentEvent;
168: } else {
169: // we're going to finish soon, but must first generate the last END_DOCUMENT or END_ELEMENT event
170: currentEvent = ((instruction instanceof DocumentInstr) ? END_DOCUMENT
171: : END_ELEMENT);
172: return currentEvent;
173: }
174: }
175:
176: // Read the next item from the current content iterator
177:
178: SequenceIterator iterator = ((SequenceIterator) iteratorStack
179: .peek());
180: if (alreadyRead) {
181: Item item = iterator.current();
182: alreadyRead = false;
183: nameCode = nextNameCode;
184: return processItem(iterator, item);
185: } else {
186: return processItem(iterator, iterator.next());
187: }
188: } catch (XPathException e) {
189: // report any dynamic errors unless already reported
190: context.getController().reportFatalError(e);
191: throw e;
192: }
193: }
194:
195: private FastStringBuffer textNodeBuffer = new FastStringBuffer(100);
196:
197: /**
198: * Process an item in the content of an element or document node
199: * @param iterator the iterator over the contents of the element or document
200: * @param item the current item to be processed, or null if the end of the content
201: * iterator has been reached
202: * @return the next event code
203: * @throws XPathException if a dynamic error occurs
204: */
205:
206: private int processItem(SequenceIterator iterator, Item item)
207: throws XPathException {
208: if (item == null) {
209: // we've reached the end of the children
210: if (stripDepth == constructorStack.size()) {
211: stripDepth = -1;
212: }
213: ParentNodeConstructor inst = (ParentNodeConstructor) constructorStack
214: .pop();
215: if (inst instanceof DocumentInstr) {
216: iteratorStack.pop();
217: if (iteratorStack.isEmpty()) {
218: currentEvent = END_DOCUMENT;
219: nameCode = -1;
220: return currentEvent;
221: }
222: // skip the END_DOCUMENT event for a nested document node
223: return next();
224: } else {
225: currentEvent = END_ELEMENT;
226: nameCode = -1;
227: iteratorStack.pop();
228: return currentEvent;
229: }
230:
231: } else if (item instanceof UnconstructedParent) {
232: // this represents a nested element or document node constructor
233: UnconstructedParent parent = (UnconstructedParent) item;
234: ParentNodeConstructor inst = parent.getInstruction();
235:
236: constructorStack.push(inst);
237: if (stripDepth < 0
238: && inst.getValidationAction() == Validation.STRIP) {
239: stripDepth = constructorStack.size();
240: }
241: SequenceIterator content = inst.getContentExpression()
242: .iterate(parent.getXPathContext());
243: if (inst instanceof DocumentInstr) {
244: iteratorStack.push(content);
245: // skip the START_DOCUMENT event
246: return next();
247: } else {
248: currentEvent = START_ELEMENT;
249: nameCode = ((UnconstructedElement) item).getNameCode();
250: processAttributesAndNamespaces((ElementCreator) inst,
251: content);
252: alreadyRead = true;
253: iteratorStack.push(content);
254: return currentEvent;
255: }
256:
257: } else if (item instanceof AtomicValue) {
258: textNodeBuffer.setLength(0);
259: textNodeBuffer.append(item.getStringValueCS());
260: while (true) {
261: Item next = iterator.next();
262: if (next instanceof AtomicValue) {
263: textNodeBuffer.append(' ');
264: textNodeBuffer.append(next.getStringValueCS());
265: continue;
266: } else {
267: currentEvent = TEXT;
268: nameCode = -1;
269: alreadyRead = true;
270: return currentEvent;
271: }
272: }
273:
274: } else {
275: nameCode = ((NodeInfo) item).getNameCode();
276: switch (((NodeInfo) item).getNodeKind()) {
277: case Type.TEXT:
278: currentEvent = TEXT;
279: textNodeBuffer.setLength(0);
280: textNodeBuffer.append(item.getStringValueCS());
281: // TODO: avoid this unnecessary copy.
282: return currentEvent;
283:
284: case Type.COMMENT:
285: currentEvent = COMMENT;
286: return currentEvent;
287:
288: case Type.PROCESSING_INSTRUCTION:
289: currentEvent = PROCESSING_INSTRUCTION;
290: return currentEvent;
291:
292: case Type.ATTRIBUTE:
293: if (!allowAttributes) {
294: DynamicError de;
295: if (constructorStack.peek() instanceof DocumentInstr) {
296: de = new DynamicError(
297: "Attributes cannot be attached to a document node");
298: if (context.getController().getExecutable()
299: .getHostLanguage() == Configuration.XQUERY) {
300: de.setErrorCode("XQTY0004");
301: } else {
302: de.setErrorCode("XTDE0420");
303: }
304: } else {
305: de = new DynamicError(
306: "Attributes in the content of an element must come before the child nodes");
307: if (context.getController().getExecutable()
308: .getHostLanguage() == Configuration.XQUERY) {
309: de.setErrorCode("XQDY0024");
310: } else {
311: de.setErrorCode("XTDE0410");
312: }
313: }
314: de.setXPathContext(context);
315: de.setLocator(getSourceLocator());
316: throw de;
317: }
318: currentEvent = ATTRIBUTE;
319: return currentEvent;
320:
321: case Type.NAMESPACE:
322: if (!allowAttributes) {
323: DynamicError de = new DynamicError(
324: "Namespace nodes in the content of an element must come before the child nodes");
325: de.setErrorCode("XTDE0410");
326: de.setXPathContext(context);
327: de.setLocator(getSourceLocator());
328: throw de;
329: }
330: currentEvent = NAMESPACE;
331: return currentEvent;
332:
333: case Type.ELEMENT:
334: subordinateTreeWalker = TreeWalker
335: .makeTreeWalker((NodeInfo) item);
336: subordinateTreeWalker.setPipelineConfiguration(pipe);
337: currentEvent = subordinateTreeWalker.next();
338: nameCode = subordinateTreeWalker.getNameCode();
339: return currentEvent;
340:
341: case Type.DOCUMENT:
342: subordinateTreeWalker = TreeWalker
343: .makeTreeWalker((NodeInfo) item);
344: subordinateTreeWalker.setPipelineConfiguration(pipe);
345: subordinateTreeWalker = new DocumentEventIgnorer(
346: subordinateTreeWalker);
347: subordinateTreeWalker.setPipelineConfiguration(pipe);
348: currentEvent = subordinateTreeWalker.next();
349: nameCode = -1;
350: return currentEvent;
351:
352: default:
353: throw new IllegalStateException();
354:
355: }
356: }
357: }
358:
359: /**
360: * Following a START_ELEMENT event, evaluate the contents of the element to obtain all attributes and namespaces.
361: * This process stops when the first event other than an attribute or namespace is read. We then remember the
362: * extra event, which will be the next event returned in the normal sequence. Note that the relative order
363: * of attributes and namespaces is undefined.
364: * @param inst The instruction that creates the element node
365: * @param content Iterator over the expression that generates the attributes, namespaces, and content of the
366: * element
367: * @throws XPathException if any dynamic error occurs
368: */
369: private void processAttributesAndNamespaces(ElementCreator inst,
370: SequenceIterator content) throws XPathException {
371: foundAttributes = false;
372: additionalNamespaces.clear();
373: activeNamespaces = inst.getActiveNamespaces();
374: if (activeNamespaces == null) {
375: activeNamespaces = EMPTY_INT_ARRAY;
376: }
377: boolean preserve = (stripDepth < 0);
378: while (true) {
379: Item next = content.next();
380: if (next == null) {
381: break;
382: } else if (next instanceof NodeInfo) {
383: NodeInfo node = (NodeInfo) next;
384: int kind = node.getNodeKind();
385: if (kind == Type.ATTRIBUTE) {
386: if (!foundAttributes) {
387: if (attributes == null) {
388: attributes = new AttributeCollectionImpl(
389: context.getNamePool());
390: }
391: attributes.clear();
392: foundAttributes = true;
393: }
394: int index = attributes.getIndexByFingerprint(node
395: .getFingerprint());
396: if (index >= 0) {
397: // Attribute already exists. In XQuery this is an error. In XSLT, the last attribute wins
398: if (context.getController().getExecutable()
399: .getHostLanguage() == Configuration.XSLT) {
400: attributes.setAttribute(index, node
401: .getNameCode(), preserve ? node
402: .getTypeAnnotation() : -1, node
403: .getStringValue(), 0, 0);
404: } else {
405: DynamicError de = new DynamicError(
406: "The attributes of an element must have distinct names");
407: de.setErrorCode("XQDY0025");
408: de.setXPathContext(context);
409: de.setLocator(getSourceLocator());
410: throw de;
411: }
412: } else {
413: attributes.addAttribute(node.getNameCode(),
414: preserve ? node.getTypeAnnotation()
415: : -1, node.getStringValue(), 0,
416: 0);
417: }
418: } else if (kind == Type.NAMESPACE) {
419: additionalNamespaces.add(node);
420: } else if (kind == Type.TEXT
421: && node.getStringValue().equals("")) {
422: continue; // ignore zero-length text nodes
423: } else {
424: nextNameCode = ((NodeInfo) next).getNameCode();
425: break;
426: }
427: } else {
428: break;
429: }
430: }
431: }
432:
433: /**
434: * Get the event most recently returned by next(), or by other calls that change
435: * the position, for example getStringValue() and skipToMatchingEnd(). This
436: * method does not change the position of the PullProvider.
437: *
438: * @return the current event
439: */
440:
441: public int current() {
442: return currentEvent;
443: }
444:
445: /**
446: * Get the attributes associated with the current element. This method must
447: * be called only after a START_ELEMENT event has been notified. The contents
448: * of the returned AttributeCollection are guaranteed to remain unchanged
449: * until the next START_ELEMENT event, but may be modified thereafter. The object
450: * should not be modified by the client.
451: * <p/>
452: * <p>Attributes may be read before or after reading the namespaces of an element,
453: * but must not be read after the first child node has been read, or after calling
454: * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
455: *
456: * @return an AttributeCollection representing the attributes of the element
457: * that has just been notified.
458: */
459:
460: public AttributeCollection getAttributes() throws XPathException {
461: if (subordinateTreeWalker != null) {
462: return subordinateTreeWalker.getAttributes();
463: } else {
464: if (foundAttributes) {
465: return attributes;
466: } else {
467: return AttributeCollectionImpl.EMPTY_ATTRIBUTE_COLLECTION;
468: }
469: }
470: }
471:
472: /**
473: * Get the namespace declarations associated with the current element. This method must
474: * be called only after a START_ELEMENT event has been notified. In the case of a top-level
475: * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
476: * is not included in the sequence being read), the NamespaceDeclarations object returned
477: * will contain a namespace declaration for each namespace that is in-scope for this element
478: * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
479: * a set of namespace declarations and undeclarations, representing the differences between
480: * this element and its parent.
481: * <p/>
482: * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
483: * <p/>
484: * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
485: * event, but may then be overwritten. The object should not be modified by the client.</p>
486: * <p/>
487: * <p>Namespaces may be read before or after reading the attributes of an element,
488: * but must not be read after the first child node has been read, or after calling
489: * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
490: */
491:
492: public NamespaceDeclarations getNamespaceDeclarations()
493: throws XPathException {
494: if (subordinateTreeWalker != null) {
495: return subordinateTreeWalker.getNamespaceDeclarations();
496: } else {
497: return this ;
498: }
499: }
500:
501: /**
502: * Skip the current subtree. This method may be called only immediately after
503: * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
504: * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
505: * the event following the END_DOCUMENT or END_ELEMENT.
506: * @throws IllegalStateException if the method is called at any time other than
507: * immediately after a START_DOCUMENT or START_ELEMENT event.
508: */
509:
510: public int skipToMatchingEnd() throws XPathException {
511: if (currentEvent != START_DOCUMENT
512: && currentEvent != START_ELEMENT) {
513: throw new IllegalStateException();
514: }
515: if (subordinateTreeWalker != null) {
516: return subordinateTreeWalker.skipToMatchingEnd();
517: } else {
518: SequenceIterator content = (SequenceIterator) iteratorStack
519: .peek();
520: if (alreadyRead) {
521: alreadyRead = false;
522: }
523: while (true) {
524: Item next = content.next();
525: if (next == null) {
526: break;
527: }
528: }
529: return (currentEvent == START_DOCUMENT ? END_DOCUMENT
530: : END_ELEMENT);
531: }
532: }
533:
534: /**
535: * Close the event reader. This indicates that no further events are required.
536: * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
537: * been reported, but it is recommended to close it if reading terminates
538: * prematurely. Once an event reader has been closed, the effect of further
539: * calls on next() is undefined.
540: */
541:
542: public void close() {
543: if (subordinateTreeWalker != null) {
544: subordinateTreeWalker.close();
545: } else {
546: // do nothing
547: }
548: }
549:
550: /**
551: * Set the initial nameCode (the nameCode of the node at the root of the tree being walked)
552: */
553:
554: public void setNameCode(int nameCode) {
555: this .nameCode = nameCode;
556: }
557:
558: /**
559: * Get the nameCode identifying the name of the current node. This method
560: * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
561: * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
562: * <b>but not this one</b>, it can also be used after {@link #END_ELEMENT}.
563: * If called at other times, the result is undefined and may result in an IllegalStateException.
564: * If called when the current node is an unnamed namespace node (a node representing the default namespace)
565: * the returned value is -1.
566: *
567: * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
568: * and namespace URI from the name pool.
569: */
570:
571: public int getNameCode() {
572: if (subordinateTreeWalker != null) {
573: return subordinateTreeWalker.getNameCode();
574: }
575: return nameCode;
576: }
577:
578: /**
579: * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
580: * it does not contain any information about the prefix: so two elements with the same fingerprint
581: * have the same name, excluding prefix. This method
582: * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
583: * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
584: * If called at other times, the result is undefined and may result in an IllegalStateException.
585: * If called when the current node is an unnamed namespace node (a node representing the default namespace)
586: * the returned value is -1.
587: *
588: * @return the fingerprint. The fingerprint can be used to obtain the local name
589: * and namespace URI from the name pool.
590: */
591:
592: public int getFingerprint() {
593: int nc = getNameCode();
594: if (nc == -1) {
595: return -1;
596: } else {
597: return nc & NamePool.FP_MASK;
598: }
599: }
600:
601: /**
602: * Get the string value of the current element, text node, processing-instruction,
603: * or top-level attribute or namespace node, or atomic value.
604: * <p/>
605: * <p>In other situations the result is undefined and may result in an IllegalStateException.</p>
606: * <p/>
607: * <p>If the most recent event was a {@link #START_ELEMENT}, this method causes the content
608: * of the element to be read. The current event on completion of this method will be the
609: * corresponding {@link #END_ELEMENT}. The next call of next() will return the event following
610: * the END_ELEMENT event.</p>
611: *
612: * @return the String Value of the node in question, defined according to the rules in the
613: * XPath data model.
614: */
615:
616: public CharSequence getStringValue() throws XPathException {
617: if (subordinateTreeWalker != null) {
618: return subordinateTreeWalker.getStringValue();
619: } else if (currentEvent == TEXT) {
620: return textNodeBuffer;
621: } else if (currentEvent != START_ELEMENT
622: && currentEvent != START_DOCUMENT) {
623: SequenceIterator content = (SequenceIterator) iteratorStack
624: .peek();
625: if (content.current() == null) {
626: return "";
627: }
628: return content.current().getStringValue();
629: } else {
630: FastStringBuffer sb = new FastStringBuffer(100);
631: SequenceIterator content = (SequenceIterator) iteratorStack
632: .peek();
633: if (alreadyRead) {
634: if (content.current() == null) {
635: return "";
636: }
637: processText(content.current(), sb);
638: alreadyRead = false;
639: }
640: while (true) {
641: Item next = content.next();
642: if (next == null) {
643: break;
644: }
645: processText(next, sb);
646: }
647: return sb;
648: }
649: }
650:
651: /**
652: * Add the string value of a child item to a string buffer that is used to accumulate the
653: * string value of a document or element node
654: * @param item the child item
655: * @param sb the string buffer where the content is accumulated
656: */
657:
658: private void processText(Item item, FastStringBuffer sb) {
659: if (item instanceof UnconstructedParent) {
660: sb.append(item.getStringValueCS());
661: } else if (item instanceof AtomicValue) {
662: sb.append(item.getStringValueCS());
663: } else {
664: NodeInfo node = (NodeInfo) item;
665: switch (node.getNodeKind()) {
666: case Type.DOCUMENT:
667: case Type.ELEMENT:
668: case Type.TEXT:
669: sb.append(node.getStringValueCS());
670: default:
671: // do nothing
672: }
673: }
674: }
675:
676: /**
677: * Get an atomic value. This call may be used only when the last event reported was
678: * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
679: * a free-standing atomic value; it is never used when reading the content of a node.
680: */
681:
682: public AtomicValue getAtomicValue() {
683: throw new IllegalStateException();
684: }
685:
686: /**
687: * Get the type annotation of the current attribute or element node, or atomic value.
688: * The result of this method is undefined unless the most recent event was START_ELEMENT,
689: * ATTRIBUTE, or ATOMIC_VALUE.
690: *
691: * @return the type annotation. This code is the fingerprint of a type name, which may be
692: * resolved to a {@link net.sf.saxon.type.SchemaType} by access to the Configuration.
693: */
694:
695: public int getTypeAnnotation() {
696: if (subordinateTreeWalker != null && stripDepth < 0) {
697: return subordinateTreeWalker.getTypeAnnotation();
698: } else {
699: return -1;
700: }
701: }
702:
703: /**
704: * Get the location of the current event.
705: * For an event stream representing a real document, the location information
706: * should identify the location in the lexical XML source. For a constructed document, it should
707: * identify the location in the query or stylesheet that caused the node to be created.
708: * A value of null can be returned if no location information is available.
709: */
710:
711: public SourceLocator getSourceLocator() {
712: return instruction;
713: }
714:
715: /**
716: * Get the number of declarations (and undeclarations) in this list.
717: */
718:
719: public int getLength() {
720: return activeNamespaces.length + additionalNamespaces.size();
721: }
722:
723: /**
724: * Get the prefix of the n'th declaration (or undeclaration) in the list,
725: * counting from zero.
726: *
727: * @param index the index identifying which declaration is required.
728: * @return the namespace prefix. For a declaration or undeclaration of the
729: * default namespace, this is the zero-length string.
730: * @throws IndexOutOfBoundsException if the index is out of range.
731: */
732:
733: public String getPrefix(int index) {
734: if (index < activeNamespaces.length) {
735: return getNamePool().getPrefixFromNamespaceCode(
736: activeNamespaces[index]);
737: } else {
738: return ((NodeInfo) additionalNamespaces.get(index
739: - activeNamespaces.length)).getLocalPart();
740: }
741: }
742:
743: /**
744: * Get the namespace URI of the n'th declaration (or undeclaration) in the list,
745: * counting from zero.
746: *
747: * @param index the index identifying which declaration is required.
748: * @return the namespace URI. For a namespace undeclaration, this is the
749: * zero-length string.
750: * @throws IndexOutOfBoundsException if the index is out of range.
751: */
752:
753: public String getURI(int index) {
754: if (index < activeNamespaces.length) {
755: return getNamePool().getURIFromNamespaceCode(
756: activeNamespaces[index]);
757: } else {
758: return ((NodeInfo) additionalNamespaces.get(index
759: - activeNamespaces.length)).getStringValue();
760: }
761: }
762:
763: /**
764: * Get the n'th declaration in the list in the form of a namespace code. Namespace
765: * codes can be translated into a prefix and URI by means of methods in the
766: * NamePool
767: *
768: * @param index the index identifying which declaration is required.
769: * @return the namespace code. This is an integer whose upper half indicates
770: * the prefix (0 represents the default namespace), and whose lower half indicates
771: * the URI (0 represents an undeclaration).
772: * @throws IndexOutOfBoundsException if the index is out of range.
773: * @see net.sf.saxon.om.NamePool#getPrefixFromNamespaceCode(int)
774: * @see net.sf.saxon.om.NamePool#getURIFromNamespaceCode(int)
775: */
776:
777: public int getNamespaceCode(int index) {
778: if (index < activeNamespaces.length) {
779: return activeNamespaces[index];
780: } else {
781: return getNamePool().allocateNamespaceCode(
782: getPrefix(index), getURI(index));
783: }
784: }
785:
786: /**
787: * Get all the namespace codes, as an array.
788: *
789: * @param buffer a sacrificial array that the method is free to use to contain the result.
790: * May be null.
791: * @return an integer array containing namespace codes. The array may be filled completely
792: * with namespace codes, or it may be incompletely filled, in which case a -1 integer acts
793: * as a terminator.
794: */
795:
796: // TODO: not coded for efficiency!
797: public int[] getNamespaceCodes(int[] buffer) {
798: if (buffer.length < getLength()) {
799: buffer = new int[getLength()];
800: } else {
801: buffer[getLength()] = -1;
802: }
803: for (int i = 0; i < getLength(); i++) {
804: buffer[i] = getNamespaceCode(i);
805: }
806: return buffer;
807: }
808:
809: private static final int[] EMPTY_INT_ARRAY = new int[0];
810: }
811:
812: //
813: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
814: // you may not use this file except in compliance with the License. You may obtain a copy of the
815: // License at http://www.mozilla.org/MPL/
816: //
817: // Software distributed under the License is distributed on an "AS IS" basis,
818: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
819: // See the License for the specific language governing rights and limitations under the License.
820: //
821: // The Original Code is: all this file.
822: //
823: // The Initial Developer of the Original Code is Michael H. Kay.
824: //
825: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
826: //
827: // Contributor(s): none.
828: //
|