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

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

The tooltip formatter should no longer crash and burn on null strings

  • Property svn:keywords set to Author Date Id Revision
File size: 37.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 (FileNotFoundException fnf_exception) {
257 if(!file.getName().endsWith(METADATA_XML)) {
258 fnf_exception.printStackTrace();
259 return false;
260 }
261 return true;
262 }
263 catch (IOException ioe) {
264 ioe.printStackTrace();
265 return false;
266 }
267 }
268
269 /** Given a starting directory, searches for the collect.cfg file and returns it if found.
270 * @return The collect.cfg File or null if not found.
271 */
272 static final public File findConfigFile(File start) {
273 if(start == null) {
274 return null;
275 }
276 // See if the collect.cfg files here.
277 File collect_cfg = new File(start, "collect.cfg");
278 if(collect_cfg.exists()) {
279 return collect_cfg;
280 }
281 // Search for the existance of collect.cfg in a etc directory.
282 File etc_dir = new File(start, "etc" + File.separator + "collect.cfg");
283 if(etc_dir.exists()) {
284 return etc_dir;
285 }
286 // Otherwise search this directories parent if its not null.
287 return findConfigFile(start.getParentFile());
288 }
289
290 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
291 static final public String BYTE_SUFFIX = " b";
292 static final public long GIGABYTE = 1024000000l;
293 static final public String GIGABYTE_SUFFIX = " Gb";
294 static final public long KILOBYTE = 1024l;
295 static final public String KILOBYTE_SUFFIX = " kb";
296 static final public long MEGABYTE = 1024000l;
297 static final public String MEGABYTE_SUFFIX = " mb";
298 static final public String formatFileLength(long length) {
299 StringBuffer result = new StringBuffer("");
300 float number = 0f;
301 String suffix = null;
302 // Determine the floating point number and the suffix (radix) used.
303 if(length >= GIGABYTE) {
304 number = (float) length / (float) GIGABYTE;
305 suffix = GIGABYTE_SUFFIX;
306 }
307 else if(length >= MEGABYTE) {
308 number = (float) length / (float) MEGABYTE;
309 suffix = MEGABYTE_SUFFIX;
310 }
311 else if(length >= KILOBYTE) {
312 number = (float) length / (float) KILOBYTE;
313 suffix = KILOBYTE_SUFFIX;
314 }
315 else {
316 // Don't need to do anything fancy if the file is smaller than a kilobyte
317 return length + BYTE_SUFFIX;
318 }
319 // 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.
320 String number_str = Float.toString(number);
321 char number_char[] = number_str.toCharArray();
322 int pos = 0;
323 // Print the characters up to the '.'
324 while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
325 result.append(number_char[pos]);
326 pos++;
327 }
328 if(pos < number_char.length) {
329 // Print the '.' and at most two characters after it
330 result.append(number_char[pos]);
331 pos++;
332 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
333 result.append(number_char[pos]);
334 }
335 // Search through the remaining string for 'E'
336 while(pos < number_char.length && number_char[pos] != 'E') {
337 pos++;
338 }
339 // If we still have string then we found an E. Copy the remaining string.
340 while(pos < number_char.length) {
341 result.append(number_char[pos]);
342 pos++;
343 }
344 }
345 // Add suffix
346 result.append(suffix);
347 // Done
348 return result.toString();
349 }
350
351 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
352 * @param text The <strong>String</strong> requiring formatting.
353 * @param width The maximum width per line as an <i>int</i>.
354 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
355 * 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.
356 */
357 static public String formatHTMLWidth(String text, int width) {
358 if(text == null) {
359 return "Error";
360 }
361 HTMLStringTokenizer html = new HTMLStringTokenizer(text);
362 int current_width = 0;
363 int threshold = width / 2;
364 Stack lines = new Stack();
365 String line = "";
366 while(html.hasMoreTokens()) {
367 String token = html.nextToken();
368 while(token != null) {
369 if(html.isTag()) {
370 // Insert smart HTML tag code here.
371 token = null;
372 }
373 else {
374 // If the token is bigger than two thirds width, before we've even started break it down.
375 if(current_width + 1 + token.length() > width && token.length() > threshold) {
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 // If adding the next token would push us over the maximum line width.
389 else if(current_width + 1 + token.length() > width) {
390 line = space(line, width, current_width);
391 lines.push(line);
392 line = token;
393 current_width = token.length();
394 token = null;
395 }
396 // Otherwise we should be able to just add the token, give or take.
397 else {
398 if(current_width == 0) {
399 line = line + token;
400 current_width = token.length();
401 }
402 else {
403 // Special case for standard punctuation which may exist after a tag like so:
404 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation.
405 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
406 line = line + token;
407 current_width = current_width + 1;
408 }
409 else {
410 line = line + " " + token;
411 current_width = current_width + 1 + token.length();
412 }
413 }
414 token = null;
415 }
416 }
417 }
418 }
419 String result = line;
420 while(!lines.empty()) {
421 result = (String)lines.pop() + "<BR>" + result;
422 }
423 // Replace ' ' with "&nbsp;"
424 boolean tag = false;
425 int pos = 0;
426 while(pos < result.length()) {
427 if(result.charAt(pos) == '<') {
428 tag = true;
429 }
430 else if(result.charAt(pos) == '>') {
431 tag = false;
432 }
433 else if(result.charAt(pos) == ' ' && !tag) {
434 String prefix = result.substring(0, pos);
435 String suffix = result.substring(pos + 1);
436 result = prefix + "&nbsp;" + suffix;
437 }
438 pos++;
439 }
440 result = "<HTML>" + result + "</HTML>";
441 return result;
442 }
443 /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
444 * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
445 * @param raw The raw filename path <strong>String</strong>.
446 * @param width The maximum width as an <i>int</i>.
447 * @return A path <strong>String</strong> no longer than width.
448 */
449 static public String formatPath(String key, String raw, int width) {
450 JLabel label = new JLabel(Dictionary.get(key, raw));
451 int position = -1;
452 while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
453 raw = "..." + raw.substring(position + 1);
454 label.setText(Dictionary.get(key, raw));
455 }
456 if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
457 raw = raw.substring(3);
458 }
459 return raw;
460 }
461
462 /** Method which constructs the archive directory given a certain collection.
463 * @param col_dir The location of the collection directory as a <strong>String</strong>.
464 * @return The location of the given collections archive directory, also as a <strong>String</strong>.
465 */
466 static public String getArchiveDir(String gsdl_path, String col_name) {
467 return gsdl_path + File.separator + COL_DIR + col_name + File.separator + ARCHIVE_DIR;
468 }
469 /** Method which constructs the build directory given a certain collection.
470 * @param col_dir The location of the collection directory as a <strong>String</strong>.
471 * @return The location of the given collections build directory, also as a <strong>String</strong>.
472 */
473 static public String getBuildDir(String col_dir) {
474 if(col_dir == null) {
475 return BASE_DIR + BUILD_DIR;
476 }
477 return col_dir + BUILD_DIR;
478 }
479 /** Builds the private cache dir by appending col_dir and 'cache'.
480 * @param col_dir A String representing the directory path of the current collection.
481 * @return A String representing the path to the private file cache within the current collection.
482 */
483 public static String getCacheDir(String col_dir) {
484 return col_dir + GCACHE_DIR;
485 }
486 /** Method which constructs the collection 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 getCollectionDir(String gsdl_path) {
491 return gsdl_path + COL_DIR;
492 }
493 /** Method which constructs the configuration file given a certain collection.
494 * @param col_dir The location of the collection directory as a <strong>String</strong>.
495 * @return The location of the given collections configuration file, also as a <strong>String</strong>.
496 */
497 static public String getConfigDir(String col_dir) {
498 return col_dir + CONFIG_DIR;
499 }
500
501 static public String getDateString() {
502 Calendar current = Calendar.getInstance();
503 String day_name = null;
504 switch(current.get(Calendar.DAY_OF_WEEK)) {
505 case Calendar.MONDAY: day_name = "Dates.Mon"; break;
506 case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
507 case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
508 case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
509 case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
510 case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
511 case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
512 default: day_name = "";
513 }
514 String month_name = null;
515 switch(current.get(Calendar.MONTH)) {
516 case Calendar.JANUARY: month_name = "Dates.Jan"; break;
517 case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
518 case Calendar.MARCH: month_name = "Dates.Mar"; break;
519 case Calendar.APRIL: month_name = "Dates.Apr"; break;
520 case Calendar.MAY: month_name = "Dates.May"; break;
521 case Calendar.JUNE: month_name = "Dates.Jun"; break;
522 case Calendar.JULY: month_name = "Dates.Jul"; break;
523 case Calendar.AUGUST: month_name = "Dates.Aug"; break;
524 case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
525 case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
526 case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
527 case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
528 default: month_name = "";
529 }
530 int day = current.get(Calendar.DAY_OF_MONTH);
531 int hour = current.get(Calendar.HOUR_OF_DAY);
532 int minute = current.get(Calendar.MINUTE);
533 int second = current.get(Calendar.SECOND);
534 int year = current.get(Calendar.YEAR);
535
536 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);
537 }
538
539 /** Retrieves and formats the depth field of the config file to four characters.
540 * @param length The length of the desired string as an <i>int</i>.
541 * @return A <strong>String</strong> representation of the mirroring depth padded to length characters.
542 */
543 /* private static String getDepthString(int length) {
544 return pad("" + Gatherer.self.config.getInt("mirroring.depth", false), length);
545 } */
546 /** Method which constructs the etc directory given a certain collection.
547 * @param col_dir The location of the collection directory as a <strong>String</strong>.
548 * @return The location of the given collections etc directory, also as a <strong>String</strong>.
549 */
550 public static String getEtcDir(String col_dir) {
551 return col_dir + ETC_DIR;
552 }
553 /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
554 * @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists.
555 */
556 static public ImageIcon getImage(String filename) {
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 return image;
568 }
569
570 /** Method which constructs the import directory given a certain collection.
571 * @param col_dir The location of the collection directory as a <strong>String</strong>.
572 * @return The location of the given collections import directory, also as a <strong>String</strong>.
573 */
574 public static String getImportDir(String col_dir) {
575 return col_dir + IMPORT_DIR;
576 }
577 /** Method which constructs the index directory given a certain collection.
578 * @param col_dir The location of the collection directory as a <strong>String</strong>.
579 * @return The location of the given collections index directory, also as a <strong>String</strong>.
580 */
581 static public String getIndexDir(String col_dir) {
582 return col_dir + INDEX_DIR;
583 }
584 /** Method which constructs the log directory given a certain collection.
585 * @param col_dir The location of the collection directory as a <strong>String</strong>.
586 * @return The location of the given collections log directory, also as a <strong>String</strong>.
587 */
588 public static String getLogDir(String col_dir) {
589 return col_dir + LOG_DIR;
590 }
591 /** Determine this machines name.
592 * @return The name as a <strong>String</strong>.
593 */
594 static public String getMachineName() {
595 try {
596 return InetAddress.getLocalHost().getHostName();
597 }
598 catch(UnknownHostException ex) {
599 }
600 return "Unknown Machine";
601 }
602 /** Method which constructs the metadata directory given a certain collection.
603 * @param col_dir The location of the collection directory as a <strong>String</strong>.
604 * @return The location of the given collections metadata directory, also as a <strong>String</strong>.
605 */
606 static public String getMetadataDir(String col_dir) {
607 return col_dir + META_DIR;
608 }
609
610
611 /* static private File getRecycleDirectory() {
612 return new File(RECYCLE);
613 } */
614
615 /** Determine whether a character is a hexidecimal one.
616 * @param chr The <i>char</i> in question.
617 * @return An <i>int</i> representing the value of the hexidecimal character or -1 if not a hexidecimal.
618 */
619 static private int hexidecimal(char chr) {
620 switch(chr) {
621 case '0':
622 return 0;
623 case '1':
624 return 1;
625 case '2':
626 return 2;
627 case '3':
628 return 3;
629 case '4':
630 return 4;
631 case '5':
632 return 5;
633 case '6':
634 return 6;
635 case '7':
636 return 7;
637 case '8':
638 return 8;
639 case '9':
640 return 9;
641 case 'A':
642 return 10;
643 case 'B':
644 return 11;
645 case 'C':
646 return 12;
647 case 'D':
648 return 13;
649 case 'E':
650 return 14;
651 case 'F':
652 return 15;
653 default:
654 return -1;
655 }
656 }
657
658 /** A string is a valid hierarchy index if it matches '[0-9](\.[0-9])*' */
659 static public boolean isIndex(String raw) {
660 boolean result = true;
661 for(int i = 0; result && i < raw.length(); i++) {
662 char c = raw.charAt(i);
663 if(Character.isDigit(c) || (c == '.' && (i != 0 || i != raw.length() - 1))) {
664 // Valid index
665 }
666 else {
667 result = false;
668 }
669 }
670 return result;
671 }
672
673 /** Determines if the given file is a descendant of the folder described.
674 * @param pos_parent the possible parent folder as a String
675 * @param child the child File which we are checking
676 * @return true if the child is a descendant of the pos_parent, false otherwise
677 */
678 static public boolean isParentFolderOf(String pos_parent, File child) {
679 while(child != null) {
680 if(pos_parent.equals(child.getAbsolutePath())) {
681 return true;
682 }
683 // 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.
684 child = child.getParentFile();
685 }
686 return false;
687 }
688
689 /** Method to determine if the host system is Microsoft Windows based.
690 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
691 */
692 public static boolean isWindows() {
693 Properties props = System.getProperties();
694 String os_name = props.getProperty("os.name","");
695 if(os_name.startsWith("Windows")) {
696 return true;
697 }
698 return false;
699 }
700 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
701 * @param str The target <strong>String</strong> that needs to be padded.
702 * @param length The desired length of the string as an <i>int</i>.
703 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
704 */
705 public static String pad(String str, int length) {
706 return pad(str, length, ' ', true);
707 }
708 public static String pad(String str_raw, int length, char fill, boolean end) {
709 StringBuffer str = new StringBuffer(str_raw);
710 while(str.length() < length) {
711 if(end) {
712 str.insert(0, fill);
713 }
714 else {
715 str.append(fill);
716 }
717 }
718 return str.toString();
719 }
720
721 /** 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. */
722 static public Document parse(String filename, boolean use_classloader) {
723 File file = null;
724 if(use_classloader) {
725 try {
726 URL url = ClassLoader.getSystemResource(filename);
727 file = new File(URLDecoder.decode(url.getFile(), ENCODING));
728 url = null;
729 }
730 catch (Exception error) {
731 // Most likely file name.
732 file = new File("classes" + File.separator + filename);
733 //Gatherer.printStackTrace(error);
734 }
735 }
736 if(file == null) {
737 file = new File(filename);
738 }
739 return parse(file, true);
740 }
741 /** Parse in a xml document from a given file. */
742 static public Document parse(File file) {
743 return parse(file, true);
744 }
745 /** Parse in a xml document from a given file. */
746 static public Document parse(File file, boolean noisey) {
747 Document document = null;
748 try {
749 FileInputStream fis = new FileInputStream(file);
750 InputStreamReader isr = new InputStreamReader(fis, ENCODING);
751 Reader r = new BufferedReader(isr);
752 InputSource isc = new InputSource(r);
753 DOMParser parser = new DOMParser();
754 parser.setFeature("http://xml.org/sax/features/validation", false);
755 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
756 // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
757 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
758 parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
759 parser.parse(isc);
760 document = parser.getDocument();
761 isr.close();
762 fis.close();
763 parser = null;
764 isc = null;
765 r = null;
766 isr = null;
767 fis = null;
768 file = null;
769 }
770 catch (Exception error) {
771 if(noisey) {
772 error.printStackTrace();
773 Gatherer.printStackTrace(error);
774 }
775 }
776 return document;
777 }
778
779 /** 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.
780 * @param original The <strong>String</strong> to justify.
781 * @param width The desired width as an <i>int</i>.
782 * @param current_width An <i>int</i> representing the current width of the string, which takes into account special characters.
783 * @return The newly justified <strong>String</strong>.
784 */
785 static public String space(String original, int width, int current_width) {
786 // Strip trailing whitespace.
787 while(original.charAt(original.length() - 1) == ' ') {
788 original = original.substring(0, original.length() - 2);
789 }
790 int diff = width - current_width;
791 // Now add diff spaces, one at each existing space.
792 int pos = 0;
793 while(diff > 0) {
794 if(pos == original.length()) {
795 pos = 0;
796 }
797 if(original.charAt(pos) == ' ') {
798 // Insert a space.
799 String prefix = original.substring(0, pos);
800 String suffix = original.substring(pos);
801 original = prefix + " " + suffix;
802 pos = pos + 2;
803 diff--;
804 }
805 pos++;
806 }
807 return original;
808 }
809 /** 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.
810 * @param raw The <strong>Strong</strong> containing the mangled text.
811 * @return A <strong>String</strong> with new lines and extra spaces removed.
812 */
813 static public String stripNL(String raw_str) {
814 byte raw[] = raw_str.getBytes();
815 byte formatted[] = new byte[raw.length];
816 byte previous = '\0';
817 int j = 0;
818 for(int i = 0; i < raw.length; i++) {
819 if(raw[i] == '\n') {
820 // Skip new lines.
821 }
822 else if(raw[i] == '\t') {
823 // Skip tabs.
824 }
825 else if(raw[i] == ' ' && raw[i] == previous) {
826 // Skip erroneous whitespace.
827 }
828 else {
829 formatted[j] = raw[i];
830 j++;
831 }
832 previous = raw[i];
833 }
834 byte finish[] = new byte[j];
835 System.arraycopy(formatted, 0, finish, 0, j);
836 return new String(finish);
837 }
838 /** Trims the string text to the length specified removing end characters and adding if necessary.
839 * @param text A <strong>String</strong> which you wish to ensure is shorter than length.
840 * @param length An <i>int</i> specifying the strings maximum length after which its trimmed.
841 * @return The trimmed <strong>String</strong>.
842 */
843 public static String trim(String text, int length) {
844 if(text.length() > length) {
845 text = text.substring(0, length);
846 text = text + "...";
847 }
848 return text;
849 }
850
851 static public String trimCenter(String text, int length) {
852 if(text.length() > length) {
853 int half = (length - 3) / 2;
854 StringBuffer temp = new StringBuffer(text.substring(0, half));
855 temp.append("...");
856 temp.append(text.substring(text.length() - half));
857 text = temp.toString();
858 }
859 return text;
860 }
861 /** 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).
862 * @param roots A <strong>File[]</strong> containing all of the file system roots registered on this system.
863 * @return A filtered <strong>File[]</strong> containing only those drives that are accessible and/or are floppy-disk media drives.
864 */
865 /* private static File[] validateDrives(File roots[]) {
866 Vector valid = new Vector();
867 for(int i = 0; i < roots.length; i++) {
868 String name = roots[i].getAbsolutePath();
869 name = name.toLowerCase();
870 if(!name.startsWith("a:") && !name.startsWith("b:")) {
871 valid.add(roots[i]);
872 }
873 }
874 roots = new File[valid.size()];
875 for(int i = 0; i < roots.length; i++) {
876 roots[i] = (File)valid.get(i);
877 }
878 return roots;
879 } */
880}
Note: See TracBrowser for help on using the repository browser.