/plugins/XML/tags/release-2-0-4/xml/CatalogManager.java

# · Java · 639 lines · 454 code · 76 blank · 109 comment · 94 complexity · 9cd0ba2d9916c9f12f79de07e691a43a MD5 · raw file

  1. /*
  2. * CatalogManager.java
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001, 2003 Slava Pestov
  7. * Portions copyright (C) 2002 Chris Stevenson
  8. *
  9. * The XML plugin is licensed under the GNU General Public License, with
  10. * the following exception:
  11. *
  12. * "Permission is granted to link this code with software released under
  13. * the Apache license version 1.1, for example used by the Xerces XML
  14. * parser package."
  15. */
  16. package xml;
  17. //{{{ Imports
  18. import java.awt.Component;
  19. import java.io.*;
  20. import java.net.MalformedURLException;
  21. import java.net.URL;
  22. import java.util.HashMap;
  23. import java.util.HashSet;
  24. import java.util.Iterator;
  25. import java.util.Set;
  26. import javax.swing.JOptionPane;
  27. import javax.swing.SwingUtilities;
  28. import org.apache.xml.resolver.Catalog;
  29. import org.gjt.sp.jedit.*;
  30. import org.gjt.sp.jedit.io.VFS;
  31. import org.gjt.sp.jedit.io.VFSManager;
  32. import org.gjt.sp.jedit.msg.VFSUpdate;
  33. import org.gjt.sp.util.Log;
  34. import org.xml.sax.InputSource;
  35. //}}}
  36. /**
  37. *
  38. */
  39. public class CatalogManager
  40. {
  41. //{{{ resolve() method
  42. /**
  43. * @param current
  44. * @param publicId
  45. * @param systemId
  46. */
  47. public static InputSource resolve(String current,
  48. String publicId, String systemId)
  49. throws Exception
  50. {
  51. load();
  52. if(publicId != null && publicId.length() == 0)
  53. publicId = null;
  54. if(systemId != null && systemId.length() == 0)
  55. systemId = null;
  56. String newSystemId = null;
  57. /* we need this hack to support relative path names inside
  58. * cached files. we want them to be resolved relative to
  59. * the original system ID of the cached resource, not the
  60. * cache file name on disk. */
  61. String parent;
  62. if(current != null)
  63. {
  64. Entry entry = (Entry)reverseResourceCache.get(current);
  65. if(entry != null)
  66. parent = entry.uri;
  67. else
  68. parent = MiscUtilities.getParentOfPath(current);
  69. }
  70. else
  71. parent = null;
  72. if(publicId == null && systemId != null && parent != null)
  73. {
  74. if(systemId.startsWith(parent))
  75. {
  76. // first, try resolving a relative name,
  77. // to handle jEdit built-in DTDs
  78. newSystemId = systemId.substring(
  79. parent.length());
  80. if(newSystemId.startsWith("/"))
  81. newSystemId = newSystemId.substring(1);
  82. newSystemId = resolveSystem(newSystemId);
  83. }
  84. }
  85. // next, try resolving full path name
  86. if(newSystemId == null)
  87. {
  88. if(publicId == null)
  89. newSystemId = resolveSystem(systemId);
  90. else
  91. newSystemId = resolvePublic(systemId,publicId);
  92. }
  93. // well, the catalog can't help us, so just assume the
  94. // system id points to a file
  95. if(newSystemId == null)
  96. {
  97. if(systemId == null)
  98. return null;
  99. else if(MiscUtilities.isURL(systemId))
  100. newSystemId = systemId;
  101. // XXX: is this correct?
  102. /* else if(systemId.startsWith("/"))
  103. newSystemId = "file://" + systemId;
  104. else if(parent != null && !MiscUtilities.isURL(parent))
  105. newSystemId = parent + systemId; */
  106. }
  107. if(newSystemId == null)
  108. return null;
  109. Buffer buf = jEdit.getBuffer(XmlPlugin.uriToFile(newSystemId));
  110. if(buf != null)
  111. {
  112. if(buf.isPerformingIO())
  113. VFSManager.waitForRequests();
  114. Log.log(Log.DEBUG,CatalogManager.class,"Found open buffer for " + newSystemId);
  115. InputSource source = new InputSource(systemId);
  116. try
  117. {
  118. buf.readLock();
  119. source.setCharacterStream(new StringReader(buf.getText(0,
  120. buf.getLength())));
  121. }
  122. finally
  123. {
  124. buf.readUnlock();
  125. }
  126. return source;
  127. }
  128. else if(newSystemId.startsWith("file:")
  129. || newSystemId.startsWith("jeditresource:"))
  130. {
  131. InputSource source = new InputSource(systemId);
  132. source.setByteStream(new URL(newSystemId).openStream());
  133. return source;
  134. }
  135. else if(!network)
  136. return null;
  137. else
  138. {
  139. final String _newSystemId = newSystemId;
  140. final VFS vfs = VFSManager.getVFSForPath(_newSystemId);
  141. // use a final array to pass a mutable value from the
  142. // invokeAndWait() call
  143. final Object[] session = new Object[1];
  144. Runnable run = new Runnable()
  145. {
  146. public void run()
  147. {
  148. View view = jEdit.getActiveView();
  149. if (Resolver.getNetworkModeVal() == Resolver.LOCAL) return;
  150. if (Resolver.getNetworkModeVal()==Resolver.ASK &&
  151. showDownloadResourceDialog(view,_newSystemId))
  152. {
  153. session[0] = vfs.createVFSSession(
  154. _newSystemId,view);
  155. }
  156. }
  157. };
  158. if(SwingUtilities.isEventDispatchThread())
  159. run.run();
  160. else
  161. {
  162. try
  163. {
  164. SwingUtilities.invokeAndWait(run);
  165. }
  166. catch(Exception e)
  167. {
  168. throw new RuntimeException(e);
  169. // Log.log(Log.ERROR,CatalogManager.class,e);
  170. }
  171. }
  172. if(session[0] != null)
  173. {
  174. InputSource source = new InputSource(systemId);
  175. if(Resolver.isUsingCache())
  176. {
  177. File file;
  178. try
  179. {
  180. file = copyToLocalFile(session[0],vfs,newSystemId);
  181. }
  182. finally
  183. {
  184. vfs._endVFSSession(session,null);
  185. }
  186. addUserResource(publicId,systemId,file.toURL().toString());
  187. source.setByteStream(new FileInputStream(file));
  188. }
  189. else
  190. source.setByteStream(vfs._createInputStream(session,newSystemId,false,null));
  191. return source;
  192. }
  193. else
  194. throw new IOException(jEdit.getProperty("xml.network-error"));
  195. }
  196. } //}}}
  197. /*{{{ reload() method
  198. public static void reload(Entry e)
  199. {
  200. if(e.type == Entry.PUBLIC)
  201. {
  202. throw new RuntimeException(
  203. "Cannot reload a DTD from a PUBLIC id, only a SYSTEM id." );
  204. }
  205. try
  206. {
  207. if(isLocal(e))
  208. {
  209. File oldDtdFile = new File((String)resourceCache.get(e));
  210. File newFile = copyToLocalFile(new URL(e.id));
  211. oldDtdFile.delete();
  212. newFile.renameTo(oldDtdFile);
  213. JOptionPane.showMessageDialog(
  214. null,
  215. "Reloaded DTD to " + oldDtdFile );
  216. }
  217. }
  218. catch (Exception ex)
  219. {
  220. JOptionPane.showMessageDialog( null, ex.getMessage() );
  221. }
  222. } *///}}}
  223. //{{{ isLocal() method
  224. public static boolean isLocal(Entry e)
  225. {
  226. if(e == null || jEdit.getSettingsDirectory() == null)
  227. return false;
  228. try
  229. {
  230. URL url = new File(jEdit.getSettingsDirectory()).toURL();
  231. String fileUrl = (String)resourceCache.get(e);
  232. return fileUrl.startsWith(url.toString());
  233. }
  234. catch (MalformedURLException ex)
  235. {
  236. return false;
  237. }
  238. } //}}}
  239. //{{{ propertiesChanged() method
  240. public static void propertiesChanged()
  241. {
  242. if(jEdit.getSettingsDirectory() == null)
  243. {
  244. Resolver.setUsingCache(false);
  245. }
  246. else
  247. {
  248. resourceDir = MiscUtilities.constructPath(
  249. jEdit.getSettingsDirectory(),"dtds");
  250. }
  251. network = Resolver.getNetworkModeVal() != Resolver.LOCAL;
  252. if(!Resolver.isUsingCache())
  253. clearCache();
  254. loadedCatalogs = false;
  255. } //}}}
  256. //{{{ save() method
  257. public static void save()
  258. {
  259. if(loadedCache)
  260. {
  261. int systemCount = 0;
  262. int publicCount = 0;
  263. Iterator keys = resourceCache.keySet().iterator();
  264. while(keys.hasNext())
  265. {
  266. Entry entry = (Entry)keys.next();
  267. Object uri = resourceCache.get(entry);
  268. if(uri == IGNORE)
  269. continue;
  270. if(entry.type == Entry.PUBLIC)
  271. {
  272. jEdit.setProperty("xml.cache.public-id." + publicCount,entry.id);
  273. jEdit.setProperty("xml.cache.public-id." + publicCount
  274. + ".uri",uri.toString());
  275. publicCount++;
  276. }
  277. else
  278. {
  279. jEdit.setProperty("xml.cache.system-id." + systemCount,entry.id);
  280. jEdit.setProperty("xml.cache.system-id." + systemCount
  281. + ".uri",uri.toString());
  282. systemCount++;
  283. }
  284. }
  285. jEdit.unsetProperty("xml.cache.public-id." + publicCount);
  286. jEdit.unsetProperty("xml.cache.public-id." + publicCount + ".uri");
  287. jEdit.unsetProperty("xml.cache.system-id." + systemCount);
  288. jEdit.unsetProperty("xml.cache.system-id." + systemCount + ".uri");
  289. }
  290. } //}}}
  291. //{{{ clearCache() method
  292. public static void clearCache()
  293. {
  294. load();
  295. Iterator files = resourceCache.values().iterator();
  296. while(files.hasNext())
  297. {
  298. Object obj = files.next();
  299. if(obj instanceof String)
  300. {
  301. String file = (String)XmlPlugin.uriToFile((String)obj);
  302. Log.log(Log.NOTICE,CatalogManager.class,"Deleting " + file);
  303. new File(file).delete();
  304. }
  305. }
  306. resourceCache.clear();
  307. } //}}}
  308. //{{{ reloadCatalogs() method
  309. public static void reloadCatalogs()
  310. {
  311. loadedCatalogs = false;
  312. } //}}}
  313. //{{{ Package-private members
  314. //{{{ init() method
  315. static void init()
  316. {
  317. EditBus.addToBus(vfsUpdateHandler = new VFSUpdateHandler());
  318. } //}}}
  319. //{{{ uninit() method
  320. static void uninit()
  321. {
  322. EditBus.removeFromBus(vfsUpdateHandler);
  323. } //}}}
  324. //}}}
  325. //{{{ Private members
  326. //{{{ Static variables
  327. private static boolean loadedCache;
  328. private static boolean loadedCatalogs;
  329. private static boolean network;
  330. private static Catalog catalog;
  331. private static Set catalogFiles;
  332. private static HashMap resourceCache;
  333. private static HashMap reverseResourceCache;
  334. private static String resourceDir;
  335. // placeholder for DTDs we never want to download
  336. private static Object IGNORE = new Object();
  337. private static EBComponent vfsUpdateHandler;
  338. //}}}
  339. //{{{ addUserResource() method
  340. /**
  341. * Don't want this public because then invoking {@link clearCache()}
  342. * will remove this file, not what you would expect!
  343. */
  344. private static void addUserResource(String publicId, String systemId, String url)
  345. {
  346. if(publicId != null)
  347. {
  348. Entry pe = new Entry( Entry.PUBLIC, publicId, url );
  349. resourceCache.put( pe, url );
  350. }
  351. Entry se = new Entry( Entry.SYSTEM, systemId, url );
  352. resourceCache.put( se, url );
  353. reverseResourceCache.put(url,se);
  354. } //}}}
  355. //{{{ copyToLocalFile() method
  356. private static File copyToLocalFile(Object session, VFS vfs, String path)
  357. throws IOException
  358. {
  359. if(jEdit.getSettingsDirectory() == null)
  360. return null;
  361. // String userDir = jEdit.getSettingsDirectory();
  362. File _resourceDir = new File(resourceDir);
  363. if (!_resourceDir.exists())
  364. _resourceDir.mkdir();
  365. // Need to put this "copy from one stream to another"
  366. // into a common method some day, since other parts
  367. // of jEdit need it too...
  368. BufferedInputStream in = new BufferedInputStream(
  369. vfs._createInputStream(session,path,false,null));
  370. File localFile = File.createTempFile("cache", ".xml", _resourceDir);
  371. BufferedOutputStream out = new BufferedOutputStream(
  372. new FileOutputStream(localFile));
  373. byte[] buf = new byte[4096];
  374. int count = 0;
  375. while ((count = in.read(buf)) != -1)
  376. out.write(buf,0,count);
  377. out.close();
  378. return localFile;
  379. } //}}}
  380. //{{{ resolvePublic() method
  381. private static String resolvePublic(String systemId, String publicId)
  382. throws Exception
  383. {
  384. Entry e = new Entry(Entry.PUBLIC,publicId,null);
  385. String uri = (String)resourceCache.get(e);
  386. if(uri == null)
  387. return catalog.resolvePublic(publicId,null);
  388. else if(uri == IGNORE)
  389. return null;
  390. else
  391. return uri;
  392. } //}}}
  393. //{{{ resolveSystem() method
  394. public static String resolveSystem(String id) throws Exception
  395. {
  396. Entry e = new Entry(Entry.SYSTEM,id,null);
  397. String uri = (String)resourceCache.get(e);
  398. if(uri == null)
  399. return catalog.resolveSystem(id);
  400. else if(uri == IGNORE)
  401. return null;
  402. else
  403. return uri;
  404. } //}}}
  405. //{{{ showDownloadResourceDialog() method
  406. private static boolean showDownloadResourceDialog(Component comp, String systemId)
  407. {
  408. Entry e = new Entry(Entry.SYSTEM,systemId,null);
  409. if(resourceCache.get(e) == IGNORE)
  410. return false;
  411. int result = GUIUtilities.confirm(comp,"xml.download-resource",
  412. new String[] { systemId },JOptionPane.YES_NO_OPTION,
  413. JOptionPane.QUESTION_MESSAGE);
  414. if(result == JOptionPane.YES_OPTION)
  415. return true;
  416. else
  417. {
  418. resourceCache.put(e,IGNORE);
  419. return false;
  420. }
  421. } //}}}
  422. //{{{ load() method
  423. private synchronized static void load()
  424. {
  425. if(!loadedCache)
  426. {
  427. loadedCache = true;
  428. resourceCache = new HashMap();
  429. reverseResourceCache = new HashMap();
  430. int i;
  431. String id, prop, uri;
  432. i = 0;
  433. while((id = jEdit.getProperty(prop = "xml.cache"
  434. + ".public-id." + i++)) != null)
  435. {
  436. try
  437. {
  438. uri = jEdit.getProperty(prop + ".uri");
  439. resourceCache.put(new Entry(Entry.PUBLIC,id,uri),uri);
  440. }
  441. catch(Exception ex2)
  442. { ex2.printStackTrace();
  443. Log.log(Log.ERROR,CatalogManager.class,ex2);
  444. }
  445. }
  446. i = 0;
  447. while((id = jEdit.getProperty(prop = "xml.cache"
  448. + ".system-id." + i++)) != null)
  449. {
  450. try
  451. {
  452. uri = jEdit.getProperty(prop + ".uri");
  453. Entry se = new Entry(Entry.SYSTEM,id,uri);
  454. resourceCache.put(se,uri);
  455. reverseResourceCache.put(uri,se);
  456. }
  457. catch(Exception ex2)
  458. {
  459. ex2.printStackTrace();
  460. Log.log(Log.ERROR,CatalogManager.class,ex2);
  461. }
  462. }
  463. }
  464. if(!loadedCatalogs)
  465. {
  466. loadedCatalogs = true;
  467. catalog = new Catalog();
  468. catalogFiles = new HashSet();
  469. catalog.setupReaders();
  470. //catalog.setParserClass("org.apache.xerces.parsers.SAXParser");
  471. try
  472. {
  473. catalog.loadSystemCatalogs();
  474. catalog.parseCatalog("jeditresource:XML.jar!/xml/dtds/catalog");
  475. int i = 0;
  476. String prop, uri;
  477. while((uri = jEdit.getProperty(
  478. prop = "xml.catalog." + i++)) != null)
  479. {
  480. Log.log(Log.MESSAGE,CatalogManager.class,
  481. "Loading catalog: " + uri);
  482. try
  483. {
  484. if(MiscUtilities.isURL(uri))
  485. catalogFiles.add(uri);
  486. else
  487. {
  488. catalogFiles.add(
  489. MiscUtilities
  490. .resolveSymlinks(
  491. uri));
  492. }
  493. catalog.parseCatalog(uri);
  494. }
  495. catch(Exception ex2)
  496. {
  497. ex2.printStackTrace();
  498. Log.log(Log.ERROR,CatalogManager.class,ex2);
  499. }
  500. }
  501. }
  502. catch(Exception ex1)
  503. {
  504. Log.log(Log.ERROR,CatalogManager.class,ex1);
  505. ex1.printStackTrace();
  506. }
  507. }
  508. } //}}}
  509. //}}}
  510. //{{{ Entry class
  511. public static class Entry
  512. {
  513. public static final int SYSTEM = 0;
  514. public static final int PUBLIC = 1;
  515. public int type;
  516. public String id;
  517. public String uri;
  518. public Entry(int type, String id, String uri)
  519. {
  520. this.type = type;
  521. this.id = id;
  522. this.uri = uri;
  523. }
  524. public boolean equals(Object o)
  525. {
  526. if(o instanceof Entry)
  527. {
  528. Entry e = (Entry)o;
  529. return e.type == type && e.id.equals(id);
  530. }
  531. else
  532. return false;
  533. }
  534. public int hashCode()
  535. {
  536. return id.hashCode();
  537. }
  538. } //}}}
  539. //{{{ VFSUpdateHandler class
  540. /**
  541. * Reloads a catalog file when the user changes it on disk.
  542. */
  543. public static class VFSUpdateHandler implements EBComponent
  544. {
  545. public void handleMessage(EBMessage msg)
  546. {
  547. if(!loadedCatalogs)
  548. return;
  549. if(msg instanceof VFSUpdate)
  550. {
  551. String path = ((VFSUpdate)msg).getPath();
  552. if(catalogFiles.contains(path))
  553. loadedCatalogs = false;
  554. }
  555. }
  556. } //}}}
  557. }