/plugins/XML/branches/buffertagparser/xml/Resolver.java

#
Java | 968 lines | 647 code | 124 blank | 197 comment | 132 complexity | 057aad174dbab1f6da6720c215692542 MD5 | raw file

✨ Summary
  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 18426 2010-08-27 06:42:52Z 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)
  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. private 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. parent = MiscUtilities.getParentOfPath(current);
  420. }
  421. else
  422. parent = null;
  423. // try the catalog
  424. if(publicId == null)
  425. newSystemId = resolvePublicOrSystem(systemId,false);
  426. else
  427. {
  428. newSystemId = resolvePublicOrSystem(publicId,true);
  429. if(newSystemId == null && systemId != null){
  430. //try the systemId as a backup
  431. // calling again resolvePublicOrSystem in case
  432. // the systemId is in cache
  433. newSystemId = resolvePublicOrSystem(systemId,false);
  434. }
  435. }
  436. // well, the catalog can't help us, so just assume the
  437. // system id points to a file or URL
  438. if(newSystemId == null)
  439. {
  440. // had a public Id, but catalog returned null
  441. if(systemId == null)
  442. return null;
  443. // succeeds if it's a fully qualified url :
  444. // "http://www.jedit.org" succeeds, "../test.txt" fails
  445. else if(MiscUtilities.isURL(systemId))
  446. newSystemId = systemId;
  447. else
  448. {
  449. // systemId is absolute or no parent, use systemId
  450. if(new File(systemId).isAbsolute() || parent == null)
  451. {
  452. newSystemId = systemId;
  453. }
  454. // systemId is relative, use parent
  455. //need this to resolve xinclude.mod from user-guide.xml
  456. else
  457. {
  458. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"using parent !");
  459. newSystemId = parent + systemId;
  460. // set the systemId, overwise jing has trouble opening the file
  461. // FIXME: apply this fix for over kinds of resources
  462. systemId = newSystemId;
  463. }
  464. // when resolving "../simple/actions.xsd" from test_data/schema_loader/actions.xml
  465. // insert file:// at the begining
  466. if(!MiscUtilities.isURL(newSystemId))
  467. {
  468. try{
  469. newSystemId = new File(newSystemId).toURI().toURL().toString();;
  470. //set it, otherwise it will never get a sound system id
  471. systemId = newSystemId;
  472. }catch(java.net.MalformedURLException mue){
  473. //too bad, try something else
  474. }
  475. }
  476. }
  477. }
  478. // don't throw the IOException, as we don't have a
  479. // meaningful message to display to the user
  480. if(newSystemId == null)
  481. return null;
  482. // prevent the dialog from always poping-up when viewing
  483. // https://jedit.svn.sourceforge.net/svnroot/jedit/plugins/XML/trunk/test_data/dir%20with%20space/actions.xsd
  484. String lastChance = resolvePublicOrSystemFromCache(newSystemId,false);
  485. if(lastChance != null && lastChance != IGNORE){
  486. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"was going to fetch it again !");
  487. newSystemId = lastChance;
  488. }
  489. return new String[]{systemId,newSystemId};
  490. }
  491. public InputSource resolveEntity(String name, String publicId, String current,
  492. String systemId) throws SAXException, java.io.IOException
  493. {
  494. String[] sids = resolveEntityToPathInternal(name,publicId,current,systemId);
  495. if(sids == null)return null;
  496. else return openEntity(name, publicId, current,sids[0],sids[1]);
  497. }
  498. /** open an already resolved Entity.
  499. * Not public because the systemId may have to be changed (see result of resolveEntityToPathInternal)
  500. */
  501. private InputSource openEntity(String name, String publicId, String current,
  502. String systemId,String newSystemId) throws SAXException, java.io.IOException
  503. {
  504. // don't throw the IOException, as we don't have a
  505. // meaningful message to display to the user
  506. if(newSystemId == null)
  507. return null;
  508. Buffer buf = jEdit.getBuffer(PathUtilities.urlToPath(newSystemId));
  509. if(buf != null)
  510. {
  511. if(buf.isPerformingIO())
  512. VFSManager.waitForRequests();
  513. if(DEBUG_RESOLVER)Log.log(Log.DEBUG, getClass(), "Found open buffer for " + newSystemId);
  514. InputSource source = new InputSource(publicId);
  515. //use the original systemId
  516. source.setSystemId(systemId);
  517. try
  518. {
  519. buf.readLock();
  520. source.setCharacterStream(new StringReader(buf.getText(0,
  521. buf.getLength())));
  522. }
  523. finally
  524. {
  525. buf.readUnlock();
  526. }
  527. return source;
  528. }
  529. else if(newSystemId.startsWith("file:")
  530. || newSystemId.startsWith("jar:file:")
  531. || newSystemId.startsWith("jeditresource:"))
  532. {
  533. // don't keep a relative URL such as locate.rng
  534. if(newSystemId.startsWith("jeditresource:")){
  535. systemId=newSystemId;
  536. }
  537. // pretend to be reading the file from whatever the systemId was
  538. // eg. http://slackerdoc.tigris.org/xsd/slackerdoc.xsd when we
  539. // are reading ~/.jedit/dtds/cache1345.xml
  540. if(DEBUG_RESOLVER)
  541. {
  542. Log.log(Log.DEBUG,Resolver.class,"resolving to local file: "+newSystemId);
  543. Log.log(Log.DEBUG,Resolver.class,"systemId=: "+systemId);
  544. }
  545. InputSource source = new InputSource(systemId);
  546. source.setPublicId(publicId);
  547. InputStream is = new URL(newSystemId).openStream();
  548. source.setByteStream(is);
  549. return source;
  550. }
  551. else if (LOCAL.equals(getNetworkMode()))
  552. {
  553. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"refusing to fetch remote entity (configured for Local-only)");
  554. // returning null would not be as informing
  555. // TODO: prevent the 'premature end of file' error from showing
  556. throw new IOException(jEdit.getProperty("xml.network.error"));
  557. }
  558. else
  559. {
  560. final String _newSystemId = newSystemId;
  561. final VFS vfs = VFSManager.getVFSForPath(_newSystemId);
  562. // use a final array to pass a mutable value from the
  563. // invokeAndWait() call
  564. final Object[] sessionArray = new Object[1];
  565. Runnable run = new Runnable()
  566. {
  567. public void run()
  568. {
  569. View view = jEdit.getActiveView();
  570. if (ALWAYS.equals(getNetworkMode())
  571. || (ASK.equals(getNetworkMode())
  572. && showDownloadResourceDialog(view,_newSystemId))
  573. )
  574. {
  575. sessionArray[0] = vfs.createVFSSession(
  576. _newSystemId,view);
  577. }
  578. }
  579. };
  580. if(SwingUtilities.isEventDispatchThread())
  581. run.run();
  582. else
  583. {
  584. try
  585. {
  586. SwingUtilities.invokeAndWait(run);
  587. }
  588. catch(Exception e)
  589. {
  590. throw new RuntimeException(e);
  591. }
  592. }
  593. Object session = sessionArray[0];
  594. if(session != null)
  595. {
  596. InputSource source = new InputSource(systemId);
  597. source.setPublicId(publicId);
  598. if(isUsingCache())
  599. {
  600. File file;
  601. try
  602. {
  603. file = copyToLocalFile(session,vfs,newSystemId);
  604. }
  605. finally
  606. {
  607. vfs._endVFSSession(session,null);
  608. }
  609. addUserResource(publicId,systemId,file.toURL().toString());
  610. source.setByteStream(new FileInputStream(file));
  611. }
  612. else
  613. source.setByteStream(vfs._createInputStream(session,newSystemId,false,null));
  614. if(DEBUG_RESOLVER)
  615. {
  616. Log.log(Log.DEBUG,Resolver.class,"resolving to remote file: "+newSystemId);
  617. Log.log(Log.DEBUG,Resolver.class,"systemId=: "+systemId);
  618. }
  619. return source;
  620. }
  621. else
  622. {
  623. // returning null would not be as informing
  624. // TODO: prevent the 'premature end of file' error from showing
  625. throw new IOException(jEdit.getProperty("xml.network.error"));
  626. }
  627. }
  628. } //}}}
  629. // {{{ clearCache
  630. public void clearCache()
  631. {
  632. Iterator files = resourceCache.values().iterator();
  633. while(files.hasNext())
  634. {
  635. Object obj = files.next();
  636. if(obj instanceof String)
  637. {
  638. String file = (String)PathUtilities.urlToPath((String)obj);
  639. Log.log(Log.NOTICE, getClass(), "Deleting " + file);
  640. new File(file).delete();
  641. }
  642. }
  643. //clear the properties !
  644. int i=0;
  645. String prop;
  646. while(jEdit.getProperty(prop = "xml.cache"
  647. + ".public-id." + i++) != null)
  648. {
  649. System.out.println("unset "+prop);
  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. System.out.println("unset "+prop);
  658. jEdit.unsetProperty(prop);
  659. jEdit.unsetProperty(prop+".uri");
  660. }
  661. resourceCache.clear();
  662. } //}}}
  663. /**
  664. * called from actions.xml
  665. */
  666. public synchronized void reloadCatalogs()
  667. {
  668. loadedCatalogs = false;
  669. load();
  670. }
  671. private String resolvePublicOrSystemFromCache(String id, boolean isPublic){
  672. Entry e = new Entry(isPublic ? Entry.PUBLIC : Entry.SYSTEM,id,null);
  673. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"resolvePublicOrSystemFromCache("+id+")");
  674. String uri = resourceCache.get(e);
  675. if(DEBUG_RESOLVER)
  676. {
  677. if(uri == IGNORE){
  678. Log.log(Log.DEBUG,Resolver.class,"ignored!");
  679. }else if(uri == null){
  680. Log.log(Log.DEBUG,Resolver.class,"not found "+id+" in cache");
  681. }else{
  682. Log.log(Log.DEBUG,Resolver.class,"found "+id+" in cache: "+uri);
  683. }
  684. }
  685. return uri;
  686. }
  687. // TODO: remove package access (for XMLPlugin)
  688. String resolvePublicOrSystem(String id,boolean isPublic) throws IOException
  689. {
  690. String uri = resolvePublicOrSystemFromCache(id, isPublic);
  691. if(uri == null)
  692. if(isPublic){
  693. return catalog.resolvePublic(id,null);
  694. }else{
  695. return catalog.resolveSystem(id);
  696. }
  697. else if(uri == IGNORE)
  698. return null;
  699. else{
  700. return uri;
  701. }
  702. } //}}}
  703. //{{{ copyToLocalFile() method
  704. private static File copyToLocalFile(Object session, VFS vfs, String path)
  705. throws IOException
  706. {
  707. if(jEdit.getSettingsDirectory() == null)
  708. return null;
  709. File _resourceDir = new File(resourceDir);
  710. if (!_resourceDir.exists())
  711. _resourceDir.mkdir();
  712. // Need to put this "copy from one stream to another"
  713. // into a common method some day, since other parts
  714. // of jEdit need it too...
  715. BufferedInputStream in = new BufferedInputStream(
  716. vfs._createInputStream(session,path,false,null));
  717. File localFile = File.createTempFile("cache", ".xml", _resourceDir);
  718. BufferedOutputStream out = new BufferedOutputStream(
  719. new FileOutputStream(localFile));
  720. byte[] buf = new byte[4096];
  721. int count = 0;
  722. while ((count = in.read(buf)) != -1)
  723. out.write(buf,0,count);
  724. out.close();
  725. return localFile;
  726. } //}}}
  727. private boolean showDownloadResourceDialog(Component comp, String systemId)
  728. {
  729. Entry e = new Entry(Entry.SYSTEM,systemId,null);
  730. if(resourceCache.get(e) == IGNORE)
  731. return false;
  732. int result = GUIUtilities.confirm(comp,"xml.download-resource",
  733. new String[] { systemId },JOptionPane.YES_NO_OPTION,
  734. JOptionPane.QUESTION_MESSAGE);
  735. if(result == JOptionPane.YES_OPTION)
  736. return true;
  737. else
  738. {
  739. resourceCache.put(e,IGNORE);
  740. return false;
  741. }
  742. } //}}}
  743. //{{{ addUserResource() method
  744. /**
  745. * Don't want this public because then invoking {@link clearCache()}
  746. * will remove this file, not what you would expect!
  747. */
  748. private void addUserResource(String publicId, String systemId, String url)
  749. {
  750. if(DEBUG_RESOLVER)Log.log(Log.DEBUG,Resolver.class,"addUserResource("+publicId+","+systemId+","+url+")");
  751. if(publicId != null)
  752. {
  753. Entry pe = new Entry( Entry.PUBLIC, publicId, url );
  754. resourceCache.put( pe, url );
  755. }
  756. Entry se = new Entry( Entry.SYSTEM, systemId, url );
  757. resourceCache.put( se, url );
  758. } //}}}
  759. public static class Entry
  760. {
  761. public static final int SYSTEM = 0;
  762. public static final int PUBLIC = 1;
  763. public int type;
  764. public String id;
  765. public String uri;
  766. public Entry(int type, String id, String uri)
  767. {
  768. this.type = type;
  769. this.id = id;
  770. this.uri = uri;
  771. }
  772. public boolean equals(Object o)
  773. {
  774. if(o instanceof Entry)
  775. {
  776. Entry e = (Entry)o;
  777. return e.type == type && e.id.equals(id);
  778. }
  779. else
  780. return false;
  781. }
  782. public int hashCode()
  783. {
  784. return id.hashCode();
  785. }
  786. public String toString(){
  787. return "Resolver.Entry{"
  788. +(type==SYSTEM?"SYSTEM":"PUBLIC")
  789. +",id="+id
  790. +",uri="+uri
  791. +"}";
  792. }
  793. } //}}}
  794. //{{{ VFSUpdateHandler class
  795. /**
  796. * Reloads all catalog files when the user changes one of it on disk.
  797. * copied from CatalogManager
  798. */
  799. public class VFSUpdateHandler implements EBComponent
  800. {
  801. public void handleMessage(EBMessage msg)
  802. {
  803. if(!loadedCatalogs)
  804. return;
  805. if(msg instanceof VFSUpdate)
  806. {
  807. String path = ((VFSUpdate)msg).getPath();
  808. if(catalogFiles.contains(path))
  809. loadedCatalogs = false;
  810. }
  811. }
  812. } //}}}
  813. //{{{ propertiesChanged() method
  814. public void propertiesChanged()
  815. {
  816. // reload the list of catalogs, in case the user added some in the
  817. // option pane
  818. loadedCatalogs = false;
  819. } //}}}
  820. static public boolean isUsingCache() {
  821. if(jEdit.getSettingsDirectory() == null) return false;
  822. return jEdit.getBooleanProperty(CACHE);
  823. }
  824. static public void setUsingCache(boolean newCache) {
  825. jEdit.setBooleanProperty(CACHE, newCache);
  826. }
  827. /**
  828. *
  829. * @return the network mode: LOCAL, ASK, or ALWAYS
  830. */
  831. static public String getNetworkMode() {
  832. return jEdit.getProperty(MODE);
  833. }
  834. static public void setNetworkMode(String newMode) {
  835. if(!LOCAL.equals(newMode) && !ASK.equals(newMode) && !ALWAYS.equals(newMode))
  836. newMode = ASK;
  837. jEdit.setProperty(MODE, newMode);
  838. }
  839. public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException
  840. {
  841. // TODO Auto-generated method stub
  842. return null;
  843. }
  844. }