KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > instruct > ResultDocument


1 package net.sf.saxon.instruct;
2
3 import net.sf.saxon.Controller;
4 import net.sf.saxon.OutputURIResolver;
5 import net.sf.saxon.event.SaxonOutputKeys;
6 import net.sf.saxon.event.SequenceReceiver;
7 import net.sf.saxon.event.StandardOutputResolver;
8 import net.sf.saxon.expr.*;
9 import net.sf.saxon.om.*;
10 import net.sf.saxon.pattern.NoNodeTest;
11 import net.sf.saxon.style.StandardNames;
12 import net.sf.saxon.trans.DynamicError;
13 import net.sf.saxon.trans.XPathException;
14 import net.sf.saxon.trans.SaxonErrorCode;
15 import net.sf.saxon.type.ItemType;
16 import net.sf.saxon.type.SchemaType;
17 import net.sf.saxon.value.Value;
18
19 import javax.xml.transform.OutputKeys JavaDoc;
20 import javax.xml.transform.Result JavaDoc;
21 import javax.xml.transform.TransformerException JavaDoc;
22 import javax.xml.transform.dom.DOMResult JavaDoc;
23 import javax.xml.transform.sax.SAXResult JavaDoc;
24 import javax.xml.transform.stream.StreamResult JavaDoc;
25 import java.io.PrintStream JavaDoc;
26 import java.io.File JavaDoc;
27 import java.util.*;
28
29 /**
30  * The compiled form of an xsl:result-document element in the stylesheet.
31  * <p>
32  * The xsl:result-document element takes an attribute HREF="filename". The filename will
33  * often contain parameters, e.g. {position()} to ensure that a different file is produced
34  * for each element instance.
35  * <p>
36  * There is a further attribute "format" which determines the format of the
37  * output file, it identifies the name of an xsl:output element containing the output
38  * format details. In addition, individual serialization properties may be specified as attributes.
39  * These are attribute value templates, so they may need to be computed at run-time.
40  */

41
42 public class ResultDocument extends Instruction {
43
44     private Expression href;
45     private Expression content;
46     private Properties outputProperties;
47     private String JavaDoc baseURI; // needed only for saxon:next-in-chain
48
private int validationAction;
49     private SchemaType schemaType;
50     private HashMap serializationAttributes;
51     private NamespaceResolver nsResolver;
52
53     public ResultDocument(Properties outputProperties, // defines the fixed properties
54
Expression href,
55                           String JavaDoc baseURI,
56                           int validationAction,
57                           SchemaType schemaType,
58                           HashMap serializationAttributes, // defines computed properties only
59
NamespaceResolver nsResolver) {
60         this.outputProperties = outputProperties;
61         this.href = href;
62         this.baseURI = baseURI;
63         this.validationAction = validationAction;
64         this.schemaType = schemaType;
65         this.serializationAttributes = serializationAttributes;
66         this.nsResolver = nsResolver;
67         adoptChildExpression(href);
68         for (Iterator it = serializationAttributes.values().iterator(); it.hasNext();) {
69             adoptChildExpression((Expression) it.next());
70         }
71     }
72
73     /**
74      * Set the expression that constructs the content
75      */

76
77     public void setContent(Expression content) {
78         this.content = content;
79         adoptChildExpression(content);
80     }
81
82     /**
83      * Simplify an expression. This performs any static optimization (by rewriting the expression
84      * as a different expression). The default implementation does nothing.
85      * @return the simplified expression
86      * @throws net.sf.saxon.trans.XPathException
87      * if an error is discovered during expression rewriting
88      */

89
90     public Expression simplify(StaticContext env) throws XPathException {
91         content = content.simplify(env);
92         if (href != null) {
93             href = href.simplify(env);
94         }
95         for (Iterator it = serializationAttributes.keySet().iterator(); it.hasNext();) {
96             Object JavaDoc key = it.next();
97             Expression value = (Expression)serializationAttributes.get(key);
98             if (!(value instanceof Value)) {
99                 value = value.simplify(env);
100                 serializationAttributes.put(key, value);
101             }
102         }
103         return this;
104     }
105
106     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
107         content = content.typeCheck(env, contextItemType);
108         adoptChildExpression(content);
109         if (href != null) {
110             href = href.typeCheck(env, contextItemType);
111             adoptChildExpression(href);
112         }
113         for (Iterator it = serializationAttributes.keySet().iterator(); it.hasNext();) {
114             Object JavaDoc key = it.next();
115             Expression value = (Expression)serializationAttributes.get(key);
116             if (!(value instanceof Value)) {
117                 value = value.typeCheck(env, contextItemType);
118                 adoptChildExpression(value);
119                 serializationAttributes.put(key, value);
120             }
121         }
122         return this;
123     }
124
125     public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
126         content = content.optimize(opt, env, contextItemType);
127         adoptChildExpression(content);
128         if (href != null) {
129             href = href.optimize(opt, env, contextItemType);
130             adoptChildExpression(href);
131         }
132         for (Iterator it = serializationAttributes.keySet().iterator(); it.hasNext();) {
133             Object JavaDoc key = it.next();
134             Expression value = (Expression)serializationAttributes.get(key);
135             if (!(value instanceof Value)) {
136                 value = value.optimize(opt, env, contextItemType);
137                 adoptChildExpression(value);
138                 serializationAttributes.put(key, value);
139             }
140         }
141         return this;
142     }
143
144     /**
145      * Handle promotion offers, that is, non-local tree rewrites.
146      * @param offer The type of rewrite being offered
147      * @throws XPathException
148      */

149
150     protected void promoteInst(PromotionOffer offer) throws XPathException {
151         content = doPromotion(content, offer);
152         if (href != null) {
153             href = doPromotion(href, offer);
154         }
155         for (Iterator it = serializationAttributes.keySet().iterator(); it.hasNext();) {
156             Object JavaDoc key = it.next();
157             Expression value = (Expression)serializationAttributes.get(key);
158             if (!(value instanceof Value)) {
159                 value = doPromotion(value, offer);
160                 serializationAttributes.put(key, value);
161             }
162         }
163     }
164
165     /**
166      * Get the name of this instruction for diagnostic and tracing purposes
167      * (the string "xsl:result-document")
168      */

169
170     public int getInstructionNameCode() {
171         return StandardNames.XSL_RESULT_DOCUMENT;
172     }
173
174     /**
175      * Get the item type of the items returned by evaluating this instruction
176      * @return the static item type of the instruction. This is empty: the result-document instruction
177      * returns nothing.
178      */

179
180     public ItemType getItemType() {
181         return NoNodeTest.getInstance();
182     }
183
184
185
186     /**
187      * Get all the XPath expressions associated with this instruction
188      * (in XSLT terms, the expression present on attributes of the instruction,
189      * as distinct from the child instructions in a sequence construction)
190      */

191
192     public Iterator iterateSubExpressions() {
193         ArrayList list = new ArrayList(6);
194         list.add(content);
195         if (href != null) {
196             list.add(href);
197         }
198         for (Iterator it = serializationAttributes.values().iterator(); it.hasNext();) {
199             list.add(it.next());
200         }
201         return list.iterator();
202     }
203
204     public TailCall processLeavingTail(XPathContext context) throws XPathException {
205         Controller controller = context.getController();
206         XPathContext c2 = context.newMinorContext();
207         c2.setOrigin(this);
208
209         Result JavaDoc result;
210         OutputURIResolver resolver = null;
211
212         if (href == null) {
213             result = controller.getPrincipalResult();
214         } else {
215             try {
216                 String JavaDoc base = controller.getBaseOutputURI();
217                 if (base == null && controller.getConfiguration().isAllowExternalFunctions()) {
218                     // if calling external functions is allowed, then the stylesheet is trusted, so
219
// we allow it to write to files relative to the current directory
220
base = new File JavaDoc(System.getProperty("user.dir")).toURI().toString();
221                 }
222
223                 resolver = controller.getOutputURIResolver();
224
225                 String JavaDoc hrefValue = href.evaluateAsString(context);
226                 result = resolver.resolve(hrefValue, base);
227                 if (result == null) {
228                     resolver = StandardOutputResolver.getInstance();
229                     result = resolver.resolve(hrefValue, base);
230                 }
231             } catch (TransformerException JavaDoc e) {
232                 throw DynamicError.makeDynamicError(e);
233             }
234         }
235
236         if (!controller.checkUniqueOutputDestination(result.getSystemId())) {
237             DynamicError err = new DynamicError("Cannot write more than one result document to the same URI: " +
238                     result.getSystemId());
239             err.setXPathContext(context);
240             err.setErrorCode("XTDE1490");
241             throw err;
242         }
243
244         boolean timing = controller.getConfiguration().isTiming();
245         if (timing) {
246             String JavaDoc dest = result.getSystemId();
247             if (dest == null) {
248                 if (result instanceof StreamResult JavaDoc) {
249                     dest = "anonymous output stream";
250                 } else if (result instanceof SAXResult JavaDoc) {
251                     dest = "SAX2 ContentHandler";
252                 } else if (result instanceof DOMResult JavaDoc) {
253                     dest = "DOM tree";
254                 } else {
255                     dest = result.getClass().getName();
256                 }
257             }
258             System.err.println("Writing to " + dest);
259         }
260
261         Properties props = outputProperties;
262         if (serializationAttributes.size() > 0) {
263             props = new Properties(outputProperties);
264             final NamePool namePool = context.getController().getNamePool();
265             for (Iterator it = serializationAttributes.keySet().iterator(); it.hasNext();) {
266                 Integer JavaDoc key = (Integer JavaDoc) it.next();
267                 Expression exp = (Expression) serializationAttributes.get(key);
268                 String JavaDoc value = exp.evaluateAsString(context);
269                 try {
270                     setSerializationProperty(props, key.intValue(), value, namePool, nsResolver);
271                 } catch (DynamicError e) {
272                     if (NamespaceConstant.SAXON.equals(e.getErrorCodeNamespace()) &&
273                             "warning".equals(e.getErrorCodeLocalPart())) {
274                         try {
275                             context.getController().getErrorListener().warning(e);
276                         } catch (TransformerException JavaDoc e2) {
277                             throw DynamicError.makeDynamicError(e2);
278                         }
279                     } else {
280                         e.setXPathContext(context);
281                         e.setLocator(getSourceLocator());
282                         throw e;
283                     }
284                 }
285             }
286         }
287         String JavaDoc nextInChain = outputProperties.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN);
288         if (nextInChain != null) {
289             try {
290                 result = controller.prepareNextStylesheet(nextInChain, baseURI, result);
291             } catch (TransformerException JavaDoc e) {
292                 throw DynamicError.makeDynamicError(e);
293             }
294         }
295
296         // TODO: cache the serializer and reuse it if the serialization properties are fixed at
297
// compile time (that is, if serializationAttributes.isEmpty). Need to save the serializer
298
// in a form where the final output destination can be changed.
299

300         c2.changeOutputDestination(props,
301                 result,
302                 true,
303                 validationAction,
304                 schemaType);
305         SequenceReceiver out = c2.getReceiver();
306
307         out.startDocument(0);
308         content.process(c2);
309         out.endDocument();
310         out.close();
311         if (resolver != null) {
312             try {
313                 resolver.close(result);
314             } catch (TransformerException JavaDoc e) {
315                 throw DynamicError.makeDynamicError(e);
316             }
317         }
318         return null;
319     }
320
321     /**
322      * Validate a serialization property and add its value to a Properties collection
323      */

324
325     public static void setSerializationProperty(Properties details, int fp,
326                                                 String JavaDoc value, NamePool pool,
327                                                 NamespaceResolver nsResolver)
328             throws XPathException {
329         String JavaDoc lname = pool.getLocalName(fp);
330         String JavaDoc uri = pool.getURI(fp);
331         if (uri.equals("")) {
332             if (lname.equals(StandardNames.METHOD)) {
333                 if (value.equals("xml") || value.equals("html") ||
334                         value.equals("text") || value.equals("xhtml")) {
335                     details.put(OutputKeys.METHOD, value);
336                 } else {
337                     String JavaDoc[] parts;
338                     try {
339                         parts = Name.getQNameParts(value);
340                         String JavaDoc prefix = parts[0];
341                         if (prefix.equals("")) {
342                             DynamicError err = new DynamicError("method must be xml, html, xhtml, or text, or a prefixed name");
343                             err.setErrorCode("XTSE1570");
344                             throw err;
345                         } else {
346                             String JavaDoc muri = nsResolver.getURIForPrefix(prefix, false);
347                             if (muri==null) {
348                                 DynamicError err = new DynamicError("Namespace prefix '" + prefix + "' has not been declared");
349                                 err.setErrorCode("XTSE1570");
350                                 throw err;
351                             }
352                             details.put(OutputKeys.METHOD, '{' + muri + '}' + parts[1]);
353                         }
354                     } catch (QNameException e) {
355                         DynamicError err = new DynamicError("Invalid method name. " + e.getMessage());
356                         err.setErrorCode("XTSE1570");
357                         throw err;
358                     }
359                 }
360             } else
361
362             if (lname.equals(StandardNames.OUTPUT_VERSION)) {
363                 details.put(OutputKeys.VERSION, value);
364             } else
365
366             if (lname.equals("byte-order-mark")) {
367                 if (value.equals("yes") || value.equals("no")) {
368                     details.put(SaxonOutputKeys.BYTE_ORDER_MARK, value);
369                 } else {
370                     DynamicError err = new DynamicError("byte-order-mark value must be 'yes' or 'no'");
371                     err.setErrorCode("XTDE0030");
372                     throw err;
373                 }
374             } else
375
376             if (lname.equals(StandardNames.INDENT)) {
377                 if (value.equals("yes") || value.equals("no")) {
378                     details.put(OutputKeys.INDENT, value);
379                 } else {
380                     DynamicError err = new DynamicError("indent must be 'yes' or 'no'");
381                     err.setErrorCode("XTDE0030");
382                     throw err;
383                 }
384             } else
385
386             if (lname.equals(StandardNames.ENCODING)) {
387                 details.put(OutputKeys.ENCODING, value);
388             } else
389
390             if (lname.equals(StandardNames.MEDIA_TYPE)) {
391                 details.put(OutputKeys.MEDIA_TYPE, value);
392             } else
393
394             if (lname.equals(StandardNames.DOCTYPE_SYSTEM)) {
395                 details.put(OutputKeys.DOCTYPE_SYSTEM, value);
396             } else
397
398             if (lname.equals(StandardNames.DOCTYPE_PUBLIC)) {
399                 details.put(OutputKeys.DOCTYPE_PUBLIC, value);
400             } else
401
402             if (lname.equals(StandardNames.OMIT_XML_DECLARATION)) {
403                 if (value.equals("yes") || value.equals("no")) {
404                     details.put(OutputKeys.OMIT_XML_DECLARATION, value);
405                 } else {
406                     DynamicError err = new DynamicError("omit-xml-declaration attribute must be 'yes' or 'no'");
407                     err.setErrorCode("XTDE0030");
408                     throw err;
409                 }
410             } else
411
412             if (lname.equals(StandardNames.STANDALONE)) {
413                 if (value.equals("yes") || value.equals("no") || value.equals("omit")) {
414                     details.put(OutputKeys.STANDALONE, value);
415                     DynamicError err = new DynamicError("standalone attribute must be 'yes' or 'no' or 'omit'");
416                     err.setErrorCode("XTDE0030");
417                     throw err;
418                 }
419             } else
420
421             if (lname.equals(StandardNames.CDATA_SECTION_ELEMENTS)) {
422                 String JavaDoc existing = details.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
423                 if (existing == null) {
424                     existing = "";
425                 }
426                 String JavaDoc s = "";
427                 StringTokenizer st = new StringTokenizer(value);
428                 while (st.hasMoreTokens()) {
429                     String JavaDoc displayname = st.nextToken();
430                     try {
431                         String JavaDoc[] parts = Name.getQNameParts(displayname);
432                         String JavaDoc muri = nsResolver.getURIForPrefix(parts[0], true);
433                         if (muri==null) {
434                             DynamicError err = new DynamicError("Namespace prefix '" + parts[0] + "' has not been declared");
435                             err.setErrorCode("XT0030");
436                             throw err;
437                         }
438                         s += " {" + muri + '}' + parts[1];
439                     } catch (QNameException err) {
440                         DynamicError e = new DynamicError("Invalid CDATA element name. " + err.getMessage());
441                         e.setErrorCode("XTDE0030");
442                         throw e;
443                     }
444
445                     details.put(OutputKeys.CDATA_SECTION_ELEMENTS, existing + s);
446                 }
447             } else
448
449             if (lname.equals(StandardNames.USE_CHARACTER_MAPS)) {
450                 String JavaDoc existing = details.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS);
451                 if (existing == null) {
452                     existing = "";
453                 }
454                 details.put(SaxonOutputKeys.USE_CHARACTER_MAPS, existing + value);
455             } else
456
457
458             if (lname.equals(StandardNames.UNDECLARE_PREFIXES)) {
459                 if (value.equals("yes") || value.equals("no")) {
460                     details.put(SaxonOutputKeys.UNDECLARE_PREFIXES, value);
461                 } else {
462                     DynamicError err = new DynamicError("undeclare-namespaces value must be 'yes' or 'no'");
463                     err.setErrorCode("XTDE0030");
464                     throw err;
465                 }
466             } else
467
468             if (lname.equals(StandardNames.INCLUDE_CONTENT_TYPE)) {
469                 if (value.equals("yes") || value.equals("no")) {
470                     details.put(SaxonOutputKeys.INCLUDE_CONTENT_TYPE, value);
471                 } else {
472                     DynamicError err = new DynamicError("include-content-type attribute must be 'yes' or 'no'");
473                     err.setErrorCode("XTDE0030");
474                     throw err;
475                 }
476             } else
477
478             if (lname.equals(StandardNames.ESCAPE_URI_ATTRIBUTES)) {
479                 if (value.equals("yes") || value.equals("no")) {
480                     details.put(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES, value);
481                 } else {
482                     DynamicError err = new DynamicError("escape-uri-attributes value must be 'yes' or 'no'");
483                     err.setErrorCode("XTDE0030");
484                     throw err;
485                 }
486             } else
487
488             if (lname.equals(StandardNames.NORMALIZATION_FORM)) {
489                 if (value.equals("NFC") || value.equals("NFD") ||
490                         value.equals("NFKC") || value.equals("NFKD")) {
491                     details.put(SaxonOutputKeys.NORMALIZATION_FORM, value);
492                 } else if (value.equals("none")) {
493                     // do nothing
494
} else {
495                     DynamicError err = new DynamicError("escape-uri-attributes value must be 'yes' or 'no'");
496                     err.setErrorCode("XTDE0030");
497                     throw err;
498                 }
499             }
500
501         } else if (uri.equals(NamespaceConstant.SAXON)) {
502
503             if (lname.equals("character-representation")) {
504                 details.put(SaxonOutputKeys.CHARACTER_REPRESENTATION, value);
505             } else
506
507             if (lname.equals("indent-spaces")) {
508                 try {
509                     Integer.parseInt(value);
510                     details.put(OutputKeys.INDENT, "yes");
511                     details.put(SaxonOutputKeys.INDENT_SPACES, value);
512                 } catch (NumberFormatException JavaDoc err) {
513                     DynamicError e = new DynamicError("saxon:indent-spaces must be an integer");
514                     e.setErrorCode(NamespaceConstant.SAXON, SaxonErrorCode.SXWN9002);
515                     throw e;
516                 }
517             } else
518
519             if (lname.equals("next-in-chain")) {
520                 DynamicError e = new DynamicError("saxon:next-in-chain value cannot be specified dynamically");
521                 e.setErrorCode(NamespaceConstant.SAXON, SaxonErrorCode.SXWN9004);
522                 throw e;
523             } else
524
525             if (lname.equals("require-well-formed")) {
526                 if (value.equals("yes") || value.equals("no")) {
527                     details.put(SaxonOutputKeys.REQUIRE_WELL_FORMED, value);
528                 } else {
529                     DynamicError e = new DynamicError("saxon:require-well-formed value must be 'yes' or 'no'");
530                     e.setErrorCode(NamespaceConstant.SAXON, SaxonErrorCode.SXWN9003);
531                     throw e;
532                 }
533             }
534
535         } else {
536
537             // deal with user-defined attributes
538
details.put('{' + uri + '}' + lname, value);
539         }
540
541     }
542
543     /**
544      * Diagnostic print of expression structure. The expression is written to the System.err
545      * output stream
546      *
547      * @param level indentation level for this expression
548      * @param out
549      */

550
551     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
552         out.println(ExpressionTool.indent(level) + "result-document");
553         content.display(level + 1, pool, out);
554     }
555 }
556
557 //
558
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
559
// you may not use this file except in compliance with the License. You may obtain a copy of the
560
// License at http://www.mozilla.org/MPL/
561
//
562
// Software distributed under the License is distributed on an "AS IS" basis,
563
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
564
// See the License for the specific language governing rights and limitations under the License.
565
//
566
// The Original Code is: all this file.
567
//
568
// The Initial Developer of the Original Code is Michael H. Kay.
569
//
570
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
571
//
572
// Additional Contributor(s): Brett Knights [brett@knightsofthenet.com]
573
//
574
Popular Tags