001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.sort.IntHashMap;
005: import net.sf.saxon.sort.IntIterator;
006: import net.sf.saxon.sort.IntHashSet;
007: import net.sf.saxon.expr.Expression;
008: import net.sf.saxon.expr.ExpressionTool;
009: import net.sf.saxon.instruct.Executable;
010: import net.sf.saxon.instruct.ResultDocument;
011: import net.sf.saxon.om.*;
012: import net.sf.saxon.trans.XPathException;
013: import net.sf.saxon.type.ItemType;
014: import net.sf.saxon.type.SchemaType;
015: import net.sf.saxon.value.EmptySequence;
016: import net.sf.saxon.value.StringValue;
017:
018: import java.util.*;
019:
020: /**
021: * An xsl:result-document element in the stylesheet. <BR>
022: * The xsl:result-document element takes an attribute href="filename". The filename will
023: * often contain parameters, e.g. {position()} to ensure that a different file is produced
024: * for each element instance. <BR>
025: * There is a further attribute "name" which determines the format of the
026: * output file, it identifies the name of an xsl:output element containing the output
027: * format details.
028: */
029:
030: public class XSLResultDocument extends StyleElement {
031:
032: private static final HashSet fans = new HashSet(25); // formatting attribute names
033:
034: static {
035: fans.add(StandardNames.METHOD);
036: fans.add(StandardNames.OUTPUT_VERSION);
037: fans.add(StandardNames.BYTE_ORDER_MARK);
038: fans.add(StandardNames.INDENT);
039: fans.add(StandardNames.ENCODING);
040: fans.add(StandardNames.MEDIA_TYPE);
041: fans.add(StandardNames.DOCTYPE_SYSTEM);
042: fans.add(StandardNames.DOCTYPE_PUBLIC);
043: fans.add(StandardNames.OMIT_XML_DECLARATION);
044: fans.add(StandardNames.STANDALONE);
045: fans.add(StandardNames.CDATA_SECTION_ELEMENTS);
046: fans.add(StandardNames.INCLUDE_CONTENT_TYPE);
047: fans.add(StandardNames.ESCAPE_URI_ATTRIBUTES);
048: fans.add(StandardNames.UNDECLARE_PREFIXES);
049: fans.add(StandardNames.NORMALIZATION_FORM);
050: fans.add(StandardNames.SAXON_NEXT_IN_CHAIN);
051: fans.add(StandardNames.SAXON_CHARACTER_REPRESENTATION);
052: fans.add(StandardNames.SAXON_INDENT_SPACES);
053: fans.add(StandardNames.SAXON_REQUIRE_WELL_FORMED);
054: }
055:
056: private Expression href;
057: private int format = -1; // fingerprint of required xsl:output element
058: private Expression formatExpression = null; // used when format is an AVT
059: private int validationAction = Validation.STRIP;
060: private SchemaType schemaType = null;
061: private IntHashMap serializationAttributes = new IntHashMap(10);
062:
063: /**
064: * Determine whether this node is an instruction.
065: * @return true - it is an instruction
066: */
067:
068: public boolean isInstruction() {
069: return true;
070: }
071:
072: /**
073: * Determine whether this type of element is allowed to contain a template-body
074: * @return true: yes, it may contain a template-body
075: */
076:
077: public boolean mayContainSequenceConstructor() {
078: return true;
079: }
080:
081: /**
082: * Determine the type of item returned by this instruction (only relevant if
083: * it is an instruction). Default implementation returns Type.ITEM, indicating
084: * that we don't know, it might be anything. Returns null in the case of an element
085: * such as xsl:sort or xsl:variable that can appear in a sequence constructor but
086: * contributes nothing to the result sequence.
087: * @return the item type returned
088: */
089:
090: protected ItemType getReturnedItemType() {
091: return null;
092: }
093:
094: public void prepareAttributes() throws XPathException {
095: AttributeCollection atts = getAttributeList();
096:
097: String formatAttribute = null;
098: String hrefAttribute = null;
099: String validationAtt = null;
100: String typeAtt = null;
101: String useCharacterMapsAtt = null;
102:
103: for (int a = 0; a < atts.getLength(); a++) {
104: int nc = atts.getNameCode(a);
105: String f = getNamePool().getClarkName(nc);
106: if (f == StandardNames.FORMAT) {
107: formatAttribute = atts.getValue(a).trim();
108: } else if (f == StandardNames.HREF) {
109: hrefAttribute = atts.getValue(a).trim();
110: } else if (f == StandardNames.VALIDATION) {
111: validationAtt = atts.getValue(a).trim();
112: } else if (f == StandardNames.TYPE) {
113: typeAtt = atts.getValue(a).trim();
114: } else if (f == StandardNames.USE_CHARACTER_MAPS) {
115: useCharacterMapsAtt = atts.getValue(a).trim();
116: } else if (fans.contains(f) || !(f.startsWith("{}"))) {
117: // this is a serialization attribute
118: String val = atts.getValue(a).trim();
119: Expression exp = makeAttributeValueTemplate(val);
120: serializationAttributes.put(nc & 0xfffff, exp);
121: } else {
122: checkUnknownAttribute(nc);
123: }
124: }
125:
126: if (hrefAttribute == null) {
127: //href = StringValue.EMPTY_STRING;
128: } else {
129: href = makeAttributeValueTemplate(hrefAttribute);
130: }
131:
132: if (formatAttribute != null) {
133: formatExpression = makeAttributeValueTemplate(formatAttribute);
134: if (formatExpression instanceof StringValue) {
135: try {
136: format = makeNameCode(formatAttribute.trim()) & 0xfffff;
137: } catch (NamespaceException err) {
138: compileError(err.getMessage(), "XTSE0280");
139: } catch (XPathException err) {
140: compileError(err.getMessage());
141: }
142: }
143: }
144:
145: if (validationAtt == null) {
146: validationAction = getContainingStylesheet()
147: .getDefaultValidation();
148: } else {
149: validationAction = Validation.getCode(validationAtt);
150: if (validationAction != Validation.STRIP
151: && !getConfiguration().isSchemaAware(
152: Configuration.XSLT)) {
153: compileError(
154: "To perform validation, a schema-aware XSLT processor is needed",
155: "XTSE1660");
156: }
157: if (validationAction == Validation.INVALID) {
158: compileError("Invalid value of @validation attribute",
159: "XTSE0020");
160: }
161: }
162: if (typeAtt != null) {
163: if (!getConfiguration().isSchemaAware(Configuration.XSLT)) {
164: compileError(
165: "The @type attribute is available only with a schema-aware XSLT processor",
166: "XTSE1660");
167: }
168: schemaType = getSchemaType(typeAtt);
169: }
170:
171: if (typeAtt != null && validationAtt != null) {
172: compileError(
173: "The @validation and @type attributes are mutually exclusive",
174: "XTSE1505");
175: }
176:
177: if (useCharacterMapsAtt != null) {
178: String s = XSLOutput.prepareCharacterMaps(this ,
179: useCharacterMapsAtt, new Properties());
180: serializationAttributes.put(getNamePool().allocate("", "",
181: StandardNames.USE_CHARACTER_MAPS), new StringValue(
182: s));
183: }
184: }
185:
186: public void validate() throws XPathException {
187: checkWithinTemplate();
188: if (href != null
189: && !getPreparedStylesheet().getConfiguration()
190: .isAllowExternalFunctions()) {
191: compileError("xsl:result-document is disabled when extension functions are disabled");
192: }
193: href = typeCheck("href", href);
194: formatExpression = typeCheck("format", formatExpression);
195: IntIterator it = serializationAttributes.keyIterator();
196:
197: while (it.hasNext()) {
198: int fp = it.next();
199: String displayName = getNamePool().getDisplayName(fp);
200: final Expression exp1 = (Expression) serializationAttributes
201: .get(fp);
202: final Expression exp2 = typeCheck(displayName, exp1);
203: if (exp1 != exp2) {
204: serializationAttributes.put(fp, exp2);
205: }
206: }
207:
208: }
209:
210: public Expression compile(Executable exec) throws XPathException {
211: Properties globalProps;
212: if (formatExpression == null) {
213: try {
214: globalProps = getPrincipalStylesheet()
215: .gatherOutputProperties(format);
216: } catch (XPathException err) {
217: compileError(
218: "Named output format has not been defined",
219: "XTSE1460");
220: return null;
221: }
222: } else {
223: globalProps = new Properties();
224: getPrincipalStylesheet().setNeedsDynamicOutputProperties(
225: true);
226: }
227:
228: // If no serialization method was specified, we can work it out statically if the
229: // first contained instruction is a literal result element. This saves effort at run-time.
230:
231: int key = getNamePool().allocate("", "", StandardNames.METHOD);
232: if (formatExpression == null
233: && globalProps.getProperty("method") == null
234: && serializationAttributes.get(key) == null) {
235: AxisIterator kids = iterateAxis(Axis.CHILD);
236: NodeInfo first = (NodeInfo) kids.next();
237: if (first instanceof LiteralResultElement) {
238: if (first.getFingerprint() == getNamePool().allocate(
239: "", NamespaceConstant.XHTML, "html")) {
240: if (backwardsCompatibleModeIsEnabled()) {
241: globalProps.setProperty("method", "xml");
242: } else {
243: globalProps.setProperty("method", "xhtml");
244: }
245: } else if (first.getLocalPart()
246: .equalsIgnoreCase("html")
247: && first.getURI().equals("")) {
248: globalProps.setProperty("method", "html");
249: } else {
250: globalProps.setProperty("method", "xml");
251: }
252: }
253: }
254:
255: Properties localProps = new Properties();
256:
257: IntHashSet fixed = new IntHashSet(10);
258: boolean needsNamespaceContext = (formatExpression != null);
259: NameChecker checker = exec.getConfiguration().getNameChecker();
260: for (IntIterator it = serializationAttributes.keyIterator(); it
261: .hasNext();) {
262: int fp = it.next();
263: Expression exp = (Expression) serializationAttributes
264: .get(fp);
265: if (exp instanceof StringValue) {
266: String s = ((StringValue) exp).getStringValue();
267: String lname = getNamePool().getLocalName(fp);
268: String uri = getNamePool().getURI(fp);
269: try {
270: ResultDocument.setSerializationProperty(localProps,
271: uri, lname, s, getStaticContext()
272: .getNamespaceResolver(), false,
273: checker);
274: fixed.add(fp);
275: } catch (XPathException e) {
276: if (NamespaceConstant.SAXON.equals(e
277: .getErrorCodeNamespace())) {
278: compileWarning(e.getMessage(), e
279: .getErrorCodeLocalPart());
280: } else {
281: compileError(e);
282: }
283: }
284: } else {
285: String lname = getNamePool().getLocalName(fp);
286: if (lname.equals("method")
287: || lname.equals("cdata-section-elements")) {
288: needsNamespaceContext = true;
289: }
290: }
291: }
292: for (IntIterator it = fixed.iterator(); it.hasNext();) {
293: serializationAttributes.remove(it.next());
294: }
295:
296: ResultDocument inst = new ResultDocument(globalProps,
297: localProps, href, formatExpression, getBaseURI(),
298: validationAction, schemaType, serializationAttributes,
299: (needsNamespaceContext ? getStaticContext()
300: .getNamespaceResolver() : null));
301:
302: Expression b = compileSequenceConstructor(exec,
303: iterateAxis(Axis.CHILD), true);
304: if (b == null) {
305: b = EmptySequence.getInstance();
306: }
307: inst.setContent(b);
308: ExpressionTool.makeParentReferences(inst);
309: return inst;
310: }
311:
312: }
313:
314: //
315: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
316: // you may not use this file except in compliance with the License. You may obtain a copy of the
317: // License at http://www.mozilla.org/MPL/
318: //
319: // Software distributed under the License is distributed on an "AS IS" basis,
320: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
321: // See the License for the specific language governing rights and limitations under the License.
322: //
323: // The Original Code is: all this file.
324: //
325: // The Initial Developer of the Original Code is Michael H. Kay.
326: //
327: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
328: //
329: // Additional Contributor(s): Brett Knights [brett@knightsofthenet.com]
330: //
|