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

Last change on this file was 34394, checked in by ak19, 4 years ago

Bugfix 1 for GLI metadata slowdown: selecting multiple Gathererd files in GLI became very slow. Kathy and Dr Bainbridge had tracked this down to code I had added to support non basic ASCII filenames in GLI, which was making an expensive win operating system function call on Windows for each selected file, launching a Java Process for each. The speed of selecting multiple files is now back to being almost as fast as in 3.09. Tested on Windows and linux. Had to treat windows as a special case because I can't get the code modifications to work on Linux: the perl code stores a hex-encoded string for the filename that GLI now uses when OS is Windows and compares against the hex encoded name of a file selected. But on linux the hex encoded value generated by perl is not the same as that which java generates and after trying repeatedly, I'e not been able to succeed to get it to work. So the code behaves as before for Linux.

  • Property svn:keywords set to Author Date Id Revision
File size: 29.5 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!
51import org.greenstone.gatherer.util.SafeProcess; // for the closeResource() static method
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 /** Platform independent NEWLINE character */
76 public static final String NEWLINE;
77
78 // NEWLINE related code copied across from GS3 src code
79 // Before Java 7, no System.lineSeparator() or System.getProperty("line.separator")
80 // And on local linux, am compiling with JDK 6, so need this.
81 // http://stackoverflow.com/questions/207947/how-do-i-get-a-platform-dependent-new-line-character
82 // http://stackoverflow.com/questions/2591083/getting-java-version-at-runtime
83 // https://www.tutorialspoint.com/java/lang/package_getspecificationversion.htm
84 // https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html
85 // Can initialise static final vars on declaration or in static initialisation code block
86 // http://stackoverflow.com/questions/2339932/java-can-final-variables-be-initialized-in-static-initialization-block
87 // Initialise object member final vars on declaration or in constructors
88 static {
89 double java_version = Double.parseDouble(System.getProperty("java.specification.version"));
90 if(java_version >= 1.7) {
91 NEWLINE = System.getProperty("line.separator");
92 } else {
93 NEWLINE = isWindows() ? "\r\n" : "\n";
94 }
95 }
96
97 // Copied from GS3 main java code at GSDL3SRCHOME\src\java\org\greenstone/util\Misc.java
98 // Debugging function to print a string's non-basic chars in hex, so stringToHex on all non-basic and non-printable ASCII
99 // Dr Bainbridge said that printing anything with charCode over 128 in hex is okay, but I'd already made extra allowances for non-printable ASCII
100 // Based on https://stackoverflow.com/questions/923863/converting-a-string-to-hexadecimal-in-java
101 public static String debugUnicodeString(String str) {
102 String result = "";
103 for(int i = 0; i < str.length(); i++) {
104 int charCode = str.codePointAt(i); // unicode codepoint / ASCII code
105
106 // ASCII table: https://cdn.sparkfun.com/assets/home_page_posts/2/1/2/1/ascii_table_black.png
107 // If the unicode character code pt is less than the ASCII code for space and greater than for tilda, let's display the char in hex (x0000 format)
108 if((charCode >= 20 && charCode <= 126) || charCode == 9 || charCode == 10 || charCode == 13) { // space, tilda, TAB, LF, CR are printable, leave them in for XML element printing
109 result += str.charAt(i);
110 } else {
111 result += "x{" + String.format("%04x", charCode) + "}"; // looks like: x{4-char-codepoint}
112 }
113 }
114
115 return result;
116 }
117
118 // Version of debugUnicodeString that, on Windows, mimics perl unicode::debug_unicode_string
119 // exactly by producing hex/unicode codepoints for ALL codepoints beyond ASCII
120 public static String stringToHex(String str) {
121 String result = "";
122 for(int i = 0; i < str.length(); i++) {
123 int charCode = str.codePointAt(i); // unicode codepoint / ASCII code
124
125 if(charCode <=127) { // ASCII
126 result += str.charAt(i);
127 } else { // non-ASCII
128 result += "\\x{" + String.format("%04x", charCode) + "}"; // looks like: \x{4-char-codepoint}
129 }
130 }
131
132 return result;
133 }
134
135 /**
136 * returns the short filename (8.3) for a file in Windows
137 *
138 * @param longFileName - must be the full path to an actual existing file
139 * @return a string with the short filename, or null if an error occurred or the
140 * file does not exist.
141 */
142 public static String getWindowsShortFileName(String longFileName) throws Exception {
143 if(!Utility.isWindows()) {
144 return longFileName;
145 } else {
146 //return WindowsNativeFunctions.getEightPointThree(longFileName);
147 return getMSDOSName(longFileName);
148 }
149 }
150
151 /*
152 * The means of getting a Windows Short FileName described at
153 * http://dolf.trieschnigg.nl/eightpointthree/eightpointthree.html looked ideal
154 * as it uses the non-JNI NativeCall jar file with imports of com.eaio.nativecall.*
155 * However, after trying this solution and making all the changes necessary for it,
156 * I wasn't able to use it after all, because when I finally could run it, the NativeCall.dll
157 * included in the jar file was of 32 bit and didn't match my 64 bit Windows OS.
158 * I tried the newer jar NativeCal.jar file and it wasn't compatible with the sample code
159 * and things wouldn't compile. In the end, I opted for plan B below, as it at least works.
160 */
161 /**
162 * getMSDOSName() and its helper function getAbsolutePath(fileName)
163 * are from https://stackoverflow.com/questions/18893284/how-to-get-short-filenames-in-windows-using-java
164 * getMSDOSName() modified to use our SafeProcess class.
165 *
166 * @param fileName - the regular fileName to be converted. Must be the full path to an actual existing file
167 * @return Windows shortfile name for the fileName parameter given.
168 */
169 public static String getMSDOSName(String fileName)
170 throws IOException, InterruptedException {
171
172 String path = getAbsolutePath(fileName);
173
174 SafeProcess process = new SafeProcess("cmd /c for %I in (\"" + path + "\") do @echo %~fsI");
175 int returnVal = process.runProcess();
176 if(returnVal != 0) {
177 return null;
178 }
179
180 String data = process.getStdOutput();
181 if(data == null) {
182 return null;
183 }
184 else return data.replaceAll("\\r\\n", "");
185 }
186 public static String getAbsolutePath(String fileName)
187 throws IOException {
188 File file = new File(fileName);
189 String path = file.getAbsolutePath();
190
191 if (file.exists() == false)
192 file = new File(path);
193
194 path = file.getCanonicalPath();
195
196 if (file.isDirectory() && (path.endsWith(File.separator) == false))
197 path += File.separator;
198
199 return path;
200 }
201
202 /**
203 * Handy function to display the list of calling functions by
204 * printing out the stack trace even when you don't have an exception
205 */
206 static public void printStackTrace() {
207 // https://stackoverflow.com/questions/1069066/get-current-stack-trace-in-java
208
209 //Thread.dumpStack(); // looks too much like an exception, though the newlines separating each function call is handy
210 //new Exception().printStackTrace(); // outputs in the format of an exception too
211 //System.err.println("\n@@@@ stacktrace:\n" + Arrays.toString(Thread.currentThread().getStackTrace()) + "\n"); // outputs all in one line
212
213 System.err.println("\n@@@@ stacktrace:");
214 StackTraceElement[] els = new Throwable().getStackTrace(); // starts at index 1, which is this function
215 //StackTraceElement[] els = Thread.currentThread().getStackTrace(); starts at index 0, "java.lang.Thread.getStackTrace()"
216 for(StackTraceElement ste : els) {
217 System.err.println(" " + ste);
218 }
219 }
220
221 /**
222 * Handy function to display the parent of the calling function
223 * (the function that called the function that called printCaller())
224 */
225 static public void printCaller() {
226 //int parent = 1;
227 // this function printCaller() itself adds another layer on the callstack since
228 // it calls the overloaded method, so need to add 1 more to parent
229 //printCaller(parent++);
230
231 StackTraceElement[] callstack = Thread.currentThread().getStackTrace();
232 StackTraceElement requestor = callstack[2]; // the calling function, the function that called this one
233 if(callstack.length > 3) {
234 StackTraceElement caller_requested = callstack[3]; // the function requested
235 System.err.println("\n@@@ Function " + requestor + " called by:\n "
236 + caller_requested + " at 1 ancestors back\n");
237 } else {
238 StackTraceElement caller_requested = callstack[callstack.length-1]; // the function requested
239 System.err.println("\n@@@ Don't have callers beyond requestor function " + requestor + "\n");
240 }
241 }
242
243 /**
244 * Handy function to display the nth ancestor of the calling function
245 * where ancestor=0 would be the calling function itself
246 */
247 static public void printCaller(int ancestor) {
248 // https://stackoverflow.com/questions/1069066/get-current-stack-trace-in-java
249
250 // Thread.currentThread().getStackTrace() starts at index 0: "java.lang.Thread.getStackTrace()"
251 // index 1 will be this method (printCaller) and index 2 will be the calling function itself who wants
252 // to know who called it. So need to at least start at index 3 to get informative caller information
253
254 StackTraceElement[] callstack = Thread.currentThread().getStackTrace();
255 StackTraceElement requestor = callstack[2]; // the calling function, the function that called this one
256 if(callstack.length > (ancestor+3)) {
257 StackTraceElement caller_requested = callstack[ancestor+3]; // the function requested
258 System.err.println("\n@@@ Function " + requestor + " called by:\n "
259 + caller_requested + " at " + ancestor + " ancestors back\n");
260 } else {
261 StackTraceElement caller_requested = callstack[callstack.length-1]; // the function requested
262 System.err.println("\n@@@ Don't have " + ancestor + " ancestor callers. Function " + requestor + " called by:\n "
263 + caller_requested + " at max " + (callstack.length-1) + " ancestors back\n");
264 }
265 }
266
267 /**
268 * Reads in a text file and returns the contents as a String
269 */
270 static public String readFile(File file) {
271 BufferedReader fin = null;
272 StringBuffer contents = new StringBuffer();
273
274 try {
275 fin = new BufferedReader(new FileReader(file));
276 String line = null;
277 while((line = fin.readLine()) != null) {
278 contents.append(line);
279 contents.append("\n");
280 }
281 } catch(IOException e) {
282 System.err.println("*** Could not read in file: " + file.toString());
283 System.err.println("*** Exception occurred: " + e.getMessage());
284 } finally {
285 SafeProcess.closeResource(fin);
286 }
287
288 return contents.toString();
289 }
290
291 /**
292 * Delete a file or directory
293 * @param file The <strong>File</strong> you want to delete.
294 * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
295 */
296 static public boolean delete(File file)
297 {
298 // Nothing to do if it doesn't exist
299 if (!file.exists()) {
300 return true;
301 }
302
303 return deleteInternal(file);
304 }
305
306
307 /** Convenience function. */
308 static public boolean delete(String filename)
309 {
310 return delete(new File(filename));
311 }
312
313
314 /** In Java you have to make sure a directory is empty before you delete it, so recursively delete. */
315 static private boolean deleteInternal(File file)
316 {
317 // If file is a directory, we have to recursively delete its contents first
318 if (file.isDirectory()) {
319 File files[] = file.listFiles();
320 for (int i = 0; i < files.length; i++) {
321 if (deleteInternal(files[i]) == false) {
322 System.err.println("Error: Could not delete folder " + file);
323 return false;
324 }
325 }
326 }
327
328 // Delete file
329 if (file.delete() == false) {
330 System.err.println("Error: Could not delete file " + file);
331 return false;
332 }
333
334 return true;
335 }
336
337
338 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
339 static final public String BYTE_SUFFIX = " b";
340 static final public long GIGABYTE = 1024000000l;
341 static final public String GIGABYTE_SUFFIX = " Gb";
342 static final public long KILOBYTE = 1024l;
343 static final public String KILOBYTE_SUFFIX = " kb";
344 static final public long MEGABYTE = 1024000l;
345 static final public String MEGABYTE_SUFFIX = " Mb";
346 static final public String formatFileLength(long length) {
347 StringBuffer result = new StringBuffer("");
348 float number = 0f;
349 String suffix = null;
350 // Determine the floating point number and the suffix (radix) used.
351 if(length >= GIGABYTE) {
352 number = (float) length / (float) GIGABYTE;
353 suffix = GIGABYTE_SUFFIX;
354 }
355 else if(length >= MEGABYTE) {
356 number = (float) length / (float) MEGABYTE;
357 suffix = MEGABYTE_SUFFIX;
358 }
359 else if(length >= KILOBYTE) {
360 number = (float) length / (float) KILOBYTE;
361 suffix = KILOBYTE_SUFFIX;
362 }
363 else {
364 // Don't need to do anything fancy if the file is smaller than a kilobyte
365 return length + BYTE_SUFFIX;
366 }
367 // 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.
368 String number_str = Float.toString(number);
369 char number_char[] = number_str.toCharArray();
370 int pos = 0;
371 // Print the characters up to the '.'
372 while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
373 result.append(number_char[pos]);
374 pos++;
375 }
376 if(pos < number_char.length) {
377 // Print the '.' and at most two characters after it
378 result.append(number_char[pos]);
379 pos++;
380 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
381 result.append(number_char[pos]);
382 }
383 // Search through the remaining string for 'E'
384 while(pos < number_char.length && number_char[pos] != 'E') {
385 pos++;
386 }
387 // If we still have string then we found an E. Copy the remaining string.
388 while(pos < number_char.length) {
389 result.append(number_char[pos]);
390 pos++;
391 }
392 }
393 // Add suffix
394 result.append(suffix);
395 // Done
396 return result.toString();
397 }
398
399 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
400 * @param text The <strong>String</strong> requiring formatting.
401 * @param width The maximum width per line as an <i>int</i>.
402 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
403 * 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.
404 */
405 static public String formatHTMLWidth(String text, int width) {
406 if(text == null) {
407 return "Error";
408 }
409 HTMLStringTokenizer html = new HTMLStringTokenizer(text);
410 int current_width = 0;
411 int threshold = width / 2;
412 Stack lines = new Stack();
413 String line = "";
414 while(html.hasMoreTokens()) {
415 String token = html.nextToken();
416 while(token != null) {
417 if(html.isTag()) {
418 // Insert smart HTML tag code here.
419 token = null;
420 }
421 else {
422 // If the token is bigger than two thirds width, before we've even started break it down.
423 if(current_width + 1 + token.length() > width && token.length() > threshold) {
424 if(width == current_width) {
425 lines.push(line);
426 line = token;
427 current_width = token.length();
428 }
429 else {
430 String prefix = token.substring(0, width - 1 - current_width);
431 token = token.substring(prefix.length());
432 if(current_width == 0) {
433 line = line + prefix;
434 }
435 else {
436 line = line + " " + prefix;
437 }
438 lines.push(line);
439 line = "";
440 current_width = 0;
441 }
442 }
443 // If adding the next token would push us over the maximum line width.
444 else if(current_width + 1 + token.length() > width) {
445 lines.push(line);
446 line = token;
447 current_width = token.length();
448 token = null;
449 }
450 // Otherwise we should be able to just add the token, give or take.
451 else {
452 if(current_width == 0) {
453 line = line + token;
454 current_width = token.length();
455 }
456 else {
457 // Special case for standard punctuation which may exist after a tag like so:
458 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation.
459 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
460 line = line + token;
461 current_width = current_width + 1;
462 }
463 else {
464 line = line + " " + token;
465 current_width = current_width + 1 + token.length();
466 }
467 }
468 token = null;
469 }
470 }
471 }
472 }
473 String result = line;
474 while(!lines.empty()) {
475 result = (String)lines.pop() + "<BR>" + result;
476 }
477 // Replace ' ' with "&nbsp;"
478 boolean tag = false;
479 int pos = 0;
480 while(pos < result.length()) {
481 if(result.charAt(pos) == '<') {
482 tag = true;
483 }
484 else if(result.charAt(pos) == '>') {
485 tag = false;
486 }
487 else if(result.charAt(pos) == ' ' && !tag) {
488 String prefix = result.substring(0, pos);
489 String suffix = result.substring(pos + 1);
490 result = prefix + "&nbsp;" + suffix;
491 }
492 pos++;
493 }
494 result = "<HTML>" + result + "</HTML>";
495 return result;
496 }
497
498
499 static public String getDateString() {
500 Calendar current = Calendar.getInstance();
501 String day_name = null;
502 switch(current.get(Calendar.DAY_OF_WEEK)) {
503 case Calendar.MONDAY: day_name = "Dates.Mon"; break;
504 case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
505 case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
506 case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
507 case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
508 case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
509 case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
510 default: day_name = "";
511 }
512 String month_name = null;
513 switch(current.get(Calendar.MONTH)) {
514 case Calendar.JANUARY: month_name = "Dates.Jan"; break;
515 case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
516 case Calendar.MARCH: month_name = "Dates.Mar"; break;
517 case Calendar.APRIL: month_name = "Dates.Apr"; break;
518 case Calendar.MAY: month_name = "Dates.May"; break;
519 case Calendar.JUNE: month_name = "Dates.Jun"; break;
520 case Calendar.JULY: month_name = "Dates.Jul"; break;
521 case Calendar.AUGUST: month_name = "Dates.Aug"; break;
522 case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
523 case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
524 case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
525 case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
526 default: month_name = "";
527 }
528 int day = current.get(Calendar.DAY_OF_MONTH);
529 int hour = current.get(Calendar.HOUR_OF_DAY);
530 int minute = current.get(Calendar.MINUTE);
531 int second = current.get(Calendar.SECOND);
532 int year = current.get(Calendar.YEAR);
533
534 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);
535 }
536
537
538 /** Determine this machines name.
539 * @return The name as a <strong>String</strong>.
540 */
541 static public String getMachineName() {
542 try {
543 return InetAddress.getLocalHost().getHostName();
544 }
545 catch(UnknownHostException ex) {
546 }
547 return "Unknown Machine";
548 }
549
550
551 static public String getSitesDir(String gsdl3_path) {
552 return gsdl3_path + "sites" + File.separator;
553
554 }
555
556 /** @return the OSdir foldername: windows, linux or darwin */
557 public static String getOSdirName() {
558 if(Utility.isWindows()) {
559 return "windows";
560 }
561 if(Utility.isMac()) {
562 return "darwin";
563 }
564 return "linux"; // else assume it's a linux machine, OSdirname = linux
565 }
566
567
568 /** Method to determine if the host system is MacOS based.
569 * @return a boolean which is true if the platform is MacOS, false otherwise
570 */
571 public static boolean isMac() {
572 Properties props = System.getProperties();
573 String os_name = props.getProperty("os.name","");
574 if(os_name.startsWith("Mac OS")) {
575 return true;
576 }
577 return false;
578 }
579
580
581 /** Method to determine if the host system is Microsoft Windows based.
582 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
583 */
584 public static boolean isWindows() {
585 Properties props = System.getProperties();
586 String os_name = props.getProperty("os.name","");
587 if(os_name.startsWith("Windows")) {
588 return true;
589 }
590 return false;
591 }
592
593 public static boolean isWindows9x() {
594 Properties props = System.getProperties();
595 String os_name = props.getProperty("os.name","");
596 if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
597 return true;
598 }
599 return false;
600 }
601 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
602 * @param str The target <strong>String</strong> that needs to be padded.
603 * @param length The desired length of the string as an <i>int</i>.
604 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
605 */
606 static private String pad(String str_raw, int length, char fill, boolean end) {
607 StringBuffer str = new StringBuffer(str_raw);
608 while(str.length() < length) {
609 if(end) {
610 str.insert(0, fill);
611 }
612 else {
613 str.append(fill);
614 }
615 }
616 return str.toString();
617 }
618
619
620 /** Builds the cache dir by appending the user path and 'cache'.
621 * @return a File representing the path to the private file cache within the current collection.
622 */
623 public static File getCacheDir() {
624 return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
625 }
626
627 /** Method which constructs the log directory given a certain collection.
628 * @param col_dir The location of the collection directory as a <strong>String</strong>.
629 * @return The location of the given collections log directory, also as a <strong>String</strong>.
630 */
631 public static String getLogDir(String col_dir) {
632 if(col_dir != null) {
633 return col_dir + LOG_DIR;
634 }
635 else {
636 return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
637 }
638 }
639
640 static final private String APPLICATION_DATA_FOLDER = "Application Data";
641 static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
642 static final private String USER_HOME_PROPERTY = "user.home";
643 static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
644 /** Definition of an important directory name, in this case the log directory for the collection. */
645 static final public String LOG_DIR = "log" + File.separator;
646
647 static public File getGLIUserFolder()
648 {
649 if (Utility.isWindows()) {
650 return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
651 }
652 else {
653 return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
654 }
655 }
656
657 static private HashMap plugin_map = null;
658
659 static private void setUpPluginNameMap() {
660 plugin_map = new HashMap();
661 plugin_map.put("GAPlug", "GreenstoneXMLPlugin");
662 plugin_map.put("RecPlug", "DirectoryPlugin");
663 plugin_map.put("ArcPlug","ArchivesInfPlugin");
664 plugin_map.put("TEXTPlug","TextPlugin");
665 plugin_map.put("XMLPlug","ReadXMLFile");
666 plugin_map.put("EMAILPlug","EmailPlugin");
667 plugin_map.put("SRCPlug","SourceCodePlugin");
668 plugin_map.put("NULPlug","NulPlugin");
669 plugin_map.put("W3ImgPlug","HTMLImagePlugin");
670 plugin_map.put("PagedImgPlug","PagedImagePlugin");
671 plugin_map.put("METSPlug", "GreenstoneMETSPlugin");
672 plugin_map.put("DBPlug", "DatabasePlugin");
673 plugin_map.put("PPTPlug", "PowerPointPlugin");
674 plugin_map.put("PSPlug", "PostScriptPlugin");
675 }
676
677 static public String ensureNewPluginName(String plugin) {
678 if (plugin.endsWith("Plugin")) return plugin;
679 if (plugin_map == null) {
680 setUpPluginNameMap();
681 }
682 String new_name = (String)plugin_map.get(plugin);
683 if (new_name != null) return new_name;
684 new_name = plugin.replaceAll("Plug", "Plugin");
685 return new_name;
686 }
687
688
689 /** Write out a property line--a (property, value) pair--to the gsdl(3)site.cfg file.
690 * If the file already contains the line as-is, it is not re-written.
691 * If the file doesn't contain the line, it is appended.
692 * If the file contained a different value for the property, the line is corrected
693 * and the file is written out.
694 * If the propertyValue parameter is null, the property line is removed from the file.
695 * Not using the Properties class, as we want to keep the file's contents in the
696 * same order and preserve all the comments in as they're meant to help the user.
697 * Return the old value for the property, if it existed, else "".
698 */
699 public static String updatePropertyConfigFile(
700 String filename, String propertyName, String propertyValue)
701 {
702 File propFile = new File(filename);
703 String oldValue = "";
704 if(!propFile.exists()) {
705 System.err.println("*** Unable to update property " + propertyName + " in file "
706 + filename + " to\n" + propertyValue + ". File does not (yet) exist.\n");
707 return oldValue;
708 }
709 BufferedReader fin = null;
710 BufferedWriter fout = null;
711 StringBuffer contents = new StringBuffer();
712 String insertLine = null;
713 if(propertyValue != null) {
714 insertLine = propertyName+"\t"+propertyValue+"\n"; // new line after every propertyLine
715 }
716 boolean found = false;
717 try {
718 fin = new BufferedReader(new FileReader(filename));
719 String line = "";
720 while((line = fin.readLine()) != null) {
721 line = line.trim(); // remove any preceding (surrounding) whitespace
722 if(line.startsWith(propertyName)) { // won't match comment
723 found = true;
724 // store the previous value for the property
725 oldValue = line;
726 oldValue = oldValue.substring(propertyName.length());
727 oldValue = oldValue.trim();
728
729 if(propertyValue != null) { // line should be removed if propertyValue == null
730 if(line.equals(insertLine)) { // file is already correct, nothing to do
731 fin.close();
732 fin = null;
733 break;
734 } else {
735 contents.append(insertLine);
736 }
737 }
738 } else { // any other line
739 contents.append(line);
740 contents.append("\n"); // ensures the required new line at end of file
741 }
742 }
743
744 if(fin != null) { // need to write something out to the file
745 fin.close();
746 fin = null;
747
748 // if collecthome/property wasn't already specified in the file, append it
749 // but only if we have a value to write out to the file
750 if(!found && propertyValue != null) {
751 fout = new BufferedWriter(new FileWriter(filename, true)); // append mode
752 fout.write(insertLine, 0, insertLine.length());
753 } else {
754 fout = new BufferedWriter(new FileWriter(filename)); // hopefully this will overwrite
755 fout.write(contents.toString(), 0, contents.length());
756 }
757
758 fout.close();
759 fout = null;
760
761 } // else the file is fine
762 } catch(IOException e) {
763 System.err.println("*** Could not update file: " + filename);
764 System.err.println("with the " + propertyName + " property set to " + propertyValue);
765 System.err.println("Exception occurred: " + e.getMessage());
766 } finally {
767 SafeProcess.closeResource(fin);
768 SafeProcess.closeResource(fout);
769
770 }
771 return oldValue;
772 }
773}
Note: See TracBrowser for help on using the repository browser.