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

Last change on this file since 8266 was 8266, checked in by mdewsnip, 20 years ago

Moved the init_images function from Gatherer into Utility (it is used by the GEMS).

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