001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Controller;
004: import net.sf.saxon.style.StandardNames;
005: import net.sf.saxon.event.*;
006: import net.sf.saxon.expr.Expression;
007: import net.sf.saxon.expr.StaticProperty;
008: import net.sf.saxon.expr.XPathContext;
009: import net.sf.saxon.om.Item;
010: import net.sf.saxon.om.NodeInfo;
011: import net.sf.saxon.om.Validation;
012: import net.sf.saxon.pattern.NodeKindTest;
013: import net.sf.saxon.pull.UnconstructedElement;
014: import net.sf.saxon.trans.DynamicError;
015: import net.sf.saxon.trans.XPathException;
016: import net.sf.saxon.type.ItemType;
017: import net.sf.saxon.type.ValidationException;
018: import net.sf.saxon.type.TypeHierarchy;
019:
020: /**
021: * An instruction that creates an element node. There are two subtypes, FixedElement
022: * for use where the name is known statically, and Element where it is computed
023: * dynamically. To allow use in both XSLT and XQuery, the class acts both as an
024: * Instruction and as an Expression.
025: */
026:
027: public abstract class ElementCreator extends ParentNodeConstructor {
028:
029: /**
030: * The inheritNamespaces flag indicates that the namespace nodes on the element created by this instruction
031: * are to be inherited (copied) on the children of this element. That is, if this flag is false, the child
032: * elements must carry a namespace undeclaration for all the namespaces on the parent, unless they are
033: * redeclared in some way.
034: */
035:
036: protected boolean inheritNamespaces = true;
037:
038: public ElementCreator() {
039: }
040:
041: /**
042: * Get the item type of the value returned by this instruction
043: * @return the item type
044: * @param th
045: */
046:
047: public ItemType getItemType(TypeHierarchy th) {
048: return NodeKindTest.ELEMENT;
049: }
050:
051: /**
052: * Get the static properties of this expression (other than its type). The result is
053: * bit-signficant. These properties are used for optimizations. In general, if
054: * property bit is set, it is true, but if it is unset, the value is unknown.
055: *
056: * @return a set of flags indicating static properties of this expression
057: */
058:
059: public int computeSpecialProperties() {
060: return super .computeSpecialProperties()
061: | StaticProperty.SINGLE_DOCUMENT_NODESET;
062: }
063:
064: /**
065: * Set the validation mode for the new element
066: */
067:
068: public void setValidationMode(int mode) {
069: validation = mode;
070: }
071:
072: /**
073: * Get the validation mode for the constructed element
074: */
075:
076: public int getValidationMode() {
077: return validation;
078: }
079:
080: /**
081: * Suppress validation on contained element constructors, on the grounds that the parent element
082: * is already performing validation. The default implementation does nothing.
083: */
084:
085: public void suppressValidation(int validationMode) {
086: if (validation == validationMode) {
087: setValidationMode(Validation.PRESERVE);
088: }
089: }
090:
091: public abstract int getNameCode(XPathContext context)
092: throws XPathException;
093:
094: /**
095: * Callback to output namespace nodes for the new element.
096: * @param context The execution context
097: * @param receiver the Receiver where the namespace nodes are to be written
098: * @throws net.sf.saxon.trans.XPathException
099: */
100:
101: protected abstract void outputNamespaceNodes(XPathContext context,
102: Receiver receiver) throws XPathException;
103:
104: /**
105: * Callback to get a list of the intrinsic namespaces that need to be generated for the element.
106: * The result is an array of namespace codes, the codes either occupy the whole array or are
107: * terminated by a -1 entry. A result of null is equivalent to a zero-length array.
108: */
109:
110: public int[] getActiveNamespaces() throws XPathException {
111: return null;
112: }
113:
114: /**
115: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
116: * This method indicates which of these methods is prefered. For instructions this is the process() method.
117: */
118:
119: public int getImplementationMethod() {
120: return Expression.PROCESS_METHOD | Expression.EVALUATE_METHOD;
121: }
122:
123: /**
124: * Evaluate the instruction to produce a new element node. This method is typically used when there is
125: * a parent element or document in a result tree, to which the new element is added.
126: * @param context
127: * @return null (this instruction never returns a tail call)
128: * @throws XPathException
129: */
130: public TailCall processLeavingTail(XPathContext context)
131: throws XPathException {
132:
133: try {
134:
135: int nameCode = getNameCode(context);
136: int typeCode = (validation == Validation.PRESERVE ? StandardNames.XS_ANY_TYPE
137: : StandardNames.XDT_UNTYPED);
138:
139: Controller controller = context.getController();
140: XPathContext c2 = context;
141: SequenceReceiver out = context.getReceiver();
142:
143: Receiver validator = controller.getConfiguration()
144: .getElementValidator(out, nameCode, locationId,
145: getSchemaType(), validation,
146: controller.getNamePool());
147:
148: if (validator != out) {
149: c2 = context.newMinorContext();
150: c2.setOrigin(this );
151: out = new TreeReceiver(validator);
152: out.setPipelineConfiguration(controller
153: .makePipelineConfiguration());
154: c2.setReceiver(out);
155: }
156: int properties = (inheritNamespaces ? 0
157: : ReceiverOptions.DISINHERIT_NAMESPACES);
158: out
159: .startElement(nameCode, typeCode, locationId,
160: properties);
161:
162: // output the required namespace nodes via a callback
163:
164: outputNamespaceNodes(c2, out);
165:
166: // process subordinate instructions to generate attributes and content
167: content.process(c2);
168:
169: // output the element end tag (which will fail if validation fails)
170: out.endElement();
171: return null;
172:
173: } catch (DynamicError e) {
174: if (e.getXPathContext() == null) {
175: e.setXPathContext(context);
176: }
177: if (e.getLocator() == null) {
178: e.setLocator(this );
179: }
180: throw e;
181: }
182: }
183:
184: /**
185: * Evaluate the constructor, returning the constructed element node. If lazy construction
186: * mode is in effect, then an UnconstructedParent object is returned instead.
187: */
188:
189: public Item evaluateItem(XPathContext context)
190: throws XPathException {
191: //
192: if (isLazyConstruction()) {
193: UnconstructedElement e = new UnconstructedElement(this ,
194: context);
195: // The name code is evaluated eagerly. It's usually already known, and it's usually needed.
196: // Evaluating it now removes problems with error handling.
197: e.setNameCode(getNameCode(context));
198: return e;
199: } else {
200: return constructElement(context);
201: }
202: }
203:
204: /**
205: * Construct the element node as a free-standing (parentless) node in a tiny tree
206: * @param context
207: * @return the constructed element node
208: * @throws XPathException
209: */
210: private NodeInfo constructElement(XPathContext context)
211: throws XPathException {
212: try {
213: Controller controller = context.getController();
214: XPathContext c2 = context.newMinorContext();
215: c2.setOrigin(this );
216: SequenceReceiver old = c2.getReceiver();
217: SequenceOutputter seq;
218: if (old instanceof SequenceWriter
219: && !((SequenceWriter) old).hasOpenNodes()) {
220: // Reuse the current SequenceOutputter if possible. This enables the construction of
221: // a single TinyTree to hold a sequence of parentless elements (a forest).
222: seq = (SequenceOutputter) old;
223: } else {
224: seq = new SequenceOutputter(1);
225: }
226: seq.setPipelineConfiguration(controller
227: .makePipelineConfiguration());
228:
229: int nameCode = getNameCode(c2);
230: int typeCode = (validation == Validation.PRESERVE ? StandardNames.XS_ANY_TYPE
231: : StandardNames.XDT_UNTYPED);
232:
233: Receiver validator = controller.getConfiguration()
234: .getElementValidator(seq, nameCode, locationId,
235: getSchemaType(), validation,
236: controller.getNamePool());
237:
238: SequenceReceiver ini = seq;
239: if (validator == seq) {
240: c2.setTemporaryReceiver(seq);
241: } else {
242: TreeReceiver tr = new TreeReceiver(validator);
243: tr.setPipelineConfiguration(seq
244: .getPipelineConfiguration());
245: c2.setReceiver(tr);
246: ini = tr;
247: }
248:
249: ini.open();
250: int properties = (inheritNamespaces ? 0
251: : ReceiverOptions.DISINHERIT_NAMESPACES);
252: ini
253: .startElement(nameCode, typeCode, locationId,
254: properties);
255:
256: // output the namespace nodes for the new element
257: outputNamespaceNodes(c2, ini);
258:
259: content.process(c2);
260:
261: ini.endElement();
262: ini.close();
263:
264: // the constructed element is the first and only item in the sequence
265: return (NodeInfo) seq.popLastItem();
266:
267: } catch (XPathException err) {
268: if (err instanceof ValidationException) {
269: ((ValidationException) err).setSourceLocator(this );
270: ((ValidationException) err).setSystemId(getSystemId());
271: }
272: if (err.getLocator() == null) {
273: err.setLocator(this );
274: }
275: if (err instanceof DynamicError
276: && ((DynamicError) err).getXPathContext() == null) {
277: ((DynamicError) err).setXPathContext(context);
278: }
279: throw err;
280: }
281: }
282: }
283:
284: //
285: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
286: // you may not use this file except in compliance with the License. You may obtain a copy of the
287: // License at http://www.mozilla.org/MPL/
288: //
289: // Software distributed under the License is distributed on an "AS IS" basis,
290: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
291: // See the License for the specific language governing rights and limitations under the License.
292: //
293: // The Original Code is: all this file.
294: //
295: // The Initial Developer of the Original Code is Michael H. Kay.
296: //
297: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
298: //
299: // Contributor(s): none.
300: //
|