jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/Output.java
changeset 12457 c348e06f0e82
parent 6 7f561c08de6b
equal deleted inserted replaced
12324:1d7e6da6adc8 12457:c348e06f0e82
       
     1 /*
       
     2  * reserved comment block
       
     3  * DO NOT REMOVE OR ALTER!
       
     4  */
       
     5 /*
       
     6  * Copyright 2001-2004 The Apache Software Foundation.
       
     7  *
       
     8  * Licensed under the Apache License, Version 2.0 (the "License");
       
     9  * you may not use this file except in compliance with the License.
       
    10  * You may obtain a copy of the License at
       
    11  *
       
    12  *     http://www.apache.org/licenses/LICENSE-2.0
       
    13  *
       
    14  * Unless required by applicable law or agreed to in writing, software
       
    15  * distributed under the License is distributed on an "AS IS" BASIS,
       
    16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    17  * See the License for the specific language governing permissions and
       
    18  * limitations under the License.
       
    19  */
       
    20 /*
       
    21  * $Id: Output.java,v 1.2.4.1 2005/09/12 10:53:00 pvedula Exp $
       
    22  */
       
    23 
       
    24 package com.sun.org.apache.xalan.internal.xsltc.compiler;
       
    25 
       
    26 import java.io.OutputStreamWriter;
       
    27 import java.util.Properties;
       
    28 import java.util.StringTokenizer;
       
    29 
       
    30 import javax.xml.transform.OutputKeys;
       
    31 
       
    32 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
       
    33 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
       
    34 import com.sun.org.apache.bcel.internal.generic.InstructionList;
       
    35 import com.sun.org.apache.bcel.internal.generic.PUSH;
       
    36 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
       
    37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
       
    38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
       
    39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
       
    40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
       
    41 import com.sun.org.apache.xml.internal.serializer.Encodings;
       
    42 import com.sun.org.apache.xml.internal.utils.XML11Char;
       
    43 
       
    44 /**
       
    45  * @author Jacek Ambroziak
       
    46  * @author Santiago Pericas-Geertsen
       
    47  * @author Morten Jorgensen
       
    48  */
       
    49 final class Output extends TopLevelElement {
       
    50 
       
    51     // TODO: use three-value variables for boolean values: true/false/default
       
    52 
       
    53     // These attributes are extracted from the xsl:output element. They also
       
    54     // appear as fields (with the same type, only public) in the translet
       
    55     private String  _version;
       
    56     private String  _method;
       
    57     private String  _encoding;
       
    58     private boolean _omitHeader = false;
       
    59     private String  _standalone;
       
    60     private String  _doctypePublic;
       
    61     private String  _doctypeSystem;
       
    62     private String  _cdata;
       
    63     private boolean _indent = false;
       
    64     private String  _mediaType;
       
    65     private String _indentamount;
       
    66 
       
    67     // Disables this output element (when other element has higher precedence)
       
    68     private boolean _disabled = false;
       
    69 
       
    70     // Some global constants
       
    71     private final static String STRING_SIG = "Ljava/lang/String;";
       
    72     private final static String XML_VERSION = "1.0";
       
    73     private final static String HTML_VERSION = "4.0";
       
    74 
       
    75     /**
       
    76      * Displays the contents of this element (for debugging)
       
    77      */
       
    78     public void display(int indent) {
       
    79         indent(indent);
       
    80         Util.println("Output " + _method);
       
    81     }
       
    82 
       
    83     /**
       
    84      * Disables this <xsl:output> element in case where there are some other
       
    85      * <xsl:output> element (from a different imported/included stylesheet)
       
    86      * with higher precedence.
       
    87      */
       
    88     public void disable() {
       
    89         _disabled = true;
       
    90     }
       
    91 
       
    92     public boolean enabled() {
       
    93         return !_disabled;
       
    94     }
       
    95 
       
    96     public String getCdata() {
       
    97         return _cdata;
       
    98     }
       
    99 
       
   100     public String getOutputMethod() {
       
   101         return _method;
       
   102     }
       
   103 
       
   104     private void transferAttribute(Output previous, String qname) {
       
   105         if (!hasAttribute(qname) && previous.hasAttribute(qname)) {
       
   106             addAttribute(qname, previous.getAttribute(qname));
       
   107         }
       
   108     }
       
   109 
       
   110     public void mergeOutput(Output previous) {
       
   111         // Transfer attributes from previous xsl:output
       
   112         transferAttribute(previous, "version");
       
   113         transferAttribute(previous, "method");
       
   114         transferAttribute(previous, "encoding");
       
   115         transferAttribute(previous, "doctype-system");
       
   116         transferAttribute(previous, "doctype-public");
       
   117         transferAttribute(previous, "media-type");
       
   118         transferAttribute(previous, "indent");
       
   119         transferAttribute(previous, "omit-xml-declaration");
       
   120         transferAttribute(previous, "standalone");
       
   121 
       
   122         // Merge cdata-section-elements
       
   123         if (previous.hasAttribute("cdata-section-elements")) {
       
   124             // addAttribute works as a setter if it already exists
       
   125             addAttribute("cdata-section-elements",
       
   126                 previous.getAttribute("cdata-section-elements") + ' ' +
       
   127                 getAttribute("cdata-section-elements"));
       
   128         }
       
   129 
       
   130         // Transfer non-standard attributes as well
       
   131         String prefix = lookupPrefix("http://xml.apache.org/xalan");
       
   132         if (prefix != null) {
       
   133             transferAttribute(previous, prefix + ':' + "indent-amount");
       
   134         }
       
   135         prefix = lookupPrefix("http://xml.apache.org/xslt");
       
   136         if (prefix != null) {
       
   137             transferAttribute(previous, prefix + ':' + "indent-amount");
       
   138         }
       
   139     }
       
   140 
       
   141     /**
       
   142      * Scans the attribute list for the xsl:output instruction
       
   143      */
       
   144     public void parseContents(Parser parser) {
       
   145         final Properties outputProperties = new Properties();
       
   146 
       
   147         // Ask the parser if it wants this <xsl:output> element
       
   148         parser.setOutput(this);
       
   149 
       
   150         // Do nothing if other <xsl:output> element has higher precedence
       
   151         if (_disabled) return;
       
   152 
       
   153         String attrib = null;
       
   154 
       
   155         // Get the output version
       
   156         _version = getAttribute("version");
       
   157         if (_version.equals(Constants.EMPTYSTRING)) {
       
   158             _version = null;
       
   159         }
       
   160         else {
       
   161             outputProperties.setProperty(OutputKeys.VERSION, _version);
       
   162         }
       
   163 
       
   164         // Get the output method - "xml", "html", "text" or <qname> (but not ncname)
       
   165         _method = getAttribute("method");
       
   166         if (_method.equals(Constants.EMPTYSTRING)) {
       
   167             _method = null;
       
   168         }
       
   169         if (_method != null) {
       
   170             _method = _method.toLowerCase();
       
   171             if ((_method.equals("xml"))||
       
   172                 (_method.equals("html"))||
       
   173                 (_method.equals("text"))||
       
   174                 ((XML11Char.isXML11ValidQName(_method)&&(_method.indexOf(":") > 0)))) {
       
   175                outputProperties.setProperty(OutputKeys.METHOD, _method);
       
   176             } else {
       
   177                 reportError(this, parser, ErrorMsg.INVALID_METHOD_IN_OUTPUT, _method);
       
   178             }
       
   179         }
       
   180 
       
   181         // Get the output encoding - any value accepted here
       
   182         _encoding = getAttribute("encoding");
       
   183         if (_encoding.equals(Constants.EMPTYSTRING)) {
       
   184             _encoding = null;
       
   185         }
       
   186         else {
       
   187             try {
       
   188                 // Create a write to verify encoding support
       
   189                 String canonicalEncoding;
       
   190                 canonicalEncoding = Encodings.convertMime2JavaEncoding(_encoding);
       
   191                 OutputStreamWriter writer =
       
   192                     new OutputStreamWriter(System.out, canonicalEncoding);
       
   193             }
       
   194             catch (java.io.UnsupportedEncodingException e) {
       
   195                 ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_ENCODING,
       
   196                                             _encoding, this);
       
   197                 parser.reportError(Constants.WARNING, msg);
       
   198             }
       
   199             outputProperties.setProperty(OutputKeys.ENCODING, _encoding);
       
   200         }
       
   201 
       
   202         // Should the XML header be omitted - translate to true/false
       
   203         attrib = getAttribute("omit-xml-declaration");
       
   204         if (!attrib.equals(Constants.EMPTYSTRING)) {
       
   205             if (attrib.equals("yes")) {
       
   206                 _omitHeader = true;
       
   207             }
       
   208             outputProperties.setProperty(OutputKeys.OMIT_XML_DECLARATION, attrib);
       
   209         }
       
   210 
       
   211         // Add 'standalone' decaration to output - use text as is
       
   212         _standalone = getAttribute("standalone");
       
   213         if (_standalone.equals(Constants.EMPTYSTRING)) {
       
   214             _standalone = null;
       
   215         }
       
   216         else {
       
   217             outputProperties.setProperty(OutputKeys.STANDALONE, _standalone);
       
   218         }
       
   219 
       
   220         // Get system/public identifiers for output DOCTYPE declaration
       
   221         _doctypeSystem = getAttribute("doctype-system");
       
   222         if (_doctypeSystem.equals(Constants.EMPTYSTRING)) {
       
   223             _doctypeSystem = null;
       
   224         }
       
   225         else {
       
   226             outputProperties.setProperty(OutputKeys.DOCTYPE_SYSTEM, _doctypeSystem);
       
   227         }
       
   228 
       
   229 
       
   230         _doctypePublic = getAttribute("doctype-public");
       
   231         if (_doctypePublic.equals(Constants.EMPTYSTRING)) {
       
   232             _doctypePublic = null;
       
   233         }
       
   234         else {
       
   235             outputProperties.setProperty(OutputKeys.DOCTYPE_PUBLIC, _doctypePublic);
       
   236         }
       
   237 
       
   238         // Names the elements of whose text contents should be output as CDATA
       
   239         _cdata = getAttribute("cdata-section-elements");
       
   240         if (_cdata.equals(Constants.EMPTYSTRING)) {
       
   241             _cdata = null;
       
   242         }
       
   243         else {
       
   244             StringBuffer expandedNames = new StringBuffer();
       
   245             StringTokenizer tokens = new StringTokenizer(_cdata);
       
   246 
       
   247             // Make sure to store names in expanded form
       
   248             while (tokens.hasMoreTokens()) {
       
   249                 String qname = tokens.nextToken();
       
   250                 if (!XML11Char.isXML11ValidQName(qname)) {
       
   251                     ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, qname, this);
       
   252                     parser.reportError(Constants.ERROR, err);
       
   253                 }
       
   254                 expandedNames.append(
       
   255                    parser.getQName(qname).toString()).append(' ');
       
   256             }
       
   257             _cdata = expandedNames.toString();
       
   258             outputProperties.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS,
       
   259                 _cdata);
       
   260         }
       
   261 
       
   262         // Get the indent setting - only has effect for xml and html output
       
   263         attrib = getAttribute("indent");
       
   264         if (!attrib.equals(EMPTYSTRING)) {
       
   265             if (attrib.equals("yes")) {
       
   266                 _indent = true;
       
   267             }
       
   268             outputProperties.setProperty(OutputKeys.INDENT, attrib);
       
   269         }
       
   270         else if (_method != null && _method.equals("html")) {
       
   271             _indent = true;
       
   272         }
       
   273 
       
   274         // indent-amount: extension attribute of xsl:output
       
   275         _indentamount = getAttribute(
       
   276             lookupPrefix("http://xml.apache.org/xalan"), "indent-amount");
       
   277         //  Hack for supporting Old Namespace URI.
       
   278         if (_indentamount.equals(EMPTYSTRING)){
       
   279             _indentamount = getAttribute(
       
   280                 lookupPrefix("http://xml.apache.org/xslt"), "indent-amount");
       
   281         }
       
   282         if (!_indentamount.equals(EMPTYSTRING)) {
       
   283             outputProperties.setProperty("indent_amount", _indentamount);
       
   284         }
       
   285 
       
   286         // Get the MIME type for the output file
       
   287         _mediaType = getAttribute("media-type");
       
   288         if (_mediaType.equals(Constants.EMPTYSTRING)) {
       
   289             _mediaType = null;
       
   290         }
       
   291         else {
       
   292             outputProperties.setProperty(OutputKeys.MEDIA_TYPE, _mediaType);
       
   293         }
       
   294 
       
   295         // Implied properties
       
   296         if (_method != null) {
       
   297             if (_method.equals("html")) {
       
   298                 if (_version == null) {
       
   299                     _version = HTML_VERSION;
       
   300                 }
       
   301                 if (_mediaType == null) {
       
   302                     _mediaType = "text/html";
       
   303                 }
       
   304             }
       
   305             else if (_method.equals("text")) {
       
   306                 if (_mediaType == null) {
       
   307                     _mediaType = "text/plain";
       
   308                 }
       
   309             }
       
   310         }
       
   311 
       
   312         // Set output properties in current stylesheet
       
   313         parser.getCurrentStylesheet().setOutputProperties(outputProperties);
       
   314     }
       
   315 
       
   316     /**
       
   317      * Compile code that passes the information in this <xsl:output> element
       
   318      * to the appropriate fields in the translet
       
   319      */
       
   320     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
       
   321 
       
   322         // Do nothing if other <xsl:output> element has higher precedence
       
   323         if (_disabled) return;
       
   324 
       
   325         ConstantPoolGen cpg = classGen.getConstantPool();
       
   326         InstructionList il = methodGen.getInstructionList();
       
   327 
       
   328         int field = 0;
       
   329         il.append(classGen.loadTranslet());
       
   330 
       
   331         // Only update _version field if set and different from default
       
   332         if ((_version != null) && (!_version.equals(XML_VERSION))) {
       
   333             field = cpg.addFieldref(TRANSLET_CLASS, "_version", STRING_SIG);
       
   334             il.append(DUP);
       
   335             il.append(new PUSH(cpg, _version));
       
   336             il.append(new PUTFIELD(field));
       
   337         }
       
   338 
       
   339         // Only update _method field if "method" attribute used
       
   340         if (_method != null) {
       
   341             field = cpg.addFieldref(TRANSLET_CLASS, "_method", STRING_SIG);
       
   342             il.append(DUP);
       
   343             il.append(new PUSH(cpg, _method));
       
   344             il.append(new PUTFIELD(field));
       
   345         }
       
   346 
       
   347         // Only update if _encoding field is "encoding" attribute used
       
   348         if (_encoding != null) {
       
   349             field = cpg.addFieldref(TRANSLET_CLASS, "_encoding", STRING_SIG);
       
   350             il.append(DUP);
       
   351             il.append(new PUSH(cpg, _encoding));
       
   352             il.append(new PUTFIELD(field));
       
   353         }
       
   354 
       
   355         // Only update if "omit-xml-declaration" used and set to 'yes'
       
   356         if (_omitHeader) {
       
   357             field = cpg.addFieldref(TRANSLET_CLASS, "_omitHeader", "Z");
       
   358             il.append(DUP);
       
   359             il.append(new PUSH(cpg, _omitHeader));
       
   360             il.append(new PUTFIELD(field));
       
   361         }
       
   362 
       
   363         // Add 'standalone' decaration to output - use text as is
       
   364         if (_standalone != null) {
       
   365             field = cpg.addFieldref(TRANSLET_CLASS, "_standalone", STRING_SIG);
       
   366             il.append(DUP);
       
   367             il.append(new PUSH(cpg, _standalone));
       
   368             il.append(new PUTFIELD(field));
       
   369         }
       
   370 
       
   371         // Set system/public doctype only if both are set
       
   372         field = cpg.addFieldref(TRANSLET_CLASS,"_doctypeSystem",STRING_SIG);
       
   373         il.append(DUP);
       
   374         il.append(new PUSH(cpg, _doctypeSystem));
       
   375         il.append(new PUTFIELD(field));
       
   376         field = cpg.addFieldref(TRANSLET_CLASS,"_doctypePublic",STRING_SIG);
       
   377         il.append(DUP);
       
   378         il.append(new PUSH(cpg, _doctypePublic));
       
   379         il.append(new PUTFIELD(field));
       
   380 
       
   381         // Add 'medye-type' decaration to output - if used
       
   382         if (_mediaType != null) {
       
   383             field = cpg.addFieldref(TRANSLET_CLASS, "_mediaType", STRING_SIG);
       
   384             il.append(DUP);
       
   385             il.append(new PUSH(cpg, _mediaType));
       
   386             il.append(new PUTFIELD(field));
       
   387         }
       
   388 
       
   389         // Compile code to set output indentation on/off
       
   390         if (_indent) {
       
   391             field = cpg.addFieldref(TRANSLET_CLASS, "_indent", "Z");
       
   392             il.append(DUP);
       
   393             il.append(new PUSH(cpg, _indent));
       
   394             il.append(new PUTFIELD(field));
       
   395         }
       
   396 
       
   397         //Compile code to set indent amount.
       
   398         if(_indentamount != null && !_indentamount.equals(EMPTYSTRING)){
       
   399             field = cpg.addFieldref(TRANSLET_CLASS, "_indentamount", "I");
       
   400             il.append(DUP);
       
   401             il.append(new PUSH(cpg, Integer.parseInt(_indentamount)));
       
   402             il.append(new PUTFIELD(field));
       
   403         }
       
   404 
       
   405         // Forward to the translet any elements that should be output as CDATA
       
   406         if (_cdata != null) {
       
   407             int index = cpg.addMethodref(TRANSLET_CLASS,
       
   408                                          "addCdataElement",
       
   409                                          "(Ljava/lang/String;)V");
       
   410 
       
   411             StringTokenizer tokens = new StringTokenizer(_cdata);
       
   412             while (tokens.hasMoreTokens()) {
       
   413                 il.append(DUP);
       
   414                 il.append(new PUSH(cpg, tokens.nextToken()));
       
   415                 il.append(new INVOKEVIRTUAL(index));
       
   416             }
       
   417         }
       
   418         il.append(POP); // Cleanup - pop last translet reference off stack
       
   419     }
       
   420 
       
   421 }