Changeset 4363


Ignore:
Timestamp:
2003-05-27T15:34:50+12:00 (21 years ago)
Author:
kjdon
Message:

re-tabbed the code for java

Location:
trunk/gli/src/org/greenstone/gatherer
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/gli/src/org/greenstone/gatherer/Configuration.java

    r4348 r4363  
    5454 */
    5555public class Configuration
    56       extends Hashtable {
    57     public File exec_file;
    58     /** The path (or url) to the webserver which is serving the Greenstone collection. */
    59     public String exec_path = null;
    60     /** The path to the Greenstone Suite installation directory. */
    61     public String gsdl_path = "";
     56    extends Hashtable {
     57    public File exec_file;
     58    /** The path (or url) to the webserver which is serving the Greenstone collection. */
     59    public String exec_path = null;
     60    /** The path to the Greenstone Suite installation directory. */
     61    public String gsdl_path = "";
    6262    /** The path to the PERL executable, up to and including Perl.exe. */
    6363    public String perl_path = "";
    64     /** The password for the proxy server indicated above. */
    65     public String proxy_pass = null;
    66     /** The username for the proxy server indicated above. */
    67     public String proxy_user = null;
    68     /** The screen size of the desktop the Gatherer will be displayed on. */
    69     public Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
    70     /** Collection level configuration (which in some cases overrides general configuration. */
    71     private Document collection_config;
    72     /** The general configuration settings. */
    73     private Document general_config;
    74     private int cache_hit = 0;
    75     private int cache_miss = 0;
    76     public URL exec_address = null;
    77     /** The string identifying an argument's name attribute. */
    78     static final private String ARGUMENT_NAME = "name";
    79     /** The name of the general Gatherer configuration file. */
    80     static final private String CONFIG_XML = "config.xml";
    81     /** 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). */
    82     static final private String GATHERER_CONFIG = "GathererConfig";
    83     /** The string identifying an argument element. */
    84     static final private String GATHERER_CONFIG_ARGUMENT = "Argument";
    85     /** The name of a Name Element. */
    86     static final private String NAME = "Name";
    87     /** The name of the other arguments element. */
    88     static final private String OTHER = "Other";
    89     /** The name of an information Element within the Other subtree. */
    90     static final private String OTHER_INFO = "Info";
    91     /** The name of the general Gatherer configuration template. */
    92     static final private String TEMPLATE_CONFIG_XML = "xml/config.xml";
    93     /** The first of two patterns used during tokenization, this pattern handles a comma separated list. */
    94     static final private String TOKENIZER_PATTERN1 = " ,\n\t";
    95     /** The second of two patterns used during tokenization, this pattern handles an underscore separated list. */
    96     static final private String TOKENIZER_PATTERN2 = "_\n\t";
    97     /** Constructor.
    98       * @param gsdl_path The path to the Greenstone directory as a <strong>String</strong>.
    99       * @param exec_path A <strong>String</strong> containing the path or url to the webserver serving the greenstone collections.
    100       * @param perl_path The path to the PERL executable, as a <strong>String</strong>.
    101       */
    102     public Configuration(String gsdl_path, String exec_path, String perl_path) {
    103           super();
    104           this.gsdl_path = gsdl_path;
    105           this.exec_path = exec_path;
    106           // 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.
    107           System.err.println("EXEC_PATH = " + exec_path);
    108           if(exec_path != null && exec_path.length() > 0) {
    109                 try {
    110                      exec_address = new URL(exec_path);
    111                 }
    112                 catch (MalformedURLException error) {
    113                      ///ystem.err.println("Not an address.");
    114                 }
    115           }
    116           // If the above failed, then its up to us to try and figure out what to do.
    117           if(exec_address == null) {
     64    /** The password for the proxy server indicated above. */
     65    public String proxy_pass = null;
     66    /** The username for the proxy server indicated above. */
     67    public String proxy_user = null;
     68    /** The screen size of the desktop the Gatherer will be displayed on. */
     69    public Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
     70    /** Collection level configuration (which in some cases overrides general configuration. */
     71    private Document collection_config;
     72    /** The general configuration settings. */
     73    private Document general_config;
     74    private int cache_hit = 0;
     75    private int cache_miss = 0;
     76    public URL exec_address = null;
     77    /** The string identifying an argument's name attribute. */
     78    static final private String ARGUMENT_NAME = "name";
     79    /** The name of the general Gatherer configuration file. */
     80    static final private String CONFIG_XML = "config.xml";
     81    /** 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). */
     82    static final private String GATHERER_CONFIG = "GathererConfig";
     83    /** The string identifying an argument element. */
     84    static final private String GATHERER_CONFIG_ARGUMENT = "Argument";
     85    /** The name of a Name Element. */
     86    static final private String NAME = "Name";
     87    /** The name of the other arguments element. */
     88    static final private String OTHER = "Other";
     89    /** The name of an information Element within the Other subtree. */
     90    static final private String OTHER_INFO = "Info";
     91    /** The name of the general Gatherer configuration template. */
     92    static final private String TEMPLATE_CONFIG_XML = "xml/config.xml";
     93    /** The first of two patterns used during tokenization, this pattern handles a comma separated list. */
     94    static final private String TOKENIZER_PATTERN1 = " ,\n\t";
     95    /** The second of two patterns used during tokenization, this pattern handles an underscore separated list. */
     96    static final private String TOKENIZER_PATTERN2 = "_\n\t";
     97    /** Constructor.
     98     * @param gsdl_path The path to the Greenstone directory as a <strong>String</strong>.
     99     * @param exec_path A <strong>String</strong> containing the path or url to the webserver serving the greenstone collections.
     100     * @param perl_path The path to the PERL executable, as a <strong>String</strong>.
     101     */
     102    public Configuration(String gsdl_path, String exec_path, String perl_path) {
     103    super();
     104    this.gsdl_path = gsdl_path;
     105    this.exec_path = exec_path;
     106    // 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.
     107    System.err.println("EXEC_PATH = " + exec_path);
     108    if(exec_path != null && exec_path.length() > 0) {
     109        try {
     110        exec_address = new URL(exec_path);
     111        }
     112        catch (MalformedURLException error) {
     113        ///ystem.err.println("Not an address.");
     114        }
     115    }
     116    // If the above failed, then its up to us to try and figure out what to do.
     117    if(exec_address == null) {
    118118                // Try building a file from the given exec_path
    119                 try {
    120                      File local_file = new File(exec_path);
    121                      if(local_file.exists()) {
    122                           // All good. I hope.
    123                           exec_file = local_file;
    124                      }
    125                      else {
    126                           ///ystem.err.println("No local library at given file path.");
    127                      }
    128                 }
     119        try {
     120        File local_file = new File(exec_path);
     121        if(local_file.exists()) {
     122            // All good. I hope.
     123            exec_file = local_file;
     124        }
     125        else {
     126            ///ystem.err.println("No local library at given file path.");
     127        }
     128        }
    129129                // All sorts of errors might be thrown by a bogus file path.
    130                 catch (Exception error) {
    131                      System.err.println("Not a valid file.");
    132                 }
     130        catch (Exception error) {
     131        System.err.println("Not a valid file.");
     132        }
    133133                // We can generate the path to where the local library should be and use that if it is there.
    134                 if(exec_file == null) {
    135                      File server_exe = new File(gsdl_path + Utility.SERVER_EXE);
    136                      if(server_exe.exists()) {
    137                           exec_file = server_exe;
    138                      }
    139                      else {
    140                           ///ystem.err.println("No local library.");
    141                      }
    142                 }
     134        if(exec_file == null) {
     135        File server_exe = new File(gsdl_path + Utility.SERVER_EXE);
     136        if(server_exe.exists()) {
     137            exec_file = server_exe;
     138        }
     139        else {
     140            ///ystem.err.println("No local library.");
     141        }
     142        }
    143143                // If we get to here with no exec_address nor an exec_file its just plain not going to work.
    144           }
    145           else {
     144    }
     145    else {
    146146                ///ystem.err.println("exec_address != null -> " + exec_address);
    147           }
    148           ///ystem.err.println("Perl path.");
    149           this.perl_path = perl_path;
    150           // Ensure the perl path includes exe under windoze
    151           if(Utility.isWindows() && !perl_path.toLowerCase().endsWith(".exe")) {
    152                 if(!perl_path.endsWith(File.separator)) {
    153                      perl_path = perl_path + File.separator;
    154                 }
    155                 perl_path = perl_path + "perl.exe";
    156           }
    157           // Try to reload the configuration.
    158           File config_xml = new File(CONFIG_XML);
    159           if(config_xml.exists()) {
    160                 general_config = Utility.parse(CONFIG_XML, false);
    161           }
    162           // If that fails retrieve the default configuration file from our xml library, which I'll personally guarantee to work.
    163           if(general_config == null) {
    164                 general_config = Utility.parse(TEMPLATE_CONFIG_XML, true);
    165                 Gatherer.println("Loaded default Gatherer configuration template.");
    166           }
    167           else {
    168                 Gatherer.println("Loaded current Gatherer configuration.");
    169           }
    170           // Re-establish the color settings.
    171           updateUI();
    172 
    173           System.err.println("EXEC_FILE = " + exec_file);
    174           System.err.println("EXEC_ADDRESS = " + exec_address);
    175     }
    176 
    177     /** The default get action retrieves the named property from the desired configuration, and returns a true or false. */
    178     public boolean get(String property, boolean general) {
    179           String raw = getString(property, general);
    180           return (raw != null && raw.equalsIgnoreCase("true"));
    181     }
    182 
    183     /** Retrieve all of the configuration preferences which match a certain string. They are returned as a hash map of property names to String objects. */
    184     public HashMap getAll(String property_pattern, boolean general) {
    185           HashMap properties = new HashMap();
    186           try {
     147    }
     148    ///ystem.err.println("Perl path.");
     149    this.perl_path = perl_path;
     150    // Ensure the perl path includes exe under windoze
     151    if(Utility.isWindows() && !perl_path.toLowerCase().endsWith(".exe")) {
     152        if(!perl_path.endsWith(File.separator)) {
     153        perl_path = perl_path + File.separator;
     154        }
     155        perl_path = perl_path + "perl.exe";
     156    }
     157    // Try to reload the configuration.
     158    File config_xml = new File(CONFIG_XML);
     159    if(config_xml.exists()) {
     160        general_config = Utility.parse(CONFIG_XML, false);
     161    }
     162    // If that fails retrieve the default configuration file from our xml library, which I'll personally guarantee to work.
     163    if(general_config == null) {
     164        general_config = Utility.parse(TEMPLATE_CONFIG_XML, true);
     165        Gatherer.println("Loaded default Gatherer configuration template.");
     166    }
     167    else {
     168        Gatherer.println("Loaded current Gatherer configuration.");
     169    }
     170    // Re-establish the color settings.
     171    updateUI();
     172
     173    System.err.println("EXEC_FILE = " + exec_file);
     174    System.err.println("EXEC_ADDRESS = " + exec_address);
     175    }
     176
     177    /** The default get action retrieves the named property from the desired configuration, and returns a true or false. */
     178    public boolean get(String property, boolean general) {
     179    String raw = getString(property, general);
     180    return (raw != null && raw.equalsIgnoreCase("true"));
     181    }
     182
     183    /** Retrieve all of the configuration preferences which match a certain string. They are returned as a hash map of property names to String objects. */
     184    public HashMap getAll(String property_pattern, boolean general) {
     185    HashMap properties = new HashMap();
     186    try {
    187187                // Locate the appropriate element
    188                 Element document_element = null;
    189                 if(general) {
    190                      document_element = general_config.getDocumentElement();
    191                 }
    192                 else if(collection_config != null) {
    193                      document_element = collection_config.getDocumentElement();
    194                 }
    195                 if(document_element != null) {
    196                      // Retrieve the Gatherer element
    197                      Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG);
    198                      NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT);
    199                      for(int i = 0; i < arguments.getLength(); i++) {
    200                           Element argument_element = (Element) arguments.item(i);
    201                           if(argument_element.getAttribute(ARGUMENT_NAME).matches(property_pattern)) {
    202                                 String result = MSMUtils.getValue(argument_element);
    203                                 // 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.
    204                                 put(argument_element.getAttribute(ARGUMENT_NAME) + general, new SoftReference(argument_element));
    205                                 // Add mapping to the properties we're going to return
    206                                 properties.put(argument_element.getAttribute(ARGUMENT_NAME), result);
    207                           }
    208                      }
    209                 }
    210           }
    211           catch(Exception error) {
    212           }
    213           return properties;
    214     }
    215 
    216     /** Retrieve the information subtree containing the arguments for the desired external program. If the program has marked superclasses append their arguments as well. */
    217     public Element getArguments(String filename) {
    218           Element argument_element = null;
    219           try {
     188        Element document_element = null;
     189        if(general) {
     190        document_element = general_config.getDocumentElement();
     191        }
     192        else if(collection_config != null) {
     193        document_element = collection_config.getDocumentElement();
     194        }
     195        if(document_element != null) {
     196        // Retrieve the Gatherer element
     197        Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG);
     198        NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT);
     199        for(int i = 0; i < arguments.getLength(); i++) {
     200            Element argument_element = (Element) arguments.item(i);
     201            if(argument_element.getAttribute(ARGUMENT_NAME).matches(property_pattern)) {
     202            String result = MSMUtils.getValue(argument_element);
     203            // 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.
     204            put(argument_element.getAttribute(ARGUMENT_NAME) + general, new SoftReference(argument_element));
     205            // Add mapping to the properties we're going to return
     206            properties.put(argument_element.getAttribute(ARGUMENT_NAME), result);
     207            }
     208        }
     209        }
     210    }
     211    catch(Exception error) {
     212    }
     213    return properties;
     214    }
     215
     216    /** Retrieve the information subtree containing the arguments for the desired external program. If the program has marked superclasses append their arguments as well. */
     217    public Element getArguments(String filename) {
     218    Element argument_element = null;
     219    try {
    220220                // Retrieve the other information subtree.
    221                 Element document_element = general_config.getDocumentElement();
    222                 Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER);
    223                 NodeList argument_elements = other_element.getElementsByTagName(OTHER_INFO);
    224                 for(int i = 0; argument_element == null && i < argument_elements.getLength(); i++) {
    225                      Element possible_element = (Element) argument_elements.item(i);
    226                      Element possible_name_element = (Element) MSMUtils.getNodeFromNamed(possible_element, NAME);
    227                      String possible_name = MSMUtils.getValue(possible_name_element);
    228                      ///ystem.err.println("Does " + possible_name + " equal " + filename);
    229                      if(possible_name.equalsIgnoreCase(filename)) {
    230                           argument_element = possible_element;
    231                      }
    232                      possible_name = null;
    233                      possible_name_element = null;
    234                      possible_element = null;
    235                 }
    236                 argument_elements = null;
    237                 other_element = null;
    238                 document_element = null;
    239           }
    240           catch(Exception error) {
    241           }
    242           return argument_element;
    243     }
    244 
    245     /** Retrieve the value of the named property as a Color. */
    246     public Color getColor(String property, boolean general) {
    247           Color result = Color.white; // Default
    248           try {
    249                 String raw = getString(property, general);
     221        Element document_element = general_config.getDocumentElement();
     222        Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER);
     223        NodeList argument_elements = other_element.getElementsByTagName(OTHER_INFO);
     224        for(int i = 0; argument_element == null && i < argument_elements.getLength(); i++) {
     225        Element possible_element = (Element) argument_elements.item(i);
     226        Element possible_name_element = (Element) MSMUtils.getNodeFromNamed(possible_element, NAME);
     227        String possible_name = MSMUtils.getValue(possible_name_element);
     228        ///ystem.err.println("Does " + possible_name + " equal " + filename);
     229        if(possible_name.equalsIgnoreCase(filename)) {
     230            argument_element = possible_element;
     231        }
     232        possible_name = null;
     233        possible_name_element = null;
     234        possible_element = null;
     235        }
     236        argument_elements = null;
     237        other_element = null;
     238        document_element = null;
     239    }
     240    catch(Exception error) {
     241    }
     242    return argument_element;
     243    }
     244
     245    /** Retrieve the value of the named property as a Color. */
     246    public Color getColor(String property, boolean general) {
     247    Color result = Color.white; // Default
     248    try {
     249        String raw = getString(property, general);
    250250                // Color is a RGB triplet list, comma separated (also remove whitespace)
    251                 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1);
    252                 int red = Integer.parseInt(tokenizer.nextToken());
    253                 int green = Integer.parseInt(tokenizer.nextToken());
    254                 int blue = Integer.parseInt(tokenizer.nextToken());
    255                 result = new Color(red, green, blue);
    256           }
    257           catch(Exception error) {
    258                 Gatherer.printStackTrace(error);
    259           }
    260           return result;
    261     }
    262 
    263     /** Retrieve the value of the named property as a Dimension. */
    264     public Dimension getDimension(String property, boolean general) {
    265           Dimension result = new Dimension(100, 100); // Default
    266           try {
    267                 String raw = getString(property, general);
     251        StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1);
     252        int red = Integer.parseInt(tokenizer.nextToken());
     253        int green = Integer.parseInt(tokenizer.nextToken());
     254        int blue = Integer.parseInt(tokenizer.nextToken());
     255        result = new Color(red, green, blue);
     256    }
     257    catch(Exception error) {
     258        Gatherer.printStackTrace(error);
     259    }
     260    return result;
     261    }
     262
     263    /** Retrieve the value of the named property as a Dimension. */
     264    public Dimension getDimension(String property, boolean general) {
     265    Dimension result = new Dimension(100, 100); // Default
     266    try {
     267        String raw = getString(property, general);
    268268                // Dimension is a width by height pair, comma separated (also remove whitespace)
    269                 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1);
    270                 int width = Integer.parseInt(tokenizer.nextToken());
    271                 int height = Integer.parseInt(tokenizer.nextToken());
    272                 result = new Dimension(width, height);
    273           }
    274           catch(Exception error) {
    275                 Gatherer.printStackTrace(error);
    276           }
    277           return result;
    278     }
    279 
    280     /** Retrieve the value of the named property as a FontUIResource. */
    281     public FontUIResource getFont(String property, boolean general) {
    282           FontUIResource result = new FontUIResource("Times New Roman", Font.PLAIN, 10);
    283           try {
    284                 String raw = getString(property, general);
     269        StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1);
     270        int width = Integer.parseInt(tokenizer.nextToken());
     271        int height = Integer.parseInt(tokenizer.nextToken());
     272        result = new Dimension(width, height);
     273    }
     274    catch(Exception error) {
     275        Gatherer.printStackTrace(error);
     276    }
     277    return result;
     278    }
     279
     280    /** Retrieve the value of the named property as a FontUIResource. */
     281    public FontUIResource getFont(String property, boolean general) {
     282    FontUIResource result = new FontUIResource("Times New Roman", Font.PLAIN, 10);
     283    try {
     284        String raw = getString(property, general);
    285285                // Font is a face, style, size triplet.
    286                 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1);
    287                 String face = tokenizer.nextToken();
    288                 int style = Font.PLAIN;
    289                 String temp = tokenizer.nextToken().toUpperCase();
    290                 if(temp.equals("BOLD")) {
    291                      style = Font.BOLD;
    292                 }
    293                 else if(temp.equals("ITALIC")) {
    294                      style = Font.ITALIC;
    295                 }
    296                 int size = Integer.parseInt(tokenizer.nextToken());
    297                 result = new FontUIResource(face, style, size);
    298           }
    299           catch(Exception error) {
    300                 Gatherer.printStackTrace(error);
    301           }
    302           return result;
    303     }
    304 
    305     /** Retrieve the value of the named property as an integer. */
    306     public int getInt(String property, boolean general) {
    307           int result = -1;
    308           try {
    309                 String raw = getString(property, general);
    310                 result = Integer.parseInt(raw);
    311           }
    312           catch(Exception error) {
    313                 Gatherer.printStackTrace(error);
    314           }
    315           return result;
    316     }
    317 
    318     /** Retrieve the value of the named property as a Locale. */
    319     public Locale getLocale(String property, boolean general) {
    320           Locale result = Locale.getDefault();
    321           try {
    322                 String raw = getString(property, general);
     286        StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1);
     287        String face = tokenizer.nextToken();
     288        int style = Font.PLAIN;
     289        String temp = tokenizer.nextToken().toUpperCase();
     290        if(temp.equals("BOLD")) {
     291        style = Font.BOLD;
     292        }
     293        else if(temp.equals("ITALIC")) {
     294        style = Font.ITALIC;
     295        }
     296        int size = Integer.parseInt(tokenizer.nextToken());
     297        result = new FontUIResource(face, style, size);
     298    }
     299    catch(Exception error) {
     300        Gatherer.printStackTrace(error);
     301    }
     302    return result;
     303    }
     304
     305    /** Retrieve the value of the named property as an integer. */
     306    public int getInt(String property, boolean general) {
     307    int result = -1;
     308    try {
     309        String raw = getString(property, general);
     310        result = Integer.parseInt(raw);
     311    }
     312    catch(Exception error) {
     313        Gatherer.printStackTrace(error);
     314    }
     315    return result;
     316    }
     317
     318    /** Retrieve the value of the named property as a Locale. */
     319    public Locale getLocale(String property, boolean general) {
     320    Locale result = Locale.getDefault();
     321    try {
     322        String raw = getString(property, general);
    323323                // Locale is a underscore separated code.
    324                 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN2);
    325                 String language = tokenizer.nextToken();
    326                 String country = tokenizer.nextToken();
    327                 result = new Locale(language, country);
    328           }
    329           catch(Exception error) {
    330                 Gatherer.printStackTrace(error);
    331           }
    332           return result;
    333     }
    334 
    335     /** Retrieve the value of the named property, and noting whether we consult the general or collection specific configuration. */
    336     public String getString(String property, boolean general) {
    337           // Its up to this method to find the appropriate node and retrieve the data itself.
    338           String result = "";
    339           try {
     324        StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN2);
     325        String language = tokenizer.nextToken();
     326        String country = tokenizer.nextToken();
     327        result = new Locale(language, country);
     328    }
     329    catch(Exception error) {
     330        Gatherer.printStackTrace(error);
     331    }
     332    return result;
     333    }
     334
     335    /** Retrieve the value of the named property, and noting whether we consult the general or collection specific configuration. */
     336    public String getString(String property, boolean general) {
     337    // Its up to this method to find the appropriate node and retrieve the data itself.
     338    String result = "";
     339    try {
    340340                // First of all we look in the cache to see if we have a match.
    341                 SoftReference reference = (SoftReference) get(property + general);
    342                 if(reference != null) {
    343                      Element argument_element = (Element) reference.get();
    344                      if(argument_element != null) {
    345                           cache_hit++;
    346                           result = MSMUtils.getValue(argument_element);
    347                      }
    348                 }
     341        SoftReference reference = (SoftReference) get(property + general);
     342        if(reference != null) {
     343        Element argument_element = (Element) reference.get();
     344        if(argument_element != null) {
     345            cache_hit++;
     346            result = MSMUtils.getValue(argument_element);
     347        }
     348        }
    349349                // We may have missed in the cache, or the reference may have been consumed.
    350                 if(result.length() == 0) {
    351                      cache_miss++;
    352                      // Locate the appropriate element
    353                      Element document_element = null;
    354                      if(general) {
    355                           document_element = general_config.getDocumentElement();
    356                      }
    357                      else if(collection_config != null) {
    358                           document_element = collection_config.getDocumentElement();
    359                      }
    360                      if(document_element != null) {
    361                           // Retrieve the Gatherer element
    362                           Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG);
    363                           NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT);
    364                           for(int i = 0; result.length() == 0 && i < arguments.getLength(); i++) {
    365                                 Element argument_element = (Element) arguments.item(i);
    366                                 if(argument_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) {
    367                                     result = MSMUtils.getValue(argument_element);
    368                                     // 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.
    369                                     put(property + general, new SoftReference(argument_element));
    370                                 }
    371                           }
    372                      }
    373                 }
    374           }
    375           catch (Exception error) {
    376                 Gatherer.printStackTrace(error);
    377           }
    378           // If we still have no result, and the search was made in the collection configuration, retrieve the general one instead.
    379           if(result.length() == 0 && !general) {
    380                 result = getString(property, true);
    381           }
    382           return result;
    383     }
    384 
    385     /** Retrieve the path to the PERL scripts within the Greenstone directory.
    386       * @return A <strong>String</strong> containing the path.
    387       */
    388     public String getScriptPath() {
    389           return gsdl_path + "bin" + File.separator + "script" + File.separator;
    390     }
    391 
    392     /** Export the general configuration to file. */
    393     public void save() {
    394           ///ystem.err.println("Hits " + cache_hit + " vs Misses " + cache_miss);
    395           Utility.export(general_config, Utility.BASE_DIR + CONFIG_XML);
    396     }
    397 
    398     /** Set the named property, from the specified configuration, using the given boolean value. */
    399     public void set(String property, boolean general, boolean value) {
    400           setString(property, general, (value ? "true" : "false"));
    401     }
    402 
    403     /** Add a subtree of argument information to the other arguments part of the general configuration. This overwrites any such existing subtree. */
    404     public void setArguments(Element arguments_element) {
    405           try {
    406                 Element document_element = general_config.getDocumentElement();
    407                 Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER);
     350        if(result.length() == 0) {
     351        cache_miss++;
     352        // Locate the appropriate element
     353        Element document_element = null;
     354        if(general) {
     355            document_element = general_config.getDocumentElement();
     356        }
     357        else if(collection_config != null) {
     358            document_element = collection_config.getDocumentElement();
     359        }
     360        if(document_element != null) {
     361            // Retrieve the Gatherer element
     362            Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG);
     363            NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT);
     364            for(int i = 0; result.length() == 0 && i < arguments.getLength(); i++) {
     365            Element argument_element = (Element) arguments.item(i);
     366            if(argument_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) {
     367                result = MSMUtils.getValue(argument_element);
     368                // 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.
     369                put(property + general, new SoftReference(argument_element));
     370            }
     371            }
     372        }
     373        }
     374    }
     375    catch (Exception error) {
     376        Gatherer.printStackTrace(error);
     377    }
     378    // If we still have no result, and the search was made in the collection configuration, retrieve the general one instead.
     379    if(result.length() == 0 && !general) {
     380        result = getString(property, true);
     381    }
     382    return result;
     383    }
     384
     385    /** Retrieve the path to the PERL scripts within the Greenstone directory.
     386     * @return A <strong>String</strong> containing the path.
     387     */
     388    public String getScriptPath() {
     389    return gsdl_path + "bin" + File.separator + "script" + File.separator;
     390    }
     391
     392    /** Export the general configuration to file. */
     393    public void save() {
     394    ///ystem.err.println("Hits " + cache_hit + " vs Misses " + cache_miss);
     395    Utility.export(general_config, Utility.BASE_DIR + CONFIG_XML);
     396    }
     397
     398    /** Set the named property, from the specified configuration, using the given boolean value. */
     399    public void set(String property, boolean general, boolean value) {
     400    setString(property, general, (value ? "true" : "false"));
     401    }
     402
     403    /** Add a subtree of argument information to the other arguments part of the general configuration. This overwrites any such existing subtree. */
     404    public void setArguments(Element arguments_element) {
     405    try {
     406        Element document_element = general_config.getDocumentElement();
     407        Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER);
    408408                // Retrieve the name of the information
    409                 Element arguments_name_element = (Element)MSMUtils.getNodeFromNamed(arguments_element, NAME);
    410                 String filename = MSMUtils.getValue(arguments_element);
     409        Element arguments_name_element = (Element)MSMUtils.getNodeFromNamed(arguments_element, NAME);
     410        String filename = MSMUtils.getValue(arguments_element);
    411411                // Find any argument information subtree starting with the same name
    412                 Element obsolete_arguments_element = getArguments(filename);
     412        Element obsolete_arguments_element = getArguments(filename);
    413413                // Create a copy of the arguments_element within our tree (import).
    414                 Element our_arguments_element = (Element) general_config.importNode(arguments_element, true);
     414        Element our_arguments_element = (Element) general_config.importNode(arguments_element, true);
    415415                // Now we insert this new node into the tree. If a previous node exists we replace it instead.
    416                 if(obsolete_arguments_element == null) {
    417                      other_element.appendChild(our_arguments_element);
    418                 }
    419                 else {
    420                      other_element.replaceChild(our_arguments_element, obsolete_arguments_element);
    421                 }
    422                 our_arguments_element = null;
    423                 obsolete_arguments_element = null;
    424                 filename = null;
    425                 arguments_name_element = null;
    426                 other_element = null;
    427                 document_element = null;
    428           }
    429           catch (Exception error) {
    430                 Gatherer.println("Error in Configuration.setArguments(): " + error);
    431                 Gatherer.printStackTrace(error);
    432           }
    433     }
    434 
    435     /** Set the collection configuration. */
    436     public void setCollectionConfiguration(Document collection_config) {
    437           this.collection_config = collection_config;
    438           updateUI();
    439           ///atherer.println("Collection configuration set.");
    440     }
    441 
    442     /** Set the named property, from the specified configuration, using the given Color value. */
    443     public void setColor(String property, boolean general, Color value) {
    444           StringBuffer text = new StringBuffer("");
    445           text.append(value.getRed());
    446           text.append(", ");
    447           text.append(value.getGreen());
    448           text.append(", ");
    449           text.append(value.getBlue());
    450           setString(property, general, text.toString());
    451     }
    452 
    453     /** Set the named property, from the specified configuration, using the given Dimension value. */
    454     public void setDimension(String property, boolean general, Dimension value) {
    455           StringBuffer text = new StringBuffer("");
    456           text.append(value.width);
    457           text.append(", ");
    458           text.append(value.height);
    459           setString(property, general, text.toString());
    460     }
    461 
    462     /** Set the named property, from the specified configuration, using the given Font value. */
    463     public void setFont(String property, boolean general, Font value) {
    464           StringBuffer text = new StringBuffer("");
    465           text.append(value.getName());
    466           text.append(", ");
    467           switch(value.getStyle()) {
    468           case Font.BOLD:
    469                 text.append("BOLD");
    470                 break;
    471           case Font.ITALIC:
    472                 text.append("ITALIC");
    473                 break;
    474           default:
    475                 text.append("PLAIN");
    476           }
    477           text.append(", ");
    478           text.append(value.getSize());
    479           setString(property, general, text.toString());
    480     }
    481 
    482     /** Set the named property, from the specified configuration, using the given integer value. */
    483     public void setInt(String property, boolean general, int value) {
    484           setString(property, general, String.valueOf(value));
    485     }
    486 
    487     /** Set the named property, from the specified configuration, using the given Locale value. */
    488     public void setLocale(String property, boolean general, Locale value) {
    489           StringBuffer text = new StringBuffer("");
    490           text.append(value.getLanguage());
    491           text.append("_");
    492           text.append(value.getCountry());
    493           setString(property, general, text.toString());
    494     }
    495 
    496     /** Sets the value of the named property argument using the given string. */
    497     public void setString(String property, boolean general, String value) {
    498           ///atherer.println("Set configuration property: " + property + " = " + value + (general ? "" : " [Collection]"));
    499           try {
    500                 Document document = null;
    501                 if(general) {
    502                      document = general_config;
    503                 }
    504                 else if(collection_config != null) {
    505                      document = collection_config;
    506                 }
    507                 if(document != null) {
    508                      Element argument_element = null;
    509                      // Try to retrieve from cache
    510                      SoftReference reference = (SoftReference) get(property + general);
    511                      if(reference != null) {
    512                           argument_element = (Element) reference.get();
    513                      }
    514                      if(argument_element == null) {
    515                           Element document_element = document.getDocumentElement();
    516                           Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG);
    517                           NodeList arguments = document_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT);
    518                           boolean found = false;
    519                           for(int i = 0; argument_element == null && i < arguments.getLength(); i++) {
    520                                 Element possible_element = (Element) arguments.item(i);
    521                                 if(possible_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) {
    522                                     argument_element = possible_element;
    523                                 }
    524                           }
    525                           // If argument element is still null, create it in the target document.
    526                           if(argument_element == null) {
    527                                 argument_element = document.createElement(GATHERER_CONFIG_ARGUMENT);
    528                                 argument_element.setAttribute(ARGUMENT_NAME, property);
    529                                 gatherer_element.appendChild(argument_element);
    530                           }
    531                           // Update cache
    532                           put(property + general, new SoftReference(argument_element));
    533 
    534                      }
    535                      if(value == null) {
    536                           value = "";
    537                      }
    538                      // Now remove any current text node children.
    539                      NodeList children = argument_element.getChildNodes();
    540                      for(int i = 0; i < children.getLength(); i++) {
    541                           argument_element.removeChild(children.item(i));
    542                      }
    543                      // Add a new text node child with the new value
    544                      argument_element.appendChild(document.createTextNode(value));
    545                 }
    546           }
    547           catch (Exception error) {
    548           }
    549     }
    550 
    551     private void updateUI() {
    552           // Buttons
    553           UIManager.put("Button.select", new ColorUIResource(getColor("coloring.button_selected_background", false)));
    554           UIManager.put("Button.background", new ColorUIResource(getColor("coloring.button_background", false)));
    555           UIManager.put("Button.foreground", new ColorUIResource(getColor("coloring.button_foreground", false)));
    556           UIManager.put("ToggleButton.background", new ColorUIResource(getColor("coloring.button_background", false)));
    557           UIManager.put("ToggleButton.foreground", new ColorUIResource(getColor("coloring.button_foreground", false)));
    558           UIManager.put("ToggleButton.select", new ColorUIResource(getColor("coloring.button_selected_background", false)));
    559 
    560           // All the things with a lovely Collection green background
    561           UIManager.put("OptionPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
    562           UIManager.put("Panel.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
    563           UIManager.put("Label.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
    564           UIManager.put("TabbedPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
    565           UIManager.put("SplitPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
    566           UIManager.put("CheckBox.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
    567 
    568           // Editable coloring
    569           UIManager.put("ComboBox.background", new ColorUIResource(getColor("coloring.button_background", false))); // Indicate clickable
    570           UIManager.put("Tree.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
    571           UIManager.put("Tree.textBackground", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
    572           UIManager.put("ProgressBar.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
    573           UIManager.put("TextArea.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
    574           UIManager.put("TextField.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
    575           UIManager.put("Table.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
    576 
    577           // Selection color
    578           UIManager.put("TabbedPane.selected", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
    579           UIManager.put("Tree.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
    580           UIManager.put("ComboBox.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
    581           UIManager.put("ProgressBar.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
    582           UIManager.put("TextArea.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
    583           UIManager.put("TextField.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
    584 
    585           // Scroll bar stuff
    586           UIManager.put("ScrollBar.background", new ColorUIResource(getColor("coloring.scrollbar_background", false)));
    587           UIManager.put("ScrollBar.thumb", new ColorUIResource(getColor("coloring.scrollbar_foreground", false)));
    588           if(Gatherer.g_man != null) {
    589                 JPanel pane = (JPanel) Gatherer.g_man.getContentPane();
    590                 pane.updateUI();
     416        if(obsolete_arguments_element == null) {
     417        other_element.appendChild(our_arguments_element);
     418        }
     419        else {
     420        other_element.replaceChild(our_arguments_element, obsolete_arguments_element);
     421        }
     422        our_arguments_element = null;
     423        obsolete_arguments_element = null;
     424        filename = null;
     425        arguments_name_element = null;
     426        other_element = null;
     427        document_element = null;
     428    }
     429    catch (Exception error) {
     430        Gatherer.println("Error in Configuration.setArguments(): " + error);
     431        Gatherer.printStackTrace(error);
     432    }
     433    }
     434
     435    /** Set the collection configuration. */
     436    public void setCollectionConfiguration(Document collection_config) {
     437    this.collection_config = collection_config;
     438    updateUI();
     439    ///atherer.println("Collection configuration set.");
     440    }
     441
     442    /** Set the named property, from the specified configuration, using the given Color value. */
     443    public void setColor(String property, boolean general, Color value) {
     444    StringBuffer text = new StringBuffer("");
     445    text.append(value.getRed());
     446    text.append(", ");
     447    text.append(value.getGreen());
     448    text.append(", ");
     449    text.append(value.getBlue());
     450    setString(property, general, text.toString());
     451    }
     452
     453    /** Set the named property, from the specified configuration, using the given Dimension value. */
     454    public void setDimension(String property, boolean general, Dimension value) {
     455    StringBuffer text = new StringBuffer("");
     456    text.append(value.width);
     457    text.append(", ");
     458    text.append(value.height);
     459    setString(property, general, text.toString());
     460    }
     461
     462    /** Set the named property, from the specified configuration, using the given Font value. */
     463    public void setFont(String property, boolean general, Font value) {
     464    StringBuffer text = new StringBuffer("");
     465    text.append(value.getName());
     466    text.append(", ");
     467    switch(value.getStyle()) {
     468    case Font.BOLD:
     469        text.append("BOLD");
     470        break;
     471    case Font.ITALIC:
     472        text.append("ITALIC");
     473        break;
     474    default:
     475        text.append("PLAIN");
     476    }
     477    text.append(", ");
     478    text.append(value.getSize());
     479    setString(property, general, text.toString());
     480    }
     481
     482    /** Set the named property, from the specified configuration, using the given integer value. */
     483    public void setInt(String property, boolean general, int value) {
     484    setString(property, general, String.valueOf(value));
     485    }
     486
     487    /** Set the named property, from the specified configuration, using the given Locale value. */
     488    public void setLocale(String property, boolean general, Locale value) {
     489    StringBuffer text = new StringBuffer("");
     490    text.append(value.getLanguage());
     491    text.append("_");
     492    text.append(value.getCountry());
     493    setString(property, general, text.toString());
     494    }
     495
     496    /** Sets the value of the named property argument using the given string. */
     497    public void setString(String property, boolean general, String value) {
     498    ///atherer.println("Set configuration property: " + property + " = " + value + (general ? "" : " [Collection]"));
     499    try {
     500        Document document = null;
     501        if(general) {
     502        document = general_config;
     503        }
     504        else if(collection_config != null) {
     505        document = collection_config;
     506        }
     507        if(document != null) {
     508        Element argument_element = null;
     509        // Try to retrieve from cache
     510        SoftReference reference = (SoftReference) get(property + general);
     511        if(reference != null) {
     512            argument_element = (Element) reference.get();
     513        }
     514        if(argument_element == null) {
     515            Element document_element = document.getDocumentElement();
     516            Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG);
     517            NodeList arguments = document_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT);
     518            boolean found = false;
     519            for(int i = 0; argument_element == null && i < arguments.getLength(); i++) {
     520            Element possible_element = (Element) arguments.item(i);
     521            if(possible_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) {
     522                argument_element = possible_element;
     523            }
     524            }
     525            // If argument element is still null, create it in the target document.
     526            if(argument_element == null) {
     527            argument_element = document.createElement(GATHERER_CONFIG_ARGUMENT);
     528            argument_element.setAttribute(ARGUMENT_NAME, property);
     529            gatherer_element.appendChild(argument_element);
     530            }
     531            // Update cache
     532            put(property + general, new SoftReference(argument_element));
     533
     534        }
     535        if(value == null) {
     536            value = "";
     537        }
     538        // Now remove any current text node children.
     539        NodeList children = argument_element.getChildNodes();
     540        for(int i = 0; i < children.getLength(); i++) {
     541            argument_element.removeChild(children.item(i));
     542        }
     543        // Add a new text node child with the new value
     544        argument_element.appendChild(document.createTextNode(value));
     545        }
     546    }
     547    catch (Exception error) {
     548    }
     549    }
     550
     551    private void updateUI() {
     552    // Buttons
     553    UIManager.put("Button.select", new ColorUIResource(getColor("coloring.button_selected_background", false)));
     554    UIManager.put("Button.background", new ColorUIResource(getColor("coloring.button_background", false)));
     555    UIManager.put("Button.foreground", new ColorUIResource(getColor("coloring.button_foreground", false)));
     556    UIManager.put("ToggleButton.background", new ColorUIResource(getColor("coloring.button_background", false)));
     557    UIManager.put("ToggleButton.foreground", new ColorUIResource(getColor("coloring.button_foreground", false)));
     558    UIManager.put("ToggleButton.select", new ColorUIResource(getColor("coloring.button_selected_background", false)));
     559
     560    // All the things with a lovely Collection green background
     561    UIManager.put("OptionPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
     562    UIManager.put("Panel.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
     563    UIManager.put("Label.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
     564    UIManager.put("TabbedPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
     565    UIManager.put("SplitPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
     566    UIManager.put("CheckBox.background", new ColorUIResource(getColor("coloring.collection_heading_background", false)));
     567
     568    // Editable coloring
     569    UIManager.put("ComboBox.background", new ColorUIResource(getColor("coloring.button_background", false))); // Indicate clickable
     570    UIManager.put("Tree.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
     571    UIManager.put("Tree.textBackground", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
     572    UIManager.put("ProgressBar.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
     573    UIManager.put("TextArea.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
     574    UIManager.put("TextField.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
     575    UIManager.put("Table.background", new ColorUIResource(getColor("coloring.collection_tree_background", false)));
     576
     577    // Selection color
     578    UIManager.put("TabbedPane.selected", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
     579    UIManager.put("Tree.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
     580    UIManager.put("ComboBox.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
     581    UIManager.put("ProgressBar.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
     582    UIManager.put("TextArea.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
     583    UIManager.put("TextField.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false)));
     584
     585    // Scroll bar stuff
     586    UIManager.put("ScrollBar.background", new ColorUIResource(getColor("coloring.scrollbar_background", false)));
     587    UIManager.put("ScrollBar.thumb", new ColorUIResource(getColor("coloring.scrollbar_foreground", false)));
     588    if(Gatherer.g_man != null) {
     589        JPanel pane = (JPanel) Gatherer.g_man.getContentPane();
     590        pane.updateUI();
    591591                // Also update all of the tabs according to workflow.
    592                 Gatherer.g_man.workflowUpdate("Browser", get("workflow.browse", false));
    593                 Gatherer.g_man.workflowUpdate("Mirroring", get("workflow.mirror", false));
    594                 Gatherer.g_man.workflowUpdate("Collection", get("workflow.gather", false));
    595                 Gatherer.g_man.workflowUpdate("MetaEdit", get("workflow.enrich", false));
    596                 Gatherer.g_man.workflowUpdate("Build", get("workflow.design", false));
    597                 Gatherer.g_man.workflowUpdate("Export", get("workflow.export", false));
    598                 Gatherer.g_man.workflowUpdate("Create", get("workflow.create", false));
    599                 Gatherer.g_man.workflowUpdate("Preview", get("workflow.preview", false));
    600           }
    601     }
     592        Gatherer.g_man.workflowUpdate("Browser", get("workflow.browse", false));
     593        Gatherer.g_man.workflowUpdate("Mirroring", get("workflow.mirror", false));
     594        Gatherer.g_man.workflowUpdate("Collection", get("workflow.gather", false));
     595        Gatherer.g_man.workflowUpdate("MetaEdit", get("workflow.enrich", false));
     596        Gatherer.g_man.workflowUpdate("Build", get("workflow.design", false));
     597        Gatherer.g_man.workflowUpdate("Export", get("workflow.export", false));
     598        Gatherer.g_man.workflowUpdate("Create", get("workflow.create", false));
     599        Gatherer.g_man.workflowUpdate("Preview", get("workflow.preview", false));
     600    }
     601    }
    602602}
  • trunk/gli/src/org/greenstone/gatherer/Dictionary.java

    r4293 r4363  
    6868 */
    6969public class Dictionary
    70     extends HashMap {
    71     /** A String which more explicitly states the Locale of this dictionary. */
    72     public String language = null;
    73     /** A static reference to ourself. */
    74     static public Dictionary self;
    75     /** The font used when displaying various html text. */
    76     private FontUIResource font = null;
    77     /** A reference to remind us of the current locale. */
    78     private Locale locale = null;
    79     /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/
    80     private ResourceBundle dictionary = null;
    81     /**  Constructs the Dictionary class by first checking if a Locale has been set. If not the default locale is used, and a ResourceBundle is created. Finally a single important String, Language, is made available outside the class so a more read-able version of the Locale of this Dictionary is present.
    82       * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle.
     70    extends HashMap {
     71    /** A String which more explicitly states the Locale of this dictionary. */
     72    public String language = null;
     73    /** A static reference to ourself. */
     74    static public Dictionary self;
     75    /** The font used when displaying various html text. */
     76    private FontUIResource font = null;
     77    /** A reference to remind us of the current locale. */
     78    private Locale locale = null;
     79    /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/
     80    private ResourceBundle dictionary = null;
     81    /**  Constructs the Dictionary class by first checking if a Locale has been set. If not the default locale is used, and a ResourceBundle is created. Finally a single important String, Language, is made available outside the class so a more read-able version of the Locale of this Dictionary is present.
     82     * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle.
    8383      */
    84     public Dictionary(Locale locale, FontUIResource font) {
    85           super();
    86           this.self = this;
    87           // Initialize.
    88           this.font = font;
    89           if(locale == null) {
    90                 this.locale = Locale.getDefault();
    91           }
    92           else {
    93                 this.locale = locale;
    94                 Locale.setDefault(locale);
    95           }
    96           dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale);
    97           // Now quickly read in language name.
    98           language = dictionary.getString("Language");
    99     }
    100     /** Change the currently loaded dictionary and update registered (ie dynamic) components as possible. */
    101     public void changeDictionary(Locale locale) {
    102           this.locale = locale;
    103           // Load new dictionary
    104           dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
    105           // Refresh all registered component
    106           for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
    107                 Object component = keys.next();
    108                 String[] args = (String[]) get(component);
    109                 if(component instanceof AbstractButton) {
    110                      register((AbstractButton)component, args, true);
    111                 }
    112                 else if(component instanceof JComboBox) {
    113                      register((JComboBox)component, args, true);
    114                 }
    115                 else if(component instanceof JDialog) {
    116                      register((JDialog)component, args, true);
    117                 }
    118                 else if(component instanceof JFrame) {
    119                      register((JFrame)component, args, true);
    120                 }
    121                 else if(component instanceof JLabel) {
    122                      register((JLabel)component, args, true);
    123                 }
    124                 else if(component instanceof JTabbedPane) {
    125                      register((JTabbedPane)component, args, true);
    126                 }
    127                 else if(component instanceof JTextComponent) {
    128                      register((JTextComponent)component, args, true);
    129                 }
    130                 else if(component instanceof JTree) {
    131                      register((JTree)component, args, true);
    132                 }
    133                 else if(component instanceof TitledBorder) {
    134                      register((TitledBorder)component, args, true);
    135                 }
    136                 args = null;
    137                 component = null;
    138           }
    139     }
    140     /** Remove the component from our registered components list. */
    141     public void deregister(Object component) {
    142           remove(component);
    143     }
     84    public Dictionary(Locale locale, FontUIResource font) {
     85    super();
     86    this.self = this;
     87    // Initialize.
     88    this.font = font;
     89    if(locale == null) {
     90        this.locale = Locale.getDefault();
     91    }
     92    else {
     93        this.locale = locale;
     94        Locale.setDefault(locale);
     95    }
     96    dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale);
     97    // Now quickly read in language name.
     98    language = dictionary.getString("Language");
     99    }
     100    /** Change the currently loaded dictionary and update registered (ie dynamic) components as possible. */
     101    public void changeDictionary(Locale locale) {
     102    this.locale = locale;
     103    // Load new dictionary
     104    dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
     105    // Refresh all registered component
     106    for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
     107        Object component = keys.next();
     108        String[] args = (String[]) get(component);
     109        if(component instanceof AbstractButton) {
     110        register((AbstractButton)component, args, true);
     111        }
     112        else if(component instanceof JComboBox) {
     113        register((JComboBox)component, args, true);
     114        }
     115        else if(component instanceof JDialog) {
     116        register((JDialog)component, args, true);
     117        }
     118        else if(component instanceof JFrame) {
     119        register((JFrame)component, args, true);
     120        }
     121        else if(component instanceof JLabel) {
     122        register((JLabel)component, args, true);
     123        }
     124        else if(component instanceof JTabbedPane) {
     125        register((JTabbedPane)component, args, true);
     126        }
     127        else if(component instanceof JTextComponent) {
     128        register((JTextComponent)component, args, true);
     129        }
     130        else if(component instanceof JTree) {
     131        register((JTree)component, args, true);
     132        }
     133        else if(component instanceof TitledBorder) {
     134        register((TitledBorder)component, args, true);
     135        }
     136        args = null;
     137        component = null;
     138    }
     139    }
     140    /** Remove the component from our registered components list. */
     141    public void deregister(Object component) {
     142    remove(component);
     143    }
    144144
    145     /** Overloaded to call get with both a key and an empty argument array.
     145    /** Overloaded to call get with both a key and an empty argument array.
    146146      * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
    147147      * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
    148148      */
    149     public String get(String key) {
    150           return get(key, (String[])null);
    151     }
    152     /** Convienence method with transforms the second string argument into a string array. */
    153     public String get(String key, String arg) {
    154           String[] args = new String[1];
    155           args[0] = arg;
    156           return get(key, args);
    157     }
    158     /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR>
     149    public String get(String key) {
     150    return get(key, (String[])null);
     151    }
     152    /** Convienence method with transforms the second string argument into a string array. */
     153    public String get(String key, String arg) {
     154    String[] args = new String[1];
     155    args[0] = arg;
     156    return get(key, args);
     157    }
     158    /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR>
    159159      * Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {<I>n</I>}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Farg<I>n</I>.
    160160      * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
     
    162162      * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
    163163      */
    164     public String get(String key, String args[]) {
    165           try {
    166                 String initial = dictionary.getString(key);
     164    public String get(String key, String args[]) {
     165    try {
     166        String initial = dictionary.getString(key);
    167167                // If the string contains arguments we have to insert them.
    168                 String complete = "";
     168        String complete = "";
    169169                // While we still have initial string left.
    170                 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
    171                      // Remove preamble
    172                      int opening = initial.indexOf('{');
    173                      int closing = initial.indexOf('}');
    174                      complete = complete + initial.substring(0, opening);
    175                      // Parse arg_num
    176                      String arg_str = initial.substring(opening + 1, closing);
    177                      int arg_num = Integer.parseInt(arg_str);
    178                      if(closing + 1 < initial.length()) {
    179                           initial = initial.substring(closing + 1);
    180                      }
    181                      else {
    182                           initial = "";
    183                      }
    184                      // Insert argument
    185                      if(args != null && 0 <= arg_num && arg_num < args.length) {
    186                           complete = complete + args[arg_num];
    187                      }
    188                      else if(arg_num >= 32) {
    189                           String f_subargs[] = new String[1];
    190                           if(font != null) {
    191                                 f_subargs[0] = font.getFontName();
    192                           }
    193                           else {
    194                                 f_subargs[0] = "Arial";
    195                           }
    196                           complete = complete + get("Farg" + arg_num, f_subargs);
    197                      }
    198                 }
    199                return complete + initial;
    200           }
    201           catch (Exception e) {
    202                 if(!key.endsWith("_Tooltip")) {
    203                      Gatherer.println("Missing value for key: " + key);
    204                 }
    205                 return key;
    206           }
    207     }
    208     /** Retrieve the two letter code of the current language we are using, according to the stored locale.
     170        while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
     171        // Remove preamble
     172        int opening = initial.indexOf('{');
     173        int closing = initial.indexOf('}');
     174        complete = complete + initial.substring(0, opening);
     175        // Parse arg_num
     176        String arg_str = initial.substring(opening + 1, closing);
     177        int arg_num = Integer.parseInt(arg_str);
     178        if(closing + 1 < initial.length()) {
     179            initial = initial.substring(closing + 1);
     180        }
     181        else {
     182            initial = "";
     183        }
     184        // Insert argument
     185        if(args != null && 0 <= arg_num && arg_num < args.length) {
     186            complete = complete + args[arg_num];
     187        }
     188        else if(arg_num >= 32) {
     189            String f_subargs[] = new String[1];
     190            if(font != null) {
     191            f_subargs[0] = font.getFontName();
     192            }
     193            else {
     194            f_subargs[0] = "Arial";
     195            }
     196            complete = complete + get("Farg" + arg_num, f_subargs);
     197        }
     198        }
     199        return complete + initial;
     200    }
     201    catch (Exception e) {
     202        if(!key.endsWith("_Tooltip")) {
     203        Gatherer.println("Missing value for key: " + key);
     204        }
     205        return key;
     206    }
     207    }
     208    /** Retrieve the two letter code of the current language we are using, according to the stored locale.
    209209      * @return A <strong>String</strong> containing the two letter ISO639 language code.
    210210      */
    211     public String getLanguage() {
    212           return locale.getLanguage();
    213     }
    214     /** Register an abstract button component. */
    215     public void register(AbstractButton component, String[] args, boolean already_registered) {
    216           if(component != null) {
     211    public String getLanguage() {
     212    return locale.getLanguage();
     213    }
     214    /** Register an abstract button component. */
     215    public void register(AbstractButton component, String[] args, boolean already_registered) {
     216    if(component != null) {
    217217                // Determine the key
    218                 String key = "";
    219                 if(!already_registered) {
    220                      key = component.getText();
    221                 }
    222                 else {
    223                      key = args[args.length - 1];
    224                 }
     218        String key = "";
     219        if(!already_registered) {
     220        key = component.getText();
     221        }
     222        else {
     223        key = args[args.length - 1];
     224        }
    225225                // Update the component using the AWTEvent queue
    226                 String value = get(key, args);
    227                 String tooltip = get(key + "_Tooltip", (String[])null);
    228                 ChangeTask task = new AbstractButtonChangeTask(component, key, value, tooltip);
    229                 SwingUtilities.invokeLater(task);
     226        String value = get(key, args);
     227        String tooltip = get(key + "_Tooltip", (String[])null);
     228        ChangeTask task = new AbstractButtonChangeTask(component, key, value, tooltip);
     229        SwingUtilities.invokeLater(task);
    230230                // Register as necessary
    231                 if(!already_registered) {
    232                      args = ArrayTools.add(args, key);
    233                      put(component, args);
    234                 }
    235           }
    236     }
    237     /** Register a combobox component. */
    238     public void register(JComboBox component, String[] args, boolean already_registered) {
    239           if(component != null) {
     231        if(!already_registered) {
     232        args = ArrayTools.add(args, key);
     233        put(component, args);
     234        }
     235    }
     236    }
     237    /** Register a combobox component. */
     238    public void register(JComboBox component, String[] args, boolean already_registered) {
     239    if(component != null) {
    240240                // If not already registered then args will be null.
    241                 if(!already_registered) {
    242                      args = new String[component.getItemCount()];
    243                 }
     241        if(!already_registered) {
     242        args = new String[component.getItemCount()];
     243        }
    244244                // Retrieve the tooltip. The key is mostly derived from the comboboxes name.
    245                 String key = component.getName();
    246                 String tooltip = get(key + "_Tooltip", (String[])null);
    247                 ChangeTask task = new JComboBoxChangeTask(component, key, -1, tooltip);
    248                 SwingUtilities.invokeLater(task);
     245        String key = component.getName();
     246        String tooltip = get(key + "_Tooltip", (String[])null);
     247        ChangeTask task = new JComboBoxChangeTask(component, key, -1, tooltip);
     248        SwingUtilities.invokeLater(task);
    249249                // Iterate through the combobox, updating values and recording the original key of each item in args.
    250                 for(int i = 0; i < args.length; i++) {
    251                      if(args[i] == null) {
    252                           args[i] = component.getItemAt(i).toString();
    253                      }
    254                      String value = get(args[i], (String[])null);
    255                      task = new JComboBoxChangeTask(component, key, i, value);
    256                      SwingUtilities.invokeLater(task);
    257                 }
     250        for(int i = 0; i < args.length; i++) {
     251        if(args[i] == null) {
     252            args[i] = component.getItemAt(i).toString();
     253        }
     254        String value = get(args[i], (String[])null);
     255        task = new JComboBoxChangeTask(component, key, i, value);
     256        SwingUtilities.invokeLater(task);
     257        }
    258258                // Register if necessary
    259                 if(!already_registered) {
    260                      put(component, args);
    261                 }
    262           }
    263     }
    264     /** Register a dialog component. */
    265     public void register(JDialog component, String[] args, boolean already_registered) {
    266           if(component != null) {
     259        if(!already_registered) {
     260        put(component, args);
     261        }
     262    }
     263    }
     264    /** Register a dialog component. */
     265    public void register(JDialog component, String[] args, boolean already_registered) {
     266    if(component != null) {
    267267                // Determine the key
    268                 String key = "";
    269                 if(!already_registered) {
    270                      key = component.getTitle();
    271                 }
    272                 else {
    273                      key = args[args.length - 1];
    274                 }
     268        String key = "";
     269        if(!already_registered) {
     270        key = component.getTitle();
     271        }
     272        else {
     273        key = args[args.length - 1];
     274        }
    275275                // Update the component using the AWTEvent queue
    276                 String value = get(key, args);
    277                 ChangeTask task = new JDialogChangeTask(component, key, value);
    278                 SwingUtilities.invokeLater(task);
     276        String value = get(key, args);
     277        ChangeTask task = new JDialogChangeTask(component, key, value);
     278        SwingUtilities.invokeLater(task);
    279279                // Register as necessary
    280                 if(!already_registered) {
    281                      args = ArrayTools.add(args, key);
    282                      put(component, args);
    283                 }
    284           }
    285     }
    286     /** Register a frame component. */
    287     public void register(JFrame component, String[] args, boolean already_registered) {
    288           if(component != null) {
     280        if(!already_registered) {
     281        args = ArrayTools.add(args, key);
     282        put(component, args);
     283        }
     284    }
     285    }
     286    /** Register a frame component. */
     287    public void register(JFrame component, String[] args, boolean already_registered) {
     288    if(component != null) {
    289289                // Determine the key
    290                 String key = "";
    291                 if(!already_registered) {
    292                      key = component.getTitle();
    293                 }
    294                 else {
    295                      key = args[args.length - 1];
    296                 }
     290        String key = "";
     291        if(!already_registered) {
     292        key = component.getTitle();
     293        }
     294        else {
     295        key = args[args.length - 1];
     296        }
    297297                // Update the component using the AWTEvent queue
    298                 String value = get(key, args);
    299                 ChangeTask task = new JFrameChangeTask(component, key, value);
    300                 SwingUtilities.invokeLater(task);
     298        String value = get(key, args);
     299        ChangeTask task = new JFrameChangeTask(component, key, value);
     300        SwingUtilities.invokeLater(task);
    301301                // Register as necessary
    302                 if(!already_registered) {
    303                      args = ArrayTools.add(args, key);
    304                      put(component, args);
    305                 }
    306           }
    307     }
    308     /** Register a label component. */
    309     public void register(JLabel component, String[] args, boolean already_registered) {
    310           if(component != null) {
     302        if(!already_registered) {
     303        args = ArrayTools.add(args, key);
     304        put(component, args);
     305        }
     306    }
     307    }
     308    /** Register a label component. */
     309    public void register(JLabel component, String[] args, boolean already_registered) {
     310    if(component != null) {
    311311                // Determine the key
    312                 String key = "";
    313                 if(!already_registered) {
    314                      key = component.getText();
    315                 }
    316                 else {
    317                      key = args[args.length - 1];
    318                 }
     312        String key = "";
     313        if(!already_registered) {
     314        key = component.getText();
     315        }
     316        else {
     317        key = args[args.length - 1];
     318        }
    319319                // Update the component using the AWTEvent queue
    320                 String value = get(key, args);
    321                 ChangeTask task = new JLabelChangeTask(component, key, value);
    322                 SwingUtilities.invokeLater(task);
     320        String value = get(key, args);
     321        ChangeTask task = new JLabelChangeTask(component, key, value);
     322        SwingUtilities.invokeLater(task);
    323323                // Register as necessary
    324                 if(!already_registered) {
    325                      args = ArrayTools.add(args, key);
    326                      put(component, args);
    327                 }
    328           }
    329     }
    330     /** Register a tab pane component. */
    331     public void register(JTabbedPane component, String[] args, boolean already_registered) {
    332           if(component != null) {
     324        if(!already_registered) {
     325        args = ArrayTools.add(args, key);
     326        put(component, args);
     327        }
     328    }
     329    }
     330    /** Register a tab pane component. */
     331    public void register(JTabbedPane component, String[] args, boolean already_registered) {
     332    if(component != null) {
    333333                // If not already registered then args will be null.
    334                 if(!already_registered) {
    335                      args = new String[component.getTabCount()];
    336                 }
     334        if(!already_registered) {
     335        args = new String[component.getTabCount()];
     336        }
    337337                // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
    338                 for(int i = 0; i < args.length; i++) {
    339                      if(args[i] == null) {
    340                           args[i] = component.getTitleAt(i);
    341                      }
    342                      String value = get(args[i], (String[])null);
    343                      String tooltip = get(args[i] + "_Tooltip", (String[])null);
    344                      ChangeTask task = new JTabbedPaneChangeTask(component, args[i], i, value, tooltip);
    345                      SwingUtilities.invokeLater(task);
    346                 }
     338        for(int i = 0; i < args.length; i++) {
     339        if(args[i] == null) {
     340            args[i] = component.getTitleAt(i);
     341        }
     342        String value = get(args[i], (String[])null);
     343        String tooltip = get(args[i] + "_Tooltip", (String[])null);
     344        ChangeTask task = new JTabbedPaneChangeTask(component, args[i], i, value, tooltip);
     345        SwingUtilities.invokeLater(task);
     346        }
    347347                // Register if necessary
    348                 if(!already_registered) {
    349                      put(component, args);
    350                 }
    351           }
    352     }
    353     /** Register a text component. */
    354     public void register(JTextComponent component, String[] args, boolean already_registered) {
    355           if(component != null) {
     348        if(!already_registered) {
     349        put(component, args);
     350        }
     351    }
     352    }
     353    /** Register a text component. */
     354    public void register(JTextComponent component, String[] args, boolean already_registered) {
     355    if(component != null) {
    356356                // Determine the key
    357                 String key = "";
    358                 if(!already_registered) {
    359                      key = component.getText();
    360                 }
    361                 else {
    362                      key = args[args.length - 1];
    363                 }
     357        String key = "";
     358        if(!already_registered) {
     359        key = component.getText();
     360        }
     361        else {
     362        key = args[args.length - 1];
     363        }
    364364                // Update the component using the AWTEvent queue
    365                 String value = get(key, args);
    366                 String tooltip = get(key + "_Tooltip", (String[])null);
    367                 ChangeTask task = new JTextComponentChangeTask(component, key, value, tooltip);
    368                 SwingUtilities.invokeLater(task);
     365        String value = get(key, args);
     366        String tooltip = get(key + "_Tooltip", (String[])null);
     367        ChangeTask task = new JTextComponentChangeTask(component, key, value, tooltip);
     368        SwingUtilities.invokeLater(task);
    369369                // Register as necessary
    370                 if(!already_registered) {
    371                      args = ArrayTools.add(args, key);
    372                      put(component, args);
    373                 }
    374           }
    375     }
    376     /** Register a tree component. */
    377     public void register(JTree component, String[] args, boolean already_registered) {
    378           if(component != null) {
     370        if(!already_registered) {
     371        args = ArrayTools.add(args, key);
     372        put(component, args);
     373        }
     374    }
     375    }
     376    /** Register a tree component. */
     377    public void register(JTree component, String[] args, boolean already_registered) {
     378    if(component != null) {
    379379                // Retrieve the tooltip using the components name
    380                 String key = component.getName();
    381                 String tooltip = get(key + "_Tooltip", (String[])null);
    382                 ChangeTask task = new JTreeChangeTask(component, key, tooltip);
    383                 SwingUtilities.invokeLater(task);
     380        String key = component.getName();
     381        String tooltip = get(key + "_Tooltip", (String[])null);
     382        ChangeTask task = new JTreeChangeTask(component, key, tooltip);
     383        SwingUtilities.invokeLater(task);
    384384                // A tree can never be previously registered. In otherwords the keys are harvested each time. Thus for a tree to remain consistant its up to the implementer to implement DictionaryTreeNode for the tree nodes!
    385                 ArrayList nodes = new ArrayList();
    386                 nodes.add(component.getModel().getRoot());
    387                 while(nodes.size() > 0) {
    388                      DictionaryTreeNode node = (DictionaryTreeNode) nodes.remove(0);
    389                      // Update
    390                      String value = get(node.getKey(), (String[])null);
    391                      task = new JTreeChangeTask(component, node.getKey(), node, value);
    392                      SwingUtilities.invokeLater(task);
    393                      // Add children to nodes
    394                      for(int i = 0; i < node.getChildCount(); i++) {
    395                           nodes.add(node.getChildAt(i));
    396                      }
    397                 }
    398           }
    399     }
    400     /** Register a titled border component. */
    401     public void register(TitledBorder component, String[] args, boolean already_registered) {
    402           if(component != null) {
     385        ArrayList nodes = new ArrayList();
     386        nodes.add(component.getModel().getRoot());
     387        while(nodes.size() > 0) {
     388        DictionaryTreeNode node = (DictionaryTreeNode) nodes.remove(0);
     389        // Update
     390        String value = get(node.getKey(), (String[])null);
     391        task = new JTreeChangeTask(component, node.getKey(), node, value);
     392        SwingUtilities.invokeLater(task);
     393        // Add children to nodes
     394        for(int i = 0; i < node.getChildCount(); i++) {
     395            nodes.add(node.getChildAt(i));
     396        }
     397        }
     398    }
     399    }
     400    /** Register a titled border component. */
     401    public void register(TitledBorder component, String[] args, boolean already_registered) {
     402    if(component != null) {
    403403                // Determine the key
    404                 String key = "";
    405                 if(!already_registered) {
    406                      key = component.getTitle();
    407                 }
    408                 else {
    409                      key = args[args.length - 1];
    410                 }
     404        String key = "";
     405        if(!already_registered) {
     406        key = component.getTitle();
     407        }
     408        else {
     409        key = args[args.length - 1];
     410        }
    411411                // Update the component using the AWTEvent queue
    412                 String value = get(key, args);
    413                 ChangeTask task = new TitledBorderChangeTask(component, key, value);
    414                 SwingUtilities.invokeLater(task);
     412        String value = get(key, args);
     413        ChangeTask task = new TitledBorderChangeTask(component, key, value);
     414        SwingUtilities.invokeLater(task);
    415415                // Register as necessary
    416                 if(!already_registered) {
    417                      args = ArrayTools.add(args, key);
    418                      put(component, args);
    419                 }
    420           }
    421     }
    422     /** A get method called internally by components that have been previously been registered, which means that arg[index] is the original key value. Index is usually the last entry in the array, however this is not true for comboboxes, tabbed panes and trees. */
    423     private String get(String[] args, int index) {
    424           return get(args[index], args);
    425     }
     416        if(!already_registered) {
     417        args = ArrayTools.add(args, key);
     418        put(component, args);
     419        }
     420    }
     421    }
     422    /** A get method called internally by components that have been previously been registered, which means that arg[index] is the original key value. Index is usually the last entry in the array, however this is not true for comboboxes, tabbed panes and trees. */
     423    private String get(String[] args, int index) {
     424    return get(args[index], args);
     425    }
    426426
    427     private abstract class ChangeTask
    428           implements Runnable {
    429           protected String key;
    430           protected String value;
    431           public ChangeTask(String key, String value) {
    432                 this.key = key;
    433                 this.value = value;
    434           }
    435           public void run() {
    436           }
    437     }
    438     /** Update the text and tooltip for this button. */
    439     private class AbstractButtonChangeTask
    440           extends ChangeTask {
    441           private AbstractButton component;
    442           private String tooltip;
    443           public AbstractButtonChangeTask(AbstractButton component, String key, String value, String tooltip) {
    444                 super(key, value);
    445                 this.component = component;
    446                 this.tooltip = tooltip;
    447           }
    448           public void run() {
    449                 component.setText(value);
    450                 if(!tooltip.equals(key+"_Tooltip")) {
    451                      component.setToolTipText(tooltip);
    452                 }
    453                 else {
    454                      component.setToolTipText(null);
    455                 }
    456           }
    457     }
    458     /** Update the text associated with a combobox. If the index used is -1 then we are setting the tooltip for this combobox. */
    459     private class JComboBoxChangeTask
    460           extends ChangeTask {
    461           private int index;
    462           private JComboBox component;
    463           public JComboBoxChangeTask(JComboBox component, String key, int index, String value) {
    464                 super(key, value);
    465                 this.component = component;
    466                 this.index = index;
    467           }
    468           public void run() {
    469                 if(index != -1) {
    470                      try {
    471                           MutableComboBoxEntry entry = (MutableComboBoxEntry)component.getItemAt(index);
    472                           entry.setText(value);
    473                      }
    474                      catch (Exception error) {
    475                      }
    476                 }
    477                 else {
    478                      if(!value.equals(key+"_Tooltip")) {
    479                           component.setToolTipText(value);
    480                      }
    481                      else {
    482                           component.setToolTipText(null);
    483                      }
    484                 }
    485           }
    486     }
    487     /** Update the title of this dialog. */
    488     private class JDialogChangeTask
    489           extends ChangeTask {
    490           private JDialog component;
    491           public JDialogChangeTask(JDialog component, String key, String value) {
    492                 super(key, value);
    493                 this.component = component;
    494           }
    495           public void run() {
    496                 component.setTitle(value);
    497           }
    498     }
    499     /** Update the title of this frame. */
    500     private class JFrameChangeTask
    501           extends ChangeTask {
    502           private JFrame component;
    503           public JFrameChangeTask(JFrame component, String key, String value) {
    504                 super(key, value);
    505                 this.component = component;
    506           }
    507           public void run() {
    508                 component.setTitle(value);
    509           }
    510     }
    511     /** Update the text of this label. */
    512     private class JLabelChangeTask
    513           extends ChangeTask {
    514           private JLabel component;
    515           public JLabelChangeTask(JLabel component, String key, String value) {
    516                 super(key, value);
    517                 this.component = component;
    518           }
    519           public void run() {
    520                 component.setText(value);
    521           }
    522     }
    523     /** Updates a tabbed panes tab title and tooltip. */
    524     private class JTabbedPaneChangeTask
    525           extends ChangeTask {
    526           private int index;
    527           private JTabbedPane component;
    528           private String tooltip;
    529           public JTabbedPaneChangeTask(JTabbedPane component, String key, int index, String value, String tooltip) {
    530                 super(key, value);
    531                 this.component = component;
    532                 this.index = index;
    533                 this.tooltip = tooltip;
    534           }
    535           public void run() {
    536                 component.setTitleAt(index, value);
    537                 if(!tooltip.equals(key+"_Tooltip")) {
    538                      component.setToolTipTextAt(index, tooltip);
    539                 }
    540                 else {
    541                      component.setToolTipTextAt(index, null);
    542                 }
    543           }
    544     }
    545     /** Update the text and tooltip of this text component. */
    546     private class JTextComponentChangeTask
    547           extends ChangeTask {
    548           private JTextComponent component;
    549           private String tooltip;
    550           public JTextComponentChangeTask(JTextComponent component, String key, String value, String tooltip) {
    551                 super(key, value);
    552                 this.component = component;
    553                 this.tooltip = tooltip;
    554           }
    555           public void run() {
    556                 component.setText(value);
    557                 if(!tooltip.equals(key+"_Tooltip")) {
    558                      component.setToolTipText(tooltip);
    559                 }
    560                 else {
    561                      component.setToolTipText(null);
    562                 }
    563           }
    564     }
    565     /** Update the tooltip of a tree and its tree node's labels. Shouldn't really ever be used on a dynamic tree, but is quite useful for a 'contents' tree type control. */
    566     private class JTreeChangeTask
    567           extends ChangeTask {
    568           private DictionaryTreeNode node;
    569           private JTree component;
    570           public JTreeChangeTask(JTree component, String key, String value) {
    571                 super(key, value);
    572                 this.component = component;
    573           }
    574           public JTreeChangeTask(JTree component, String key, DictionaryTreeNode node, String value) {
    575                 super(key, value);
    576                 this.component = component;
    577                 this.node = node;
    578           }
    579           public void run() {
    580                 if(value != null) {
    581                      node.setText(value);
    582                      ((DefaultTreeModel)component.getModel()).nodeChanged((TreeNode)node);
    583                 }
     427    private abstract class ChangeTask
     428    implements Runnable {
     429    protected String key;
     430    protected String value;
     431    public ChangeTask(String key, String value) {
     432        this.key = key;
     433        this.value = value;
     434    }
     435    public void run() {
     436    }
     437    }
     438    /** Update the text and tooltip for this button. */
     439    private class AbstractButtonChangeTask
     440    extends ChangeTask {
     441    private AbstractButton component;
     442    private String tooltip;
     443    public AbstractButtonChangeTask(AbstractButton component, String key, String value, String tooltip) {
     444        super(key, value);
     445        this.component = component;
     446        this.tooltip = tooltip;
     447    }
     448    public void run() {
     449        component.setText(value);
     450        if(!tooltip.equals(key+"_Tooltip")) {
     451        component.setToolTipText(tooltip);
     452        }
     453        else {
     454        component.setToolTipText(null);
     455        }
     456    }
     457    }
     458    /** Update the text associated with a combobox. If the index used is -1 then we are setting the tooltip for this combobox. */
     459    private class JComboBoxChangeTask
     460    extends ChangeTask {
     461    private int index;
     462    private JComboBox component;
     463    public JComboBoxChangeTask(JComboBox component, String key, int index, String value) {
     464        super(key, value);
     465        this.component = component;
     466        this.index = index;
     467    }
     468    public void run() {
     469        if(index != -1) {
     470        try {
     471            MutableComboBoxEntry entry = (MutableComboBoxEntry)component.getItemAt(index);
     472            entry.setText(value);
     473        }
     474        catch (Exception error) {
     475        }
     476        }
     477        else {
     478        if(!value.equals(key+"_Tooltip")) {
     479            component.setToolTipText(value);
     480        }
     481        else {
     482            component.setToolTipText(null);
     483        }
     484        }
     485    }
     486    }
     487    /** Update the title of this dialog. */
     488    private class JDialogChangeTask
     489    extends ChangeTask {
     490    private JDialog component;
     491    public JDialogChangeTask(JDialog component, String key, String value) {
     492        super(key, value);
     493        this.component = component;
     494    }
     495    public void run() {
     496        component.setTitle(value);
     497    }
     498    }
     499    /** Update the title of this frame. */
     500    private class JFrameChangeTask
     501    extends ChangeTask {
     502    private JFrame component;
     503    public JFrameChangeTask(JFrame component, String key, String value) {
     504        super(key, value);
     505        this.component = component;
     506    }
     507    public void run() {
     508        component.setTitle(value);
     509    }
     510    }
     511    /** Update the text of this label. */
     512    private class JLabelChangeTask
     513    extends ChangeTask {
     514    private JLabel component;
     515    public JLabelChangeTask(JLabel component, String key, String value) {
     516        super(key, value);
     517        this.component = component;
     518    }
     519    public void run() {
     520        component.setText(value);
     521    }
     522    }
     523    /** Updates a tabbed panes tab title and tooltip. */
     524    private class JTabbedPaneChangeTask
     525    extends ChangeTask {
     526    private int index;
     527    private JTabbedPane component;
     528    private String tooltip;
     529    public JTabbedPaneChangeTask(JTabbedPane component, String key, int index, String value, String tooltip) {
     530        super(key, value);
     531        this.component = component;
     532        this.index = index;
     533        this.tooltip = tooltip;
     534    }
     535    public void run() {
     536        component.setTitleAt(index, value);
     537        if(!tooltip.equals(key+"_Tooltip")) {
     538        component.setToolTipTextAt(index, tooltip);
     539        }
     540        else {
     541        component.setToolTipTextAt(index, null);
     542        }
     543    }
     544    }
     545    /** Update the text and tooltip of this text component. */
     546    private class JTextComponentChangeTask
     547    extends ChangeTask {
     548    private JTextComponent component;
     549    private String tooltip;
     550    public JTextComponentChangeTask(JTextComponent component, String key, String value, String tooltip) {
     551        super(key, value);
     552        this.component = component;
     553        this.tooltip = tooltip;
     554    }
     555    public void run() {
     556        component.setText(value);
     557        if(!tooltip.equals(key+"_Tooltip")) {
     558        component.setToolTipText(tooltip);
     559        }
     560        else {
     561        component.setToolTipText(null);
     562        }
     563    }
     564    }
     565    /** Update the tooltip of a tree and its tree node's labels. Shouldn't really ever be used on a dynamic tree, but is quite useful for a 'contents' tree type control. */
     566    private class JTreeChangeTask
     567    extends ChangeTask {
     568    private DictionaryTreeNode node;
     569    private JTree component;
     570    public JTreeChangeTask(JTree component, String key, String value) {
     571        super(key, value);
     572        this.component = component;
     573    }
     574    public JTreeChangeTask(JTree component, String key, DictionaryTreeNode node, String value) {
     575        super(key, value);
     576        this.component = component;
     577        this.node = node;
     578    }
     579    public void run() {
     580        if(value != null) {
     581        node.setText(value);
     582        ((DefaultTreeModel)component.getModel()).nodeChanged((TreeNode)node);
     583        }
    584584                // Set the tool tip
    585                 else {
    586                      if(!value.equals(key+"_Tooltip")) {
    587                           component.setToolTipText(value);
    588                      }
    589                      else {
    590                           component.setToolTipText(null);
    591                      }
    592                 }
    593           }
    594     }
    595     /** Update the title of this titled border. */
    596     private class TitledBorderChangeTask
    597           extends ChangeTask {
    598           private TitledBorder component;
    599           public TitledBorderChangeTask(TitledBorder component, String key, String value) {
    600                 super(key, value);
    601                 this.component = component;
    602           }
    603           public void run() {
    604                 component.setTitle(value);
    605                 component.getParent().repaint();
    606           }
    607     }
     585        else {
     586        if(!value.equals(key+"_Tooltip")) {
     587            component.setToolTipText(value);
     588        }
     589        else {
     590            component.setToolTipText(null);
     591        }
     592        }
     593    }
     594    }
     595    /** Update the title of this titled border. */
     596    private class TitledBorderChangeTask
     597    extends ChangeTask {
     598    private TitledBorder component;
     599    public TitledBorderChangeTask(TitledBorder component, String key, String value) {
     600        super(key, value);
     601        this.component = component;
     602    }
     603    public void run() {
     604        component.setTitle(value);
     605        component.getParent().repaint();
     606    }
     607    }
    608608}
    609609
  • trunk/gli/src/org/greenstone/gatherer/GAuthenticator.java

    r4293 r4363  
    5555import javax.swing.JPasswordField;
    5656/** Provides a graphic authenticator for network password requests.
    57 * @author John Thompson, Greenstone Digital Library, University of Waikato
    58 * @version 2.3
    59 */
     57 * @author John Thompson, Greenstone Digital Library, University of Waikato
     58 * @version 2.3
     59 */
    6060public class GAuthenticator
    61     extends Authenticator {
    62     /** Indicates if this authentication prompt been cancelled, and if so rolls-back authentication. */
    63     private boolean authentication_cancelled = false;
    64     /** The button used to cancel a prompt. */
    65     private JButton cancel_button = null;
    66     /** The button used to submit the login/password. */
    67     private JButton ok_button = null;
    68     /** A reference to the dialog prompt created so inner classes can dispose of it. */
    69     private JDialog dialog = null;
    70     /** The password is a special starred out password field. */
    71     private JPasswordField password = null;
    72     /** The default size of this dialog. */
    73     static final private Dimension SIZE = new Dimension(410,130);
    74     /** Constructor. */
    75     public GAuthenticator() {
    76     }
    77     /** Prompt the user for authentication using a pretty dialog box.
     61    extends Authenticator {
     62    /** Indicates if this authentication prompt been cancelled, and if so rolls-back authentication. */
     63    private boolean authentication_cancelled = false;
     64    /** The button used to cancel a prompt. */
     65    private JButton cancel_button = null;
     66    /** The button used to submit the login/password. */
     67    private JButton ok_button = null;
     68    /** A reference to the dialog prompt created so inner classes can dispose of it. */
     69    private JDialog dialog = null;
     70    /** The password is a special starred out password field. */
     71    private JPasswordField password = null;
     72    /** The default size of this dialog. */
     73    static final private Dimension SIZE = new Dimension(410,130);
     74    /** Constructor. */
     75    public GAuthenticator() {
     76    }
     77    /** Prompt the user for authentication using a pretty dialog box.
    7878      * @return A <strong>PasswordAuthentication</strong> object containing the login and password valuees the user has submitted.
    7979      * @see org.greenstone.gatherer.GAuthenticator.AuthenticationActionListener
    8080      * @see org.greenstone.gatherer.GAuthenticator.RequestFocusListener
    8181      */
    82     protected PasswordAuthentication getPasswordAuthentication() {
    83           // Component definition.
    84           dialog = new JDialog (Gatherer.g_man, get("Title"), true);
    85           dialog.setModal(true);
    86           dialog.setSize(SIZE);
    87           JPanel content_pane = (JPanel)dialog.getContentPane();
    88           JLabel title_label = new JLabel(getRequestingPrompt());
    89           JPanel user_panel = new JPanel();
    90           JLabel username_label = new JLabel(get("Username"));
    91           JTextField username = new JTextField();
    92           JPanel password_panel = new JPanel();
    93           JLabel password_label = new JLabel(get("Password"));
    94           password = new JPasswordField();
    95           password.setEchoChar ('*');
    96           JPanel button_panel = new JPanel();
    97           ok_button = new JButton(get("General.OK"));
    98           cancel_button = new JButton(get("General.Cancel"));
    99           // Connect listeners.
    100           cancel_button.addActionListener(new AuthenticationActionListener(true));
    101           ok_button.addActionListener(new AuthenticationActionListener(false));
    102           password.addActionListener(new AuthenticationActionListener(false));
    103           username.addActionListener(new RequestFocusListener(password));
    104           // Layout the components.
    105           user_panel.setLayout(new GridLayout(1,2));
    106           user_panel.add(username_label);
    107           user_panel.add(username);
     82    protected PasswordAuthentication getPasswordAuthentication() {
     83    // Component definition.
     84    dialog = new JDialog (Gatherer.g_man, get("Title"), true);
     85    dialog.setModal(true);
     86    dialog.setSize(SIZE);
     87    JPanel content_pane = (JPanel)dialog.getContentPane();
     88    JLabel title_label = new JLabel(getRequestingPrompt());
     89    JPanel user_panel = new JPanel();
     90    JLabel username_label = new JLabel(get("Username"));
     91    JTextField username = new JTextField();
     92    JPanel password_panel = new JPanel();
     93    JLabel password_label = new JLabel(get("Password"));
     94    password = new JPasswordField();
     95    password.setEchoChar ('*');
     96    JPanel button_panel = new JPanel();
     97    ok_button = new JButton(get("General.OK"));
     98    cancel_button = new JButton(get("General.Cancel"));
     99    // Connect listeners.
     100    cancel_button.addActionListener(new AuthenticationActionListener(true));
     101    ok_button.addActionListener(new AuthenticationActionListener(false));
     102    password.addActionListener(new AuthenticationActionListener(false));
     103    username.addActionListener(new RequestFocusListener(password));
     104    // Layout the components.
     105    user_panel.setLayout(new GridLayout(1,2));
     106    user_panel.add(username_label);
     107    user_panel.add(username);
    108108
    109           password_panel.setLayout(new GridLayout(1,2));
    110           password_panel.add(password_label);
    111           password_panel.add(password);
     109    password_panel.setLayout(new GridLayout(1,2));
     110    password_panel.add(password_label);
     111    password_panel.add(password);
    112112
    113           button_panel.setLayout(new GridLayout(1,2));
    114           button_panel.add(ok_button);
    115           button_panel.add(cancel_button);
     113    button_panel.setLayout(new GridLayout(1,2));
     114    button_panel.add(ok_button);
     115    button_panel.add(cancel_button);
    116116
    117           content_pane.setLayout(new GridLayout(4,1));
    118           content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    119           content_pane.add(title_label);
    120           content_pane.add(user_panel);
    121           content_pane.add(password_panel);
    122           content_pane.add(button_panel);
    123           // Position the window.
    124           Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
    125           dialog.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
    126           dialog.show();
    127           if(!authentication_cancelled) {
    128                 return new PasswordAuthentication(username.getText(), password.getPassword());
    129           } else {
    130                 return null;
    131           }
    132     }
     117    content_pane.setLayout(new GridLayout(4,1));
     118    content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     119    content_pane.add(title_label);
     120    content_pane.add(user_panel);
     121    content_pane.add(password_panel);
     122    content_pane.add(button_panel);
     123    // Position the window.
     124    Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
     125    dialog.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
     126    dialog.show();
     127    if(!authentication_cancelled) {
     128        return new PasswordAuthentication(username.getText(), password.getPassword());
     129    } else {
     130        return null;
     131    }
     132    }
    133133
    134     /** Retrieve a phrase from the dictionary.
     134    /** Retrieve a phrase from the dictionary.
    135135      * @param key A <strong>String</strong> used to determine what phrase to retrieve.
    136136      * @return The required phrase as a <strong>String</strong>, or at least some meaningful error message.
     
    138138      * @see org.greenstone.gatherer.Gatherer
    139139      */
    140     private String get(String key) {
    141           if(key.indexOf(".") == -1) {
    142                 key = "GAuthenticator." + key;
    143           }
    144           return Gatherer.dictionary.get(key);
    145     }
    146     /** Detects actions upon any control that attempt to submit the current details for authentication. */
    147     private class AuthenticationActionListener
    148           implements ActionListener {
    149           /** <i>true</i> if this authentication action cancels the authentication, <i>false</i> otherwise. */
    150           private boolean cancel_action = false;
    151           /** Constructor.
    152             * @param cancel_action <i>true</i> if this authentication action cancels the authentication, <i>false</i> otherwise.
     140    private String get(String key) {
     141    if(key.indexOf(".") == -1) {
     142        key = "GAuthenticator." + key;
     143    }
     144    return Gatherer.dictionary.get(key);
     145    }
     146    /** Detects actions upon any control that attempt to submit the current details for authentication. */
     147    private class AuthenticationActionListener
     148    implements ActionListener {
     149    /** <i>true</i> if this authentication action cancels the authentication, <i>false</i> otherwise. */
     150    private boolean cancel_action = false;
     151    /** Constructor.
     152     * @param cancel_action <i>true</i> if this authentication action cancels the authentication, <i>false</i> otherwise.
    153153            */
    154           public AuthenticationActionListener(boolean cancel_action) {
    155                 this.cancel_action = cancel_action;
    156           }
    157           /** Any implementation of an ActionListener must include this method so that we can be informed when an action has been performed on our registered controls, allowing us to dispose of the authentication dialog after determining if this is a submit action or a cancel one.
     154    public AuthenticationActionListener(boolean cancel_action) {
     155        this.cancel_action = cancel_action;
     156    }
     157    /** Any implementation of an ActionListener must include this method so that we can be informed when an action has been performed on our registered controls, allowing us to dispose of the authentication dialog after determining if this is a submit action or a cancel one.
    158158            * @param event An <strong>ActionEvent</strong> with information about the event that fired this method.
    159159            */
    160           public void actionPerformed(ActionEvent event) {
    161                 authentication_cancelled = cancel_action;
    162                 dialog.dispose();
    163           }
    164     }
    165     /** This listener detects actions on registered controls, and when they occur ensures the focus is moved to some targetted component. */
    166     private class RequestFocusListener
    167           implements ActionListener {
    168           /*The <strong>Component</strong> you wish to gain focus when an action is performed on a registered control. */
    169           private Component target = null;
    170           /** Constructor.
    171             * @param target The <strong>Component</strong> you wish to gain focus when an action is performed on a registered control.
     160    public void actionPerformed(ActionEvent event) {
     161        authentication_cancelled = cancel_action;
     162        dialog.dispose();
     163    }
     164    }
     165    /** This listener detects actions on registered controls, and when they occur ensures the focus is moved to some targetted component. */
     166    private class RequestFocusListener
     167    implements ActionListener {
     168    /*The <strong>Component</strong> you wish to gain focus when an action is performed on a registered control. */
     169    private Component target = null;
     170    /** Constructor.
     171     * @param target The <strong>Component</strong> you wish to gain focus when an action is performed on a registered control.
    172172            */
    173           public RequestFocusListener(Component target) {
    174                 this.target = target;
    175           }
    176           /** Any implementation of an ActionListener must include this method so that we can be informed when an action has been performed on our registered controls, allowing us to request focus in the target control.
     173    public RequestFocusListener(Component target) {
     174        this.target = target;
     175    }
     176    /** Any implementation of an ActionListener must include this method so that we can be informed when an action has been performed on our registered controls, allowing us to request focus in the target control.
    177177            * @param event An <strong>ActionEvent</strong> with information about the event that fired this method.
    178178            */
    179           public void actionPerformed(ActionEvent event) {
    180                 target.requestFocus();
    181           }
    182     }
     179    public void actionPerformed(ActionEvent event) {
     180        target.requestFocus();
     181    }
     182    }
    183183}
    184184
  • trunk/gli/src/org/greenstone/gatherer/Gatherer.java

    r4349 r4363  
    6262import sun.misc.*;
    6363/** Containing the main() method for the Gatherer, this class is the starting point for the rest of the application. It first parses the command line arguments, preparing to update the configuration as required. Next it loads several important support classes such as the Configuration and Dictionary. Finally it creates the other important managers and sends them on their way.
    64 * @author John Thompson, Greenstone Digital Library, University of Waikato
    65 * @version 2.3
    66 */
     64 * @author John Thompson, Greenstone Digital Library, University of Waikato
     65 * @version 2.3
     66 */
    6767public class Gatherer {
    68     /** Has the exit flag been set? <i>true</i> if so, <i>false</i> otherwise. */
    69     public boolean exit = false;
    70     /** The size of the Gatherer window. */
    71     public Dimension frame_size = null;
    72     /** A temporary shared memory area to store HIndexes to speed up metadata.xml writing. */
    73     public Hashtable known_indexes = null;
    74     /** Legacy copy of the debug_ps. */
    75     public PrintStream debug_ps;
    76     /** All of the external applications that must exit before we close the Gatherer. */
    77     public Vector apps = new Vector();
    78     /** Messages that have been issued before we have anyway to show them, ie prior to Log initialization. */
    79     public Vector waiting_messages = new Vector();
    80     /** The manager in charge of remembering what file extension gets opened with what program. */
    81     static public FileAssociationManager assoc_man;
    82     /** A public reference to the CollectionManager. */
    83     static public CollectionManager c_man;
    84     /** A public reference to the Gatherer's configuration. */
    85     static public Configuration config;
    86     /** A public reference to the Dictionary. */
    87     static public Dictionary dictionary;
    88     /** A public reference to the FileManager. */
    89     static public FileManager f_man;
    90     /** A public reference to the GUIManager. */
    91     static public GUIManager g_man;
    92     /** A static reference to ourselves. */
    93     static public Gatherer self;
    94     /** A public reference to the message log. */
    95     static public Log log;
    96     /** The debug print stream. */
    97     static public PrintStream debug;
    98     /** The name of the necessary environment variable to check for in the programs environment. */
    99     static public String KEY = "GSDLPATH";
    100     /** Extra environment information which must be set before shell processes will run properly. Should always be null if the startup script/program has done its job properly. */
    101     static public String extra_env[] = null;
    102     private GSDLSiteConfig gsdlsite_cfg = null;
    103     private ExternalApplication server = null;
    104     /** The name of the Gatherers configuration file. */
    105     static private String CONFIG_FILE_NAME = "gatherer.cfg";
    106     /** Constructor. Make the three main modules, c_man, f_man and g_man, and any other necessary classes such as Dictionary.
    107       * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>.
    108       * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
    109       * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
    110       * @param debug <i>true</i> to print verbose debug messages to "debug.txt", <i>false</i> otherwise.
    111       * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
    112       * @param splash A reference to the splash screen.
    113       * @param no_load <i>true</i> to prevent the previously opened collection from reopening.
    114       * @see java.io.FileOutputStream
    115       * @see java.io.PrintStream
    116       * @see java.lang.Exception
    117       * @see java.lang.StringBuffer
    118       * @see java.util.Calendar
    119       * @see org.greenstone.gatherer.Configuration
    120       * @see org.greenstone.gatherer.Dictionary
    121       * @see org.greenstone.gatherer.Gatherer.CTRLCHandler
    122       * @see org.greenstone.gatherer.GAuthenticator
    123       * @see org.greenstone.gatherer.collection.CollectionManager
    124       * @see org.greenstone.gatherer.file.FileManager
    125       * @see org.greenstone.gatherer.gui.GUIManager
    126       * @see org.greenstone.gatherer.gui.Splash
    127       */
    128     public Gatherer() {
    129           this.self = this;
    130     }
    131 
    132     public void run(Dimension size, String gsdl_path, String exec_path, boolean debug_enabled, String perl_path, boolean no_load, Splash splash, String open_collection) {
    133 
    134           // This will hopefully catch ctrl-c and terminate, and exit gracefully. However it is platform specific, and may not be supported by some JVMs.
    135           /** It does, but I get bloddy sick of it working when the Gatherer hangs.
    136           CTRLCHandler handler = new CTRLCHandler();
    137           Signal.handle(new Signal("INT"), handler);
    138           Signal.handle(new Signal("TERM"), handler);
    139           handler = null;
    140           */
    141           // Create the debug stream only if required.
    142           if(debug_enabled) {
    143                 try {
    144                      Calendar now = Calendar.getInstance();
    145                      StringBuffer name = new StringBuffer("debug");
    146                      name.append(now.get(Calendar.DATE));
    147                      name.append("-");
    148                      name.append(now.get(Calendar.MONTH));
    149                      name.append("-");
    150                      name.append(now.get(Calendar.YEAR));
    151                      name.append(".txt");
    152                      this.debug = new PrintStream(new FileOutputStream(name.toString()));
    153                      Properties props = System.getProperties();
    154                      props.list(debug);
    155                      // Legacy
    156                      debug_ps = debug;
    157                 }
    158                 catch(Exception error) {
    159                      ///ystem.err.println("Error in Gatherer.init(): " + error);
    160                      error.printStackTrace();
    161                      System.exit(1);
    162                 }
    163           }
    164           try {
     68    /** Has the exit flag been set? <i>true</i> if so, <i>false</i> otherwise. */
     69    public boolean exit = false;
     70    /** The size of the Gatherer window. */
     71    public Dimension frame_size = null;
     72    /** A temporary shared memory area to store HIndexes to speed up metadata.xml writing. */
     73    public Hashtable known_indexes = null;
     74    /** Legacy copy of the debug_ps. */
     75    public PrintStream debug_ps;
     76    /** All of the external applications that must exit before we close the Gatherer. */
     77    public Vector apps = new Vector();
     78    /** Messages that have been issued before we have anyway to show them, ie prior to Log initialization. */
     79    public Vector waiting_messages = new Vector();
     80    /** The manager in charge of remembering what file extension gets opened with what program. */
     81    static public FileAssociationManager assoc_man;
     82    /** A public reference to the CollectionManager. */
     83    static public CollectionManager c_man;
     84    /** A public reference to the Gatherer's configuration. */
     85    static public Configuration config;
     86    /** A public reference to the Dictionary. */
     87    static public Dictionary dictionary;
     88    /** A public reference to the FileManager. */
     89    static public FileManager f_man;
     90    /** A public reference to the GUIManager. */
     91    static public GUIManager g_man;
     92    /** A static reference to ourselves. */
     93    static public Gatherer self;
     94    /** A public reference to the message log. */
     95    static public Log log;
     96    /** The debug print stream. */
     97    static public PrintStream debug;
     98    /** The name of the necessary environment variable to check for in the programs environment. */
     99    static public String KEY = "GSDLPATH";
     100    /** Extra environment information which must be set before shell processes will run properly. Should always be null if the startup script/program has done its job properly. */
     101    static public String extra_env[] = null;
     102    private GSDLSiteConfig gsdlsite_cfg = null;
     103    private ExternalApplication server = null;
     104    /** The name of the Gatherers configuration file. */
     105    static private String CONFIG_FILE_NAME = "gatherer.cfg";
     106    /** Constructor. Make the three main modules, c_man, f_man and g_man, and any other necessary classes such as Dictionary.
     107     * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>.
     108     * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
     109     * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
     110     * @param debug <i>true</i> to print verbose debug messages to "debug.txt", <i>false</i> otherwise.
     111     * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
     112     * @param splash A reference to the splash screen.
     113     * @param no_load <i>true</i> to prevent the previously opened collection from reopening.
     114     * @see java.io.FileOutputStream
     115     * @see java.io.PrintStream
     116     * @see java.lang.Exception
     117     * @see java.lang.StringBuffer
     118     * @see java.util.Calendar
     119     * @see org.greenstone.gatherer.Configuration
     120     * @see org.greenstone.gatherer.Dictionary
     121     * @see org.greenstone.gatherer.Gatherer.CTRLCHandler
     122     * @see org.greenstone.gatherer.GAuthenticator
     123     * @see org.greenstone.gatherer.collection.CollectionManager
     124     * @see org.greenstone.gatherer.file.FileManager
     125     * @see org.greenstone.gatherer.gui.GUIManager
     126     * @see org.greenstone.gatherer.gui.Splash
     127     */
     128    public Gatherer() {
     129    this.self = this;
     130    }
     131
     132    public void run(Dimension size, String gsdl_path, String exec_path, boolean debug_enabled, String perl_path, boolean no_load, Splash splash, String open_collection) {
     133
     134    // This will hopefully catch ctrl-c and terminate, and exit gracefully. However it is platform specific, and may not be supported by some JVMs.
     135    /** It does, but I get bloddy sick of it working when the Gatherer hangs.
     136        CTRLCHandler handler = new CTRLCHandler();
     137        Signal.handle(new Signal("INT"), handler);
     138        Signal.handle(new Signal("TERM"), handler);
     139        handler = null;
     140    */
     141    // Create the debug stream only if required.
     142    if(debug_enabled) {
     143        try {
     144        Calendar now = Calendar.getInstance();
     145        StringBuffer name = new StringBuffer("debug");
     146        name.append(now.get(Calendar.DATE));
     147        name.append("-");
     148        name.append(now.get(Calendar.MONTH));
     149        name.append("-");
     150        name.append(now.get(Calendar.YEAR));
     151        name.append(".txt");
     152        this.debug = new PrintStream(new FileOutputStream(name.toString()));
     153        Properties props = System.getProperties();
     154        props.list(debug);
     155        // Legacy
     156        debug_ps = debug;
     157        }
     158        catch(Exception error) {
     159        ///ystem.err.println("Error in Gatherer.init(): " + error);
     160        error.printStackTrace();
     161        System.exit(1);
     162        }
     163    }
     164    try {
    165165                // Create log
    166                 log = new Log();
     166        log = new Log();
    167167                // Load Config
    168                 loadConfig(gsdl_path, exec_path, perl_path);
     168        loadConfig(gsdl_path, exec_path, perl_path);
    169169
    170170                // Read Dictionary
    171                 dictionary = new Dictionary(config.getLocale("general.locale", true), config.getFont("general.font", true));
     171        dictionary = new Dictionary(config.getLocale("general.locale", true), config.getFont("general.font", true));
    172172
    173173                // If we were given a server run it if neccessary.
    174                 if(config.exec_file != null) {
    175                      startServerEXE();
    176                 }
     174        if(config.exec_file != null) {
     175        startServerEXE();
     176        }
    177177
    178178                // Having loaded the configuration (necessary to determine if certain warnings have been disabled) and dictionary, we now check if the necessary path variables have been provided.
    179179
    180                 if(config.exec_file == null && config.exec_address == null) {
    181                      missingEXEC(dictionary);
    182                 }
    183                 if(gsdl_path == null) {
    184                      missingGSDL(dictionary);
    185                 }
     180        if(config.exec_file == null && config.exec_address == null) {
     181        missingEXEC(dictionary);
     182        }
     183        if(gsdl_path == null) {
     184        missingGSDL(dictionary);
     185        }
    186186                // Perl path is a little different as it is perfectly ok to start the Gatherer without providing a perl path
    187                 boolean found_perl = false;
    188                 if(config.perl_path != null) {
    189                      // See if the file pointed to actually exists
    190                      File perl_file = new File(config.perl_path);
    191                      found_perl = perl_file.exists();
    192                      perl_file = null;
    193                 }
    194                 if(config.perl_path == null || !found_perl) {
    195                      // Run test to see if we can run perl as is.
    196                      PerlTest perl_test = new PerlTest();
    197                      if(perl_test.found()) {
    198                           // If so replace the perl path with the system default (or null for unix).
    199                           config.perl_path = perl_test.toString();
    200                           found_perl = true;
    201                      }
    202                 }
    203                 if(!found_perl) {
    204                      // Time for an error message.
    205                      missingPERL(dictionary);
    206                 }
    207                 else {
    208                      ///ystem.err.println("Now perl_path = " + config.perl_path);
    209                 }
     187        boolean found_perl = false;
     188        if(config.perl_path != null) {
     189        // See if the file pointed to actually exists
     190        File perl_file = new File(config.perl_path);
     191        found_perl = perl_file.exists();
     192        perl_file = null;
     193        }
     194        if(config.perl_path == null || !found_perl) {
     195        // Run test to see if we can run perl as is.
     196        PerlTest perl_test = new PerlTest();
     197        if(perl_test.found()) {
     198            // If so replace the perl path with the system default (or null for unix).
     199            config.perl_path = perl_test.toString();
     200            found_perl = true;
     201        }
     202        }
     203        if(!found_perl) {
     204        // Time for an error message.
     205        missingPERL(dictionary);
     206        }
     207        else {
     208        ///ystem.err.println("Now perl_path = " + config.perl_path);
     209        }
    210210
    211211                // Size the screen
    212                 Dimension temp = config.getDimension("general.size", true);
    213                 if(temp != null) {
    214                      size = temp;
    215                 }
    216                 if (size.height > config.screen_size.height) {
    217                      size.setSize(size.width, config.screen_size.height);
    218                 }
    219                 if (size.width > config.screen_size.width) {
    220                      size.setSize(config.screen_size.width, size.height);
    221                 }
     212        Dimension temp = config.getDimension("general.size", true);
     213        if(temp != null) {
     214        size = temp;
     215        }
     216        if (size.height > config.screen_size.height) {
     217        size.setSize(size.width, config.screen_size.height);
     218        }
     219        if (size.width > config.screen_size.width) {
     220        size.setSize(config.screen_size.width, size.height);
     221        }
    222222                // Set default font
    223                 setUIFont(config.getFont("general.font", true), config.getFont("general.tooltip_font", true));
     223        setUIFont(config.getFont("general.font", true), config.getFont("general.tooltip_font", true));
    224224                // Set up proxy
    225                 setProxy();
     225        setProxy();
    226226                // Now we set up an Authenticator
    227                 Authenticator.setDefault(new GAuthenticator());
    228 
    229                 assoc_man = new FileAssociationManager();
     227        Authenticator.setDefault(new GAuthenticator());
     228
     229        assoc_man = new FileAssociationManager();
    230230                // Create File Manager
    231                 f_man = new FileManager();
     231        f_man = new FileManager();
    232232                // Create Collection Manager
    233                 c_man = new CollectionManager();
     233        c_man = new CollectionManager();
    234234                // If there was an open collection last session, reopen it.
    235                 if(open_collection == null) {
    236                      open_collection = config.getString("general.open_collection", true);
    237                 }
    238                 if(!no_load && open_collection.length() > 0) {
    239                      c_man.loadCollection(open_collection);
    240                 }
     235        if(open_collection == null) {
     236        open_collection = config.getString("general.open_collection", true);
     237        }
     238        if(!no_load && open_collection.length() > 0) {
     239        c_man.loadCollection(open_collection);
     240        }
    241241                // Create GUI Manager (last) or else suffer the death of a thousand NPE's
    242                 splash.toFront();
    243                 g_man = new GUIManager(size);
    244                 g_man.display();
     242        splash.toFront();
     243        g_man = new GUIManager(size);
     244        g_man.display();
    245245
    246246                // Center the screen, if this is do-able (not under most linux window managers apparently. In fact you're lucky if they listen to any of your screen size requests).
    247                 g_man.setLocation((config.screen_size.width - size.width) / 2, (config.screen_size.height - size.height) / 2);
    248                 g_man.setVisible(true);
     247        g_man.setLocation((config.screen_size.width - size.width) / 2, (config.screen_size.height - size.height) / 2);
     248        g_man.setVisible(true);
    249249                // The 'after-display' triggers several events which don't occur until after the visual components are actually available on screen. Examples of these would be the various html renderings, as they can't happen offscreen.
    250                 g_man.afterDisplay();
     250        g_man.afterDisplay();
    251251                // Hide the splash.
    252                 splash.hide();
    253                 splash.destroy();
    254                 splash = null;
    255           }
    256           catch (Exception error) {
    257                 error.printStackTrace();
    258           }
    259     }
    260     /** Writes a message to the debug filestream.
    261       * @param message The message as a <strong>String</strong>.
    262       */
    263     public void debug(String message) {
    264           debug(null, message);
    265     }
    266     /** Writes a message to the debug filestream.
    267       * @param error The <strong>Exception</strong> associated with this message, or <i>null</i> for no exception.
    268       * @param message The message as a <strong>String</strong>.
    269       * @see java.io.FileOutputStream
    270       * @see java.io.PrintStream
    271       * @see java.lang.Exception
    272       */
     252        splash.hide();
     253        splash.destroy();
     254        splash = null;
     255    }
     256    catch (Exception error) {
     257        error.printStackTrace();
     258    }
     259    }
     260    /** Writes a message to the debug filestream.
     261     * @param message The message as a <strong>String</strong>.
     262     */
     263    public void debug(String message) {
     264    debug(null, message);
     265    }
     266    /** Writes a message to the debug filestream.
     267     * @param error The <strong>Exception</strong> associated with this message, or <i>null</i> for no exception.
     268     * @param message The message as a <strong>String</strong>.
     269     * @see java.io.FileOutputStream
     270     * @see java.io.PrintStream
     271     * @see java.lang.Exception
     272     */
    273273    public void debug(Exception exception, String message) {
    274           if(message != null) {
    275                 Gatherer.println(message);
    276           }
    277           if(exception != null) {
    278                 Gatherer.printStackTrace(exception);
    279           }
    280     }
    281     /** Exits the Gatherer after ensuring that things needing saving are saved.
    282       * @see java.io.FileOutputStream
    283       * @see java.io.PrintStream
    284       * @see java.lang.Exception
    285       * @see javax.swing.JOptionPane
    286       * @see org.greenstone.gatherer.Configuration
    287       * @see org.greenstone.gatherer.collection.CollectionManager
    288       * @see org.greenstone.gatherer.gui.GUIManager
    289       */
    290     public void exit() {
    291           exit = true;
    292           // If we have an open collection make note of it.
    293           config.setString("general.open_collection", true, null);
    294           if(c_man.ready()) {
     274    if(message != null) {
     275        Gatherer.println(message);
     276    }
     277    if(exception != null) {
     278        Gatherer.printStackTrace(exception);
     279    }
     280    }
     281    /** Exits the Gatherer after ensuring that things needing saving are saved.
     282     * @see java.io.FileOutputStream
     283     * @see java.io.PrintStream
     284     * @see java.lang.Exception
     285     * @see javax.swing.JOptionPane
     286     * @see org.greenstone.gatherer.Configuration
     287     * @see org.greenstone.gatherer.collection.CollectionManager
     288     * @see org.greenstone.gatherer.gui.GUIManager
     289     */
     290    public void exit() {
     291    exit = true;
     292    // If we have an open collection make note of it.
     293    config.setString("general.open_collection", true, null);
     294    if(c_man.ready()) {
    295295                ///ystem.err.println("Collection open.");
    296                 if(c_man.saved()) {
    297                      ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time.");
    298                      config.setString("general.open_collection", true, c_man.getCollectionFilename());
    299                 }
    300                 c_man.closeCollection();
    301           }
    302           if(assoc_man != null) {
    303                 assoc_man.destroy();
    304                 assoc_man = null;
    305           }
    306           // Check the current size of the Gatherer, and if its reasonable (ie greater than minsize) then store it for next time.
    307           Dimension size = g_man.getSize();
    308           if(size.width > 100 && size.height > 100) {
    309                 config.setDimension("general.size", true, size);
    310           }
    311 
    312           // Save configuration.
    313           saveConfig();
    314           // Flush debug
    315           if(debug != null) {
    316                 try {
    317                      debug.flush();
    318                      debug.close();
    319                 }
    320                 catch (Exception error) {
    321                      error.printStackTrace();
    322                 }
    323           }
    324 
    325           // If we started a server, we should try to stop it.
    326           if(gsdlsite_cfg != null) {
    327                 stopServerEXE();
    328           }
    329 
    330           if(apps.size() == 0) {
    331                 System.exit(0);
    332           }
    333           else {
    334                 JOptionPane.showMessageDialog(g_man, get("General.Outstanding_Processes"), get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
    335                 g_man.hide();
    336           }
    337     }
    338     /** Overloaded to call get with both a key and an empty argument array.
    339       * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
    340       * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
    341       */
    342     public String get(String key) {
    343           return dictionary.get(key, (String[])null);
    344     }
    345     /** Overloaded to call get with both a key and an argument array with one element.
    346       * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
    347       * @param arg A single argument as a <strong>String</strong>.
    348       * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
    349       */
    350     public String get(String key, String arg) {
    351           String args[] = new String[1];
    352           args[0] = arg;
    353           return dictionary.get(key, args);
    354     }
    355     /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR>
    356       * Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {<I>n</I>}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Farg<I>n</I>.
    357       * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
    358       * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
    359       * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatically populated with formatting Strings of with argument String provided in the get call.
    360       * @see org.greenstone.gatherer.Gatherer
    361       * @see org.greenstone.gatherer.Dictionary
    362       */
    363     public String get(String key, String args[]) {
    364           return dictionary.get(key, args);
    365     }
    366     /** Retrieve the metadata directory, as required by any MSMCaller implementation.
    367       * @return The currently active collection metadata directory as a <strong>String</strong>.
    368       * @see org.greenstone.gatherer.collection.CollectionManager
    369       */
    370     public String getCollectionMetadata() {
    371           if(c_man != null && c_man.ready()) {
    372                 return c_man.getCollectionMetadata();
    373           }
    374           return "";
    375     }
    376     /** Retrieve a reference to the frame that any dialog boxes will appear relative to, as required by any MSMCaller or CDMCaller implementation.
    377       * @return A <strong>JFrame</strong>.
    378       * @see org.greenstone.gatherer.gui.GUIManager
    379       */
    380     public JFrame getFrame() {
    381           return g_man;
    382     }
    383     /** Method to retrieve a reference to the metadata set manager class. This is then used to create the 'metadataset' commands in the collection configuration file.
    384       * @return A reference to the <Strong>MetadataSetManager</strong>.
    385       * @see org.greenstone.gatherer.collection.CollectionManager
    386       */
    387     public MetadataSetManager getMSM() {
    388           if(c_man != null && c_man.getCollection() != null && c_man.getCollection().msm != null) {
    389                 return c_man.getCollection().msm;
    390           }
    391           return null;
    392     }
    393     /** Used to 'spawn' a new child application when a file is double clicked.
    394       * @param command The command to run in the child process to start the application, garnered from the registry of a default associations file, and presented as a <strong>String</strong>.
    395       * @see java.util.Vector
    396       * @see org.greenstone.gatherer.Gatherer.ExternalApplication
    397       */
    398     public void spawnApplication(File file) {
    399           String command = assoc_man.getCommand(file);
    400           if(command != null) {
    401                 ExternalApplication app = new ExternalApplication(command);
    402                 apps.add(app);
    403                 app.start();
    404           }
    405           else {
     296        if(c_man.saved()) {
     297        ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time.");
     298        config.setString("general.open_collection", true, c_man.getCollectionFilename());
     299        }
     300        c_man.closeCollection();
     301    }
     302    if(assoc_man != null) {
     303        assoc_man.destroy();
     304        assoc_man = null;
     305    }
     306    // Check the current size of the Gatherer, and if its reasonable (ie greater than minsize) then store it for next time.
     307    Dimension size = g_man.getSize();
     308    if(size.width > 100 && size.height > 100) {
     309        config.setDimension("general.size", true, size);
     310    }
     311
     312    // Save configuration.
     313    saveConfig();
     314    // Flush debug
     315    if(debug != null) {
     316        try {
     317        debug.flush();
     318        debug.close();
     319        }
     320        catch (Exception error) {
     321        error.printStackTrace();
     322        }
     323    }
     324
     325    // If we started a server, we should try to stop it.
     326    if(gsdlsite_cfg != null) {
     327        stopServerEXE();
     328    }
     329
     330    if(apps.size() == 0) {
     331        System.exit(0);
     332    }
     333    else {
     334        JOptionPane.showMessageDialog(g_man, get("General.Outstanding_Processes"), get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
     335        g_man.hide();
     336    }
     337    }
     338    /** Overloaded to call get with both a key and an empty argument array.
     339     * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
     340     * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
     341     */
     342    public String get(String key) {
     343    return dictionary.get(key, (String[])null);
     344    }
     345    /** Overloaded to call get with both a key and an argument array with one element.
     346     * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
     347     * @param arg A single argument as a <strong>String</strong>.
     348     * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
     349     */
     350    public String get(String key, String arg) {
     351    String args[] = new String[1];
     352    args[0] = arg;
     353    return dictionary.get(key, args);
     354    }
     355    /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR>
     356     * Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {<I>n</I>}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Farg<I>n</I>.
     357     * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
     358     * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
     359     * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatically populated with formatting Strings of with argument String provided in the get call.
     360     * @see org.greenstone.gatherer.Gatherer
     361     * @see org.greenstone.gatherer.Dictionary
     362     */
     363    public String get(String key, String args[]) {
     364    return dictionary.get(key, args);
     365    }
     366    /** Retrieve the metadata directory, as required by any MSMCaller implementation.
     367     * @return The currently active collection metadata directory as a <strong>String</strong>.
     368     * @see org.greenstone.gatherer.collection.CollectionManager
     369     */
     370    public String getCollectionMetadata() {
     371    if(c_man != null && c_man.ready()) {
     372        return c_man.getCollectionMetadata();
     373    }
     374    return "";
     375    }
     376    /** Retrieve a reference to the frame that any dialog boxes will appear relative to, as required by any MSMCaller or CDMCaller implementation.
     377     * @return A <strong>JFrame</strong>.
     378     * @see org.greenstone.gatherer.gui.GUIManager
     379     */
     380    public JFrame getFrame() {
     381    return g_man;
     382    }
     383    /** Method to retrieve a reference to the metadata set manager class. This is then used to create the 'metadataset' commands in the collection configuration file.
     384     * @return A reference to the <Strong>MetadataSetManager</strong>.
     385     * @see org.greenstone.gatherer.collection.CollectionManager
     386     */
     387    public MetadataSetManager getMSM() {
     388    if(c_man != null && c_man.getCollection() != null && c_man.getCollection().msm != null) {
     389        return c_man.getCollection().msm;
     390    }
     391    return null;
     392    }
     393    /** Used to 'spawn' a new child application when a file is double clicked.
     394     * @param command The command to run in the child process to start the application, garnered from the registry of a default associations file, and presented as a <strong>String</strong>.
     395     * @see java.util.Vector
     396     * @see org.greenstone.gatherer.Gatherer.ExternalApplication
     397     */
     398    public void spawnApplication(File file) {
     399    String command = assoc_man.getCommand(file);
     400    if(command != null) {
     401        ExternalApplication app = new ExternalApplication(command);
     402        apps.add(app);
     403        app.start();
     404    }
     405    else {
    406406                ///ystem.err.println("No open command available.");
    407           }
    408     }
    409 
    410     /** Some startup arguments to the Gatherer have been encoded, where ' ' is replaced with '%', in order to allow arguments containing spaces to be parsed correctly by the JVM, and this method restores these arguments to thier original state.
    411       * @param encoded An encoded <strong>String</strong>.
    412       * @return The decoded <strong>String</strong>.
    413       */
     407    }
     408    }
     409
     410    /** Some startup arguments to the Gatherer have been encoded, where ' ' is replaced with '%', in order to allow arguments containing spaces to be parsed correctly by the JVM, and this method restores these arguments to thier original state.
     411     * @param encoded An encoded <strong>String</strong>.
     412     * @return The decoded <strong>String</strong>.
     413     */
    414414    static public String decode(String encoded) {
    415           return encoded.replace('%', ' ');
    416     }
    417     /** The entry point into the Gatherer. Parses arguments.
    418       * @param args A collection of arguments that may include: initial screen size, dictionary, path to the GSDL etc.
    419       * @see java.io.File
    420       * @see java.io.FileInputStream
    421       * @see java.lang.Exception
    422       * @see java.util.Properties
    423       * @see org.greenstone.gatherer.Dictionary
    424       * @see org.greenstone.gatherer.Gatherer
    425       * @see org.greenstone.gatherer.gui.Splash
    426       */
    427     static public void main(String[] args) {
    428           // A serious hack, but its good enough to stop crappy 'Could not lock user prefs' error messages.
    429           // Thanks to Walter Schatz from the java forums.
    430           System.setProperty("java.util.prefs.syncInterval","2000000"); // One message every 600 hours!
    431 
    432           Gatherer gatherer = new Gatherer();
    433 
    434           boolean debug = false;
    435           boolean no_load = false;
    436           Dictionary dictionary = new Dictionary(null, null); // Default dictionary. Only used for starting error messages.
    437           Dimension size = new Dimension(800, 540);
    438           String exec_path = null;
    439           String extra = null;
    440           String filename = null;
    441           String gsdl_path = null;
    442           String perl_path = null;
    443           // Parse arguments
    444           for(int i = 0; i < args.length; i++) {
    445               if(args[i].equals("-gsdl")) {
    446                      gsdl_path = decode(args[i+1]);
    447                      if(!gsdl_path.endsWith(File.separator)) {
    448                           gsdl_path = gsdl_path + File.separator;
    449                      }
    450               }
    451                 if(args[i].equals("-load")) {
    452                      filename = decode(args[i+1]);
    453               }
    454               else if(args[i].equals("-library") && (i + 1) < args.length) {
    455                      exec_path = args[i+1];
    456                      // If we are on a non-windows system, and thus the local server is unavailable, we can append http:// if no protocol found.
    457                      if(exec_path.lastIndexOf(":", 5) == -1) {
    458                           exec_path = "http://" + exec_path;
    459                      }
    460                      // If the user has given us an address, but it ends with a '/' we assume we're using the greenstone library.cgi
    461                      if(exec_path.startsWith("http://") && exec_path.endsWith("/")) {
    462                           exec_path = exec_path + "library";
    463                      }
    464               }
    465               else if(args[i].equals("-perl")) {
    466                      perl_path = decode(args[i+1]);
    467                      // Test whether this points to the Perl bin directory or the Perl executable itself.
    468                      File perl_file = new File(perl_path);
    469                      if(perl_file.isDirectory()) {
    470                           // If this is windows we create a child file perl.exe, otherwise we create perl
    471                           if(Utility.isWindows()) {
    472                                 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_WINDOWS);
    473                           }
    474                           else {
    475                                 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_UNIX);
    476                           }
    477                           // And store this new path.
    478                           perl_path = perl_file.getAbsolutePath();
    479                           perl_file = null;
    480                      }
    481                      // Otherwise its fine as it is
    482                      ///ystem.err.println("Perl executable is: " + perl_path);
    483               }
    484               else if(args[i].equals("--help") || args[i].equals("-help") || args[i].equals("?") || args[i].equals("/?") || args[i].equals("/help")) {
    485                      printUsage(dictionary);
    486                      System.exit(0);
    487               }
    488               else if(args[i].equals("--debug") || args[i].equals("-debug")) {
    489                      debug = true;
    490               }
     415    return encoded.replace('%', ' ');
     416    }
     417    /** The entry point into the Gatherer. Parses arguments.
     418     * @param args A collection of arguments that may include: initial screen size, dictionary, path to the GSDL etc.
     419     * @see java.io.File
     420     * @see java.io.FileInputStream
     421     * @see java.lang.Exception
     422     * @see java.util.Properties
     423     * @see org.greenstone.gatherer.Dictionary
     424     * @see org.greenstone.gatherer.Gatherer
     425     * @see org.greenstone.gatherer.gui.Splash
     426     */
     427    static public void main(String[] args) {
     428    // A serious hack, but its good enough to stop crappy 'Could not lock user prefs' error messages.
     429    // Thanks to Walter Schatz from the java forums.
     430    System.setProperty("java.util.prefs.syncInterval","2000000"); // One message every 600 hours!
     431
     432    Gatherer gatherer = new Gatherer();
     433
     434    boolean debug = false;
     435    boolean no_load = false;
     436    Dictionary dictionary = new Dictionary(null, null); // Default dictionary. Only used for starting error messages.
     437    Dimension size = new Dimension(800, 540);
     438    String exec_path = null;
     439    String extra = null;
     440    String filename = null;
     441    String gsdl_path = null;
     442    String perl_path = null;
     443    // Parse arguments
     444    for(int i = 0; i < args.length; i++) {
     445        if(args[i].equals("-gsdl")) {
     446        gsdl_path = decode(args[i+1]);
     447        if(!gsdl_path.endsWith(File.separator)) {
     448            gsdl_path = gsdl_path + File.separator;
     449        }
     450        }
     451        if(args[i].equals("-load")) {
     452        filename = decode(args[i+1]);
     453        }
     454        else if(args[i].equals("-library") && (i + 1) < args.length) {
     455        exec_path = args[i+1];
     456        // If we are on a non-windows system, and thus the local server is unavailable, we can append http:// if no protocol found.
     457        if(exec_path.lastIndexOf(":", 5) == -1) {
     458            exec_path = "http://" + exec_path;
     459        }
     460        // If the user has given us an address, but it ends with a '/' we assume we're using the greenstone library.cgi
     461        if(exec_path.startsWith("http://") && exec_path.endsWith("/")) {
     462            exec_path = exec_path + "library";
     463        }
     464        }
     465        else if(args[i].equals("-perl")) {
     466        perl_path = decode(args[i+1]);
     467        // Test whether this points to the Perl bin directory or the Perl executable itself.
     468        File perl_file = new File(perl_path);
     469        if(perl_file.isDirectory()) {
     470            // If this is windows we create a child file perl.exe, otherwise we create perl
     471            if(Utility.isWindows()) {
     472            perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_WINDOWS);
     473            }
     474            else {
     475            perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_UNIX);
     476            }
     477            // And store this new path.
     478            perl_path = perl_file.getAbsolutePath();
     479            perl_file = null;
     480        }
     481        // Otherwise its fine as it is
     482        ///ystem.err.println("Perl executable is: " + perl_path);
     483        }
     484        else if(args[i].equals("--help") || args[i].equals("-help") || args[i].equals("?") || args[i].equals("/?") || args[i].equals("/help")) {
     485        printUsage(dictionary);
     486        System.exit(0);
     487        }
     488        else if(args[i].equals("--debug") || args[i].equals("-debug")) {
     489        debug = true;
     490        }
    491491                // Don't load any previous collection. Convenient for me
    492                 else if(args[i].equals("-no_load")) {
    493                      no_load = true;
    494                 }
     492        else if(args[i].equals("-no_load")) {
     493        no_load = true;
     494        }
    495495                // Check if they want it skinned.
    496                 else if(args[i].equals("-skinlf")) {
    497                      // SkinLF
    498                      try {
    499                           SkinLookAndFeel.setSkin(SkinLookAndFeel.loadThemePackDefinition(SkinUtils.toURL(new File("lib/greenaqua/greenaqua.xml"))));
    500                           SkinLookAndFeel.enable();
    501                      }
    502                      catch (Exception error) {
    503                           ///ystem.err.println("Error: " + error);
    504                           error.printStackTrace();
    505                      }
    506                 }
    507           }
    508 
    509           // Splash screen.
    510           Splash splash = new Splash();
    511 
    512           // We take appropriate action when an empty gsdl_path is detected.
    513           if(gsdl_path == null) {
     496        else if(args[i].equals("-skinlf")) {
     497        // SkinLF
     498        try {
     499            SkinLookAndFeel.setSkin(SkinLookAndFeel.loadThemePackDefinition(SkinUtils.toURL(new File("lib/greenaqua/greenaqua.xml"))));
     500            SkinLookAndFeel.enable();
     501        }
     502        catch (Exception error) {
     503            ///ystem.err.println("Error: " + error);
     504            error.printStackTrace();
     505        }
     506        }
     507    }
     508
     509    // Splash screen.
     510    Splash splash = new Splash();
     511
     512    // We take appropriate action when an empty gsdl_path is detected.
     513    if(gsdl_path == null) {
    514514                // Check for the presence of the path.cfg file.
    515                 File path_file = new File("path.cfg");
    516                 if(path_file.exists()) {
    517                      try {
    518                           // Read in then add each property to the Java Environment.
    519                           FileInputStream prop_file = new FileInputStream(path_file);
    520                           Properties p = new Properties();
    521                           p.load(prop_file);
    522                           extra = p.getProperty(KEY);
    523                      }
    524                      catch (Exception error) {
    525                           System.out.println("Error in main():");
    526                           error.printStackTrace();
    527                           System.exit(1);
    528                      }
    529                 }
    530                 else {
    531                      missingGSDL(dictionary);
    532                 }
    533           }
    534           // We take appropriate action when an empty bin_path is detected.
    535           gatherer.run(size, gsdl_path, exec_path, debug, perl_path, no_load, splash, filename);
    536     }
    537     /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
    538       */
    539     static public void missingEXEC(Dictionary dictionary) {
    540           WarningDialog dialog = new WarningDialog("warning.MissingEXEC", false);
    541           dialog.display();
    542           dialog.dispose();
    543           dialog = null;
    544           ///ystem.out.println(dictionary.get("General.Missing_EXEC"));
    545     }
    546     /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer.
    547       */
    548     static public void missingGSDL(Dictionary dictionary) {
    549           WarningDialog dialog = new WarningDialog("warning.MissingGSDL", false);
    550           dialog.display();
    551           dialog.dispose();
    552           dialog = null;
    553           ///ystem.out.println(dictionary.get("General.Missing_GSDL"));
    554     }
    555     /** Prints a warning message about a missing PERL path, which although not fatal pretty much ensures no collection creation/building will work properly in the Gatherer. */
    556     static public void missingPERL(Dictionary dictionary) {
    557           WarningDialog dialog = new WarningDialog("warning.MissingPERL", false);
    558           dialog.display();
    559           dialog.dispose();
    560           dialog = null;
    561           ///ystem.out.println(dictionary.get("General.Missing_PERL"));
    562     }
    563     /** Print a message to the debug stream. */
    564     static synchronized public void println(String message) {
    565           if(debug != null) {
    566                 debug.println(message);
    567           }
    568           else {
    569                 System.err.println(message);
    570           }
    571     }
    572     /** Print a stack trace to the debug stream. */
    573     static synchronized public void printStackTrace(Exception exception) {
    574           if(debug != null) {
    575                 exception.printStackTrace(debug);
    576           }
    577           else {
    578                 exception.printStackTrace();
    579           }
    580     }
    581     /** Prints a usage message to screen.
    582       */
    583     static public void printUsage(Dictionary dictionary) {
    584           System.out.println(dictionary.get("General.Usage"));
    585     }
    586 
    587     /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
    588       * @see java.lang.Exception
    589       * @see java.lang.System
    590       * @see java.net.Authenticator
    591       * @see org.greenstone.gatherer.Configuration
    592       * @see org.greenstone.gatherer.GAuthenticator
    593       */
    594     static public void setProxy() {
    595           try {// Can throw several exceptions
    596                 if(Gatherer.config.get("general.use_proxy", true)) {
    597                      System.setProperty("http.proxyType", "4");
    598                      System.setProperty("http.proxyHost", Gatherer.config.getString("general.proxy_host", true));
    599                      System.setProperty("http.proxyPort", Gatherer.config.getString("general.proxy_port", true));
    600                      System.setProperty("http.proxySet", "true");
    601                 } else {
    602                      System.setProperty("http.proxySet", "false");
    603                 }
    604           } catch (Exception error) {
    605                 Gatherer.println("Error in Gatherer.initProxy(): " + error);
    606                 Gatherer.printStackTrace(error);
    607           }
    608     }
    609 
    610     /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font.
    611       * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>.
    612       * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>.
    613       * @see java.util.Enumeration
    614       * @see javax.swing.UIManager
    615       */
    616     static public void setUIFont (FontUIResource f, FontUIResource ttf){
    617           // sets the default font for all Swing components.
    618           // ex.
    619           //  setUIFont (new FontUIResource("Serif",Font.ITALIC,12));
    620           //
    621           Enumeration keys = UIManager.getDefaults().keys();
    622           while (keys.hasMoreElements()) {
    623                 Object key = keys.nextElement();
    624                 Object value = UIManager.get (key);
    625                 if (value instanceof FontUIResource)
    626                      UIManager.put (key, f);
    627           }
    628           // Now set the tooltip font to some fixed width font
    629           UIManager.put("ToolTip.font", ttf);
    630     }
    631 
    632     /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization.
    633       * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>.
    634       * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
    635       * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
    636       * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
    637       * @see java.io.FileInputStream
    638       * @see java.io.ObjectInputStream
    639       * @see java.lang.Exception
    640       * @see org.greenstone.gatherer.Configuration
    641       */
    642     private void loadConfig(String gsdl_path, String exec_path, String perl_path) {
    643           try {
    644                 config = new Configuration(gsdl_path, exec_path, perl_path);
    645           }
    646           catch (Exception error) {
    647                 Gatherer.println("config.xml is not a well formed XML document.");
    648                 Gatherer.printStackTrace(error);
    649           }
    650     }
    651 
    652     /** Creates and dispatches a message given the initial details.
    653       * @param level An <i>int</i> indicating the message level for this message.
    654       * @param message A <strong>String</strong> which contains the payload of this message.
    655       * @see org.greenstone.gatherer.Message
    656       * @see org.greenstone.gatherer.Log
    657       */
    658     private void message(int level, String message) {
    659           Message msg = new Message(Message.GENERAL, level, message);
    660           log.add(msg);
    661     }
    662 
    663     /** Causes the general configuration file to export itself to xml. Doesn't effect any remaining collection configuration, as its up to the collection manager to handle them (especially since we have no idea where they are going). */
    664     private void saveConfig() {
    665           try {
    666                 config.save();
    667           } catch (Exception error) {
    668                 Gatherer.printStackTrace(error);
    669           }
    670     }
    671 
    672     private void startServerEXE() {
    673           if(config.exec_file != null && config.exec_address == null && Utility.isWindows()) {
     515        File path_file = new File("path.cfg");
     516        if(path_file.exists()) {
     517        try {
     518            // Read in then add each property to the Java Environment.
     519            FileInputStream prop_file = new FileInputStream(path_file);
     520            Properties p = new Properties();
     521            p.load(prop_file);
     522            extra = p.getProperty(KEY);
     523        }
     524        catch (Exception error) {
     525            System.out.println("Error in main():");
     526            error.printStackTrace();
     527            System.exit(1);
     528        }
     529        }
     530        else {
     531        missingGSDL(dictionary);
     532        }
     533    }
     534    // We take appropriate action when an empty bin_path is detected.
     535    gatherer.run(size, gsdl_path, exec_path, debug, perl_path, no_load, splash, filename);
     536    }
     537    /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
     538     */
     539    static public void missingEXEC(Dictionary dictionary) {
     540    WarningDialog dialog = new WarningDialog("warning.MissingEXEC", false);
     541    dialog.display();
     542    dialog.dispose();
     543    dialog = null;
     544    ///ystem.out.println(dictionary.get("General.Missing_EXEC"));
     545    }
     546    /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer.
     547     */
     548    static public void missingGSDL(Dictionary dictionary) {
     549    WarningDialog dialog = new WarningDialog("warning.MissingGSDL", false);
     550    dialog.display();
     551    dialog.dispose();
     552    dialog = null;
     553    ///ystem.out.println(dictionary.get("General.Missing_GSDL"));
     554    }
     555    /** Prints a warning message about a missing PERL path, which although not fatal pretty much ensures no collection creation/building will work properly in the Gatherer. */
     556    static public void missingPERL(Dictionary dictionary) {
     557    WarningDialog dialog = new WarningDialog("warning.MissingPERL", false);
     558    dialog.display();
     559    dialog.dispose();
     560    dialog = null;
     561    ///ystem.out.println(dictionary.get("General.Missing_PERL"));
     562    }
     563    /** Print a message to the debug stream. */
     564    static synchronized public void println(String message) {
     565    if(debug != null) {
     566        debug.println(message);
     567    }
     568    else {
     569        System.err.println(message);
     570    }
     571    }
     572    /** Print a stack trace to the debug stream. */
     573    static synchronized public void printStackTrace(Exception exception) {
     574    if(debug != null) {
     575        exception.printStackTrace(debug);
     576    }
     577    else {
     578        exception.printStackTrace();
     579    }
     580    }
     581    /** Prints a usage message to screen.
     582     */
     583    static public void printUsage(Dictionary dictionary) {
     584    System.out.println(dictionary.get("General.Usage"));
     585    }
     586
     587    /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
     588     * @see java.lang.Exception
     589     * @see java.lang.System
     590     * @see java.net.Authenticator
     591     * @see org.greenstone.gatherer.Configuration
     592     * @see org.greenstone.gatherer.GAuthenticator
     593     */
     594    static public void setProxy() {
     595    try {// Can throw several exceptions
     596        if(Gatherer.config.get("general.use_proxy", true)) {
     597        System.setProperty("http.proxyType", "4");
     598        System.setProperty("http.proxyHost", Gatherer.config.getString("general.proxy_host", true));
     599        System.setProperty("http.proxyPort", Gatherer.config.getString("general.proxy_port", true));
     600        System.setProperty("http.proxySet", "true");
     601        } else {
     602        System.setProperty("http.proxySet", "false");
     603        }
     604    } catch (Exception error) {
     605        Gatherer.println("Error in Gatherer.initProxy(): " + error);
     606        Gatherer.printStackTrace(error);
     607    }
     608    }
     609
     610    /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font.
     611     * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>.
     612     * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>.
     613     * @see java.util.Enumeration
     614     * @see javax.swing.UIManager
     615     */
     616    static public void setUIFont (FontUIResource f, FontUIResource ttf){
     617    // sets the default font for all Swing components.
     618    // ex.
     619    //  setUIFont (new FontUIResource("Serif",Font.ITALIC,12));
     620    //
     621    Enumeration keys = UIManager.getDefaults().keys();
     622    while (keys.hasMoreElements()) {
     623        Object key = keys.nextElement();
     624        Object value = UIManager.get (key);
     625        if (value instanceof FontUIResource)
     626        UIManager.put (key, f);
     627    }
     628    // Now set the tooltip font to some fixed width font
     629    UIManager.put("ToolTip.font", ttf);
     630    }
     631
     632    /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization.
     633     * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>.
     634     * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
     635     * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
     636     * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
     637     * @see java.io.FileInputStream
     638     * @see java.io.ObjectInputStream
     639     * @see java.lang.Exception
     640     * @see org.greenstone.gatherer.Configuration
     641     */
     642    private void loadConfig(String gsdl_path, String exec_path, String perl_path) {
     643    try {
     644        config = new Configuration(gsdl_path, exec_path, perl_path);
     645    }
     646    catch (Exception error) {
     647        Gatherer.println("config.xml is not a well formed XML document.");
     648        Gatherer.printStackTrace(error);
     649    }
     650    }
     651
     652    /** Creates and dispatches a message given the initial details.
     653     * @param level An <i>int</i> indicating the message level for this message.
     654     * @param message A <strong>String</strong> which contains the payload of this message.
     655     * @see org.greenstone.gatherer.Message
     656     * @see org.greenstone.gatherer.Log
     657     */
     658    private void message(int level, String message) {
     659    Message msg = new Message(Message.GENERAL, level, message);
     660    log.add(msg);
     661    }
     662
     663    /** Causes the general configuration file to export itself to xml. Doesn't effect any remaining collection configuration, as its up to the collection manager to handle them (especially since we have no idea where they are going). */
     664    private void saveConfig() {
     665    try {
     666        config.save();
     667    } catch (Exception error) {
     668        Gatherer.printStackTrace(error);
     669    }
     670    }
     671
     672    private void startServerEXE() {
     673    if(config.exec_file != null && config.exec_address == null && Utility.isWindows()) {
    674674                // First of all we create a GSDLSiteCFG object and check if a URL is already present, in which case the server is already running.
    675                 gsdlsite_cfg = new GSDLSiteConfig(config.exec_file);
    676                 String url = gsdlsite_cfg.getURL();
     675        gsdlsite_cfg = new GSDLSiteConfig(config.exec_file);
     676        String url = gsdlsite_cfg.getURL();
    677677                // If its already running then set exec address.
    678                 if(url != null) {
    679                      try {
    680                           config.exec_address = new URL(url);
    681                      }
    682                      catch(Exception error) {
    683                      }
    684                 }
     678        if(url != null) {
     679        try {
     680            config.exec_address = new URL(url);
     681        }
     682        catch(Exception error) {
     683        }
     684        }
    685685                // Otherwise its time to run the server in a spawned process.
    686                 if(config.exec_address == null && config.exec_file.exists()) {
    687                      // Configure for immediate entry. Note that this only works if the gsdlsite.cfg file exists.
    688                      gsdlsite_cfg.set();
    689                      // Spawn server
    690                      server = new ExternalApplication(config.exec_file.getAbsolutePath());
    691                      server.start();
    692                      // Now we have to wait until program has started. We do this by reloading and checking
    693                      try {
    694                           gsdlsite_cfg.load();
    695                           while((url = gsdlsite_cfg.getURL()) == null) {
    696                                 synchronized(this) {
    697                                     wait(1000);
    698                                 }
    699                                 gsdlsite_cfg.load();
    700                           }
    701                           // Ta-da. Now the url should be available.
    702                           config.exec_address = new URL(url);
    703                      }
    704                      catch (Exception error) {
    705                           error.printStackTrace();
    706                      }
    707                 }
     686        if(config.exec_address == null && config.exec_file.exists()) {
     687        // Configure for immediate entry. Note that this only works if the gsdlsite.cfg file exists.
     688        gsdlsite_cfg.set();
     689        // Spawn server
     690        server = new ExternalApplication(config.exec_file.getAbsolutePath());
     691        server.start();
     692        // Now we have to wait until program has started. We do this by reloading and checking
     693        try {
     694            gsdlsite_cfg.load();
     695            while((url = gsdlsite_cfg.getURL()) == null) {
     696            synchronized(this) {
     697                wait(1000);
     698            }
     699            gsdlsite_cfg.load();
     700            }
     701            // Ta-da. Now the url should be available.
     702            config.exec_address = new URL(url);
     703        }
     704        catch (Exception error) {
     705            error.printStackTrace();
     706        }
     707        }
    708708                // Can't do a damb thing.
    709           }
    710           System.err.println("Having started server.exe, exec_address is: " + config.exec_address);
    711     }
    712 
    713     private void stopServerEXE() {
    714          // See if its already exited for some reason.
    715          gsdlsite_cfg.load();
    716          if(gsdlsite_cfg.getURL() != null) {
    717               // Send the command for it to exit.
    718               //Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.QUIT_COMMAND);
    719               // Wait until it exits.
    720               try {
    721                     gsdlsite_cfg.load();
    722                     while(gsdlsite_cfg.getURL() != null) {
    723                         synchronized(this) {
    724                               wait(1000);
    725                         }
    726                         gsdlsite_cfg.load();
    727                     }
    728               }
    729               catch (Exception error) {
    730                     error.printStackTrace();
    731               }
    732          }
    733          // Restore the gsdlsite_cfg
    734          if(gsdlsite_cfg != null) {
    735               gsdlsite_cfg.restore();
    736          }
    737          gsdlsite_cfg = null;
    738     }
    739 
    740     /** This private class contains an instance of an external application running within a JVM shell. It is important that this process sits in its own thread, but its more important that when we exit the Gatherer we don't actually System.exit(0) the Gatherer object until the user has volunteerily ended all of these child processes. Otherwise when we quit the Gatherer any changes the users may have made in external programs will be lost and the child processes are automatically deallocated. */
    741     private class ExternalApplication
    742           extends Thread {
    743           private Process process = null;
    744           /** The initial command string given to this sub-process. */
    745           private String command = null;
    746           /** Constructor.
    747             * @param command The initial command <strong>String</strong>.
    748             */
    749           public ExternalApplication(String command) {
    750                 this.command = command;
    751           }
    752           /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
    753             * @see java.lang.Exception
    754             * @see java.lang.Process
    755             * @see java.lang.Runtime
    756             * @see java.lang.System
    757             * @see java.util.Vector
    758             */
    759           public void run() {
     709    }
     710    System.err.println("Having started server.exe, exec_address is: " + config.exec_address);
     711    }
     712
     713    private void stopServerEXE() {
     714    // See if its already exited for some reason.
     715    gsdlsite_cfg.load();
     716    if(gsdlsite_cfg.getURL() != null) {
     717        // Send the command for it to exit.
     718        //Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.QUIT_COMMAND);
     719        // Wait until it exits.
     720        try {
     721        gsdlsite_cfg.load();
     722        while(gsdlsite_cfg.getURL() != null) {
     723            synchronized(this) {
     724            wait(1000);
     725            }
     726            gsdlsite_cfg.load();
     727        }
     728        }
     729        catch (Exception error) {
     730        error.printStackTrace();
     731        }
     732    }
     733    // Restore the gsdlsite_cfg
     734    if(gsdlsite_cfg != null) {
     735        gsdlsite_cfg.restore();
     736    }
     737    gsdlsite_cfg = null;
     738    }
     739
     740    /** This private class contains an instance of an external application running within a JVM shell. It is important that this process sits in its own thread, but its more important that when we exit the Gatherer we don't actually System.exit(0) the Gatherer object until the user has volunteerily ended all of these child processes. Otherwise when we quit the Gatherer any changes the users may have made in external programs will be lost and the child processes are automatically deallocated. */
     741    private class ExternalApplication
     742    extends Thread {
     743    private Process process = null;
     744    /** The initial command string given to this sub-process. */
     745    private String command = null;
     746    /** Constructor.
     747     * @param command The initial command <strong>String</strong>.
     748     */
     749    public ExternalApplication(String command) {
     750        this.command = command;
     751    }
     752    /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
     753     * @see java.lang.Exception
     754     * @see java.lang.Process
     755     * @see java.lang.Runtime
     756     * @see java.lang.System
     757     * @see java.util.Vector
     758     */
     759    public void run() {
    760760                // Call an external process using the args.
    761                 try {
    762                      debug("Running " + command);
    763                      Runtime rt = Runtime.getRuntime();
    764                      process = rt.exec(command);
    765                      process.waitFor();
    766                 }
    767                 catch (Exception error) {
    768                      debug(error, "Error in ExternalApplication.run(): " + error);
    769                 }
     761        try {
     762        debug("Running " + command);
     763        Runtime rt = Runtime.getRuntime();
     764        process = rt.exec(command);
     765        process.waitFor();
     766        }
     767        catch (Exception error) {
     768        debug(error, "Error in ExternalApplication.run(): " + error);
     769        }
    770770                // Remove ourself from Gatherer list of threads.
    771                 apps.remove(this);
     771        apps.remove(this);
    772772                // Call exit if we were the last outstanding child process thread.
    773                 if(apps.size() == 0 && exit == true) {
    774                      stopServerEXE();
    775                      System.exit(0);
    776                 }
    777           }
    778           public void stopExternalApplication() {
    779             if(process != null) {
    780                 process.destroy();
    781             }
    782           }
    783     }
    784     /** This class is intented to detect a specific SIGNAL, in this case SIGINT, and exit properly, rather than letting the Gatherer be interrupted which has the potential to leave erroneous lock files. */
    785     private class CTRLCHandler
    786           implements SignalHandler {
    787             /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */
    788           private boolean ignore = false;
    789             /** The method called by the system to inform us a signal has occured.
    790             * @param sig The <strong>Signal</strong> itself.
    791             * @see org.greenstone.gatherer.collection.CollectionManager
    792             */
    793           public void handle(Signal sig) {
    794                 if(!ignore) {
    795                      ignore = true;
    796                      // handle SIGINT
    797                      System.out.println("Caught Ctrl-C...");
    798                      if(c_man != null && c_man.ready()) {
    799                           c_man.closeCollection();
    800                      }
    801                      exit();
    802                      ignore = false;
    803                 }
    804           }
    805     }
    806 
    807     private class PerlTest {
    808 
    809           private String[] command = new String[2];
    810 
    811           public PerlTest() {
    812                 if(Utility.isWindows()) {
    813                      command[0] = Utility.PERL_EXECUTABLE_WINDOWS;
    814                 }
    815                 else {
    816                      command[0] = Utility.PERL_EXECUTABLE_UNIX;
    817                 }
    818                 command[1] = "-version";
    819           }
    820 
    821           public boolean found() {
    822                 boolean found = false;
    823                 try {
    824                      Process prcs = Runtime.getRuntime().exec(command);
    825                      prcs.waitFor();
    826                      found = (prcs.exitValue() == 0);
    827                      prcs = null;
    828                 }
    829                 catch(Exception error) {
    830                 }
    831                 return found;
    832           }
    833 
    834           public String toString() {
    835                 return command[0];
    836           }
    837     }
     773        if(apps.size() == 0 && exit == true) {
     774        stopServerEXE();
     775        System.exit(0);
     776        }
     777    }
     778    public void stopExternalApplication() {
     779        if(process != null) {
     780        process.destroy();
     781        }
     782    }
     783    }
     784    /** This class is intented to detect a specific SIGNAL, in this case SIGINT, and exit properly, rather than letting the Gatherer be interrupted which has the potential to leave erroneous lock files. */
     785    private class CTRLCHandler
     786    implements SignalHandler {
     787    /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */
     788    private boolean ignore = false;
     789    /** The method called by the system to inform us a signal has occured.
     790     * @param sig The <strong>Signal</strong> itself.
     791     * @see org.greenstone.gatherer.collection.CollectionManager
     792     */
     793    public void handle(Signal sig) {
     794        if(!ignore) {
     795        ignore = true;
     796        // handle SIGINT
     797        System.out.println("Caught Ctrl-C...");
     798        if(c_man != null && c_man.ready()) {
     799            c_man.closeCollection();
     800        }
     801        exit();
     802        ignore = false;
     803        }
     804    }
     805    }
     806
     807    private class PerlTest {
     808
     809    private String[] command = new String[2];
     810
     811    public PerlTest() {
     812        if(Utility.isWindows()) {
     813        command[0] = Utility.PERL_EXECUTABLE_WINDOWS;
     814        }
     815        else {
     816        command[0] = Utility.PERL_EXECUTABLE_UNIX;
     817        }
     818        command[1] = "-version";
     819    }
     820
     821    public boolean found() {
     822        boolean found = false;
     823        try {
     824        Process prcs = Runtime.getRuntime().exec(command);
     825        prcs.waitFor();
     826        found = (prcs.exitValue() == 0);
     827        prcs = null;
     828        }
     829        catch(Exception error) {
     830        }
     831        return found;
     832    }
     833
     834    public String toString() {
     835        return command[0];
     836    }
     837    }
    838838}
    839839
  • trunk/gli/src/org/greenstone/gatherer/Log.java

    r4293 r4363  
    4747 */
    4848public class Log
    49     implements ActionListener {
    50     /** A reference to the dialog box the log will be displayed in so our inner classes have the ability to interact. */
    51     private JDialog dialog  = null;
    52     /** Constructor */
    53     public Log() {
    54     }
    55     /** Any implementation of ActionListener must include this method so
     49    implements ActionListener {
     50    /** A reference to the dialog box the log will be displayed in so our inner classes have the ability to interact. */
     51    private JDialog dialog  = null;
     52    /** Constructor */
     53    public Log() {
     54    }
     55    /** Any implementation of ActionListener must include this method so
    5656      * we can be informed when an action has occured.
    5757      * @param event An <strong>ActionEvent</strong> which contains the details about the
    5858      * event which fired this call.
    5959      */
    60     public void actionPerformed(ActionEvent event) {
    61           if(event.getSource() == dialog) {
    62                 dialog.dispose();
    63           }
    64     }
    65     /** Add a message to the log. Note that this may be called from within
     60    public void actionPerformed(ActionEvent event) {
     61    if(event.getSource() == dialog) {
     62        dialog.dispose();
     63    }
     64    }
     65    /** Add a message to the log. Note that this may be called from within
    6666      * several different threads so I'd better synchronize.
    6767      * @param message A <strong>Message</strong> which contains, surprisingly enough, the
     
    6969      * @see org.greenstone.gatherer.Gatherer
    7070      */
    71     public synchronized void add(Message message) {
    72           //Gatherer.println(message.toString());
    73           /* @TODO */
    74     }
    75     /** Create a modal dialog box components to allow the user to view, and
     71    public synchronized void add(Message message) {
     72    //Gatherer.println(message.toString());
     73    /* @TODO */
     74    }
     75    /** Create a modal dialog box components to allow the user to view, and
    7676      * filter the log.
    7777      */
    78     public void display() {
    79           /* @TODO */
    80     }
     78    public void display() {
     79    /* @TODO */
     80    }
    8181}
    8282
  • trunk/gli/src/org/greenstone/gatherer/Message.java

    r4293 r4363  
    5353import java.util.Random;
    5454/** Provides an event-type wrapper around a message for displaying to the user.
    55 * @author John Thompson, Greenstone Digital Library, University of Waikato
    56 * @version 2.1
    57 */
     55 * @author John Thompson, Greenstone Digital Library, University of Waikato
     56 * @version 2.1
     57 */
    5858public class Message {
    59     /** The level of this message. See above. */
    60     public int level;
    61     /** The source of this message. See above. */
    62     public int source;
    63     /** The age of this message. Used for ordering. */
    64     public long time;
    65     /** The actual message content of this message. */
    66     public String message;
    67     /** Used to indicate the source of the message is anywhere in general. */
    68     static final public int GENERAL   = 0;
    69     /** Used to indicate the source of the message is the browsing methods. */
    70     static final public int BROWSER   = 1;
    71     /** Used to indicate the source of the message is the mirroring methods. */
    72     static final public int MIRRORING = 2;
    73     /** Used to indicate the source of the message is the file collection methods. */
    74     static final public int COLLECT   = 3;
    75     /** Used to indicate the source of the message is the metadata and set methods. */
    76     static final public int METAEDIT  = 4;
    77     /** Used to indicate the source of the message is the building methods. */
    78     static final public int BUILDING  = 5;
    79     /** Used to indicate the source of the message is the preview methods. */
    80     static final public int FINAL     = 6;
    81     /** Used to indicate the source of the message is the log itself. */
    82     static final public int LOG       = 7;
    83     /** Used to indicate the level of the message is Main. */
    84     static final public int MAIN = 0;
    85     /** Used to indicate the level of the message is Event. */
    86     static final public int EVENT = 1;
    87     /** Used to indicate the level of the message is Information. */
    88     static final public int INFO = 2;
    89     /** Used to indicate the level of the message is Error. */
    90     static final public int ERROR = 3;
     59    /** The level of this message. See above. */
     60    public int level;
     61    /** The source of this message. See above. */
     62    public int source;
     63    /** The age of this message. Used for ordering. */
     64    public long time;
     65    /** The actual message content of this message. */
     66    public String message;
     67    /** Used to indicate the source of the message is anywhere in general. */
     68    static final public int GENERAL   = 0;
     69    /** Used to indicate the source of the message is the browsing methods. */
     70    static final public int BROWSER   = 1;
     71    /** Used to indicate the source of the message is the mirroring methods. */
     72    static final public int MIRRORING = 2;
     73    /** Used to indicate the source of the message is the file collection methods. */
     74    static final public int COLLECT   = 3;
     75    /** Used to indicate the source of the message is the metadata and set methods. */
     76    static final public int METAEDIT  = 4;
     77    /** Used to indicate the source of the message is the building methods. */
     78    static final public int BUILDING  = 5;
     79    /** Used to indicate the source of the message is the preview methods. */
     80    static final public int FINAL     = 6;
     81    /** Used to indicate the source of the message is the log itself. */
     82    static final public int LOG       = 7;
     83    /** Used to indicate the level of the message is Main. */
     84    static final public int MAIN = 0;
     85    /** Used to indicate the level of the message is Event. */
     86    static final public int EVENT = 1;
     87    /** Used to indicate the level of the message is Information. */
     88    static final public int INFO = 2;
     89    /** Used to indicate the level of the message is Error. */
     90    static final public int ERROR = 3;
    9191
    92     /**
    93       * Creates a new message.
    94       * @param source the source of this message as an <i>int</i>. See above.
    95       * @param level the level of this message as an <i>int</i>. See above.
    96       * @param message the message content of this message as a <strong>String</strong>.
    97       */
    98     public Message(int source, int level, String message) {
    99           this.level = level;
    100           this.source = source;
    101           time = (new Date()).getTime();
    102           this.message = message;
    103     }
    104     /**
    105       * Used to display a message, stating level and source.
    106       * @param gatherer the main <strong>Gatherer</strong> class, used to get at the Dictionary.
    107       * @see org.greenstone.gatherer.Dictionary
    108       */
    109     public String toString(Gatherer gatherer) {
    110           String source_str = "";
    111           switch(source) {
    112           case GENERAL:
    113                 source_str = gatherer.dictionary.get("Source.General");
    114                 break;
    115           case BROWSER:
    116                 source_str = gatherer.dictionary.get("Source.Browser");
    117                 break;
    118           case MIRRORING:
    119                 source_str = gatherer.dictionary.get("Source.Mirroring");
    120                 break;
    121           case COLLECT:
    122                 source_str = gatherer.dictionary.get("Source.Collect");
    123                 break;
    124           case METAEDIT:
    125                 source_str = gatherer.dictionary.get("Source.MetaEdit");
    126                 break;
    127           case BUILDING:
    128                 source_str = gatherer.dictionary.get("Source.Building");
    129                 break;
    130           default:
    131                 source_str = gatherer.dictionary.get("Source.Unknown");
    132           }
     92    /**
     93     * Creates a new message.
     94     * @param source the source of this message as an <i>int</i>. See above.
     95     * @param level the level of this message as an <i>int</i>. See above.
     96     * @param message the message content of this message as a <strong>String</strong>.
     97     */
     98    public Message(int source, int level, String message) {
     99    this.level = level;
     100    this.source = source;
     101    time = (new Date()).getTime();
     102    this.message = message;
     103    }
     104    /**
     105     * Used to display a message, stating level and source.
     106     * @param gatherer the main <strong>Gatherer</strong> class, used to get at the Dictionary.
     107     * @see org.greenstone.gatherer.Dictionary
     108     */
     109    public String toString(Gatherer gatherer) {
     110    String source_str = "";
     111    switch(source) {
     112    case GENERAL:
     113        source_str = gatherer.dictionary.get("Source.General");
     114        break;
     115    case BROWSER:
     116        source_str = gatherer.dictionary.get("Source.Browser");
     117        break;
     118    case MIRRORING:
     119        source_str = gatherer.dictionary.get("Source.Mirroring");
     120        break;
     121    case COLLECT:
     122        source_str = gatherer.dictionary.get("Source.Collect");
     123        break;
     124    case METAEDIT:
     125        source_str = gatherer.dictionary.get("Source.MetaEdit");
     126        break;
     127    case BUILDING:
     128        source_str = gatherer.dictionary.get("Source.Building");
     129        break;
     130    default:
     131        source_str = gatherer.dictionary.get("Source.Unknown");
     132    }
    133133
    134           String level_str = "";
    135           switch(level) {
    136           case MAIN:
    137                 level_str = gatherer.dictionary.get("Level.Main");
    138                 break;
    139           case EVENT:
    140                 level_str = gatherer.dictionary.get("Level.Event");
    141                 break;
    142           case INFO:
    143                 level_str = gatherer.dictionary.get("Level.Information");
    144                 break;
    145           case ERROR:
    146                 level_str = gatherer.dictionary.get("Level.Error");
    147                 break;
    148           default:
    149                 level_str = gatherer.dictionary.get("Level.Unknown");
    150           }
    151           return source_str + " " + level_str + " > " + message;
    152     }
    153     /** Used during testing testing to generate a random message.
    154     * @return A new <strong>Message</strong> object which has a random source, level and a message body of the current system time.
    155     */
    156     static public Message random() {
    157           Random generator = new Random();
    158           int s = generator.nextInt(6);
    159           int l = generator.nextInt(4);
    160           String m = new Long(System.currentTimeMillis()).toString();
    161           return new Message(s,l,m);
    162     }
     134    String level_str = "";
     135    switch(level) {
     136    case MAIN:
     137        level_str = gatherer.dictionary.get("Level.Main");
     138        break;
     139    case EVENT:
     140        level_str = gatherer.dictionary.get("Level.Event");
     141        break;
     142    case INFO:
     143        level_str = gatherer.dictionary.get("Level.Information");
     144        break;
     145    case ERROR:
     146        level_str = gatherer.dictionary.get("Level.Error");
     147        break;
     148    default:
     149        level_str = gatherer.dictionary.get("Level.Unknown");
     150    }
     151    return source_str + " " + level_str + " > " + message;
     152    }
     153    /** Used during testing testing to generate a random message.
     154     * @return A new <strong>Message</strong> object which has a random source, level and a message body of the current system time.
     155     */
     156    static public Message random() {
     157    Random generator = new Random();
     158    int s = generator.nextInt(6);
     159    int l = generator.nextInt(4);
     160    String m = new Long(System.currentTimeMillis()).toString();
     161    return new Message(s,l,m);
     162    }
    163163}
  • trunk/gli/src/org/greenstone/gatherer/WGet.java

    r4293 r4363  
    4545import org.greenstone.gatherer.util.GURL;
    4646/** This class provides access to the functionality of the WGet program, either by calling it via a shell script or by the JNI. It maintains a queue of pending jobs, and the component for showing these tasks to the user.
    47 * @author John Thompson, Greenstone Digital Library, University of Waikato
    48 * @version 2.3
    49 */
     47 * @author John Thompson, Greenstone Digital Library, University of Waikato
     48 * @version 2.3
     49 */
    5050public class WGet
    51     extends Thread {
    52     /** <i>true</i> if there is a task currently being carried out, <i>false</i> otherwise. */
    53     private boolean busy = false;
    54     /** <i>true</i> if verbose debug messages should be displayed, <i>false</i> otherwise. */
    55     private boolean debug = false;
    56     /** <i>true</i> if successfully completed tasks should be automatically removed from the job queue. */
    57     private boolean remove_complete_jobs = true;
    58     /** The panel that the task list will be shown it. */
    59     private JPanel list_pane;
    60     /** The job currently underway. */
    61     private Job job;
    62     /** A scroll pane which will be used to display the list of pending tasks. */
    63     private JScrollPane list_scroll;
    64     /** A queue of download tasks. */
    65     private Vector job_queue;
    66     /** A static flag used to switch between simple and advanced modes. If <i>true</i> the Process object is used to externally call the Wget program. If <i>false</i> the native WGet libraries are statically loaded and the JNI used to download directly. */
    67     static final private boolean simple = true;
    68     /** Load the WGet native library. */
    69     static {
    70           if(!simple) {
    71                 System.load(System.getProperty("user.dir") + File.separator + "libgatherer.so");
    72           }
    73     }
    74 
    75     /** Constructor. Nothing special. */
    76     public WGet() {
    77           job = null;
    78           job_queue = new Vector();
    79           list_pane = new JPanel();
    80           list_pane.setLayout(new BoxLayout(list_pane, BoxLayout.Y_AXIS));
    81           //list_pane.setLayout(new GridLayout(1,1));
    82           list_scroll = new JScrollPane(list_pane);
    83           //list_scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    84     }
    85 
    86     /** Called by the WGet native code to inform us of a new download starting within the given job.
     51    extends Thread {
     52    /** <i>true</i> if there is a task currently being carried out, <i>false</i> otherwise. */
     53    private boolean busy = false;
     54    /** <i>true</i> if verbose debug messages should be displayed, <i>false</i> otherwise. */
     55    private boolean debug = false;
     56    /** <i>true</i> if successfully completed tasks should be automatically removed from the job queue. */
     57    private boolean remove_complete_jobs = true;
     58    /** The panel that the task list will be shown it. */
     59    private JPanel list_pane;
     60    /** The job currently underway. */
     61    private Job job;
     62    /** A scroll pane which will be used to display the list of pending tasks. */
     63    private JScrollPane list_scroll;
     64    /** A queue of download tasks. */
     65    private Vector job_queue;
     66    /** A static flag used to switch between simple and advanced modes. If <i>true</i> the Process object is used to externally call the Wget program. If <i>false</i> the native WGet libraries are statically loaded and the JNI used to download directly. */
     67    static final private boolean simple = true;
     68    /** Load the WGet native library. */
     69    static {
     70    if(!simple) {
     71        System.load(System.getProperty("user.dir") + File.separator + "libgatherer.so");
     72    }
     73    }
     74
     75    /** Constructor. Nothing special. */
     76    public WGet() {
     77    job = null;
     78    job_queue = new Vector();
     79    list_pane = new JPanel();
     80    list_pane.setLayout(new BoxLayout(list_pane, BoxLayout.Y_AXIS));
     81    //list_pane.setLayout(new GridLayout(1,1));
     82    list_scroll = new JScrollPane(list_pane);
     83    //list_scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
     84    }
     85
     86    /** Called by the WGet native code to inform us of a new download starting within the given job.
    8787     * @param url The url that is being downloaded, as a <strong>String</strong>.
    8888     * @see org.greenstone.gatherer.collection.Job
    8989     */
    90     public synchronized void addDownload(String url) {
    91           job.addDownload(url);
    92     }
    93 
    94     /* Used to advise the Job of a newly parsed link. Its up to Job to decide if it already knows about this url, and if not to update its progress bar.
     90    public synchronized void addDownload(String url) {
     91    job.addDownload(url);
     92    }
     93
     94    /* Used to advise the Job of a newly parsed link. Its up to Job to decide if it already knows about this url, and if not to update its progress bar.
    9595      * @param url The url in question as a <strong>String</strong>.
    9696      * @param type <i>true</i> if this is an internal link, <i>false</i> for and external one.
     
    9898      * @see org.greenstone.gatherer.collection.Job
    9999      */
    100     public synchronized boolean addLink(String url, int type) {
    101           return job.addLink(url, type);
    102     }
    103 
    104     /* Whenever files are moved into or out of the collection we need to
     100    public synchronized boolean addLink(String url, int type) {
     101    return job.addLink(url, type);
     102    }
     103
     104    /* Whenever files are moved into or out of the collection we need to
    105105      * run convertLinks on the files remaining. This ensures that we have
    106106      * the most efficient balance between local and absolute links.
     
    110110      */
    111111     // public void convertLinks(Vector records) {
    112     public void convertLinks() {
    113           Vector args = new Vector();
    114      Vector files = new Vector();
    115      Vector urls = new Vector();
    116      // Args - there ain't many
    117      args.add("-d");
    118      args.add("-o");
    119      args.add("debug.txt");
    120      args.add("-P");
    121      args.add("/tmp/");
    122 
    123      // Downloaded urls (two entries for each record). The pattern here is:
    124      // local file
    125      // original url of file
    126      /*
    127      for(Enumeration e = records.elements(); e.hasMoreElements();) {
    128           urls.add(((GURL)e).getLocalName());
    129           urls.add(((GURL)e).getURL());
    130      }
    131      */
    132      //urls.add("/tmp//www.cs.waikato.ac.nz/index.html");
    133      //urls.add("http://www.cs.waikato.ac.nz/index.html");
    134      //urls.add("/tmp//www.cs.waikato.ac.nz/events.html");
    135      //urls.add("http://www.cs.waikato.ac.nz/events.html");
    136      //urls.add("/tmp//www.cs.waikato.ac.nz/history.html");
    137      //urls.add("http://www.cs.waikato.ac.nz/history.html");
    138      //urls.add("/tmp//www.cs.waikato.ac.nz/icons/cs_title_logo.gif");
    139      //urls.add("http://www.cs.waikato.ac.nz/icons/cs_title_logo.gif");
    140      //urls.add("/tmp//www.cs.waikato.ac.nz/icons/scms_title_logo.gif");
    141      //urls.add("http://www.cs.waikato.ac.nz/icons/scms_title_logo.gif");
    142 
    143      // Downloaded files (html only). We race back through our records
    144      // looking for html/text content ones.
    145      /*
    146      for(Enumeration e = records.elements(); e.hasMoreElements(); ) {
    147           GURL record = (GURL)e;
    148           if(record.isHTML()) {
    149                 files.add(record.getLocalName());
    150           }
    151      }
    152      */
    153      //files.add("/tmp//www.cs.waikato.ac.nz/index.html");
    154 
    155      //wren(args.size(), args.toArray(), urls.size(), urls.toArray(),
    156      //     files.size(), files.toArray());
    157   }
    158 
    159     /** This method is called to delete a certain job from the queue.
     112    public void convertLinks() {
     113    Vector args = new Vector();
     114    Vector files = new Vector();
     115    Vector urls = new Vector();
     116    // Args - there ain't many
     117    args.add("-d");
     118    args.add("-o");
     119    args.add("debug.txt");
     120    args.add("-P");
     121    args.add("/tmp/");
     122
     123    // Downloaded urls (two entries for each record). The pattern here is:
     124    // local file
     125    // original url of file
     126    /*
     127      for(Enumeration e = records.elements(); e.hasMoreElements();) {
     128      urls.add(((GURL)e).getLocalName());
     129      urls.add(((GURL)e).getURL());
     130      }
     131    */
     132    //urls.add("/tmp//www.cs.waikato.ac.nz/index.html");
     133    //urls.add("http://www.cs.waikato.ac.nz/index.html");
     134    //urls.add("/tmp//www.cs.waikato.ac.nz/events.html");
     135    //urls.add("http://www.cs.waikato.ac.nz/events.html");
     136    //urls.add("/tmp//www.cs.waikato.ac.nz/history.html");
     137    //urls.add("http://www.cs.waikato.ac.nz/history.html");
     138    //urls.add("/tmp//www.cs.waikato.ac.nz/icons/cs_title_logo.gif");
     139    //urls.add("http://www.cs.waikato.ac.nz/icons/cs_title_logo.gif");
     140    //urls.add("/tmp//www.cs.waikato.ac.nz/icons/scms_title_logo.gif");
     141    //urls.add("http://www.cs.waikato.ac.nz/icons/scms_title_logo.gif");
     142
     143    // Downloaded files (html only). We race back through our records
     144    // looking for html/text content ones.
     145    /*
     146      for(Enumeration e = records.elements(); e.hasMoreElements(); ) {
     147      GURL record = (GURL)e;
     148      if(record.isHTML()) {
     149      files.add(record.getLocalName());
     150      }
     151      }
     152    */
     153    //files.add("/tmp//www.cs.waikato.ac.nz/index.html");
     154
     155    //wren(args.size(), args.toArray(), urls.size(), urls.toArray(),
     156    //      files.size(), files.toArray());
     157    }
     158
     159    /** This method is called to delete a certain job from the queue.
    160160      * This job may be pending, complete or even in progress. However
    161161      * if it is currently downloading then the delete method must
     
    164164      * @param delete_me The <strong>Job</strong> that is to be deleted.
    165165      */
    166     public void deleteJob(Job delete_me) {
    167           if(delete_me == job) {
     166    public void deleteJob(Job delete_me) {
     167    if(delete_me == job) {
    168168                // While this seems wasteful its only for the briefest moment.
    169                 while(busy) {
    170                 }
    171                 job = null;
    172           }
    173           if(delete_me.hasSignalledStop()) {
    174                 list_pane.remove(delete_me.getProgressBar());
    175                 list_pane.updateUI();
    176                 job_queue.remove(delete_me);
    177           }
    178           else {
    179                 Gatherer.println("Somehow we're trying to delete a job that is still running.");
    180           }
    181     }
    182 
    183   /** Called by the WGet native code when the current download,
     169        while(busy) {
     170        }
     171        job = null;
     172    }
     173    if(delete_me.hasSignalledStop()) {
     174        list_pane.remove(delete_me.getProgressBar());
     175        list_pane.updateUI();
     176        job_queue.remove(delete_me);
     177    }
     178    else {
     179        Gatherer.println("Somehow we're trying to delete a job that is still running.");
     180    }
     181    }
     182
     183    /** Called by the WGet native code when the current download,
    184184    * for the indicated job, is completed. In turn all download
    185185    * listeners are informed.
    186186    * @see org.greenstone.gatherer.collection.Job
    187187    */
    188   public synchronized void downloadComplete() {
    189      job.downloadComplete();
    190   }
    191 
    192   /** Called by the WGet native code when the requested download returns
     188    public synchronized void downloadComplete() {
     189    job.downloadComplete();
     190    }
     191
     192    /** Called by the WGet native code when the requested download returns
    193193    * a status code other than 200-399 for the specified download job.
    194194    * for.
    195195    * @see org.greenstone.gatherer.collection.Job
    196196    */
    197   public synchronized void downloadFailed() {
    198      // Add the failed download as a new job if the user so requests.
    199      job.downloadFailed();
    200   }
    201 
    202   /** Called by the WGet native code when some non-fatal error has caused
     197    public synchronized void downloadFailed() {
     198    // Add the failed download as a new job if the user so requests.
     199    job.downloadFailed();
     200    }
     201
     202    /** Called by the WGet native code when some non-fatal error has caused
    203203    * a download to fail. An example of a warning would be if a file can't
    204204    * be downloaded as doing so would clobber an existing file and the -nc
     
    206206    * @see org.greenstone.gatherer.collection.Job
    207207    */
    208   public synchronized void downloadWarning() {
    209      job.downloadWarning();
    210   }
    211 
    212     /** Used by other graphic functions to get a reference to the
     208    public synchronized void downloadWarning() {
     209    job.downloadWarning();
     210    }
     211
     212    /** Used by other graphic functions to get a reference to the
    213213      * scroll pane containing the current list of jobs.
    214214      */
    215     public JScrollPane getJobList() {
    216           return list_scroll;
    217     }
    218 
    219   /** Returns the current state of the stop flag for the job indicated.
     215    public JScrollPane getJobList() {
     216    return list_scroll;
     217    }
     218
     219    /** Returns the current state of the stop flag for the job indicated.
    220220    * @return A boolean representing whether the user has requested to
    221221    * stop.
    222222    * @see org.greenstone.gatherer.collection.Job
    223223    */
    224   public synchronized boolean hasSignalledStop() {
    225      return job.hasSignalledStop();
    226   }
    227 
    228     /** Creates a new mirroring job on the queue given the target url and the destination (private, public). All other details are harvested from the config file, but these two must be  captured from the GUI's current state.
     224    public synchronized boolean hasSignalledStop() {
     225    return job.hasSignalledStop();
     226    }
     227
     228    /** Creates a new mirroring job on the queue given the target url and the destination (private, public). All other details are harvested from the config file, but these two must be  captured from the GUI's current state.
    229229      * @param url A <strong>GURL</strong> which points to the root url for the mirroring.
    230230      * @param model The <strong>GTreeModel</strong> that any new records should be added to.
     
    236236      * @see org.greenstone.gatherer.util.GURL
    237237      */
    238     public void newJob(GURL url, TreeModel model, String destination) {
    239           if(url.valid()) {
     238    public void newJob(GURL url, TreeModel model, String destination) {
     239    if(url.valid()) {
    240240                // Create the job and fill in the details from gatherer.config.
    241                 Gatherer.println("About to create a new job");
    242                 Job new_job = new Job(model, Gatherer.config.get("mirroring.overwrite", false), Gatherer.config.get("mirroring.debug", false), Gatherer.config.get("mirroring.no_parents", false), Gatherer.config.get("mirroring.other_hosts", false), Gatherer.config.get("mirroring.page_requisites", false), Gatherer.config.get("mirroring.quiet", false), url, Gatherer.config.getInt("mirroring.depth", false), destination, Gatherer.config.proxy_pass, Gatherer.config.proxy_user, this, simple);
     241        Gatherer.println("About to create a new job");
     242        Job new_job = new Job(model, Gatherer.config.get("mirroring.overwrite", false), Gatherer.config.get("mirroring.debug", false), Gatherer.config.get("mirroring.no_parents", false), Gatherer.config.get("mirroring.other_hosts", false), Gatherer.config.get("mirroring.page_requisites", false), Gatherer.config.get("mirroring.quiet", false), url, Gatherer.config.getInt("mirroring.depth", false), destination, Gatherer.config.proxy_pass, Gatherer.config.proxy_user, this, simple);
    243243                // Add to job_queue job list.
    244                 job_queue.add(new_job);
     244        job_queue.add(new_job);
    245245                // Now add it to the visual component, job list.
    246                 list_pane.add(new_job.getProgressBar());
     246        list_pane.add(new_job.getProgressBar());
    247247                //list_pane.setAlignmentX(Component.LEFT_ALIGNMENT);
    248                 list_pane.updateUI();
    249                 new_job = null;
    250                 synchronized(this) {
    251                      notify(); // Just incase its sleeping.
    252                 }
    253           }
    254     }
    255 
    256   /** Called by the WGet native code to signal the current progress of
     248        list_pane.updateUI();
     249        new_job = null;
     250        synchronized(this) {
     251        notify(); // Just incase its sleeping.
     252        }
     253    }
     254    }
     255
     256    /** Called by the WGet native code to signal the current progress of
    257257    * downloading for the specified job.
    258258    * @param current A long representing the number of bytes that have
     
    262262    * @see org.greenstone.gatherer.collection.Job
    263263    */
    264   public synchronized void updateProgress(long current, long expected) {
    265      job.updateProgress(current, expected);
    266   }
    267 
    268   /* There may be times when the download thread is sleeping, but the
     264    public synchronized void updateProgress(long current, long expected) {
     265    job.updateProgress(current, expected);
     266    }
     267
     268    /* There may be times when the download thread is sleeping, but the
    269269    * user has indicated that a previously paused job should now begin
    270270    * again. The flag within the job will change, so we tell the thread
    271271    * to start again.
    272272    */
    273   public void resumeThread() {
    274      synchronized(this) {
    275         notify(); // Just incase its sleeping.
    276      }
    277   }
    278 
    279   /* This begins the WGet thread, which simply iterates through the waiting
     273    public void resumeThread() {
     274    synchronized(this) {
     275        notify(); // Just incase its sleeping.
     276    }
     277    }
     278
     279    /* This begins the WGet thread, which simply iterates through the waiting
    280280    * jobs attempting each one. Successful downloads are removed from the
    281281    * waiting list.
     
    283283    * @see org.greenstone.gatherer.collection.Job
    284284    */
    285   public void run() {
    286      while(true) {
    287         // If there are jobs job_queue and we have more room.
    288         if(job_queue.size() > 0) {
    289           // Get the first job that isn't stopped.
    290           for(Enumeration e = job_queue.elements(); e.hasMoreElements();) {
    291             job = (Job) e.nextElement();
    292             if(job.getState() == Job.RUNNING) {
    293                 Gatherer.println("Job " + job.toString() + " Begun.");
     285    public void run() {
     286    while(true) {
     287        // If there are jobs job_queue and we have more room.
     288        if(job_queue.size() > 0) {
     289        // Get the first job that isn't stopped.
     290        for(Enumeration e = job_queue.elements(); e.hasMoreElements();) {
     291            job = (Job) e.nextElement();
     292            if(job.getState() == Job.RUNNING) {
     293            Gatherer.println("Job " + job.toString() + " Begun.");
    294294                // A lock to prevent us deleting this job while its being
    295295                // run, unless you want things to go really wrong.
    296                 busy = true;
    297                 if(simple) {
    298                     job.callWGet();
    299                 }
    300                 else {
    301                     job.callWGetNative();
    302                 }
    303                 busy = false;
    304                 Gatherer.println("Job " + job.toString() + " complete.");
     296            busy = true;
     297            if(simple) {
     298                job.callWGet();
     299            }
     300            else {
     301                job.callWGetNative();
     302            }
     303            busy = false;
     304            Gatherer.println("Job " + job.toString() + " complete.");
    305305                // And if the user has requested that complete jobs
    306306                // be removed, then remove it from the list.
    307                 if(remove_complete_jobs) {
    308                      deleteJob(job);
    309                 }
    310                 job = null;
    311              }
    312           }
     307            if(remove_complete_jobs) {
     308                deleteJob(job);
     309            }
     310            job = null;
     311            }
    313312        }
    314         // In order to save processor time, I'll suspend the thread
    315         // if theres no advantage to it running. Actions such as
    316         // new or complete jobs will resume the thread.
    317         else {
    318           try {
    319              synchronized(this) {
    320                   Gatherer.println("WGet thread is waiting for Jobs.");
    321                   wait();
    322              }
    323           } catch (InterruptedException e) {
    324              // Time to get going again.
    325           }
     313        }
     314        // In order to save processor time, I'll suspend the thread
     315        // if theres no advantage to it running. Actions such as
     316        // new or complete jobs will resume the thread.
     317        else {
     318        try {
     319            synchronized(this) {
     320            Gatherer.println("WGet thread is waiting for Jobs.");
     321            wait();
     322            }
     323        } catch (InterruptedException e) {
     324            // Time to get going again.
    326325        }
    327      } // End While.
    328   }
    329 
    330   /* Link to the call to the WGet Native method for downloading.
     326        }
     327    } // End While.
     328    }
     329   
     330    /* Link to the call to the WGet Native method for downloading.
    331331    * @param argc An int representing the number of elements in argv.
    332332    * @param argv An array of objects passed as arguments to wget.
     
    334334    * messages.
    335335    */
    336   public native int wget(int argc, Object argv[], boolean debug);
    337 
    338   /* Link to the call to the WGet Native method for converting url links.
     336    public native int wget(int argc, Object argv[], boolean debug);
     337
     338    /* Link to the call to the WGet Native method for converting url links.
    339339    * @param argc An int representing the number of object elements in argv.
    340340    * @param argv An array of objects passed as arguments to wget.
     
    346346    * @param filev An array of strings representing files.
    347347    */
    348    public native void wren(int argc, Object argv[], int urlc, Object urlv[], int filec, Object filev[]);
     348    public native void wren(int argc, Object argv[], int urlc, Object urlv[], int filec, Object filev[]);
    349349}
    350350
  • trunk/gli/src/org/greenstone/gatherer/cdm/custom/CustomAZList.java

    r4293 r4363  
    6161 */
    6262final public class CustomAZList
    63     implements CustomClassifier {
    64     /** When this the control dialog is disposed, should we go ahead a add this custom AZ list (by processing all file records and building a new hierarchy)? */
    65     private boolean process = false;
    66     /** A reference to the Gatherer for access to the Collection and MetadataSetManager. */
    67     private Gatherer gatherer = null;
    68     /** A mapping from a String pattern to a specific GValueNode within the hidden hierarchy. */
    69     private Hashtable mappings = null;
    70     /** The button used to cancel the dialog. */
    71     private JButton cancel = null;
    72     /** The button used to confirm and close the dialog. */
    73     private JButton ok = null;
    74     /** The dialog which contains the controls for configuring this custom classifier. */
    75     private JDialog controls = null;
    76     /** The checkbox used to indicate whether there is a name given for the classifier button (in the Greenstone collections menubar). */
    77     private JCheckBox buttonname_label = null;
    78     /** The checkbox used to indicate that the final classifier should be sorted in some way. */
    79     private JCheckBox sort_label = null;
    80     /** The control allowing you to choose the assigned metadata this classifier should be based on. */
    81     private JComboBox metadata = null;
    82     /** The metadata the final classifier should be sorted by. */
    83     private JComboBox sort = null;
    84     /** A field for entering the button name. */
    85     private JTextField buttonname = null;
    86     /** A field which provides a clear indication of the current ranges selected for your custom classifier. */
    87     private JTextField separators_preview = null;
    88     /** An array of togglebuttons showing what separations are available. */
    89     private JToggleButton separators[] = null;
    90     /** A private dictionary for this custom classifier. */
    91     private ResourceBundle dictionary = null;
    92     /** The name of the hidden metadata. We at least luck out in that we don't need a live reference to the metadata, because the user can never change it (or if they do its their own stinking fault). */
    93     private String hidden_mde_name = null;
    94     /** The name of this pseudo-classifier. */
    95     final static public String NAME = "CustomAZList";
    96     /** The size of a label. */
    97     final static private Dimension LABEL_SIZE = new Dimension(200,25);
    98     /** The size of the controls for this pseudo-classifier. */
    99     final static private Dimension SIZE = new Dimension(550,400);
    100     /** An array of values for the separators. */
    101     final static private String VALUES[] = {"Numbers", "A", "B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
    102     /** The same array as above, except as integer values (note that "Numbers" becomes "#"). */
    103     static public int INT_VALUES[] = {'#','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
    104     /** A third array which is almost the same as the others (can anyone else say poor code reuse) but subsitutes "#" for "Numbers". */
    105     static public String STRING_VALUES[] = {"#","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
    106     /** Default Constructor. Needed to load this class dynamically. */
    107     public CustomAZList() {}
    108     /** Constructor.
     63    implements CustomClassifier {
     64    /** When this the control dialog is disposed, should we go ahead a add this custom AZ list (by processing all file records and building a new hierarchy)? */
     65    private boolean process = false;
     66    /** A reference to the Gatherer for access to the Collection and MetadataSetManager. */
     67    private Gatherer gatherer = null;
     68    /** A mapping from a String pattern to a specific GValueNode within the hidden hierarchy. */
     69    private Hashtable mappings = null;
     70    /** The button used to cancel the dialog. */
     71    private JButton cancel = null;
     72    /** The button used to confirm and close the dialog. */
     73    private JButton ok = null;
     74    /** The dialog which contains the controls for configuring this custom classifier. */
     75    private JDialog controls = null;
     76    /** The checkbox used to indicate whether there is a name given for the classifier button (in the Greenstone collections menubar). */
     77    private JCheckBox buttonname_label = null;
     78    /** The checkbox used to indicate that the final classifier should be sorted in some way. */
     79    private JCheckBox sort_label = null;
     80    /** The control allowing you to choose the assigned metadata this classifier should be based on. */
     81    private JComboBox metadata = null;
     82    /** The metadata the final classifier should be sorted by. */
     83    private JComboBox sort = null;
     84    /** A field for entering the button name. */
     85    private JTextField buttonname = null;
     86    /** A field which provides a clear indication of the current ranges selected for your custom classifier. */
     87    private JTextField separators_preview = null;
     88    /** An array of togglebuttons showing what separations are available. */
     89    private JToggleButton separators[] = null;
     90    /** A private dictionary for this custom classifier. */
     91    private ResourceBundle dictionary = null;
     92    /** The name of the hidden metadata. We at least luck out in that we don't need a live reference to the metadata, because the user can never change it (or if they do its their own stinking fault). */
     93    private String hidden_mde_name = null;
     94    /** The name of this pseudo-classifier. */
     95    final static public String NAME = "CustomAZList";
     96    /** The size of a label. */
     97    final static private Dimension LABEL_SIZE = new Dimension(200,25);
     98    /** The size of the controls for this pseudo-classifier. */
     99    final static private Dimension SIZE = new Dimension(550,400);
     100    /** An array of values for the separators. */
     101    final static private String VALUES[] = {"Numbers", "A", "B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
     102    /** The same array as above, except as integer values (note that "Numbers" becomes "#"). */
     103    static public int INT_VALUES[] = {'#','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
     104    /** A third array which is almost the same as the others (can anyone else say poor code reuse) but subsitutes "#" for "Numbers". */
     105    static public String STRING_VALUES[] = {"#","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
     106    /** Default Constructor. Needed to load this class dynamically. */
     107    public CustomAZList() {}
     108    /** Constructor.
    109109      * @param gatherer A reference to the <strong>Gatherer</strong>.
    110110      */
    111     public CustomAZList(Gatherer gatherer) {
    112           this.gatherer = gatherer;
    113     }
    114     /** Used to compare this classifier to another classifier for the purposes of ordering.
     111    public CustomAZList(Gatherer gatherer) {
     112    this.gatherer = gatherer;
     113    }
     114    /** Used to compare this classifier to another classifier for the purposes of ordering.
    115115      * @param object The other classifier as an <strong>Object</strong>.
    116116      * @see org.greenstone.gatherer.cdm.Classifier
    117117      * @see org.greenstone.gatherer.cdm.CustomClassifier
    118118      */
    119     public int compareTo(Object object) {
    120           if(object instanceof Classifier) {
    121                 Classifier classifier = (Classifier) object;
    122                 return NAME.compareTo(classifier.getName());
    123           }
    124           else if(object instanceof CustomClassifier) {
    125                 CustomClassifier classifier = (CustomClassifier) object;
    126                 return NAME.compareTo(classifier.getName());
    127           }             
    128           return NAME.compareTo(object.toString());
    129     }
    130     /** Ensure that the dialog can be correctly garbage collected. */
    131     public void destroy() {
    132           controls = null;
    133     }
    134     /** Produce a new copy of this custom classifier. Remember that what the classifier manager does is create instances of all possible classifiers, then as they are assigned creates and assigns copies of the original reserve. This way we only have to parse arguments once.
     119    public int compareTo(Object object) {
     120    if(object instanceof Classifier) {
     121        Classifier classifier = (Classifier) object;
     122        return NAME.compareTo(classifier.getName());
     123    }
     124    else if(object instanceof CustomClassifier) {
     125        CustomClassifier classifier = (CustomClassifier) object;
     126        return NAME.compareTo(classifier.getName());
     127    }               
     128    return NAME.compareTo(object.toString());   
     129    }
     130    /** Ensure that the dialog can be correctly garbage collected. */
     131    public void destroy() {
     132    controls = null;
     133    }
     134    /** Produce a new copy of this custom classifier. Remember that what the classifier manager does is create instances of all possible classifiers, then as they are assigned creates and assigns copies of the original reserve. This way we only have to parse arguments once.
    135135      * @return A new <strong>CustomClassifier</strong> which is a copy of this one.
    136136      * @see org.greenstone.gatherer.Gatherer
    137137      */
    138     public CustomClassifier copy() {
    139           return new CustomAZList(gatherer);
    140     }
    141     /** Show the controls for configuring this pseudo-classifier.
     138    public CustomClassifier copy() {
     139    return new CustomAZList(gatherer);
     140    }
     141    /** Show the controls for configuring this pseudo-classifier.
    142142      * @param show <i>true</i> to actually display the configuration dialog on screen, <i>false</i> to do everything except show the control and process the data (useful for setting the control up and/or reloading custom classifiers from collect.cfg).
    143143      * @see org.greenstone.gatherer.Configuration
     
    152152      * @see org.greenstone.gatherer.msm.MetadataSetManager
    153153      */
    154     public boolean display(boolean show) {
    155           if(controls == null) {
    156                 Vector elements = gatherer.c_man.getCollection().msm.getAssignedElements();
     154    public boolean display(boolean show) {
     155    if(controls == null) {
     156        Vector elements = gatherer.c_man.getCollection().msm.getAssignedElements();
    157157                // Creation
    158                 controls = new JDialog();
    159                 controls.setModal(true);
    160                 controls.setSize(SIZE);
    161                 controls.setTitle(get("Title_JDialog"));
    162                 JPanel content_pane = (JPanel) controls.getContentPane();
    163                 JPanel upper_pane = new JPanel();
    164                 JPanel metadata_pane = new JPanel();
    165                 JLabel metadata_label = new JLabel(get("Metadata_JLabel"));
    166                 metadata_label.setPreferredSize(LABEL_SIZE);
    167                 metadata = new JComboBox(elements);
    168                 JPanel buttonname_pane = new JPanel();
    169                 buttonname_label = new JCheckBox(get("Buttonname_JCheckBox"));
    170                 buttonname_label.setPreferredSize(LABEL_SIZE);
    171                 buttonname = new JTextField();
    172                 buttonname.setBackground(Color.lightGray);
    173                 buttonname.setEnabled(false);
    174                 JPanel sort_pane = new JPanel();
    175                 sort_label = new JCheckBox(get("Sort_JLabel"));
    176                 sort = new JComboBox(elements);
    177                 JPanel separations_pane = new JPanel();
    178                 JLabel separations_label = new JLabel(get("Separations_JLabel"));
    179                 separations_label.setPreferredSize(LABEL_SIZE);
    180                 separators_preview = new JTextField();
    181                 separators_preview.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    182                 separators_preview.setEnabled(false); // View only.
    183                 separators_preview.setForeground(Color.black);
    184                 JPanel lower_pane = new JPanel();
    185                 JPanel button_pane = new JPanel();
    186                 ok = new JButton(get("OK_JButton"));
    187                 cancel = new JButton(get("Cancel_JButton"));
     158        controls = new JDialog();
     159        controls.setModal(true);
     160        controls.setSize(SIZE);
     161        controls.setTitle(get("Title_JDialog"));
     162        JPanel content_pane = (JPanel) controls.getContentPane();
     163        JPanel upper_pane = new JPanel();
     164        JPanel metadata_pane = new JPanel();
     165        JLabel metadata_label = new JLabel(get("Metadata_JLabel"));
     166        metadata_label.setPreferredSize(LABEL_SIZE);
     167        metadata = new JComboBox(elements);
     168        JPanel buttonname_pane = new JPanel();
     169        buttonname_label = new JCheckBox(get("Buttonname_JCheckBox"));
     170        buttonname_label.setPreferredSize(LABEL_SIZE);
     171        buttonname = new JTextField();
     172        buttonname.setBackground(Color.lightGray);
     173        buttonname.setEnabled(false);
     174        JPanel sort_pane = new JPanel();
     175        sort_label = new JCheckBox(get("Sort_JLabel"));
     176        sort = new JComboBox(elements);
     177        JPanel separations_pane = new JPanel();
     178        JLabel separations_label = new JLabel(get("Separations_JLabel"));
     179        separations_label.setPreferredSize(LABEL_SIZE);
     180        separators_preview = new JTextField();
     181        separators_preview.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     182        separators_preview.setEnabled(false); // View only.
     183        separators_preview.setForeground(Color.black);
     184        JPanel lower_pane = new JPanel();
     185        JPanel button_pane = new JPanel();
     186        ok = new JButton(get("OK_JButton"));
     187        cancel = new JButton(get("Cancel_JButton"));
    188188                // Connection
    189                 buttonname_label.addActionListener(new ButtonNameListener());
    190                 cancel.addActionListener(new CancelListener());
    191                 ok.addActionListener(new OKListener());
    192                 sort_label.addActionListener(new SortListener());
     189        buttonname_label.addActionListener(new ButtonNameListener());
     190        cancel.addActionListener(new CancelListener());
     191        ok.addActionListener(new OKListener());
     192        sort_label.addActionListener(new SortListener());
    193193                // The Toggle Button block. Done all at once so we don't end up with three for loops.
    194                 JPanel center_pane = new JPanel();
    195                 center_pane.setLayout(new GridLayout(3, 9));
    196                 separators = new JToggleButton[VALUES.length];
    197                 SeparatorListener sl = new SeparatorListener();
    198                 for(int i = 0; i < VALUES.length; i++) {
    199                      separators[i] = new JToggleButton(get(VALUES[i]));
    200                      separators[i].addActionListener(sl);
    201                      if(i != 0) {
    202                           center_pane.add(separators[i]);
    203                      }
    204                      else {
    205                           JLabel temp = new JLabel(get(VALUES[0]));
    206                           temp.setHorizontalAlignment(JLabel.CENTER);
    207                           center_pane.add(temp);
    208                      }
    209                 }
    210                 separators_preview.setText(getPreview(true));
     194        JPanel center_pane = new JPanel();
     195        center_pane.setLayout(new GridLayout(3, 9));
     196        separators = new JToggleButton[VALUES.length];
     197        SeparatorListener sl = new SeparatorListener();
     198        for(int i = 0; i < VALUES.length; i++) {
     199        separators[i] = new JToggleButton(get(VALUES[i]));
     200        separators[i].addActionListener(sl);
     201        if(i != 0) {
     202            center_pane.add(separators[i]);
     203        }
     204        else {
     205            JLabel temp = new JLabel(get(VALUES[0]));
     206            temp.setHorizontalAlignment(JLabel.CENTER);
     207            center_pane.add(temp);
     208        }
     209        }
     210        separators_preview.setText(getPreview(true));
    211211                // Layout
    212                 metadata_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
    213                 metadata_pane.setLayout(new BorderLayout());
    214                 metadata_pane.add(metadata_label, BorderLayout.WEST);
    215                 metadata_pane.add(metadata, BorderLayout.CENTER);
     212        metadata_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
     213        metadata_pane.setLayout(new BorderLayout());
     214        metadata_pane.add(metadata_label, BorderLayout.WEST);
     215        metadata_pane.add(metadata, BorderLayout.CENTER);
    216216               
    217                 buttonname_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
    218                 buttonname_pane.setLayout(new BorderLayout());
    219                 buttonname_pane.add(buttonname_label, BorderLayout.WEST);
    220                 buttonname_pane.add(buttonname, BorderLayout.CENTER);
     217        buttonname_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
     218        buttonname_pane.setLayout(new BorderLayout());
     219        buttonname_pane.add(buttonname_label, BorderLayout.WEST);
     220        buttonname_pane.add(buttonname, BorderLayout.CENTER);
    221221               
    222                 sort_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
    223                 sort_pane.setLayout(new BorderLayout());
    224                 sort_pane.add(sort_label, BorderLayout.WEST);
    225                 sort_pane.add(sort, BorderLayout.CENTER);
     222        sort_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
     223        sort_pane.setLayout(new BorderLayout());
     224        sort_pane.add(sort_label, BorderLayout.WEST);
     225        sort_pane.add(sort, BorderLayout.CENTER);
    226226
    227                 upper_pane.setBorder(BorderFactory.createEmptyBorder(0,0,4,0));
    228                 upper_pane.setLayout(new GridLayout(3, 1));
    229                 upper_pane.add(metadata_pane);
    230                 upper_pane.add(buttonname_pane);
    231                 upper_pane.add(separations_label);
     227        upper_pane.setBorder(BorderFactory.createEmptyBorder(0,0,4,0));
     228        upper_pane.setLayout(new GridLayout(3, 1));
     229        upper_pane.add(metadata_pane);
     230        upper_pane.add(buttonname_pane);
     231        upper_pane.add(separations_label);
    232232               
    233                 separations_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createRaisedBevelBorder()));
    234                 separations_pane.setLayout(new BorderLayout());
    235                 separations_pane.add(separators_preview, BorderLayout.CENTER);
     233        separations_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createRaisedBevelBorder()));
     234        separations_pane.setLayout(new BorderLayout());
     235        separations_pane.add(separators_preview, BorderLayout.CENTER);
    236236               
    237                 lower_pane.setLayout(new GridLayout(2,1));
    238                 lower_pane.add(separations_pane);
    239                 lower_pane.add(button_pane);
     237        lower_pane.setLayout(new GridLayout(2,1));
     238        lower_pane.add(separations_pane);
     239        lower_pane.add(button_pane);
    240240               
    241                 button_pane.setLayout(new GridLayout(1,2));
    242                 button_pane.add(ok);
    243                 button_pane.add(cancel);
     241        button_pane.setLayout(new GridLayout(1,2));
     242        button_pane.add(ok);
     243        button_pane.add(cancel);
    244244               
    245                 content_pane.setBorder(BorderFactory.createEmptyBorder(4,5,5,5));
    246                 content_pane.setLayout(new BorderLayout());
    247                 content_pane.add(upper_pane, BorderLayout.NORTH);
    248                 content_pane.add(center_pane, BorderLayout.CENTER);
    249                 content_pane.add(lower_pane, BorderLayout.SOUTH);
     245        content_pane.setBorder(BorderFactory.createEmptyBorder(4,5,5,5));
     246        content_pane.setLayout(new BorderLayout());
     247        content_pane.add(upper_pane, BorderLayout.NORTH);
     248        content_pane.add(center_pane, BorderLayout.CENTER);
     249        content_pane.add(lower_pane, BorderLayout.SOUTH);
    250250                // Display
    251                 Dimension screen_size = Gatherer.config.screen_size;
    252                 controls.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
    253                 screen_size = null;
    254                 if(show) {
    255                      controls.show();
    256                      // Create the new metadata, hierarchy etc.
    257                      if(process) {
    258                           process((ElementWrapper)metadata.getSelectedItem(), getPreview(false));
    259                           return true;
    260                      }
    261                      return false;
    262                 }
    263                 else {
    264                      return true;
    265                 }
     251        Dimension screen_size = Gatherer.config.screen_size;
     252        controls.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
     253        screen_size = null;
     254        if(show) {
     255        controls.show();
     256        // Create the new metadata, hierarchy etc.
     257        if(process) {
     258            process((ElementWrapper)metadata.getSelectedItem(), getPreview(false));
     259            return true;
     260        }
     261        return false;
     262        }
     263        else {
     264        return true;
     265        }
    266266                // We do everything -except- display the control.
    267           }
    268           else {
    269                 process = false;
    270                 controls.show();
    271                 if(process) {
    272                      process((ElementWrapper)metadata.getSelectedItem(), getPreview(false));
    273                      return true;
    274                 }
    275                 return false;
    276           }
    277     }
    278     /** Determine if this classifier and another given classifier are equal.
     267    }
     268    else {
     269        process = false;
     270        controls.show();
     271        if(process) {
     272        process((ElementWrapper)metadata.getSelectedItem(), getPreview(false));
     273        return true;
     274        }
     275        return false;
     276    }
     277    }
     278    /** Determine if this classifier and another given classifier are equal.
    279279      * @param object The classifier to check against for equality, as an <strong>Object</strong>.
    280280      * @see org.greenstone.gatherer.cdm.CustomClassifier
    281281      */
    282     public boolean equals(Object object) {
    283           if(object instanceof CustomClassifier) {
    284                 CustomClassifier classifier = (CustomClassifier) object;
    285                 if(getCommand().equalsIgnoreCase(classifier.getCommand())) {
    286                      return true;
    287                 }
    288                 return false;
    289           }
    290           else {
    291                 if(getCommand().equalsIgnoreCase(object.toString())) {
    292                      return true;
    293                 }
    294                 return false;
    295           }
    296     }
    297     /** Method to return this pseudo-classifier represented as a String.
     282    public boolean equals(Object object) {
     283    if(object instanceof CustomClassifier) {
     284        CustomClassifier classifier = (CustomClassifier) object;
     285        if(getCommand().equalsIgnoreCase(classifier.getCommand())) {
     286        return true;
     287        }
     288        return false;
     289    }
     290    else {
     291        if(getCommand().equalsIgnoreCase(object.toString())) {
     292        return true;
     293        }
     294        return false;
     295    }
     296    }
     297    /** Method to return this pseudo-classifier represented as a String.
    298298      * @return A <strong>String</strong>.
    299299      */
    300     public String getCommand() {
    301           StringBuffer text = new StringBuffer("classify ");
    302           text.append("Hierarchy");
    303           text.append(" -metadata ");
    304           text.append(hidden_mde_name); // @TODO
    305           if(buttonname_label.isSelected()) {
    306                 text.append(" -buttonname \"");
    307                 text.append(buttonname.getText());
    308                 text.append("\"");
    309           }
    310           text.append(" -hfile ");
    311           text.append(hidden_mde_name + ".txt");
    312           text.append(" -hlist_at_top");
    313           text.append("\n");
    314           return text.toString();
    315     }
    316     /** Retrieve the custom command, a command line that overrides and replaces some other 'actual' classifier.
     300    public String getCommand() {
     301    StringBuffer text = new StringBuffer("classify ");
     302    text.append("Hierarchy");
     303    text.append(" -metadata ");
     304    text.append(hidden_mde_name); // @TODO
     305    if(buttonname_label.isSelected()) {
     306        text.append(" -buttonname \"");
     307        text.append(buttonname.getText());
     308        text.append("\"");
     309    }
     310    text.append(" -hfile ");
     311    text.append(hidden_mde_name + ".txt");
     312    text.append(" -hlist_at_top");
     313    text.append("\n");
     314    return text.toString();
     315    }
     316    /** Retrieve the custom command, a command line that overrides and replaces some other 'actual' classifier.
    317317      * @param index The number of the classifer this one is replacing.
    318318      */
    319     public String getCustomCommand(int index) {
    320           StringBuffer text = new StringBuffer("customclassifier ");
    321           text.append(NAME);
    322           text.append(" -replaces CL");
    323           text.append(index);
    324           text.append(" -separations ");
    325           text.append(getPreview(false));
    326           return text.toString();
    327     }
    328     /** Get the name of this custom classifier.
     319    public String getCustomCommand(int index) {
     320    StringBuffer text = new StringBuffer("customclassifier ");
     321    text.append(NAME);
     322    text.append(" -replaces CL");
     323    text.append(index);
     324    text.append(" -separations ");
     325    text.append(getPreview(false));
     326    return text.toString();
     327    }
     328    /** Get the name of this custom classifier.
    329329      * @return The name as a <strong>String</strong>.
    330330      */
    331     public String getName() {
    332           return NAME;
    333     }
    334     /** Process a record by adding hidden metadata as necessary.
     331    public String getName() {
     332    return NAME;
     333    }
     334    /** Process a record by adding hidden metadata as necessary.
    335335      * @param record The <strong>FileNode</strong> to be edited.
    336336      * @see org.greenstone.gatherer.msm.ElementWrapper
    337337      */
    338     public void process(FileNode record) {
    339           ElementWrapper element = (ElementWrapper) metadata.getSelectedItem();
    340           if(element != null) {
    341                 addMetadata(0L, record, element);
    342           }
    343     }
    344     /** Recreate a CustomAZList given several parameters including the real classifier created during custom design.
     338    public void process(FileNode record) {
     339    ElementWrapper element = (ElementWrapper) metadata.getSelectedItem();
     340    if(element != null) {
     341        addMetadata(0L, record, element);
     342    }
     343    }
     344    /** Recreate a CustomAZList given several parameters including the real classifier created during custom design.
    345345      * @param classifier The real <strong>Classifier</strong>.
    346346      * @param separations A <strong>String</strong> representing the choosen separations.
     
    353353      * @see org.greenstone.gatherer.valuetree.GValueNode
    354354      */
    355     public void recreate(Classifier classifier, String separations) {
    356           // Rebuild controls
    357           display(false);
    358           // Calculate original element.
    359           hidden_mde_name = classifier.getArgument("metadata").getValue();
    360           ElementWrapper hidden_mde = gatherer.c_man.getCollection().msm.getElement(hidden_mde_name);
    361           String mde_name = (hidden_mde_name.substring(MetadataSetManager.HIDDEN.length() + 1)).replace('_','.');
    362           ElementWrapper mde = gatherer.c_man.getCollection().msm.getElement(mde_name);
    363           // Set metadata element combobox.
    364           metadata.setSelectedItem(mde);
    365           // Set button name.
    366           String bn = classifier.getArgument("buttonname").getValue();
    367           if(bn != null) {
    368                 buttonname_label.setSelected(true);
    369                 buttonname.setBackground(Color.white);
    370                 buttonname.setEnabled(true);
    371                 buttonname.setText(bn);
    372           }
    373           // Set sort element.
    374           String sort_mde_name = classifier.getArgument("sort").getValue();
    375           ElementWrapper sort_mde = gatherer.c_man.getCollection().msm.getElement(sort_mde_name);
    376           if(sort_mde != null) {
    377                 sort_label.setSelected(true);
    378                 sort.setBackground(Color.white);
    379                 sort.setEnabled(true);
    380                 sort.setSelectedItem(sort_mde);
    381           }
    382           // Recover value tree.
    383           GValueModel model = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde);
    384           // For each token in the tokenizer, set toggle buttons and recover node.
    385           StringTokenizer tokenizer = new StringTokenizer(separations, ",");
    386           mappings = new Hashtable();
    387           while(tokenizer.hasMoreTokens()) {
    388                 String key = tokenizer.nextToken();
    389                 for(int j = 1; j < STRING_VALUES.length; j++) {
    390                      if(key.startsWith(STRING_VALUES[0])) {
    391                           j = STRING_VALUES.length;
    392                      }
    393                      else if(key.startsWith(STRING_VALUES[j])) {
    394                           separators[j].setSelected(true);
    395                           j = STRING_VALUES.length;
    396                      }
    397                 }
     355    public void recreate(Classifier classifier, String separations) {
     356    // Rebuild controls
     357    display(false);
     358    // Calculate original element.
     359    hidden_mde_name = classifier.getArgument("metadata").getValue();
     360    ElementWrapper hidden_mde = gatherer.c_man.getCollection().msm.getElement(hidden_mde_name);
     361    String mde_name = (hidden_mde_name.substring(MetadataSetManager.HIDDEN.length() + 1)).replace('_','.');
     362    ElementWrapper mde = gatherer.c_man.getCollection().msm.getElement(mde_name);
     363    // Set metadata element combobox.
     364    metadata.setSelectedItem(mde);
     365    // Set button name.
     366    String bn = classifier.getArgument("buttonname").getValue();
     367    if(bn != null) {
     368        buttonname_label.setSelected(true);
     369        buttonname.setBackground(Color.white);
     370        buttonname.setEnabled(true);
     371        buttonname.setText(bn);
     372    }
     373    // Set sort element.
     374    String sort_mde_name = classifier.getArgument("sort").getValue();
     375    ElementWrapper sort_mde = gatherer.c_man.getCollection().msm.getElement(sort_mde_name);
     376    if(sort_mde != null) {
     377        sort_label.setSelected(true);
     378        sort.setBackground(Color.white);
     379        sort.setEnabled(true);
     380        sort.setSelectedItem(sort_mde);
     381    }
     382    // Recover value tree.
     383    GValueModel model = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde);
     384    // For each token in the tokenizer, set toggle buttons and recover node.
     385    StringTokenizer tokenizer = new StringTokenizer(separations, ",");
     386    mappings = new Hashtable();
     387    while(tokenizer.hasMoreTokens()) {
     388        String key = tokenizer.nextToken();
     389        for(int j = 1; j < STRING_VALUES.length; j++) {
     390        if(key.startsWith(STRING_VALUES[0])) {
     391            j = STRING_VALUES.length;
     392        }
     393        else if(key.startsWith(STRING_VALUES[j])) {
     394            separators[j].setSelected(true);
     395            j = STRING_VALUES.length;
     396        }
     397        }
    398398                // Rebuild mappings hashtable for this pattern. Add it if not present.
    399                 GValueNode node = model.addValue(key);
     399        GValueNode node = model.addValue(key);
    400400                // If the key is simple, ie "C", then add the mapping "C"->node
    401                 if(node == null) {
    402                      // Do nothing.
    403                 }
    404                 else if(key.indexOf("-") == -1) {
    405                      mappings.put(key, node);
    406                 }
     401        if(node == null) {
     402        // Do nothing.
     403        }
     404        else if(key.indexOf("-") == -1) {
     405        mappings.put(key, node);
     406        }
    407407                // If the key is more complex, say "D-L", then we add mappings for all string values so "D"->node, "E"->node, ... "L"->node. This is miles eaiser to do here, than when you are trying to match a records metadata ie matchin "D-L" to "Igloos are cool". Note that it is guarantee that the keys are all of the same length, ie "A-L", "MA-ML", "MMA-STF", "STFA-ZZZZ".
    408                 else {
    409                      String start = key.substring(0, key.indexOf("-"));
    410                      String end = key.substring(key.indexOf("-") + 1);
    411                      for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) {
    412                           ///ystem.err.println("Adding " + key + " -> " + node);
    413                           mappings.put(key, node); 
    414                      }
    415                 }
    416           }
    417           separators_preview.setText(getPreview(true));
    418     }
    419     /** Sets the value of Gatherer, for those classes loaded dynamically.
     408        else {
     409        String start = key.substring(0, key.indexOf("-"));
     410        String end = key.substring(key.indexOf("-") + 1);
     411        for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) {
     412            ///ystem.err.println("Adding " + key + " -> " + node);
     413            mappings.put(key, node);   
     414        }
     415        }
     416    }
     417    separators_preview.setText(getPreview(true));
     418    }
     419    /** Sets the value of Gatherer, for those classes loaded dynamically.
    420420      * @param gatherer A reference to the <strong>Gatherer</strong>.
    421421      */
    422     public void setGatherer(Gatherer gatherer) {
    423           this.gatherer = gatherer;
    424     }
     422    public void setGatherer(Gatherer gatherer) {
     423    this.gatherer = gatherer;
     424    }
    425425
    426     public void setManager(ClassifierManager manager) {
    427     }
     426    public void setManager(ClassifierManager manager) {
     427    }
    428428
    429     /** Translate this object into a string such as you would find in the collection configuration file.
     429    /** Translate this object into a string such as you would find in the collection configuration file.
    430430      * @return A <strong>String</strong> representation.
    431431      */
    432     public String toString() {
    433           StringBuffer text = new StringBuffer("classify ");
    434           text.append(NAME);
    435           text.append(" -metadata ");
    436           text.append(metadata.getSelectedItem().toString());
    437           if(buttonname_label.isSelected()) {
    438                 text.append(" -buttonname \"");
    439                 text.append(buttonname.getText());
    440                 text.append("\"");
    441           }
    442           text.append(" -separations ");
    443           text.append(getPreview(false));
    444           return text.toString();
    445     }
    446     /** Add a pertinant piece of metadata to the given record. */
    447     private void addMetadata(long id, FileNode record, ElementWrapper element) {
    448           // Add custom metadata based on target metadata. No recursion.
    449           ArrayList temp_value = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile(), element);
    450           boolean found = false; // Only want to add the custom classifier metadata once.
    451           for(int i = 0; !found && i < temp_value.size(); i++) {
    452                 String metadata_value = (String)temp_value.get(i);
    453                 for(Enumeration keys = mappings.keys(); metadata_value != null && keys.hasMoreElements(); ) {
    454                      String key = (String)keys.nextElement();
    455                      // Try to match the value and the pattern. Remember to check for any case, ie key is '#' and metadata_value starts with anything other than a character.
    456                      if(metadata_value.toUpperCase().startsWith(key) || (key.equals("#") && !Character.isLetter(metadata_value.charAt(0)))) {
    457                           GValueNode node = (GValueNode) mappings.get(key);
    458                           if(node != null) { // And it shouldn't.
    459                                 Metadata metadata = new Metadata(node);
    460                                 //record.addMetadata(id, metadata, MetaEditPromptBox.ACCUMULATE_ALL, false, 1);
    461                                 Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, record, null, metadata);
    462                                 found = true;
    463                           }
    464                      }
    465                 }
    466           }
    467     }
    468     /** Create a separation preview String from the currently selected toggle buttons.
     432    public String toString() {
     433    StringBuffer text = new StringBuffer("classify ");
     434    text.append(NAME);
     435    text.append(" -metadata ");
     436    text.append(metadata.getSelectedItem().toString());
     437    if(buttonname_label.isSelected()) {
     438        text.append(" -buttonname \"");
     439        text.append(buttonname.getText());
     440        text.append("\"");
     441    }
     442    text.append(" -separations ");
     443    text.append(getPreview(false));
     444    return text.toString();
     445    }
     446    /** Add a pertinant piece of metadata to the given record. */
     447    private void addMetadata(long id, FileNode record, ElementWrapper element) {
     448    // Add custom metadata based on target metadata. No recursion.
     449    ArrayList temp_value = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile(), element);
     450    boolean found = false; // Only want to add the custom classifier metadata once.
     451    for(int i = 0; !found && i < temp_value.size(); i++) {
     452        String metadata_value = (String)temp_value.get(i);
     453        for(Enumeration keys = mappings.keys(); metadata_value != null && keys.hasMoreElements(); ) {
     454        String key = (String)keys.nextElement();
     455        // Try to match the value and the pattern. Remember to check for any case, ie key is '#' and metadata_value starts with anything other than a character.
     456        if(metadata_value.toUpperCase().startsWith(key) || (key.equals("#") && !Character.isLetter(metadata_value.charAt(0)))) {
     457            GValueNode node = (GValueNode) mappings.get(key);
     458            if(node != null) { // And it shouldn't.
     459            Metadata metadata = new Metadata(node);
     460            //record.addMetadata(id, metadata, MetaEditPromptBox.ACCUMULATE_ALL, false, 1);
     461            Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, record, null, metadata);
     462            found = true;
     463            }
     464        }
     465        }
     466    }
     467    }
     468    /** Create a separation preview String from the currently selected toggle buttons.
    469469      * @param spaces <i>true</i> if the returned String should contain spaces for readability, <i>false</i> if we mean to process it and spaces would just make that more difficult.
    470470      * @return A <strong>String</strong> representing the current separation state, such as "#, A-L, M-Z".
    471471      */
    472     private String getPreview(boolean spaces) {
    473           String sep_sep = ",";
    474           if(spaces) {
    475                 sep_sep = ", ";
    476           }
    477           StringBuffer preview = new StringBuffer(get(VALUES[0]));
    478           for(int i = 1; i < separators.length - 1; i++) {
    479                 if(separators[i].isSelected()) {
    480                      // Special case, where we specifically avoid #-#, A...
    481                      if(i == 1) {
    482                           preview.append(sep_sep);
    483                           preview.append(get(VALUES[i]));
    484                      }
    485                      // Check previous value. We don't want ...A-L, M-M, N...
    486                      else if(separators[i - 1].isSelected()) {
    487                           preview.append(sep_sep);
    488                           preview.append(get(VALUES[i]));
    489                      }
    490                      // Otherwise ..-VALUE[i-1], VALUE[i]...
    491                      else {
    492                           preview.append("-");
    493                           preview.append(get(VALUES[i-1]));
    494                           preview.append(sep_sep);
    495                           preview.append(get(VALUES[i]));
    496                      }
    497                 }
    498           }
    499           // Special case of "...-Y,Z"
    500           if(separators[separators.length - 1].isSelected()) {
    501                 preview.append("-");
    502                 preview.append(get(VALUES[VALUES.length - 2]));
    503                 preview.append(sep_sep);
    504                 preview.append(get(VALUES[VALUES.length - 1]));
    505           }
    506           // Otherwise "...-Z"
    507           else {
    508                 preview.append("-");
    509                 preview.append(get(VALUES[VALUES.length - 1]));
    510           }             
    511           return preview.toString();
    512     }
    513     /** Load the custom argument dictionary if necessary, then retrieve the requested phrase.
     472    private String getPreview(boolean spaces) {
     473    String sep_sep = ",";
     474    if(spaces) {
     475        sep_sep = ", ";
     476    }
     477    StringBuffer preview = new StringBuffer(get(VALUES[0]));
     478    for(int i = 1; i < separators.length - 1; i++) {
     479        if(separators[i].isSelected()) {
     480        // Special case, where we specifically avoid #-#, A...
     481        if(i == 1) {
     482            preview.append(sep_sep);
     483            preview.append(get(VALUES[i]));
     484        }
     485        // Check previous value. We don't want ...A-L, M-M, N...
     486        else if(separators[i - 1].isSelected()) {
     487            preview.append(sep_sep);
     488            preview.append(get(VALUES[i]));
     489        }
     490        // Otherwise ..-VALUE[i-1], VALUE[i]...
     491        else {
     492            preview.append("-");
     493            preview.append(get(VALUES[i-1]));
     494            preview.append(sep_sep);
     495            preview.append(get(VALUES[i]));
     496        }
     497        }
     498    }
     499    // Special case of "...-Y,Z"
     500    if(separators[separators.length - 1].isSelected()) {
     501        preview.append("-");
     502        preview.append(get(VALUES[VALUES.length - 2]));
     503        preview.append(sep_sep);
     504        preview.append(get(VALUES[VALUES.length - 1]));
     505    }
     506    // Otherwise "...-Z"
     507    else {
     508        preview.append("-");
     509        preview.append(get(VALUES[VALUES.length - 1]));
     510    }               
     511    return preview.toString();
     512    }
     513    /** Load the custom argument dictionary if necessary, then retrieve the requested phrase.
    514514      * @param key A key <strong>String</strong> which maps to the phrase to retrieve.
    515515      * @return A phrase as a <strong>String</strong>.
     
    517517      * @see org.greenstone.gatherer.Gatherer
    518518      */
    519     private String get(String key) {
    520           if(dictionary == null) {
    521                 dictionary = ResourceBundle.getBundle("org.greenstone.gatherer.cdm.custom." + NAME, Gatherer.config.getLocale("general.locale", false));
    522           }
    523           return dictionary.getString(key);
    524     }
    525     /** Listens to the buttonname checkbox, and enable buttonname appropriately when action detected. */
    526     private class ButtonNameListener
    527           implements ActionListener {
    528           /** When an action is performed on a registered control, this method is called which either enables or disables the buttonname field depending on the buttonname checkbox.
    529             * @param event An <strong>ActionEvent</strong> containing information about the action performed.
     519    private String get(String key) {
     520    if(dictionary == null) {
     521        dictionary = ResourceBundle.getBundle("org.greenstone.gatherer.cdm.custom." + NAME, Gatherer.config.getLocale("general.locale", false));
     522    }
     523    return dictionary.getString(key);
     524    }
     525    /** Listens to the buttonname checkbox, and enable buttonname appropriately when action detected. */
     526    private class ButtonNameListener
     527    implements ActionListener {
     528    /** When an action is performed on a registered control, this method is called which either enables or disables the buttonname field depending on the buttonname checkbox.
     529     * @param event An <strong>ActionEvent</strong> containing information about the action performed.
    530530            */
    531           public void actionPerformed(ActionEvent event) {
    532                 if(buttonname_label.isSelected()) {
    533                      buttonname.setBackground(Color.white);
    534                      buttonname.setEnabled(true);
    535                 }
    536                 else {
    537                      buttonname.setBackground(Color.lightGray);
    538                      buttonname.setEnabled(false);
    539                 }                   
    540           }
    541     }
    542     /** Listens to the cancel button, and disposes appropriately when action detected. */
    543     private class CancelListener
    544           implements ActionListener {
    545           /** If the cancel button is pressed then we should dispose of the dialog without processing any records.
    546             * @param event An <strong>ActionEvent</strong> containing information about the action performed.
     531    public void actionPerformed(ActionEvent event) {
     532        if(buttonname_label.isSelected()) {
     533        buttonname.setBackground(Color.white);
     534        buttonname.setEnabled(true);
     535        }
     536        else {
     537        buttonname.setBackground(Color.lightGray);
     538        buttonname.setEnabled(false);
     539        }                   
     540    }
     541    }
     542    /** Listens to the cancel button, and disposes appropriately when action detected. */
     543    private class CancelListener
     544    implements ActionListener {
     545    /** If the cancel button is pressed then we should dispose of the dialog without processing any records.
     546     * @param event An <strong>ActionEvent</strong> containing information about the action performed.
    547547            */
    548           public void actionPerformed(ActionEvent event) {
    549                 process = false;
    550                 controls.dispose();
    551           }
    552     }
    553     /** Listens to the ok button, and sets process to true then disposes appropriately when action detected. */
    554     private class OKListener
    555           implements ActionListener {
    556           /** If the ok button is pressed then we should dispose of the dialog after processing all records.
    557             * @param event An <strong>ActionEvent</strong> containing information about the action performed.
     548    public void actionPerformed(ActionEvent event) {
     549        process = false;
     550        controls.dispose();
     551    }
     552    }
     553    /** Listens to the ok button, and sets process to true then disposes appropriately when action detected. */
     554    private class OKListener
     555    implements ActionListener {
     556    /** If the ok button is pressed then we should dispose of the dialog after processing all records.
     557     * @param event An <strong>ActionEvent</strong> containing information about the action performed.
    558558            */
    559           public void actionPerformed(ActionEvent event) {
    560                 process = true;
    561                 controls.dispose();
    562           }
    563     }
    564     /** Listens for the toggling of a separator value, and updates the separator preview field. */
    565     private class SeparatorListener
    566           implements ActionListener {
    567           /** When one of the separator buttons is toggled we update the separator preview field.
    568             * @param event An <strong>ActionEvent</strong> containing information about the action performed.
     559    public void actionPerformed(ActionEvent event) {
     560        process = true;
     561        controls.dispose();
     562    }
     563    }
     564    /** Listens for the toggling of a separator value, and updates the separator preview field. */
     565    private class SeparatorListener
     566    implements ActionListener {
     567    /** When one of the separator buttons is toggled we update the separator preview field.
     568     * @param event An <strong>ActionEvent</strong> containing information about the action performed.
    569569            */
    570           public void actionPerformed(ActionEvent event) {
    571                 separators_preview.setText(getPreview(true));
    572           }
    573     }
    574     /** Listens to the sort checkbox, and enables/disables sort appropriately when action detected. */
    575     private class SortListener
    576           implements ActionListener {
    577           /** If this checkbox is actioned, enabled or disable the sort control based on whether we are checked (selected) or not.
    578             * @param event An <strong>ActionEvent</strong> containing information about the action performed.
     570    public void actionPerformed(ActionEvent event) {
     571        separators_preview.setText(getPreview(true));
     572    }
     573    }
     574    /** Listens to the sort checkbox, and enables/disables sort appropriately when action detected. */
     575    private class SortListener
     576    implements ActionListener {
     577    /** If this checkbox is actioned, enabled or disable the sort control based on whether we are checked (selected) or not.
     578     * @param event An <strong>ActionEvent</strong> containing information about the action performed.
    579579            */
    580           public void actionPerformed(ActionEvent event) {
    581                 if(sort_label.isSelected()) {
    582                      sort.setBackground(Color.white);
    583                      sort.setEnabled(true);
    584                 }
    585                 else {
    586                      sort.setBackground(Color.lightGray);
    587                      sort.setEnabled(false);
    588                 }                   
    589           }
    590     }
    591     /** Display a progress bar while creating the required hidden system hierarchy.
     580    public void actionPerformed(ActionEvent event) {
     581        if(sort_label.isSelected()) {
     582        sort.setBackground(Color.white);
     583        sort.setEnabled(true);
     584        }
     585        else {
     586        sort.setBackground(Color.lightGray);
     587        sort.setEnabled(false);
     588        }                   
     589    }
     590    }
     591    /** Display a progress bar while creating the required hidden system hierarchy.
    592592      * @param element An <strong>ElementWrapper</strong> containing the metadata element we want to build a custom classifier on.
    593593      * @param state A <strong>String</strong> representing the currently selected separators, of the form "#,A-L,MA-Ml,MM-MZ,N-Z". */
    594     private void process(ElementWrapper element, String state) {
    595           // Create a new progress bar dialog, using these divisions:
    596           // 33% - Creation and removal of old metadata tree.
    597           // 33% - Creation of new metadata tree, 33 / <number of separations>.
    598           // 33% - Allocation of metadata to records, 33 / <number of files>.
    599           // Hopefully the previous dialog should have already vanished (its dispose() caused by the process click).
    600           ProgressDialog pd = new ProgressDialog("Custom Classifier Creation", "Preparing for operation.");
    601           // Step 1: Create the new dummy element and add it if necessary.
    602           pd.setText("Creating dummy metadata element.");
    603           MetadataSet hidden_mds = gatherer.c_man.getCollection().msm.getSet(MetadataSetManager.HIDDEN);
    604           Element hidden_e = hidden_mds.getElement(element.toString().replace('.','_'));
    605           boolean found = true;
    606           ElementWrapper hidden_mde = null;
    607           if(hidden_mde != null) {
    608                 hidden_mde = new ElementWrapper(hidden_e);
    609           }
    610           else {
    611                 hidden_mde = hidden_mds.addElement(element.toString().replace('.','_'));
    612                 found = false;
    613           }
    614           hidden_mde_name = hidden_mde.toString();
    615           pd.increase(1,2,20);
    616           // Step 2: Remove any existing value tree for this element. Wastefull I know but I don't want to lag for too long, and this is the quickest way.
    617           pd.setText("Removing any existing hierarchy.");
    618           if(found) {
    619                 hidden_mds.removeValueTree(hidden_mde);
    620           }
    621           pd.increase(1,2,20);
    622           // Step 3: Create a value tree for this element.
    623           pd.setText("Creating new hierary root.");
    624           GValueModel value_tree = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde);
    625           pd.increase(5, 5, 5);
    626           // Step 4: Build the single level value hierarchy using state's value. Here a hashtable mapping string patterns to value nodes is built.
    627           pd.setText("Creating separations.");
    628           StringTokenizer tokenizer = new StringTokenizer(state, ",");
    629           int separator_count = tokenizer.countTokens();
    630           mappings = new Hashtable();
    631           for(int i = 0; i < separator_count; i++) {
    632                 String key = tokenizer.nextToken();
    633                 GValueNode node = value_tree.addValue(key);
     594    private void process(ElementWrapper element, String state) {
     595    // Create a new progress bar dialog, using these divisions:
     596    // 33% - Creation and removal of old metadata tree.
     597    // 33% - Creation of new metadata tree, 33 / <number of separations>.
     598    // 33% - Allocation of metadata to records, 33 / <number of files>.
     599    // Hopefully the previous dialog should have already vanished (its dispose() caused by the process click).
     600    ProgressDialog pd = new ProgressDialog("Custom Classifier Creation", "Preparing for operation.");
     601    // Step 1: Create the new dummy element and add it if necessary.
     602    pd.setText("Creating dummy metadata element.");
     603    MetadataSet hidden_mds = gatherer.c_man.getCollection().msm.getSet(MetadataSetManager.HIDDEN);
     604    Element hidden_e = hidden_mds.getElement(element.toString().replace('.','_'));
     605    boolean found = true;
     606    ElementWrapper hidden_mde = null;
     607    if(hidden_mde != null) {
     608        hidden_mde = new ElementWrapper(hidden_e);
     609    }
     610    else {
     611        hidden_mde = hidden_mds.addElement(element.toString().replace('.','_'));
     612        found = false;
     613    }
     614    hidden_mde_name = hidden_mde.toString();
     615    pd.increase(1,2,20);
     616    // Step 2: Remove any existing value tree for this element. Wastefull I know but I don't want to lag for too long, and this is the quickest way.
     617    pd.setText("Removing any existing hierarchy.");
     618    if(found) {
     619        hidden_mds.removeValueTree(hidden_mde);
     620    }
     621    pd.increase(1,2,20);
     622    // Step 3: Create a value tree for this element.
     623    pd.setText("Creating new hierary root.");
     624    GValueModel value_tree = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde);
     625    pd.increase(5, 5, 5);
     626    // Step 4: Build the single level value hierarchy using state's value. Here a hashtable mapping string patterns to value nodes is built.
     627    pd.setText("Creating separations.");
     628    StringTokenizer tokenizer = new StringTokenizer(state, ",");
     629    int separator_count = tokenizer.countTokens();
     630    mappings = new Hashtable();
     631    for(int i = 0; i < separator_count; i++) {
     632        String key = tokenizer.nextToken();
     633        GValueNode node = value_tree.addValue(key);
    634634                // If the key is simple, ie "C", then add the mapping "C"->node
    635                 if(key.indexOf("-") == -1) {
    636                      mappings.put(key, node);
    637                 }
     635        if(key.indexOf("-") == -1) {
     636        mappings.put(key, node);
     637        }
    638638                // If the key is more complex, say "D-L", then we add mappings for all string values so "D"->node, "E"->node, ... "L"->node. This is miles eaiser to do here, than when you are trying to match a records metadata ie matchin "D-L" to "Igloos are cool". Note that it is guarantee that the keys are all of the same length, ie "A-L", "MA-ML", "MMA-STF", "STFA-ZZZZ".
    639                 else {
    640                      String start = key.substring(0, key.indexOf("-"));
    641                      String end = key.substring(key.indexOf("-") + 1);
    642                      for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) {
    643                           mappings.put(key, node); 
    644                      }
    645                 }
     639        else {
     640        String start = key.substring(0, key.indexOf("-"));
     641        String end = key.substring(key.indexOf("-") + 1);
     642        for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) {
     643            mappings.put(key, node);   
     644        }
     645        }
    646646                // Each iteration.
    647                 pd.increase(1, separator_count, 25);
    648           }
    649           // Step 5: Recurse the entire tree, adding the appropriate values where necessary (overwriting any existing value for this element).
    650           pd.setText("Allocating metadata to records.");
    651           int document_count = gatherer.c_man.getCollection().getDocumentCount();
    652           TreeModel record_tree = gatherer.c_man.getRecordSet();
    653           FileNode records[] = ArrayTools.add(null, (FileNode)record_tree.getRoot());
    654           while(records != null) {
    655                 if(records[0].isLeaf()) {
    656                      addMetadata(0L, records[0], element);
    657                      // Each iteration.
    658                      pd.increase(1, document_count, 50);
    659                 }
    660                 else {
    661                      // Add all children to records at once hopefully.
    662                      FileNode children[] = new FileNode[records[0].getChildCount()];
    663                      for(int i = 0; i < children.length; i++) {
    664                           children[i] = (FileNode) records[0].getChildAt(i);
    665                      }
    666                      records = ArrayTools.add(records, children);
    667                 }
    668                 records = ArrayTools.tail(records);
    669           }
    670           // Step 6: Done.
    671           pd.dispose();
    672     }
    673     /** A dialog window used to show the progress of processing the file records in terms of adding hidden metadata. */
    674     private class ProgressDialog
    675           extends JDialog {
    676           /** The current value represented by the progress bar (where value is double and the progress bar shows an int). */
    677           private double value = 0.0;
    678           /** The default size of this dialog. */
    679           private Dimension dialog_size = new Dimension(500,100);
    680           /** The label explaining what action is currently happening (1 of 5 steps). */
    681           private JLabel label = new JLabel();
    682           /** The progress bar itself. */
    683           private JProgressBar bar = new JProgressBar();
    684           /** Constructor.
    685             * @param t The title for this dialog as a <strong>String</strong>.
    686             * @param l The initial label for the progress bar also a <strong>String</strong>.
    687             */
    688           public ProgressDialog(String t, String l) {
    689                 super();
    690                 setModal(false);
    691                 setSize(dialog_size);
    692                 setTitle(t);
     647        pd.increase(1, separator_count, 25);
     648    }
     649    // Step 5: Recurse the entire tree, adding the appropriate values where necessary (overwriting any existing value for this element).
     650    pd.setText("Allocating metadata to records.");
     651    int document_count = gatherer.c_man.getCollection().getDocumentCount();
     652    TreeModel record_tree = gatherer.c_man.getRecordSet();
     653    FileNode records[] = ArrayTools.add(null, (FileNode)record_tree.getRoot());
     654    while(records != null) {
     655        if(records[0].isLeaf()) {
     656        addMetadata(0L, records[0], element);
     657        // Each iteration.
     658        pd.increase(1, document_count, 50);
     659        }
     660        else {
     661        // Add all children to records at once hopefully.
     662        FileNode children[] = new FileNode[records[0].getChildCount()];
     663        for(int i = 0; i < children.length; i++) {
     664            children[i] = (FileNode) records[0].getChildAt(i);
     665        }
     666        records = ArrayTools.add(records, children);
     667        }
     668        records = ArrayTools.tail(records);
     669    }
     670    // Step 6: Done.
     671    pd.dispose();
     672    }
     673    /** A dialog window used to show the progress of processing the file records in terms of adding hidden metadata. */
     674    private class ProgressDialog
     675    extends JDialog {
     676    /** The current value represented by the progress bar (where value is double and the progress bar shows an int). */
     677    private double value = 0.0;
     678    /** The default size of this dialog. */
     679    private Dimension dialog_size = new Dimension(500,100);
     680    /** The label explaining what action is currently happening (1 of 5 steps). */
     681    private JLabel label = new JLabel();
     682    /** The progress bar itself. */
     683    private JProgressBar bar = new JProgressBar();
     684    /** Constructor.
     685     * @param t The title for this dialog as a <strong>String</strong>.
     686     * @param l The initial label for the progress bar also a <strong>String</strong>.
     687     */
     688    public ProgressDialog(String t, String l) {
     689        super();
     690        setModal(false);
     691        setSize(dialog_size);
     692        setTitle(t);
    693693                // Create
    694                 JPanel content_pane = (JPanel) getContentPane();
    695                 label.setText(l);
    696                 bar.setMaximum(100);
    697                 bar.setMinimum(0);
    698                 bar.setValue(0);
     694        JPanel content_pane = (JPanel) getContentPane();
     695        label.setText(l);
     696        bar.setMaximum(100);
     697        bar.setMinimum(0);
     698        bar.setValue(0);
    699699                // Connect
    700700                // Layout
    701                 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    702                 content_pane.setLayout(new BorderLayout());
    703                 content_pane.add(label, BorderLayout.NORTH);
    704                 content_pane.add(bar, BorderLayout.CENTER);
     701        content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     702        content_pane.setLayout(new BorderLayout());
     703        content_pane.add(label, BorderLayout.NORTH);
     704        content_pane.add(bar, BorderLayout.CENTER);
    705705                // Display
    706                 Dimension screen_size = Gatherer.config.screen_size;
    707                 setLocation((screen_size.width - dialog_size.width) / 2, (screen_size.height - dialog_size.height) / 2);
    708                 screen_size = null;
    709                 show();
    710           }
    711           /** Increase the progress bar by a certain ammount.
     706        Dimension screen_size = Gatherer.config.screen_size;
     707        setLocation((screen_size.width - dialog_size.width) / 2, (screen_size.height - dialog_size.height) / 2);
     708        screen_size = null;
     709        show();
     710    }
     711    /** Increase the progress bar by a certain ammount.
    712712            * @param item An <i>int</i> indicating what item number this is.
    713713            * @param out_of An <i>int</i> detailing the number of items expected in total for this phase.
    714714            * @param total The maximum value of the progress bar as an <i>int</i>.
    715715            */
    716           public void increase(int item, int out_of, int total) {
    717                 value = value + (((double) item / (double) out_of) * (double)total);
    718                 int int_value = (int) value;
     716    public void increase(int item, int out_of, int total) {
     717        value = value + (((double) item / (double) out_of) * (double)total);
     718        int int_value = (int) value;
    719719                ///ystem.err.println("Value is " + value + " which rounds to " + int_value);
    720                 if(int_value != bar.getValue()) {
    721                      bar.setValue(int_value);
    722                 }
    723           }
    724           /** Set the message shown in the progress bar.
     720        if(int_value != bar.getValue()) {
     721        bar.setValue(int_value);
     722        }
     723    }
     724    /** Set the message shown in the progress bar.
    725725            * @param l The new progress bar label as a <strong>String</strong>.
    726726            */
    727           public void setText(String l) {
    728                 label.setText(l);
    729           }
    730     }
    731     /** The String equivelent of int++, this method increments the value of a String by one letter at a time.
     727    public void setText(String l) {
     728        label.setText(l);
     729    }
     730    }
     731    /** The String equivelent of int++, this method increments the value of a String by one letter at a time.
    732732      * @param str The <strong>String</strong> to be incremented.
    733733      * @return A <strong>String</strong> whose 'checksum' value (adding the INT_VALUE[] of the letters together) is one greater than the initial String, while still containing only those values in STRING_VALUE[].
    734734      */
    735     public String increment(String str) {
    736           ///ystem.err.print("Incrementing " + str + " -> ");
    737           char chars[] = str.toCharArray();
    738           boolean found = false;
    739           int pos = str.length() - 1;
    740           while(found == false && pos >= 0) {
    741                 if((int)chars[pos] + 1 <= INT_VALUES[26]) {
    742                      if((int)chars[pos] == INT_VALUES[0]) { // Anything else symbol
    743                           chars[pos] = (char) INT_VALUES[1];
    744                      }
    745                      else {
    746                           chars[pos] = (char)((int)chars[pos] + 1);
    747                      }
    748                      found = true;
    749                
    750                 else {
    751                      chars[pos] = (char)INT_VALUES[1];
    752                      pos--;
    753                 }
    754           }
    755           // If we haven't found it yet, return null.
    756           if(!found) {
    757                 return null;
    758           }
    759           ///ystem.err.println(String.valueOf(chars));
    760           return String.valueOf(chars);
    761     }
     735    public String increment(String str) {
     736    ///ystem.err.print("Incrementing " + str + " -> ");
     737    char chars[] = str.toCharArray();
     738    boolean found = false;
     739    int pos = str.length() - 1;
     740    while(found == false && pos >= 0) {
     741        if((int)chars[pos] + 1 <= INT_VALUES[26]) {
     742        if((int)chars[pos] == INT_VALUES[0]) { // Anything else symbol
     743            chars[pos] = (char) INT_VALUES[1];
     744        }
     745        else {
     746            chars[pos] = (char)((int)chars[pos] + 1);
     747        }
     748        found = true;
     749       
     750        else {
     751        chars[pos] = (char)INT_VALUES[1];
     752        pos--;
     753        }
     754    }
     755    // If we haven't found it yet, return null.
     756    if(!found) {
     757        return null;
     758    }
     759    ///ystem.err.println(String.valueOf(chars));
     760    return String.valueOf(chars);
     761    }
    762762}
    763763
Note: See TracChangeset for help on using the changeset viewer.