/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * Author: John Thompson, Greenstone Digital Library, University of Waikato * * Copyright (C) 1999 New Zealand Digital Library Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.cdm; /************************************************************************************** * Written: 01/05/02 * Revised: 16/08/02 Optimized and Commented. * 11/07/03 DOM support **************************************************************************************/ import java.io.*; import java.util.*; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.cdm.Argument; import org.greenstone.gatherer.cdm.ArgumentContainer; import org.greenstone.gatherer.cdm.CollectionConfiguration; import org.greenstone.gatherer.cdm.CollectionDesignManager; import org.greenstone.gatherer.cdm.DOMProxyListEntry; import org.greenstone.gatherer.util.StaticStrings; import org.greenstone.gatherer.util.Utility; import org.w3c.dom.*; /** This class is responsible for storing information from a parsed classinfo.pl call in such a way that it allows easy access to parsed details for the purposes of user design and specification of classifiers. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ public class Classifier extends ArrayList implements ArgumentContainer, Comparable, DOMProxyListEntry, Serializable { static final public String CLASSIFIER_PREFIX = "CL"; private boolean is_abstract = false; /** A reference to the classifier that this one inherits from. */ private Classifier super_classifier = null; /** The element this classifier is based upon. */ private Element element; /** A description of this classifier. */ private String description = null; /** The name of the classifier as it would appear in the collect.cfg file. */ private String name = null; /** This string is filled out the first time this classifier is created, and remains unchanged there-after. It is used to match up with Format commands that may not yet have been instantiated (and thus only have offline references along the lines of 'CL1' to figure out what Classifier they want.) */ private String old_position_string = null; /** Constructor used only in DOMProxyListModel initializations. */ public Classifier() { } public Classifier(Element element, Classifier base_classifier) { super(); this.element = element; this.name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE); ///atherer.println("Establishing Classifier: " + name); // Parse in any argument options for this classifier, keeping a list of the ones found HashMap known_arguments = new HashMap(); NodeList option_elements = element.getElementsByTagName(StaticStrings.OPTION_ELEMENT); int option_elements_length = option_elements.getLength(); for(int i = 0; i < option_elements_length; i++) { Element option_element = (Element) option_elements.item(i); Argument argument = new Argument(option_element); ///atherer.println("Rebuilding existing argument: " + argument.getName()); argument.setOwner(name); add(argument); known_arguments.put(argument.getName(), argument); } // If a base classifier was given if(base_classifier != null) { // Copy the details, and add a reference to whatever base_classifiers super classifier is. description = base_classifier.getDescription(); // Now search through the 'dummy' arguments belonging to the base classifier. For each found, if it is already assigned, fill out further details such as type. If any are found that are not already assigned for this classifier, copy them and add them, but without a value. ArrayList all_arguments = base_classifier.getArguments(true, true); int argument_count = all_arguments.size(); for(int j = 0; j < argument_count; j++) { Argument base_argument = (Argument) all_arguments.get(j); String base_argument_name = base_argument.getName(); ///atherer.println("Library indicates this classifier should have an argument: " + base_argument_name); Argument existing_argument = (Argument) known_arguments.get(base_argument_name); // Found an existing argument. Complete its details if(existing_argument != null) { ///atherer.println("Found existing argument. Filling out details."); existing_argument.setCustomArgument(false); existing_argument.setDefaultValue(base_argument.getDefaultValue()); existing_argument.setDescription(base_argument.getDescription()); existing_argument.setOptions(base_argument.getOptions()); existing_argument.setRequired(base_argument.isRequired()); existing_argument.setType(base_argument.getType()); } // No existing argument. Copy base_argument and add it, but do not set its assigned flag. That should be set the first time its changed by the user. else { ///atherer.println("No such argument. Adding new, unassigned, argument."); // The trick thing is that we have to create a new element in the DOM as well. Argument new_argument = base_argument.copy(); new_argument.setOwner(name); Element argument_element = CollectionDesignManager.collect_config.document.createElement(StaticStrings.OPTION_ELEMENT); argument_element.setAttribute(StaticStrings.NAME_ATTRIBUTE, base_argument_name); argument_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.FALSE_STR); argument_element.setAttribute(StaticStrings.CUSTOM_ATTRIBUTE, StaticStrings.FALSE_STR); new_argument.setElement(argument_element); // All done. Add it. element.appendChild(argument_element); add(new_argument); } } } old_position_string = getPositionString(); } /** Constructor. * @param name The name of this classifier as a String. * @param description A description of this classifier as a String. * @param super_classifier The super class of this classifier, as a Classifier. */ public Classifier(String name, String description, Classifier super_classifier) { super(); this.description = description; this.name = name; this.super_classifier = super_classifier; } /** Method to add an argument to this classifier. Only adds the argument if it isn't already present. * @param argument The Argument to add. */ public void addArgument(Argument argument) { if(element == null && !contains(argument)) { add(argument); argument.setOwner(name); } } /** Method to compare two classifiers for ordering. * @param object The classifier we are comparing to, as an Object. * @return An int specifying the classifier order, using values as set out in String. * @see java.lang.String#compareTo */ public int compareTo(Object object) { if(object == null) { return -1; } return toString().compareTo(object.toString()); } /** The assigned classifier constructor. * @param element the DOM Element this classifier is based upon */ public DOMProxyListEntry create(Element element) { String classifier_name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE); // Determine the base classifier from the classifier name Classifier base_classifier = CollectionDesignManager.classifier_manager.getBaseClassifier(classifier_name); Classifier classifier = new Classifier(element, base_classifier); base_classifier = null; classifier_name = null; return classifier; } /** Method to determine if two classifiers are equal. * @param object The classifier to test against, as an Object. * @return true if the classifier names match, false otherwise. */ public boolean equals(Object object) { return (compareTo(object) == 0); } /** Method to retrieve an argument by its name. * @param name The name of the argument as a String. * @return The Argument requested, or null if no such argument. */ public Argument getArgument(String name) { // The name given may still include the '-' if(name.startsWith("-")) { name = name.substring(1); } ArrayList arguments = getArguments(true, true); for(int i = 0; i < arguments.size(); i++) { Argument argument = (Argument)arguments.get(i); if(argument.getName().equals(name)) { return argument; } } return null; } /** Retrieve all of the arguments available to this base classifier, including its super classifiers arguments. Some complexity is added by allowing the caller to choose whether they want normal arguments, custom arguments, or both. * @return an ArrayList of all of the arguments, starting with those for this classifier and ending with the arguments for basplug or similiar root classifier */ public ArrayList getArguments(boolean include_normal, boolean include_custom) { ArrayList arguments = new ArrayList(); if(include_normal && include_custom) { arguments.addAll(this); } else { int size = size(); for(int i = 0; i < size; i++) { Argument argument = (Argument) get(i); if(argument.isCustomArgument()) { if(include_custom && !arguments.contains(argument)) { arguments.add(argument); } } else { if(include_normal && !arguments.contains(argument)) { arguments.add(argument); } } argument = null; } } if(super_classifier != null) { ArrayList remainder = super_classifier.getArguments(include_normal, include_custom); remainder.removeAll(arguments); arguments.addAll(remainder); } return arguments; } /** Method to retrieve a classifiers custom argument information. Custom arguments are defined to be those that have not got matching arguments in the base reference classifier from the library. Of course if there is no base classifier then all arguments are considered to be custom. * @return the custom arguments as a String */ public String getCustom() { StringBuffer custom_text = new StringBuffer(); // Retrieve all of the arguments, and append any that are custom into one long string ArrayList arguments = getArguments(false, true); int arguments_size = arguments.size(); boolean first = true; for(int i = 0; i < arguments_size; i++) { Argument argument = (Argument) arguments.get(i); if(argument.isAssigned()) { if(!first) { custom_text.append(" "); } custom_text.append(argument.toString()); first = false; } } return custom_text.toString(); } public String getDescription() { return description; } public Element getElement() { return element; } /** Method to retrieve a classifiers name. * @return A String containing the classifiers name. */ public String getName() { if(name == null && element != null) { name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE); } return name; } /* private String getOldPositionString() { return old_position_string; } */ /** Generate the string showing this classifiers position. */ public String getPositionString() { String position_string = CLASSIFIER_PREFIX; if(element != null) { // Determine our place in the collect.cfg file int position_int = CollectionDesignManager.classifier_manager.indexOf(this) + 1; if(position_int != -1) { position_string = position_string + position_int; } } return position_string; } public boolean isAbstract() { return is_abstract; } public boolean isAssigned() { return (element != null && !element.getAttribute(CollectionConfiguration.ASSIGNED_ATTRIBUTE).equals(CollectionConfiguration.FALSE_STR)); } public void setAssigned(boolean assigned) { if(element != null) { element.setAttribute(CollectionConfiguration.ASSIGNED_ATTRIBUTE, (assigned ? CollectionConfiguration.TRUE_STR : CollectionConfiguration.FALSE_STR)); } } /** Set the custom arguments. This turns out to be quite tricky. We must parse in the string, searching for arguments (for that we use a handy method in CollectionConfiguration). Next, for each argument, we check if we already know about it. If so we update its value, otherwise we create a new argument and assign it (must assign!). * @param custom_str the custom arguments all splodged together in one String */ public void setCustom(String custom_str) { HashMap raw_arguments = CollectionConfiguration.parseArguments(new CommandTokenizer(custom_str)); ArrayList custom_arguments = getArguments(false, true); int size = custom_arguments.size(); for(int i = 0; i < size; i++) { Argument argument = (Argument) custom_arguments.get(i); String original_argument_name = StaticStrings.MINUS_CHARACTER + argument.getName(); if(raw_arguments.containsKey(original_argument_name)) { // Set as assigned argument.setAssigned(true); String argument_value = (String)raw_arguments.remove(original_argument_name); if(argument_value != null) { argument.setValue(argument_value); argument_value = null; } } // We've removed it from our custom statement, so unassign else { argument.setAssigned(false); } argument = null; } // Any left over, add to the classifier Iterator argument_names = raw_arguments.keySet().iterator(); while(argument_names.hasNext()) { String argument_name = (String) argument_names.next(); String argument_value = (String) raw_arguments.get(argument_name); // The tricky thing is that we have to create a new element in the DOM as well. Element argument_element = CollectionDesignManager.collect_config.document.createElement(StaticStrings.OPTION_ELEMENT); argument_element.setAttribute(StaticStrings.NAME_ATTRIBUTE, argument_name.substring(1)); argument_element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.TRUE_STR); argument_element.setAttribute(StaticStrings.CUSTOM_ATTRIBUTE, StaticStrings.TRUE_STR); Argument argument = new Argument(argument_element); argument_name = null; if(argument_value != null) { argument.setValue(argument_value); argument_value = null; } // All done. Add it. element.appendChild(argument_element); add(argument); argument_element = null; } raw_arguments = null; } /** Method to set the value of desc. * @param description The new value of desc as a String. */ public void setDescription(String description) { this.description = description; } public void setElement(Element element) { this.element = element; } public void setIsAbstract(boolean is_abstract) { this.is_abstract = is_abstract; } /** Method to set the value of name. * @param name The new value of name as a String. */ public void setName(String name) { this.name = name; } /** Method to set the value of the super_classifier. * @param super_classifier The new value of super_classifier as a Classifier, or null if this class has no inheritance. */ public void setSuper(Classifier super_classifier) { this.super_classifier = super_classifier; } /** Method to print out this classifier as it would appear to the user in the interface * @return A String containing a single classifier command. */ public String toString() { if(element != null) { if(name == null) { name = element.getAttribute(StaticStrings.TYPE_ATTRIBUTE); } StringBuffer text = new StringBuffer(StaticStrings.CLASSIFY_STR); text.append(" "); text.append(name); text.append(" "); ArrayList arguments = getArguments(true, true); int arguments_size = arguments.size(); for(int i = 0; i < arguments_size; i++) { Argument argument = (Argument)arguments.get(i); if(argument.isAssigned()) { text.append(argument.toString()); text.append(" "); } } return text.substring(0, text.length() - 1); } else { return name; } } }