1   /*
2    *  Files.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  $Id: Files.java,v 1.31 2001/11/15 15:22:45 valyt Exp $
12   */
13  
14  package gate.util;
15  import java.io.*;
16  import java.util.*;
17  import java.lang.*;
18  import gnu.regexp.*;
19  
20  /** Some utilities for use with Files and with resources.
21    * <P>
22    * <B>Note</B> that there is a terminology conflict between the use
23    * of "resources" here and <TT>gate.Resource</TT> and its inheritors.
24    * <P>
25    * Java "resources" are files that live on the CLASSPATH or in a Jar
26    * file that are <I>not</I> <TT>.class</TT> files. For example: a
27    * <TT>.gif</TT> file that is used by a GUI, or one of the XML files
28    * used for testing GATE's document format facilities. This class
29    * allows you to access these files in various ways (as streams, as
30    * byte arrays, etc.).
31    * <P>
32    * GATE resources are components (Java Beans) that provide all of the
33    * natural language processing capabilities of a GATE-based system, and
34    * the language data that such systems analsyse and produce. For
35    * example: parsers, lexicons, generators, corpora.
36    * <P>
37    * Where we say "resource" in this class we mean Java resource; elsewhere
38    * in the system we almost always mean GATE resource.
39    */
40  public class Files {
41  
42    /** Debug flag */
43    private static final boolean DEBUG = false;
44  
45    /** Used to generate temporary resources names*/
46    static long resourceIndex = 0;
47  
48    /**Where on the classpath the gate resources are to be found*/
49    protected static String resourcePath = "/gate/resources";
50  
51    /**Gets the path for the gate resources within the classpath*/
52    public static String getResourcePath(){
53      return resourcePath;
54    }
55  
56    /** It returns the last component in a file path.
57      * It takes E.g: d:/tmp/file.txt and returns file.txt
58      */
59    public static String getLastPathComponent(String path){
60      if(path == null || path.length() == 0) return "";
61      //we should look both for "/" and "\" as on windows the file separator is"\"
62      //but a path coming from an URL will be separated by "/"
63      int index = path.lastIndexOf('/');
64      if(index == -1) index = path.lastIndexOf('\\');
65      if(index == -1) return path;
66      else return path.substring(index + 1);
67    }// getLastPathComponent()
68  
69    /** Get a string representing the contents of a text file. */
70    public static String getString(String fileName) throws IOException {
71      return getString(new File(fileName));
72    } // getString(fileName)
73  
74    /** Get a string representing the contents of a text file. */
75    public static String getString(File textFile) throws IOException {
76      FileInputStream fis = new FileInputStream(textFile);
77      int len = (int) textFile.length();
78      byte[] textBytes = new byte[len];
79      fis.read(textBytes, 0, len);
80      fis.close();
81      return new String(textBytes);
82    } // getString(File)
83  
84    /** Get a byte array representing the contents of a binary file. */
85    public static byte[] getByteArray(File binaryFile) throws IOException {
86      FileInputStream fis = new FileInputStream(binaryFile);
87      int len = (int) binaryFile.length();
88      byte[] bytes = new byte[len];
89      fis.read(bytes, 0, len);
90      fis.close();
91      return bytes;
92    } // getByteArray(File)
93  
94    /** Get a resource from the classpath as a String.
95      */
96    public static String getResourceAsString(String resourceName)
97    throws IOException {
98      InputStream resourceStream = getResourceAsStream(resourceName);
99      BufferedReader resourceReader =
100       new BufferedReader(new InputStreamReader(resourceStream));
101     StringBuffer resourceBuffer = new StringBuffer();
102 
103     int i;
104 
105     int charsRead = 0;
106     final int size = 1024;
107     char[] charArray = new char[size];
108 
109     while( (charsRead = resourceReader.read(charArray,0,size)) != -1 )
110       resourceBuffer.append (charArray,0,charsRead);
111 
112     while( (i = resourceReader.read()) != -1 )
113       resourceBuffer.append((char) i);
114 
115     resourceReader.close();
116     return resourceBuffer.toString();
117   } // getResourceAsString(String)
118 
119   /** Get a resource from the GATE resources directory as a String.
120     * The resource name should be relative to <code>resourcePath</code> which
121     * is equal with <TT>gate/resources</TT>; e.g.
122     * for a resource stored as <TT>gate/resources/jape/Test11.jape</TT>,
123     * this method should be passed the name <TT>jape/Test11.jape</TT>.
124     */
125   public static String getGateResourceAsString(String resourceName)
126     throws IOException {
127 
128     InputStream resourceStream = getGateResourceAsStream(resourceName);
129     BufferedReader resourceReader =
130       new BufferedReader(new InputStreamReader(resourceStream));
131     StringBuffer resourceBuffer = new StringBuffer();
132 
133     int i;
134 
135     int charsRead = 0;
136     final int size = 1024;
137     char[] charArray = new char[size];
138 
139     while( (charsRead = resourceReader.read(charArray,0,size)) != -1 )
140       resourceBuffer.append (charArray,0,charsRead);
141 
142     while( (i = resourceReader.read()) != -1 )
143       resourceBuffer.append((char) i);
144 
145     resourceReader.close();
146     return resourceBuffer.toString();
147   } // getGateResourceAsString(String)
148 
149   /**
150     * Writes a temporary file into the default temporary directory,
151     * form an InputStream a unique ID is generated and associated automaticaly
152     * with the file name...
153     */
154   public static File writeTempFile(InputStream contentStream)
155     throws IOException {
156 
157     File resourceFile  = null;
158     FileOutputStream resourceFileOutputStream = null;
159 
160     // create a temporary file name
161     resourceFile = File.createTempFile ("gateResource", ".tmp");
162     resourceFileOutputStream = new FileOutputStream(resourceFile);
163     resourceFile.deleteOnExit ();
164 
165     if (contentStream == null)
166       return resourceFile;
167 
168     int bytesRead = 0;
169     final int readSize = 1024;
170     byte[] bytes = new byte[readSize];
171     while( (bytesRead = contentStream.read(bytes,0,readSize) ) != -1 )
172       resourceFileOutputStream.write(bytes,0, bytesRead);
173 
174     resourceFileOutputStream.close();
175     contentStream.close ();
176     return resourceFile;
177   }// writeTempFile()
178 
179   /**
180     * Writes aString into a temporary file located inside
181     * the default temporary directory defined by JVM, using the specific
182     * anEncoding.
183     * An unique ID is generated and associated automaticaly with the file name.
184     * @param aString the String to be written. If is null then the file will be
185     * empty.
186     * @param anEncoding the encoding to be used. If is null then the default
187     * encoding will be used.
188     * @return the tmp file containing the string.
189     */
190   public static File writeTempFile(String aString, String anEncoding) throws
191       UnsupportedEncodingException, IOException{
192     File resourceFile  = null;
193     OutputStreamWriter writer = null;
194 
195     // Create a temporary file name
196     resourceFile = File.createTempFile ("gateResource", ".tmp");
197     resourceFile.deleteOnExit ();
198 
199     if (aString == null) return resourceFile;
200     // Prepare the writer
201     if (anEncoding == null){
202       // Use default encoding
203       writer = new OutputStreamWriter(new FileOutputStream(resourceFile));
204 
205     }else {
206       // Use the specified encoding
207       writer = new OutputStreamWriter(
208                       new FileOutputStream(resourceFile),anEncoding);
209     }// End if
210 
211     // This Action is added only when a gate.Document is created.
212     // So, is for sure that the resource is a gate.Document
213     writer.write(aString);
214     writer.flush();
215     writer.close();
216     return resourceFile;
217   }// writeTempFile()
218 
219   /**
220     * Writes aString into a temporary file located inside
221     * the default temporary directory defined by JVM, using the default
222     * encoding.
223     * An unique ID is generated and associated automaticaly with the file name.
224     * @param aString the String to be written. If is null then the file will be
225     * empty.
226     * @return the tmp file containing the string.
227     */
228   public static File writeTempFile(String aString) throws IOException{
229     return writeTempFile(aString,null);
230   }// writeTempFile()
231 
232 
233   /** Get a resource from the classpath as a byte array.
234     */
235   public static byte[] getResourceAsByteArray(String resourceName)
236     throws IOException, IndexOutOfBoundsException, ArrayStoreException {
237 
238     InputStream resourceInputStream = getResourceAsStream(resourceName);
239     BufferedInputStream resourceStream =
240       new BufferedInputStream(resourceInputStream);
241     byte b;
242     final int bufSize = 1024;
243     byte[] buf = new byte[bufSize];
244     int i = 0;
245 
246     // get the whole resource into buf (expanding the array as needed)
247     while( (b = (byte) resourceStream.read()) != -1 ) {
248       if(i == buf.length) {
249         byte[] newBuf = new byte[buf.length * 2];
250         System.arraycopy (buf,0,newBuf,0,i);
251         buf = newBuf;
252       }
253       buf[i++] = b;
254     }
255 
256     // close the resource stream
257     resourceStream.close();
258 
259     // copy the contents of buf to an array of the correct size
260     byte[] bytes = new byte[i];
261     // copy from buf to bytes
262     System.arraycopy (buf,0,bytes,0,i);
263     return bytes;
264   } // getResourceAsByteArray(String)
265 
266   /** Get a resource from the GATE resources directory as a byte array.
267     * The resource name should be relative to <code>resourcePath<code> which
268     * is equal with <TT>gate/resources</TT>; e.g.
269     * for a resource stored as <TT>gate/resources/jape/Test11.jape</TT>,
270     * this method should be passed the name <TT>jape/Test11.jape</TT>.
271     */
272   public static byte[] getGateResourceAsByteArray(String resourceName)
273     throws IOException, IndexOutOfBoundsException, ArrayStoreException {
274 
275     InputStream resourceInputStream = getGateResourceAsStream(resourceName);
276     BufferedInputStream resourceStream =
277       new BufferedInputStream(resourceInputStream);
278     byte b;
279     final int bufSize = 1024;
280     byte[] buf = new byte[bufSize];
281     int i = 0;
282 
283     // get the whole resource into buf (expanding the array as needed)
284     while( (b = (byte) resourceStream.read()) != -1 ) {
285       if(i == buf.length) {
286         byte[] newBuf = new byte[buf.length * 2];
287         System.arraycopy (buf,0,newBuf,0,i);
288         buf = newBuf;
289       }
290       buf[i++] = b;
291     }
292 
293     // close the resource stream
294     resourceStream.close();
295 
296     // copy the contents of buf to an array of the correct size
297     byte[] bytes = new byte[i];
298 
299     // copy from buf to bytes
300     System.arraycopy (buf,0,bytes,0,i);
301     return bytes;
302   } // getResourceGateAsByteArray(String)
303 
304 
305   /** Get a resource from the classpath as an InputStream.
306     */
307   public static InputStream getResourceAsStream(String resourceName)
308     throws IOException {
309 
310     return  Files.class.getResourceAsStream(resourceName);
311     //return  ClassLoader.getSystemResourceAsStream(resourceName);
312   } // getResourceAsStream(String)
313 
314   /** Get a resource from the GATE resources directory as an InputStream.
315     * The resource name should be relative to <code>resourcePath<code> which
316     * is equal with <TT>gate/resources</TT>; e.g.
317     * for a resource stored as <TT>gate/resources/jape/Test11.jape</TT>,
318     * this method should be passed the name <TT>jape/Test11.jape</TT>.
319     */
320   public static InputStream getGateResourceAsStream(String resourceName)
321     throws IOException {
322 
323     if(resourceName.startsWith("/") || resourceName.startsWith("\\") )
324       return getResourceAsStream(resourcePath + resourceName);
325     else return getResourceAsStream(resourcePath + "/" + resourceName);
326   } // getResourceAsStream(String)
327 
328 
329   /** This method takes a regular expression and a directory name and returns
330     * the set of Files that match the pattern under that directory.
331     */
332   public static Set Find(String regex, String pathFile) {
333     Set regexfinal = new HashSet();
334     String[] tab;
335     File file = null;
336     PrintStream printstr = null;
337     Object obj = new Object();
338     //open a file
339     try {
340       file = new File(pathFile);
341     } catch(NullPointerException npe) {
342       npe.printStackTrace(Err.getPrintWriter());
343       //System.exit(1);
344     }
345     //generate a regular expression
346     try {
347       RE regexp = new RE("^"+regex);
348       if (file.isDirectory()){
349         tab = file.list();
350         for (int i=0;i<=tab.length-1;i++){
351           String finalPath = pathFile+"/"+tab[i];
352           REMatch m1 = regexp.getMatch(finalPath);
353           if (regexp.getMatch(finalPath) != null){
354             regexfinal.add(finalPath);
355           }
356         }
357       }
358       else {
359         if (file.isFile()){
360           if (regexp.getMatch(pathFile) != null) {
361             regexfinal.add(file.getAbsolutePath());
362         }
363       }
364     }
365     } catch(REException ree) {
366       ree.printStackTrace(Err.getPrintWriter());
367       //System.exit(1);
368     }
369     return regexfinal;
370   } //find
371 
372   /** Recursively remove a directory <B>even if it contains other files
373     * or directories</B>. Returns true when the directory and all its
374     * contents are successfully removed, else false.
375     */
376   public static boolean rmdir(File dir) {
377     if(dir == null || ! dir.isDirectory()) // only delete directories
378       return false;
379 
380     // list all the members of the dir
381     String[] members = dir.list();
382 
383     // return value indicating success or failure
384     boolean succeeded = true;
385 
386     // for each member, if is dir then recursively delete; if file then delete
387     for(int i = 0; i<members.length; i++) {
388       File member = new File(dir, members[i]);
389 
390       if(member.isFile()) {
391         if(! member.delete())
392           succeeded = false;
393       } else {
394         if(! Files.rmdir(member))
395           succeeded = false;
396       }
397     }
398 
399     // delete the directory itself
400     dir.delete();
401 
402     // return status value
403     return succeeded;
404   } // rmdir(File)
405 
406   /**
407    * This method updates an XML element with a new set of attributes.
408    * If the element is not found the XML is unchanged. The attributes
409    * keys and values must all be Strings.
410    *
411    * @param xml A stream of the XML data.
412    * @param elementName The name of the element to update.
413    * @param newAttrs The new attributes to place on the element.
414    * @return A string of the whole XML source, with the element updated.
415    */
416   public static String updateXmlElement(
417     BufferedReader xml, String elementName, Map newAttrs
418   ) throws IOException {
419     String line = null;
420     String nl = Strings.getNl();
421     StringBuffer newXml = new StringBuffer();
422 
423     // read the whole source
424     while( ( line = xml.readLine() ) != null ) {
425       newXml.append(line);
426       newXml.append(nl);
427     }
428 
429     // find the location of the element
430     int start = newXml.toString().indexOf("<" + elementName);
431     if(start == -1) return newXml.toString();
432     int end =   newXml.toString().indexOf(">", start);
433     if(end == -1)   return newXml.toString();
434 
435     // check if the old element is empty (ends in "/>") or not
436     boolean isEmpty = false;
437     if(newXml.toString().charAt(end - 1) == '/') isEmpty = true;
438 
439     // create the new element string with the new attributes
440     StringBuffer newElement = new StringBuffer();
441     newElement.append("<");
442     newElement.append(elementName);
443 
444     // add in the new attributes
445     Iterator iter = newAttrs.entrySet().iterator();
446     while(iter.hasNext()) {
447       Map.Entry entry = (Map.Entry) iter.next();
448       String key =   (String) entry.getKey();
449       String value = (String) entry.getValue();
450 
451       newElement.append(" ");newElement.append(key);
452       newElement.append("=\"");
453       newElement.append(value);
454       newElement.append("\"" + nl);
455     }
456 
457     // terminate the element
458     if(isEmpty) newElement.append("/");
459     newElement.append(">");
460 
461     // replace the old string
462     newXml.replace(start, end + 1, newElement.toString());
463 
464     return newXml.toString();
465   } // updateXmlElement(Reader...)
466 
467   /**
468    * This method updates an XML element in an XML file
469    * with a new set of attributes. If the element is not found the XML
470    * file is unchanged. The attributes keys and values must all be Strings.
471    *
472    * @param xmlFile An XML file.
473    * @param elementName The name of the element to update.
474    * @param newAttrs The new attributes to place on the element.
475    * @return A string of the whole XML file, with the element updated (the
476    *   file is also overwritten).
477    */
478   public static String updateXmlElement(
479     File xmlFile, String elementName, Map newAttrs
480   ) throws IOException {
481     BufferedReader fileReader = new BufferedReader(new FileReader(xmlFile));
482     String newXml = updateXmlElement(fileReader, elementName, newAttrs);
483     fileReader.close();
484 
485     FileWriter fileWriter = new FileWriter(xmlFile);
486     fileWriter.write(newXml);
487     fileWriter.close();
488 
489     return newXml;
490   } // updateXmlElement(File...)
491 
492 } // class Files
493