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

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

added new directory definition for import.bak - a backup dir to put old metadata.xml files

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