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

Last change on this file since 6540 was 6539, checked in by jmt12, 20 years ago

Heres a bunch of other changed files. If it wasn't a Friday afternoon I might be bothered finding out what I actually changed in them. Such changes include: a new option or three on preferences, a bug fix for the GDM classes, several changes to CDM to allow for G2.39 configuration files, a fix to Codec to allow for quotes in format strings and more work on CommandTokenizer to allow for stupid, stupid, stupid collectionextra's starting with speech marks then a new line. Plus other stuff. And things. Peace Out.

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