001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Controller;
005: import net.sf.saxon.value.Whitespace;
006: import net.sf.saxon.event.*;
007: import net.sf.saxon.instruct.LocalParam;
008: import net.sf.saxon.instruct.ParameterSet;
009: import net.sf.saxon.instruct.RegexIterator;
010: import net.sf.saxon.instruct.Template;
011: import net.sf.saxon.om.*;
012: import net.sf.saxon.sort.CodepointCollator;
013: import net.sf.saxon.sort.GroupIterator;
014: import net.sf.saxon.trace.InstructionInfoProvider;
015: import net.sf.saxon.trans.DynamicError;
016: import net.sf.saxon.trans.Mode;
017: import net.sf.saxon.trans.XPathException;
018: import net.sf.saxon.type.SchemaType;
019:
020: import javax.xml.transform.Result;
021: import java.util.Comparator;
022: import java.util.Properties;
023:
024: /**
025: * This class represents a minor change in the dynamic context in which an XPath expression is evaluated:
026: * a "major context" object allows all aspects of the dynamic context to change, whereas
027: * a "minor context" only allows changes to the focus and the destination for push output.
028: */
029:
030: public class XPathContextMinor implements XPathContext {
031:
032: Controller controller;
033: SequenceIterator currentIterator;
034: int last = -1;
035: SequenceReceiver currentReceiver;
036: boolean isTemporaryDestination = false;
037: XPathContext caller = null;
038: Object origin = null;
039:
040: /**
041: * Private Constructor
042: */
043:
044: protected XPathContextMinor() {
045: }
046:
047: /**
048: * Construct a new context as a copy of another. The new context is effectively added
049: * to the top of a stack, and contains a pointer to the previous context
050: */
051:
052: public XPathContextMajor newContext() {
053: return XPathContextMajor.newContext(this );
054: }
055:
056: public XPathContextMinor newMinorContext() {
057: XPathContextMinor c = new XPathContextMinor();
058: c.controller = controller;
059: c.caller = this ;
060: c.currentIterator = currentIterator;
061: c.currentReceiver = currentReceiver;
062: c.last = last;
063: c.isTemporaryDestination = isTemporaryDestination;
064: return c;
065: }
066:
067: /**
068: * Set the calling XPathContext
069: */
070:
071: public void setCaller(XPathContext caller) {
072: this .caller = caller;
073: }
074:
075: /**
076: * Construct a new context without copying (used for the context in a function call)
077: */
078:
079: public XPathContextMajor newCleanContext() {
080: XPathContextMajor c = new XPathContextMajor(this
081: .getController());
082: c.setCaller(this );
083: return c;
084: }
085:
086: /**
087: * Get the XSLT-specific part of the context
088: */
089:
090: public XPathContextMajor.XSLTContext getXSLTContext() {
091: return getCaller().getXSLTContext();
092: }
093:
094: /**
095: * Get the local parameters for the current template call.
096: * @return the supplied parameters
097: */
098:
099: public ParameterSet getLocalParameters() {
100: return getCaller().getLocalParameters();
101: }
102:
103: /**
104: * Get the tunnel parameters for the current template call.
105: * @return the supplied tunnel parameters
106: */
107:
108: public ParameterSet getTunnelParameters() {
109: return getCaller().getTunnelParameters();
110: }
111:
112: /**
113: * Set the creating expression (for use in diagnostics). The origin is generally set to "this" by the
114: * object that creates the new context. It's up to the debugger to determine whether this information
115: * is useful. Where possible, the object will be an {@link InstructionInfoProvider}, allowing information
116: * about the calling instruction to be obtained.
117: */
118:
119: public void setOrigin(InstructionInfoProvider expr) {
120: origin = expr;
121: }
122:
123: /**
124: * Set the type of creating expression (for use in diagnostics). When a new context is created, either
125: * this method or {@link #setOrigin} should be called.
126: * @param loc The originating location: the argument must be one of the integer constants in class
127: * {@link net.sf.saxon.trace.Location}
128: */
129:
130: public void setOriginatingConstructType(int loc) {
131: origin = new Integer(loc);
132: }
133:
134: /**
135: * Get the type of location from which this context was created.
136: */
137:
138: public int getOriginatingConstructType() {
139: if (origin instanceof InstructionInfoProvider) {
140: return ((InstructionInfoProvider) origin)
141: .getInstructionInfo().getConstructType();
142: } else {
143: return ((Integer) origin).intValue();
144: }
145: }
146:
147: /**
148: * Get information about the creating expression or other construct.
149: */
150:
151: public InstructionInfoProvider getOrigin() {
152: if (origin instanceof InstructionInfoProvider) {
153: return (InstructionInfoProvider) origin;
154: } else {
155: return null;
156: }
157: }
158:
159: /**
160: * Get the Controller. May return null when running outside XSLT or XQuery
161: */
162:
163: public final Controller getController() {
164: return controller;
165: }
166:
167: /**
168: * Get the Configuration
169: */
170:
171: public final Configuration getConfiguration() {
172: return controller.getConfiguration();
173: }
174:
175: /**
176: * Get the Name Pool
177: */
178:
179: public final NamePool getNamePool() {
180: return controller.getNamePool();
181: }
182:
183: /**
184: * Get a NameChecker for checking names against the XML 1.0 or XML 1.1 specification as appropriate
185: */
186:
187: public final NameChecker getNameChecker() {
188: return controller.getConfiguration().getNameChecker();
189: }
190:
191: /**
192: * Get the calling XPathContext (the next one down the stack). This will be null if unknown, or
193: * if the bottom of the stack has been reached.
194: */
195:
196: public final XPathContext getCaller() {
197: return caller;
198: }
199:
200: /**
201: * Set a new sequence iterator.
202: */
203:
204: public void setCurrentIterator(SequenceIterator iter) {
205: currentIterator = iter;
206: last = 0;
207: }
208:
209: /**
210: * Get the current iterator.
211: * This encapsulates the context item, context position, and context size.
212: * @return the current iterator, or null if there is no current iterator
213: * (which means the context item, position, and size are undefined).
214: */
215:
216: public final SequenceIterator getCurrentIterator() {
217: return currentIterator;
218: }
219:
220: /**
221: * Get the context position (the position of the context item)
222: * @return the context position (starting at one)
223: * @throws DynamicError if the context position is undefined
224: */
225:
226: public final int getContextPosition() throws DynamicError {
227: if (currentIterator == null) {
228: DynamicError e = new DynamicError(
229: "The context position is currently undefined");
230: e.setXPathContext(this );
231: e.setErrorCode("XPDY0002");
232: throw e;
233: }
234: return currentIterator.position();
235: }
236:
237: /**
238: * Get the context item
239: * @return the context item, or null if the context item is undefined
240: */
241:
242: public final Item getContextItem() {
243: if (currentIterator == null) {
244: return null;
245: }
246: return currentIterator.current();
247: }
248:
249: /**
250: * Get the context size (the position of the last item in the current node list)
251: * @return the context size
252: * @throws XPathException if the context position is undefined
253: */
254:
255: public final int getLast() throws XPathException {
256: if (last > 0) {
257: return last;
258: }
259: if (currentIterator == null) {
260: DynamicError e = new DynamicError(
261: "The context size is currently undefined");
262: e.setXPathContext(this );
263: e.setErrorCode("XPDY0002");
264: throw e;
265: }
266: if ((currentIterator.getProperties() & SequenceIterator.LAST_POSITION_FINDER) == 0) {
267: SequenceIterator another = currentIterator.getAnother();
268: last = 0;
269: while (another.next() != null) {
270: last++;
271: }
272: return last;
273: } else {
274: last = ((LastPositionFinder) currentIterator)
275: .getLastPosition();
276: return last;
277: }
278: }
279:
280: /**
281: * Determine whether the context position is the same as the context size
282: * that is, whether position()=last()
283: */
284:
285: public final boolean isAtLast() throws XPathException {
286: if ((currentIterator.getProperties() & SequenceIterator.LOOKAHEAD) != 0) {
287: return !((LookaheadIterator) currentIterator).hasNext();
288: }
289: return getContextPosition() == getLast();
290: }
291:
292: /**
293: * Get a named collation
294: */
295:
296: public final Comparator getCollation(String name)
297: throws XPathException {
298: if (name.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
299: return CodepointCollator.getInstance();
300: }
301: Comparator collation = null;
302: if (controller != null) {
303: collation = controller.getExecutable().getNamedCollation(
304: name);
305:
306: if (collation == null) {
307: Configuration config = controller.getConfiguration();
308: collation = config.getCollationURIResolver().resolve(
309: name, null, config);
310: //collation = CollationFactory.makeCollationFromURI(name, getController().getConfiguration());
311: }
312: }
313: if (collation == null) {
314: DynamicError e = new DynamicError("Unknown collation "
315: + name);
316: e.setXPathContext(this );
317: throw e;
318: }
319: return collation;
320: }
321:
322: /**
323: * Get the default collation
324: */
325:
326: public final Comparator getDefaultCollation() {
327: if (controller != null) {
328: return controller.getExecutable().getDefaultCollation();
329: } else {
330: return CodepointCollator.getInstance();
331: }
332: }
333:
334: /**
335: * Get a reference to the local stack frame for variables. Note that it's
336: * the caller's job to make a local copy of this. This is used for creating
337: * a Closure containing a retained copy of the variables for delayed evaluation.
338: * @return array of variables.
339: */
340:
341: public StackFrame getStackFrame() {
342: return getCaller().getStackFrame();
343: }
344:
345: /**
346: * Get the value of a local variable, identified by its slot number
347: */
348:
349: public ValueRepresentation evaluateLocalVariable(int slotnumber) {
350: return getCaller().evaluateLocalVariable(slotnumber);
351: }
352:
353: /**
354: * Set the value of a local variable, identified by its slot number
355: */
356:
357: public void setLocalVariable(int slotnumber,
358: ValueRepresentation value) {
359: getCaller().setLocalVariable(slotnumber, value);
360: }
361:
362: /**
363: * Set a new output destination, supplying the output format details. <BR>
364: * This affects all further output until resetOutputDestination() is called. Note that
365: * it is the caller's responsibility to close the Writer after use.
366: *
367: * @exception XPathException if any dynamic error occurs; and
368: * specifically, if an attempt is made to switch to a final output
369: * destination while writing a temporary tree or sequence
370: * @param props properties defining the output format
371: * @param result Details of the new output destination
372: * @param isFinal true if the destination is a final result tree
373: * (either the principal output or a secondary result tree); false if
374: * it is a temporary tree, xsl:attribute, etc.
375: */
376:
377: public void changeOutputDestination(Properties props,
378: Result result, boolean isFinal, int validation,
379: SchemaType schemaType) throws XPathException {
380: if (isFinal && isTemporaryDestination) {
381: DynamicError err = new DynamicError(
382: "Cannot switch to a final result destination while writing a temporary tree");
383: err.setErrorCode("XTDE1480");
384: throw err;
385: }
386: if (isFinal) {
387: validation |= Validation.VALIDATE_OUTPUT;
388: } else {
389: isTemporaryDestination = true;
390: }
391: PipelineConfiguration pipe = controller
392: .makePipelineConfiguration();
393: pipe.setSerializing(isFinal);
394: ComplexContentOutputter out = new ComplexContentOutputter();
395: out.setPipelineConfiguration(pipe);
396:
397: if (props == null) {
398: props = new Properties();
399: }
400:
401: Receiver receiver = ResultWrapper.getReceiver(result, pipe,
402: props);
403:
404: // add a validator to the pipeline if required
405:
406: receiver = controller.getConfiguration().getDocumentValidator(
407: receiver, receiver.getSystemId(),
408: controller.getNamePool(), validation, Whitespace.NONE,
409: schemaType);
410:
411: // add a filter to remove duplicate namespaces
412:
413: NamespaceReducer ne = new NamespaceReducer();
414: ne.setUnderlyingReceiver(receiver);
415: ne.setPipelineConfiguration(pipe);
416: out.setReceiver(ne);
417:
418: out.open();
419: currentReceiver = out;
420: }
421:
422: /**
423: * Set the output destination to write to a sequence. <BR>
424: * This affects all further output until resetOutputDestination() is called.
425: *
426: * @param out The SequenceReceiver to be used
427: */
428:
429: public void setTemporaryReceiver(SequenceReceiver out) {
430: isTemporaryDestination = true;
431: currentReceiver = out;
432: }
433:
434: /**
435: * Change the Receiver to which output is written
436: */
437:
438: public void setReceiver(SequenceReceiver receiver) {
439: currentReceiver = receiver;
440: }
441:
442: /**
443: * Get the Receiver to which output is currently being written.
444: *
445: * @return the current Receiver
446: */
447: public final SequenceReceiver getReceiver() {
448: return currentReceiver;
449: }
450:
451: /**
452: * Use local parameter. This is called when a local xsl:param element is processed.
453: * If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
454: * Otherwise the method returns false, so the xsl:param default will be evaluated
455: * @param fingerprint The fingerprint of the parameter name
456: * @param binding The XSLParam element to bind its value to
457: * @param isTunnel True if a tunnel parameter is required, else false
458: * @return true if a parameter of this name was supplied, false if not
459: */
460:
461: public boolean useLocalParameter(int fingerprint,
462: LocalParam binding, boolean isTunnel) throws XPathException {
463: return getCaller().useLocalParameter(fingerprint, binding,
464: isTunnel);
465: }
466:
467: /**
468: * Get the current mode.
469: * @return the current mode
470: */
471:
472: public Mode getCurrentMode() {
473: return getCaller().getCurrentMode();
474: }
475:
476: /**
477: * Get the current template. This is used to support xsl:apply-imports
478: *
479: * @return the current template
480: */
481:
482: public Template getCurrentTemplate() {
483: return getCaller().getCurrentTemplate();
484: }
485:
486: /**
487: * Get the current group iterator. This supports the current-group() and
488: * current-grouping-key() functions in XSLT 2.0
489: * @return the current grouped collection
490: */
491:
492: public GroupIterator getCurrentGroupIterator() {
493: return getCaller().getCurrentGroupIterator();
494: }
495:
496: /**
497: * Get the current regex iterator. This supports the functionality of the regex-group()
498: * function in XSLT 2.0.
499: * @return the current regular expressions iterator
500: */
501:
502: public RegexIterator getCurrentRegexIterator() {
503: return getCaller().getCurrentRegexIterator();
504: }
505:
506: /**
507: * Get the implicit timezone, as a positive or negative offset from UTC in minutes.
508: * The range is -14hours to +14hours
509: */
510:
511: public final int getImplicitTimezone() {
512: return getConfiguration().getImplicitTimezone();
513: }
514: }
515:
516: //
517: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
518: // you may not use this file except in compliance with the License. You may obtain a copy of the
519: // License at http://www.mozilla.org/MPL/
520: //
521: // Software distributed under the License is distributed on an "AS IS" basis,
522: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
523: // See the License for the specific language governing rights and limitations under the License.
524: //
525: // The Original Code is: all this file.
526: //
527: // The Initial Developer of the Original Code is Michael H. Kay.
528: //
529: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
530: //
531: // Contributor(s): none.
532: //
|