1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-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: OutputPropertiesFactory.java,v 1.2.4.1 2005/09/15 08:15:21 suresh_emailid Exp $ 22 */ 23 package com.sun.org.apache.xml.internal.serializer; 24 25 import java.io.BufferedInputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.security.AccessController; 29 import java.security.PrivilegedAction; 30 import java.util.Enumeration; 31 import java.util.Properties; 32 33 import javax.xml.transform.OutputKeys; 34 35 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey; 36 import com.sun.org.apache.xml.internal.serializer.utils.Utils; 37 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException; 38 39 /** 40 * This class is a factory to generate a set of default properties 41 * of key/value pairs that are used to create a serializer through the 42 * factory {@link SerializerFactory SerilizerFactory}. 43 * The properties generated by this factory 44 * may be modified to non-default values before the SerializerFactory is used to 45 * create a Serializer. 46 * <p> 47 * The given output types supported are "xml", "text", and "html". 48 * These type strings can be obtained from the 49 * {@link Method Method} class in this package. 50 * <p> 51 * Other constants defined in this class are the non-standard property keys 52 * that can be used to set non-standard property values on a java.util.Properties object 53 * that is used to create or configure a serializer. Here are the non-standard keys: 54 * <ul> 55 * <li> <b>S_KEY_INDENT_AMOUNT </b> - 56 * The non-standard property key to use to set the indentation amount. 57 * The "indent" key needs to have a value of "yes", and this 58 * properties value is a the number of whitespaces to indent by per 59 * indentation level. 60 * 61 * <li> <b>S_KEY_CONTENT_HANDLER </b> - 62 * This non-standard property key is used to set the name of the fully qualified 63 * Java class that implements the ContentHandler interface. 64 * The output of the serializer will be SAX events sent to this an 65 * object of this class. 66 * 67 * <li> <b>S_KEY_ENTITIES </b> - 68 * This non-standard property key is used to specify the name of the property file 69 * that specifies character to entity reference mappings. A line in such a 70 * file is has the name of the entity and the numeric (base 10) value 71 * of the corresponding character, like this one: <br> quot=34 <br> 72 * 73 * <li> <b>S_USE_URL_ESCAPING </b> - 74 * This non-standard property key is used to set a value of "yes" if the href values for HTML serialization should 75 * use %xx escaping. 76 * 77 * <li> <b>S_OMIT_META_TAG </b> - 78 * This non-standard property key is used to set a value of "yes" if the META tag should be omitted where it would 79 * otherwise be supplied. 80 * </ul> 81 * 82 * @see SerializerFactory 83 * @see Method 84 * @see Serializer 85 */ 86 public final class OutputPropertiesFactory 87 { 88 /** S_BUILTIN_EXTENSIONS_URL is a mnemonic for the XML Namespace 89 *(http://xml.apache.org/xalan) predefined to signify Xalan's 90 * built-in XSLT Extensions. When used in stylesheets, this is often 91 * bound to the "xalan:" prefix. 92 */ 93 private static final String 94 S_BUILTIN_EXTENSIONS_URL = "http://xml.apache.org/xalan"; 95 96 /** 97 * The old built-in extension url. It is still supported for 98 * backward compatibility. 99 */ 100 private static final String 101 S_BUILTIN_OLD_EXTENSIONS_URL = "http://xml.apache.org/xslt"; 102 103 //************************************************************ 104 //* PUBLIC CONSTANTS 105 //************************************************************ 106 /** 107 * This is not a public API. 108 * This is the built-in extensions namespace, 109 * reexpressed in {namespaceURI} syntax 110 * suitable for prepending to a localname to produce a "universal 111 * name". 112 */ 113 public static final String S_BUILTIN_EXTENSIONS_UNIVERSAL = 114 "{" + S_BUILTIN_EXTENSIONS_URL + "}"; 115 116 // Some special Xalan keys. 117 118 /** 119 * The non-standard property key to use to set the 120 * number of whitepaces to indent by, per indentation level, 121 * if indent="yes". 122 */ 123 public static final String S_KEY_INDENT_AMOUNT = 124 S_BUILTIN_EXTENSIONS_UNIVERSAL + "indent-amount"; 125 126 /** 127 * The non-standard property key to use to set the 128 * number of whitepaces to indent by, per indentation level, 129 * if indent="yes". 130 */ 131 public static final String S_KEY_LINE_SEPARATOR = 132 S_BUILTIN_EXTENSIONS_UNIVERSAL + "line-separator"; 133 134 /** This non-standard property key is used to set the name of the fully qualified 135 * Java class that implements the ContentHandler interface. 136 * Fully qualified name of class with a default constructor that 137 * implements the ContentHandler interface, where the result tree events 138 * will be sent to. 139 */ 140 141 public static final String S_KEY_CONTENT_HANDLER = 142 S_BUILTIN_EXTENSIONS_UNIVERSAL + "content-handler"; 143 144 /** 145 * This non-standard property key is used to specify the name of the property file 146 * that specifies character to entity reference mappings. 147 */ 148 public static final String S_KEY_ENTITIES = 149 S_BUILTIN_EXTENSIONS_UNIVERSAL + "entities"; 150 151 /** 152 * This non-standard property key is used to set a value of "yes" if the href values for HTML serialization should 153 * use %xx escaping. */ 154 public static final String S_USE_URL_ESCAPING = 155 S_BUILTIN_EXTENSIONS_UNIVERSAL + "use-url-escaping"; 156 157 /** 158 * This non-standard property key is used to set a value of "yes" if the META tag should be omitted where it would 159 * otherwise be supplied. 160 */ 161 public static final String S_OMIT_META_TAG = 162 S_BUILTIN_EXTENSIONS_UNIVERSAL + "omit-meta-tag"; 163 164 /** 165 * The old built-in extension namespace, this is not a public API. 166 */ 167 public static final String S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL = 168 "{" + S_BUILTIN_OLD_EXTENSIONS_URL + "}"; 169 170 /** 171 * This is not a public API, it is only public because it is used 172 * by outside of this package, 173 * it is the length of the old built-in extension namespace. 174 */ 175 public static final int S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN = 176 S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL.length(); 177 178 //************************************************************ 179 //* PRIVATE CONSTANTS 180 //************************************************************ 181 182 private static final String S_XSLT_PREFIX = "xslt.output."; 183 private static final int S_XSLT_PREFIX_LEN = S_XSLT_PREFIX.length(); 184 private static final String S_XALAN_PREFIX = "org.apache.xslt."; 185 private static final int S_XALAN_PREFIX_LEN = S_XALAN_PREFIX.length(); 186 187 /** Synchronization object for lazy initialization of the above tables. */ 188 private static Integer m_synch_object = new Integer(1); 189 190 /** the directory in which the various method property files are located */ 191 private static final String PROP_DIR = "com/sun/org/apache/xml/internal/serializer/"; 192 /** property file for default XML properties */ 193 private static final String PROP_FILE_XML = "output_xml.properties"; 194 /** property file for default TEXT properties */ 195 private static final String PROP_FILE_TEXT = "output_text.properties"; 196 /** property file for default HTML properties */ 197 private static final String PROP_FILE_HTML = "output_html.properties"; 198 /** property file for default UNKNOWN (Either XML or HTML, to be determined later) properties */ 199 private static final String PROP_FILE_UNKNOWN = "output_unknown.properties"; 200 201 //************************************************************ 202 //* PRIVATE STATIC FIELDS 203 //************************************************************ 204 205 /** The default properties of all output files. */ 206 private static Properties m_xml_properties = null; 207 208 /** The default properties when method="html". */ 209 private static Properties m_html_properties = null; 210 211 /** The default properties when method="text". */ 212 private static Properties m_text_properties = null; 213 214 /** The properties when method="" for the "unknown" wrapper */ 215 private static Properties m_unknown_properties = null; 216 217 private static final Class 218 ACCESS_CONTROLLER_CLASS = findAccessControllerClass(); 219 220 private static Class findAccessControllerClass() { 221 try 222 { 223 // This Class was introduced in JDK 1.2. With the re-architecture of 224 // security mechanism ( starting in JDK 1.2 ), we have option of 225 // giving privileges to certain part of code using doPrivileged block. 226 // In JDK1.1.X applications won't be having security manager and if 227 // there is security manager ( in applets ), code need to be signed 228 // and trusted for having access to resources. 229 230 return Class.forName("java.security.AccessController"); 231 } 232 catch (Exception e) 233 { 234 //User may be using older JDK ( JDK <1.2 ). Allow him/her to use it. 235 // But don't try to use doPrivileged 236 } 237 238 return null; 239 } 240 241 /** 242 * Creates an empty OutputProperties with the property key/value defaults specified by 243 * a property file. The method argument is used to construct a string of 244 * the form output_[method].properties (for instance, output_html.properties). 245 * The output_xml.properties file is always used as the base. 246 * 247 * <p>Anything other than 'text', 'xml', and 'html', will 248 * use the output_xml.properties file.</p> 249 * 250 * @param method non-null reference to method name. 251 * 252 * @return Properties object that holds the defaults for the given method. 253 */ 254 static public final Properties getDefaultMethodProperties(String method) 255 { 256 String fileName = null; 257 Properties defaultProperties = null; 258 // According to this article : Double-check locking does not work 259 // http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-toolbox.html 260 try 261 { 262 synchronized (m_synch_object) 263 { 264 if (null == m_xml_properties) // double check 265 { 266 fileName = PROP_FILE_XML; 267 m_xml_properties = loadPropertiesFile(fileName, null); 268 } 269 } 270 271 if (method.equals(Method.XML)) 272 { 273 defaultProperties = m_xml_properties; 274 } 275 else if (method.equals(Method.HTML)) 276 { 277 if (null == m_html_properties) // double check 278 { 279 fileName = PROP_FILE_HTML; 280 m_html_properties = 281 loadPropertiesFile(fileName, m_xml_properties); 282 } 283 284 defaultProperties = m_html_properties; 285 } 286 else if (method.equals(Method.TEXT)) 287 { 288 if (null == m_text_properties) // double check 289 { 290 fileName = PROP_FILE_TEXT; 291 m_text_properties = 292 loadPropertiesFile(fileName, m_xml_properties); 293 if (null 294 == m_text_properties.getProperty(OutputKeys.ENCODING)) 295 { 296 String mimeEncoding = Encodings.getMimeEncoding(null); 297 m_text_properties.put( 298 OutputKeys.ENCODING, 299 mimeEncoding); 300 } 301 } 302 303 defaultProperties = m_text_properties; 304 } 305 else if (method.equals(com.sun.org.apache.xml.internal.serializer.Method.UNKNOWN)) 306 { 307 if (null == m_unknown_properties) // double check 308 { 309 fileName = PROP_FILE_UNKNOWN; 310 m_unknown_properties = 311 loadPropertiesFile(fileName, m_xml_properties); 312 } 313 314 defaultProperties = m_unknown_properties; 315 } 316 else 317 { 318 // TODO: Calculate res file from name. 319 defaultProperties = m_xml_properties; 320 } 321 } 322 catch (IOException ioe) 323 { 324 throw new WrappedRuntimeException( 325 Utils.messages.createMessage( 326 MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY, 327 new Object[] { fileName, method }), 328 ioe); 329 } 330 // wrap these cached defaultProperties in a new Property object just so 331 // that the caller of this method can't modify the default values 332 return new Properties(defaultProperties); 333 } 334 335 /** 336 * Load the properties file from a resource stream. If a 337 * key name such as "org.apache.xslt.xxx", fix up the start of 338 * string to be a curly namespace. If a key name starts with 339 * "xslt.output.xxx", clip off "xslt.output.". If a key name *or* a 340 * key value is discovered, check for \u003a in the text, and 341 * fix it up to be ":", since earlier versions of the JDK do not 342 * handle the escape sequence (at least in key names). 343 * 344 * @param resourceName non-null reference to resource name. 345 * @param defaults Default properties, which may be null. 346 */ 347 static private Properties loadPropertiesFile( 348 final String resourceName, 349 Properties defaults) 350 throws IOException 351 { 352 353 // This static method should eventually be moved to a thread-specific class 354 // so that we can cache the ContextClassLoader and bottleneck all properties file 355 // loading throughout Xalan. 356 357 Properties props = new Properties(defaults); 358 359 InputStream is = null; 360 BufferedInputStream bis = null; 361 362 try 363 { 364 if (ACCESS_CONTROLLER_CLASS != null) 365 { 366 is = (InputStream) AccessController 367 .doPrivileged(new PrivilegedAction() { 368 public Object run() 369 { 370 return OutputPropertiesFactory.class 371 .getResourceAsStream(resourceName); 372 } 373 }); 374 } 375 else 376 { 377 // User may be using older JDK ( JDK < 1.2 ) 378 is = OutputPropertiesFactory.class 379 .getResourceAsStream(resourceName); 380 } 381 382 bis = new BufferedInputStream(is); 383 props.load(bis); 384 } 385 catch (IOException ioe) 386 { 387 if (defaults == null) 388 { 389 throw ioe; 390 } 391 else 392 { 393 throw new WrappedRuntimeException( 394 Utils.messages.createMessage( 395 MsgKey.ER_COULD_NOT_LOAD_RESOURCE, 396 new Object[] { resourceName }), 397 ioe); 398 //"Could not load '"+resourceName+"' (check CLASSPATH), now using just the defaults ", ioe); 399 } 400 } 401 catch (SecurityException se) 402 { 403 // Repeat IOException handling for sandbox/applet case -sc 404 if (defaults == null) 405 { 406 throw se; 407 } 408 else 409 { 410 throw new WrappedRuntimeException( 411 Utils.messages.createMessage( 412 MsgKey.ER_COULD_NOT_LOAD_RESOURCE, 413 new Object[] { resourceName }), 414 se); 415 //"Could not load '"+resourceName+"' (check CLASSPATH, applet security), now using just the defaults ", se); 416 } 417 } 418 finally 419 { 420 if (bis != null) 421 { 422 bis.close(); 423 } 424 if (is != null) 425 { 426 is.close(); 427 } 428 } 429 430 // Note that we're working at the HashTable level here, 431 // and not at the Properties level! This is important 432 // because we don't want to modify the default properties. 433 // NB: If fixupPropertyString ends up changing the property 434 // name or value, we need to remove the old key and re-add 435 // with the new key and value. However, then our Enumeration 436 // could lose its place in the HashTable. So, we first 437 // clone the HashTable and enumerate over that since the 438 // clone will not change. When we migrate to Collections, 439 // this code should be revisited and cleaned up to use 440 // an Iterator which may (or may not) alleviate the need for 441 // the clone. Many thanks to Padraig O'hIceadha 442 // <padraig@gradient.ie> for finding this problem. Bugzilla 2000. 443 444 Enumeration keys = ((Properties) props.clone()).keys(); 445 while (keys.hasMoreElements()) 446 { 447 String key = (String) keys.nextElement(); 448 // Now check if the given key was specified as a 449 // System property. If so, the system property 450 // overides the default value in the propery file. 451 String value = null; 452 try 453 { 454 value = System.getProperty(key); 455 } 456 catch (SecurityException se) 457 { 458 // No-op for sandbox/applet case, leave null -sc 459 } 460 if (value == null) 461 value = (String) props.get(key); 462 463 String newKey = fixupPropertyString(key, true); 464 String newValue = null; 465 try 466 { 467 newValue = System.getProperty(newKey); 468 } 469 catch (SecurityException se) 470 { 471 // No-op for sandbox/applet case, leave null -sc 472 } 473 if (newValue == null) 474 newValue = fixupPropertyString(value, false); 475 else 476 newValue = fixupPropertyString(newValue, false); 477 478 if (key != newKey || value != newValue) 479 { 480 props.remove(key); 481 props.put(newKey, newValue); 482 } 483 484 } 485 486 return props; 487 } 488 489 /** 490 * Fix up a string in an output properties file according to 491 * the rules of {@link #loadPropertiesFile}. 492 * 493 * @param s non-null reference to string that may need to be fixed up. 494 * @return A new string if fixup occured, otherwise the s argument. 495 */ 496 static private String fixupPropertyString(String s, boolean doClipping) 497 { 498 int index; 499 if (doClipping && s.startsWith(S_XSLT_PREFIX)) 500 { 501 s = s.substring(S_XSLT_PREFIX_LEN); 502 } 503 if (s.startsWith(S_XALAN_PREFIX)) 504 { 505 s = 506 S_BUILTIN_EXTENSIONS_UNIVERSAL 507 + s.substring(S_XALAN_PREFIX_LEN); 508 } 509 if ((index = s.indexOf("\\u003a")) > 0) 510 { 511 String temp = s.substring(index + 6); 512 s = s.substring(0, index) + ":" + temp; 513 514 } 515 return s; 516 } 517 518 }