1. /*
  2. * Copyright 1999-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: OutputPropertiesFactory.java,v 1.5 2004/02/18 22:57:44 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.serializer;
  20. import java.io.BufferedInputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.security.AccessController;
  24. import java.security.PrivilegedAction;
  25. import java.util.Enumeration;
  26. import java.util.Properties;
  27. import javax.xml.transform.OutputKeys;
  28. import com.sun.org.apache.xml.internal.res.XMLErrorResources;
  29. import com.sun.org.apache.xml.internal.res.XMLMessages;
  30. import com.sun.org.apache.xml.internal.utils.Constants;
  31. import com.sun.org.apache.xml.internal.utils.WrappedRuntimeException;
  32. /**
  33. * This class acts as a factory to generate properties for the given output type
  34. * ("xml", "text", "html")..
  35. */
  36. public class OutputPropertiesFactory
  37. {
  38. //************************************************************
  39. //* PUBLIC CONSTANTS
  40. //************************************************************
  41. /** Built-in extensions namespace, reexpressed in {namespaceURI} syntax
  42. * suitable for prepending to a localname to produce a "universal
  43. * name".
  44. */
  45. public static final String S_BUILTIN_EXTENSIONS_UNIVERSAL =
  46. "{" + Constants.S_BUILTIN_EXTENSIONS_URL + "}";
  47. // Some special Xalan keys.
  48. /** The number of whitespaces to indent by, if indent="yes". */
  49. public static final String S_KEY_INDENT_AMOUNT =
  50. S_BUILTIN_EXTENSIONS_UNIVERSAL + "indent-amount";
  51. /**
  52. * Fully qualified name of class with a default constructor that
  53. * implements the ContentHandler interface, where the result tree events
  54. * will be sent to.
  55. */
  56. public static final String S_KEY_CONTENT_HANDLER =
  57. S_BUILTIN_EXTENSIONS_UNIVERSAL + "content-handler";
  58. /** File name of file that specifies character to entity reference mappings. */
  59. public static final String S_KEY_ENTITIES =
  60. S_BUILTIN_EXTENSIONS_UNIVERSAL + "entities";
  61. /** Use a value of "yes" if the href values for HTML serialization should
  62. * use %xx escaping. */
  63. public static final String S_USE_URL_ESCAPING =
  64. S_BUILTIN_EXTENSIONS_UNIVERSAL + "use-url-escaping";
  65. /** Use a value of "yes" if the META tag should be omitted where it would
  66. * otherwise be supplied.
  67. */
  68. public static final String S_OMIT_META_TAG =
  69. S_BUILTIN_EXTENSIONS_UNIVERSAL + "omit-meta-tag";
  70. /**
  71. * The old built-in extension namespace
  72. */
  73. public static final String S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL =
  74. "{" + Constants.S_BUILTIN_OLD_EXTENSIONS_URL + "}";
  75. /**
  76. * The length of the old built-in extension namespace
  77. */
  78. public static final int S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN =
  79. S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL.length();
  80. //************************************************************
  81. //* PRIVATE CONSTANTS
  82. //************************************************************
  83. private static final String S_XSLT_PREFIX = "xslt.output.";
  84. private static final int S_XSLT_PREFIX_LEN = S_XSLT_PREFIX.length();
  85. private static final String S_XALAN_PREFIX = "org.apache.xslt.";
  86. private static final int S_XALAN_PREFIX_LEN = S_XALAN_PREFIX.length();
  87. /** Synchronization object for lazy initialization of the above tables. */
  88. private static Integer m_synch_object = new Integer(1);
  89. /** the directory in which the various method property files are located */
  90. private static final String PROP_DIR = "com/sun/org/apache/xml/internal/serializer/";
  91. /** property file for default XML properties */
  92. private static final String PROP_FILE_XML = "output_xml.properties";
  93. /** property file for default TEXT properties */
  94. private static final String PROP_FILE_TEXT = "output_text.properties";
  95. /** property file for default HTML properties */
  96. private static final String PROP_FILE_HTML = "output_html.properties";
  97. /** property file for default UNKNOWN (Either XML or HTML, to be determined later) properties */
  98. private static final String PROP_FILE_UNKNOWN = "output_unknown.properties";
  99. //************************************************************
  100. //* PRIVATE STATIC FIELDS
  101. //************************************************************
  102. /** The default properties of all output files. */
  103. private static Properties m_xml_properties = null;
  104. /** The default properties when method="html". */
  105. private static Properties m_html_properties = null;
  106. /** The default properties when method="text". */
  107. private static Properties m_text_properties = null;
  108. /** The properties when method="" for the "unknown" wrapper */
  109. private static Properties m_unknown_properties = null;
  110. private static final Class
  111. ACCESS_CONTROLLER_CLASS = findAccessControllerClass();
  112. private static Class findAccessControllerClass() {
  113. try
  114. {
  115. // This Class was introduced in JDK 1.2. With the re-architecture of
  116. // security mechanism ( starting in JDK 1.2 ), we have option of
  117. // giving privileges to certain part of code using doPrivileged block.
  118. // In JDK1.1.X applications won't be having security manager and if
  119. // there is security manager ( in applets ), code need to be signed
  120. // and trusted for having access to resources.
  121. return Class.forName("java.security.AccessController");
  122. }
  123. catch (Exception e)
  124. {
  125. //User may be using older JDK ( JDK <1.2 ). Allow him/her to use it.
  126. // But don't try to use doPrivileged
  127. }
  128. return null;
  129. }
  130. /**
  131. * Creates an empty OutputProperties with the defaults specified by
  132. * a property file. The method argument is used to construct a string of
  133. * the form output_[method].properties (for instance, output_html.properties).
  134. * The output_xml.properties file is always used as the base.
  135. * <p>At the moment, anything other than 'text', 'xml', and 'html', will
  136. * use the output_xml.properties file.</p>
  137. *
  138. * @param method non-null reference to method name.
  139. *
  140. * @return Properties object that holds the defaults for the given method.
  141. */
  142. static public Properties getDefaultMethodProperties(String method)
  143. {
  144. String fileName = null;
  145. Properties defaultProperties = null;
  146. // According to this article : Double-check locking does not work
  147. // http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-toolbox.html
  148. try
  149. {
  150. synchronized (m_synch_object)
  151. {
  152. if (null == m_xml_properties) // double check
  153. {
  154. fileName = PROP_FILE_XML;
  155. m_xml_properties = loadPropertiesFile(fileName, null);
  156. }
  157. }
  158. if (method.equals(Method.XML))
  159. {
  160. defaultProperties = m_xml_properties;
  161. }
  162. else if (method.equals(Method.HTML))
  163. {
  164. if (null == m_html_properties) // double check
  165. {
  166. fileName = PROP_FILE_HTML;
  167. m_html_properties =
  168. loadPropertiesFile(fileName, m_xml_properties);
  169. }
  170. defaultProperties = m_html_properties;
  171. }
  172. else if (method.equals(Method.TEXT))
  173. {
  174. if (null == m_text_properties) // double check
  175. {
  176. fileName = PROP_FILE_TEXT;
  177. m_text_properties =
  178. loadPropertiesFile(fileName, m_xml_properties);
  179. if (null
  180. == m_text_properties.getProperty(OutputKeys.ENCODING))
  181. {
  182. String mimeEncoding = Encodings.getMimeEncoding(null);
  183. m_text_properties.put(
  184. OutputKeys.ENCODING,
  185. mimeEncoding);
  186. }
  187. }
  188. defaultProperties = m_text_properties;
  189. }
  190. else if (method.equals(com.sun.org.apache.xml.internal.serializer.Method.UNKNOWN))
  191. {
  192. if (null == m_unknown_properties) // double check
  193. {
  194. fileName = PROP_FILE_UNKNOWN;
  195. m_unknown_properties =
  196. loadPropertiesFile(fileName, m_xml_properties);
  197. }
  198. defaultProperties = m_unknown_properties;
  199. }
  200. else
  201. {
  202. // TODO: Calculate res file from name.
  203. defaultProperties = m_xml_properties;
  204. }
  205. }
  206. catch (IOException ioe)
  207. {
  208. throw new WrappedRuntimeException(
  209. XMLMessages.createXMLMessage(
  210. XMLErrorResources.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
  211. new Object[] { fileName, method }),
  212. ioe);
  213. }
  214. return new Properties(defaultProperties);
  215. }
  216. /**
  217. * Load the properties file from a resource stream. If a
  218. * key name such as "org.apache.xslt.xxx", fix up the start of
  219. * string to be a curly namespace. If a key name starts with
  220. * "xslt.output.xxx", clip off "xslt.output.". If a key name *or* a
  221. * key value is discovered, check for \u003a in the text, and
  222. * fix it up to be ":", since earlier versions of the JDK do not
  223. * handle the escape sequence (at least in key names).
  224. *
  225. * @param resourceName non-null reference to resource name.
  226. * @param defaults Default properties, which may be null.
  227. */
  228. static private Properties loadPropertiesFile(
  229. final String resourceName,
  230. Properties defaults)
  231. throws IOException
  232. {
  233. // This static method should eventually be moved to a thread-specific class
  234. // so that we can cache the ContextClassLoader and bottleneck all properties file
  235. // loading throughout Xalan.
  236. Properties props = new Properties(defaults);
  237. InputStream is = null;
  238. BufferedInputStream bis = null;
  239. try
  240. {
  241. if (ACCESS_CONTROLLER_CLASS != null)
  242. {
  243. is = (InputStream) AccessController
  244. .doPrivileged(new PrivilegedAction() {
  245. public Object run()
  246. {
  247. return OutputPropertiesFactory.class
  248. .getResourceAsStream(resourceName);
  249. }
  250. });
  251. }
  252. else
  253. {
  254. // User may be using older JDK ( JDK < 1.2 )
  255. is = OutputPropertiesFactory.class
  256. .getResourceAsStream(resourceName);
  257. }
  258. bis = new BufferedInputStream(is);
  259. props.load(bis);
  260. }
  261. catch (IOException ioe)
  262. {
  263. if (defaults == null)
  264. {
  265. throw ioe;
  266. }
  267. else
  268. {
  269. throw new WrappedRuntimeException(
  270. XMLMessages.createXMLMessage(
  271. XMLErrorResources.ER_COULD_NOT_LOAD_RESOURCE,
  272. new Object[] { resourceName }),
  273. ioe);
  274. //"Could not load '"+resourceName+"' (check CLASSPATH), now using just the defaults ", ioe);
  275. }
  276. }
  277. catch (SecurityException se)
  278. {
  279. // Repeat IOException handling for sandbox/applet case -sc
  280. if (defaults == null)
  281. {
  282. throw se;
  283. }
  284. else
  285. {
  286. throw new WrappedRuntimeException(
  287. XMLMessages.createXMLMessage(
  288. XMLErrorResources.ER_COULD_NOT_LOAD_RESOURCE,
  289. new Object[] { resourceName }),
  290. se);
  291. //"Could not load '"+resourceName+"' (check CLASSPATH, applet security), now using just the defaults ", se);
  292. }
  293. }
  294. finally
  295. {
  296. if (bis != null)
  297. {
  298. bis.close();
  299. }
  300. if (is != null)
  301. {
  302. is.close();
  303. }
  304. }
  305. // Note that we're working at the HashTable level here,
  306. // and not at the Properties level! This is important
  307. // because we don't want to modify the default properties.
  308. // NB: If fixupPropertyString ends up changing the property
  309. // name or value, we need to remove the old key and re-add
  310. // with the new key and value. However, then our Enumeration
  311. // could lose its place in the HashTable. So, we first
  312. // clone the HashTable and enumerate over that since the
  313. // clone will not change. When we migrate to Collections,
  314. // this code should be revisited and cleaned up to use
  315. // an Iterator which may (or may not) alleviate the need for
  316. // the clone. Many thanks to Padraig O'hIceadha
  317. // <padraig@gradient.ie> for finding this problem. Bugzilla 2000.
  318. Enumeration keys = ((Properties) props.clone()).keys();
  319. while (keys.hasMoreElements())
  320. {
  321. String key = (String) keys.nextElement();
  322. // Now check if the given key was specified as a
  323. // System property. If so, the system property
  324. // overides the default value in the propery file.
  325. String value = null;
  326. try
  327. {
  328. value = System.getProperty(key);
  329. }
  330. catch (SecurityException se)
  331. {
  332. // No-op for sandbox/applet case, leave null -sc
  333. }
  334. if (value == null)
  335. value = (String) props.get(key);
  336. String newKey = fixupPropertyString(key, true);
  337. String newValue = null;
  338. try
  339. {
  340. newValue = System.getProperty(newKey);
  341. }
  342. catch (SecurityException se)
  343. {
  344. // No-op for sandbox/applet case, leave null -sc
  345. }
  346. if (newValue == null)
  347. newValue = fixupPropertyString(value, false);
  348. else
  349. newValue = fixupPropertyString(newValue, false);
  350. if (key != newKey || value != newValue)
  351. {
  352. props.remove(key);
  353. props.put(newKey, newValue);
  354. }
  355. }
  356. return props;
  357. }
  358. /**
  359. * Fix up a string in an output properties file according to
  360. * the rules of {@link #loadPropertiesFile}.
  361. *
  362. * @param s non-null reference to string that may need to be fixed up.
  363. * @return A new string if fixup occured, otherwise the s argument.
  364. */
  365. static private String fixupPropertyString(String s, boolean doClipping)
  366. {
  367. int index;
  368. if (doClipping && s.startsWith(S_XSLT_PREFIX))
  369. {
  370. s = s.substring(S_XSLT_PREFIX_LEN);
  371. }
  372. if (s.startsWith(S_XALAN_PREFIX))
  373. {
  374. s =
  375. S_BUILTIN_EXTENSIONS_UNIVERSAL
  376. + s.substring(S_XALAN_PREFIX_LEN);
  377. }
  378. if ((index = s.indexOf("\\u003a")) > 0)
  379. {
  380. String temp = s.substring(index + 6);
  381. s = s.substring(0, index) + ":" + temp;
  382. }
  383. return s;
  384. }
  385. }