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

Last change on this file since 30701 was 30701, checked in by ak19, 8 years ago

All the changes to get the new collectionConfig.xml Editor (ConfigFileEditor.java) to work.

  • Property svn:keywords set to Author Date Id Revision
File size: 21.8 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 * Reads in a text file and returns the contents as a String
79 */
80 static public String readFile(File file) {
81 BufferedReader fin = null;
82 StringBuffer contents = new StringBuffer();
83
84 try {
85 fin = new BufferedReader(new FileReader(file));
86 String line = null;
87 while((line = fin.readLine()) != null) {
88 contents.append(line);
89 contents.append("\n");
90 }
91 } catch(IOException e) {
92 System.err.println("*** Could not read in file: " + file.toString());
93 System.err.println("*** Exception occurred: " + e.getMessage());
94 } finally {
95 try {
96 if(fin != null) {
97 fin.close();
98 fin = null;
99 }
100 } catch(IOException ioe) {
101 fin = null;
102
103 }
104 }
105
106 return contents.toString();
107 }
108
109 /**
110 * Delete a file or directory
111 * @param file The <strong>File</strong> you want to delete.
112 * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
113 */
114 static public boolean delete(File file)
115 {
116 // Nothing to do if it doesn't exist
117 if (!file.exists()) {
118 return true;
119 }
120
121 return deleteInternal(file);
122 }
123
124
125 /** Convenience function. */
126 static public boolean delete(String filename)
127 {
128 return delete(new File(filename));
129 }
130
131
132 /** In Java you have to make sure a directory is empty before you delete it, so recursively delete. */
133 static private boolean deleteInternal(File file)
134 {
135 // If file is a directory, we have to recursively delete its contents first
136 if (file.isDirectory()) {
137 File files[] = file.listFiles();
138 for (int i = 0; i < files.length; i++) {
139 if (deleteInternal(files[i]) == false) {
140 System.err.println("Error: Could not delete folder " + file);
141 return false;
142 }
143 }
144 }
145
146 // Delete file
147 if (file.delete() == false) {
148 System.err.println("Error: Could not delete file " + file);
149 return false;
150 }
151
152 return true;
153 }
154
155
156 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
157 static final public String BYTE_SUFFIX = " b";
158 static final public long GIGABYTE = 1024000000l;
159 static final public String GIGABYTE_SUFFIX = " Gb";
160 static final public long KILOBYTE = 1024l;
161 static final public String KILOBYTE_SUFFIX = " kb";
162 static final public long MEGABYTE = 1024000l;
163 static final public String MEGABYTE_SUFFIX = " Mb";
164 static final public String formatFileLength(long length) {
165 StringBuffer result = new StringBuffer("");
166 float number = 0f;
167 String suffix = null;
168 // Determine the floating point number and the suffix (radix) used.
169 if(length >= GIGABYTE) {
170 number = (float) length / (float) GIGABYTE;
171 suffix = GIGABYTE_SUFFIX;
172 }
173 else if(length >= MEGABYTE) {
174 number = (float) length / (float) MEGABYTE;
175 suffix = MEGABYTE_SUFFIX;
176 }
177 else if(length >= KILOBYTE) {
178 number = (float) length / (float) KILOBYTE;
179 suffix = KILOBYTE_SUFFIX;
180 }
181 else {
182 // Don't need to do anything fancy if the file is smaller than a kilobyte
183 return length + BYTE_SUFFIX;
184 }
185 // 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.
186 String number_str = Float.toString(number);
187 char number_char[] = number_str.toCharArray();
188 int pos = 0;
189 // Print the characters up to the '.'
190 while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
191 result.append(number_char[pos]);
192 pos++;
193 }
194 if(pos < number_char.length) {
195 // Print the '.' and at most two characters after it
196 result.append(number_char[pos]);
197 pos++;
198 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
199 result.append(number_char[pos]);
200 }
201 // Search through the remaining string for 'E'
202 while(pos < number_char.length && number_char[pos] != 'E') {
203 pos++;
204 }
205 // If we still have string then we found an E. Copy the remaining string.
206 while(pos < number_char.length) {
207 result.append(number_char[pos]);
208 pos++;
209 }
210 }
211 // Add suffix
212 result.append(suffix);
213 // Done
214 return result.toString();
215 }
216
217 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
218 * @param text The <strong>String</strong> requiring formatting.
219 * @param width The maximum width per line as an <i>int</i>.
220 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
221 * 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.
222 */
223 static public String formatHTMLWidth(String text, int width) {
224 if(text == null) {
225 return "Error";
226 }
227 HTMLStringTokenizer html = new HTMLStringTokenizer(text);
228 int current_width = 0;
229 int threshold = width / 2;
230 Stack lines = new Stack();
231 String line = "";
232 while(html.hasMoreTokens()) {
233 String token = html.nextToken();
234 while(token != null) {
235 if(html.isTag()) {
236 // Insert smart HTML tag code here.
237 token = null;
238 }
239 else {
240 // If the token is bigger than two thirds width, before we've even started break it down.
241 if(current_width + 1 + token.length() > width && token.length() > threshold) {
242 if(width == current_width) {
243 lines.push(line);
244 line = token;
245 current_width = token.length();
246 }
247 else {
248 String prefix = token.substring(0, width - 1 - current_width);
249 token = token.substring(prefix.length());
250 if(current_width == 0) {
251 line = line + prefix;
252 }
253 else {
254 line = line + " " + prefix;
255 }
256 lines.push(line);
257 line = "";
258 current_width = 0;
259 }
260 }
261 // If adding the next token would push us over the maximum line width.
262 else if(current_width + 1 + token.length() > width) {
263 lines.push(line);
264 line = token;
265 current_width = token.length();
266 token = null;
267 }
268 // Otherwise we should be able to just add the token, give or take.
269 else {
270 if(current_width == 0) {
271 line = line + token;
272 current_width = token.length();
273 }
274 else {
275 // Special case for standard punctuation which may exist after a tag like so:
276 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation.
277 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
278 line = line + token;
279 current_width = current_width + 1;
280 }
281 else {
282 line = line + " " + token;
283 current_width = current_width + 1 + token.length();
284 }
285 }
286 token = null;
287 }
288 }
289 }
290 }
291 String result = line;
292 while(!lines.empty()) {
293 result = (String)lines.pop() + "<BR>" + result;
294 }
295 // Replace ' ' with "&nbsp;"
296 boolean tag = false;
297 int pos = 0;
298 while(pos < result.length()) {
299 if(result.charAt(pos) == '<') {
300 tag = true;
301 }
302 else if(result.charAt(pos) == '>') {
303 tag = false;
304 }
305 else if(result.charAt(pos) == ' ' && !tag) {
306 String prefix = result.substring(0, pos);
307 String suffix = result.substring(pos + 1);
308 result = prefix + "&nbsp;" + suffix;
309 }
310 pos++;
311 }
312 result = "<HTML>" + result + "</HTML>";
313 return result;
314 }
315
316
317 static public String getDateString() {
318 Calendar current = Calendar.getInstance();
319 String day_name = null;
320 switch(current.get(Calendar.DAY_OF_WEEK)) {
321 case Calendar.MONDAY: day_name = "Dates.Mon"; break;
322 case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
323 case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
324 case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
325 case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
326 case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
327 case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
328 default: day_name = "";
329 }
330 String month_name = null;
331 switch(current.get(Calendar.MONTH)) {
332 case Calendar.JANUARY: month_name = "Dates.Jan"; break;
333 case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
334 case Calendar.MARCH: month_name = "Dates.Mar"; break;
335 case Calendar.APRIL: month_name = "Dates.Apr"; break;
336 case Calendar.MAY: month_name = "Dates.May"; break;
337 case Calendar.JUNE: month_name = "Dates.Jun"; break;
338 case Calendar.JULY: month_name = "Dates.Jul"; break;
339 case Calendar.AUGUST: month_name = "Dates.Aug"; break;
340 case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
341 case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
342 case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
343 case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
344 default: month_name = "";
345 }
346 int day = current.get(Calendar.DAY_OF_MONTH);
347 int hour = current.get(Calendar.HOUR_OF_DAY);
348 int minute = current.get(Calendar.MINUTE);
349 int second = current.get(Calendar.SECOND);
350 int year = current.get(Calendar.YEAR);
351
352 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);
353 }
354
355
356 /** Determine this machines name.
357 * @return The name as a <strong>String</strong>.
358 */
359 static public String getMachineName() {
360 try {
361 return InetAddress.getLocalHost().getHostName();
362 }
363 catch(UnknownHostException ex) {
364 }
365 return "Unknown Machine";
366 }
367
368
369 static public String getSitesDir(String gsdl3_path) {
370 return gsdl3_path + "sites" + File.separator;
371
372 }
373
374 /** @return the OSdir foldername: windows, linux or darwin */
375 public static String getOSdirName() {
376 if(Utility.isWindows()) {
377 return "windows";
378 }
379 if(Utility.isMac()) {
380 return "darwin";
381 }
382 return "linux"; // else assume it's a linux machine, OSdirname = linux
383 }
384
385
386 /** Method to determine if the host system is MacOS based.
387 * @return a boolean which is true if the platform is MacOS, false otherwise
388 */
389 public static boolean isMac() {
390 Properties props = System.getProperties();
391 String os_name = props.getProperty("os.name","");
392 if(os_name.startsWith("Mac OS")) {
393 return true;
394 }
395 return false;
396 }
397
398
399 /** Method to determine if the host system is Microsoft Windows based.
400 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
401 */
402 public static boolean isWindows() {
403 Properties props = System.getProperties();
404 String os_name = props.getProperty("os.name","");
405 if(os_name.startsWith("Windows")) {
406 return true;
407 }
408 return false;
409 }
410
411 public static boolean isWindows9x() {
412 Properties props = System.getProperties();
413 String os_name = props.getProperty("os.name","");
414 if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
415 return true;
416 }
417 return false;
418 }
419 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
420 * @param str The target <strong>String</strong> that needs to be padded.
421 * @param length The desired length of the string as an <i>int</i>.
422 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
423 */
424 static private String pad(String str_raw, int length, char fill, boolean end) {
425 StringBuffer str = new StringBuffer(str_raw);
426 while(str.length() < length) {
427 if(end) {
428 str.insert(0, fill);
429 }
430 else {
431 str.append(fill);
432 }
433 }
434 return str.toString();
435 }
436
437
438 /** Builds the cache dir by appending the user path and 'cache'.
439 * @return a File representing the path to the private file cache within the current collection.
440 */
441 public static File getCacheDir() {
442 return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
443 }
444
445 /** Method which constructs the log directory given a certain collection.
446 * @param col_dir The location of the collection directory as a <strong>String</strong>.
447 * @return The location of the given collections log directory, also as a <strong>String</strong>.
448 */
449 public static String getLogDir(String col_dir) {
450 if(col_dir != null) {
451 return col_dir + LOG_DIR;
452 }
453 else {
454 return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
455 }
456 }
457
458 static final private String APPLICATION_DATA_FOLDER = "Application Data";
459 static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
460 static final private String USER_HOME_PROPERTY = "user.home";
461 static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
462 /** Definition of an important directory name, in this case the log directory for the collection. */
463 static final public String LOG_DIR = "log" + File.separator;
464
465 static public File getGLIUserFolder()
466 {
467 if (Utility.isWindows()) {
468 return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
469 }
470 else {
471 return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
472 }
473 }
474
475 static private HashMap plugin_map = null;
476
477 static private void setUpPluginNameMap() {
478 plugin_map = new HashMap();
479 plugin_map.put("GAPlug", "GreenstoneXMLPlugin");
480 plugin_map.put("RecPlug", "DirectoryPlugin");
481 plugin_map.put("ArcPlug","ArchivesInfPlugin");
482 plugin_map.put("TEXTPlug","TextPlugin");
483 plugin_map.put("XMLPlug","ReadXMLFile");
484 plugin_map.put("EMAILPlug","EmailPlugin");
485 plugin_map.put("SRCPlug","SourceCodePlugin");
486 plugin_map.put("NULPlug","NulPlugin");
487 plugin_map.put("W3ImgPlug","HTMLImagePlugin");
488 plugin_map.put("PagedImgPlug","PagedImagePlugin");
489 plugin_map.put("METSPlug", "GreenstoneMETSPlugin");
490 plugin_map.put("DBPlug", "DatabasePlugin");
491 plugin_map.put("PPTPlug", "PowerPointPlugin");
492 plugin_map.put("PSPlug", "PostScriptPlugin");
493 }
494
495 static public String ensureNewPluginName(String plugin) {
496 if (plugin.endsWith("Plugin")) return plugin;
497 if (plugin_map == null) {
498 setUpPluginNameMap();
499 }
500 String new_name = (String)plugin_map.get(plugin);
501 if (new_name != null) return new_name;
502 new_name = plugin.replaceAll("Plug", "Plugin");
503 return new_name;
504 }
505
506
507 /** Write out a property line--a (property, value) pair--to the gsdl(3)site.cfg file.
508 * If the file already contains the line as-is, it is not re-written.
509 * If the file doesn't contain the line, it is appended.
510 * If the file contained a different value for the property, the line is corrected
511 * and the file is written out.
512 * If the propertyValue parameter is null, the property line is removed from the file.
513 * Not using the Properties class, as we want to keep the file's contents in the
514 * same order and preserve all the comments in as they're meant to help the user.
515 * Return the old value for the property, if it existed, else "".
516 */
517 public static String updatePropertyConfigFile(
518 String filename, String propertyName, String propertyValue)
519 {
520 File propFile = new File(filename);
521 String oldValue = "";
522 if(!propFile.exists()) {
523 System.err.println("*** Unable to update property " + propertyName + " in file "
524 + filename + " to\n" + propertyValue + ". File does not (yet) exist.\n");
525 return oldValue;
526 }
527 BufferedReader fin = null;
528 BufferedWriter fout = null;
529 StringBuffer contents = new StringBuffer();
530 String insertLine = null;
531 if(propertyValue != null) {
532 insertLine = propertyName+"\t"+propertyValue+"\n"; // new line after every propertyLine
533 }
534 boolean found = false;
535 try {
536 fin = new BufferedReader(new FileReader(filename));
537 String line = "";
538 while((line = fin.readLine()) != null) {
539 line = line.trim(); // remove any preceding (surrounding) whitespace
540 if(line.startsWith(propertyName)) { // won't match comment
541 found = true;
542 // store the previous value for the property
543 oldValue = line;
544 oldValue = oldValue.substring(propertyName.length());
545 oldValue = oldValue.trim();
546
547 if(propertyValue != null) { // line should be removed if propertyValue == null
548 if(line.equals(insertLine)) { // file is already correct, nothing to do
549 fin.close();
550 fin = null;
551 break;
552 } else {
553 contents.append(insertLine);
554 }
555 }
556 } else { // any other line
557 contents.append(line);
558 contents.append("\n"); // ensures the required new line at end of file
559 }
560 }
561
562 if(fin != null) { // need to write something out to the file
563 fin.close();
564 fin = null;
565
566 // if collecthome/property wasn't already specified in the file, append it
567 // but only if we have a value to write out to the file
568 if(!found && propertyValue != null) {
569 fout = new BufferedWriter(new FileWriter(filename, true)); // append mode
570 fout.write(insertLine, 0, insertLine.length());
571 } else {
572 fout = new BufferedWriter(new FileWriter(filename)); // hopefully this will overwrite
573 fout.write(contents.toString(), 0, contents.length());
574 }
575
576 fout.close();
577 fout = null;
578
579 } // else the file is fine
580 } catch(IOException e) {
581 System.err.println("*** Could not update file: " + filename);
582 System.err.println("with the " + propertyName + " property set to " + propertyValue);
583 System.err.println("Exception occurred: " + e.getMessage());
584 } finally {
585 try {
586 if(fin != null) {
587 fin.close();
588 fin = null;
589 }
590 if(fout != null) {
591 fout.close();
592 fout = null;
593 }
594 } catch(IOException ioe) {
595 fin = null;
596 fout = null;
597 }
598
599 }
600 return oldValue;
601 }
602
603 // For safely closing streams/handles/resources.
604 // For examples of use look in the Input- and OutputStreamGobbler classes.
605 // http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html
606 // http://stackoverflow.com/questions/481446/throws-exception-in-finally-blocks
607 public static void closeResource(Closeable resourceHandle) {
608 try {
609 if(resourceHandle != null) {
610 resourceHandle.close();
611 resourceHandle = null;
612 }
613 } catch(Exception e) {
614 System.err.println("Exception closing resource: " + e.getMessage());
615 e.printStackTrace();
616 }
617 }
618
619 public static void closeProcess(Process prcs) {
620 if( prcs != null ) {
621 closeResource(prcs.getErrorStream());
622 closeResource(prcs.getOutputStream());
623 closeResource(prcs.getInputStream());
624 prcs.destroy();
625 }
626 }
627}
Note: See TracBrowser for help on using the repository browser.