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

Last change on this file since 23143 was 23143, checked in by ak19, 14 years ago

More changes to making collectdir movable: 1. When not working with a remote GS or server.exe: if the collecthome set in GLI is not the same as the one in gsdlsite.cfg used by the apache web server, then a dialog pops up on exiting GLI allowing the user to set the collecthome value for both GLI (in the user's GLI config.xml) and the server (in gsdlsite.cfg). 2. Collecthome line in gsdlsite.cfg is removed if it is the default GS collect folder. (Just as a recent commit stores an empty string for the open_collection element in the GLI config.xml file if no collection is left open on exiting GLI and the collect folder is the default GS collect directory.) 3. Some bug fixes.

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