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

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

Fixes to variants of debug function printCaller().

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