001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.event.Receiver;
004: import net.sf.saxon.expr.*;
005: import net.sf.saxon.om.NamePool;
006: import net.sf.saxon.om.QNameException;
007: import net.sf.saxon.om.Validation;
008: import net.sf.saxon.pattern.CombinedNodeTest;
009: import net.sf.saxon.pattern.ContentTypeTest;
010: import net.sf.saxon.pattern.NameTest;
011: import net.sf.saxon.style.StandardNames;
012: import net.sf.saxon.trace.InstructionInfo;
013: import net.sf.saxon.trace.Location;
014: import net.sf.saxon.trans.StaticError;
015: import net.sf.saxon.trans.XPathException;
016: import net.sf.saxon.type.*;
017: import net.sf.saxon.value.StringValue;
018:
019: import java.io.PrintStream;
020: import java.util.Iterator;
021:
022: /**
023: * An instruction that creates an element node whose name is known statically.
024: * Used for literal results elements in XSLT, for direct element constructors
025: * in XQuery, and for xsl:element in cases where the name and namespace are
026: * known statically.
027: */
028:
029: public class FixedElement extends ElementCreator {
030:
031: private int nameCode;
032: protected int[] namespaceCodes = null;
033: private ItemType itemType;
034:
035: /**
036: * Create an instruction that creates a new element node
037: * @param nameCode Represents the name of the element node
038: * @param namespaceCodes List of namespaces to be added to the element node.
039: * May be null if none are required.
040: * @param inheritNamespaces true if the children of this element are to inherit its namespaces
041: * @param schemaType Type annotation for the new element node
042: */
043: public FixedElement(int nameCode, int[] namespaceCodes,
044: boolean inheritNamespaces, SchemaType schemaType,
045: int validation) {
046: this .nameCode = nameCode;
047: this .namespaceCodes = namespaceCodes;
048: this .inheritNamespaces = inheritNamespaces;
049: setSchemaType(schemaType);
050: this .validation = validation;
051: }
052:
053: public InstructionInfo getInstructionInfo() {
054: InstructionDetails details = (InstructionDetails) super
055: .getInstructionInfo();
056: details.setConstructType(Location.LITERAL_RESULT_ELEMENT);
057: details.setObjectNameCode(nameCode);
058: return details;
059: }
060:
061: /**
062: * Simplify an expression. This performs any static optimization (by rewriting the expression
063: * as a different expression).
064: *
065: * @return the simplified expression
066: * @throws net.sf.saxon.trans.XPathException
067: * if an error is discovered during expression rewriting
068: */
069:
070: public Expression simplify(StaticContext env) throws XPathException {
071: setLazyConstruction(env.getConfiguration()
072: .isLazyConstructionMode());
073: if (getSchemaType() == null) {
074: if (validation == Validation.STRICT) {
075: SchemaDeclaration decl = env.getConfiguration()
076: .getElementDeclaration(nameCode & 0xfffff);
077: if (decl == null) {
078: StaticError err = new StaticError(
079: "There is no global element declaration for "
080: + env.getNamePool().getDisplayName(
081: nameCode)
082: + ", so strict validation will fail");
083: err.setIsTypeError(true);
084: err.setLocator(this );
085: throw err;
086: }
087: setSchemaType(decl.getType());
088: itemType = new CombinedNodeTest(new NameTest(
089: Type.ELEMENT, nameCode, env.getNamePool()),
090: Token.INTERSECT, new ContentTypeTest(
091: Type.ELEMENT, getSchemaType(), env
092: .getConfiguration()));
093: getSchemaType().analyzeContentExpression(content,
094: Type.ELEMENT, env);
095: SchemaType xsiType = getXSIType(env);
096: if (xsiType != null) {
097: xsiType.analyzeContentExpression(content,
098: Type.ELEMENT, env);
099: try {
100: xsiType.isTypeDerivationOK(getSchemaType(), 0);
101: } catch (SchemaException e) {
102: throw new StaticError(
103: "The specified xsi:type "
104: + xsiType.getDescription()
105: + " is not validly derived from the required type "
106: + getSchemaType()
107: .getDescription());
108: }
109: }
110: } else if (validation == Validation.LAX) {
111: SchemaDeclaration decl = env.getConfiguration()
112: .getElementDeclaration(nameCode & 0xfffff);
113: if (decl == null) {
114: env
115: .issueWarning(
116: "There is no global element declaration for "
117: + env.getNamePool()
118: .getDisplayName(
119: nameCode)
120: + ", so lax validation has no effect",
121: this );
122: itemType = new CombinedNodeTest(
123: new NameTest(Type.ELEMENT, nameCode, env
124: .getNamePool()),
125: Token.INTERSECT,
126: new ContentTypeTest(
127: Type.ELEMENT,
128: BuiltInSchemaFactory
129: .getSchemaType(StandardNames.XDT_UNTYPED),
130: env.getConfiguration()));
131: } else {
132: setSchemaType(decl.getType());
133: itemType = new CombinedNodeTest(new NameTest(
134: Type.ELEMENT, nameCode, env.getNamePool()),
135: Token.INTERSECT, new ContentTypeTest(
136: Type.ELEMENT, getSchemaType(), env
137: .getConfiguration()));
138: getSchemaType().analyzeContentExpression(content,
139: Type.ELEMENT, env);
140: }
141: } else if (validation == Validation.PRESERVE) {
142: // we know the result will be an element of type xs:anyType
143: itemType = new CombinedNodeTest(
144: new NameTest(Type.ELEMENT, nameCode, env
145: .getNamePool()),
146: Token.INTERSECT,
147: new ContentTypeTest(
148: Type.ELEMENT,
149: BuiltInSchemaFactory
150: .getSchemaType(StandardNames.XS_ANY_TYPE),
151: env.getConfiguration()));
152: } else {
153: // we know the result will be an untyped element
154: itemType = new CombinedNodeTest(
155: new NameTest(Type.ELEMENT, nameCode, env
156: .getNamePool()),
157: Token.INTERSECT,
158: new ContentTypeTest(
159: Type.ELEMENT,
160: BuiltInSchemaFactory
161: .getSchemaType(StandardNames.XDT_UNTYPED),
162: env.getConfiguration()));
163: }
164: } else {
165: itemType = new CombinedNodeTest(new NameTest(Type.ELEMENT,
166: nameCode, env.getNamePool()), Token.INTERSECT,
167: new ContentTypeTest(Type.ELEMENT, getSchemaType(),
168: env.getConfiguration()));
169: getSchemaType().analyzeContentExpression(content,
170: Type.ELEMENT, env);
171: }
172: return super .simplify(env);
173: }
174:
175: /**
176: * Get the type of the item returned by this instruction
177: * @return the item type
178: * @param th
179: */
180: public ItemType getItemType(TypeHierarchy th) {
181: if (itemType == null) {
182: return super .getItemType(th);
183: }
184: return itemType;
185: }
186:
187: /**
188: * Callback from the superclass ElementCreator to get the nameCode
189: * for the element name
190: * @param context The evaluation context (not used)
191: * @return the name code for the element name
192: */
193:
194: public int getNameCode(XPathContext context) {
195: return nameCode;
196: }
197:
198: /**
199: * Determine whether the element constructor creates a fixed xsi:type attribute, and if so, return the
200: * relevant type.
201: * @return the type denoted by the constructor's xsi:type attribute if there is one.
202: * Return null if there is no xsi:type attribute, or if the value of the xsi:type
203: * attribute is a type that is not statically known (this is allowed)
204: * @throws XPathException if there is an xsi:type attribute and its value is not a QName.
205: */
206:
207: public SchemaType getXSIType(StaticContext env)
208: throws XPathException {
209: if (content instanceof FixedAttribute) {
210: return testForXSIType((FixedAttribute) content, env);
211: } else if (content instanceof Block) {
212: Iterator iter = content.iterateSubExpressions();
213: while (iter.hasNext()) {
214: Expression exp = (Expression) iter.next();
215: if (exp instanceof FixedAttribute) {
216: SchemaType type = testForXSIType(
217: (FixedAttribute) exp, env);
218: if (type != null) {
219: return type;
220: }
221: }
222: }
223: return null;
224: } else {
225: return null;
226: }
227: }
228:
229: private SchemaType testForXSIType(FixedAttribute fat,
230: StaticContext env) throws XPathException {
231: int att = fat.evaluateNameCode(null) & NamePool.FP_MASK;
232: if (att == StandardNames.XSI_TYPE) {
233: Expression attValue = fat.getSelect();
234: if (attValue instanceof StringValue) {
235: try {
236: NamePool pool = env.getNamePool();
237: String[] parts = env.getConfiguration()
238: .getNameChecker().getQNameParts(
239: ((StringValue) attValue)
240: .getStringValue());
241: // The only namespace bindings we can trust are those declared on this element
242: int uriCode = -1;
243: for (int n = 0; n < namespaceCodes.length; n++) {
244: String prefix = pool
245: .getPrefixFromNamespaceCode(namespaceCodes[n]);
246: if (prefix.equals(parts[0])) {
247: uriCode = namespaceCodes[n] & 0xffff;
248: break;
249: }
250: }
251: if (uriCode == -1) {
252: return null;
253: }
254: String uri = pool
255: .getURIFromURICode((short) uriCode);
256: int typefp = pool.allocate(parts[0], uri, parts[1])
257: & NamePool.FP_MASK;
258: SchemaType type = env.getConfiguration()
259: .getSchemaType(typefp);
260: return type;
261: } catch (QNameException e) {
262: throw new StaticError(e.getMessage());
263: }
264: }
265: }
266: return null;
267: }
268:
269: /**
270: * Check that any elements and attributes constructed or returned by this expression are acceptable
271: * in the content model of a given complex type. It's always OK to say yes, since the check will be
272: * repeated at run-time. The process of checking element and attribute constructors against the content
273: * model of a complex type also registers the type of content expected of those constructors, so the
274: * static validation can continue recursively.
275: */
276:
277: public void checkPermittedContents(SchemaType parentType,
278: StaticContext env, boolean whole) throws XPathException {
279: if (parentType instanceof SimpleType) {
280: StaticError err = new StaticError(
281: "Element "
282: + env.getNamePool()
283: .getDisplayName(nameCode)
284: + " is not permitted here: the containing element is of simple type "
285: + parentType.getDescription());
286: err.setIsTypeError(true);
287: err.setLocator(this );
288: throw err;
289: } else if (((ComplexType) parentType).isSimpleContent()) {
290: StaticError err = new StaticError(
291: "Element "
292: + env.getNamePool()
293: .getDisplayName(nameCode)
294: + " is not permitted here: the containing element has a complex type with simple content");
295: err.setIsTypeError(true);
296: err.setLocator(this );
297: throw err;
298: }
299: SchemaType type;
300: try {
301: type = ((ComplexType) parentType)
302: .getElementParticleType(nameCode & 0xfffff);
303: } catch (SchemaException e) {
304: throw new StaticError(e);
305: }
306: if (type == null) {
307: StaticError err = new StaticError(
308: "Element "
309: + env.getNamePool()
310: .getDisplayName(nameCode)
311: + " is not permitted in the content model of the complex type "
312: + parentType.getDescription());
313: err.setIsTypeError(true);
314: err.setLocator(this );
315: throw err;
316: }
317: if (type instanceof AnyType) {
318: return;
319: }
320:
321: try {
322: content.checkPermittedContents(type, env, true);
323: } catch (XPathException e) {
324: if (e.getLocator() == null || e.getLocator() == e) {
325: e.setLocator(this );
326: }
327: throw e;
328: }
329: }
330:
331: /**
332: * Callback from the superclass ElementCreator to output the namespace nodes
333: * @param context The evaluation context (not used)
334: * @param out The receiver to handle the output
335: */
336:
337: protected void outputNamespaceNodes(XPathContext context,
338: Receiver out) throws XPathException {
339: if (namespaceCodes != null) {
340: for (int i = 0; i < namespaceCodes.length; i++) {
341: out.namespace(namespaceCodes[i], 0);
342: }
343: }
344: }
345:
346: /**
347: * Callback to get a list of the intrinsic namespaces that need to be generated for the element.
348: * The result is an array of namespace codes, the codes either occupy the whole array or are
349: * terminated by a -1 entry. A result of null is equivalent to a zero-length array.
350: */
351:
352: public int[] getActiveNamespaces() throws XPathException {
353: return namespaceCodes;
354: }
355:
356: /**
357: * Display this instruction as an expression, for diagnostics
358: */
359:
360: public void display(int level, NamePool pool, PrintStream out) {
361: out.println(ExpressionTool.indent(level) + "element ");
362: out.println(ExpressionTool.indent(level + 1)
363: + "name "
364: + (pool == null ? nameCode + "" : pool
365: .getDisplayName(nameCode)));
366: out.println(ExpressionTool.indent(level + 1) + "content");
367: content.display(level + 1, pool, out);
368: }
369: }
370:
371: //
372: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
373: // you may not use this file except in compliance with the License. You may obtain a copy of the
374: // License at http://www.mozilla.org/MPL/
375: //
376: // Software distributed under the License is distributed on an "AS IS" basis,
377: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
378: // See the License for the specific language governing rights and limitations under the License.
379: //
380: // The Original Code is: all this file.
381: //
382: // The Initial Developer of the Original Code is Michael H. Kay.
383: //
384: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
385: //
386: // Contributor(s): none.
387: //
|