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

Last change on this file since 8622 was 8622, checked in by mdewsnip, 19 years ago

Tidied up some more local library stuff.

  • Property svn:keywords set to Author Date Id Revision
File size: 46.8 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 java.util.zip.*;
44import javax.swing.*;
45import javax.swing.tree.*;
46import org.apache.xerces.parsers.*;
47import org.apache.xml.serialize.*;
48import org.greenstone.gatherer.Configuration;
49import org.greenstone.gatherer.DebugStream;
50import org.greenstone.gatherer.Dictionary;
51// Don't even think about adding import org.greenstone.gatherer.Gatherer in here!
52// The functions in this class should be independent of the Gatherer class. Put your function somewhere else buster!
53import org.w3c.dom.*;
54import org.xml.sax.*;
55
56/** To provide a library of common methods, in a static context, for use in the Gatherer.
57 * @author John Thompson, Greenstone Digital Library, University of Waikato
58 * @version 2.3b
59 */
60public class Utility {
61 static final public Dimension BUTTON_SIZE = new Dimension(160, 35);
62 static final public Dimension DOUBLE_IMAGE_BUTTON_SIZE = new Dimension(190, 35);
63 static final public Dimension LABEL_SIZE = new Dimension(150, 25);
64 /** The default size of a gatherer progress bar, in either the download view or the build view. */
65 static final public Dimension PROGRESS_BAR_SIZE = new Dimension(580,65);
66 /** The number of kilobytes to use as a io buffer. */
67 static final public int FACTOR = 1;
68 /** The size of the io buffer, calculated as FACTOR * 1024. */
69 static final public int BUFFER_SIZE = FACTOR * 1024;
70 /** Definition of an important directory name, in this case the archives directory of the collection. */
71 static final public String ARCHIVES_DIR = "archives" + File.separator;
72 /** Definition of an important directory name, in this case the base dir, or the working directory of the Gatherer. */
73 static public String BASE_DIR = System.getProperty("user.dir") + File.separator;
74 static final public String BUILD_CFG_FILENAME = "build.cfg";
75 /** Definition of an important directory name, in this case the building directory for the collection. */
76 static final public String BUILD_DIR = "building" + File.separator;
77 /** Definition of an important directory name, in this case the public web cache for the Gatherer. */
78 static public String CACHE_DIR = BASE_DIR + "cache" + File.separator;
79 static final public String CFG_COLLECTIONMETA_COLLECTIONNAME = "collectionmeta\tcollectionname";
80 static final public String CFG_COLLECTIONMETA_COLLECTIONEXTRA = "collectionmeta\tcollectionextra";
81 static final public String CFG_COLLECTIONMETA_ICONCOLLECTION = "collectionmeta\ticoncollection";
82 static final public String CFG_CLASSIFY = "classify";
83 static final public String CFG_CLASSIFY_BUTTONNAME = "-buttonname";
84 static final public String CFG_CLASSIFY_HFILE = "-hfile";
85 static final public String CFG_CLASSIFY_METADATA = "-metadata";
86 static final public String CFG_CLASSIFY_SORT = "-sort";
87 static final public String CFG_CREATOR = "creator";
88 static final public String CFG_FORMAT = "format";
89 static final public String CFG_MAINTAINER = "maintainer";
90 /** Definition of an important directory name, in this case the parent directory of all the collections in the gsdl. */
91 static final public String COL_DIR = "collect" + File.separator;
92 static final public String COLLECTION_TREE = "Collection";
93 /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
94 static final public String CONFIG_FILE = "etc" + File.separator + "collect.cfg";
95 /** The default file name for the urls missing any file. */
96 static final public String DEFAULT_FILE = "index.html";
97 /** The default protocol header for those urls missing any protocol. */
98 static final public String DEFAULT_PROTOCOL = "http://";
99 /** The default dictionary to load. */
100 static final public String DICTIONARY = "dictionary";
101 static final public String ENCODING = "UTF-8";
102 static final public String ENGLISH_VALUE = "en";
103 /** Definition of an important directory name, in this case the etc (or extra information) directory for the collection. */
104 static final public String ETC_DIR = "etc" + File.separator;
105 static final public String EXTRACTED_METADATA_NAMESPACE = "ex";
106 /** The location of the default greenstone metadata file. */
107 static final public String GREENSTONEDIRECTORYMETADATA_TEMPLATE = "xml/metadata.xml";
108 /** Definition of an important directory name, in this case the private web cache directory for the collection. */
109 static final public String GCACHE_DIR = "cache" + File.separator;
110 static final public String GLI_ARCHIVE = "GLI.jar";
111 static final public String GLI_EXTENSION = ".col";
112 /** Definition of an important directory name, in this case the location of help documentation. */
113 static public String HELP_DIR = BASE_DIR + "help" + File.separator;
114 /** Definition of an important directory name, in this case the images directory for the collection. */
115 static final public String IMAGES_DIR = "images" + File.separator;
116 /** Definition of an important directory name, in this case the import directory for the collection. */
117 static final public String IMPORT_DIR = "import" + File.separator;
118 /** Definition of an important directory name, in this case the backup import directory for the collection. */
119 static final public String IMPORT_BAK_DIR = "import.bak" + File.separator;
120 /** Definition of an important directory name, in this case the index directory for the collection. */
121 static final public String INDEX_DIR = "index" + File.separator;
122 static final public String LANGUAGE_ATTRIBUTE = "language";
123 /** Definition of an important directory name, in this case the log directory for the collection. */
124 static final public String LOG_DIR = "log" + File.separator;
125 /** Definition of an important directory name, in this case the macros directory for the collection. */
126 static final public String MACROS_DIR = "macros" + File.separator;
127 /** Definition of an important directory name, in this case the location of the expected collection metadata sets.. */
128 static final public String META_DIR = "metadata" + File.separator; // Col. Copy
129 /** Definition of an important directory name, in this case the location of the default metadata sets. */
130 static public String METADATA_DIR = BASE_DIR + "metadata" + File.separator;
131 /** Definition of an important zip file, in this case zipped up version of metadata file stored in JAR file */
132 static final public String METADATA_ZIP = "metadata.zip";
133 /** The location the gatherer expects to find metadata set information. */
134 static final public String METADATA_SET_TEMPLATE = "xml/template.mds";
135 static final public String METADATA_VALUE_TEMPLATE = "xml/template.mdv";
136 static final public String METADATA_XML = "metadata.xml";
137 static final public String NAME_ELEMENT = "Name";
138 /** The default name of the perl executable under unix. */
139 static final public String PERL_EXECUTABLE_UNIX = "perl";
140 /** The default name of the perl executable under windows. */
141 static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
142 /** The name of the Gatherer. */
143 static final public String PROGRAM_NAME = "Greenstone Librarian Interface";
144 /** The current version of the Gatherer. */
145 static final public String PROGRAM_VERSION = "v2.52";
146 /** Definition of an important directory name, in this case the location of the recycled files location. */
147 static public String RECYCLE = BASE_DIR + "recycle" + File.separator;
148 /** Definition of an important directory name, in this case the location of image and other resources. */
149 static public String RES_DIR = BASE_DIR + "resource" + File.separator;
150 /** Definition of an important directory name, in this case the location of opening (or welcome) screen html. */
151 static 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
155 // These are out of alphabetic order to avoid forward reference error.
156 /** The default icon to produce a 'help-icon' sized blank space before a menu entry. */
157 static public Class base = null;
158 static public ImageIcon BLANK_ICON = null;
159 /** The default error icon image. */
160 static public ImageIcon ERROR_ICON = null;
161 static public ImageIcon HELP_ICON = null;
162 /** The image for a toggle button whose state is 'on'. */
163 static public ImageIcon ON_ICON = null;
164 /** The image for a toggle button whose state is 'off'. */
165 static public ImageIcon OFF_ICON = null;
166
167 /** Decodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces "\n" with a newline.
168 * @param raw The <strong>String</strong> before decoding, read from the configuration file..
169 * @return A <strong>String</strong> ready to be placed in a component.
170 */
171 static public String decodeGreenstone(String raw) {
172 raw = raw.replaceAll("&apos;", "\'");
173 raw = raw.replaceAll("&gt;", ">");
174 raw = raw.replaceAll("&lt;", "<");
175 raw = raw.replaceAll("&quot;", "\"");
176 raw = raw.replaceAll("&#39;", "\'");
177 raw = raw.replaceAll("\\\\n", "\n");
178 return raw;
179 }
180
181
182 /** 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.
183 * @param file The <strong>File</strong> you want to delete.
184 * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
185 */
186 static public boolean delete(File file) {
187 boolean result = true;
188 // If file is a directory, delete the file's children.
189 if(file.isDirectory()) {
190 File files[] = file.listFiles();
191 for(int i = 0; files != null && result && i < files.length; i++) {
192 result = delete(files[i]);
193 }
194 }
195 if(result) {
196 // Delete file.
197 return file.delete();
198 }
199 return result;
200 }
201
202 static public boolean delete(String filename) {
203 return delete(new File(filename));
204 }
205
206
207 /** 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. */
208 static public boolean export(Document document, String filename) {
209 return export(document, new File(filename));
210 }
211
212 static public boolean export(Document document, File file) {
213 try {
214 OutputStream os = new FileOutputStream(file);
215 // Create an output format for our document.
216 OutputFormat f = new OutputFormat(document);
217 f.setEncoding(ENCODING);
218 f.setIndenting(true);
219 f.setLineWidth(0); // Why isn't this working!
220 f.setPreserveSpace(false);
221 // Create the necessary writer stream for serialization.
222 OutputStreamWriter osw = new OutputStreamWriter(os, ENCODING);
223 Writer w = new BufferedWriter(osw);
224 // Generate a new serializer from the above.
225 XMLSerializer s = new XMLSerializer(w, f);
226 s.asDOMSerializer();
227 // Finally serialize the document to file.
228 s.serialize(document);
229 // And close.
230 os.close();
231 return true;
232 }
233 // A file not found exception is most likely thrown because the directory the metadata.xml file is attempting to be written to no longer has any files in it. I'll add a test in MetadataXMLFile to test for this, but if it still happens ignore it (a non-existant directory can't really have metadata added to it any way.
234 catch (Exception exception) {
235 if(!file.getName().endsWith(METADATA_XML)) {
236 DebugStream.printStackTrace(exception);
237 return false;
238 }
239 return true;
240 }
241 }
242
243 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
244 static final public String BYTE_SUFFIX = " b";
245 static final public long GIGABYTE = 1024000000l;
246 static final public String GIGABYTE_SUFFIX = " Gb";
247 static final public long KILOBYTE = 1024l;
248 static final public String KILOBYTE_SUFFIX = " kb";
249 static final public long MEGABYTE = 1024000l;
250 static final public String MEGABYTE_SUFFIX = " mb";
251 static final public String formatFileLength(long length) {
252 StringBuffer result = new StringBuffer("");
253 float number = 0f;
254 String suffix = null;
255 // Determine the floating point number and the suffix (radix) used.
256 if(length >= GIGABYTE) {
257 number = (float) length / (float) GIGABYTE;
258 suffix = GIGABYTE_SUFFIX;
259 }
260 else if(length >= MEGABYTE) {
261 number = (float) length / (float) MEGABYTE;
262 suffix = MEGABYTE_SUFFIX;
263 }
264 else if(length >= KILOBYTE) {
265 number = (float) length / (float) KILOBYTE;
266 suffix = KILOBYTE_SUFFIX;
267 }
268 else {
269 // Don't need to do anything fancy if the file is smaller than a kilobyte
270 return length + BYTE_SUFFIX;
271 }
272 // 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.
273 String number_str = Float.toString(number);
274 char number_char[] = number_str.toCharArray();
275 int pos = 0;
276 // Print the characters up to the '.'
277 while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
278 result.append(number_char[pos]);
279 pos++;
280 }
281 if(pos < number_char.length) {
282 // Print the '.' and at most two characters after it
283 result.append(number_char[pos]);
284 pos++;
285 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
286 result.append(number_char[pos]);
287 }
288 // Search through the remaining string for 'E'
289 while(pos < number_char.length && number_char[pos] != 'E') {
290 pos++;
291 }
292 // If we still have string then we found an E. Copy the remaining string.
293 while(pos < number_char.length) {
294 result.append(number_char[pos]);
295 pos++;
296 }
297 }
298 // Add suffix
299 result.append(suffix);
300 // Done
301 return result.toString();
302 }
303
304 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
305 * @param text The <strong>String</strong> requiring formatting.
306 * @param width The maximum width per line as an <i>int</i>.
307 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
308 * 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.
309 */
310 static public String formatHTMLWidth(String text, int width) {
311 if(text == null) {
312 return "Error";
313 }
314 HTMLStringTokenizer html = new HTMLStringTokenizer(text);
315 int current_width = 0;
316 int threshold = width / 2;
317 Stack lines = new Stack();
318 String line = "";
319 while(html.hasMoreTokens()) {
320 String token = html.nextToken();
321 while(token != null) {
322 if(html.isTag()) {
323 // Insert smart HTML tag code here.
324 token = null;
325 }
326 else {
327 // If the token is bigger than two thirds width, before we've even started break it down.
328 if(current_width + 1 + token.length() > width && token.length() > threshold) {
329 if(width == current_width) {
330 lines.push(line);
331 line = token;
332 current_width = token.length();
333 }
334 else {
335 String prefix = token.substring(0, width - 1 - current_width);
336 token = token.substring(prefix.length());
337 if(current_width == 0) {
338 line = line + prefix;
339 }
340 else {
341 line = line + " " + prefix;
342 }
343 lines.push(line);
344 line = "";
345 current_width = 0;
346 }
347 }
348 // If adding the next token would push us over the maximum line width.
349 else if(current_width + 1 + token.length() > width) {
350 line = space(line, width, current_width);
351 lines.push(line);
352 line = token;
353 current_width = token.length();
354 token = null;
355 }
356 // Otherwise we should be able to just add the token, give or take.
357 else {
358 if(current_width == 0) {
359 line = line + token;
360 current_width = token.length();
361 }
362 else {
363 // Special case for standard punctuation which may exist after a tag like so:
364 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation.
365 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
366 line = line + token;
367 current_width = current_width + 1;
368 }
369 else {
370 line = line + " " + token;
371 current_width = current_width + 1 + token.length();
372 }
373 }
374 token = null;
375 }
376 }
377 }
378 }
379 String result = line;
380 while(!lines.empty()) {
381 result = (String)lines.pop() + "<BR>" + result;
382 }
383 // Replace ' ' with "&nbsp;"
384 boolean tag = false;
385 int pos = 0;
386 while(pos < result.length()) {
387 if(result.charAt(pos) == '<') {
388 tag = true;
389 }
390 else if(result.charAt(pos) == '>') {
391 tag = false;
392 }
393 else if(result.charAt(pos) == ' ' && !tag) {
394 String prefix = result.substring(0, pos);
395 String suffix = result.substring(pos + 1);
396 result = prefix + "&nbsp;" + suffix;
397 }
398 pos++;
399 }
400 result = "<HTML>" + result + "</HTML>";
401 return result;
402 }
403 /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
404 * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
405 * @param raw The raw filename path <strong>String</strong>.
406 * @param width The maximum width as an <i>int</i>.
407 * @return A path <strong>String</strong> no longer than width.
408 */
409 static public String formatPath(String key, String raw, int width) {
410 JLabel label = new JLabel(Dictionary.get(key, raw));
411 int position = -1;
412 while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
413 raw = "..." + raw.substring(position + 1);
414 label.setText(Dictionary.get(key, raw));
415 }
416 if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
417 raw = raw.substring(3);
418 }
419 return raw;
420 }
421
422 /** Method which constructs the archive directory given a certain collection.
423 * @param gsdl_path The location of the collection directory as a <strong>String</strong>.
424 * @return The location of the given collections archive directory, also as a <strong>String</strong>.
425 */
426 static public String getArchiveDir(String col_dir) {
427 return col_dir + ARCHIVES_DIR;
428 }
429
430 /** Method which constructs the build directory given a certain collection.
431 * @param col_dir The location of the collection directory as a <strong>String</strong>.
432 * @return The location of the given collections build directory, also as a <strong>String</strong>.
433 */
434 static public String getBuildDir(String col_dir) {
435 if(col_dir == null) {
436 return BASE_DIR + BUILD_DIR;
437 }
438 return col_dir + BUILD_DIR;
439 }
440 /** Builds the cache dir by appending the user path and 'cache'.
441 * @return a File representing the path to the private file cache within the current collection.
442 */
443 public static File getCacheDir() {
444 return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
445 }
446 /** Method which constructs the collect directory for Greenstone.
447 * @param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>.
448 * @return The location of the collection directory, also as a <strong>String</strong>.
449 */
450 public static String getCollectDir(String gsdl_path) {
451 return gsdl_path + COL_DIR;
452 }
453 static public String getCollectDir(String gsdl3_path, String site_name) {
454 return getSitesDir(gsdl3_path) + site_name + File.separator + COL_DIR;
455 }
456
457
458 /** Method which constructs a collection's top level directory
459 * @@param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>.
460 * @param The name of the collection as a <strong>String</strong>.
461 * @return The location of the collection's base directory
462 */
463 public static String getCollectionDir(String gsdl_path, String coll_name) {
464 return getCollectDir(gsdl_path) + coll_name + File.separator;
465 }
466 public static String getCollectionDir(String gsdl3_path, String site_name, String coll_name) {
467 return getCollectDir(gsdl3_path, site_name) + coll_name + File.separator;
468 }
469
470
471 /** Method which constructs the configuration file given a certain collection.
472 * @param col_dir The location of the collection directory as a <strong>String</strong>.
473 * @return The location of the given collections configuration file, also as a <strong>String</strong>.
474 */
475 static public String getConfigFile(String col_dir) {
476 return col_dir + CONFIG_FILE;
477 }
478
479 static public String getDateString() {
480 Calendar current = Calendar.getInstance();
481 String day_name = null;
482 switch(current.get(Calendar.DAY_OF_WEEK)) {
483 case Calendar.MONDAY: day_name = "Dates.Mon"; break;
484 case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
485 case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
486 case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
487 case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
488 case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
489 case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
490 default: day_name = "";
491 }
492 String month_name = null;
493 switch(current.get(Calendar.MONTH)) {
494 case Calendar.JANUARY: month_name = "Dates.Jan"; break;
495 case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
496 case Calendar.MARCH: month_name = "Dates.Mar"; break;
497 case Calendar.APRIL: month_name = "Dates.Apr"; break;
498 case Calendar.MAY: month_name = "Dates.May"; break;
499 case Calendar.JUNE: month_name = "Dates.Jun"; break;
500 case Calendar.JULY: month_name = "Dates.Jul"; break;
501 case Calendar.AUGUST: month_name = "Dates.Aug"; break;
502 case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
503 case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
504 case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
505 case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
506 default: month_name = "";
507 }
508 int day = current.get(Calendar.DAY_OF_MONTH);
509 int hour = current.get(Calendar.HOUR_OF_DAY);
510 int minute = current.get(Calendar.MINUTE);
511 int second = current.get(Calendar.SECOND);
512 int year = current.get(Calendar.YEAR);
513
514 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);
515 }
516
517
518 /** Method which constructs the etc directory given a certain collection.
519 * @param col_dir The location of the collection directory as a <strong>String</strong>.
520 * @return The location of the given collections etc directory, also as a <strong>String</strong>.
521 */
522 public static String getEtcDir(String col_dir) {
523 return col_dir + ETC_DIR;
524 }
525
526 static final private String APPLICATION_DATA_FOLDER = "Application Data";
527 static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
528 static final private String USER_HOME_PROPERTY = "user.home";
529 static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
530
531 static public File getGLIUserFolder() {
532 File gli_user_folder = null;
533 if(Utility.isWindows()) {
534 gli_user_folder = new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
535 }
536 else {
537 gli_user_folder = new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
538 }
539 return gli_user_folder;
540 }
541
542 /** Retrieve the full file path to the help index xml file.
543 * @return the full path as a String
544 */
545 static public String getHelpFolder() {
546
547 String help_folder = "help/" + Configuration.getLanguage() + "/";
548
549 // Try in the JAR/classes directory first
550 URL help_folder_url = base.getResource("/" + help_folder);
551 if (help_folder_url != null) {
552 return help_folder;
553 }
554
555 // Look in the base directory next
556 File help_folder_file = new File(help_folder);
557 if (help_folder_file.exists()) {
558 return help_folder;
559 }
560
561 // Resort to English
562 return "help/" + StaticStrings.ENGLISH_LANGUAGE_STR + "/";
563 }
564
565 /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
566 * @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists.
567 */
568 static public ImageIcon getImage(String filename) {
569 return getImage(filename, false);
570 }
571
572 static public ImageIcon getImage(String filename, boolean wait_until_complete) {
573 ImageIcon image = null;
574 try {
575 image = new ImageIcon(base.getResource("/images/" + Dictionary.get("Version") + "/" + filename));
576 }
577 catch(NullPointerException exception) {
578 image = new ImageIcon(base.getResource("/images/" + filename));
579 }
580 if(image == null) {
581 image = ERROR_ICON;
582 }
583
584 if(wait_until_complete) {
585 int load_status;
586 do {
587 load_status = image.getImageLoadStatus();
588 }
589 while(load_status != MediaTracker.ABORTED && load_status != MediaTracker.ERRORED && load_status != MediaTracker.COMPLETE);
590 }
591 return image;
592 }
593
594 /** Method which constructs the images directory given a certain collection.
595 * @param col_dir The location of the collection directory as a <strong>String</strong>.
596 * @return The location of the given collections images directory, also as a <strong>String</strong>.
597 */
598 public static String getImagesDir(String col_dir) {
599 return col_dir + IMAGES_DIR;
600 }
601 /** Method which constructs the import directory given a certain collection.
602 * @param col_dir The location of the collection directory as a <strong>String</strong>.
603 * @return The location of the given collections import directory, also as a <strong>String</strong>.
604 */
605 public static String getImportDir(String col_dir) {
606 return col_dir + IMPORT_DIR;
607 }
608 /** Method which constructs the index directory 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 index directory, also as a <strong>String</strong>.
611 */
612 static public String getIndexDir(String col_dir) {
613 return col_dir + INDEX_DIR;
614 }
615 /** Method which constructs the log directory given a certain collection.
616 * @param col_dir The location of the collection directory as a <strong>String</strong>.
617 * @return The location of the given collections log directory, also as a <strong>String</strong>.
618 */
619 public static String getLogDir(String col_dir) {
620 if(col_dir != null) {
621 return col_dir + LOG_DIR;
622 }
623 else {
624 return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
625 }
626 }
627 /** Determine this machines name.
628 * @return The name as a <strong>String</strong>.
629 */
630 static public String getMachineName() {
631 try {
632 return InetAddress.getLocalHost().getHostName();
633 }
634 catch(UnknownHostException ex) {
635 }
636 return "Unknown Machine";
637 }
638 /** Method which constructs the metadata directory given a certain collection.
639 * @param col_dir The location of the collection directory as a <strong>String</strong>.
640 * @return The location of the given collections metadata directory, also as a <strong>String</strong>.
641 */
642 static public String getMetadataDir(String col_dir) {
643 return col_dir + META_DIR;
644 }
645
646 static public void extractFromJar(String filename, String dst_dir, boolean must_be_present)
647 {
648 try {
649 // setup input stream for slurping out file
650 InputStream fis = base.getResourceAsStream("/"+filename);
651 BufferedInputStream fbis = new BufferedInputStream(fis);
652 DataInputStream fdbis = new DataInputStream(fbis);
653
654 // set up output stream for writing to disk
655 String ofname = dst_dir + filename;
656 FileOutputStream fos = new FileOutputStream(ofname);
657 BufferedOutputStream bfos = new BufferedOutputStream(fos);
658
659 byte[] buf = new byte[1024];
660 int len;
661 int total_bytes = 0;
662 while ((len = fdbis.read(buf)) >= 0) {
663 bfos.write(buf,0,len);
664 total_bytes += len;
665 }
666
667 fdbis.close();
668 bfos.close();
669 }
670 catch (Exception error) {
671 if (must_be_present) {
672 error.printStackTrace();
673 }
674 }
675 }
676
677
678 /** Method which unzips a given metadata resoure
679 * @param zip_fname The name of the zip file as a <strong>String</strong>.
680 * @param dst_dir The destination directory for unzipping, also as a <strong>String</strong>.
681 * @param use_classloader Used to determine whether or not to look in resource bundle.
682 */
683 static public void unzipFromJar(String jar_zip_fname, String dst_dir) {
684
685 File file = null;
686 if (!dst_dir.endsWith(File.separator)) {
687 dst_dir += File.separator;
688 }
689
690 extractFromJar(jar_zip_fname,dst_dir,true);
691
692 String zip_ofname = dst_dir + jar_zip_fname;
693 File zip_file = new File(zip_ofname);
694
695 try {
696 ZipFile zipfile = new ZipFile(zip_file);
697
698 Enumeration e = zipfile.entries();
699 while (e.hasMoreElements()) {
700 ZipEntry zipentry = (ZipEntry) e.nextElement();
701 String zentryname = dst_dir + zipentry.getName();
702 DebugStream.println(" Unzipping: " + zentryname);
703
704 if (zipentry.isDirectory()) {
705 // Create named directory
706 boolean success = (new File(zentryname)).mkdir();
707 if (!success) {
708 System.err.println("Error: unable to create directory '"+zentryname+"'");
709 }
710 }
711 else {
712 // Write out file to disk
713
714 // set up input stream
715 InputStream zis = zipfile.getInputStream(zipentry);
716 BufferedInputStream bzis = new BufferedInputStream(zis);
717 DataInputStream dbzis = new DataInputStream(bzis);
718
719 // set up output stream
720 FileOutputStream fzos = new FileOutputStream(zentryname);
721 BufferedOutputStream bfzos = new BufferedOutputStream(fzos);
722
723 byte[] buf = new byte[1024];
724 int len;
725 while ((len = dbzis.read(buf)) >= 0) {
726 bfzos.write(buf,0,len);
727 }
728
729 dbzis.close();
730 bfzos.close();
731 }
732 }
733 }
734 catch (ZipException error) {
735 System.err.println("Error: Unable to open '"+zip_file.getAbsolutePath()+"'");
736 DebugStream.printStackTrace(error);
737 }
738
739 catch (Exception error) {
740 error.printStackTrace();
741 }
742 }
743
744
745 static protected String unixStylePath(String path)
746 {
747 String unix_path = path.replace('\\','/');
748 return unix_path;
749 }
750
751 static protected void zipFunc (ZipOutputStream zos, String file_path, int prefix_strip)
752 {
753 // Using try is required because of file io.
754 try {
755 // Create a Zip Entry and put it into the archive (no data yet).
756
757 // Strip off col_dir prefix
758 String zip_path = file_path.substring(prefix_strip);
759 // Zip files use '/' for directory separator
760 String unix_style_path = unixStylePath(zip_path);
761 ZipEntry fileEntry = new ZipEntry(unix_style_path);
762 zos.putNextEntry(fileEntry);
763
764 // Create a file input stream and a buffered input stream.
765 FileInputStream fis = new FileInputStream(file_path);
766 BufferedInputStream bis = new BufferedInputStream(fis);
767
768 // Create a byte array object named data and declare byte count variable.
769 byte[] data = new byte[1024];
770 int byteCount;
771 // Create a loop that reads from the buffered input stream and writes
772 // to the zip output stream until the bis has been entirely read.
773 while ((byteCount = bis.read(data, 0, 1024)) > -1) {
774 zos.write(data, 0, byteCount);
775 }
776 }
777 catch (IOException e) {
778 e.printStackTrace();
779 }
780
781 DebugStream.println(" Zipping up: " + file_path);
782 }
783
784 static protected void dirFunc (ZipOutputStream zos, String dir_name, int prefix_strip)
785 {
786 File dirObj = new File(dir_name);
787
788 if (dirObj.exists() == true) {
789 if (dirObj.isDirectory() == true) {
790 // Create an array of File objects, one for each file or directory in dirObj.
791 File [] fileList = dirObj.listFiles();
792
793 // Loop through File array and display.
794 for (int i = 0; i < fileList.length; i++) {
795 File file = fileList[i];
796 if (file.isDirectory()) {
797 String dir_path = file.getPath();
798 String zip_path = dir_path.substring(prefix_strip);
799 // Zip files use '/' for directory separator
800 String unix_style_path
801 = unixStylePath(zip_path+File.separator);
802 ZipEntry dirEntry = new ZipEntry(unix_style_path);
803
804 try {
805 zos.putNextEntry(dirEntry);
806 }
807 catch (IOException e) {
808 e.printStackTrace();
809 }
810
811 dirFunc(zos,dir_path,prefix_strip);
812 } else if (file.isFile()) {
813 // Call the zipFunc function
814 String file_path = fileList[i].getPath();
815 zipFunc(zos,file_path,prefix_strip);
816 }
817 }
818 }
819 else {
820 System.err.println (dir_name+" is not a directory.");
821 }
822 }
823 else {
824 System.err.println ("Directory "+dir_name+" does not exist.");
825 }
826 }
827
828
829
830 static public void zipup(String col_name, String dir_or_file)
831 {
832 String col_dir = Utility.getCollectDir(Configuration.gsdl_path);
833 int prefix_strip = col_dir.length();
834
835 String zip_fname = col_dir + col_name + ".zip";
836 String zip_dir_or_file = col_dir + col_name + File.separator + dir_or_file;
837 File zip_dof = new File(zip_dir_or_file);
838
839 try {
840 FileOutputStream fos = new FileOutputStream(zip_fname);
841 ZipOutputStream zos = new ZipOutputStream(fos);
842
843 if (zip_dof.exists()) {
844 if (zip_dof.isDirectory()) {
845 String zip_dir = zip_dir_or_file;
846 dirFunc(zos,zip_dir,prefix_strip);
847 }
848 else {
849 String zip_full_file = zip_dir_or_file;
850
851 String zip_path = zip_full_file.substring(prefix_strip);
852
853 for (int i=1; i<zip_path.length(); i++) {
854 String ch = String.valueOf(zip_path.charAt(i));
855
856 if (ch.equals(File.separator)) {
857 String dir_path = zip_path.substring(0,i);
858 // Zip files use '/' for directory separator
859 String unix_style_path
860 = unixStylePath(dir_path+File.separator);
861 ZipEntry dirEntry = new ZipEntry(unix_style_path);
862 zos.putNextEntry(dirEntry);
863 }
864 }
865 zipFunc(zos,zip_full_file,prefix_strip);
866 }
867 }
868 else {
869 System.err.println("Warning: " + zip_dir_or_file + " does not exist!");
870 }
871
872 // Close the file output streams for both the file and the zip.
873 zos.flush();
874 zos.close();
875 fos.close();
876 }
877 catch (IOException e) {
878 e.printStackTrace();
879 }
880 }
881
882
883 static public void unzip(String col_name)
884 {
885 String col_dir = Utility.getCollectDir(Configuration.gsdl_path);
886 String zip_fname = col_dir + col_name + ".zip";
887 int zip_mode = ZipFile.OPEN_READ | ZipFile.OPEN_DELETE;
888
889 try {
890 ZipFile zipfile = new ZipFile(new File(zip_fname), zip_mode);
891
892 Enumeration e = zipfile.entries();
893 while (e.hasMoreElements()) {
894 ZipEntry zipentry = (ZipEntry) e.nextElement();
895 String zentryname = col_dir + zipentry.getName();
896 DebugStream.println(" Unzipping: " + zentryname);
897
898 if (zipentry.isDirectory()) {
899 // Create named directory
900 boolean success = (new File(zentryname)).mkdir();
901 if (!success) {
902 System.err.println("Error: unable to create directory '"+zentryname+"'");
903 }
904 }
905 else {
906 // Write out file to disk
907
908 // set up input stream
909 InputStream zis = zipfile.getInputStream(zipentry);
910 BufferedInputStream bzis = new BufferedInputStream(zis);
911 DataInputStream dbzis = new DataInputStream(bzis);
912
913 // set up output stream
914 FileOutputStream fzos = new FileOutputStream(zentryname);
915 BufferedOutputStream bfzos = new BufferedOutputStream(fzos);
916
917 byte[] buf = new byte[1024];
918 int len;
919 while ((len = dbzis.read(buf)) >= 0) {
920 bfzos.write(buf,0,len);
921 }
922
923 dbzis.close();
924 bfzos.close();
925 }
926 }
927 }
928 catch (ZipException error) {
929 System.err.println("Error: Unable to open '"+zip_fname+"'");
930 DebugStream.printStackTrace(error);
931 }
932
933 catch (Exception error) {
934 error.printStackTrace();
935 }
936 }
937
938
939 static public String getSitesDir(String gsdl3_path) {
940 return gsdl3_path + File.separator + "web"
941 + File.separator + "sites" + File.separator;
942
943 }
944
945
946 /** returns the path to the greenstone version of wget */
947 static public String getWGetPath(String gsdl_path) {
948 if (isWindows()) {
949 return gsdl_path + "bin" + File.separator +
950 "windows" + File.separator +"wget.exe";
951 }
952 // is it the same for macs??
953 return gsdl_path + "bin" + File.separator +
954 "linux" + File.separator + "wget";
955 }
956
957
958 static public void initImages(Object base_object)
959 {
960 base = base_object.getClass();
961
962 BLANK_ICON = new ImageIcon(base.getResource("/images/blank.gif"));
963 ERROR_ICON = new ImageIcon(base.getResource("/images/error.gif"));
964 HELP_ICON = new ImageIcon(base.getResource("/images/help.gif"));
965 ON_ICON = new ImageIcon(base.getResource("/images/check.gif"));
966 OFF_ICON = new ImageIcon(base.getResource("/images/cross.gif"));
967 }
968
969
970 /** A string is a valid hierarchy index if it matches '[0-9](\.[0-9])*' */
971 static public boolean isIndex(String raw) {
972 boolean result = true;
973 for(int i = 0; result && i < raw.length(); i++) {
974 char c = raw.charAt(i);
975 if(Character.isDigit(c) || (c == '.' && (i != 0 || i != raw.length() - 1))) {
976 // Valid index
977 }
978 else {
979 result = false;
980 }
981 }
982 return result;
983 }
984
985 /** Method to determine if the host system is MacOS based.
986 * @return a boolean which is true if the platform is MacOS, false otherwise
987 */
988 public static boolean isMac() {
989 Properties props = System.getProperties();
990 String os_name = props.getProperty("os.name","");
991 if(os_name.startsWith("Mac OS")) {
992 return true;
993 }
994 return false;
995 }
996
997 /** Determines if the given file is a descendant of the folder described.
998 * @param pos_parent the possible parent folder as a String
999 * @param child the child File which we are checking
1000 * @return true if the child is a descendant of the pos_parent, false otherwise
1001 */
1002 static public boolean isParentFolderOf(String pos_parent, File child) {
1003 while(child != null) {
1004 if(pos_parent.equals(child.getAbsolutePath())) {
1005 return true;
1006 }
1007 // 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.
1008 child = child.getParentFile();
1009 }
1010 return false;
1011 }
1012
1013 /** Method to determine if the host system is Microsoft Windows based.
1014 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
1015 */
1016 public static boolean isWindows() {
1017 Properties props = System.getProperties();
1018 String os_name = props.getProperty("os.name","");
1019 if(os_name.startsWith("Windows")) {
1020 return true;
1021 }
1022 return false;
1023 }
1024
1025 public static boolean isWindows9x() {
1026 Properties props = System.getProperties();
1027 String os_name = props.getProperty("os.name","");
1028 if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
1029 return true;
1030 }
1031 return false;
1032 }
1033 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
1034 * @param str The target <strong>String</strong> that needs to be padded.
1035 * @param length The desired length of the string as an <i>int</i>.
1036 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
1037 */
1038 public static String pad(String str, int length) {
1039 return pad(str, length, ' ', true);
1040 }
1041 public static String pad(String str_raw, int length, char fill, boolean end) {
1042 StringBuffer str = new StringBuffer(str_raw);
1043 while(str.length() < length) {
1044 if(end) {
1045 str.insert(0, fill);
1046 }
1047 else {
1048 str.append(fill);
1049 }
1050 }
1051 return str.toString();
1052 }
1053
1054
1055 /** 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. */
1056 static public Document parse(String filename, boolean use_classloader)
1057 {
1058 // Try the class loader if desired (for the applet JAR file)
1059 if (use_classloader) {
1060 InputStream is = base.getResourceAsStream("/" + filename);
1061 if (is != null) {
1062 return parse(is, true);
1063 }
1064 }
1065
1066 // Try the file outside the classes directory
1067 return parse(new File(filename), true);
1068 }
1069
1070 /** Parse in a xml document from a given file. */
1071 static public Document parse(File file) {
1072 return parse(file, true);
1073 }
1074
1075 /** Parse in a xml document from a given file. */
1076 static public Document parse(File file, boolean noisey)
1077 {
1078 Document document = null;
1079 try {
1080 if (file.exists()) {
1081 DebugStream.println("Parsing XML file: " + file);
1082 FileInputStream fis = new FileInputStream(file);
1083 document = parse(fis, noisey);
1084 }
1085 }
1086 catch (Exception error) {
1087 if(noisey) {
1088 error.printStackTrace();
1089 DebugStream.println("Exception in Utility.parse() - Unexpected");
1090 }
1091 else {
1092 DebugStream.println("Exception in Utility.parse() - Expected");
1093 DebugStream.printStackTrace(error);
1094 }
1095 }
1096
1097 return document;
1098 }
1099
1100 /** Parse in a xml document from a given URL. */
1101 static public Document parse(URL url, boolean noisey)
1102 {
1103 Document document = null;
1104 try {
1105
1106 URLConnection connection = url.openConnection();
1107 InputStream is = connection.getInputStream();
1108 document = parse(is,noisey);
1109 }
1110 catch (Exception error) {
1111 if(noisey) {
1112 error.printStackTrace();
1113 DebugStream.println("Exception in Utility.parse() - Unexpected");
1114 }
1115 else {
1116 DebugStream.println("Exception in Utility.parse() - Expected");
1117 DebugStream.printStackTrace(error);
1118 }
1119 }
1120
1121 return document;
1122 }
1123
1124 /** Parse in a xml document from a given file. */
1125 static public Document parse(InputStream is, boolean noisey) {
1126 Document document = null;
1127 try {
1128 InputStreamReader isr = new InputStreamReader(is, ENCODING);
1129 Reader r = new BufferedReader(isr);
1130 InputSource isc = new InputSource(r);
1131 DOMParser parser = new DOMParser();
1132 parser.setFeature("http://xml.org/sax/features/validation", false);
1133 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
1134 // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
1135 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
1136 parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
1137 parser.parse(isc);
1138 document = parser.getDocument();
1139 isr.close();
1140 is.close();
1141 parser = null;
1142 isc = null;
1143 r = null;
1144 isr = null;
1145 is = null;
1146 }
1147 catch (Exception error) {
1148 if(noisey) {
1149 error.printStackTrace();
1150 DebugStream.println("Exception in Utility.parse() - Unexpected");
1151 }
1152 else {
1153 DebugStream.println("Exception in Utility.parse() - Expected");
1154 }
1155 DebugStream.printStackTrace(error);
1156 }
1157 return document;
1158 }
1159
1160
1161 static public StringBuffer readXMLStream(InputStream input_stream)
1162 {
1163 StringBuffer xml = new StringBuffer("");
1164
1165 try {
1166 InputStreamReader isr = new InputStreamReader(input_stream, "UTF-8");
1167 BufferedReader buffered_in = new BufferedReader(isr);
1168
1169 String line = "";
1170 boolean xml_content = false;
1171 while((line = buffered_in.readLine()) != null) {
1172 if(xml_content) {
1173 xml.append(line);
1174 xml.append("\n");
1175 }
1176 else if(line.trim().startsWith("<?xml")) {
1177 xml_content = true;
1178 xml.append(line);
1179 xml.append("\n");
1180 }
1181 }
1182 buffered_in = null;
1183 }
1184 catch (Exception error) {
1185 System.err.println("Failed when trying to parse XML stream");
1186 error.printStackTrace();
1187 }
1188
1189 return xml;
1190 }
1191
1192
1193 /** 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.
1194 * @param original The <strong>String</strong> to justify.
1195 * @param width The desired width as an <i>int</i>.
1196 * @param current_width An <i>int</i> representing the current width of the string, which takes into account special characters.
1197 * @return The newly justified <strong>String</strong>.
1198 */
1199 static private String space(String original, int width, int current_width) {
1200 // Strip trailing whitespace.
1201 while(original.charAt(original.length() - 1) == ' ') {
1202 original = original.substring(0, original.length() - 2);
1203 }
1204 int diff = width - current_width;
1205 // Now add diff spaces, one at each existing space.
1206 int pos = 0;
1207 while(diff > 0) {
1208 if(pos == original.length()) {
1209 pos = 0;
1210 }
1211 if(original.charAt(pos) == ' ') {
1212 // Insert a space.
1213 String prefix = original.substring(0, pos);
1214 String suffix = original.substring(pos);
1215 original = prefix + " " + suffix;
1216 pos = pos + 2;
1217 diff--;
1218 }
1219 pos++;
1220 }
1221 return original;
1222 }
1223
1224
1225 /** I think this works a bit better on Unicode strings. */
1226 static public String stripNL(String raw_string)
1227 {
1228 String stripped_string = new String();
1229 for (int i = 0; i < raw_string.length(); i++) {
1230 char raw_character = raw_string.charAt(i);
1231 if (raw_character != '\n' && raw_character != '\t') {
1232 stripped_string = stripped_string + raw_character;
1233 }
1234 }
1235 return stripped_string;
1236 }
1237
1238
1239 static public String trimCenter(String text, int length) {
1240 if(text.length() > length) {
1241 int half = (length - 3) / 2;
1242 StringBuffer temp = new StringBuffer(text.substring(0, half));
1243 temp.append("...");
1244 temp.append(text.substring(text.length() - half));
1245 text = temp.toString();
1246 }
1247 return text;
1248 }
1249}
Note: See TracBrowser for help on using the repository browser.