001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Controller;
005: import net.sf.saxon.event.Receiver;
006: import net.sf.saxon.expr.*;
007: import net.sf.saxon.om.*;
008: import net.sf.saxon.pattern.ContentTypeTest;
009: import net.sf.saxon.pattern.NodeKindTest;
010: import net.sf.saxon.style.StandardNames;
011: import net.sf.saxon.trans.DynamicError;
012: import net.sf.saxon.trans.StaticError;
013: import net.sf.saxon.trans.XPathException;
014: import net.sf.saxon.type.*;
015: import net.sf.saxon.value.QNameValue;
016: import net.sf.saxon.value.StringValue;
017: import net.sf.saxon.value.SequenceType;
018: import net.sf.saxon.value.AtomicValue;
019:
020: import java.io.PrintStream;
021: import java.util.ArrayList;
022: import java.util.Iterator;
023:
024: /**
025: * An instruction representing an xsl:element element in an XSLT stylesheet,
026: * or a computed element constructor in XQuery. (In both cases, if the element name
027: * is expressed as a compile-time expression, then a FixedElement instruction
028: * is used instead.)
029: * @see FixedElement
030: */
031:
032: public class ComputedElement extends ElementCreator {
033:
034: private Expression elementName;
035: private Expression namespace = null;
036: private NamespaceResolver nsContext;
037: private boolean allowNameAsQName;
038: private ItemType itemType;
039:
040: /**
041: * Create an instruction that creates a new element node
042: *
043: * @param elementName Expression that evaluates to produce the name of the
044: * element node as a lexical QName
045: * @param namespace Expression that evaluates to produce the namespace URI of
046: * the element node. Set to null if the namespace is to be deduced from the prefix
047: * of the elementName.
048: * @param nsContext Saved copy of the static namespace context for the instruction.
049: * Can be set to null if namespace is supplied.
050: * @param schemaType The required schema type for the content
051: * @param allowQName
052: */
053: public ComputedElement(Expression elementName,
054: Expression namespace, NamespaceResolver nsContext,
055: SchemaType schemaType, int validation,
056: boolean inheritNamespaces, boolean allowQName) {
057: this .elementName = elementName;
058: this .namespace = namespace;
059: this .nsContext = nsContext;
060: setSchemaType(schemaType);
061: this .validation = validation;
062: this .inheritNamespaces = inheritNamespaces;
063: this .allowNameAsQName = allowQName;
064: adoptChildExpression(elementName);
065: adoptChildExpression(namespace);
066: }
067:
068: public Expression simplify(StaticContext env) throws XPathException {
069: elementName = elementName.simplify(env);
070: if (namespace != null) {
071: namespace = namespace.simplify(env);
072: }
073: Configuration config = env.getConfiguration();
074: setLazyConstruction(config.isLazyConstructionMode());
075:
076: if (getSchemaType() != null) {
077: itemType = new ContentTypeTest(Type.ELEMENT,
078: getSchemaType(), config);
079: getSchemaType().analyzeContentExpression(content,
080: Type.ELEMENT, env);
081: } else if (validation == Validation.STRIP
082: || !config.isSchemaAware(Configuration.XML_SCHEMA)) {
083: itemType = new ContentTypeTest(Type.ELEMENT,
084: BuiltInSchemaFactory
085: .getSchemaType(StandardNames.XDT_UNTYPED),
086: config);
087: } else {
088: // paradoxically, we know less about the type if validation="strict" is specified!
089: // We know that it won't be untyped, but we have no way of representing that.
090: itemType = NodeKindTest.ELEMENT;
091: }
092: return super .simplify(env);
093: }
094:
095: public Expression typeCheck(StaticContext env,
096: ItemType contextItemType) throws XPathException {
097: elementName = elementName.typeCheck(env, contextItemType);
098: adoptChildExpression(elementName);
099: RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION,
100: "element/name", 0, null);
101: role.setSourceLocator(this );
102: if (allowNameAsQName) {
103: // Can only happen in XQuery
104: elementName = TypeChecker.staticTypeCheck(elementName,
105: SequenceType.SINGLE_ATOMIC, false, role, env);
106: } else {
107: elementName = TypeChecker.staticTypeCheck(elementName,
108: SequenceType.SINGLE_STRING, false, role, env);
109: }
110: if (namespace != null) {
111: namespace = namespace.typeCheck(env, contextItemType);
112: adoptChildExpression(namespace);
113:
114: role = new RoleLocator(RoleLocator.INSTRUCTION,
115: "attribute/namespace", 0, null);
116: role.setSourceLocator(this );
117: namespace = TypeChecker.staticTypeCheck(namespace,
118: SequenceType.SINGLE_STRING, false, role, env);
119: }
120: return super .typeCheck(env, contextItemType);
121: }
122:
123: /**
124: * Get the item type of the value returned by this instruction
125: *
126: * @return the item type
127: * @param th
128: */
129:
130: public ItemType getItemType(TypeHierarchy th) {
131: if (itemType == null) {
132: return super .getItemType(th);
133: }
134: return itemType;
135: }
136:
137: public Iterator iterateSubExpressions() {
138: ArrayList list = new ArrayList(8);
139: list.add(content);
140: list.add(elementName);
141: if (namespace != null) {
142: list.add(namespace);
143: }
144: return list.iterator();
145: }
146:
147: /**
148: * Offer promotion for subexpressions. The offer will be accepted if the subexpression
149: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
150: * By default the offer is not accepted - this is appropriate in the case of simple expressions
151: * such as constant values and variable references where promotion would give no performance
152: * advantage. This method is always called at compile time.
153: *
154: * @param offer details of the offer, for example the offer to move
155: * expressions that don't depend on the context to an outer level in
156: * the containing expression
157: * @throws net.sf.saxon.trans.XPathException if any error is detected
158: */
159:
160: protected void promoteInst(PromotionOffer offer)
161: throws XPathException {
162: elementName = doPromotion(elementName, offer);
163: if (namespace != null) {
164: namespace = doPromotion(namespace, offer);
165: }
166: super .promoteInst(offer);
167: }
168:
169: /**
170: * Check that any elements and attributes constructed or returned by this expression are acceptable
171: * in the content model of a given complex type. It's always OK to say yes, since the check will be
172: * repeated at run-time. The process of checking element and attribute constructors against the content
173: * model of a complex type also registers the type of content expected of those constructors, so the
174: * static validation can continue recursively.
175: */
176:
177: public void checkPermittedContents(SchemaType parentType,
178: StaticContext env, boolean whole) throws XPathException {
179: if (parentType instanceof SimpleType) {
180: StaticError err = new StaticError(
181: "Elements are not permitted here: the containing element has the simple type "
182: + parentType.getDescription());
183: err.setIsTypeError(true);
184: err.setLocator(this );
185: throw err;
186: } else if (((ComplexType) parentType).isSimpleContent()) {
187: StaticError err = new StaticError(
188: "Elements are not permitted here: the containing element has a complex type with simple content");
189: err.setIsTypeError(true);
190: err.setLocator(this );
191: throw err;
192: }
193: // NOTE: we could in principle check that if all the elements permitted in the content of the parentType
194: // themselves have a simple type (not uncommon, perhaps) then this element must not have element content.
195: }
196:
197: /**
198: * Callback from the superclass ElementCreator to get the nameCode
199: * for the element name
200: *
201: * @param context The evaluation context (not used)
202: * @return the name code for the element name
203: */
204:
205: public int getNameCode(XPathContext context) throws XPathException,
206: XPathException {
207:
208: Controller controller = context.getController();
209: NamePool pool = controller.getNamePool();
210:
211: String prefix;
212: String localName;
213: String uri;
214:
215: // name needs to be evaluated at run-time
216: AtomicValue nameValue = (AtomicValue) elementName
217: .evaluateItem(context);
218: nameValue = nameValue.getPrimitiveValue();
219: if (nameValue instanceof StringValue) { // which includes UntypedAtomic
220: // this will always be the case in XSLT
221: CharSequence rawName = nameValue.getStringValueCS();
222: try {
223: String[] parts = controller.getConfiguration()
224: .getNameChecker().getQNameParts(rawName);
225: prefix = parts[0];
226: localName = parts[1];
227: } catch (QNameException err) {
228: DynamicError err1 = new DynamicError(
229: "Invalid element name. " + err.getMessage(),
230: this );
231: err1.setErrorCode((isXSLT(context) ? "XTDE0820"
232: : "XQDY0074"));
233: err1.setXPathContext(context);
234: throw dynamicError(this , err1, context);
235: }
236: } else if (nameValue instanceof QNameValue && allowNameAsQName) {
237: // this is allowed in XQuery
238: localName = ((QNameValue) nameValue).getLocalName();
239: uri = ((QNameValue) nameValue).getNamespaceURI();
240: namespace = new StringValue(uri);
241: prefix = ((QNameValue) nameValue).getPrefix();
242: } else {
243: DynamicError err = new DynamicError(
244: "Computed element name has incorrect type");
245: err
246: .setErrorCode((isXSLT(context) ? "XTDE0820"
247: : "XPTY0004"));
248: err.setIsTypeError(true);
249: err.setXPathContext(context);
250: throw dynamicError(this , err, context);
251: }
252:
253: if (namespace == null) {
254: uri = nsContext.getURIForPrefix(prefix, true);
255: if (uri == null) {
256: DynamicError err = new DynamicError(
257: "Undeclared prefix in element name: " + prefix,
258: this );
259: err.setErrorCode((isXSLT(context) ? "XTDE0830"
260: : "XQDY0074"));
261: err.setXPathContext(context);
262: throw dynamicError(this , err, context);
263: }
264:
265: } else {
266: uri = namespace.evaluateAsString(context);
267: if (uri.equals("")) {
268: // there is a special rule for this case in the specification;
269: // we force the element to go in the null namespace
270: prefix = "";
271: }
272: if (prefix.equals("xmlns")) {
273: // this isn't a legal prefix so we mustn't use it
274: prefix = "x-xmlns";
275: }
276: }
277:
278: return pool.allocate(prefix, uri, localName);
279:
280: }
281:
282: /**
283: * Callback to output namespace nodes for the new element.
284: *
285: * @param context The execution context
286: * @param out the Receiver where the namespace nodes are to be written
287: * @throws XPathException
288: */
289: protected void outputNamespaceNodes(XPathContext context,
290: Receiver out) throws XPathException {
291: // do nothing
292: }
293:
294: /**
295: * Get the name of this instruction for diagnostic and tracing purposes
296: */
297:
298: public int getInstructionNameCode() {
299: return StandardNames.XSL_ELEMENT;
300: }
301:
302: /**
303: * Display this instruction as an expression, for diagnostics
304: */
305:
306: public void display(int level, NamePool pool, PrintStream out) {
307: out.println(ExpressionTool.indent(level) + "element ");
308: out.println(ExpressionTool.indent(level + 1) + "name");
309: elementName.display(level + 2, pool, out);
310: out.println(ExpressionTool.indent(level + 1) + "content");
311: content.display(level + 1, pool, out);
312: }
313: }
314:
315: //
316: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
317: // you may not use this file except in compliance with the License. You may obtain a copy of the
318: // License at http://www.mozilla.org/MPL/
319: //
320: // Software distributed under the License is distributed on an "AS IS" basis,
321: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
322: // See the License for the specific language governing rights and limitations under the License.
323: //
324: // The Original Code is: all this file.
325: //
326: // The Initial Developer of the Original Code is Michael H. Kay.
327: //
328: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
329: //
330: // Contributor(s): none.
331: //
|