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

Last change on this file since 5241 was 5241, checked in by jmt12, 21 years ago

Added new static string to utility - build cfg file. Part of solving 203B142.

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