001: package net.sf.saxon.pull;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.style.StandardNames;
005: import net.sf.saxon.value.Value;
006: import net.sf.saxon.event.PipelineConfiguration;
007: import net.sf.saxon.event.Receiver;
008: import net.sf.saxon.event.SequenceOutputter;
009: import net.sf.saxon.expr.StackFrame;
010: import net.sf.saxon.expr.StaticProperty;
011: import net.sf.saxon.expr.XPathContext;
012: import net.sf.saxon.expr.XPathContextMajor;
013: import net.sf.saxon.instruct.ParentNodeConstructor;
014: import net.sf.saxon.om.*;
015: import net.sf.saxon.pattern.NodeTest;
016: import net.sf.saxon.tinytree.TinyBuilder;
017: import net.sf.saxon.trace.Location;
018: import net.sf.saxon.trans.XPathException;
019: import net.sf.saxon.trans.UncheckedXPathException;
020:
021: /**
022: * This class represents a virtual element node, the result of an element constructor that
023: * (in general) hasn't been fully evaluated. This is similar to a Closure, except that it is
024: * a NodeInfo rather than a Value. The object is capable of materializing the element if it
025: * is actually needed, but the aim is to avoid materializing the element wherever possible,
026: * at any rate not until its parent element is constructed so that this element can be built
027: * in-situ rather than being built as a standalone element and then later copied.
028: *
029: * This class is not currently used for elements that require schema validation.
030: */
031: public abstract class UnconstructedParent implements NodeInfo {
032:
033: private ParentNodeConstructor instruction;
034: private XPathContextMajor savedXPathContext;
035: NodeInfo node = null;
036:
037: public UnconstructedParent(ParentNodeConstructor instruction,
038: XPathContext context) {
039: this .instruction = instruction;
040: savedXPathContext = context.newContext();
041: savedXPathContext
042: .setOriginatingConstructType(Location.LAZY_EVALUATION);
043:
044: // Make a copy of all local variables. If the value of any local variable is a closure
045: // whose depth exceeds a certain threshold, we evaluate the closure eagerly to avoid
046: // creating deeply nested lists of Closures, which consume memory unnecessarily
047:
048: // We only copy the local variables if the expression has dependencies on local variables.
049: // It would be even smarter to copy only those variables that we need; but that gives
050: // diminishing returns.
051:
052: if ((instruction.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES) != 0) {
053: StackFrame localStackFrame = context.getStackFrame();
054: ValueRepresentation[] local = localStackFrame
055: .getStackFrameValues();
056: if (local != null) {
057: ValueRepresentation[] savedStackFrame = new ValueRepresentation[local.length];
058: System.arraycopy(local, 0, savedStackFrame, 0,
059: local.length);
060: savedXPathContext.setStackFrame(localStackFrame
061: .getStackFrameMap(), savedStackFrame);
062: }
063: }
064:
065: // Make a copy of the context item
066: SequenceIterator currentIterator = context.getCurrentIterator();
067: if (currentIterator != null) {
068: Item contextItem = currentIterator.current();
069: AxisIterator single = SingletonIterator
070: .makeIterator(contextItem);
071: single.next();
072: savedXPathContext.setCurrentIterator(single);
073: // we don't save position() and last() because we have no way
074: // of restoring them. Instead, we prevent lazy construction if there is a dependency
075: // on position() or last()
076: }
077: savedXPathContext.setReceiver(new SequenceOutputter());
078: }
079:
080: public XPathContext getXPathContext() {
081: return savedXPathContext;
082: }
083:
084: public ParentNodeConstructor getInstruction() {
085: return instruction;
086: }
087:
088: public PullProvider getPuller() {
089: if (node == null) {
090: VirtualTreeWalker walker = new VirtualTreeWalker(
091: instruction, savedXPathContext);
092: walker.setPipelineConfiguration(savedXPathContext
093: .getController().makePipelineConfiguration());
094: walker.setNameCode(getNameCode());
095: return walker;
096: } else {
097: return TreeWalker.makeTreeWalker(node);
098: }
099: }
100:
101: /**
102: * Method to construct the node when this is required.
103: *
104: * @throws XPathException if any failure occurs
105: */
106: void construct() throws XPathException {
107: PipelineConfiguration pipe = savedXPathContext.getController()
108: .makePipelineConfiguration();
109: PullProvider puller = getPuller();
110: puller.setPipelineConfiguration(pipe);
111: TinyBuilder builder = new TinyBuilder();
112: builder.setPipelineConfiguration(pipe);
113:
114: builder.open();
115: new PullPushCopier(puller, builder).copy();
116: builder.close();
117:
118: node = builder.getCurrentRoot();
119: }
120:
121: /**
122: * Method to construct the node when this is required.
123: * <p>
124: * Note that this may throw an UncheckedXPathException. This is because many of the methods on the
125: * NodeInfo class are exception-free; we can't throw an XPathException on these interfaces, but may need
126: * to in this case because lazy computation of expressions may throw errors.
127: *
128: * @throws UncheckedXPathException
129: */
130:
131: void tryToConstruct() {
132: try {
133: construct();
134: } catch (XPathException err) {
135: throw new UncheckedXPathException(err);
136: }
137: }
138:
139: /**
140: * Determine whether this is the same node as another node.
141: * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b).
142: * This method has the same semantics as isSameNode() in DOM Level 3, but
143: * works on Saxon NodeInfo objects rather than DOM Node objects.
144: *
145: * @param other the node to be compared with this node
146: * @return true if this NodeInfo object and the supplied NodeInfo object represent
147: * the same node in the tree.
148: */
149:
150: public boolean isSameNodeInfo(NodeInfo other) {
151: if (this == other) {
152: return true;
153: }
154: if (other instanceof UnconstructedParent) {
155: return false;
156: }
157: if (node != null) {
158: return node.isSameNodeInfo(other);
159: }
160: return false;
161: }
162:
163: /**
164: * Get the System ID for the node.
165: *
166: * @return the System Identifier of the entity in the source document
167: * containing the node, or null if not known. Note this is not the
168: * same as the base URI: the base URI can be modified by xml:base, but
169: * the system ID cannot.
170: */
171:
172: public String getSystemId() {
173: if (node == null) {
174: tryToConstruct();
175: }
176: return node.getSystemId();
177: }
178:
179: /**
180: * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
181: * in the node. This will be the same as the System ID unless xml:base has been used.
182: *
183: * @return the base URI of the node
184: */
185:
186: public String getBaseURI() {
187: if (node == null) {
188: tryToConstruct();
189: }
190: return node.getBaseURI();
191: }
192:
193: /**
194: * Get line number
195: *
196: * @return the line number of the node in its original source document; or
197: * -1 if not available
198: */
199:
200: public int getLineNumber() {
201: return -1;
202: }
203:
204: /**
205: * Determine the relative position of this node and another node, in document order.
206: * The other node will always be in the same document.
207: *
208: * @param other The other node, whose position is to be compared with this
209: * node
210: * @return -1 if this node precedes the other node, +1 if it follows the
211: * other node, or 0 if they are the same node. (In this case,
212: * isSameNode() will always return true, and the two nodes will
213: * produce the same result for generateId())
214: */
215:
216: public int compareOrder(NodeInfo other) {
217: if (node == null) {
218: tryToConstruct();
219: }
220: return node.compareOrder(other);
221: }
222:
223: /**
224: * Return the string value of the node. The interpretation of this depends on the type
225: * of node. For an element it is the accumulated character content of the element,
226: * including descendant elements.
227: *
228: * @return the string value of the node
229: */
230:
231: public String getStringValue() {
232: return getStringValueCS().toString();
233: }
234:
235: /**
236: * Get fingerprint. The fingerprint is a coded form of the expanded name
237: * of the node: two nodes
238: * with the same name code have the same namespace URI and the same local name.
239: * A fingerprint of -1 should be returned for a node with no name.
240: *
241: * @return an integer fingerprint; two nodes with the same fingerprint have
242: * the same expanded QName
243: */
244:
245: public int getFingerprint() {
246: int nc = getNameCode();
247: if (nc == -1) {
248: return -1;
249: }
250: return nc & NamePool.FP_MASK;
251: }
252:
253: /**
254: * Get the local part of the name of this node. This is the name after the ":" if any.
255: *
256: * @return the local part of the name. For an unnamed node, returns "". Unlike the DOM
257: * interface, this returns the full name in the case of a non-namespaced name.
258: */
259:
260: public String getLocalPart() {
261: return getNamePool().getLocalName(getNameCode());
262: }
263:
264: /**
265: * Get the URI part of the name of this node. This is the URI corresponding to the
266: * prefix, or the URI of the default namespace if appropriate.
267: *
268: * @return The URI of the namespace of this node. For an unnamed node,
269: * or for a node with an empty prefix, return an empty
270: * string.
271: */
272:
273: public String getURI() {
274: return getNamePool().getURI(getNameCode());
275: }
276:
277: /**
278: * Get the display name of this node. For elements and attributes this is [prefix:]localname.
279: * For unnamed nodes, it is an empty string.
280: *
281: * @return The display name of this node. For a node with no name, return
282: * an empty string.
283: */
284:
285: public String getDisplayName() {
286: return getNamePool().getDisplayName(getNameCode());
287: }
288:
289: /**
290: * Get the prefix of the name of the node. This is defined only for elements and attributes.
291: * If the node has no prefix, or for other kinds of node, return a zero-length string.
292: *
293: * @return The prefix of the name of the node.
294: */
295:
296: public String getPrefix() {
297: return getNamePool().getPrefix(getNameCode());
298: }
299:
300: /**
301: * Get the configuration
302: */
303:
304: public Configuration getConfiguration() {
305: return savedXPathContext.getConfiguration();
306: }
307:
308: /**
309: * Get the NamePool that holds the namecode for this node
310: *
311: * @return the namepool
312: */
313:
314: public NamePool getNamePool() {
315: return getConfiguration().getNamePool();
316: }
317:
318: /**
319: * Get the type annotation of this node, if any.
320: * Returns -1 for kinds of nodes that have no annotation, and for elements annotated as
321: * untyped, and attributes annotated as untypedAtomic.
322: *
323: * @return the type annotation of the node.
324: * @see net.sf.saxon.type.Type
325: */
326:
327: public int getTypeAnnotation() {
328: return StandardNames.XDT_UNTYPED;
329: }
330:
331: /**
332: * Get the NodeInfo object representing the parent of this node
333: *
334: * @return the parent of this node; null if this node has no parent
335: */
336:
337: public NodeInfo getParent() {
338: return null;
339: }
340:
341: /**
342: * Return an iteration over all the nodes reached by the given axis from this node
343: *
344: * @param axisNumber an integer identifying the axis; one of the constants
345: * defined in class net.sf.saxon.om.Axis
346: * @return an AxisIterator that scans the nodes reached by the axis in
347: * turn.
348: * @throws UnsupportedOperationException if the namespace axis is
349: * requested and this axis is not supported for this implementation.
350: * @see net.sf.saxon.om.Axis
351: */
352:
353: public AxisIterator iterateAxis(byte axisNumber) {
354: if (node == null) {
355: tryToConstruct();
356: }
357: return node.iterateAxis(axisNumber);
358: }
359:
360: /**
361: * Return an iteration over all the nodes reached by the given axis from this node
362: * that match a given NodeTest
363: *
364: * @param axisNumber an integer identifying the axis; one of the constants
365: * defined in class net.sf.saxon.om.Axis
366: * @param nodeTest A pattern to be matched by the returned nodes; nodes
367: * that do not match this pattern are not included in the result
368: * @return a NodeEnumeration that scans the nodes reached by the axis in
369: * turn.
370: * @throws UnsupportedOperationException if the namespace axis is
371: * requested and this axis is not supported for this implementation.
372: * @see net.sf.saxon.om.Axis
373: */
374:
375: public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
376: if (node == null) {
377: tryToConstruct();
378: }
379: return node.iterateAxis(axisNumber, nodeTest);
380: }
381:
382: /**
383: * Get the value of a given attribute of this node
384: *
385: * @param fingerprint The fingerprint of the attribute name
386: * @return the attribute value if it exists or null if not
387: */
388:
389: public String getAttributeValue(int fingerprint) {
390: if (node == null) {
391: tryToConstruct();
392: }
393: return node.getAttributeValue(fingerprint);
394: }
395:
396: /**
397: * Get the root node of the tree containing this node
398: *
399: * @return the NodeInfo representing the top-level ancestor of this node.
400: * This will not necessarily be a document node
401: */
402:
403: public NodeInfo getRoot() {
404: return this ;
405: }
406:
407: /**
408: * Get the root node, if it is a document node.
409: *
410: * @return the DocumentInfo representing the containing document. If this
411: * node is part of a tree that does not have a document node as its
412: * root, return null.
413: */
414:
415: public DocumentInfo getDocumentRoot() {
416: return null;
417: }
418:
419: /**
420: * Determine whether the node has any children. <br />
421: * Note: the result is equivalent to <br />
422: * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
423: *
424: * @return True if the node has one or more children
425: */
426:
427: public boolean hasChildNodes() {
428: if (node == null) {
429: tryToConstruct();
430: }
431: return node.hasChildNodes();
432: }
433:
434: /**
435: * Get a character string that uniquely identifies this node.
436: * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
437: *
438: * @return a string that uniquely identifies this node, across all
439: * documents. (Changed in Saxon 7.5. Previously this method returned
440: * an id that was unique within the current document, and the calling
441: * code prepended a document id).
442: */
443:
444: public String generateId() {
445: if (node == null) {
446: tryToConstruct();
447: }
448: return node.generateId();
449: }
450:
451: /**
452: * Get the document number of the document containing this node. For a free-standing
453: * orphan node, just return the hashcode.
454: */
455:
456: public int getDocumentNumber() {
457: if (node == null) {
458: tryToConstruct();
459: }
460: return node.getDocumentNumber();
461: }
462:
463: /**
464: * Copy this node to a given outputter
465: *
466: * @param out the Receiver to which the node should be copied
467: * @param whichNamespaces in the case of an element, controls
468: * which namespace nodes should be copied. Values are {@link #NO_NAMESPACES},
469: * {@link #LOCAL_NAMESPACES}, {@link #ALL_NAMESPACES}
470: * @param copyAnnotations indicates whether the type annotations
471: * of element and attribute nodes should be copied
472: * @param locationId If non-zero, identifies the location of the instruction
473: * that requested this copy. If zero, indicates that the location information
474: * for the original node is to be copied; in this case the Receiver must be
475: * a LocationCopier
476: * @throws net.sf.saxon.trans.XPathException
477: *
478: */
479:
480: public void copy(Receiver out, int whichNamespaces,
481: boolean copyAnnotations, int locationId)
482: throws XPathException {
483: if (node == null) {
484: if (whichNamespaces == NodeInfo.ALL_NAMESPACES
485: && copyAnnotations) {
486: PullProvider pull = new VirtualTreeWalker(instruction,
487: savedXPathContext);
488: PullPushCopier copier = new PullPushCopier(pull, out);
489: copier.copy();
490: return;
491: } else {
492: construct();
493: }
494: }
495: node.copy(out, whichNamespaces, copyAnnotations, locationId);
496: }
497:
498: /**
499: * Output all namespace declarations associated with this element. Does nothing if
500: * the node is not an element.
501: *
502: * @param out The relevant Receiver
503: * @param includeAncestors True if namespaces declared on ancestor
504: * elements must be output; false if it is known that these are
505: */
506:
507: public void sendNamespaceDeclarations(Receiver out,
508: boolean includeAncestors) throws XPathException {
509: if (node == null) {
510: try {
511: construct();
512: } catch (UncheckedXPathException e) {
513: throw e.getXPathException();
514: }
515: }
516: node.sendNamespaceDeclarations(out, includeAncestors);
517: }
518:
519: /**
520: * Get all namespace undeclarations and undeclarations defined on this element.
521: *
522: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
523: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
524: * @return An array of integers representing the namespace declarations and undeclarations present on
525: * this element. For a node other than an element, return null. Otherwise, the returned array is a
526: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
527: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
528: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
529: * The XML namespace is never included in the list. If the supplied array is larger than required,
530: * then the first unused entry will be set to -1.
531: * <p/>
532: * <p>For a node other than an element, the method returns null.</p>
533: */
534:
535: public int[] getDeclaredNamespaces(int[] buffer) {
536: if (node == null) {
537: tryToConstruct();
538: }
539: return node.getDeclaredNamespaces(buffer);
540: }
541:
542: /**
543: * Set the system identifier for this Source.
544: * <p/>
545: * <p>The system identifier is optional if the source does not
546: * get its data from a URL, but it may still be useful to provide one.
547: * The application can use a system identifier, for example, to resolve
548: * relative URIs and to include in error messages and warnings.</p>
549: *
550: * @param systemId The system identifier as a URL string.
551: */
552: public void setSystemId(String systemId) {
553: //
554: }
555:
556: /**
557: * Get the value of the item as a CharSequence. This is in some cases more efficient than
558: * the version of the method that returns a String.
559: */
560:
561: public CharSequence getStringValueCS() {
562: if (node == null) {
563: //construct();
564: try {
565: PullProvider puller = getPuller();
566: puller.next(); // assert: it's a START_DOCUMENT or START_ELEMENT
567: return puller.getStringValue();
568: } catch (XPathException e) {
569: throw new UncheckedXPathException(e);
570: }
571: }
572: return node.getStringValueCS();
573: }
574:
575: /**
576: * Get the typed value of the item
577: *
578: * @return the typed value of the item. In general this will be a sequence
579: * @throws net.sf.saxon.trans.XPathException
580: * where no typed value is available, e.g. for
581: * an element with complex content
582: */
583:
584: public SequenceIterator getTypedValue() throws XPathException {
585: if (node == null) {
586: construct();
587: }
588: return node.getTypedValue();
589: }
590:
591: /**
592: * Get the typed value. The result of this method will always be consistent with the method
593: * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
594: * more efficient, especially in the common case where the value is expected to be a singleton.
595: *
596: * @return the typed value. If requireSingleton is set to true, the result will always be an
597: * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
598: * values.
599: * @since 8.5
600: */
601:
602: public Value atomize() throws XPathException {
603: if (node == null) {
604: construct();
605: }
606: return node.atomize();
607: }
608:
609: }
610:
611: //
612: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
613: // you may not use this file except in compliance with the License. You may obtain a copy of the
614: // License at http://www.mozilla.org/MPL/
615: //
616: // Software distributed under the License is distributed on an "AS IS" basis,
617: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
618: // See the License for the specific language governing rights and limitations under the License.
619: //
620: // The Original Code is: all this file.
621: //
622: // The Initial Developer of the Original Code is Michael H. Kay.
623: //
624: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
625: //
626: // Contributor(s): none.
627: //
|