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

Last change on this file since 7224 was 7224, checked in by kjdon, 20 years ago

tidied up the getXXXDir methods in Utility. Utility.getCollectionDir (which returns the path to the collect directory) has been renamed to getCollectDir, and a new Utility.getCollectionDir is provided which returns the path to an individual collection's base dir

  • Property svn:keywords set to Author Date Id Revision
File size: 40.4 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 */
37package org.greenstone.gatherer.util;
38
39import java.awt.*;
40import java.io.*;
41import java.net.*;
42import java.util.*;
43import javax.swing.*;
44import javax.swing.tree.*;
45import org.apache.xerces.parsers.*;
46import org.apache.xml.serialize.*;
47import org.greenstone.gatherer.Dictionary;
48import org.greenstone.gatherer.Gatherer;
49import org.greenstone.gatherer.util.HTMLStringTokenizer;
50import org.w3c.dom.*;
51import org.xml.sax.*;
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 static final public Dimension BUTTON_SIZE = new Dimension(160, 35);
59 static final public Dimension DOUBLE_IMAGE_BUTTON_SIZE = new Dimension(190, 35);
60 static final public Dimension LABEL_SIZE = new Dimension(150, 25);
61 /** The default size of a gatherer progress bar, in either the download view or the build view. */
62 static final public Dimension PROGRESS_BAR_SIZE = new Dimension(580,65);
63 /** The number of kilobytes to use as a io buffer. */
64 static final public int FACTOR = 1;
65 /** The size of the io buffer, calculated as FACTOR * 1024. */
66 static final public int BUFFER_SIZE = FACTOR * 1024;
67 /** Definition of an important directory name, in this case the archive directory for the collection. */
68 static final public String ARCHIVE_DIR = "archives" + File.separator;
69 /** Definition of an important directory name, in this case the base dir, or the working directory of the Gatherer. */
70 static final public String BASE_DIR = System.getProperty("user.dir") + File.separator;
71 static final public String BUILD_CFG_FILENAME = "build.cfg";
72 /** Definition of an important directory name, in this case the building directory for the collection. */
73 static final public String BUILD_DIR = "building" + File.separator;
74 /** Definition of an important directory name, in this case the public web cache for the Gatherer. */
75 static final public String CACHE_DIR = BASE_DIR + "cache" + File.separator;
76 static final public String CFG_COLLECTIONMETA_COLLECTIONNAME = "collectionmeta\tcollectionname";
77 static final public String CFG_COLLECTIONMETA_COLLECTIONEXTRA = "collectionmeta\tcollectionextra";
78 static final public String CFG_COLLECTIONMETA_ICONCOLLECTION = "collectionmeta\ticoncollection";
79 static final public String CFG_CLASSIFY = "classify";
80 static final public String CFG_CLASSIFY_BUTTONNAME = "-buttonname";
81 static final public String CFG_CLASSIFY_HFILE = "-hfile";
82 static final public String CFG_CLASSIFY_METADATA = "-metadata";
83 static final public String CFG_CLASSIFY_SORT = "-sort";
84 static final public String CFG_CREATOR = "creator";
85 static final public String CFG_FORMAT = "format";
86 static final public String CFG_MAINTAINER = "maintainer";
87 /** Definition of an important directory name, in this case the parent directory of all the collections in the gsdl. */
88 static final public String COL_DIR = "collect" + File.separator;
89 static final public String COLLECTION_TREE = "Collection";
90 /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
91 static final public String CONFIG_DIR = "etc" + File.separator + "collect.cfg";
92 /** The default file name for the urls missing any file. */
93 static final public String DEFAULT_FILE = "index.html";
94 static final public String DEFAULT_NAMESPACE = "gsp";
95 /** The default protocol header for those urls missing any protocol. */
96 static final public String DEFAULT_PROTOCOL = "http://";
97 /** The default dictionary to load. */
98 static final public String DICTIONARY = "dictionary";
99 static final public String ENCODING = "UTF-8";
100 static final public String ENGLISH_VALUE = "en";
101 /** Definition of an important directory name, in this case the etc (or extra information) directory for the collection. */
102 static final public String ETC_DIR = "etc" + File.separator;
103 static final public String EXTRACTED_METADATA_NAMESPACE = "ex";
104 /** The location of the default greenstone metadata file. */
105 static final public String GREENSTONEDIRECTORYMETADATA_TEMPLATE = "xml/metadata.xml";
106 /** Definition of an important directory name, in this case the private web cache directory for the collection. */
107 static final public String GCACHE_DIR = "cache" + File.separator;
108 static final public String GLI_ARCHIVE = "GLI.jar";
109 static final public String GLI_EXTENSION = ".col";
110 /** Definition of an important directory name, in this case the location of help documentation. */
111 static final public String HELP_DIR = BASE_DIR + "help" + File.separator;
112 /** Definition of an important directory name, in this case the images directory for the collection. */
113 static final public String IMAGES_DIR = "images" + File.separator;
114 /** Definition of an important directory name, in this case the import directory for the collection. */
115 static final public String IMPORT_DIR = "import" + File.separator;
116 /** Definition of an important directory name, in this case the backup import directory for the collection. */
117 static final public String IMPORT_BAK_DIR = "import.bak" + File.separator;
118 /** Definition of an important directory name, in this case the index directory for the collection. */
119 static final public String INDEX_DIR = "index" + File.separator;
120 static final public String LANGUAGE_ATTRIBUTE = "language";
121 /** Definition of an important directory name, in this case the log directory for the collection. */
122 static final public String LOG_DIR = "log" + File.separator;
123 /** Definition of an important directory name, in this case the location of the expected collection metadata sets.. */
124 static final public String META_DIR = "metadata" + File.separator; // Col. Copy
125 /** Definition of an important directory name, in this case the location of the default metadata sets. */
126 static final public String METADATA_DIR = BASE_DIR + "metadata" + File.separator;
127 /** The location the gatherer expects to find metadata set information. */
128 static final public String METADATA_SET_TEMPLATE = "xml/template.mds";
129 static final public String METADATA_VALUE_TEMPLATE = "xml/template.mdv";
130 static final public String METADATA_XML = "metadata.xml";
131 static final public String NAME_ELEMENT = "Name";
132 /** The default name of the perl executable under unix. */
133 static final public String PERL_EXECUTABLE_UNIX = "perl";
134 /** The default name of the perl executable under windows. */
135 static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
136 /** The default profile file */
137 static final public String PROFILE_TEMPLATE = "xml/protemp.xml";
138 /** The name of the Gatherer. */
139 static final public String PROGRAM_NAME = "Greenstone Librarian Interface";
140 /** The current version of the Gatherer. */
141 static final public String PROGRAM_VERSION = "v2.50";
142 /** Definition of an important directory name, in this case the location of the recycled files location. */
143 static final public String RECYCLE = BASE_DIR + "recycle" + File.separator;
144 /** Definition of an important directory name, in this case the location of image and other resources. */
145 static final public String RES_DIR = BASE_DIR + "resource" + File.separator;
146 static final public String SERVER_EXE = "server.exe";
147 /** Definition of an important directory name, in this case the location of opening (or welcome) screen html. */
148 static final public String WELCOME_DIR = BASE_DIR + "welcome" + File.separator;
149 static final public String WORKSPACE_TREE = "Workspace";
150 static final public String XML_DIRECTORY = "xml" + File.separator;
151 // These are out of alphabetic order to avoid forward reference error.
152 /** The default icon to produce a 'help-icon' sized blank space before a menu entry. */
153 static final public ImageIcon BLANK_ICON = new ImageIcon(ClassLoader.getSystemResource("images/blank.gif"));
154 /** The default error icon image. */
155 static final public ImageIcon ERROR_ICON = new ImageIcon(ClassLoader.getSystemResource("images/error.gif"));
156 static final public ImageIcon HELP_ICON = new ImageIcon(ClassLoader.getSystemResource("images/help.gif"));
157 /** The image for a toggle button whose state is 'on'. */
158 static final public ImageIcon ON_ICON = new ImageIcon(ClassLoader.getSystemResource("images/check.gif"));
159 /** The image for a toggle button whose state is 'off'. */
160 static final public ImageIcon OFF_ICON = new ImageIcon(ClassLoader.getSystemResource("images/cross.gif"));
161
162 /** Decodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces "\n" with a newline.
163 * @param raw The <strong>String</strong> before decoding, read from the configuration file..
164 * @return A <strong>String</strong> ready to be placed in a component.
165 */
166 static public String decodeGreenstone(String raw) {
167 raw = raw.replaceAll("&apos;", "\'");
168 raw = raw.replaceAll("&gt;", ">");
169 raw = raw.replaceAll("&lt;", "<");
170 raw = raw.replaceAll("&quot;", "\"");
171 raw = raw.replaceAll("&#39;", "\'");
172 raw = raw.replaceAll("\\\\n", "\n");
173 return raw;
174 }
175
176 /** Takes a rfc2616 'safe' String and translates it back into its 'unsafe' form. Basically the native c wget decode_string() function, but without pointer stuff. If searches through the String looking for the pattern %xy where x and y are hexidecimal digits and where xy maps to a character.<BR> If x or y are not hexidecimal or % is followed by a \0 then the pattern is left as is.
177 * @param encoded The url-safe <strong>String</strong> to be decoded.
178 * @return The decoded <strong>String</strong>.
179 */
180 /* private static String decodeString(String encoded) {
181 String decoded = "";
182 for(int i = 0; i < encoded.length(); i++) {
183 if(encoded.charAt(i) == '%') {
184 if(hexidecimal(encoded.charAt(i+1)) != -1
185 && hexidecimal(encoded.charAt(i+2)) != -1) {
186 char unsafe_chr = (char)
187 ((hexidecimal(encoded.charAt(i+1)) * 16) +
188 hexidecimal(encoded.charAt(i+2)));
189 decoded = decoded + unsafe_chr;
190 i = i + 2;
191 }
192 }
193 else {
194 decoded = decoded + encoded.charAt(i);
195 }
196 }
197 return decoded;
198 } */
199
200 /** It turns out that in Java you have to make sure a directory is empty before you delete it (much like unix I suppose), and so just like unix I'll have to set up a recursive delete function.
201 * @param file The <strong>File</strong> you want to delete.
202 * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
203 */
204 static public boolean delete(File file) {
205 boolean result = true;
206 // If files a directory, delete files children.
207 if(file.isDirectory()) {
208 File files[] = file.listFiles();
209 for(int i = 0; files != null && result && i < files.length; i++) {
210 result = delete(files[i]);
211 }
212 }
213 if(result) {
214 // Delete file.
215 return file.delete();
216 }
217 return result;
218 }
219
220 /* static private String encodeGreenstone(String raw) {
221 raw = raw.replaceAll("<", "&lt;");
222 raw = raw.replaceAll(">", "&gt;");
223 raw = raw.replaceAll("\n", "\\\\n");
224 return raw;
225 } */
226
227 /** Using this method we can request that a certain document be written, as valid XML, to a certain output stream. This makes use of the Xerces Serialization suite, which should in no way be confused with the usual method of Serialization used by Java. */
228 static public boolean export(Document document, String filename) {
229 return export(document, new File(filename));
230 }
231
232 static public boolean export(Document document, File file) {
233 try {
234 OutputStream os = new FileOutputStream(file);
235 // Create an output format for our document.
236 OutputFormat f = new OutputFormat(document);
237 f.setEncoding(ENCODING);
238 f.setIndenting(true);
239 f.setLineWidth(0); // Why isn't this working!
240 f.setPreserveSpace(false);
241 // Create the necessary writer stream for serialization.
242 OutputStreamWriter osw = new OutputStreamWriter(os, ENCODING);
243 Writer w = new BufferedWriter(osw);
244 // Generate a new serializer from the above.
245 XMLSerializer s = new XMLSerializer(w, f);
246 s.asDOMSerializer();
247 // Finally serialize the document to file.
248 s.serialize(document);
249 // And close.
250 os.close();
251 return true;
252 }
253 // A file not found exception is most likely thrown because the directory the metadata.xml file is attempting to be written to no longer has any files in it. I'll add a test in MetadataXMLFile to test for this, but if it still happens ignore it (a non-existant directory can't really have metadata added to it any way.
254 catch (Exception exception) {
255 if(!file.getName().endsWith(METADATA_XML)) {
256 Gatherer.printStackTrace(exception);
257 return false;
258 }
259 return true;
260 }
261 }
262
263 /** Given a starting directory, searches for the collect.cfg file and returns it if found.
264 * @return The collect.cfg File or null if not found.
265 */
266 static final public File findConfigFile(File start) {
267 if(start == null) {
268 return null;
269 }
270 // See if the collect.cfg files here.
271 File collect_cfg = new File(start, "collect.cfg");
272 if(collect_cfg.exists()) {
273 return collect_cfg;
274 }
275 // Search for the existance of collect.cfg in a etc directory.
276 File etc_dir = new File(start, "etc" + File.separator + "collect.cfg");
277 if(etc_dir.exists()) {
278 return etc_dir;
279 }
280 // Otherwise search this directories parent if its not null.
281 return findConfigFile(start.getParentFile());
282 }
283
284 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
285 static final public String BYTE_SUFFIX = " b";
286 static final public long GIGABYTE = 1024000000l;
287 static final public String GIGABYTE_SUFFIX = " Gb";
288 static final public long KILOBYTE = 1024l;
289 static final public String KILOBYTE_SUFFIX = " kb";
290 static final public long MEGABYTE = 1024000l;
291 static final public String MEGABYTE_SUFFIX = " mb";
292 static final public String formatFileLength(long length) {
293 StringBuffer result = new StringBuffer("");
294 float number = 0f;
295 String suffix = null;
296 // Determine the floating point number and the suffix (radix) used.
297 if(length >= GIGABYTE) {
298 number = (float) length / (float) GIGABYTE;
299 suffix = GIGABYTE_SUFFIX;
300 }
301 else if(length >= MEGABYTE) {
302 number = (float) length / (float) MEGABYTE;
303 suffix = MEGABYTE_SUFFIX;
304 }
305 else if(length >= KILOBYTE) {
306 number = (float) length / (float) KILOBYTE;
307 suffix = KILOBYTE_SUFFIX;
308 }
309 else {
310 // Don't need to do anything fancy if the file is smaller than a kilobyte
311 return length + BYTE_SUFFIX;
312 }
313 // 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.
314 String number_str = Float.toString(number);
315 char number_char[] = number_str.toCharArray();
316 int pos = 0;
317 // Print the characters up to the '.'
318 while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
319 result.append(number_char[pos]);
320 pos++;
321 }
322 if(pos < number_char.length) {
323 // Print the '.' and at most two characters after it
324 result.append(number_char[pos]);
325 pos++;
326 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
327 result.append(number_char[pos]);
328 }
329 // Search through the remaining string for 'E'
330 while(pos < number_char.length && number_char[pos] != 'E') {
331 pos++;
332 }
333 // If we still have string then we found an E. Copy the remaining string.
334 while(pos < number_char.length) {
335 result.append(number_char[pos]);
336 pos++;
337 }
338 }
339 // Add suffix
340 result.append(suffix);
341 // Done
342 return result.toString();
343 }
344
345 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
346 * @param text The <strong>String</strong> requiring formatting.
347 * @param width The maximum width per line as an <i>int</i>.
348 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
349 * 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.
350 */
351 static public String formatHTMLWidth(String text, int width) {
352 if(text == null) {
353 return "Error";
354 }
355 HTMLStringTokenizer html = new HTMLStringTokenizer(text);
356 int current_width = 0;
357 int threshold = width / 2;
358 Stack lines = new Stack();
359 String line = "";
360 while(html.hasMoreTokens()) {
361 String token = html.nextToken();
362 while(token != null) {
363 if(html.isTag()) {
364 // Insert smart HTML tag code here.
365 token = null;
366 }
367 else {
368 // If the token is bigger than two thirds width, before we've even started break it down.
369 if(current_width + 1 + token.length() > width && token.length() > threshold) {
370 if(width == current_width) {
371 lines.push(line);
372 line = token;
373 current_width = token.length();
374 }
375 else {
376 String prefix = token.substring(0, width - 1 - current_width);
377 token = token.substring(prefix.length());
378 if(current_width == 0) {
379 line = line + prefix;
380 }
381 else {
382 line = line + " " + prefix;
383 }
384 lines.push(line);
385 line = "";
386 current_width = 0;
387 }
388 }
389 // If adding the next token would push us over the maximum line width.
390 else if(current_width + 1 + token.length() > width) {
391 line = space(line, width, current_width);
392 lines.push(line);
393 line = token;
394 current_width = token.length();
395 token = null;
396 }
397 // Otherwise we should be able to just add the token, give or take.
398 else {
399 if(current_width == 0) {
400 line = line + token;
401 current_width = token.length();
402 }
403 else {
404 // Special case for standard punctuation which may exist after a tag like so:
405 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation.
406 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
407 line = line + token;
408 current_width = current_width + 1;
409 }
410 else {
411 line = line + " " + token;
412 current_width = current_width + 1 + token.length();
413 }
414 }
415 token = null;
416 }
417 }
418 }
419 }
420 String result = line;
421 while(!lines.empty()) {
422 result = (String)lines.pop() + "<BR>" + result;
423 }
424 // Replace ' ' with "&nbsp;"
425 boolean tag = false;
426 int pos = 0;
427 while(pos < result.length()) {
428 if(result.charAt(pos) == '<') {
429 tag = true;
430 }
431 else if(result.charAt(pos) == '>') {
432 tag = false;
433 }
434 else if(result.charAt(pos) == ' ' && !tag) {
435 String prefix = result.substring(0, pos);
436 String suffix = result.substring(pos + 1);
437 result = prefix + "&nbsp;" + suffix;
438 }
439 pos++;
440 }
441 result = "<HTML>" + result + "</HTML>";
442 return result;
443 }
444 /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
445 * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
446 * @param raw The raw filename path <strong>String</strong>.
447 * @param width The maximum width as an <i>int</i>.
448 * @return A path <strong>String</strong> no longer than width.
449 */
450 static public String formatPath(String key, String raw, int width) {
451 JLabel label = new JLabel(Dictionary.get(key, raw));
452 int position = -1;
453 while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
454 raw = "..." + raw.substring(position + 1);
455 label.setText(Dictionary.get(key, raw));
456 }
457 if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
458 raw = raw.substring(3);
459 }
460 return raw;
461 }
462
463 /** Method which constructs the archive directory given a certain collection.
464 * @param gsdl_path The location of the collection directory as a <strong>String</strong>.
465 * @return The location of the given collections archive directory, also as a <strong>String</strong>.
466 */
467 static public String getArchiveDir(String col_dir) {
468 return col_dir + ARCHIVE_DIR;
469 }
470 /** Method which constructs the build directory given a certain collection.
471 * @param col_dir The location of the collection directory as a <strong>String</strong>.
472 * @return The location of the given collections build directory, also as a <strong>String</strong>.
473 */
474 static public String getBuildDir(String col_dir) {
475 if(col_dir == null) {
476 return BASE_DIR + BUILD_DIR;
477 }
478 return col_dir + BUILD_DIR;
479 }
480 /** Builds the cache dir by appending the user path and 'cache'.
481 * @return a File representing the path to the private file cache within the current collection.
482 */
483 public static File getCacheDir() {
484 return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
485 }
486 /** Method which constructs the collect directory for Greenstone.
487 * @param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>.
488 * @return The location of the collection directory, also as a <strong>String</strong>.
489 */
490 public static String getCollectDir(String gsdl_path) {
491 return gsdl_path + COL_DIR;
492 }
493
494 /** Method which constructs a collection's top level directory
495 * @@param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>.
496 * @param The name of the collection as a <strong>String</strong>.
497 * @return The location of the collection's base directory
498 */
499 public static String getCollectionDir(String gsdl_path, String coll_name) {
500 return getCollectDir(gsdl_path) + coll_name + File.separator;
501 }
502
503 /** Method which constructs the configuration file given a certain collection.
504 * @param col_dir The location of the collection directory as a <strong>String</strong>.
505 * @return The location of the given collections configuration file, also as a <strong>String</strong>.
506 */
507 static public String getConfigDir(String col_dir) {
508 return col_dir + CONFIG_DIR;
509 }
510
511 static public String getDateString() {
512 Calendar current = Calendar.getInstance();
513 String day_name = null;
514 switch(current.get(Calendar.DAY_OF_WEEK)) {
515 case Calendar.MONDAY: day_name = "Dates.Mon"; break;
516 case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
517 case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
518 case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
519 case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
520 case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
521 case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
522 default: day_name = "";
523 }
524 String month_name = null;
525 switch(current.get(Calendar.MONTH)) {
526 case Calendar.JANUARY: month_name = "Dates.Jan"; break;
527 case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
528 case Calendar.MARCH: month_name = "Dates.Mar"; break;
529 case Calendar.APRIL: month_name = "Dates.Apr"; break;
530 case Calendar.MAY: month_name = "Dates.May"; break;
531 case Calendar.JUNE: month_name = "Dates.Jun"; break;
532 case Calendar.JULY: month_name = "Dates.Jul"; break;
533 case Calendar.AUGUST: month_name = "Dates.Aug"; break;
534 case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
535 case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
536 case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
537 case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
538 default: month_name = "";
539 }
540 int day = current.get(Calendar.DAY_OF_MONTH);
541 int hour = current.get(Calendar.HOUR_OF_DAY);
542 int minute = current.get(Calendar.MINUTE);
543 int second = current.get(Calendar.SECOND);
544 int year = current.get(Calendar.YEAR);
545
546 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);
547 }
548
549 /** Retrieves and formats the depth field of the config file to four characters.
550 * @param length The length of the desired string as an <i>int</i>.
551 * @return A <strong>String</strong> representation of the mirroring depth padded to length characters.
552 */
553 /* private static String getDepthString(int length) {
554 return pad("" + Gatherer.self.config.getInt("mirroring.depth", false), length);
555 } */
556 /** Method which constructs the etc directory given a certain collection.
557 * @param col_dir The location of the collection directory as a <strong>String</strong>.
558 * @return The location of the given collections etc directory, also as a <strong>String</strong>.
559 */
560 public static String getEtcDir(String col_dir) {
561 return col_dir + ETC_DIR;
562 }
563
564 static final private String APPLICATION_DATA_FOLDER = "Application Data";
565 static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
566 static final private String USER_HOME_PROPERTY = "user.home";
567 static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
568
569 static public File getGLIUserFolder() {
570 File gli_user_folder = null;
571 if(Utility.isWindows()) {
572 gli_user_folder = new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
573 }
574 else {
575 gli_user_folder = new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
576 }
577 return gli_user_folder;
578 }
579
580 /** Retrieve the full file path to the help index xml file.
581 * @return the full path as a String
582 */
583 static public String getHelpFolder() {
584 String help_folder = HELP_DIR + Gatherer.config.getLanguage() + File.separator;
585 File help_folder_file = new File(help_folder);
586 // If that file can't be found, default to english
587 if(!help_folder_file.exists()) {
588 help_folder = HELP_DIR + StaticStrings.ENGLISH_LANGUAGE_STR + File.separator;
589 }
590 return help_folder;
591 }
592
593
594 /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
595 * @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists.
596 */
597 static public ImageIcon getImage(String filename) {
598 return getImage(filename, false);
599 }
600
601 static public ImageIcon getImage(String filename, boolean wait_until_complete) {
602 ImageIcon image = null;
603 try {
604 image = new ImageIcon(ClassLoader.getSystemResource("images/" + Dictionary.get("Version") + "/" + filename));
605 }
606 catch(NullPointerException exception) {
607 image = new ImageIcon(ClassLoader.getSystemResource("images/" + filename));
608 }
609 if(image == null) {
610 image = ERROR_ICON;
611 }
612
613 if(wait_until_complete) {
614 int load_status;
615 do {
616 load_status = image.getImageLoadStatus();
617 }
618 while(load_status != MediaTracker.ABORTED && load_status != MediaTracker.ERRORED && load_status != MediaTracker.COMPLETE);
619 }
620 return image;
621 }
622
623 /** Method which constructs the images directory given a certain collection.
624 * @param col_dir The location of the collection directory as a <strong>String</strong>.
625 * @return The location of the given collections images directory, also as a <strong>String</strong>.
626 */
627 public static String getImagesDir(String col_dir) {
628 return col_dir + IMAGES_DIR;
629 }
630 /** Method which constructs the import directory given a certain collection.
631 * @param col_dir The location of the collection directory as a <strong>String</strong>.
632 * @return The location of the given collections import directory, also as a <strong>String</strong>.
633 */
634 public static String getImportDir(String col_dir) {
635 return col_dir + IMPORT_DIR;
636 }
637 /** Method which constructs the index directory given a certain collection.
638 * @param col_dir The location of the collection directory as a <strong>String</strong>.
639 * @return The location of the given collections index directory, also as a <strong>String</strong>.
640 */
641 static public String getIndexDir(String col_dir) {
642 return col_dir + INDEX_DIR;
643 }
644 /** Method which constructs the log directory given a certain collection.
645 * @param col_dir The location of the collection directory as a <strong>String</strong>.
646 * @return The location of the given collections log directory, also as a <strong>String</strong>.
647 */
648 public static String getLogDir(String col_dir) {
649 if(col_dir != null) {
650 return col_dir + LOG_DIR;
651 }
652 else {
653 return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
654 }
655 }
656 /** Determine this machines name.
657 * @return The name as a <strong>String</strong>.
658 */
659 static public String getMachineName() {
660 try {
661 return InetAddress.getLocalHost().getHostName();
662 }
663 catch(UnknownHostException ex) {
664 }
665 return "Unknown Machine";
666 }
667 /** Method which constructs the metadata directory given a certain collection.
668 * @param col_dir The location of the collection directory as a <strong>String</strong>.
669 * @return The location of the given collections metadata directory, also as a <strong>String</strong>.
670 */
671 static public String getMetadataDir(String col_dir) {
672 return col_dir + META_DIR;
673 }
674
675
676 /* static private File getRecycleDirectory() {
677 return new File(RECYCLE);
678 } */
679
680 /** Determine whether a character is a hexidecimal one.
681 * @param chr The <i>char</i> in question.
682 * @return An <i>int</i> representing the value of the hexidecimal character or -1 if not a hexidecimal.
683 */
684 static private int hexidecimal(char chr) {
685 switch(chr) {
686 case '0':
687 return 0;
688 case '1':
689 return 1;
690 case '2':
691 return 2;
692 case '3':
693 return 3;
694 case '4':
695 return 4;
696 case '5':
697 return 5;
698 case '6':
699 return 6;
700 case '7':
701 return 7;
702 case '8':
703 return 8;
704 case '9':
705 return 9;
706 case 'A':
707 return 10;
708 case 'B':
709 return 11;
710 case 'C':
711 return 12;
712 case 'D':
713 return 13;
714 case 'E':
715 return 14;
716 case 'F':
717 return 15;
718 default:
719 return -1;
720 }
721 }
722
723 /** A string is a valid hierarchy index if it matches '[0-9](\.[0-9])*' */
724 static public boolean isIndex(String raw) {
725 boolean result = true;
726 for(int i = 0; result && i < raw.length(); i++) {
727 char c = raw.charAt(i);
728 if(Character.isDigit(c) || (c == '.' && (i != 0 || i != raw.length() - 1))) {
729 // Valid index
730 }
731 else {
732 result = false;
733 }
734 }
735 return result;
736 }
737
738 /** Method to determine if the host system is MacOS based.
739 * @return a boolean which is true if the platform is MacOS, false otherwise
740 */
741 public static boolean isMac() {
742 Properties props = System.getProperties();
743 String os_name = props.getProperty("os.name","");
744 if(os_name.startsWith("Mac OS")) {
745 return true;
746 }
747 return false;
748 }
749
750 /** Determines if the given file is a descendant of the folder described.
751 * @param pos_parent the possible parent folder as a String
752 * @param child the child File which we are checking
753 * @return true if the child is a descendant of the pos_parent, false otherwise
754 */
755 static public boolean isParentFolderOf(String pos_parent, File child) {
756 while(child != null) {
757 if(pos_parent.equals(child.getAbsolutePath())) {
758 return true;
759 }
760 // Just keep looping checking each of childs parent folders. Not a brilliantly efficient algorithm, but it should still be pretty fast compared to the algorithm for merging elements.
761 child = child.getParentFile();
762 }
763 return false;
764 }
765
766 /** Method to determine if the host system is Microsoft Windows based.
767 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
768 */
769 public static boolean isWindows() {
770 Properties props = System.getProperties();
771 String os_name = props.getProperty("os.name","");
772 if(os_name.startsWith("Windows")) {
773 return true;
774 }
775 return false;
776 }
777
778 public static boolean isWindows9x() {
779 Properties props = System.getProperties();
780 String os_name = props.getProperty("os.name","");
781 if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
782 return true;
783 }
784 return false;
785 }
786 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
787 * @param str The target <strong>String</strong> that needs to be padded.
788 * @param length The desired length of the string as an <i>int</i>.
789 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
790 */
791 public static String pad(String str, int length) {
792 return pad(str, length, ' ', true);
793 }
794 public static String pad(String str_raw, int length, char fill, boolean end) {
795 StringBuffer str = new StringBuffer(str_raw);
796 while(str.length() < length) {
797 if(end) {
798 str.insert(0, fill);
799 }
800 else {
801 str.append(fill);
802 }
803 }
804 return str.toString();
805 }
806
807 /** Parse in a xml document from a given filename. Note that this filename may need to be resolved by the class loader, especially for template files within a jar. */
808 static public Document parse(String filename, boolean use_classloader) {
809 File file = null;
810 if(use_classloader) {
811 try {
812 URL url = ClassLoader.getSystemResource(filename);
813 file = new File(URLDecoder.decode(url.getFile(), ENCODING));
814 url = null;
815 }
816 catch (Exception error) {
817 // Most likely file name.
818 file = new File("classes" + File.separator + filename);
819 //Gatherer.printStackTrace(error);
820 }
821 }
822 if(file == null) {
823 file = new File(filename);
824 }
825 return parse(file, true);
826 }
827 /** Parse in a xml document from a given file. */
828 static public Document parse(File file) {
829 return parse(file, true);
830 }
831 /** Parse in a xml document from a given file. */
832 static public Document parse(File file, boolean noisey) {
833 Document document = null;
834 try {
835 FileInputStream fis = new FileInputStream(file);
836 InputStreamReader isr = new InputStreamReader(fis, ENCODING);
837 Reader r = new BufferedReader(isr);
838 InputSource isc = new InputSource(r);
839 DOMParser parser = new DOMParser();
840 parser.setFeature("http://xml.org/sax/features/validation", false);
841 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
842 // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
843 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
844 parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
845 parser.parse(isc);
846 document = parser.getDocument();
847 isr.close();
848 fis.close();
849 parser = null;
850 isc = null;
851 r = null;
852 isr = null;
853 fis = null;
854 file = null;
855 }
856 catch (Exception error) {
857 if(noisey) {
858 error.printStackTrace();
859 Gatherer.println("Exception in Utility.parse() - Unexpected");
860 }
861 else {
862 Gatherer.println("Exception in Utility.parse() - Expected");
863 }
864 Gatherer.printStackTrace(error);
865 }
866 return document;
867 }
868
869 /** Method to spread out a line of text so that is is justified to the given width, by attempting to widen white-spacing in a balanced way.
870 * @param original The <strong>String</strong> to justify.
871 * @param width The desired width as an <i>int</i>.
872 * @param current_width An <i>int</i> representing the current width of the string, which takes into account special characters.
873 * @return The newly justified <strong>String</strong>.
874 */
875 static public String space(String original, int width, int current_width) {
876 // Strip trailing whitespace.
877 while(original.charAt(original.length() - 1) == ' ') {
878 original = original.substring(0, original.length() - 2);
879 }
880 int diff = width - current_width;
881 // Now add diff spaces, one at each existing space.
882 int pos = 0;
883 while(diff > 0) {
884 if(pos == original.length()) {
885 pos = 0;
886 }
887 if(original.charAt(pos) == ' ') {
888 // Insert a space.
889 String prefix = original.substring(0, pos);
890 String suffix = original.substring(pos);
891 original = prefix + " " + suffix;
892 pos = pos + 2;
893 diff--;
894 }
895 pos++;
896 }
897 return original;
898 }
899 /** Method to strip new lines and extra spaces from a string. Used to restore text that has been mangled into width formatted blocks by the DOM parser.
900 * @param raw_str The <strong>Strong</strong> containing the mangled text.
901 * @return A <strong>String</strong> with new lines and extra spaces removed.
902 */
903 static public String stripNL(String raw_str) {
904 byte raw[] = raw_str.getBytes();
905 byte formatted[] = new byte[raw.length];
906 byte previous = '\0';
907 int j = 0;
908 for(int i = 0; i < raw.length; i++) {
909 if(raw[i] == '\n') {
910 // Skip new lines.
911 }
912 else if(raw[i] == '\t') {
913 // Skip tabs.
914 }
915 else if(raw[i] == ' ' && raw[i] == previous) {
916 // Skip erroneous whitespace.
917 }
918 else {
919 formatted[j] = raw[i];
920 j++;
921 }
922 previous = raw[i];
923 }
924 byte finish[] = new byte[j];
925 System.arraycopy(formatted, 0, finish, 0, j);
926 return new String(finish);
927 }
928 /** Trims the string text to the length specified removing end characters and adding if necessary.
929 * @param text A <strong>String</strong> which you wish to ensure is shorter than length.
930 * @param length An <i>int</i> specifying the strings maximum length after which its trimmed.
931 * @return The trimmed <strong>String</strong>.
932 */
933 public static String trim(String text, int length) {
934 if(text.length() > length) {
935 text = text.substring(0, length);
936 text = text + "...";
937 }
938 return text;
939 }
940
941 static public String trimCenter(String text, int length) {
942 if(text.length() > length) {
943 int half = (length - 3) / 2;
944 StringBuffer temp = new StringBuffer(text.substring(0, half));
945 temp.append("...");
946 temp.append(text.substring(text.length() - half));
947 text = temp.toString();
948 }
949 return text;
950 }
951 /** This method checks to see what registered file system root directorys are mounted, and returns only accessible ones. The exception is removable media drives (in particular floppy-disk drives) which will throw all sorts of error if we test them here. Instead they are assumed to be always accessible, but a test is conducted at the time you attempt to map them to test for actual accessibility (then at least the errors are thrown after the user tries to initiate the mapping of the drive which has no disk in it).
952 * @param roots A <strong>File[]</strong> containing all of the file system roots registered on this system.
953 * @return A filtered <strong>File[]</strong> containing only those drives that are accessible and/or are floppy-disk media drives.
954 */
955 /* private static File[] validateDrives(File roots[]) {
956 Vector valid = new Vector();
957 for(int i = 0; i < roots.length; i++) {
958 String name = roots[i].getAbsolutePath();
959 name = name.toLowerCase();
960 if(!name.startsWith("a:") && !name.startsWith("b:")) {
961 valid.add(roots[i]);
962 }
963 }
964 roots = new File[valid.size()];
965 for(int i = 0; i < roots.length; i++) {
966 roots[i] = (File)valid.get(i);
967 }
968 return roots;
969 } */
970}
Note: See TracBrowser for help on using the repository browser.