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

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

Removed an unused definition.

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