/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * *

* * Author: John Thompson, Greenstone Digital Library, University of Waikato * *

* * Copyright (C) 1999 New Zealand Digital Library Project * *

* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * *

* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

* * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer; import java.awt.*; import java.io.*; import java.lang.ref.*; import java.net.*; import java.util.*; import javax.swing.*; import javax.swing.plaf.*; import org.greenstone.gatherer.msm.MSMUtils; import org.greenstone.gatherer.util.Utility; import org.w3c.dom.*; /** This class stores the various configurable settings inside the Gatherer, both during a session, and between sessions in the form of XML. However not all data members are retained during xml serialization. To further improve efficiency, the property-name -> DOM Element pairs are stored in a SoftReferenced Hashtable. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ public class Configuration extends Hashtable { static final public boolean COLLECTION_SPECIFIC = true; static final public boolean GENERAL_SETTING = true; /** The string identifying an argument's name attribute. */ static final private String ARGUMENT_NAME = "name"; /** The name of the general Gatherer configuration file. */ static final private String CONFIG_XML = "config.xml"; /** The name of the root element of the subtree containing gatherer configuration options. This is required as the document itself may contain several other subtrees of settings (such as in the case of a '.col' file). */ static final private String GATHERER_CONFIG = "GathererConfig"; /** The string identifying an argument element. */ static final private String GATHERER_CONFIG_ARGUMENT = "Argument"; static final private String GENERAL_EMAIL_SETTING = "general.email"; /** The name of a Name Element. */ static final private String NAME = "Name"; /** The name of the other arguments element. */ static final private String OTHER = "Other"; /** The name of an information Element within the Other subtree. */ static final private String OTHER_INFO = "Info"; /** The name of the general Gatherer configuration template. */ static final private String TEMPLATE_CONFIG_XML = "xml/config.xml"; /** The first of two patterns used during tokenization, this pattern handles a comma separated list. */ static final private String TOKENIZER_PATTERN1 = " ,\n\t"; /** The second of two patterns used during tokenization, this pattern handles an underscore separated list. */ static final private String TOKENIZER_PATTERN2 = "_\n\t"; public File exec_file; /** The path (or url) to the webserver which is serving the Greenstone collection. */ public String exec_path = null; /** The path to the Greenstone Suite installation directory. */ public String gsdl_path = ""; /** The path to the PERL executable, up to and including Perl.exe. */ public String perl_path = ""; /** The password for the proxy server indicated above. */ public String proxy_pass = null; /** The username for the proxy server indicated above. */ public String proxy_user = null; /** The language selected for the interface. Currently hard-wired. */ public String interface_language = "en"; /** The screen size of the desktop the Gatherer will be displayed on. */ public Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); /** Collection level configuration (which in some cases overrides general configuration. */ private Document collection_config; /** The general configuration settings. */ private Document general_config; private int cache_hit = 0; private int cache_miss = 0; public URL exec_address = null; /** Constructor. * @param gsdl_path The path to the Greenstone directory as a String. * @param exec_path A String containing the path or url to the webserver serving the greenstone collections. * @param perl_path The path to the PERL executable, as a String. */ public Configuration(String gsdl_path, String exec_path, String perl_path) { super(); this.gsdl_path = gsdl_path; this.exec_path = exec_path; // The exec_path may contain an url address, in which case we blindly use that and leave it up to the user to worry about settings and resetting. Gatherer.println("EXEC_PATH = " + exec_path); if(exec_path != null && exec_path.length() > 0) { try { exec_address = new URL(exec_path); } catch (MalformedURLException error) { ///ystem.err.println("Not an address."); } } // If the above failed, then its up to us to try and figure out what to do. if(exec_address == null) { // Try building a file from the given exec_path try { File local_file = new File(exec_path); if(local_file.exists()) { // All good. I hope. exec_file = local_file; } else { ///ystem.err.println("No local library at given file path."); } } // All sorts of errors might be thrown by a bogus file path. catch (Exception error) { Gatherer.println("Not a valid file."); } // We can generate the path to where the local library should be and use that if it is there. if(exec_file == null) { File server_exe = new File(gsdl_path + Utility.SERVER_EXE); if(server_exe.exists()) { exec_file = server_exe; } else { ///ystem.err.println("No local library."); } } // If we get to here with no exec_address nor an exec_file its just plain not going to work. } else { ///ystem.err.println("exec_address != null -> " + exec_address); } ///ystem.err.println("Perl path."); this.perl_path = perl_path; // Ensure the perl path includes exe under windoze if(Utility.isWindows() && !perl_path.toLowerCase().endsWith(".exe")) { if(!perl_path.endsWith(File.separator)) { perl_path = perl_path + File.separator; } perl_path = perl_path + "perl.exe"; } // Try to reload the configuration. File config_xml = new File(CONFIG_XML); if(config_xml.exists()) { general_config = Utility.parse(CONFIG_XML, false); } // If that fails retrieve the default configuration file from our xml library, which I'll personally guarantee to work. if(general_config == null) { general_config = Utility.parse(TEMPLATE_CONFIG_XML, true); Gatherer.println("Loaded default Gatherer configuration template."); } else { Gatherer.println("Loaded current Gatherer configuration."); } // Re-establish the color settings. updateUI(); // If we have no exec_address, see if one was specified in the config file if (exec_address == null) { String exec_address_string = getString("general.exec_address", true); if (!exec_address_string.equals("")) { try { exec_address = new URL(exec_address_string); } catch (MalformedURLException error) { ///ystem.err.println("Error: Bad address: " + exec_address_string); } } } Gatherer.println("EXEC_FILE = " + exec_file); Gatherer.println("EXEC_ADDRESS = " + exec_address); } /** The default get action retrieves the named property from the desired configuration, and returns a true or false. */ public boolean get(String property, boolean general) { String raw = getString(property, general); return (raw != null && raw.equalsIgnoreCase("true")); } /** Retrieve all of the configuration preferences which match a certain string. They are returned as a hash map of property names to String objects. */ public HashMap getAll(String property_pattern, boolean general) { HashMap properties = new HashMap(); try { // Locate the appropriate element Element document_element = null; if(general) { document_element = general_config.getDocumentElement(); } else if(collection_config != null) { document_element = collection_config.getDocumentElement(); } if(document_element != null) { // Retrieve the Gatherer element Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG); NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT); for(int i = 0; i < arguments.getLength(); i++) { Element argument_element = (Element) arguments.item(i); if(argument_element.getAttribute(ARGUMENT_NAME).matches(property_pattern)) { String result = MSMUtils.getValue(argument_element); // Store a mapping in the cache. Sometimes we will overwrite an existing value (say for collection and general level workflow options) but the processing overhead of detecting these clashes far exceeds any savings. put(argument_element.getAttribute(ARGUMENT_NAME) + general, new SoftReference(argument_element)); // Add mapping to the properties we're going to return properties.put(argument_element.getAttribute(ARGUMENT_NAME), result); } } } } catch(Exception error) { } return properties; } /** Retrieve the information subtree containing the arguments for the desired external program. If the program has marked superclasses append their arguments as well. */ public Element getArguments(String filename) { Element argument_element = null; try { // Retrieve the other information subtree. Element document_element = general_config.getDocumentElement(); Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER); NodeList argument_elements = other_element.getElementsByTagName(OTHER_INFO); for(int i = 0; argument_element == null && i < argument_elements.getLength(); i++) { Element possible_element = (Element) argument_elements.item(i); Element possible_name_element = (Element) MSMUtils.getNodeFromNamed(possible_element, NAME); String possible_name = MSMUtils.getValue(possible_name_element); ///ystem.err.println("Does " + possible_name + " equal " + filename); if(possible_name.equalsIgnoreCase(filename)) { argument_element = possible_element; } possible_name = null; possible_name_element = null; possible_element = null; } argument_elements = null; other_element = null; document_element = null; } catch(Exception error) { } return argument_element; } /** Retrieve the value of the named property as a Rectangle. */ public Rectangle getBounds(String property, boolean general) { Rectangle result = null; try { String raw = getString(property, general); // Rectangle is (x, y, width, height) StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); int x = Integer.parseInt(tokenizer.nextToken()); int y = Integer.parseInt(tokenizer.nextToken()); int width = Integer.parseInt(tokenizer.nextToken()); int height = Integer.parseInt(tokenizer.nextToken()); result = new Rectangle(x, y, width, height); } catch(Exception error) { Gatherer.printStackTrace(error); } return result; } /** Retrieve the value of the named property as a Color. */ public Color getColor(String property, boolean general) { Color result = Color.white; // Default try { String raw = getString(property, general); // Color is a RGB triplet list, comma separated (also remove whitespace) StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); int red = Integer.parseInt(tokenizer.nextToken()); int green = Integer.parseInt(tokenizer.nextToken()); int blue = Integer.parseInt(tokenizer.nextToken()); result = new Color(red, green, blue); } catch(Exception error) { Gatherer.printStackTrace(error); } return result; } /** Retrieve the value of the named property as a Dimension. */ /* private Dimension getDimension(String property, boolean general) { Dimension result = new Dimension(100, 100); // Default try { String raw = getString(property, general); // Dimension is a width by height pair, comma separated (also remove whitespace) StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); int width = Integer.parseInt(tokenizer.nextToken()); int height = Integer.parseInt(tokenizer.nextToken()); result = new Dimension(width, height); } catch(Exception error) { Gatherer.printStackTrace(error); } return result; } */ /** Retrieve the current users email. These are always stored in the general settings. * @return the email address, if it is set, as a String */ public String getEmail() { String email = getString(GENERAL_EMAIL_SETTING, true); return (email.length() > 0 ? email : null); } /** Retrieve the value of the named property as a FontUIResource. */ public FontUIResource getFont(String property, boolean general) { FontUIResource result = new FontUIResource("Times New Roman", Font.PLAIN, 10); try { String raw = getString(property, general); // Font is a face, style, size triplet. StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); String face = tokenizer.nextToken(); int style = Font.PLAIN; String temp = tokenizer.nextToken().toUpperCase(); if(temp.equals("BOLD")) { style = Font.BOLD; } else if(temp.equals("ITALIC")) { style = Font.ITALIC; } int size = Integer.parseInt(tokenizer.nextToken()); result = new FontUIResource(face, style, size); } catch(Exception error) { Gatherer.printStackTrace(error); } return result; } /** Retrieve the value of the named property as an integer. */ public int getInt(String property, boolean general) { int result = -1; try { String raw = getString(property, general); result = Integer.parseInt(raw); } catch(Exception error) { Gatherer.printStackTrace(error); } return result; } /** Retrieve the value of the named property as a Locale. */ public Locale getLocale(String property, boolean general) { Locale result = Locale.getDefault(); try { String raw = getString(property, general); // Locale is a underscore separated code. StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN2); String language = tokenizer.nextToken(); if(tokenizer.hasMoreTokens()) { String country = tokenizer.nextToken(); result = new Locale(language, country); } else { result = new Locale(language); } } catch(Exception error) { Gatherer.printStackTrace(error); } return result; } /** Retrieve the value of the named property, and noting whether we consult the general or collection specific configuration. */ public String getString(String property, boolean general) { // Its up to this method to find the appropriate node and retrieve the data itself. String result = ""; try { // First of all we look in the cache to see if we have a match. SoftReference reference = (SoftReference) get(property + general); if(reference != null) { Element argument_element = (Element) reference.get(); if(argument_element != null) { cache_hit++; result = MSMUtils.getValue(argument_element); } } // We may have missed in the cache, or the reference may have been consumed. if(result.length() == 0) { cache_miss++; // Locate the appropriate element Element document_element = null; if(general) { document_element = general_config.getDocumentElement(); } else if(collection_config != null) { document_element = collection_config.getDocumentElement(); } if(document_element != null) { // Retrieve the Gatherer element Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG); NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT); for(int i = 0; result.length() == 0 && i < arguments.getLength(); i++) { Element argument_element = (Element) arguments.item(i); if(argument_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) { result = MSMUtils.getValue(argument_element); // Store a mapping in the cache. Sometimes we will overwrite an existing value (say for collection and general level workflow options) but the processing overhead of detecting these clashes far exceeds any savings. put(property + general, new SoftReference(argument_element)); } } } } } catch (Exception error) { Gatherer.printStackTrace(error); } // If we still have no result, and the search was made in the collection configuration, retrieve the general one instead. if(result.length() == 0 && !general) { result = getString(property, true); } return result; } /** Retrieve the path to the PERL scripts within the Greenstone directory. * @return A String containing the path. */ public String getScriptPath() { return gsdl_path + "bin" + File.separator + "script" + File.separator; } /** Export the general configuration to file. */ public void save() { ///ystem.err.println("Hits " + cache_hit + " vs Misses " + cache_miss); Utility.export(general_config, Utility.BASE_DIR + CONFIG_XML); } /** Set the named property, from the specified configuration, using the given boolean value. */ public void set(String property, boolean general, boolean value) { if(property.startsWith("workflow")) { Gatherer.println("Set property: " + property + ", general=" + general + ", value=" + value); } setString(property, general, (value ? "true" : "false")); } /** Add a subtree of argument information to the other arguments part of the general configuration. This overwrites any such existing subtree. */ public void setArguments(Element arguments_element) { try { Element document_element = general_config.getDocumentElement(); Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER); // Retrieve the name of the information Element arguments_name_element = (Element)MSMUtils.getNodeFromNamed(arguments_element, NAME); String filename = MSMUtils.getValue(arguments_element); // Find any argument information subtree starting with the same name Element obsolete_arguments_element = getArguments(filename); // Create a copy of the arguments_element within our tree (import). Element our_arguments_element = (Element) general_config.importNode(arguments_element, true); // Now we insert this new node into the tree. If a previous node exists we replace it instead. if(obsolete_arguments_element == null) { other_element.appendChild(our_arguments_element); } else { other_element.replaceChild(our_arguments_element, obsolete_arguments_element); } our_arguments_element = null; obsolete_arguments_element = null; filename = null; arguments_name_element = null; other_element = null; document_element = null; } catch (Exception error) { Gatherer.println("Error in Configuration.setArguments(): " + error); Gatherer.printStackTrace(error); } } /** Set the collection configuration. */ public void setCollectionConfiguration(Document collection_config) { // clear the cached values clear(); this.collection_config = collection_config; updateUI(); ///atherer.println("Collection configuration set."); } /** Set the named property, from the specified configuration, using the given Rectangle value. */ public void setBounds(String property, boolean general, Rectangle value) { StringBuffer text = new StringBuffer(""); text.append(value.x); text.append(", "); text.append(value.y); text.append(", "); text.append(value.width); text.append(", "); text.append(value.height); setString(property, general, text.toString()); } /** Set the named property, from the specified configuration, using the given Color value. */ public void setColor(String property, boolean general, Color value) { StringBuffer text = new StringBuffer(""); text.append(value.getRed()); text.append(", "); text.append(value.getGreen()); text.append(", "); text.append(value.getBlue()); setString(property, general, text.toString()); } /** Set the named property, from the specified configuration, using the given Dimension value. */ /* private void setDimension(String property, boolean general, Dimension value) { StringBuffer text = new StringBuffer(""); text.append(value.width); text.append(", "); text.append(value.height); setString(property, general, text.toString()); } */ /** Establish the current users email. * @param email the email as a String */ public void setEmail(String email) { setString(GENERAL_EMAIL_SETTING, true, email); } /** Set the named property, from the specified configuration, using the given Font value. */ public void setFont(String property, boolean general, Font value) { StringBuffer text = new StringBuffer(""); text.append(value.getName()); text.append(", "); switch(value.getStyle()) { case Font.BOLD: text.append("BOLD"); break; case Font.ITALIC: text.append("ITALIC"); break; default: text.append("PLAIN"); } text.append(", "); text.append(value.getSize()); setString(property, general, text.toString()); } /** Set the named property, from the specified configuration, using the given integer value. */ /* private void setInt(String property, boolean general, int value) { setString(property, general, String.valueOf(value)); } */ /** Set the named property, from the specified configuration, using the given Locale value. */ public void setLocale(String property, boolean general, Locale value) { StringBuffer text = new StringBuffer(""); text.append(value.getLanguage()); String country = value.getCountry(); if(country != null && country.length() > 0) { text.append("_"); text.append(country); } country = null; setString(property, general, text.toString()); } /** Sets the value of the named property argument using the given string. */ public void setString(String property, boolean general, String value) { Gatherer.println("Set configuration property: " + property + " = " + value + (general ? "" : " [Collection]")); try { Document document = general_config; if(!general && collection_config != null) { document = collection_config; } if(document != null) { Element argument_element = null; // Try to retrieve from cache SoftReference reference = (SoftReference) get(property + general); if(reference != null) { argument_element = (Element) reference.get(); } if(argument_element == null) { Element document_element = document.getDocumentElement(); Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG); NodeList arguments = document_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT); boolean found = false; for(int i = 0; argument_element == null && i < arguments.getLength(); i++) { Element possible_element = (Element) arguments.item(i); if(possible_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) { argument_element = possible_element; } } // If argument element is still null, create it in the target document. if(argument_element == null) { argument_element = document.createElement(GATHERER_CONFIG_ARGUMENT); argument_element.setAttribute(ARGUMENT_NAME, property); gatherer_element.appendChild(argument_element); } // Update cache put(property + general, new SoftReference(argument_element)); } if(value == null) { value = ""; } // Now remove any current text node children. NodeList children = argument_element.getChildNodes(); for(int i = 0; i < children.getLength(); i++) { argument_element.removeChild(children.item(i)); } // Add a new text node child with the new value argument_element.appendChild(document.createTextNode(value)); } } catch (Exception error) { } } private void updateUI() { // Buttons UIManager.put("Button.select", new ColorUIResource(getColor("coloring.button_selected_background", false))); UIManager.put("Button.background", new ColorUIResource(getColor("coloring.button_background", false))); UIManager.put("Button.foreground", new ColorUIResource(getColor("coloring.button_foreground", false))); UIManager.put("ToggleButton.background", new ColorUIResource(getColor("coloring.button_background", false))); UIManager.put("ToggleButton.foreground", new ColorUIResource(getColor("coloring.button_foreground", false))); UIManager.put("ToggleButton.select", new ColorUIResource(getColor("coloring.button_selected_background", false))); // All the things with a lovely Collection green background UIManager.put("OptionPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); UIManager.put("Panel.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); UIManager.put("Label.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); UIManager.put("TabbedPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); UIManager.put("SplitPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); UIManager.put("CheckBox.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); // Editable coloring UIManager.put("ComboBox.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); // Indicate clickable UIManager.put("Tree.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); UIManager.put("Tree.textBackground", new ColorUIResource(getColor("coloring.collection_tree_background", false))); UIManager.put("ProgressBar.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); UIManager.put("TextArea.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); UIManager.put("TextField.background", new ColorUIResource(getColor("coloring.editable_background", false))); UIManager.put("Table.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); UIManager.put("List.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); UIManager.put("RadioButton.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); // Selection color UIManager.put("TabbedPane.selected", new ColorUIResource(getColor("coloring.collection_selection_background", false))); UIManager.put("Tree.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); UIManager.put("ComboBox.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); UIManager.put("ProgressBar.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); UIManager.put("TextArea.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); UIManager.put("TextField.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); UIManager.put("List.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); // Scroll bar stuff UIManager.put("ScrollBar.background", new ColorUIResource(getColor("coloring.scrollbar_background", false))); UIManager.put("ScrollBar.thumb", new ColorUIResource(getColor("coloring.scrollbar_foreground", false))); if (Gatherer.g_man != null) { JPanel pane = (JPanel) Gatherer.g_man.getContentPane(); pane.updateUI(); // Also update all of the tabs according to workflow. Gatherer.g_man.workflowUpdate("Hunt", get("workflow.browse", false)); Gatherer.g_man.workflowUpdate("Mirror", get("workflow.mirror", false)); Gatherer.g_man.workflowUpdate("Gather", get("workflow.gather", false)); Gatherer.g_man.workflowUpdate("Enrich", get("workflow.enrich", false)); Gatherer.g_man.workflowUpdate("Design", get("workflow.design", false)); Gatherer.g_man.workflowUpdate("Export", get("workflow.export", false)); Gatherer.g_man.workflowUpdate("Create", get("workflow.create", false)); Gatherer.g_man.workflowUpdate("Preview", get("workflow.preview", false)); } } }