Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/merlotxml/util/xml/DTDCache.java


1   /*
2   ====================================================================
3   Copyright (c) 1999-2000 ChannelPoint, Inc..  All rights reserved.
4   ====================================================================
5   
6   Redistribution and use in source and binary forms, with or without 
7   modification, are permitted provided that the following conditions 
8   are met:
9   
10  1. Redistribution of source code must retain the above copyright 
11  notice, this list of conditions and the following disclaimer. 
12  
13  2. Redistribution in binary form must reproduce the above copyright
14  notice, this list of conditions and the following disclaimer in the 
15  documentation and/or other materials provided with the distribution.
16  
17  3. All advertising materials mentioning features or use of this 
18  software must display the following acknowledgment:  "This product 
19  includes software developed by ChannelPoint, Inc. for use in the 
20  Merlot XML Editor (http://www.merlotxml.org/)."
21   
22  4. Any names trademarked by ChannelPoint, Inc. must not be used to 
23  endorse or promote products derived from this software without prior
24  written permission. For written permission, please contact
25  legal@channelpoint.com.
26  
27  5.  Products derived from this software may not be called "Merlot"
28  nor may "Merlot" appear in their names without prior written
29  permission of ChannelPoint, Inc.
30  
31  6. Redistribution of any form whatsoever must retain the following
32  acknowledgment:  "This product includes software developed by 
33  ChannelPoint, Inc. for use in the Merlot XML Editor 
34  (http://www.merlotxml.org/)."
35  
36  THIS SOFTWARE IS PROVIDED BY CHANNELPOINT, INC. "AS IS" AND ANY EXPRESSED OR 
37  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
38  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO 
39  EVENT SHALL CHANNELPOINT, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
40  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
41  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
42  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
43  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
44  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
45  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46   ====================================================================
47  
48  For more information on ChannelPoint, Inc. please see http://www.channelpoint.com.  
49  For information on the Merlot project, please see 
50  http://www.merlotxml.org/.
51  */
52  
53      package org.merlotxml.util.xml;
54  
55  import java.util.*;
56  import java.io.*;
57  import java.net.*;
58  
59  import com.ibm.xml.parser.DTD;
60  
61  import org.xml.sax.InputSource;
62  import org.xml.sax.SAXException;
63  import org.xml.sax.EntityResolver;
64  
65  import org.merlotxml.util.FileUtil;
66  import org.merlotxml.util.xml.xml4j.DTDDocumentImpl;
67  
68  import org.merlotxml.merlot.plugin.*;
69  
70  
71  /**
72   * This singleton class is responsible for loading and caching
73   * all DTD's required by the system. This manager can load DTD's from the filesystem,
74   * URL's, and zip/jar files (not currently implemented).
75   * <P>
76   * Apps should use this class to retrieve all their DTD's for valid documents (non-validating
77   * apps usually ignore the DTD anyway, so they don't really need to use this, but if they do
78   * get a DTD, it might be a good idea to call into this class.
79   * <P>
80   * Here's an example of getting a dtd:<br>
81   * <tt>DTDCacheEntry dtdentry = DTDCache.getSharedInstance().findDTD(publicId, systemId);</tt>
82   * <br>
83   * where <tt>publicId</tt> is the DOCTYPE's given public identifier (can be null), and <tt>systemId</tt>
84   * is a system designator (file path or URL)
85   * <P>
86   * This can also cache DTD's from other entity resolvers via the 
87   * <A href="#resolveDTD">resolveDTD</a> method.
88   *
89   * @author Kelly A. Campbell
90   * @author Frank Blecha
91   * @version $Id: DTDCache.java,v 1.4 2002/11/05 13:43:32 flament Exp $
92   * 
93   */
94  public class DTDCache
95  {
96      /**
97       * key is the public id, val is a DTDCacheEntry, e.g. (publicId, DTDCacheEntry)
98       */
99      protected Map _publicIdCache;
100 
101     /**
102    * key is the system id, val is a DTDCacheEntry, e.g. (systemId, DTDCacheEntry)
103    */    
104     protected Map _systemIdCache;
105 
106     /**
107    * key is the file path (including a ! and the path within a jar), val is a DTDCacheEntry
108    */
109     protected Map _filepathCache;
110   
111     /**
112    * List of unique dtd entries. key is the dtd entry, value is not used (null)
113    */
114     protected Map _dtdEntries;
115   
116     /**
117    * Properties for getting dtd path, etc. from
118    */
119     protected Properties _properties;
120   
121   
122 
123     /**
124    * singleton instance
125    */
126     protected static DTDCache _instance;
127     /**
128    * synch object for creating the instance
129    */
130     protected static final Object _synchronizer = new Object();
131   
132     
133     /**
134        url prefix to know we're working with a local file
135     */
136     protected static final String FILE_PROTOCOL_NAME = "file:";
137   
138   
139     protected DTDCache () 
140     {
141   _publicIdCache = new HashMap();
142   _systemIdCache = new HashMap();
143   _filepathCache = new HashMap();
144   _dtdEntries    = new HashMap();
145     
146     }
147   
148     /**
149    * gets the singleton instance.
150    */
151     public static DTDCache getSharedInstance() 
152     {
153   if (_instance == null) {
154       synchronized(_synchronizer) {
155     if (_instance == null) {
156         _instance = new DTDCache();
157     }
158       }
159   }
160   return _instance;
161     }
162 
163     /**
164      * set the properties. should really only be called once by some app initializer
165      */
166     public void setProperties(Properties props) 
167     {
168   _properties = props;
169     }
170   
171 
172     /**
173        @param systemID ba
174 
175        @return DTDCacheEntry null if we're unable to find the entry, otherwise the entry
176 
177        @author Frank Blecha
178     */
179     protected DTDCacheEntry findCacheEntryBySystemID(String systemID)
180     {
181   DTDCacheEntry entry = null;
182   entry = (DTDCacheEntry)_systemIdCache.get(systemID);
183   if (entry != null) {
184       debug("found cached DTD: systemId="+systemID);
185       checkCacheEntryTimestamp(entry);
186   }
187   return entry;
188     }
189 
190 
191     /**
192        @param publicID
193        @param systemID
194        @return DTDCacheEntry the cache entry or null if we 
195        couldn't find it or encoutered an error
196 
197        @author Frank Blecha
198     */
199     protected DTDCacheEntry setupCacheEntryFromFile(String publicID, String systemID)
200     {
201   DTDCacheEntry entry = null;
202   try {
203       File dtdFile = new File(systemID);
204       entry = new DTDCacheEntry(publicID,systemID);
205       entry.setTimestamp( dtdFile.lastModified() );
206       entry.setFilePath( dtdFile.getCanonicalPath() );
207   }
208   catch(IOException iex) {
209 
210   }
211   return entry;
212     }
213 
214 
215     /**
216        @param publicID
217        @param systemID
218        @return DTDCacheEntry
219        @author Frank Blecha
220     */
221     protected DTDCacheEntry setupCacheEntryFromClassLoader(String publicID, String systemID)
222     {
223   DTDCacheEntry entry = new DTDCacheEntry(publicID, systemID);
224   // ok? set the timestamp to never reload
225   entry.setTimestamp(0);
226   String dtdFile = "blahblahblah";//replace this with something meaningful later - fb3
227   entry.setFilePath(this.getClass().toString() + ":"+ dtdFile);
228   return entry;
229     }
230 
231     /*=$*/
232     protected DTDCacheEntry setupCacheEntryFromPlugins(String publicID, String systemID)
233     {
234   DTDCacheEntry entry = new DTDCacheEntry(publicID, systemID);
235   entry.setTimestamp(0);    // ok? set the timestamp to never reload
236   entry.setFilePath(systemID);
237   return entry;
238     }
239     /*=$*/
240 
241     /**
242        @param publicId
243        @param systemId
244        @return DTDCacheEntry the cache entry or null if we cannot set it up correctly.
245        @author Frank Blecha
246      */
247     protected DTDCacheEntry setupCacheEntryFromURL(String publicID, String systemID)
248     {
249   DTDCacheEntry ret = null;
250   try {
251       URL dtdURL = new URL(systemID);
252       URLConnection connection = dtdURL.openConnection();
253       ret = new DTDCacheEntry(publicID, systemID);
254       ret.setFilePath( dtdURL.toString() );
255       ret.setTimestamp( connection.getExpiration() );
256       if( ret.getTimestamp() < System.currentTimeMillis() ) {
257     /* camk comments:
258        
259        ok, they don't want us to cache it...we will anyway.  
260        Use the modified time as the timestamp
261     */
262     ret.setTimestamp( connection.getLastModified() );
263       }
264   }
265   catch(IOException iex) {
266       ret = null;
267   }
268 
269   return ret;
270     }
271 
272 
273     /**
274        see if the systemID exists as-is (is the absolute path embedded in the xml?)
275 
276        @param dtdFile - the file we're trying to locate
277        @return InputStream - the InputStream from dtdFile or null if we can't find it
278        @author Frank Blecha
279     */
280     protected InputStream findDTDFromFile( String systemID )
281     {
282   InputStream ret = null;
283   try {
284       File dtdFile = new File(systemID);
285 
286       /*
287         we may modify the systemID if it's not prepended 
288         with FILE_PROTOCOL_NAME
289       */
290       String modifiedSystemID = systemID;
291       
292       if( dtdFile.exists() ) {
293     if( ! systemID.startsWith( FILE_PROTOCOL_NAME ) ) {
294         modifiedSystemID = FILE_PROTOCOL_NAME + systemID;
295     }
296     ret = new FileInputStream( dtdFile );
297       }
298   }
299   catch(IOException iex) {
300       ret = null;
301   }
302   return ret;
303     }
304 
305 
306     /**
307        @author Frank Blecha
308     */
309     protected InputStream findDTDFromFile( String systemID, String fileLocation)
310     {
311   InputStream ret = null;
312   
313   try {
314       File f = new File(fileLocation);
315       String parent = f.getParent();
316       if (parent != null) {
317     File dtdFile = new File(parent,systemID);
318     if (dtdFile.exists() && dtdFile.canRead()) {
319         ret = new FileInputStream(dtdFile);
320     }
321       }
322   }
323   catch (IOException iex) {
324       ret = null;
325   }
326   return ret;
327     }
328 
329 
330     
331     /**
332        @param publicID
333        @param systemID
334        @return InputStream the InputStream for the DTD, or null we can't find it.
335        @author Frank Blecha
336     */
337     protected InputStream findDTDFromURL(String publicID, String systemID) 
338     {
339   InputStream ret = null;
340   try {
341       URL dtdURL = new URL( systemID );
342       URLConnection connection = dtdURL.openConnection();
343       ret = connection.getInputStream();
344   }
345   catch(IOException iex) {
346       //set ret to null, which is enough of an error indicator
347       ret = null;
348   }
349   return ret;
350     }
351 
352    /**
353        @param publicID
354        @param systemID
355        @return InputStream stream to read the dtd, or null if an error was encountered
356        @author Frank Blecha
357     */
358     protected InputStream findDTDFromClassLoader( String publicID, String systemID)
359     {
360   InputStream ret = null;
361   if ( null != getDTDPath() ) {
362 
363       StringTokenizer st = 
364     new StringTokenizer( getDTDPath(), 
365              System.getProperty("path.separator")
366              );
367 
368       while (st.hasMoreTokens()) {
369     // try the file
370     File dtdFile = new File( st.nextToken() + "/" + formatFileName(systemID) );
371     try {
372         ret = FileUtil.getInputStream(dtdFile, this.getClass());
373         break;
374     }
375     catch (FileNotFoundException e) {}
376       }//end while
377   }
378   return ret;
379     }
380 
381     /*=$*/
382     protected InputStream findDTDFromPlugins( String publicID, String systemID) {
383   //System.out.println("findDTDFromPlugins("+publicID+","+systemID+")");  
384 
385   // delete "./" 
386         if (systemID.length() <= 2) {
387             return null;
388         } else {
389             if (systemID.substring(0,2).equals("./")) {
390                 systemID = systemID.substring(2);
391             }
392 
393             List plugins = GD_PluginManager.getInstance().getPlugins();
394             Iterator it = plugins.iterator();
395             InputStream is = null;
396             while ((it.hasNext()) && (is == null)) {
397                 PluginConfig pc = (PluginConfig) it.next();            
398                 is = pc.getClassLoader().getResourceAsStream("dtd/"+systemID);       
399             }  
400             return is;
401         }
402     }
403 
404     /*=$*/
405 
406     /**
407        @param publicID
408        @param systemID
409        @return InputStream the stream to read the dtd from, or null 
410        if we got an error
411 
412        @author Frank Blecha
413     */
414     protected InputStream findDTDFromDTDPath( String publicID, String systemID)
415     {
416   InputStream ret = null;
417   try {
418       if ( null != getDTDPath() ) {
419     StringTokenizer st = 
420         new StringTokenizer( getDTDPath(),
421            System.getProperty("path.separator")
422            );     
423     while (st.hasMoreTokens()) {
424         File dtdFile = new File( st.nextToken() + "/" 
425                + formatFileName(systemID) );
426         if (dtdFile.exists()) {
427       ret = new FileInputStream(dtdFile);
428       break;
429         }
430     }//end while
431       }
432   }
433   catch(IOException iex) {
434       ret = null;
435   }
436   return ret;
437     }
438 
439 
440 
441     /**
442    * Finds a dtd given a system identifier. If it cannot be found, null is returned
443    @param publicId 
444    @param systemId can be a URL, and absolute filepath, or a filepath relative to the current document
445    @param fileLocation the location of the file which includes the given DTD
446     */
447     public DTDCacheEntry findDTDbySystemId(String publicId, String systemId, String fileLocation) 
448     {
449   try {  
450       InputStream is = null;
451       String modifiedSystemID = systemId;
452       DTDCacheEntry entry = null;
453       File dtdFile = null;
454       
455       if( (entry = findCacheEntryBySystemID(systemId)) != null ) {
456     return entry;
457       }
458       else if( (is = findDTDFromFile( systemId)) != null ) {
459     entry = setupCacheEntryFromFile(publicId, systemId);
460       }
461       else if( (is = findDTDFromURL( publicId, systemId) ) != null ) {
462     entry = setupCacheEntryFromURL(publicId, systemId);
463       }
464       else if ( (is = findDTDFromFile( systemId, fileLocation)) != null ) {
465     entry = setupCacheEntryFromFile( publicId, systemId);
466       }
467       else if( (is = findDTDFromDTDPath( publicId, systemId) ) != null ) {
468     entry = setupCacheEntryFromFile( publicId, systemId);
469     modifiedSystemID = formatFileName( systemId );
470       }
471       else if( (is = findDTDFromClassLoader(publicId, systemId) ) != null ) {
472     entry = setupCacheEntryFromClassLoader(publicId, systemId);
473     modifiedSystemID = formatFileName( systemId );
474       }
475       else if ( (is = findDTDFromPlugins(publicId, systemId) ) != null) {
476     entry = setupCacheEntryFromPlugins(publicId, systemId);
477       }
478       
479   
480       if (entry == null || is == null) {
481     //  debug("DTD SYSTEMID='"+systemId+"' NOT FOUND");
482     //        new Exception().printStackTrace();
483     
484     return null;
485       }
486       
487       
488       /* camk comments:
489         XXX this is lame and uses the xml4j parser directly instead 
490         of through the DOMLiason... needs extrapolated and 
491         insulated a bit
492       
493         more than likely, someone else (like an entity resolver)
494         wants to read this dtd stream also, so we might as
495         well cache the whole dtd into memory (lame yes, but nice 
496         if the dtd came from a remote URL, or a jar/zip file.
497       */
498       loadDTDIntoCache(is,entry);
499       
500       if ( modifiedSystemID != null)  {
501     entry.setSystemId( modifiedSystemID );
502     _systemIdCache.put( modifiedSystemID,entry);
503       }
504       
505       String filepath = entry.getFilePath();
506       if (filepath != null) {
507     _filepathCache.put(filepath,entry);
508       }
509       //  debug("DTDCache: returning PUBLIC='"+entry.getPublicId()+"' SYSTEM='"+entry.getSystemId()+"' FILE='"+filepath+"'");
510       
511       return entry;
512   }
513   catch(IOException iex) {
514       return null;
515   }
516     }
517     
518 
519 
520 
521     /**
522    * Looks in our cache for a file with a given public ID
523    */
524     public DTDCacheEntry findDTDbyPublicId(String publicId, String systemId) 
525     {
526   DTDCacheEntry entry = (DTDCacheEntry)_publicIdCache.get(publicId);
527   if (entry != null) {
528       checkCacheEntryTimestamp(entry);
529       return entry;
530   }
531     
532   return null;
533     
534     
535     }
536   
537     /**
538    * find a DTD based on the public id and system id
539    */
540     public DTDCacheEntry findDTD(String pubid, String sysid, String fileLocation) 
541     {
542   // System.out.println("DTDCache.findDTD(public='"+pubid+"', sysid='"+sysid+"')");
543       
544   DTDCacheEntry ret = null;
545   if (pubid != null) {
546       ret = findDTDbyPublicId(pubid,sysid);
547   }
548   if (ret == null && sysid != null) {
549       ret = findDTDbySystemId(pubid,sysid,fileLocation);
550   }
551   
552   return ret;
553     
554     }
555 
556     /**
557    * resolve a dtd from another resolver. This way we can cache it locally.
558    */
559     public DTDCacheEntry resolveDTD(String publicId, String systemId, EntityResolver resolver, String fileLocation)
560   throws SAXException, IOException
561     {
562   debug("Resolve DTD: "+systemId);
563       
564   InputSource is = resolver.resolveEntity(publicId, systemId);
565   if (is != null) {
566 
567       String newPublicId = is.getPublicId();
568       String newSystemId = is.getSystemId();
569       if (newPublicId != null) {
570     publicId = newPublicId;
571         //  debug("publicId = "+publicId+" newPublicId = "+newPublicId);
572 
573       }
574       if (newSystemId != null) {
575     //  debug("systemId = "+systemId+" newSystemId = "+newSystemId);
576     systemId = newSystemId;
577       }
578       
579       // check to see if the resovler put one in our cache somehow
580       if (_publicIdCache.containsKey(publicId)) {
581     DTDCacheEntry entry = (DTDCacheEntry)_publicIdCache.get(publicId);
582     checkCacheEntryTimestamp(entry);
583         //  debug("RESOLVE RETURNING DTD FROM PUBLIC MAP: "+entry);
584         
585     return entry;
586       }
587       
588       // create a new DtdEntry for it
589       DTDCacheEntry entry = new DTDCacheEntry(publicId, systemId);
590 
591       InputStream stream = is.getByteStream();
592       Reader charstream  = is.getCharacterStream();
593       if (charstream == null && stream != null) {
594     loadDTDIntoCache(stream,entry);
595       }
596       else if (charstream != null) {
597     loadDTDIntoCache(charstream,entry);
598       }
599       else {
600     return null;
601       }
602       
603       //  debug("RESOLVE RETURNING DTD ENTRY: "+entry);
604       
605       return entry;
606       
607   }
608   return null;
609     
610     }
611   
612     /**
613    * Loads a dtd into a DTDCacheEntry. 
614    */
615     public void loadDTDIntoCache(InputStream is, DTDCacheEntry entry) 
616   throws IOException
617     {
618   Reader r = new InputStreamReader(is);
619   loadDTDIntoCache(r,entry);
620 
621     }
622   
623     /**
624    * Loads a dtd into a DTDCacheEntry. The public and system id's should be set on the dtd entry.
625    */
626   
627     public void loadDTDIntoCache(Reader r, DTDCacheEntry entry) 
628   throws IOException
629     {
630   //System.out.println("loadDTDIntoCache("+r+","+entry+")");
631   BufferedReader br = new BufferedReader(r);
632   CharArrayWriter caw = new CharArrayWriter();
633   BufferedWriter bw = new BufferedWriter(caw);
634 
635   int c;
636   while ((c = br.read()) >= 0) {
637       bw.write(c);
638   }
639   bw.flush();
640   bw.close();
641   br.close();
642 
643     
644   entry.setCachedDTDStream(caw.toCharArray());
645   CharArrayReader car = new CharArrayReader(entry.getCachedDTDStream());
646     
647   // now parse the dtd for ourselves
648   String filename = entry.getFilePath();
649   if (filename == null) {
650       filename = entry.getSystemId();
651   }
652   //  System.out.println("Parser("+filename+")");
653   String errorPrefix = filename;
654   if (errorPrefix == null || errorPrefix.trim().equals("")) {
655       errorPrefix = "error";
656   }
657     
658   com.ibm.xml.parser.Parser p = new com.ibm.xml.parser.Parser(errorPrefix);
659   debug("Parsing DTD: "+errorPrefix);
660     
661   DTD dtd = p.readDTDStream(car);
662   //  debug("readDTDStream ok");
663     
664   String publicId = entry.getPublicId();
665   String systemId = entry.getSystemId();
666     
667   DTDDocumentImpl dtdimpl = new DTDDocumentImpl(dtd, publicId, systemId);
668   entry.setParsedDTD(dtdimpl);
669     
670   car.close();
671   if (systemId != null && !systemId.trim().equals("")) {
672       _systemIdCache.put(systemId,entry);
673       //  debug("added "+systemId+" to cache [2]");
674     
675   }
676   if (publicId != null && !publicId.trim().equals("")) {
677       _publicIdCache.put(publicId,entry);
678   }
679   _dtdEntries.put(entry,null);
680     
681     
682     }
683 
684     /** 
685      * Checks the timestamp associated with a cache entry and reloads the
686      * dtd file if it has changed.
687      */
688     public void checkCacheEntryTimestamp(DTDCacheEntry entry) 
689     {
690   boolean found = false;
691   if (entry != null) {
692       long timestamp = entry.getTimestamp();
693       if (timestamp > 0) {
694     File dtdFile = null;
695     InputStream is = null;
696     URL u = null;
697         
698     // first see if the systemId exists as-is (is full path hard coded in the xml?)
699     String path = entry.getFilePath();
700     if (path != null) {
701         dtdFile = new File(path);
702         if (dtdFile.exists()) {
703       // we found it
704       found = true;
705       long newtime = dtdFile.lastModified();
706       if (newtime > timestamp) {
707           entry.setTimestamp(newtime);
708           try {
709         is = new FileInputStream(dtdFile);
710           }
711           catch (Exception ex) {
712           }
713       }
714         }
715 
716         // still don't have it?
717         if (!found && is == null) {
718         // try it as a url
719       try {
720           u = new URL(path);
721           URLConnection connection = u.openConnection();
722           long newtime = connection.getExpiration();
723           if (newtime > timestamp) {
724         is = connection.getInputStream();
725         
726         entry.setTimestamp(connection.getExpiration());  // cache until the document expires
727         if (entry.getTimestamp() < System.currentTimeMillis()) {
728         // ok, they don't want us to cache it... we will anyway.
729         // use the modified time as the timestamp
730             entry.setTimestamp(connection.getLastModified());
731         }
732           }
733       }
734       catch (Exception ex) {
735       }
736         }
737         try {
738       if (is != null) {
739         //  debug("RELOADING CHANGED DTD: path = "+path);
740           loadDTDIntoCache(is,entry);
741       }
742         }
743         catch (Exception ex) {
744         }
745     }
746         
747       }  
748   }
749       
750     }
751   
752 
753     public Collection getCachedDTDEntries() 
754     {
755   Set keys = _dtdEntries.keySet();
756   TreeSet sortedkeys = new TreeSet(keys);
757   return sortedkeys;
758     }
759   
760   
761     public void printCache () 
762     {
763   Set s = _publicIdCache.keySet();
764   Iterator it = s.iterator();
765   debug("PUBLIC Id's:\n");
766     
767   while (it.hasNext()) {
768       debug(it.next()+"\n");
769   }
770   s = _systemIdCache.keySet();
771   it = s.iterator();
772   debug("\nSYSTEM Id's:\n");
773     
774   while (it.hasNext()) {
775       debug(it.next()+"\n");
776   }
777   debug("\n");
778     
779     }
780 
781 
782     /**
783        @deprecated use fixSlashes instead
784        @author Frank Blecha
785     */
786     protected String fixslashes(String s)
787     {
788   return fixSlashes(s);
789     }
790   
791 
792     /**
793      * make all slashes forward slashes cause windows sucks
794      */
795     protected String fixSlashes(String s) 
796     {
797   StringBuffer sb = new StringBuffer(s);
798   /*
799     strip off any leading slashes cause windows sucks and makes URL's like:
800     file:/C:/blah
801     when the cwd comes out like C:/blah
802   */
803   if (sb.charAt(0) == '/' && sb.charAt(2) == ':') {
804       sb.deleteCharAt(0);
805   }
806   
807   for (int i=0;i < sb.length(); i++) {
808       if (sb.charAt(i) == '\\') {
809     sb.setCharAt(i,'/');
810       }
811   }
812   return sb.toString();
813     }
814   
815     /**
816       simple debugging print routine
817     */
818     protected void debug(String s) 
819     {
820   if (System.getProperty("DEBUG") != null) {
821       System.out.println("[debug] "+s);
822   }
823     }
824 
825     /**
826        @param originalFileName - systemID
827        @return String
828        @author Frank Blecha
829     */
830     protected String formatFileName(String originalFileName )
831     {
832   String modifiedFileName = originalFileName;
833   if( originalFileName.startsWith( FILE_PROTOCOL_NAME ) ) {
834       modifiedFileName = 
835     fixSlashes(originalFileName.substring(FILE_PROTOCOL_NAME.length()));
836   }
837   String currentDir = fixSlashes( System.getProperty("user.dir") );
838    
839   if( originalFileName.startsWith( currentDir ) ) {
840       modifiedFileName = 
841     modifiedFileName.substring( currentDir.length() );
842   }
843   return modifiedFileName;
844     }
845 
846 
847     protected String getDTDPath()
848     {
849   String ret = null;
850   if (_properties != null) {
851       ret = _properties.getProperty("path.dtd"); 
852   }
853   else {
854       debug("DTDCache: properties is null");
855       ret = null;
856   }
857   return ret;
858     }        
859 
860 }