source: main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java@ 29972

Last change on this file since 29972 was 29972, checked in by ak19, 9 years ago

More dynamic processing of downloaders. GLI used to use a fixed set of downloaders, now downloadinfo.pl is first called with the listall flag to get all the downloaders that are non-abstract, then these are added to the interface.

  • Property svn:keywords set to Author Date Id Revision
File size: 21.0 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37
38package org.greenstone.gatherer.util;
39
40
41// Don't even think about adding import java.awt.* here!
42// The functions in this class should not use any graphical classes. Put your function somewhere else buster!
43import java.io.Closeable;
44import java.io.*;
45import java.net.*;
46import java.util.*;
47// Don't even think about adding import javax.swing.* here!
48// The functions in this class should not use any graphical classes. Put your function somewhere else buster!
49import org.greenstone.gatherer.Dictionary;
50// Don't even think about adding import org.greenstone.gatherer.Gatherer in here!
51// The functions in this class should be independent of the Gatherer class. Put your function somewhere else buster!
52
53
54/** To provide a library of common methods, in a static context, for use in the Gatherer.
55 * @author John Thompson, Greenstone Digital Library, University of Waikato
56 * @version 2.3b
57 */
58public class Utility
59{
60 /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
61 static final public String CONFIG_FILE = "etc" + File.separator + "collect.cfg";
62 static final public String CONFIG_GS3_FILE = "etc" + File.separator + "collectionConfig.xml";
63 static final public String COLLECT_CFG = "collect.cfg";
64 static final public String COLLECT_BAK = "collect.bak";
65 static final public String COLLECTION_CONFIG_XML = "collectionConfig.xml";
66 static final public String COLLECTION_CONFIG_BAK = "collectionConfig.bak";
67 static final public String GS3MODE_ARGUMENT = "-gs3mode";
68 static final public String BUILD_CFG = "build.cfg";
69 static final public String BUILD_CONFIG_XML = "buildConfig.xml";
70
71 /** The default name of the perl executable under unix. */
72 static final public String PERL_EXECUTABLE_UNIX = "perl";
73 /** The default name of the perl executable under windows. */
74 static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
75
76
77 /**
78 * Delete a file or directory
79 * @param file The <strong>File</strong> you want to delete.
80 * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
81 */
82 static public boolean delete(File file)
83 {
84 // Nothing to do if it doesn't exist
85 if (!file.exists()) {
86 return true;
87 }
88
89 return deleteInternal(file);
90 }
91
92
93 /** Convenience function. */
94 static public boolean delete(String filename)
95 {
96 return delete(new File(filename));
97 }
98
99
100 /** In Java you have to make sure a directory is empty before you delete it, so recursively delete. */
101 static private boolean deleteInternal(File file)
102 {
103 // If file is a directory, we have to recursively delete its contents first
104 if (file.isDirectory()) {
105 File files[] = file.listFiles();
106 for (int i = 0; i < files.length; i++) {
107 if (deleteInternal(files[i]) == false) {
108 System.err.println("Error: Could not delete folder " + file);
109 return false;
110 }
111 }
112 }
113
114 // Delete file
115 if (file.delete() == false) {
116 System.err.println("Error: Could not delete file " + file);
117 return false;
118 }
119
120 return true;
121 }
122
123
124 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
125 static final public String BYTE_SUFFIX = " b";
126 static final public long GIGABYTE = 1024000000l;
127 static final public String GIGABYTE_SUFFIX = " Gb";
128 static final public long KILOBYTE = 1024l;
129 static final public String KILOBYTE_SUFFIX = " kb";
130 static final public long MEGABYTE = 1024000l;
131 static final public String MEGABYTE_SUFFIX = " Mb";
132 static final public String formatFileLength(long length) {
133 StringBuffer result = new StringBuffer("");
134 float number = 0f;
135 String suffix = null;
136 // Determine the floating point number and the suffix (radix) used.
137 if(length >= GIGABYTE) {
138 number = (float) length / (float) GIGABYTE;
139 suffix = GIGABYTE_SUFFIX;
140 }
141 else if(length >= MEGABYTE) {
142 number = (float) length / (float) MEGABYTE;
143 suffix = MEGABYTE_SUFFIX;
144 }
145 else if(length >= KILOBYTE) {
146 number = (float) length / (float) KILOBYTE;
147 suffix = KILOBYTE_SUFFIX;
148 }
149 else {
150 // Don't need to do anything fancy if the file is smaller than a kilobyte
151 return length + BYTE_SUFFIX;
152 }
153 // Create the formatted string remembering to round the number to 2.d.p. To do this copy everything in the number string from the start to the first occurance of '.' then copy two more digits. Finally search for and print anything that appears after (and including) the optional 'E' delimter.
154 String number_str = Float.toString(number);
155 char number_char[] = number_str.toCharArray();
156 int pos = 0;
157 // Print the characters up to the '.'
158 while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
159 result.append(number_char[pos]);
160 pos++;
161 }
162 if(pos < number_char.length) {
163 // Print the '.' and at most two characters after it
164 result.append(number_char[pos]);
165 pos++;
166 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
167 result.append(number_char[pos]);
168 }
169 // Search through the remaining string for 'E'
170 while(pos < number_char.length && number_char[pos] != 'E') {
171 pos++;
172 }
173 // If we still have string then we found an E. Copy the remaining string.
174 while(pos < number_char.length) {
175 result.append(number_char[pos]);
176 pos++;
177 }
178 }
179 // Add suffix
180 result.append(suffix);
181 // Done
182 return result.toString();
183 }
184
185 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
186 * @param text The <strong>String</strong> requiring formatting.
187 * @param width The maximum width per line as an <i>int</i>.
188 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
189 * TODO Currently HTML formatting tags are simply removed from the text, as the effects of spreading HTML tags over a break are undetermined. To solve this we need to associate tags with a certain text token so if it gets broken on to the next line the tags go with it, or if the tags cover a sequence of words that are broken we need to close then reopen the tags. However all this is a major task and well beyond anything I have time to 'muck-round' on.
190 */
191 static public String formatHTMLWidth(String text, int width) {
192 if(text == null) {
193 return "Error";
194 }
195 HTMLStringTokenizer html = new HTMLStringTokenizer(text);
196 int current_width = 0;
197 int threshold = width / 2;
198 Stack lines = new Stack();
199 String line = "";
200 while(html.hasMoreTokens()) {
201 String token = html.nextToken();
202 while(token != null) {
203 if(html.isTag()) {
204 // Insert smart HTML tag code here.
205 token = null;
206 }
207 else {
208 // If the token is bigger than two thirds width, before we've even started break it down.
209 if(current_width + 1 + token.length() > width && token.length() > threshold) {
210 if(width == current_width) {
211 lines.push(line);
212 line = token;
213 current_width = token.length();
214 }
215 else {
216 String prefix = token.substring(0, width - 1 - current_width);
217 token = token.substring(prefix.length());
218 if(current_width == 0) {
219 line = line + prefix;
220 }
221 else {
222 line = line + " " + prefix;
223 }
224 lines.push(line);
225 line = "";
226 current_width = 0;
227 }
228 }
229 // If adding the next token would push us over the maximum line width.
230 else if(current_width + 1 + token.length() > width) {
231 lines.push(line);
232 line = token;
233 current_width = token.length();
234 token = null;
235 }
236 // Otherwise we should be able to just add the token, give or take.
237 else {
238 if(current_width == 0) {
239 line = line + token;
240 current_width = token.length();
241 }
242 else {
243 // Special case for standard punctuation which may exist after a tag like so:
244 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation.
245 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
246 line = line + token;
247 current_width = current_width + 1;
248 }
249 else {
250 line = line + " " + token;
251 current_width = current_width + 1 + token.length();
252 }
253 }
254 token = null;
255 }
256 }
257 }
258 }
259 String result = line;
260 while(!lines.empty()) {
261 result = (String)lines.pop() + "<BR>" + result;
262 }
263 // Replace ' ' with "&nbsp;"
264 boolean tag = false;
265 int pos = 0;
266 while(pos < result.length()) {
267 if(result.charAt(pos) == '<') {
268 tag = true;
269 }
270 else if(result.charAt(pos) == '>') {
271 tag = false;
272 }
273 else if(result.charAt(pos) == ' ' && !tag) {
274 String prefix = result.substring(0, pos);
275 String suffix = result.substring(pos + 1);
276 result = prefix + "&nbsp;" + suffix;
277 }
278 pos++;
279 }
280 result = "<HTML>" + result + "</HTML>";
281 return result;
282 }
283
284
285 static public String getDateString() {
286 Calendar current = Calendar.getInstance();
287 String day_name = null;
288 switch(current.get(Calendar.DAY_OF_WEEK)) {
289 case Calendar.MONDAY: day_name = "Dates.Mon"; break;
290 case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
291 case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
292 case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
293 case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
294 case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
295 case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
296 default: day_name = "";
297 }
298 String month_name = null;
299 switch(current.get(Calendar.MONTH)) {
300 case Calendar.JANUARY: month_name = "Dates.Jan"; break;
301 case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
302 case Calendar.MARCH: month_name = "Dates.Mar"; break;
303 case Calendar.APRIL: month_name = "Dates.Apr"; break;
304 case Calendar.MAY: month_name = "Dates.May"; break;
305 case Calendar.JUNE: month_name = "Dates.Jun"; break;
306 case Calendar.JULY: month_name = "Dates.Jul"; break;
307 case Calendar.AUGUST: month_name = "Dates.Aug"; break;
308 case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
309 case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
310 case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
311 case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
312 default: month_name = "";
313 }
314 int day = current.get(Calendar.DAY_OF_MONTH);
315 int hour = current.get(Calendar.HOUR_OF_DAY);
316 int minute = current.get(Calendar.MINUTE);
317 int second = current.get(Calendar.SECOND);
318 int year = current.get(Calendar.YEAR);
319
320 return Dictionary.get(day_name) + " " + Dictionary.get(month_name) + " " + day + " " + year + " " + Utility.pad(String.valueOf(hour), 2, '0', true) + ":" + Utility.pad(String.valueOf(minute), 2, '0', true) + ":" + Utility.pad(String.valueOf(second), 2, '0', true);
321 }
322
323
324 /** Determine this machines name.
325 * @return The name as a <strong>String</strong>.
326 */
327 static public String getMachineName() {
328 try {
329 return InetAddress.getLocalHost().getHostName();
330 }
331 catch(UnknownHostException ex) {
332 }
333 return "Unknown Machine";
334 }
335
336
337 static public String getSitesDir(String gsdl3_path) {
338 return gsdl3_path + "sites" + File.separator;
339
340 }
341
342 /** @return the OSdir foldername: windows, linux or darwin */
343 public static String getOSdirName() {
344 if(Utility.isWindows()) {
345 return "windows";
346 }
347 if(Utility.isMac()) {
348 return "darwin";
349 }
350 return "linux"; // else assume it's a linux machine, OSdirname = linux
351 }
352
353
354 /** Method to determine if the host system is MacOS based.
355 * @return a boolean which is true if the platform is MacOS, false otherwise
356 */
357 public static boolean isMac() {
358 Properties props = System.getProperties();
359 String os_name = props.getProperty("os.name","");
360 if(os_name.startsWith("Mac OS")) {
361 return true;
362 }
363 return false;
364 }
365
366
367 /** Method to determine if the host system is Microsoft Windows based.
368 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
369 */
370 public static boolean isWindows() {
371 Properties props = System.getProperties();
372 String os_name = props.getProperty("os.name","");
373 if(os_name.startsWith("Windows")) {
374 return true;
375 }
376 return false;
377 }
378
379 public static boolean isWindows9x() {
380 Properties props = System.getProperties();
381 String os_name = props.getProperty("os.name","");
382 if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
383 return true;
384 }
385 return false;
386 }
387 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
388 * @param str The target <strong>String</strong> that needs to be padded.
389 * @param length The desired length of the string as an <i>int</i>.
390 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
391 */
392 static private String pad(String str_raw, int length, char fill, boolean end) {
393 StringBuffer str = new StringBuffer(str_raw);
394 while(str.length() < length) {
395 if(end) {
396 str.insert(0, fill);
397 }
398 else {
399 str.append(fill);
400 }
401 }
402 return str.toString();
403 }
404
405
406 /** Builds the cache dir by appending the user path and 'cache'.
407 * @return a File representing the path to the private file cache within the current collection.
408 */
409 public static File getCacheDir() {
410 return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
411 }
412
413 /** Method which constructs the log directory given a certain collection.
414 * @param col_dir The location of the collection directory as a <strong>String</strong>.
415 * @return The location of the given collections log directory, also as a <strong>String</strong>.
416 */
417 public static String getLogDir(String col_dir) {
418 if(col_dir != null) {
419 return col_dir + LOG_DIR;
420 }
421 else {
422 return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
423 }
424 }
425
426 static final private String APPLICATION_DATA_FOLDER = "Application Data";
427 static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
428 static final private String USER_HOME_PROPERTY = "user.home";
429 static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
430 /** Definition of an important directory name, in this case the log directory for the collection. */
431 static final public String LOG_DIR = "log" + File.separator;
432
433 static public File getGLIUserFolder()
434 {
435 if (Utility.isWindows()) {
436 return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
437 }
438 else {
439 return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
440 }
441 }
442
443 static private HashMap plugin_map = null;
444
445 static private void setUpPluginNameMap() {
446 plugin_map = new HashMap();
447 plugin_map.put("GAPlug", "GreenstoneXMLPlugin");
448 plugin_map.put("RecPlug", "DirectoryPlugin");
449 plugin_map.put("ArcPlug","ArchivesInfPlugin");
450 plugin_map.put("TEXTPlug","TextPlugin");
451 plugin_map.put("XMLPlug","ReadXMLFile");
452 plugin_map.put("EMAILPlug","EmailPlugin");
453 plugin_map.put("SRCPlug","SourceCodePlugin");
454 plugin_map.put("NULPlug","NulPlugin");
455 plugin_map.put("W3ImgPlug","HTMLImagePlugin");
456 plugin_map.put("PagedImgPlug","PagedImagePlugin");
457 plugin_map.put("METSPlug", "GreenstoneMETSPlugin");
458 plugin_map.put("DBPlug", "DatabasePlugin");
459 plugin_map.put("PPTPlug", "PowerPointPlugin");
460 plugin_map.put("PSPlug", "PostScriptPlugin");
461 }
462
463 static public String ensureNewPluginName(String plugin) {
464 if (plugin.endsWith("Plugin")) return plugin;
465 if (plugin_map == null) {
466 setUpPluginNameMap();
467 }
468 String new_name = (String)plugin_map.get(plugin);
469 if (new_name != null) return new_name;
470 new_name = plugin.replaceAll("Plug", "Plugin");
471 return new_name;
472 }
473
474
475 /** Write out a property line--a (property, value) pair--to the gsdl(3)site.cfg file.
476 * If the file already contains the line as-is, it is not re-written.
477 * If the file doesn't contain the line, it is appended.
478 * If the file contained a different value for the property, the line is corrected
479 * and the file is written out.
480 * If the propertyValue parameter is null, the property line is removed from the file.
481 * Not using the Properties class, as we want to keep the file's contents in the
482 * same order and preserve all the comments in as they're meant to help the user.
483 * Return the old value for the property, if it existed, else "".
484 */
485 public static String updatePropertyConfigFile(
486 String filename, String propertyName, String propertyValue)
487 {
488 File propFile = new File(filename);
489 String oldValue = "";
490 if(!propFile.exists()) {
491 System.err.println("*** Unable to update property " + propertyName + " in file "
492 + filename + " to\n" + propertyValue + ". File does not (yet) exist.\n");
493 return oldValue;
494 }
495 BufferedReader fin = null;
496 BufferedWriter fout = null;
497 StringBuffer contents = new StringBuffer();
498 String insertLine = null;
499 if(propertyValue != null) {
500 insertLine = propertyName+"\t"+propertyValue+"\n"; // new line after every propertyLine
501 }
502 boolean found = false;
503 try {
504 fin = new BufferedReader(new FileReader(filename));
505 String line = "";
506 while((line = fin.readLine()) != null) {
507 line = line.trim(); // remove any preceding (surrounding) whitespace
508 if(line.startsWith(propertyName)) { // won't match comment
509 found = true;
510 // store the previous value for the property
511 oldValue = line;
512 oldValue = oldValue.substring(propertyName.length());
513 oldValue = oldValue.trim();
514
515 if(propertyValue != null) { // line should be removed if propertyValue == null
516 if(line.equals(insertLine)) { // file is already correct, nothing to do
517 fin.close();
518 fin = null;
519 break;
520 } else {
521 contents.append(insertLine);
522 }
523 }
524 } else { // any other line
525 contents.append(line);
526 contents.append("\n"); // ensures the required new line at end of file
527 }
528 }
529
530 if(fin != null) { // need to write something out to the file
531 fin.close();
532 fin = null;
533
534 // if collecthome/property wasn't already specified in the file, append it
535 // but only if we have a value to write out to the file
536 if(!found && propertyValue != null) {
537 fout = new BufferedWriter(new FileWriter(filename, true)); // append mode
538 fout.write(insertLine, 0, insertLine.length());
539 } else {
540 fout = new BufferedWriter(new FileWriter(filename)); // hopefully this will overwrite
541 fout.write(contents.toString(), 0, contents.length());
542 }
543
544 fout.close();
545 fout = null;
546
547 } // else the file is fine
548 } catch(IOException e) {
549 System.err.println("*** Could not update file: " + filename);
550 System.err.println("with the " + propertyName + " property set to " + propertyValue);
551 System.err.println("Exception occurred: " + e.getMessage());
552 } finally {
553 try {
554 if(fin != null) {
555 fin.close();
556 fin = null;
557 }
558 if(fout != null) {
559 fout.close();
560 fout = null;
561 }
562 } catch(IOException ioe) {
563 fin = null;
564 fout = null;
565 }
566
567 }
568 return oldValue;
569 }
570
571 // For safely closing streams/handles/resources.
572 // For examples of use look in the Input- and OutputStreamGobbler classes.
573 // http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html
574 // http://stackoverflow.com/questions/481446/throws-exception-in-finally-blocks
575 public static void closeResource(Closeable resourceHandle) {
576 try {
577 if(resourceHandle != null) {
578 resourceHandle.close();
579 resourceHandle = null;
580 }
581 } catch(Exception e) {
582 System.err.println("Exception closing resource: " + e.getMessage());
583 e.printStackTrace();
584 }
585 }
586
587 public static void closeProcess(Process prcs) {
588 if( prcs != null ) {
589 closeResource(prcs.getErrorStream());
590 closeResource(prcs.getOutputStream());
591 closeResource(prcs.getInputStream());
592 prcs.destroy();
593 }
594 }
595}
Note: See TracBrowser for help on using the repository browser.