001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.PreparedStylesheet;
005: import net.sf.saxon.expr.Expression;
006: import net.sf.saxon.expr.ExpressionTool;
007: import net.sf.saxon.expr.ComputedExpression;
008: import net.sf.saxon.instruct.*;
009: import net.sf.saxon.om.*;
010: import net.sf.saxon.trace.Location;
011: import net.sf.saxon.trans.SaxonErrorCode;
012: import net.sf.saxon.trans.StaticError;
013: import net.sf.saxon.trans.XPathException;
014: import net.sf.saxon.tree.DocumentImpl;
015: import net.sf.saxon.tree.TreeBuilder;
016: import net.sf.saxon.type.SchemaType;
017: import net.sf.saxon.value.EmptySequence;
018:
019: import javax.xml.transform.TransformerException;
020:
021: /**
022: * This class represents a literal result element in the style sheet
023: * (typically an HTML element to be output). <br>
024: * It is also used to represent unknown top-level elements, which are ignored.
025: */
026:
027: public class LiteralResultElement extends StyleElement {
028:
029: private int resultNameCode;
030: private int[] attributeNames;
031: private Expression[] attributeValues;
032: // private boolean[] attributeChecked;
033: private int numberOfAttributes;
034: private boolean toplevel;
035: private int[] namespaceCodes;
036: private AttributeSet[] attributeSets;
037: private SchemaType schemaType = null;
038: private int validation = Validation.STRIP;
039: private boolean inheritNamespaces = true;
040:
041: /**
042: * Determine whether this type of element is allowed to contain a template-body
043: * @return true: yes, it may contain a template-body
044: */
045:
046: public boolean mayContainSequenceConstructor() {
047: return true;
048: }
049:
050: /**
051: * Specify that this is an instruction
052: */
053:
054: public boolean isInstruction() {
055: return true;
056: }
057:
058: /**
059: * Process the attribute list
060: */
061:
062: public void prepareAttributes() throws XPathException {
063:
064: // Process the values of all attributes. At this stage we deal with attribute
065: // values (especially AVTs), but we do not apply namespace aliasing to the
066: // attribute names.
067:
068: int num = attributeList.getLength();
069:
070: if (num == 0) {
071: numberOfAttributes = 0;
072: } else {
073: NamePool namePool = getNamePool();
074: attributeNames = new int[num];
075: attributeValues = new Expression[num];
076: numberOfAttributes = 0;
077:
078: for (int i = 0; i < num; i++) {
079:
080: int anameCode = attributeList.getNameCode(i);
081: short attURIcode = namePool.getURICode(anameCode);
082:
083: if (attURIcode == NamespaceConstant.XSLT_CODE) {
084: int fp = anameCode & 0xfffff;
085:
086: if (fp == StandardNames.XSL_USE_ATTRIBUTE_SETS) {
087: // deal with this later
088: } else if (fp == StandardNames.XSL_DEFAULT_COLLATION) {
089: // already dealt with
090: } else if (fp == StandardNames.XSL_EXTENSION_ELEMENT_PREFIXES) {
091: // already dealt with
092: } else if (fp == StandardNames.XSL_EXCLUDE_RESULT_PREFIXES) {
093: // already dealt with
094: } else if (fp == StandardNames.XSL_VERSION) {
095: // already dealt with
096: } else if (fp == StandardNames.XSL_XPATH_DEFAULT_NAMESPACE) {
097: // already dealt with
098: } else if (fp == StandardNames.XSL_TYPE) {
099: // deal with this later
100: } else if (fp == StandardNames.XSL_USE_WHEN) {
101: // already dealt with
102: } else if (fp == StandardNames.XSL_VALIDATION) {
103: // deal with this later
104: } else if (fp == StandardNames.XSL_INHERIT_NAMESPACES) {
105: String inheritAtt = attributeList.getValue(i);
106: if (inheritAtt.equals("yes")) {
107: inheritNamespaces = true;
108: } else if (inheritAtt.equals("no")) {
109: inheritNamespaces = false;
110: } else {
111: compileError(
112: "The xsl:inherit-namespaces attribute has permitted values (yes, no)",
113: "XTSE0020");
114: }
115: } else {
116: compileError("Unknown XSL attribute "
117: + namePool.getDisplayName(anameCode),
118: "XTSE0010");
119: }
120: } else {
121: attributeNames[numberOfAttributes] = anameCode;
122: Expression exp = makeAttributeValueTemplate(attributeList
123: .getValue(i));
124: attributeValues[numberOfAttributes] = exp;
125: numberOfAttributes++;
126: }
127: }
128:
129: // now shorten the arrays if necessary. This is necessary if there are [xsl:]-prefixed
130: // attributes that weren't copied into the arrays.
131:
132: if (numberOfAttributes < attributeNames.length) {
133:
134: int[] attributeNames2 = new int[numberOfAttributes];
135: System.arraycopy(attributeNames, 0, attributeNames2, 0,
136: numberOfAttributes);
137: attributeNames = attributeNames2;
138:
139: Expression[] attributeValues2 = new Expression[numberOfAttributes];
140: System.arraycopy(attributeValues, 0, attributeValues2,
141: 0, numberOfAttributes);
142: attributeValues = attributeValues2;
143: }
144: }
145: }
146:
147: /**
148: * Validate that this node is OK
149: */
150:
151: public void validate() throws XPathException {
152:
153: toplevel = (getParent() instanceof XSLStylesheet);
154:
155: resultNameCode = getNameCode();
156:
157: NamePool namePool = getNamePool();
158: short elementURICode = namePool.getURICode(resultNameCode);
159:
160: if (toplevel) {
161: // A top-level element can never be a "real" literal result element,
162: // but this class gets used for unknown elements found at the top level
163:
164: if (elementURICode == 0) {
165: compileError(
166: "Top level elements must have a non-null namespace URI",
167: "XTSE0010");
168: }
169: } else {
170:
171: // Build the list of output namespace nodes
172:
173: // Up to 5.3.1 we listed the namespace nodes associated with this element that were not also
174: // associated with an ancestor literal result element (because those will already
175: // have been output). Unfortunately this isn't true if the namespace was present on an outer
176: // LRE, and was excluded at that level using exclude-result-prefixes, and is now used in an
177: // inner element: bug 5.3.1/006
178:
179: // We now use a different optimisation: if
180: // (a) this LRE has a parent that is also an LRE, and
181: // (b) this LRE has no namespace declarations of its own, and
182: // (c) this element name is in the same namespace as its parent, and
183: // (d) the parent doesn't specify xsl:inherit-namespaces="no"
184: // (e) there are no attributes in a non-null namespace,
185: // then we don't need to output any namespace declarations to the result.
186:
187: boolean optimizeNS = false;
188: NodeInfo parent = getParent();
189: if ((parent instanceof LiteralResultElement)
190: && ((LiteralResultElement) parent).inheritNamespaces
191: && (namespaceList == null || namespaceList.length == 0)
192: && (elementURICode == namePool
193: .getURICode(getParent().getFingerprint()))) {
194: optimizeNS = true;
195: }
196: if (optimizeNS) {
197: for (int a = 0; a < attributeList.getLength(); a++) {
198: if (((attributeList.getNameCode(a) >> 20) & 0xff) != 0) { // prefix != ""
199: optimizeNS = false;
200: break;
201: }
202: }
203: }
204:
205: if (optimizeNS) {
206: namespaceCodes = NodeInfo.EMPTY_NAMESPACE_LIST;
207: } else {
208: namespaceCodes = getInScopeNamespaceCodes();
209: }
210:
211: // apply any aliases required to create the list of output namespaces
212:
213: XSLStylesheet sheet = getPrincipalStylesheet();
214:
215: if (sheet.hasNamespaceAliases()) {
216: for (int i = 0; i < namespaceCodes.length; i++) {
217: // System.err.println("Examining namespace " + namespaceCodes[i]);
218: short scode = (short) (namespaceCodes[i] & 0xffff);
219: int ncode = sheet.getNamespaceAlias(scode);
220: if (ncode != -1 && (ncode & 0xffff) != scode) {
221: // apply the namespace alias. Change in 7.3: use the prefix associated
222: // with the new namespace, not the old prefix.
223: namespaceCodes[i] = ncode;
224: }
225: }
226:
227: // determine if there is an alias for the namespace of the element name
228:
229: int ercode = sheet.getNamespaceAlias(elementURICode);
230: if ((ercode & 0xffff) != elementURICode) {
231: resultNameCode = namePool.allocate(namePool
232: .getPrefixFromNamespaceCode(ercode),
233: namePool.getURIFromNamespaceCode(ercode),
234: getLocalPart());
235: }
236: }
237:
238: // deal with special attributes
239:
240: String useAttSets = getAttributeValue(StandardNames.XSL_USE_ATTRIBUTE_SETS);
241: if (useAttSets != null) {
242: attributeSets = getAttributeSets(useAttSets, null);
243: }
244:
245: String type = getAttributeValue(StandardNames.XSL_TYPE);
246: if (type != null) {
247: if (!getConfiguration().isSchemaAware(
248: Configuration.XSLT)) {
249: compileError(
250: "The xsl:type attribute is available only with a schema-aware XSLT processor",
251: "XTSE1660");
252: }
253: schemaType = getSchemaType(type);
254: }
255:
256: String validate = getAttributeValue(StandardNames.XSL_VALIDATION);
257: if (validate != null) {
258: validation = Validation.getCode(validate);
259: if (validation != Validation.STRIP
260: && !getConfiguration().isSchemaAware(
261: Configuration.XSLT)) {
262: compileError(
263: "To perform validation, a schema-aware XSLT processor is needed",
264: "XTSE1660");
265: }
266: if (validation == Validation.INVALID) {
267: compileError(
268: "Invalid value for xsl:validation. "
269: + "Permitted values are (strict, lax, preserve, strip)",
270: "XTSE0020");
271: }
272: } else {
273: validation = getContainingStylesheet()
274: .getDefaultValidation();
275: }
276:
277: // establish the names to be used for all the output attributes;
278: // also type-check the AVT expressions
279:
280: short attributeURIs[] = new short[numberOfAttributes];
281: if (numberOfAttributes > 0) {
282:
283: for (int i = 0; i < numberOfAttributes; i++) {
284:
285: int anameCode = attributeNames[i];
286: int alias = anameCode;
287: short attURIcode = namePool.getURICode(anameCode);
288:
289: if (attURIcode != 0) { // attribute has a namespace prefix
290: int newNSCode = sheet
291: .getNamespaceAlias(attURIcode);
292: if ((newNSCode & 0xffff) != attURIcode) {
293: attURIcode = (short) (newNSCode & 0xffff);
294: alias = namePool
295: .allocate(
296: namePool
297: .getPrefixFromNamespaceCode(newNSCode),
298: namePool
299: .getURIFromNamespaceCode(newNSCode),
300: attributeList
301: .getLocalName(i));
302: }
303: }
304:
305: attributeNames[i] = alias;
306: attributeURIs[i] = attURIcode;
307: attributeValues[i] = typeCheck(namePool
308: .getDisplayName(alias), attributeValues[i]);
309: }
310: }
311:
312: // remove any namespaces that are on the exclude-result-prefixes list, unless it is
313: // the namespace of the element or an attribute
314:
315: int numberExcluded = 0;
316: for (int n = 0; n < namespaceCodes.length; n++) {
317: short uricode = (short) (namespaceCodes[n] & 0xffff);
318: if (isExcludedNamespace(uricode)
319: && !sheet.isAliasResultNamespace(uricode)) {
320: // exclude it from the output namespace list
321: namespaceCodes[n] = -1;
322: numberExcluded++;
323: }
324: }
325:
326: int count = namespaceCodes.length - numberExcluded;
327: int[] newNamespaceCodes = new int[count];
328: count = 0;
329: for (int i = 0; i < namespaceCodes.length; i++) {
330: if (namespaceCodes[i] != -1) {
331: newNamespaceCodes[count++] = namespaceCodes[i];
332: }
333: }
334: namespaceCodes = newNamespaceCodes;
335: }
336: }
337:
338: /**
339: * Validate the children of this node, recursively. Overridden for top-level
340: * data elements.
341: */
342:
343: protected void validateChildren() throws XPathException {
344: if (!toplevel) {
345: super .validateChildren();
346: }
347: }
348:
349: /**
350: * Translate a namecode in the stylesheet namepool to a namecode in the target namepool
351: */
352:
353: // private int translate(int oldNameCode) {
354: // NamePool oldPool = getNamePool();
355: // NamePool newPool = getTargetNamePool();
356: // String prefix = oldPool.getPrefix(oldNameCode);
357: // String uri = oldPool.getURI(oldNameCode);
358: // String localName = oldPool.getLocalName(oldNameCode);
359: // return newPool.allocate(prefix, uri, localName);
360: // }
361: /**
362: * Process the literal result element by copying it to the result tree
363: */
364:
365: public Expression compile(Executable exec) throws XPathException {
366: // top level elements in the stylesheet are ignored
367: if (toplevel)
368: return null;
369:
370: FixedElement inst = new FixedElement(resultNameCode,
371: namespaceCodes, inheritNamespaces, schemaType,
372: validation);
373:
374: Expression content = compileSequenceConstructor(exec,
375: iterateAxis(Axis.CHILD), true);
376: if (content instanceof ComputedExpression) {
377: ((ComputedExpression) content).setParentExpression(inst);
378: }
379:
380: if (numberOfAttributes > 0) {
381: for (int i = attributeNames.length - 1; i >= 0; i--) {
382: FixedAttribute att = new FixedAttribute(
383: attributeNames[i], Validation.STRIP, null,
384: StandardNames.XDT_UNTYPED_ATOMIC);
385: try {
386: att.setSelect(attributeValues[i]);
387: } catch (XPathException err) {
388: compileError(err);
389: }
390: att.setLocationId(allocateLocationId(getSystemId(),
391: getLineNumber()));
392: att.setParentExpression(inst);
393: ExpressionTool.makeParentReferences(att);
394: if (content == null) {
395: content = att;
396: } else {
397: content = Block.makeBlock(att, content);
398: if (content instanceof ComputedExpression) {
399: ((ComputedExpression) content)
400: .setLocationId(allocateLocationId(
401: getSystemId(), getLineNumber()));
402: }
403: }
404: }
405: }
406:
407: if (attributeSets != null) {
408: UseAttributeSets use = new UseAttributeSets(attributeSets);
409: if (content == null) {
410: content = use;
411: } else {
412: content = Block.makeBlock(use, content);
413: if (content instanceof ComputedExpression) {
414: ((ComputedExpression) content)
415: .setLocationId(allocateLocationId(
416: getSystemId(), getLineNumber()));
417: }
418: }
419: }
420:
421: if (content == null) {
422: content = EmptySequence.getInstance();
423: }
424: inst.setContentExpression(content);
425:
426: ExpressionTool.makeParentReferences(inst);
427: return inst;
428: }
429:
430: /**
431: * Make a top-level literal result element into a stylesheet. This implements
432: * the "Literal Result Element As Stylesheet" facility.
433: */
434:
435: public DocumentImpl makeStylesheet(PreparedStylesheet pss,
436: StyleNodeFactory nodeFactory) throws XPathException {
437:
438: // the implementation grafts the LRE node onto a containing xsl:template and
439: // xsl:stylesheet
440:
441: NamePool pool = getNamePool();
442: String xslPrefix = getPrefixForURI(NamespaceConstant.XSLT);
443: if (xslPrefix == null) {
444: String message;
445: if (getLocalPart().equals("stylesheet")
446: || getLocalPart().equals("transform")) {
447: if (getPrefixForURI(NamespaceConstant.MICROSOFT_XSL) != null) {
448: message = "Saxon is not able to process Microsoft's WD-xsl dialect";
449: } else {
450: message = "Namespace for stylesheet element should be "
451: + NamespaceConstant.XSLT;
452: }
453: } else {
454: message = "The supplied file does not appear to be a stylesheet";
455: }
456: StaticError err = new StaticError(message);
457: err.setLocator(this );
458: err.setErrorCode(SaxonErrorCode.SXIN0004);
459: try {
460: pss.reportError(err);
461: } catch (TransformerException err2) {
462: }
463: throw err;
464:
465: }
466:
467: // check there is an xsl:version attribute (it's mandatory), and copy
468: // it to the new xsl:stylesheet element
469:
470: String version = getAttributeValue(StandardNames.XSL_VERSION);
471: if (version == null) {
472: StaticError err = new StaticError(
473: "Simplified stylesheet: xsl:version attribute is missing");
474: err.setErrorCode("XTSE0150");
475: err.setLocator(this );
476: try {
477: pss.reportError(err);
478: } catch (TransformerException err2) {
479: }
480: throw err;
481: }
482:
483: try {
484: TreeBuilder builder = new TreeBuilder();
485: builder.setPipelineConfiguration(pss.getConfiguration()
486: .makePipelineConfiguration());
487: builder.setNodeFactory(nodeFactory);
488: builder.setSystemId(this .getSystemId());
489:
490: builder.open();
491: builder.startDocument(0);
492:
493: int st = StandardNames.XSL_STYLESHEET;
494: builder.startElement(st, StandardNames.XDT_UNTYPED, 0, 0);
495: builder.namespace(NamespaceConstant.XSLT_CODE, 0);
496: builder.attribute(pool.allocate("", "", "version"),
497: StandardNames.XDT_UNTYPED_ATOMIC, version, 0, 0);
498: builder.startContent();
499:
500: int te = StandardNames.XSL_TEMPLATE;
501: builder.startElement(te, StandardNames.XDT_UNTYPED, 0, 0);
502: builder.attribute(pool.allocate("", "", "match"),
503: StandardNames.XDT_UNTYPED_ATOMIC, "/", 0, 0);
504: builder.startContent();
505:
506: builder.graftElement(this );
507:
508: builder.endElement();
509: builder.endElement();
510: builder.endDocument();
511: builder.close();
512:
513: return (DocumentImpl) builder.getCurrentRoot();
514: } catch (XPathException err) {
515: //TransformerConfigurationException e = new TransformerConfigurationException(err);
516: err.setLocator(this );
517: throw err;
518: }
519:
520: }
521:
522: /**
523: * Get the type of construct. This will be a constant in
524: * class {@link net.sf.saxon.trace.Location}. This method is part of the
525: * {@link net.sf.saxon.trace.InstructionInfo} interface
526: */
527:
528: public int getConstructType() {
529: return Location.LITERAL_RESULT_ELEMENT;
530: }
531:
532: /**
533: * Get a name identifying the object of the expression, for example a function name, template name,
534: * variable name, key name, element name, etc. This is used only where the name is known statically.
535: * If there is no name, the value will be -1.
536: */
537:
538: public int getObjectNameCode() {
539: return resultNameCode;
540: }
541:
542: /**
543: * Get the value of a particular property of the instruction. This is part of the
544: * {@link net.sf.saxon.trace.InstructionInfo} interface for run-time tracing and debugging. The properties
545: * available include all the attributes of the source instruction (named by the attribute name):
546: * these are all provided as string values.
547: * @param name The name of the required property
548: * @return The value of the requested property, or null if the property is not available
549: */
550:
551: public Object getProperty(String name) {
552: if (name.equals("name")) {
553: return getDisplayName();
554: }
555: return null;
556: }
557:
558: }
559: //
560: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
561: // you may not use this file except in compliance with the License. You may obtain a copy of the
562: // License at http://www.mozilla.org/MPL/
563: //
564: // Software distributed under the License is distributed on an "AS IS" basis,
565: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
566: // See the License for the specific language governing rights and limitations under the License.
567: //
568: // The Original Code is: all this file.
569: //
570: // The Initial Developer of the Original Code is Michael H. Kay.
571: //
572: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
573: //
574: // Contributor(s): none.
575: //
|