001: package net.sf.saxon.pull;
002:
003: import net.sf.saxon.event.PipelineConfiguration;
004: import net.sf.saxon.om.*;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.type.AtomicType;
007: import net.sf.saxon.type.Type;
008: import net.sf.saxon.type.TypeHierarchy;
009: import net.sf.saxon.value.AtomicValue;
010:
011: import javax.xml.transform.SourceLocator;
012:
013: /**
014: * This class delivers any XPath sequence through the pull interface. Atomic values
015: * in the sequence are supplied unchanged, as are top-level text, comment, attribute,
016: * namespace, and processing-instruction nodes. Elements and documents appearing in
017: * the input sequence are supplied as a sequence of events that walks recursively
018: * down the subtree rooted at that node. The input is supplied in the form of a
019: * SequenceIterator.
020: */
021:
022: public class PullFromIterator implements PullProvider {
023:
024: private SequenceIterator base;
025: private PullProvider treeWalker = null;
026: private PipelineConfiguration pipe;
027: private int currentEvent = START_OF_INPUT;
028:
029: public PullFromIterator(SequenceIterator base) {
030: this .base = base;
031: }
032:
033: /**
034: * Set configuration information. This must only be called before any events
035: * have been read.
036: */
037:
038: public void setPipelineConfiguration(PipelineConfiguration pipe) {
039: this .pipe = pipe;
040: }
041:
042: /**
043: * Get configuration information.
044: */
045:
046: public PipelineConfiguration getPipelineConfiguration() {
047: return pipe;
048: }
049:
050: /**
051: * Get the next event
052: *
053: * @return an integer code indicating the type of event. The code
054: * {@link #END_OF_INPUT} is returned at the end of the sequence.
055: */
056:
057: public int next() throws XPathException {
058: if (treeWalker == null) {
059: Item item = base.next();
060: if (item == null) {
061: currentEvent = END_OF_INPUT;
062: return currentEvent;
063: } else if (item instanceof UnconstructedParent) {
064: // this represents a lazily-evaluated element or document node constructor
065: treeWalker = ((UnconstructedParent) item).getPuller();
066: treeWalker.setPipelineConfiguration(pipe);
067: currentEvent = treeWalker.next();
068: return currentEvent;
069: } else if (item instanceof AtomicValue) {
070: currentEvent = ATOMIC_VALUE;
071: return currentEvent;
072: } else {
073: switch (((NodeInfo) item).getNodeKind()) {
074: case Type.TEXT:
075: currentEvent = TEXT;
076: return currentEvent;
077:
078: case Type.COMMENT:
079: currentEvent = COMMENT;
080: return currentEvent;
081:
082: case Type.PROCESSING_INSTRUCTION:
083: currentEvent = PROCESSING_INSTRUCTION;
084: return currentEvent;
085:
086: case Type.ATTRIBUTE:
087: currentEvent = ATTRIBUTE;
088: return currentEvent;
089:
090: case Type.NAMESPACE:
091: currentEvent = NAMESPACE;
092: return currentEvent;
093:
094: case Type.ELEMENT:
095: case Type.DOCUMENT:
096: treeWalker = TreeWalker
097: .makeTreeWalker((NodeInfo) item);
098: treeWalker.setPipelineConfiguration(pipe);
099: currentEvent = treeWalker.next();
100: return currentEvent;
101:
102: default:
103: throw new IllegalStateException();
104:
105: }
106: }
107:
108: } else {
109: // there is an active TreeWalker: just return its next event
110: int event = treeWalker.next();
111: if (event == END_OF_INPUT) {
112: treeWalker = null;
113: currentEvent = next();
114: } else {
115: currentEvent = event;
116: }
117: return currentEvent;
118:
119: }
120: }
121:
122: /**
123: * Get the event most recently returned by next(), or by other calls that change
124: * the position, for example getStringValue() and skipToMatchingEnd(). This
125: * method does not change the position of the PullProvider.
126: *
127: * @return the current event
128: */
129:
130: public int current() {
131: return currentEvent;
132: }
133:
134: /**
135: * Get the attributes associated with the current element. This method must
136: * be called only after a START_ELEMENT event has been notified. The contents
137: * of the returned AttributeCollection are guaranteed to remain unchanged
138: * until the next START_ELEMENT event, but may be modified thereafter. The object
139: * should not be modified by the client.
140: * <p/>
141: * <p>Attributes may be read before or after reading the namespaces of an element,
142: * but must not be read after the first child node has been read, or after calling
143: * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
144: *
145: * @return an AttributeCollection representing the attributes of the element
146: * that has just been notified.
147: */
148:
149: public AttributeCollection getAttributes() throws XPathException {
150: if (treeWalker != null) {
151: return treeWalker.getAttributes();
152: } else {
153: throw new IllegalStateException();
154: }
155: }
156:
157: /**
158: * Get the namespace declarations associated with the current element. This method must
159: * be called only after a START_ELEMENT event has been notified. In the case of a top-level
160: * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
161: * is not included in the sequence being read), the NamespaceDeclarations object returned
162: * will contain a namespace declaration for each namespace that is in-scope for this element
163: * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
164: * a set of namespace declarations and undeclarations, representing the differences between
165: * this element and its parent.
166: * <p/>
167: * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
168: * <p/>
169: * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
170: * event, but may then be overwritten. The object should not be modified by the client.</p>
171: * <p/>
172: * <p>Namespaces may be read before or after reading the attributes of an element,
173: * but must not be read after the first child node has been read, or after calling
174: * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
175: */
176:
177: public NamespaceDeclarations getNamespaceDeclarations()
178: throws XPathException {
179: if (treeWalker != null) {
180: return treeWalker.getNamespaceDeclarations();
181: } else {
182: throw new IllegalStateException();
183: }
184: }
185:
186: /**
187: * Skip the current subtree. This method may be called only immediately after
188: * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
189: * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
190: * the event following the END_DOCUMENT or END_ELEMENT.
191: */
192:
193: public int skipToMatchingEnd() throws XPathException {
194: if (treeWalker != null) {
195: return treeWalker.skipToMatchingEnd();
196: } else {
197: throw new IllegalStateException();
198: }
199: }
200:
201: /**
202: * Close the event reader. This indicates that no further events are required.
203: * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
204: * been reported, but it is recommended to close it if reading terminates
205: * prematurely. Once an event reader has been closed, the effect of further
206: * calls on next() is undefined.
207: */
208:
209: public void close() {
210: if (treeWalker != null) {
211: treeWalker.close();
212: }
213: }
214:
215: /**
216: * Get the nameCode identifying the name of the current node. This method
217: * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
218: * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
219: * including this one, it can also be used after {@link #END_ELEMENT}.
220: * If called at other times, the result is undefined and may result in an IllegalStateException.
221: * If called when the current node is an unnamed namespace node (a node representing the default namespace)
222: * the returned value is -1.
223: *
224: * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
225: * and namespace URI from the name pool.
226: */
227:
228: public int getNameCode() {
229: if (treeWalker != null) {
230: return treeWalker.getNameCode();
231: } else {
232: Item item = base.current();
233: if (item instanceof NodeInfo) {
234: return ((NodeInfo) item).getNameCode();
235: } else {
236: throw new IllegalStateException();
237: }
238: }
239: }
240:
241: /**
242: * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
243: * it does not contain any information about the prefix: so two elements with the same fingerprint
244: * have the same name, excluding prefix. This method
245: * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
246: * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
247: * If called at other times, the result is undefined and may result in an IllegalStateException.
248: * If called when the current node is an unnamed namespace node (a node representing the default namespace)
249: * the returned value is -1.
250: *
251: * @return the fingerprint. The fingerprint can be used to obtain the local name
252: * and namespace URI from the name pool.
253: */
254:
255: public int getFingerprint() {
256: int nc = getNameCode();
257: if (nc == -1) {
258: return -1;
259: } else {
260: return nc & NamePool.FP_MASK;
261: }
262: }
263:
264: /**
265: * Get the string value of the current element, text node, processing-instruction,
266: * or top-level attribute or namespace node, or atomic value.
267: * <p/>
268: * <p>In other situations the result is undefined and may result in an IllegalStateException.</p>
269: * <p/>
270: * <p>If the most recent event was a {@link #START_ELEMENT}, this method causes the content
271: * of the element to be read. The current event on completion of this method will be the
272: * corresponding {@link #END_ELEMENT}. The next call of next() will return the event following
273: * the END_ELEMENT event.</p>
274: *
275: * @return the String Value of the node in question, defined according to the rules in the
276: * XPath data model.
277: */
278:
279: public CharSequence getStringValue() throws XPathException {
280: if (treeWalker != null) {
281: return treeWalker.getStringValue();
282: } else {
283: Item item = base.current();
284: return item.getStringValueCS();
285: }
286: }
287:
288: /**
289: * Get an atomic value. This call may be used only when the last event reported was
290: * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
291: * a free-standing atomic value; it is never used when reading the content of a node.
292: */
293:
294: public AtomicValue getAtomicValue() {
295: if (currentEvent == ATOMIC_VALUE) {
296: return (AtomicValue) base.current();
297: } else {
298: throw new IllegalStateException();
299: }
300: }
301:
302: /**
303: * Get the type annotation of the current attribute or element node, or atomic value.
304: * The result of this method is undefined unless the most recent event was START_ELEMENT,
305: * ATTRIBUTE, or ATOMIC_VALUE.
306: *
307: * @return the type annotation. This code is the fingerprint of a type name, which may be
308: * resolved to a {@link net.sf.saxon.type.SchemaType} by access to the Configuration.
309: */
310:
311: public int getTypeAnnotation() {
312: if (treeWalker != null) {
313: return treeWalker.getTypeAnnotation();
314: } else {
315: Item item = base.current();
316: if (item instanceof NodeInfo) {
317: return ((NodeInfo) item).getTypeAnnotation();
318: } else {
319: final TypeHierarchy th = pipe.getConfiguration()
320: .getNamePool().getTypeHierarchy();
321: return ((AtomicType) ((AtomicValue) item)
322: .getItemType(th)).getFingerprint();
323: }
324: }
325: }
326:
327: /**
328: * Get the location of the current event.
329: * For an event stream representing a real document, the location information
330: * should identify the location in the lexical XML source. For a constructed document, it should
331: * identify the location in the query or stylesheet that caused the node to be created.
332: * A value of null can be returned if no location information is available.
333: */
334:
335: public SourceLocator getSourceLocator() {
336: if (treeWalker != null) {
337: return treeWalker.getSourceLocator();
338: } else {
339: return null;
340: }
341: }
342: }
343:
344: //
345: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
346: // you may not use this file except in compliance with the License. You may obtain a copy of the
347: // License at http://www.mozilla.org/MPL/
348: //
349: // Software distributed under the License is distributed on an "AS IS" basis,
350: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
351: // See the License for the specific language governing rights and limitations under the License.
352: //
353: // The Original Code is: all this file.
354: //
355: // The Initial Developer of the Original Code is Michael H. Kay.
356: //
357: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
358: //
359: // Contributor(s): none.
360: //
|