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 }