KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xalan > internal > xsltc > compiler > Output


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: Output.java,v 1.27 2004/12/10 18:46:42 santiagopg Exp $
18  */

19
20 package com.sun.org.apache.xalan.internal.xsltc.compiler;
21
22 import java.io.OutputStreamWriter JavaDoc;
23 import java.util.Properties JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25
26 import javax.xml.transform.OutputKeys JavaDoc;
27
28 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
29 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
30 import com.sun.org.apache.bcel.internal.generic.InstructionList;
31 import com.sun.org.apache.bcel.internal.generic.PUSH;
32 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
37 import com.sun.org.apache.xml.internal.serializer.Encodings;
38 import com.sun.org.apache.xml.internal.utils.XMLChar;
39
40 /**
41  * @author Jacek Ambroziak
42  * @author Santiago Pericas-Geertsen
43  * @author Morten Jorgensen
44  */

45 final class Output extends TopLevelElement {
46
47     // TODO: use three-value variables for boolean values: true/false/default
48

49     // These attributes are extracted from the xsl:output element. They also
50
// appear as fields (with the same type, only public) in the translet
51
private String JavaDoc _version;
52     private String JavaDoc _method;
53     private String JavaDoc _encoding;
54     private boolean _omitHeader = false;
55     private String JavaDoc _standalone;
56     private String JavaDoc _doctypePublic;
57     private String JavaDoc _doctypeSystem;
58     private String JavaDoc _cdata;
59     private boolean _indent = false;
60     private String JavaDoc _mediaType;
61     private String JavaDoc _indentamount;
62     
63     // Disables this output element (when other element has higher precedence)
64
private boolean _disabled = false;
65
66     // Some global constants
67
private final static String JavaDoc STRING_SIG = "Ljava/lang/String;";
68     private final static String JavaDoc XML_VERSION = "1.0";
69     private final static String JavaDoc HTML_VERSION = "4.0";
70
71     /**
72      * Displays the contents of this element (for debugging)
73      */

74     public void display(int indent) {
75     indent(indent);
76     Util.println("Output " + _method);
77     }
78
79     /**
80      * Disables this <xsl:output> element in case where there are some other
81      * <xsl:output> element (from a different imported/included stylesheet)
82      * with higher precedence.
83      */

84     public void disable() {
85     _disabled = true;
86     }
87
88     public boolean enabled() {
89     return !_disabled;
90     }
91
92     public String JavaDoc getCdata() {
93     return _cdata;
94     }
95
96     public String JavaDoc getOutputMethod() {
97         return _method;
98     }
99     
100     private void transferAttribute(Output previous, String JavaDoc qname) {
101         if (!hasAttribute(qname) && previous.hasAttribute(qname)) {
102             addAttribute(qname, previous.getAttribute(qname));
103         }
104     }
105     
106     public void mergeOutput(Output previous) {
107         // Transfer attributes from previous xsl:output
108
transferAttribute(previous, "version");
109         transferAttribute(previous, "method");
110         transferAttribute(previous, "encoding");
111         transferAttribute(previous, "doctype-system");
112         transferAttribute(previous, "doctype-public");
113         transferAttribute(previous, "media-type");
114         transferAttribute(previous, "indent");
115         transferAttribute(previous, "omit-xml-declaration");
116         transferAttribute(previous, "standalone");
117         
118         // Merge cdata-section-elements
119
if (previous.hasAttribute("cdata-section-elements")) {
120             // addAttribute works as a setter if it already exists
121
addAttribute("cdata-section-elements",
122                 previous.getAttribute("cdata-section-elements") + ' ' +
123                 getAttribute("cdata-section-elements"));
124         }
125
126         // Transfer non-standard attributes as well
127
String JavaDoc prefix = lookupPrefix("http://xml.apache.org/xalan");
128         if (prefix != null) {
129             transferAttribute(previous, prefix + ':' + "indent-amount");
130         }
131         prefix = lookupPrefix("http://xml.apache.org/xslt");
132         if (prefix != null) {
133             transferAttribute(previous, prefix + ':' + "indent-amount");
134         }
135     }
136
137     /**
138      * Scans the attribute list for the xsl:output instruction
139      */

140     public void parseContents(Parser parser) {
141     final Properties JavaDoc outputProperties = new Properties JavaDoc();
142
143     // Ask the parser if it wants this <xsl:output> element
144
parser.setOutput(this);
145
146     // Do nothing if other <xsl:output> element has higher precedence
147
if (_disabled) return;
148
149     String JavaDoc attrib = null;
150
151     // Get the output version
152
_version = getAttribute("version");
153     if (_version.equals(Constants.EMPTYSTRING)) {
154         _version = null;
155     }
156     else {
157         outputProperties.setProperty(OutputKeys.VERSION, _version);
158     }
159
160     // Get the output method - "xml", "html", "text" or <qname> (but not ncname)
161
_method = getAttribute("method");
162     if (_method.equals(Constants.EMPTYSTRING)) {
163         _method = null;
164     }
165     if (_method != null) {
166             _method = _method.toLowerCase();
167             if ((_method.equals("xml"))||
168                 (_method.equals("html"))||
169                 (_method.equals("text"))||
170                 ((XMLChar.isValidQName(_method)&&(_method.indexOf(":") > 0)))) {
171            outputProperties.setProperty(OutputKeys.METHOD, _method);
172             } else {
173                 reportError(this, parser, ErrorMsg.INVALID_METHOD_IN_OUTPUT, _method);
174             }
175     }
176
177     // Get the output encoding - any value accepted here
178
_encoding = getAttribute("encoding");
179     if (_encoding.equals(Constants.EMPTYSTRING)) {
180         _encoding = null;
181     }
182     else {
183         try {
184         // Create a write to verify encoding support
185
String JavaDoc canonicalEncoding;
186                 canonicalEncoding = Encodings.convertMime2JavaEncoding(_encoding);
187         OutputStreamWriter JavaDoc writer =
188             new OutputStreamWriter JavaDoc(System.out, canonicalEncoding);
189         }
190         catch (java.io.UnsupportedEncodingException JavaDoc e) {
191         ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_ENCODING,
192                         _encoding, this);
193         parser.reportError(Constants.WARNING, msg);
194         }
195         outputProperties.setProperty(OutputKeys.ENCODING, _encoding);
196     }
197
198     // Should the XML header be omitted - translate to true/false
199
attrib = getAttribute("omit-xml-declaration");
200     if (!attrib.equals(Constants.EMPTYSTRING)) {
201         if (attrib.equals("yes")) {
202         _omitHeader = true;
203         }
204         outputProperties.setProperty(OutputKeys.OMIT_XML_DECLARATION, attrib);
205     }
206
207     // Add 'standalone' decaration to output - use text as is
208
_standalone = getAttribute("standalone");
209     if (_standalone.equals(Constants.EMPTYSTRING)) {
210         _standalone = null;
211     }
212     else {
213         outputProperties.setProperty(OutputKeys.STANDALONE, _standalone);
214     }
215
216     // Get system/public identifiers for output DOCTYPE declaration
217
_doctypeSystem = getAttribute("doctype-system");
218     if (_doctypeSystem.equals(Constants.EMPTYSTRING)) {
219         _doctypeSystem = null;
220     }
221     else {
222         outputProperties.setProperty(OutputKeys.DOCTYPE_SYSTEM, _doctypeSystem);
223     }
224
225
226     _doctypePublic = getAttribute("doctype-public");
227     if (_doctypePublic.equals(Constants.EMPTYSTRING)) {
228         _doctypePublic = null;
229     }
230     else {
231         outputProperties.setProperty(OutputKeys.DOCTYPE_PUBLIC, _doctypePublic);
232     }
233
234     // Names the elements of whose text contents should be output as CDATA
235
_cdata = getAttribute("cdata-section-elements");
236     if (_cdata.equals(Constants.EMPTYSTRING)) {
237         _cdata = null;
238     }
239     else {
240         StringBuffer JavaDoc expandedNames = new StringBuffer JavaDoc();
241         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(_cdata);
242
243         // Make sure to store names in expanded form
244
while (tokens.hasMoreTokens()) {
245                 String JavaDoc qname = tokens.nextToken();
246                 if (!XMLChar.isValidQName(qname)) {
247                     ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, qname, this);
248                     parser.reportError(Constants.ERROR, err);
249                 }
250         expandedNames.append(
251                parser.getQName(qname).toString()).append(' ');
252         }
253         _cdata = expandedNames.toString();
254         outputProperties.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS,
255         _cdata);
256     }
257
258     // Get the indent setting - only has effect for xml and html output
259
attrib = getAttribute("indent");
260     if (!attrib.equals(EMPTYSTRING)) {
261         if (attrib.equals("yes")) {
262         _indent = true;
263         }
264         outputProperties.setProperty(OutputKeys.INDENT, attrib);
265     }
266     else if (_method != null && _method.equals("html")) {
267         _indent = true;
268     }
269     
270         // indent-amount: extension attribute of xsl:output
271
_indentamount = getAttribute(
272             lookupPrefix("http://xml.apache.org/xalan"), "indent-amount");
273         // Hack for supporting Old Namespace URI.
274
if (_indentamount.equals(EMPTYSTRING)){
275             _indentamount = getAttribute(
276                 lookupPrefix("http://xml.apache.org/xslt"), "indent-amount");
277         }
278         if (!_indentamount.equals(EMPTYSTRING)) {
279             outputProperties.setProperty("indent_amount", _indentamount);
280         }
281         
282     // Get the MIME type for the output file
283
_mediaType = getAttribute("media-type");
284     if (_mediaType.equals(Constants.EMPTYSTRING)) {
285         _mediaType = null;
286     }
287     else {
288         outputProperties.setProperty(OutputKeys.MEDIA_TYPE, _mediaType);
289     }
290
291     // Implied properties
292
if (_method != null) {
293         if (_method.equals("html")) {
294         if (_version == null) {
295             _version = HTML_VERSION;
296         }
297         if (_mediaType == null) {
298             _mediaType = "text/html";
299         }
300         }
301         else if (_method.equals("text")) {
302         if (_mediaType == null) {
303             _mediaType = "text/plain";
304         }
305         }
306     }
307
308     // Set output properties in current stylesheet
309
parser.getCurrentStylesheet().setOutputProperties(outputProperties);
310     }
311
312     /**
313      * Compile code that passes the information in this <xsl:output> element
314      * to the appropriate fields in the translet
315      */

316     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
317
318     // Do nothing if other <xsl:output> element has higher precedence
319
if (_disabled) return;
320
321     ConstantPoolGen cpg = classGen.getConstantPool();
322     InstructionList il = methodGen.getInstructionList();
323
324     int field = 0;
325         il.append(classGen.loadTranslet());
326
327     // Only update _version field if set and different from default
328
if ((_version != null) && (!_version.equals(XML_VERSION))) {
329         field = cpg.addFieldref(TRANSLET_CLASS, "_version", STRING_SIG);
330         il.append(DUP);
331         il.append(new PUSH(cpg, _version));
332         il.append(new PUTFIELD(field));
333     }
334
335     // Only update _method field if "method" attribute used
336
if (_method != null) {
337         field = cpg.addFieldref(TRANSLET_CLASS, "_method", STRING_SIG);
338         il.append(DUP);
339         il.append(new PUSH(cpg, _method));
340         il.append(new PUTFIELD(field));
341     }
342
343     // Only update if _encoding field is "encoding" attribute used
344
if (_encoding != null) {
345         field = cpg.addFieldref(TRANSLET_CLASS, "_encoding", STRING_SIG);
346         il.append(DUP);
347         il.append(new PUSH(cpg, _encoding));
348         il.append(new PUTFIELD(field));
349     }
350
351     // Only update if "omit-xml-declaration" used and set to 'yes'
352
if (_omitHeader) {
353         field = cpg.addFieldref(TRANSLET_CLASS, "_omitHeader", "Z");
354         il.append(DUP);
355         il.append(new PUSH(cpg, _omitHeader));
356         il.append(new PUTFIELD(field));
357     }
358
359     // Add 'standalone' decaration to output - use text as is
360
if (_standalone != null) {
361         field = cpg.addFieldref(TRANSLET_CLASS, "_standalone", STRING_SIG);
362         il.append(DUP);
363         il.append(new PUSH(cpg, _standalone));
364         il.append(new PUTFIELD(field));
365     }
366
367     // Set system/public doctype only if both are set
368
field = cpg.addFieldref(TRANSLET_CLASS,"_doctypeSystem",STRING_SIG);
369     il.append(DUP);
370     il.append(new PUSH(cpg, _doctypeSystem));
371     il.append(new PUTFIELD(field));
372     field = cpg.addFieldref(TRANSLET_CLASS,"_doctypePublic",STRING_SIG);
373     il.append(DUP);
374     il.append(new PUSH(cpg, _doctypePublic));
375     il.append(new PUTFIELD(field));
376     
377     // Add 'medye-type' decaration to output - if used
378
if (_mediaType != null) {
379         field = cpg.addFieldref(TRANSLET_CLASS, "_mediaType", STRING_SIG);
380         il.append(DUP);
381         il.append(new PUSH(cpg, _mediaType));
382         il.append(new PUTFIELD(field));
383     }
384
385     // Compile code to set output indentation on/off
386
if (_indent) {
387         field = cpg.addFieldref(TRANSLET_CLASS, "_indent", "Z");
388         il.append(DUP);
389         il.append(new PUSH(cpg, _indent));
390         il.append(new PUTFIELD(field));
391     }
392
393         //Compile code to set indent amount.
394
if(_indentamount != null && !_indentamount.equals(EMPTYSTRING)){
395             field = cpg.addFieldref(TRANSLET_CLASS, "_indentamount", "I");
396         il.append(DUP);
397         il.append(new PUSH(cpg, Integer.parseInt(_indentamount)));
398         il.append(new PUTFIELD(field));
399         }
400         
401     // Forward to the translet any elements that should be output as CDATA
402
if (_cdata != null) {
403         int index = cpg.addMethodref(TRANSLET_CLASS,
404                      "addCdataElement",
405                      "(Ljava/lang/String;)V");
406
407         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(_cdata);
408         while (tokens.hasMoreTokens()) {
409         il.append(DUP);
410         il.append(new PUSH(cpg, tokens.nextToken()));
411         il.append(new INVOKEVIRTUAL(index));
412         }
413     }
414     il.append(POP); // Cleanup - pop last translet reference off stack
415
}
416
417 }
418
Popular Tags