/plugins/XML/trunk/xml/Resolver.java

# · Java · 967 lines · 645 code · 124 blank · 198 comment · 134 complexity · 92ee09342efe3a23257eed48d6789268 MD5 · raw file

  1. /*
  2. * Resolver.java
  3. * :folding=explicit:collapseFolds=1:
  4. *
  5. * Copyright (C) 2006 Alan Ezust
  6. * Portions Copyright (C) 2007 hertzhaft
  7. * Copyright (C) 2009 Eric Le Lay
  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.BufferedInputStream;
  20. import java.io.BufferedOutputStream;
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.InputStream;
  24. import java.io.Reader;
  25. import java.net.URL;
  26. import java.io.FileOutputStream;
  27. import java.io.IOException;
  28. import java.io.StringReader;
  29. import java.util.HashMap;
  30. import java.util.Iterator;
  31. import java.util.HashSet;
  32. import java.util.Set;
  33. import javax.swing.JOptionPane;
  34. import javax.swing.SwingUtilities;
  35. import org.apache.xml.resolver.Catalog;
  36. import org.gjt.sp.jedit.EditBus;
  37. import org.gjt.sp.jedit.EBComponent;
  38. import org.gjt.sp.jedit.EBMessage;
  39. import org.gjt.sp.jedit.Buffer;
  40. import org.gjt.sp.jedit.GUIUtilities;
  41. import org.gjt.sp.jedit.MiscUtilities;
  42. import org.gjt.sp.jedit.View;
  43. import org.gjt.sp.jedit.jEdit;
  44. import org.gjt.sp.jedit.io.VFS;
  45. import org.gjt.sp.jedit.io.VFSManager;
  46. import org.gjt.sp.jedit.msg.VFSUpdate;
  47. import org.gjt.sp.util.Log;
  48. import org.xml.sax.InputSource;
  49. import org.xml.sax.SAXException;
  50. import org.xml.sax.ext.EntityResolver2;
  51. // for jaxp
  52. import org.w3c.dom.ls.LSResourceResolver;
  53. import org.w3c.dom.ls.LSInput;
  54. import static xml.Debug.*;
  55. import xml.PathUtilities;
  56. // }}}
  57. /**
  58. * Resolver grabs and caches DTDs and xml schemas.
  59. * It also serves as a resource resolver for jeditresource: links
  60. *
  61. * @author ezust
  62. * @author kerik-sf
  63. * @version $Id: Resolver.java 21316 2012-03-11 10:33:42Z kerik-sf $
  64. *
  65. */
  66. public class Resolver implements EntityResolver2, LSResourceResolver
  67. {
  68. /** Ask before downloading */
  69. public static final String ASK = "ask";
  70. /** Local files & catalogs only */
  71. public static final String LOCAL = "local";
  72. /** Download without asking */
  73. public static final String ALWAYS = "always";
  74. public static final String MODES[] = new String[] {ASK, LOCAL, ALWAYS};
  75. private boolean loadedCache = false;
  76. private boolean loadedCatalogs = false;
  77. public static final String NETWORK_PROPS = "xml.general.network";
  78. //{{{ Package-private members
  79. //{{{ init() method
  80. void init()
  81. {
  82. // TODO: not sure about this handler : is it useful ?
  83. EditBus.addToBus(vfsUpdateHandler = new VFSUpdateHandler());
  84. } //}}}
  85. //{{{ uninit() method
  86. void uninit()
  87. {
  88. EditBus.removeFromBus(vfsUpdateHandler);
  89. // really forget current state
  90. singleton = null;
  91. } //}}}
  92. //{{{ save() method
  93. public void save()
  94. {
  95. if(loadedCache)
  96. {
  97. int systemCount = 0;
  98. int publicCount = 0;
  99. Iterator keys = resourceCache.keySet().iterator();
  100. while(keys.hasNext())
  101. {
  102. Entry entry = (Entry)keys.next();
  103. Object uri = resourceCache.get(entry);
  104. if(uri == IGNORE || uri == null)
  105. continue;
  106. if(entry.type == Entry.PUBLIC)
  107. {
  108. jEdit.setProperty("xml.cache.public-id." + publicCount,entry.id);
  109. jEdit.setProperty("xml.cache.public-id." + publicCount
  110. + ".uri",uri.toString());
  111. publicCount++;
  112. }
  113. else
  114. {
  115. jEdit.setProperty("xml.cache.system-id." + systemCount,entry.id);
  116. jEdit.setProperty("xml.cache.system-id." + systemCount
  117. + ".uri",uri.toString());
  118. systemCount++;
  119. }
  120. }
  121. jEdit.unsetProperty("xml.cache.public-id." + publicCount);
  122. jEdit.unsetProperty("xml.cache.public-id." + publicCount + ".uri");
  123. jEdit.unsetProperty("xml.cache.system-id." + systemCount);
  124. jEdit.unsetProperty("xml.cache.system-id." + systemCount + ".uri");
  125. }
  126. } //}}}
  127. //}}}
  128. /**
  129. * Ask before downloading remote files
  130. */
  131. public static final String MODE = NETWORK_PROPS + ".mode";
  132. /**
  133. * Cache downloaded remote files
  134. */
  135. public static final String CACHE = NETWORK_PROPS + ".cache";
  136. // {{{ static variables
  137. /** Internal catalog for DTDs which are packaged in
  138. * XML.jar and jEdit.jar */
  139. public static final String INTERNALCATALOG =
  140. "jeditresource:/XML.jar!/xml/dtds/catalog";
  141. //a String : type-safety of the collection
  142. private static String IGNORE = new String("IGNORE");
  143. private static Resolver singleton = null;
  144. private static String resourceDir;
  145. // }}}
  146. // {{{ Instance Variables
  147. private EBComponent vfsUpdateHandler;
  148. /** Internal catalog for DTDs which are packaged in
  149. * XML.jar and jEdit.jar
  150. Parses and manages the catalog files
  151. Moved away from Xerces' XMLCatalogResolver,
  152. as it's really an overlay on top of commons-resolver
  153. and it supports less catalog formats than commons-resolver
  154. */
  155. private Catalog catalog = null;
  156. /** Mapping from public ID to URLs */
  157. private HashMap<Entry,String> resourceCache;
  158. /** Set of catalog files to load.
  159. * A set is used to remove duplicates (either via symlinks or double entry by the user)
  160. */
  161. private Set<String> catalogFiles;
  162. // }}}
  163. // {{{ instance()
  164. /**
  165. *
  166. * @return a global catalog resolver object you can use as an
  167. * LSResourceResolver or EntityResolver.
  168. */
  169. public static synchronized Resolver instance() {
  170. if (singleton == null) {
  171. jEdit.getPlugin(xml.XmlPlugin.class.getName()).getPluginJAR().activatePlugin();
  172. singleton = new Resolver();
  173. singleton.init();
  174. singleton.load();
  175. }
  176. return singleton;
  177. }
  178. // }}}
  179. /**
  180. * You can't create an object directly.
  181. * use @ref instance() to get a singleton instance.
  182. *
  183. */
  184. private Resolver() {
  185. }
  186. // {{{ - load()
  187. private synchronized void load()
  188. {
  189. if(!loadedCache)
  190. {
  191. resourceCache = new HashMap<Entry,String>();
  192. if (isUsingCache())
  193. {
  194. resourceDir = MiscUtilities.constructPath(
  195. jEdit.getSettingsDirectory(),"dtds");
  196. }
  197. int i;
  198. String id, prop, uri;
  199. i = 0;
  200. while((id = jEdit.getProperty(prop = "xml.cache"
  201. + ".public-id." + i++)) != null)
  202. {
  203. uri = jEdit.getProperty(prop + ".uri");
  204. resourceCache.put(new Entry(Entry.PUBLIC,id,uri),uri);
  205. }
  206. i = 0;
  207. while((id = jEdit.getProperty(prop = "xml.cache"
  208. + ".system-id." + i++)) != null)
  209. {
  210. uri = jEdit.getProperty(prop + ".uri");
  211. Entry se = new Entry(Entry.SYSTEM,id,uri);
  212. resourceCache.put(se,uri);
  213. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class, "loading cache "+id+" -> "+uri);
  214. }
  215. loadedCache = true;
  216. }
  217. if (!loadedCatalogs) {
  218. loadedCatalogs = true;
  219. catalog = new Catalog();
  220. catalog.getCatalogManager().setPreferPublic(true);
  221. // don't issue an error "Cannot find CatalogManager.properties"
  222. // fix for plugin bug #1090658 - XML plugin can't find CatalogManager.properties
  223. // warning: this will also suppress warnings if somebody adds a CatalogManager.properties
  224. // and references non-existing catalogs
  225. catalog.getCatalogManager().ignoreMissingProperties(!DEBUG_RESOLVER);
  226. // verbosity to 2 lists which catalogs are loaded and syntax errors
  227. // it seems a reasonable default
  228. catalog.getCatalogManager().setVerbosity(DEBUG_RESOLVER?Integer.MAX_VALUE:2);
  229. catalog.setupReaders();
  230. catalogFiles = new HashSet<String>();
  231. catalogFiles.add(INTERNALCATALOG);
  232. try
  233. {
  234. if(DEBUG_RESOLVER)Log.log(Log.MESSAGE,Resolver.class,
  235. "Loading system catalogs");
  236. catalog.loadSystemCatalogs();
  237. if(DEBUG_RESOLVER)Log.log(Log.MESSAGE,Resolver.class,
  238. "Loading internal catalog: " + INTERNALCATALOG);
  239. catalog.parseCatalog(INTERNALCATALOG);
  240. }
  241. catch(Exception ex1){
  242. Log.log(Log.ERROR,Resolver.class,ex1);
  243. }
  244. int i = 0;
  245. String uri = null;
  246. do {
  247. String prop = "xml.catalog." + i++;
  248. uri = jEdit.getProperty(prop);
  249. if (uri == null) break;
  250. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,
  251. "Loading catalog: " + uri);
  252. if(MiscUtilities.isURL(uri))
  253. catalogFiles.add(uri);
  254. else
  255. catalogFiles.add(MiscUtilities.resolveSymlinks(uri));
  256. try
  257. {
  258. catalog.parseCatalog(uri);
  259. }
  260. catch(Exception ex2)
  261. {
  262. ex2.printStackTrace();
  263. Log.log(Log.ERROR,Resolver.class,ex2);
  264. }
  265. } while (uri != null);
  266. }
  267. } //}}}
  268. //{{{ implements LSResourceResolver
  269. public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI)
  270. {
  271. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"resolveResource("+type+","+namespaceURI+","+publicId+","+systemId+","+baseURI);
  272. try{
  273. InputSource is = resolveEntity(type,publicId,baseURI,systemId);
  274. if(is == null)return null;
  275. else return new InputSourceAsLSInput(is);
  276. }catch(SAXException e){
  277. throw new RuntimeException("Error loading resource "+systemId,e);
  278. //maybe return null
  279. }catch(IOException e){
  280. throw new RuntimeException("Error loading resource "+systemId,e);
  281. //maybe return null
  282. }
  283. }
  284. /**
  285. * wrapper arround an InputSource for DOM2 Load and Save,
  286. * needed to implement LSResourceResolver for javax.xml.validation.SchemaFactory.
  287. * No setter method is active.
  288. * Maybe this should be the other way round : implement natively LSResourceResolver
  289. * and wrap an LSInput as InputSource...
  290. */
  291. private static class InputSourceAsLSInput implements LSInput{
  292. private InputSource is;
  293. InputSourceAsLSInput(InputSource is)
  294. {
  295. this.is = is;
  296. }
  297. public String getBaseURI()
  298. {
  299. return null;
  300. }
  301. public InputStream getByteStream(){
  302. return is.getByteStream();
  303. }
  304. public boolean getCertifiedText(){
  305. return false;
  306. }
  307. public Reader getCharacterStream(){
  308. return is.getCharacterStream();
  309. }
  310. public String getEncoding(){
  311. return is.getEncoding();
  312. }
  313. public String getPublicId(){
  314. return is.getPublicId();
  315. }
  316. public String getStringData(){
  317. return null;
  318. }
  319. public String getSystemId(){
  320. return is.getSystemId();
  321. }
  322. /**
  323. * @throws UnsupportedOperationException no setter !
  324. */
  325. public void setBaseURI(String baseURI)
  326. {
  327. throw new UnsupportedOperationException("setBaseURI()");
  328. }
  329. /**
  330. * @throws UnsupportedOperationException no setter !
  331. */
  332. public void setByteStream(InputStream byteStream)
  333. {
  334. throw new UnsupportedOperationException("setByteStream()");
  335. }
  336. /**
  337. * @throws UnsupportedOperationException no setter !
  338. */
  339. public void setCertifiedText(boolean certifiedText)
  340. {
  341. throw new UnsupportedOperationException("setCertifiedText()");
  342. }
  343. /**
  344. * @throws UnsupportedOperationException no setter !
  345. */
  346. public void setCharacterStream(Reader characterStream)
  347. {
  348. throw new UnsupportedOperationException("setCharacterStream()");
  349. }
  350. /**
  351. * @throws UnsupportedOperationException no setter !
  352. */
  353. public void setEncoding(String encoding)
  354. {
  355. throw new UnsupportedOperationException("setEncoding()");
  356. }
  357. /**
  358. * @throws UnsupportedOperationException no setter !
  359. */
  360. public void setPublicId(String publicId)
  361. {
  362. throw new UnsupportedOperationException("setPublicId()");
  363. }
  364. /**
  365. * @throws UnsupportedOperationException no setter !
  366. */
  367. public void setStringData(String stringData)
  368. {
  369. throw new UnsupportedOperationException("setStringData()");
  370. }
  371. /**
  372. * @throws UnsupportedOperationException no setter !
  373. */
  374. public void setSystemId(String systemId)
  375. {
  376. throw new UnsupportedOperationException("setSystemId()");
  377. }
  378. }
  379. // }}}
  380. // {{{ resolveEntity
  381. /** implements SAX1 EntityResolver
  382. * @see org.xml.sax.ext.DefaultHandler2#resolveEntity(java.lang.String, java.lang.String)
  383. */
  384. public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException
  385. {
  386. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"simple resolveEntity("+publicId+","+systemId+")");
  387. return resolveEntity(null, publicId, null, systemId);
  388. }
  389. /**
  390. * @param name
  391. * @param publicId
  392. * @param current
  393. * @param systemId
  394. */
  395. public String resolveEntityToPath(String name, String publicId, String current,
  396. String systemId) throws java.io.IOException {
  397. String[]res =resolveEntityToPathInternal(name,publicId,current,systemId);
  398. if(res == null)return null;
  399. else return res[1];
  400. }
  401. /**
  402. * systemId may be modified, for instance if resolving docbookx.dtd,
  403. * the systemId will be the full jeditresource:XML.jar!.../docbookx.dtd
  404. * @return array [systemId to report, real systemId]
  405. */
  406. public String[] resolveEntityToPathInternal(String name, String publicId, String current,
  407. String systemId) throws java.io.IOException {
  408. if(publicId != null && publicId.length() == 0)
  409. publicId = null;
  410. if(systemId != null && systemId.length() == 0)
  411. systemId = null;
  412. String newSystemId = null;
  413. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"Resolver.resolveEntity("+name+","+publicId+","+current+","+systemId+")");
  414. //catch an error message here
  415. if(publicId == null && systemId == null)return null;
  416. String parent;
  417. if(current != null)
  418. {
  419. // FIXME: if current is an xml:base, wrongly truncates last path component
  420. parent = MiscUtilities.getParentOfPath(current);
  421. }
  422. else
  423. parent = null;
  424. // try the catalog
  425. if(publicId == null)
  426. newSystemId = resolvePublicOrSystem(systemId,false);
  427. else
  428. {
  429. newSystemId = resolvePublicOrSystem(publicId,true);
  430. if(newSystemId == null && systemId != null){
  431. //try the systemId as a backup
  432. // calling again resolvePublicOrSystem in case
  433. // the systemId is in cache
  434. newSystemId = resolvePublicOrSystem(systemId,false);
  435. }
  436. }
  437. // well, the catalog can't help us, so just assume the
  438. // system id points to a file or URL
  439. if(newSystemId == null)
  440. {
  441. // had a public Id, but catalog returned null
  442. if(systemId == null)
  443. return null;
  444. // succeeds if it's a fully qualified url :
  445. // "http://www.jedit.org" succeeds, "../test.txt" fails
  446. else if(MiscUtilities.isURL(systemId))
  447. newSystemId = systemId;
  448. else
  449. {
  450. // systemId is absolute or no parent, use systemId
  451. if(new File(systemId).isAbsolute() || parent == null)
  452. {
  453. newSystemId = systemId;
  454. }
  455. // systemId is relative, use parent
  456. //need this to resolve xinclude.mod from user-guide.xml
  457. else
  458. {
  459. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"using parent !");
  460. newSystemId = parent + systemId;
  461. // set the systemId, overwise jing has trouble opening the file
  462. // FIXME: apply this fix for over kinds of resources
  463. systemId = newSystemId;
  464. }
  465. // when resolving "../simple/actions.xsd" from test_data/schema_loader/actions.xml
  466. // insert file:// at the begining
  467. if(!MiscUtilities.isURL(newSystemId))
  468. {
  469. try{
  470. newSystemId = new File(newSystemId).toURI().toURL().toString();;
  471. //set it, otherwise it will never get a sound system id
  472. systemId = newSystemId;
  473. }catch(java.net.MalformedURLException mue){
  474. //too bad, try something else
  475. }
  476. }
  477. }
  478. }
  479. // don't throw the IOException, as we don't have a
  480. // meaningful message to display to the user
  481. if(newSystemId == null)
  482. return null;
  483. // prevent the dialog from always poping-up when viewing
  484. // https://jedit.svn.sourceforge.net/svnroot/jedit/plugins/XML/trunk/test_data/dir%20with%20space/actions.xsd
  485. String lastChance = resolvePublicOrSystemFromCache(newSystemId,false);
  486. if(lastChance != null && lastChance != IGNORE){
  487. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"was going to fetch it again !");
  488. newSystemId = lastChance;
  489. }
  490. return new String[]{systemId,newSystemId};
  491. }
  492. public InputSource resolveEntity(String name, String publicId, String current,
  493. String systemId) throws SAXException, java.io.IOException
  494. {
  495. String[] sids = resolveEntityToPathInternal(name,publicId,current,systemId);
  496. if(sids == null)return null;
  497. else return openEntity(name, publicId, current,sids[0],sids[1]);
  498. }
  499. /** open an already resolved Entity.
  500. * Not public because the systemId may have to be changed (see result of resolveEntityToPathInternal)
  501. */
  502. private InputSource openEntity(String name, String publicId, String current,
  503. String systemId,String newSystemId) throws SAXException, java.io.IOException
  504. {
  505. // don't throw the IOException, as we don't have a
  506. // meaningful message to display to the user
  507. if(newSystemId == null)
  508. return null;
  509. Buffer buf = jEdit.getBuffer(PathUtilities.urlToPath(newSystemId));
  510. if(buf != null)
  511. {
  512. if(buf.isPerformingIO())
  513. VFSManager.waitForRequests();
  514. if(DEBUG_RESOLVER)Log.log(Log.DEBUG, getClass(), "Found open buffer for " + newSystemId);
  515. InputSource source = new InputSource(publicId);
  516. //use the original systemId
  517. source.setSystemId(systemId);
  518. try
  519. {
  520. buf.readLock();
  521. source.setCharacterStream(new StringReader(buf.getText(0,
  522. buf.getLength())));
  523. }
  524. finally
  525. {
  526. buf.readUnlock();
  527. }
  528. return source;
  529. }
  530. else if(newSystemId.startsWith("file:")
  531. || newSystemId.startsWith("jar:file:")
  532. || newSystemId.startsWith("jeditresource:"))
  533. {
  534. // don't keep a relative URL such as locate.rng
  535. if(newSystemId.startsWith("jeditresource:")){
  536. systemId=newSystemId;
  537. }
  538. // pretend to be reading the file from whatever the systemId was
  539. // eg. http://slackerdoc.tigris.org/xsd/slackerdoc.xsd when we
  540. // are reading ~/.jedit/dtds/cache1345.xml
  541. if(DEBUG_RESOLVER)
  542. {
  543. Log.log(Log.DEBUG,Resolver.class,"resolving to local file: "+newSystemId);
  544. Log.log(Log.DEBUG,Resolver.class,"systemId=: "+systemId);
  545. }
  546. InputSource source = new InputSource(systemId);
  547. source.setPublicId(publicId);
  548. InputStream is = new URL(newSystemId).openStream();
  549. source.setByteStream(is);
  550. return source;
  551. }
  552. else if (LOCAL.equals(getNetworkMode()))
  553. {
  554. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"refusing to fetch remote entity (configured for Local-only)");
  555. // returning null would not be as informing
  556. // TODO: prevent the 'premature end of file' error from showing
  557. throw new IOException(jEdit.getProperty("xml.network.error"));
  558. }
  559. else
  560. {
  561. final String _newSystemId = newSystemId;
  562. final VFS vfs = VFSManager.getVFSForPath(_newSystemId);
  563. // use a final array to pass a mutable value from the
  564. // invokeAndWait() call
  565. final Object[] sessionArray = new Object[1];
  566. Runnable run = new Runnable()
  567. {
  568. public void run()
  569. {
  570. View view = jEdit.getActiveView();
  571. if (ALWAYS.equals(getNetworkMode())
  572. || (ASK.equals(getNetworkMode())
  573. && showDownloadResourceDialog(view,_newSystemId))
  574. )
  575. {
  576. sessionArray[0] = vfs.createVFSSession(
  577. _newSystemId,view);
  578. }
  579. }
  580. };
  581. if(SwingUtilities.isEventDispatchThread())
  582. run.run();
  583. else
  584. {
  585. try
  586. {
  587. SwingUtilities.invokeAndWait(run);
  588. }
  589. catch(Exception e)
  590. {
  591. throw new RuntimeException(e);
  592. }
  593. }
  594. Object session = sessionArray[0];
  595. if(session != null)
  596. {
  597. InputSource source = new InputSource(systemId);
  598. source.setPublicId(publicId);
  599. if(isUsingCache())
  600. {
  601. File file;
  602. try
  603. {
  604. file = copyToLocalFile(session,vfs,newSystemId);
  605. }
  606. finally
  607. {
  608. vfs._endVFSSession(session,null);
  609. }
  610. addUserResource(publicId,systemId,file.toURI().toURL().toString());
  611. source.setByteStream(new FileInputStream(file));
  612. }
  613. else
  614. source.setByteStream(vfs._createInputStream(session,newSystemId,false,null));
  615. if(DEBUG_RESOLVER)
  616. {
  617. Log.log(Log.DEBUG,Resolver.class,"resolving to remote file: "+newSystemId);
  618. Log.log(Log.DEBUG,Resolver.class,"systemId=: "+systemId);
  619. }
  620. return source;
  621. }
  622. else
  623. {
  624. // returning null would not be as informing
  625. // TODO: prevent the 'premature end of file' error from showing
  626. throw new IOException(jEdit.getProperty("xml.network.error"));
  627. }
  628. }
  629. } //}}}
  630. // {{{ clearCache
  631. public void clearCache()
  632. {
  633. Iterator files = resourceCache.values().iterator();
  634. while(files.hasNext())
  635. {
  636. Object obj = files.next();
  637. if(obj instanceof String)
  638. {
  639. String file = (String)PathUtilities.urlToPath((String)obj);
  640. Log.log(Log.NOTICE, getClass(), "Deleting " + file);
  641. new File(file).delete();
  642. }
  643. }
  644. //clear the properties !
  645. int i=0;
  646. String prop;
  647. while(jEdit.getProperty(prop = "xml.cache"
  648. + ".public-id." + i++) != null)
  649. {
  650. jEdit.unsetProperty(prop);
  651. jEdit.unsetProperty(prop+".uri");
  652. }
  653. i = 0;
  654. while(jEdit.getProperty(prop = "xml.cache"
  655. + ".system-id." + i++) != null)
  656. {
  657. jEdit.unsetProperty(prop);
  658. jEdit.unsetProperty(prop+".uri");
  659. }
  660. resourceCache.clear();
  661. } //}}}
  662. /**
  663. * called from actions.xml
  664. */
  665. public synchronized void reloadCatalogs()
  666. {
  667. loadedCatalogs = false;
  668. load();
  669. }
  670. private String resolvePublicOrSystemFromCache(String id, boolean isPublic){
  671. Entry e = new Entry(isPublic ? Entry.PUBLIC : Entry.SYSTEM,id,null);
  672. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"resolvePublicOrSystemFromCache("+id+")");
  673. String uri = resourceCache.get(e);
  674. if(DEBUG_RESOLVER)
  675. {
  676. if(uri == IGNORE){
  677. Log.log(Log.DEBUG,Resolver.class,"ignored!");
  678. }else if(uri == null){
  679. Log.log(Log.DEBUG,Resolver.class,"not found "+id+" in cache");
  680. }else{
  681. Log.log(Log.DEBUG,Resolver.class,"found "+id+" in cache: "+uri);
  682. }
  683. }
  684. return uri;
  685. }
  686. // TODO: remove package access (for XMLPlugin)
  687. String resolvePublicOrSystem(String id,boolean isPublic) throws IOException
  688. {
  689. String uri = resolvePublicOrSystemFromCache(id, isPublic);
  690. if(uri == null)
  691. if(isPublic){
  692. return catalog.resolvePublic(id,null);
  693. }else{
  694. return catalog.resolveSystem(id);
  695. }
  696. else if(uri == IGNORE)
  697. return null;
  698. else{
  699. return uri;
  700. }
  701. } //}}}
  702. //{{{ copyToLocalFile() method
  703. private static File copyToLocalFile(Object session, VFS vfs, String path)
  704. throws IOException
  705. {
  706. if(jEdit.getSettingsDirectory() == null)
  707. return null;
  708. File _resourceDir = new File(resourceDir);
  709. if (!_resourceDir.exists())
  710. _resourceDir.mkdir();
  711. // Need to put this "copy from one stream to another"
  712. // into a common method some day, since other parts
  713. // of jEdit need it too...
  714. BufferedInputStream in = new BufferedInputStream(
  715. vfs._createInputStream(session,path,false,null));
  716. File localFile = File.createTempFile("cache", ".xml", _resourceDir);
  717. BufferedOutputStream out = new BufferedOutputStream(
  718. new FileOutputStream(localFile));
  719. byte[] buf = new byte[4096];
  720. int count = 0;
  721. while ((count = in.read(buf)) != -1)
  722. out.write(buf,0,count);
  723. out.close();
  724. return localFile;
  725. } //}}}
  726. private boolean showDownloadResourceDialog(Component comp, String systemId)
  727. {
  728. Entry e = new Entry(Entry.SYSTEM,systemId,null);
  729. if(resourceCache.get(e) == IGNORE)
  730. return false;
  731. int result = GUIUtilities.confirm(comp,"xml.download-resource",
  732. new String[] { systemId },JOptionPane.YES_NO_OPTION,
  733. JOptionPane.QUESTION_MESSAGE);
  734. if(result == JOptionPane.YES_OPTION)
  735. return true;
  736. else
  737. {
  738. resourceCache.put(e,IGNORE);
  739. return false;
  740. }
  741. } //}}}
  742. //{{{ addUserResource() method
  743. /**
  744. * Don't want this public because then invoking {@link clearCache()}
  745. * will remove this file, not what you would expect!
  746. */
  747. private void addUserResource(String publicId, String systemId, String url)
  748. {
  749. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"addUserResource("+publicId+","+systemId+","+url+")");
  750. if(publicId != null)
  751. {
  752. Entry pe = new Entry( Entry.PUBLIC, publicId, url );
  753. resourceCache.put( pe, url );
  754. }
  755. Entry se = new Entry( Entry.SYSTEM, systemId, url );
  756. resourceCache.put( se, url );
  757. } //}}}
  758. public static class Entry
  759. {
  760. public static final int SYSTEM = 0;
  761. public static final int PUBLIC = 1;
  762. public int type;
  763. public String id;
  764. public String uri;
  765. public Entry(int type, String id, String uri)
  766. {
  767. this.type = type;
  768. this.id = id;
  769. this.uri = uri;
  770. }
  771. public boolean equals(Object o)
  772. {
  773. if(o instanceof Entry)
  774. {
  775. Entry e = (Entry)o;
  776. return e.type == type && e.id.equals(id);
  777. }
  778. else
  779. return false;
  780. }
  781. public int hashCode()
  782. {
  783. return id.hashCode();
  784. }
  785. public String toString(){
  786. return "Resolver.Entry{"
  787. +(type==SYSTEM?"SYSTEM":"PUBLIC")
  788. +",id="+id
  789. +",uri="+uri
  790. +"}";
  791. }
  792. } //}}}
  793. //{{{ VFSUpdateHandler class
  794. /**
  795. * Reloads all catalog files when the user changes one of it on disk.
  796. * copied from CatalogManager
  797. */
  798. public class VFSUpdateHandler implements EBComponent
  799. {
  800. public void handleMessage(EBMessage msg)
  801. {
  802. if(!loadedCatalogs)
  803. return;
  804. if(msg instanceof VFSUpdate)
  805. {
  806. String path = ((VFSUpdate)msg).getPath();
  807. if(catalogFiles.contains(path))
  808. loadedCatalogs = false;
  809. }
  810. }
  811. } //}}}
  812. //{{{ propertiesChanged() method
  813. public void propertiesChanged()
  814. {
  815. // reload the list of catalogs, in case the user added some in the
  816. // option pane
  817. loadedCatalogs = false;
  818. } //}}}
  819. static public boolean isUsingCache() {
  820. if(jEdit.getSettingsDirectory() == null) return false;
  821. return jEdit.getBooleanProperty(CACHE);
  822. }
  823. static public void setUsingCache(boolean newCache) {
  824. jEdit.setBooleanProperty(CACHE, newCache);
  825. }
  826. /**
  827. *
  828. * @return the network mode: LOCAL, ASK, or ALWAYS
  829. */
  830. static public String getNetworkMode() {
  831. return jEdit.getProperty(MODE);
  832. }
  833. static public void setNetworkMode(String newMode) {
  834. if(!LOCAL.equals(newMode) && !ASK.equals(newMode) && !ALWAYS.equals(newMode))
  835. newMode = ASK;
  836. jEdit.setProperty(MODE, newMode);
  837. }
  838. public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException
  839. {
  840. // not used in jEdit
  841. return null;
  842. }
  843. }