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