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

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