Resolver.java 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /* Resolver.java --
  2. Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.xml.util;
  32. import java.io.File;
  33. import java.io.IOException;
  34. import java.util.Dictionary;
  35. import org.xml.sax.EntityResolver;
  36. import org.xml.sax.InputSource;
  37. import org.xml.sax.SAXException;
  38. /**
  39. * Utility implementation of a SAX resolver, which can be used to improve
  40. * network utilization of SAX based XML components. It does this by
  41. * supporting local caches of external entities.
  42. * SAX parsers <em>should</em> use such local caches when possible.
  43. *
  44. * @see XCat
  45. */
  46. public class Resolver implements EntityResolver, Cloneable
  47. {
  48. /**
  49. * Updates a dictionary used to map PUBLIC identifiers to file names,
  50. * so that it uses the mappings in a specified directory.
  51. *
  52. * @param mappings Array of string pairs, where the first member
  53. * of each pair is a PUBLIC identifier and the second is the
  54. * name of a file, relative to the specified directory.
  55. * @param directory File holding the specified files.
  56. */
  57. public static void addDirectoryMapping (
  58. Dictionary table,
  59. String mappings [][],
  60. File directory
  61. ) throws IOException
  62. {
  63. for (int i = 0; i < mappings.length; i++) {
  64. File file = new File (directory, mappings [i][1]);
  65. String temp;
  66. if (!file.exists ()) // ?? log a warning ??
  67. continue;
  68. temp = fileToURL (file);
  69. table.put (mappings [i][0], temp);
  70. }
  71. }
  72. // FIXME: these *URL routines don't quite belong here, except
  73. // that they're all in the same spirit of making it easy to
  74. // use local filesystem URIs with XML parsers.
  75. /**
  76. * Provides the URL for a named file, without relying on the JDK 1.2
  77. * {@link java.io.File#toURL File.toURL}() utility method.
  78. *
  79. * @param filename the file name to convert. Relative file names
  80. * are resolved the way the JVM resolves them (current to the
  81. * process-global current working directory).
  82. *
  83. * @exception IOException if the file does not exist
  84. */
  85. public static String fileNameToURL (String filename)
  86. throws IOException
  87. {
  88. return fileToURL (new File (filename));
  89. }
  90. /**
  91. * Provides the URL for a file, without relying on the JDK 1.2
  92. * {@link java.io.File#toURL File.toURL}() utility method.
  93. *
  94. * @param f the file to convert. Relative file names
  95. * are resolved the way the JVM resolves them (current to the
  96. * process-global current working directory).
  97. *
  98. * @exception IOException if the file does not exist
  99. */
  100. public static String fileToURL (File f)
  101. throws IOException
  102. {
  103. String temp;
  104. // NOTE: the javax.xml.parsers.DocumentBuilder and
  105. // javax.xml.transform.stream.StreamSource versions
  106. // of this don't have this test. Some JVM versions
  107. // don't report this error sanely through URL code.
  108. if (!f.exists ())
  109. throw new IOException ("no such file: " + f.getName ());
  110. // FIXME: getAbsolutePath() seems buggy; I'm seeing components
  111. // like "/foo/../" which are clearly not "absolute"
  112. // and should have been resolved with the filesystem.
  113. // Substituting "/" would be wrong, "foo" may have been
  114. // symlinked ... the URL code will make that change
  115. // later, so that things can get _really_ broken!
  116. temp = f.getAbsolutePath ();
  117. if (File.separatorChar != '/')
  118. temp = temp.replace (File.separatorChar, '/');
  119. if (!temp.startsWith ("/"))
  120. temp = "/" + temp;
  121. if (!temp.endsWith ("/") && f.isDirectory ())
  122. temp = temp + "/";
  123. return "file:" + temp;
  124. }
  125. /**
  126. * Returns a URL string. Note that if a malformed URL is provided, or
  127. * the parameter names a nonexistent file, the resulting URL may be
  128. * malformed.
  129. *
  130. * @param fileOrURL If this is the name of a file which exists,
  131. * then its URL is returned. Otherwise the argument is returned.
  132. */
  133. public static String getURL (String fileOrURL)
  134. {
  135. try {
  136. return fileNameToURL (fileOrURL);
  137. } catch (Exception e) {
  138. return fileOrURL;
  139. }
  140. }
  141. // note: cloneable, this is just copied; unguarded against mods
  142. private Dictionary pubidMapping;
  143. /**
  144. * Constructs a resolver which understands how to map PUBLIC identifiers
  145. * to other URIs, typically for local copies of standard DTD components.
  146. *
  147. * @param dict maps PUBLIC identifiers to URIs. This is not
  148. * copied; subsequent modifications will be reported through the
  149. * resolution operations.
  150. */
  151. public Resolver (Dictionary dict)
  152. { pubidMapping = dict; }
  153. // FIXME: want notion of a "system default" resolver, presumably
  154. // loaded with all sorts of useful stuff. At the same time need
  155. // a notion of resolver chaining (failure --> next) so that subsystems
  156. // can set up things that won't interfere with other ones.
  157. /**
  158. * This parses most MIME content type strings that have <em>charset=...</em>
  159. * encoding declarations to and returns the specified encoding. This
  160. * conforms to RFC 3023, and is useful when constructing InputSource
  161. * objects from URLConnection objects or other objects using MIME
  162. * content typing.
  163. *
  164. * @param contentType the MIME content type that will be parsed; must
  165. * not be null.
  166. * @return the appropriate encoding, or null if the content type is
  167. * not text and there's no <code>charset=...</code> attribute
  168. */
  169. static public String getEncoding (String contentType)
  170. {
  171. // currently a dumb parsing algorithm that works "mostly" and handles
  172. // ..anything...charset=ABC
  173. // ..anything...charset=ABC;otherAttr=DEF
  174. // ..anything...charset=ABC (comment);otherAttr=DEF
  175. // ..anything...charset= "ABC" (comment);otherAttr=DEF
  176. int temp;
  177. String encoding;
  178. String defValue = null;
  179. if (contentType.startsWith ("text/"))
  180. defValue = contentType.startsWith ("text/html")
  181. ? "ISO-8859-1" : "US-ASCII";
  182. // Assumes 'charset' is only an attribute name, not part
  183. // of a value, comment, or other attribute name
  184. // ALSO assumes no escaped values like "\;" or "\)"
  185. if ((temp = contentType.indexOf ("charset")) != -1) {
  186. // strip out everything up to '=' ...
  187. temp = contentType.indexOf ('=', temp);
  188. if (temp == -1)
  189. return defValue;
  190. encoding = contentType.substring (temp + 1);
  191. // ... and any subsequent attributes
  192. if ((temp = encoding.indexOf (';')) != -1)
  193. encoding = encoding.substring (0, temp);
  194. // ... and any comments after value
  195. if ((temp = encoding.indexOf ('(')) != -1)
  196. encoding = encoding.substring (0, temp);
  197. // ... then whitespace, and any (double) quotes
  198. encoding = encoding.trim ();
  199. if (encoding.charAt (0) == '"')
  200. encoding = encoding.substring (1, encoding.length () - 1);
  201. } else
  202. encoding = defValue;
  203. return encoding;
  204. }
  205. /**
  206. * Uses a local dictionary of public identifiers to resolve URIs,
  207. * normally with the goal of minimizing network traffic or latencies.
  208. */
  209. public InputSource resolveEntity (String publicId, String systemId)
  210. throws IOException, SAXException
  211. {
  212. InputSource retval = null;
  213. String uri;
  214. if (publicId != null
  215. && ((uri = (String) pubidMapping.get (publicId)) != null)) {
  216. retval = new InputSource (uri);
  217. retval.setPublicId (publicId);
  218. }
  219. // Could do URN resolution here
  220. // URL resolution always done by parser
  221. // FIXME: chain to "next" resolver
  222. return retval;
  223. }
  224. }