Changeset 4366
- Timestamp:
- 2003-05-27T15:57:37+12:00 (21 years ago)
- Location:
- trunk/gli/src/org/greenstone/gatherer
- Files:
-
- 76 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/cdm/Argument.java
r4293 r4366 69 69 70 70 public class Argument 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 71 implements Comparable, Serializable { 72 /** If this argument has multiple values, then they are stored here. This was originally a Hashtable, but since the synchronization overhead is unwarrented it was changed to a HashMap. */ 73 private ArrayList values = null; 74 /** <i>true</i> if this argument is required for the applicable script to work properly, <i>false</i> otherwise. */ 75 private boolean required = false; 76 /** The type of this argument. Initially an int, but bytes are cheaper. */ 77 private byte type = FLAG; 78 /** If the argument is of type ENUM then this map holds all the various options. Each entry is an <option value> -> <description> mapping. */ 79 private HashMap list = null; 80 /** A default value for parameter-type arguments. May be a Perl pattern. */ 81 private String default_value = null; 82 /** The text description of this argument parsed from the pluginfo output. */ 83 private String desc = null; 84 /** The argument flag as it appears in the command. Also used as the unique identifier of an argument. */ 85 private String name = null; 86 /** The plugin that owns this argument, for the purposes of visualising inheritance. */ 87 private String owner = null; 88 /** If this argument has been assigned, what is its value. Must be non-null for the argument to be printed. */ 89 private String value = null; 90 /** An element of the argument type enumeration. */ 91 static final public byte ENUM = 0; 92 /** An element of the argument type enumeration. */ 93 static final public byte FLAG = 1; 94 /** An element of the argument type enumeration. */ 95 static final public byte HIERARCHY = 2; 96 /** An element of the argument type enumeration. */ 97 static final public byte INTEGER = 3; 98 /** An element of the argument type enumeration. */ 99 static final public byte LANGUAGE = 4; 100 /** An element of the argument type enumeration. */ 101 static final public byte METADATA = 5; 102 /** An element of the argument type enumeration. */ 103 static final public byte METADATUM = 6; 104 /** An element of the argument type enumeration. */ 105 static final public byte STRING = 7; 106 /** Default Constructor. */ 107 public Argument() { 108 } 109 /** Normal Constructor, based on data parsed from an information script. 110 110 * @param name The argument flag as it appears in the command, as a <strong>String</strong>. Also used as the unique identifier of an argument. 111 111 * @param desc The text description of this argument parsed from the output as a <strong>String</strong>. … … 113 113 * @param default_value The default value for a parameter type argument, which may include a Perl type regular expression, as a <strong>String</strong>. 114 114 */ 115 116 117 118 119 120 121 122 123 124 125 126 127 115 public Argument(String name, String desc, byte type, String default_value) { 116 this.default_value = default_value; 117 this.desc = desc; 118 this.name = name; 119 this.type = type; 120 if(type == ENUM) { 121 this.list = new HashMap(); 122 } 123 if(type == METADATUM) { 124 values = new ArrayList(); 125 } 126 } 127 /** Method to add an element to the option list. 128 128 * @param name The name value of the option as a <strong>String</strong>. 129 129 * @param desc The description of this options as a <strong>String</strong>. 130 130 */ 131 132 133 134 135 136 137 138 139 140 141 131 public void addOption(String name, String desc) { 132 if(type == ENUM && name != null) { 133 if(desc == null) { 134 desc = ""; 135 } 136 if(!list.containsKey(name)) { 137 list.put(name, desc); 138 } 139 } 140 } 141 /** Method to compare two arguments for ordering. 142 142 * @param object The argument we are comparing to, as an <strong>Object</strong>. 143 143 * @return An <i>int</i> specifying the argument order, using values as set out in String. … … 145 145 * @see org.greenstone.gatherer.cdm.Argument 146 146 */ 147 148 149 150 151 147 public int compareTo(Object object) { 148 Argument argument = (Argument)object; 149 return getName().compareTo(argument.getName()); 150 } 151 /** Method to deep copy this argument. 152 152 * @return A newly created <strong>Argument</strong> which has the same details as this one. 153 153 */ 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 154 public Argument copy() { 155 Argument copy = new Argument(name, desc, type, default_value); 156 copy.setRequired(required); 157 if(values != null) { 158 copy.setValues(values); 159 } 160 if(list != null) { 161 HashMap list_deep_copy = new HashMap(); 162 Iterator it = list.keySet().iterator(); 163 while(it.hasNext()) { 164 String key = (String) it.next(); 165 String desc = (String) list.get(key); 166 list_deep_copy.put(new String(key), new String(desc)); 167 } 168 copy.setOptions(list_deep_copy); 169 } 170 return copy; 171 } 172 /** Method to determine if two arguments are equal. 173 173 * @param object The argument to test against, as an <strong>Object</strong>. 174 174 * @return <i>true</i> if the arguments names match, <i>false</i> otherwise. 175 175 */ 176 177 178 179 176 public boolean equals(Object object) { 177 return (compareTo(object) == 0); 178 } 179 /** Method to retrieve the value of default_value. 180 180 * @return A <strong>String</strong> containing the default value. 181 181 */ 182 183 184 185 182 public String getDefaultValue() { 183 return default_value; 184 } 185 /** Method to retrieve this arguments description. 186 186 * @return A <strong>String</strong> containing the description. 187 187 */ 188 189 190 191 188 public String getDesc() { 189 return desc; 190 } 191 /** Method to retrieve the description of a certain list option value. 192 192 * @param key The <strong>String</strong> whose description we are searching for. 193 193 * @return The description of the desired key as a <strong>String</strong> which may be empty if no such key exists. 194 194 */ 195 196 197 198 199 200 201 195 public String getDesc(String key) { 196 if(list.containsKey(key)) { 197 return (String)list.get(key); 198 } 199 return ""; 200 } 201 /** Method to retrieve the option list for this argument. 202 202 * @return A <strong>HashMap</strong> containing <option value> -> <description> entries. 203 203 */ 204 205 206 207 204 public HashMap getList() { 205 return list; 206 } 207 /** Method to retrieve the value of name. 208 208 * @return A <strong>String</strong> containing the argument name. 209 209 */ 210 211 212 213 210 public String getName() { 211 return name; 212 } 213 /** Retrieve the name of the owner of this argument. 214 214 * @return The owners name as a <strong>String</strong>. 215 215 */ 216 217 218 219 216 public String getOwner() { 217 return owner; 218 } 219 /** Method to determine the type of this argument. 220 220 * @return An <i>byte</i> specifying the type. 221 221 */ 222 223 224 225 222 public byte getType() { 223 return type; 224 } 225 /** Method to retrieve the value of value. 226 226 * @return The value of value as a <strong>String</strong>. 227 227 */ 228 229 230 231 228 public String getValue() { 229 return value; 230 } 231 /** Retrieve the vector of values. 232 232 * @return An <strong>ArrayList</strong> of values. 233 233 */ 234 235 236 237 234 public ArrayList getValues() { 235 return values; 236 } 237 /** Method to determine if this argument has been assigned. 238 238 * @return <i>true</i> if it has, <i>false</i> otherwise. 239 239 */ 240 241 242 243 240 public boolean isAssigned() { 241 return (required || value != null || (values != null && values.size() > 0)); 242 } 243 /** Method to determine of this argument is required for the associated script to work. 244 244 * @return <i>true</i> if this argument is required, <i>false</i> otherwise. 245 245 */ 246 247 248 249 246 public boolean isRequired() { 247 return required; 248 } 249 /** Method to allow for the activation of arguments that might never have their setValue() method called. 250 250 * @param new_state The required state as a <i>boolean</i>. 251 251 */ 252 253 254 255 256 257 258 259 260 252 public void setAssigned(boolean new_state) { 253 if(new_state && (value == null || values == null)) { 254 value = ""; 255 } 256 else { 257 value = null; 258 } 259 } 260 /** Sets the value of default_value. 261 261 * @param new_default_value The new value for default_value as a <strong>String</strong>. 262 262 */ 263 264 265 266 263 public void setDefault(String new_default_value) { 264 default_value = new_default_value; 265 } 266 /** Set the value of desc. 267 267 * @param new_desc The new value of desc as a <strong>String</strong>. 268 268 */ 269 270 271 272 269 public void setDesc(String new_desc) { 270 desc = new_desc; 271 } 272 /** Set the value of name. 273 273 * @param new_name The new value of name as a <strong>String</strong>. 274 274 */ 275 276 277 278 275 public void setName(String new_name) { 276 name = new_name; 277 } 278 /** Sets the value of the options list. 279 279 * @param new_list The new options list as a <strong>HashMap</strong>. 280 280 */ 281 282 283 284 281 public void setOptions(HashMap new_list) { 282 list = new_list; 283 } 284 /** Set the owner of this argument. 285 285 * @param owner The name of the owner of this argument as a <strong>String</strong>. 286 286 */ 287 288 289 290 287 public void setOwner(String owner) { 288 this.owner = owner; 289 } 290 /** Set the value of required. 291 291 * @param new_required The new value of required as a <i>boolean</i>. 292 292 */ 293 294 295 296 293 public void setRequired(boolean new_required) { 294 required = new_required; 295 } 296 /** Set the value of type. 297 297 * @param new_type The new value of type as an <i>byte</i>. 298 298 */ 299 300 301 302 303 304 305 306 307 308 299 public void setType(byte new_type) { 300 type = new_type; 301 if(type == ENUM) { 302 list = new HashMap(); 303 } 304 if(type == METADATUM && values == null) { 305 values = new ArrayList(); 306 } 307 } 308 /** Set the value of type, by matching a type to the given string. 309 309 * @param new_type A <strong>String</strong> which contains the name of a certain argument type. 310 310 */ 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 311 public void setType(String new_type) { 312 if(new_type.equalsIgnoreCase("enum")) { 313 this.type = ENUM; 314 list = new HashMap(); 315 } 316 else if(new_type.equalsIgnoreCase("flag")) { 317 this.type = FLAG; 318 } 319 else if(new_type.equalsIgnoreCase("hierarchy")) { 320 this.type = HIERARCHY; 321 } 322 else if(new_type.equalsIgnoreCase("int")) { 323 this.type = INTEGER; 324 } 325 else if(new_type.equalsIgnoreCase("language")) { 326 this.type = LANGUAGE; 327 } 328 else if(new_type.equalsIgnoreCase("metadata")) { 329 this.type = METADATA; 330 } 331 else if(new_type.equalsIgnoreCase("metadatum")) { 332 this.type = METADATUM; 333 if(values == null) { 334 values = new ArrayList(); 335 } 336 } 337 else if(new_type.equalsIgnoreCase("string")) { 338 this.type = STRING; 339 } 340 } 341 /** Method to set the value of value. 342 342 * @param new_value The new value of value as a <strong>String</strong>. 343 343 */ 344 345 346 347 344 public void setValue(String new_value) { 345 this.value = new_value; 346 } 347 /** Set the values vector to the given values. Currently I just assign the new values, whereas I may later want to implement a deep clone. 348 348 * @param new_values An <strong>ArrayList</strong> of values. 349 349 */ 350 351 352 353 350 public void setValues(ArrayList new_values) { 351 values = new_values; 352 } 353 /** Method for translating the data of this class into a string. 354 354 * @return A <strong>String</strong> containing a fragment of the total arguments string. 355 355 */ 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 356 public String toString() { 357 switch(type) { 358 case FLAG: 359 return "-" + name; 360 case METADATUM: 361 String text = "-" + name + " "; 362 for(int i = 0; i < values.size(); i++) { 363 text = text + values.get(i).toString(); 364 if(i < values.size() - 1) { 365 text = text + ","; 366 } 367 } 368 return text; 369 default: 370 return "-" + name + " " + value; 371 } 372 } 373 373 } 374 374 -
trunk/gli/src/org/greenstone/gatherer/cdm/ArgumentConfiguration.java
r4293 r4366 103 103 104 104 public class ArgumentConfiguration 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 105 extends JDialog 106 implements ActionListener { 107 /** The data whose arguments we are editing. */ 108 private ArgumentContainer data = null; 109 /** Argument these argument controls coloured or uncoloured (alternates to indicate inheritance). */ 110 private boolean coloured = false; 111 /** Whether we have successfully edited the arguments associated with the ArgumentContainer or if we have failed to enter required arguments and have instead cancelled (which would cause argument additions to roll back). */ 112 private boolean success = false; 113 /** A reference to the main CollectionDesignManager class for access to other managers. */ 114 private CollectionDesignManager manager = null; 115 /** A reference to the Gatherer. */ 116 private Gatherer gatherer = null; 117 /** A button to cancel this dialog. */ 118 private JButton cancel = null; 119 /** A button to accept the changes and close the dialog. */ 120 private JButton ok = null; 121 /** A reference to the ourselves so our inner classes can dispose us like a dialog. */ 122 private ArgumentConfiguration self = null; 123 /** The central pane where a list of known arguments is displayed. */ 124 private JPanel central_pane = null; 125 /** The field for entering custom arguments. */ 126 private JTextField custom = null; 127 /** The name of the owner of the last argument control. */ 128 private String previous_owner = null; 129 /** The size used for an argument label. */ 130 static final private Dimension LABEL_SIZE = new Dimension(225, 25); 131 /** Size of a list. */ 132 static final private Dimension LIST_SIZE = new Dimension(380, 50); 133 /** The size used for the dialog. */ 134 static final private Dimension SIZE = new Dimension(800, 400); 135 /** Constructor. 136 * @param gatherer A reference to the <strong>Gatherer</strong>. 137 * @param manager The <strong>CollectionDesignManager</strong> for access to other configuration managers. 138 * @param data The plugin or classifier whose arguments we are configuring, in the form of its supported <strong>ArgumentContainer</strong> interface. 139 * @see org.greenstone.gatherer.Configuration 140 */ 141 public ArgumentConfiguration(Gatherer gatherer, CollectionDesignManager manager, ArgumentContainer data) { 142 super(gatherer.g_man); 143 this.data = data; 144 this.gatherer = gatherer; 145 this.manager = manager; 146 this.self = this; 147 String custom_str = data.getCustom(); 148 // Create 149 setModal(true); 150 setSize(SIZE); 151 setTitle(get("Title")); 152 JPanel button_pane = new JPanel(); 153 cancel = new JButton(get("General.Cancel")); 154 cancel.setMnemonic(KeyEvent.VK_C); 155 central_pane = new JPanel(); 156 JPanel content_pane = (JPanel) getContentPane(); 157 if(custom_str != null) { 158 custom = new JTextField(custom_str); 159 } 160 else { 161 custom = new JTextField(); 162 } 163 JLabel custom_label = new JLabel(get("Custom")); 164 custom_label.setPreferredSize(LABEL_SIZE); 165 JPanel custom_pane = new JPanel(); 166 String args[] = new String[1]; 167 args[0] = data.getName(); 168 JLabel header = new JLabel(get("Header",args)); 169 args = null; 170 header.setHorizontalAlignment(JLabel.CENTER); 171 header.setOpaque(true); 172 JPanel header_pane = new JPanel(); 173 ok = new JButton(get("General.OK")); 174 ok.setMnemonic(KeyEvent.VK_O); 175 // Listeners 176 cancel.addActionListener(this); 177 ok.addActionListener(this); 178 // Layout 179 custom_pane.setLayout(new BorderLayout()); 180 custom_pane.add(custom_label, BorderLayout.WEST); 181 custom_pane.add(custom, BorderLayout.CENTER); 182 183 header_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 184 header_pane.setLayout(new BorderLayout()); 185 header_pane.add(header, BorderLayout.NORTH); 186 header_pane.add(custom_pane, BorderLayout.CENTER); 187 188 button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 189 button_pane.setLayout(new GridLayout(1,2)); 190 button_pane.add(ok); 191 button_pane.add(cancel); 192 193 central_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 194 central_pane.setLayout(new BoxLayout(central_pane, BoxLayout.Y_AXIS)); 195 196 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 197 content_pane.setLayout(new BorderLayout()); 198 content_pane.add(header_pane, BorderLayout.NORTH); 199 content_pane.add(new JScrollPane(central_pane), BorderLayout.CENTER); 200 content_pane.add(button_pane, BorderLayout.SOUTH); 201 202 // Now generate a set of controls for each of the arguments. 203 generateControls(); 204 205 // Display on screen. 206 Dimension screen_size = gatherer.config.screen_size; 207 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 208 screen_size = null; 209 } 210 /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured on one of the controls we are listening to. 211 211 * @param event An <strong>ActionEvent</strong> containing pertinant information about the event that fired this call. 212 212 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl 213 213 * @see org.greenstone.gatherer.cdm.ArgumentContainer 214 214 */ 215 216 217 215 public void actionPerformed(ActionEvent event) { 216 boolean cont = true; 217 if(event.getSource() == ok) { 218 218 // Update the details stored in the data objects arguments. 219 220 221 219 if(custom.getText().length() > 0) { 220 data.setCustom(custom.getText()); 221 } 222 222 // Loop through each of the controls in the central pane, updating the matching argument as necessary. 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 223 for(int i = 0; i < central_pane.getComponentCount(); i++) { 224 Component component = central_pane.getComponent(i); 225 if(component instanceof ArgumentControl) { 226 // Once cont goes false it stays false 227 cont = cont && ((ArgumentControl)component).updateArgument(); 228 } 229 } 230 if(cont) { 231 success = true; 232 } 233 } 234 if(cont) { 235 dispose(); 236 } 237 } 238 /** Destructor. */ 239 public void destroy() { 240 cancel = null; 241 central_pane = null; 242 custom = null; 243 data = null; 244 gatherer = null; 245 manager = null; 246 ok = null; 247 self = null; 248 } 249 /** Method which actually forces the dialog to be shown on screen. 250 250 * @return <i>true</i> if the user completed configuration and pressed ok, <i>false</i> otherwise. 251 251 */ 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 252 public boolean display() { 253 show(); 254 return success; 255 } 256 257 private void addHeader(String name, Color color) { 258 JPanel header = new JPanel(); 259 header.setBackground(color); 260 JPanel inner_pane = new JPanel(); 261 inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder())); 262 inner_pane.setBackground(color); 263 JLabel header_label = new JLabel("<html><strong>" + name + "</strong></html>"); 264 header_label.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 265 header_label.setHorizontalAlignment(JLabel.CENTER); 266 header_label.setOpaque(true); 267 // Layout. 268 inner_pane.setLayout(new BorderLayout()); 269 inner_pane.add(header_label, BorderLayout.CENTER); 270 271 header.setLayout(new BorderLayout()); 272 header.add(inner_pane, BorderLayout.CENTER); 273 central_pane.add(header); 274 } 275 276 /** Method to iterate through the arguments associated with whatever argument container we are building an argument control view for, creating the appropriate controls for each. 277 277 * @see org.greenstone.gatherer.cdm.Argument 278 278 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl 279 279 */ 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 280 private void generateControls() { 281 ArrayList arguments = data.getArguments(); 282 int total_height = 250; 283 int size = arguments.size(); 284 for(int i = 0; i < size; i++) { 285 Argument argument = (Argument) arguments.get(i); 286 ArgumentControl argument_control = new ArgumentControl(argument); 287 total_height = total_height - argument_control.getPreferredSize().height; 288 central_pane.add(argument_control); 289 } 290 if(total_height > 0) { 291 JPanel filler = new JPanel(); 292 filler.setPreferredSize(new Dimension(100, total_height)); 293 filler.setSize(new Dimension(100, total_height)); 294 central_pane.add(filler); 295 } 296 } 297 /** Method to retrieve a phrase from the dictionary based on a key. 298 298 * @param key A <strong>String</strong> used to find the correct phrase. 299 299 * @param args A <strong>String[]</strong> of arguments used in formatting and filling out the phrase. 300 300 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. 301 301 */ 302 303 304 305 302 private String get(String key) { 303 return get(key, null); 304 } 305 /** Method to retrieve a phrase from the dictionary based on a key. 306 306 * @param key A <strong>String</strong> used to find the correct phrase. 307 307 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. 308 308 */ 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 309 private String get(String key, String args[]) { 310 if(key.indexOf(".") == -1) { 311 key = "CDM.ArgumentConfiguration." + key; 312 } 313 return gatherer.dictionary.get(key, args); 314 } 315 /** This class encapsulates all the technical difficulty of creating a specific control based on an Argument. */ 316 private class ArgumentControl 317 extends JPanel { 318 /** The Argument this control will be based on. */ 319 private Argument argument = null; 320 /** One of a possible two buttons available for adding to this control. */ 321 private JButton one = null; 322 /** The second of two buttons available for adding to this control. */ 323 private JButton two = null; 324 /** A checkbox to allow enabling or diabling of this Argument. */ 325 private JCheckBox enabled = null; 326 /** Some form of editor component, such as a JComboBox or JTextField, used to set parameters to an Argument. */ 327 private JComponent value = null; 328 /** Can be used in place of the other editor components if a list is required. */ 329 private JList list = null; 330 /** Constructor. 331 * @param argument The <strong>Argument</strong> this control will be built around. 332 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.AddListener 333 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.EnabledListener 334 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.HierarchyListener 335 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.ListOption 336 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.RemoveListener 337 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.ToolTipUpdater 338 */ 339 public ArgumentControl(Argument argument) { 340 this.argument = argument; 341 String tip = "<html>" + argument.getDesc() + "</html>"; 342 tip = Utility.formatHTMLWidth(tip, 60); 343 343 // If this is the first control, there is no history. 344 if(previous_owner == null) { 345 previous_owner = argument.getOwner(); 346 addHeader(previous_owner, Color.white); 344 if(previous_owner == null) { 345 previous_owner = argument.getOwner(); 346 addHeader(previous_owner, Color.white); 347 } 348 // Otherwise if the owner of the control has changed since the last argument, toggle the colouring of the control. 349 else if(previous_owner != argument.getOwner()) { 350 coloured = !coloured; 351 previous_owner = argument.getOwner(); 352 addHeader(previous_owner, (coloured ? Gatherer.config.getColor("coloring.collection_tree_background", false) : Color.white)); 353 } 354 // Create 355 if(coloured) { 356 setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 357 } 358 else { 359 setBackground(Color.white); 360 } 361 JLabel owner_label = new JLabel(argument.getOwner()); 362 owner_label.setOpaque(false); 363 JLabel label = new JLabel(argument.getName()); 364 label.setOpaque(false); 365 label.setPreferredSize(LABEL_SIZE); 366 label.setToolTipText(tip); 367 enabled = new JCheckBox(argument.getName()); 368 enabled.setOpaque(false); 369 enabled.setPreferredSize(LABEL_SIZE); 370 enabled.setToolTipText(tip); 371 JPanel inner_pane = new JPanel(); 372 inner_pane.setOpaque(false); 373 String existing_value = argument.getValue(); 374 String default_value = argument.getDefaultValue(); 375 switch(argument.getType()) { 376 case Argument.ENUM: 377 // Build an option model, wrapping each entry of the list table. 378 HashMap arg_list = argument.getList(); 379 ArrayList options_model = new ArrayList(); 380 Iterator it = arg_list.keySet().iterator(); 381 while(it.hasNext()) { 382 String key = (String) it.next(); 383 options_model.add(new ListOption(key, (String)arg_list.get(key))); 384 } 385 Collections.sort(options_model); 386 value = new JComboBox(options_model.toArray()); 387 ((JComboBox)value).addActionListener(new ToolTipUpdater()); 388 if(existing_value != null) { 389 // Select the correct value. Since they're all text strings we better iterate to be safe. 390 selectValue((JComboBox)value, existing_value); 391 } 392 else if(default_value != null) { 393 // Same as above except for default value. 394 selectValue((JComboBox)value, default_value); 395 } 396 break; 397 case Argument.FLAG: 398 // Only need the check box. 399 break; 400 case Argument.HIERARCHY: 401 value = new JComboBox(gatherer.c_man.msm.getAssignedElements(true)); 402 /** @TODO - figure out a smarter way of allowing Greenstone extracted metadata to be selected. */ 403 ((JComboBox)value).setEditable(true); 404 ((JComboBox)value).addItemListener(new HierarchyListener()); 405 // Now ensure we have the existing value or default value selected if either exist. 406 if(existing_value != null) { 407 selectValue((JComboBox)value, existing_value); 408 } 409 else if(default_value != null) { 410 selectValue((JComboBox)value, default_value); 411 } 412 break; 413 case Argument.INTEGER: 414 case Argument.STRING: 415 // Use a standard text field 416 if(existing_value != null) { 417 value = new JTextField(existing_value); 418 } 419 else { 420 if(default_value != null) { 421 value = new JTextField(default_value); 422 } 423 // Special test just for the hfile field. 424 else if(argument.getName().equals("hfile")) { 425 // Work through previous controls looking for the metadata one. 426 for(int i = 0; i < central_pane.getComponentCount(); i++) { 427 Object object = central_pane.getComponent(i); 428 if(object instanceof ArgumentControl) { 429 ArgumentControl control = (ArgumentControl) object; 430 if(control.toString().equals("metadata")) { 431 Object temp = control.getValue(); 432 if(temp != null) { 433 value = new JTextField(temp.toString() + ".txt"); 434 } 347 435 } 348 // Otherwise if the owner of the control has changed since the last argument, toggle the colouring of the control. 349 else if(previous_owner != argument.getOwner()) { 350 coloured = !coloured; 351 previous_owner = argument.getOwner(); 352 addHeader(previous_owner, (coloured ? Gatherer.config.getColor("coloring.collection_tree_background", false) : Color.white)); 353 } 354 // Create 355 if(coloured) { 356 setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 357 } 358 else { 359 setBackground(Color.white); 360 } 361 JLabel owner_label = new JLabel(argument.getOwner()); 362 owner_label.setOpaque(false); 363 JLabel label = new JLabel(argument.getName()); 364 label.setOpaque(false); 365 label.setPreferredSize(LABEL_SIZE); 366 label.setToolTipText(tip); 367 enabled = new JCheckBox(argument.getName()); 368 enabled.setOpaque(false); 369 enabled.setPreferredSize(LABEL_SIZE); 370 enabled.setToolTipText(tip); 371 JPanel inner_pane = new JPanel(); 372 inner_pane.setOpaque(false); 373 String existing_value = argument.getValue(); 374 String default_value = argument.getDefaultValue(); 375 switch(argument.getType()) { 376 case Argument.ENUM: 377 // Build an option model, wrapping each entry of the list table. 378 HashMap arg_list = argument.getList(); 379 ArrayList options_model = new ArrayList(); 380 Iterator it = arg_list.keySet().iterator(); 381 while(it.hasNext()) { 382 String key = (String) it.next(); 383 options_model.add(new ListOption(key, (String)arg_list.get(key))); 384 } 385 Collections.sort(options_model); 386 value = new JComboBox(options_model.toArray()); 387 ((JComboBox)value).addActionListener(new ToolTipUpdater()); 388 if(existing_value != null) { 389 // Select the correct value. Since they're all text strings we better iterate to be safe. 390 selectValue((JComboBox)value, existing_value); 391 } 392 else if(default_value != null) { 393 // Same as above except for default value. 394 selectValue((JComboBox)value, default_value); 395 } 396 break; 397 case Argument.FLAG: 398 // Only need the check box. 399 break; 400 case Argument.HIERARCHY: 401 value = new JComboBox(gatherer.c_man.msm.getAssignedElements(true)); 402 /** @TODO - figure out a smarter way of allowing Greenstone extracted metadata to be selected. */ 403 ((JComboBox)value).setEditable(true); 404 ((JComboBox)value).addItemListener(new HierarchyListener()); 405 // Now ensure we have the existing value or default value selected if either exist. 406 if(existing_value != null) { 407 selectValue((JComboBox)value, existing_value); 408 } 409 else if(default_value != null) { 410 selectValue((JComboBox)value, default_value); 411 } 412 break; 413 case Argument.INTEGER: 414 case Argument.STRING: 415 // Use a standard text field 416 if(existing_value != null) { 417 value = new JTextField(existing_value); 418 } 419 else { 420 if(default_value != null) { 421 value = new JTextField(default_value); 422 } 423 // Special test just for the hfile field. 424 else if(argument.getName().equals("hfile")) { 425 // Work through previous controls looking for the metadata one. 426 for(int i = 0; i < central_pane.getComponentCount(); i++) { 427 Object object = central_pane.getComponent(i); 428 if(object instanceof ArgumentControl) { 429 ArgumentControl control = (ArgumentControl) object; 430 if(control.toString().equals("metadata")) { 431 Object temp = control.getValue(); 432 if(temp != null) { 433 value = new JTextField(temp.toString() + ".txt"); 434 } 435 } 436 437 } 438 } 439 } 440 else { 441 value = new JTextField(); 442 } 443 } 444 break; 445 case Argument.LANGUAGE: 446 value = new JComboBox(manager.languages.getLanguageCodes().toArray()); 447 // Now ensure we have the existing value or default value selected if either exist. 448 Language selected = null; 449 if(existing_value != null) { 450 selected = manager.languages.getLanguage(existing_value, false); 451 } 452 else if(default_value != null) { 453 selected = manager.languages.getLanguage(default_value, false); 454 } 455 if(selected != null) { 456 ((JComboBox)value).setSelectedItem(selected); 457 } 458 break; 459 case Argument.METADATA: 460 value = new JComboBox(gatherer.c_man.msm.getAssignedElements()); 461 /** @TODO - figure out a smarter way of allowing Greenstone extracted metadata to be selected. */ 462 ((JComboBox)value).setEditable(true); 463 // Now ensure we have the existing value or default value selected if either exist. 464 if(existing_value != null) { 465 selectValue((JComboBox)value, existing_value); 466 } 467 else if(default_value != null) { 468 selectValue((JComboBox)value, default_value); 469 } 470 break; 471 case Argument.METADATUM: 472 // Comma separated metadata values. 473 ArrayList values = argument.getValues(); 474 value = new JComboBox(gatherer.c_man.msm.getAssignedElements()); 475 DefaultListModel model = new DefaultListModel(); 476 list = new JList(model); 477 list.setVisibleRowCount(3); 478 for(int i = 0; i < values.size(); i++) { 479 model.addElement(values.get(i)); 480 } 481 one = new JButton(get("Add")); 482 one.addActionListener(new AddListener((JComboBox)value, list)); 483 two = new JButton(get("Remove")); 484 two.addActionListener(new RemoveListener(list)); 485 if(argument.getValues().size() > 0 || argument.isRequired()) { 486 enabled.setSelected(true); 487 list.setBackground(Color.white); 488 list.setEnabled(true); 489 one.setEnabled(true); 490 two.setEnabled(true); 491 value.setEnabled(true); 492 } 493 else { 494 enabled.setSelected(false); 495 list.setBackground(Color.lightGray); 496 list.setEnabled(false); 497 one.setEnabled(false); 498 two.setEnabled(false); 499 value.setEnabled(false); 500 } 501 break; 502 } 436 437 } 438 } 439 } 440 else { 441 value = new JTextField(); 442 } 443 } 444 break; 445 case Argument.LANGUAGE: 446 value = new JComboBox(manager.languages.getLanguageCodes().toArray()); 447 // Now ensure we have the existing value or default value selected if either exist. 448 Language selected = null; 449 if(existing_value != null) { 450 selected = manager.languages.getLanguage(existing_value, false); 451 } 452 else if(default_value != null) { 453 selected = manager.languages.getLanguage(default_value, false); 454 } 455 if(selected != null) { 456 ((JComboBox)value).setSelectedItem(selected); 457 } 458 break; 459 case Argument.METADATA: 460 value = new JComboBox(gatherer.c_man.msm.getAssignedElements()); 461 /** @TODO - figure out a smarter way of allowing Greenstone extracted metadata to be selected. */ 462 ((JComboBox)value).setEditable(true); 463 // Now ensure we have the existing value or default value selected if either exist. 464 if(existing_value != null) { 465 selectValue((JComboBox)value, existing_value); 466 } 467 else if(default_value != null) { 468 selectValue((JComboBox)value, default_value); 469 } 470 break; 471 case Argument.METADATUM: 472 // Comma separated metadata values. 473 ArrayList values = argument.getValues(); 474 value = new JComboBox(gatherer.c_man.msm.getAssignedElements()); 475 DefaultListModel model = new DefaultListModel(); 476 list = new JList(model); 477 list.setVisibleRowCount(3); 478 for(int i = 0; i < values.size(); i++) { 479 model.addElement(values.get(i)); 480 } 481 one = new JButton(get("Add")); 482 one.addActionListener(new AddListener((JComboBox)value, list)); 483 two = new JButton(get("Remove")); 484 two.addActionListener(new RemoveListener(list)); 485 if(argument.getValues().size() > 0 || argument.isRequired()) { 486 enabled.setSelected(true); 487 list.setBackground(Color.white); 488 list.setEnabled(true); 489 one.setEnabled(true); 490 two.setEnabled(true); 491 value.setEnabled(true); 492 } 493 else { 494 enabled.setSelected(false); 495 list.setBackground(Color.lightGray); 496 list.setEnabled(false); 497 one.setEnabled(false); 498 two.setEnabled(false); 499 value.setEnabled(false); 500 } 501 break; 502 } 503 503 // Enable or disable as necessary. 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 504 if(argument.isRequired() || argument.isAssigned()) { 505 enabled.setSelected(true); 506 if(value != null) { 507 value.setOpaque(true); 508 value.setBackground(Color.white); 509 value.setEnabled(true); 510 } 511 } 512 else { 513 enabled.setSelected(false); 514 if(value != null) { 515 value.setOpaque(true); 516 value.setBackground(Color.lightGray); 517 value.setEnabled(false); 518 } 519 } 520 520 // Listener 521 522 523 521 if(value != null && !argument.isRequired()) { 522 enabled.addActionListener(new EnabledListener(one, two, list, value)); 523 } 524 524 // Layout 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 525 if(list == null) { 526 enabled.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 527 inner_pane.setLayout(new BorderLayout()); 528 if(argument.isRequired()) { 529 inner_pane.add(label, BorderLayout.WEST); 530 } 531 else { 532 inner_pane.add(enabled, BorderLayout.WEST); 533 } 534 if(value != null) { 535 inner_pane.add(value, BorderLayout.CENTER); 536 } 537 } 538 else { 539 JPanel control_pane = new JPanel(new GridLayout(2,1)); 540 control_pane.add(enabled); 541 control_pane.add(value); 542 control_pane.setOpaque(false); 543 544 JPanel left_pane = new JPanel(new BorderLayout()); 545 left_pane.add(control_pane, BorderLayout.CENTER); 546 left_pane.add(one, BorderLayout.SOUTH); 547 left_pane.setOpaque(false); 548 549 JPanel right_pane = new JPanel(new BorderLayout()); 550 right_pane.add(new JScrollPane(list), BorderLayout.CENTER); 551 right_pane.add(two, BorderLayout.SOUTH); 552 right_pane.setOpaque(false); 553 554 inner_pane.setLayout(new GridLayout(1,2)); 555 inner_pane.add(left_pane); 556 inner_pane.add(right_pane); 557 } 558 setLayout(new BorderLayout()); 559 559 //add(owner_label, BorderLayout.NORTH); 560 561 562 563 564 565 566 567 568 569 570 571 560 add(inner_pane, BorderLayout.CENTER); 561 } 562 public Object getValue() { 563 if(value instanceof JComboBox) { 564 return ((JComboBox)value).getSelectedItem(); 565 } 566 else if(value instanceof JTextField) { 567 return ((JTextField)value).getText(); 568 } 569 return null; 570 } 571 /** Identifies this control by returning the name of the Argument it is based on. 572 572 * @return The name of the Argument as a <strong>String</strong>. 573 573 * @see org.greenstone.gatherer.cdm.Argument 574 574 */ 575 576 577 578 575 public String toString() { 576 return argument.getName(); 577 } 578 /** Updates the enwrapped Argument using the values provided by the controls. 579 579 * @return <i>true</i> if the update was successful, <i>false</i> otherwise. 580 580 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.ArgumentControl.ListOption … … 582 582 * @see org.greenstone.gatherer.msm.ElementWrapper 583 583 */ 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 584 public boolean updateArgument() { 585 if(enabled.isSelected() || argument.isRequired()) { 586 argument.setAssigned(true); 587 String result = null; 588 switch(argument.getType()) { 589 case Argument.ENUM: 590 ListOption option = (ListOption)((JComboBox)value).getSelectedItem(); 591 result = option.getValue(); 592 if(result.length() > 0) { 593 argument.setValue(result); 594 } 595 else { 596 if(argument.isRequired()) { 597 String args[] = new String[1]; 598 args[0] = argument.getName(); 599 JOptionPane.showMessageDialog(self, get("Required_Argument", args), get("Error_Title"), JOptionPane.ERROR_MESSAGE); 600 args = null; 601 return false; 602 } 603 else { 604 argument.setValue(null); 605 } 606 } 607 return true; 608 case Argument.FLAG: 609 // Should have already been handled above. 610 return true; 611 case Argument.INTEGER: 612 result = ((JTextField)value).getText(); 613 if(result.length() > 0) { 614 // Test if the value entered is a valid int. 615 try { 616 int x = Integer.parseInt(result); 617 } 618 catch(NumberFormatException nfe) { 619 String args[] = new String[2]; 620 args[0] = argument.getName(); 621 args[1] = result; 622 JOptionPane.showMessageDialog(self, get("Bad_Integer", args), get("Error_Title"), JOptionPane.ERROR_MESSAGE); 623 args = null; 624 return false; 625 } 626 argument.setValue(result); 627 } 628 else { 629 if(argument.isRequired()) { 630 String args[] = new String[1]; 631 args[0] = argument.getName(); 632 JOptionPane.showMessageDialog(self, get("Required_Argument", args), get("Error_Title"), JOptionPane.ERROR_MESSAGE); 633 args = null; 634 return false; 635 } 636 else { 637 argument.setValue(null); 638 } 639 } 640 return true; 641 case Argument.LANGUAGE: 642 Language language = (Language) ((JComboBox)value).getSelectedItem(); 643 argument.setValue(language.getCode()); 644 // Kinda lucked out here. Its impossible not to choose an entry from these comboboxes as they are restricted. 645 return true; 646 case Argument.METADATA: 647 case Argument.HIERARCHY: 648 argument.setValue(((JComboBox)value).getSelectedItem().toString()); 649 // Kinda lucked out here. Its impossible not to choose an entry from these comboboxes as they are restricted. 650 return true; 651 case Argument.METADATUM: 652 DefaultListModel model = (DefaultListModel)list.getModel(); 653 ArrayList values = new ArrayList(); 654 for(int i = 0; i < model.size(); i++) { 655 values.add(model.get(i)); 656 } 657 argument.setValues(values); 658 return true; 659 case Argument.STRING: 660 result = ((JTextField)value).getText(); 661 if(result.length() > 0) { 662 argument.setValue(result); 663 } 664 else { 665 if(argument.isRequired()) { 666 String args[] = new String[1]; 667 args[0] = argument.getName(); 668 JOptionPane.showMessageDialog(self, get("Required_Argument", args), get("Error_Title"), JOptionPane.ERROR_MESSAGE); 669 return false; 670 } 671 else { 672 argument.setValue(null); 673 } 674 } 675 return true; 676 } 677 return false; 678 } 679 else { 680 argument.setAssigned(false); 681 return true; 682 } 683 } 684 /** Method to ensure that a certain value is selected, if it exists within that combobox to begin with. 685 685 * @param combobox The <strong>JComboBox</strong> whose selection we are trying to preset. 686 686 * @param target The desired value of the selection as a <strong>String</strong>. … … 688 688 * @see org.greenstone.gatherer.msm.ElementWrapper 689 689 */ 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 690 public void selectValue(JComboBox combobox, String target) { 691 for(int i = 0; i < combobox.getItemCount(); i++) { 692 Object object = combobox.getItemAt(i); 693 if(object instanceof ListOption) { 694 ListOption lo = (ListOption) object; 695 if(lo.getValue().equals(target)) { 696 combobox.setSelectedIndex(i); 697 return; 698 } 699 } 700 else if(object instanceof ElementWrapper) { 701 if(object.toString().equals(target)) { 702 combobox.setSelectedIndex(i); 703 return; 704 } 705 } 706 } 707 } 708 /** Forces the control into an 'enabled' mode. */ 709 public void setEnabled() { 710 enabled.setSelected(true); 711 } 712 /** Explicitly sets the value of a JTextField type control to the given String. 713 713 * @param value_str The new value of the control as a <strong>String</strong>. 714 714 */ 715 716 717 718 719 720 715 public void setValue(String value_str) { 716 ((JTextField)value).setText(value_str); 717 } 718 /** Listener which adds entries to a list from a combobox when fired. */ 719 private class AddListener 720 implements ActionListener { 721 721 /** The model behind the target list. */ 722 722 private DefaultListModel model = null; 723 723 /** The source for data to be added to the list. */ 724 724 private JComboBox source = null; 725 725 /** The list to add data to. */ 726 726 private JList target = null; 727 727 /** Constructor. 728 728 * @param source A <strong>JComboBox</strong> which serves as the source for data. 729 729 * @param target A <strong>JList</strong> which serves as the target for data. 730 730 */ 731 732 733 734 735 731 public AddListener(JComboBox source, JList target) { 732 this.model = (DefaultListModel) target.getModel(); 733 this.source = source; 734 this.target = target; 735 } 736 736 /** When the add button is clicked, we attempt to add the selected metadata from the source into the target. 737 737 * @param event An <strong>ActionEvent</strong> containing information about the event. 738 738 * @see org.greenstone.gatherer.msm.ElementWrapper 739 739 */ 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 740 public void actionPerformed(ActionEvent event) { 741 ElementWrapper element = (ElementWrapper) source.getSelectedItem(); 742 String name = element.toString(); 743 if(!model.contains(name)) { 744 boolean found = false; 745 int index = 0; 746 while(!found && index < model.size()) { 747 String sibling = (String) model.get(index); 748 if(name.compareTo(sibling) < 0) { 749 model.add(index, name); 750 found = true; 751 } 752 else { 753 index++; 754 } 755 } 756 if(!found) { 757 model.addElement(name); 758 } 759 } 760 } 761 } 762 /** Listens for actions apon the enable checkbox, and if detected enables or diables control appropriately. */ 763 private class EnabledListener 764 implements ActionListener { 765 765 /** One of two possible buttons that might have their enabled state changed by this listener. */ 766 766 private JButton one = null; 767 767 /** One of two possible buttons that might have their enabled state changed by this listener. */ 768 768 private JButton two = null; 769 769 /** An editor component, such as a JComboBox or JTextField, that might have its enabled state changed by this listener. */ 770 770 private JComponent target = null; 771 771 /** A list which might have its enabled state changed by this listener. */ 772 772 private JList list = null; 773 773 /** Constructor. 774 774 * @param one A <strong>JButton</strong> whose enabled state is determined by the listener, or <i>null</i> if no button. … … 777 777 * @param list A <strong>JComponent</strong> whose enabled state is determined by the listener, or <i>null</i> if no component. 778 778 */ 779 780 781 782 783 784 779 public EnabledListener(JButton one, JButton two, JList list, JComponent target) { 780 this.list = list; 781 this.one = one; 782 this.target = target; 783 this.two = two; 784 } 785 785 /** Any implementation of ActionListener must include this method so that we can be informed when an action has been performed on or registered check box, prompting us to change the state of the other controls as per the users request. 786 786 * @param event An <strong>ActionEvent</strong> containing information about the click. 787 787 */ 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 788 public void actionPerformed(ActionEvent event) { 789 JCheckBox source = (JCheckBox)event.getSource(); 790 if(source.isSelected()) { 791 target.setBackground(Color.white); 792 target.setEnabled(true); 793 if(one != null && two != null && list != null) { 794 one.setEnabled(true); 795 two.setEnabled(true); 796 list.setBackground(Color.white); 797 list.setEnabled(true); 798 } 799 } 800 else { 801 target.setBackground(Color.lightGray); 802 target.setEnabled(false); 803 if(one != null && two != null && list != null) { 804 one.setEnabled(false); 805 two.setEnabled(false); 806 list.setBackground(Color.lightGray); 807 list.setEnabled(false); 808 } 809 } 810 } 811 } 812 /** If a metadata element is selected that requires an hfile, then this listener defaults that hfile. */ 813 private class HierarchyListener 814 implements ItemListener { 815 815 /** Any implementation of ItemListener must include this method so that we can be informed when an item from the list is selected, and generate a predetermined hfile for that selection. 816 816 * @param event An <strong>ItemEvent</strong> containing information about the selection. … … 818 818 * @see org.greenstone.gatherer.valuetree.GValueModel 819 819 */ 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 820 public void itemStateChanged(ItemEvent event) { 821 // Determine if the selected element represents a hierarchy. 822 Object temp = ((JComboBox)value).getSelectedItem(); 823 String filename = temp.toString(); 824 // Search for a argument control called hfile and enable and set value. 825 for(int i = 0; i < central_pane.getComponentCount(); i++) { 826 Object object = central_pane.getComponent(i); 827 if(object instanceof ArgumentControl) { 828 ArgumentControl control = (ArgumentControl) object; 829 if(control.toString().equals("hfile")) { 830 control.setValue(filename + ".txt"); 831 control.setEnabled(true); 832 } 833 } 834 } 835 } 836 } 837 /** A ListOption is a compound item which is constructed from several Strings. That magic part is that the length of screen real-estate used by the text version of this item is limited. */ 838 private class ListOption 839 implements Comparable { 840 840 /** The maximum length of this String version of this item. */ 841 841 private int MAX_DESC = 35; 842 842 /** The description of the value for this item. */ 843 843 private String description = null; 844 844 /** A cached value for the text value of this option, as it never changes after the first call to toString(). */ 845 845 private String text = null; 846 846 /** The value for this item. */ 847 847 private String value = null; 848 848 /** Constructor. 849 849 * @param value The value for this item as a <strong>String</strong>. 850 850 * @param description The description of the value as a <strong>String</strong>. 851 851 */ 852 853 854 855 852 public ListOption(String value, String description) { 853 this.description = description; 854 this.value = value; 855 } 856 856 /** Compare two possible ListOption objects for ordering. 857 857 * @param object The <strong>Object</strong> to compare to. … … 859 859 * @see java.lang.String#compareTo 860 860 */ 861 862 863 861 public int compareTo(Object object) { 862 return toString().compareTo(object.toString()); 863 } 864 864 /** Tests two possible ListOption objects for equality. Uses the result from compareTo(). 865 865 * @param The <strong>Object</strong> which may be equal. 866 866 * @return <i>true</i> if the objects are equal, <i>false</i> otherwise. 867 867 */ 868 869 870 868 public boolean equals(Object object) { 869 return (compareTo(object) == 0); 870 } 871 871 /** Retrieve the description of this list item. 872 872 * @return The description as a <strong>String</strong>. 873 873 */ 874 875 876 874 public String getDesc() { 875 return description; 876 } 877 877 /** Retrieve the value of this list item. 878 878 * @return The value as a <strong>String</strong>. 879 879 */ 880 881 882 880 public String getValue() { 881 return value; 882 } 883 883 /** Convert this object into a nice readable String. 884 884 * @return A <strong>String</strong> representing this object. 885 885 */ 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 886 public String toString() { 887 if(text == null) { 888 if(description.length() >= MAX_DESC) { 889 text = value + " - " + description.substring(0, MAX_DESC); 890 } 891 else { 892 text = value + " - " + description; 893 } 894 } 895 return text; 896 } 897 } 898 /** Listener which removes entries from a list from a combobox when fired. */ 899 private class RemoveListener 900 implements ActionListener { 901 901 /** The model behind the target list. */ 902 902 private DefaultListModel model = null; 903 903 /** The list to remove data from. */ 904 904 private JList target = null; 905 905 /** Constructor. 906 906 * @param target A <strong>JList</strong>. 907 907 */ 908 909 910 911 908 public RemoveListener(JList target) { 909 this.model = (DefaultListModel) target.getModel(); 910 this.target = target; 911 } 912 912 /** When the remove button is clicked, we attempt to remove the selected metadata from the target. 913 913 * @param event An <strong>ActionEvent</strong> containing information about the event. 914 914 */ 915 916 917 918 919 920 921 922 923 924 915 public void actionPerformed(ActionEvent event) { 916 if(!target.isSelectionEmpty()) { 917 int index = target.getSelectedIndex(); 918 model.remove(index); 919 } 920 } 921 } 922 /** Listener that sets the tooltip associated to a combobox to the tooltip relevant to the selected item. */ 923 private class ToolTipUpdater 924 implements ActionListener { 925 925 /** Any implementation of an ActionListener must include this method so that we can be informed when the selection in a combobox has changed and update the tooltip accordingly. 926 926 * @param event An <strong>ActionEvent</strong> containing information about the action that fired this call. 927 927 */ 928 929 930 931 932 933 934 935 928 public void actionPerformed(ActionEvent event) { 929 JComboBox source = (JComboBox)event.getSource(); 930 ListOption lo = (ListOption)source.getSelectedItem(); 931 String description = Utility.formatHTMLWidth(lo.getDesc(), 60); 932 source.setToolTipText(description); 933 } 934 } 935 } 936 936 } -
trunk/gli/src/org/greenstone/gatherer/cdm/ArgumentContainer.java
r4293 r4366 56 56 */ 57 57 public interface ArgumentContainer { 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 58 /** Method to retrieve the list of arguments from this container. Note that this method returns both the containers arguments plus its 'supers' arguments if any, and alphabetically orders them. 59 * @return The arguments within a <strong>ArrayList</strong>. 60 */ 61 public ArrayList getArguments(); 62 /** Method to retrieve this containers custom argument string. 63 * @return The custom arguments as a <strong>String</strong>. 64 */ 65 public String getCustom(); 66 /** Method to retrieve the name associated with this argument container. 67 * @return The name as a <strong>String</strong>. 68 */ 69 public String getName(); 70 /** Method to set the custom arguments string. 71 * @param custom The new custom argument <strong>String</strong>. 72 */ 73 public void setCustom(String custom); 74 74 } 75 75 -
trunk/gli/src/org/greenstone/gatherer/cdm/Classifier.java
r4293 r4366 66 66 // #################################################################################### 67 67 public class Classifier 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 68 extends ArrayList 69 implements ArgumentContainer, Comparable, Serializable { 70 /** A reference to the classifier that this one inherits from. */ 71 private Classifier super_classifier = null; 72 /** A reference to the manager of this classifier. Only available once the classifiers been assigned, always <i>null</i> for classifiers in reserve. */ 73 private ClassifierManager manager = null; 74 /** Custom arguments (ie those unparsable from classinfo.pl) provided to this class. */ 75 private String custom = null; 76 /** A description of this classifier. */ 77 private String desc = null; 78 /** The name of the classifier as it would appear in the collect.cfg file. */ 79 private String name = null; 80 /** A list of format commands that are dependant on this classifier. */ 81 private ArrayList dependant_formats = null; 82 /** Default Constructor. 83 */ 84 public Classifier() { 85 super(); 86 dependant_formats = new ArrayList(); 87 } 88 /** Constructor. 89 89 * @param name The name of this classifier as a <strong>String</strong>. 90 90 * @param desc A description of this classifier as a <strong>String</strong>. 91 91 * @param super_classifier The super class of this classifier, as a <strong>Classifier</strong>. 92 92 */ 93 94 95 96 97 98 99 93 public Classifier(String name, String desc, Classifier super_classifier) { 94 this(); 95 this.desc = desc; 96 this.name = name; 97 this.super_classifier = super_classifier; 98 } 99 /** Method to add an argument to this classifier. Only adds the argument if it isn't already present. 100 100 * @param argument The <strong>Argument</strong> to add. 101 101 */ 102 103 104 105 106 107 108 102 public void addArgument(Argument argument) { 103 if(!contains(argument)) { 104 add(argument); 105 argument.setOwner(name); 106 } 107 } 108 /** Method to register a dependant format with this classifier. If the classifier changes, this format also needs to be updated. 109 109 * @param format A <strong>Format</strong> which is dependant on this classifier. 110 110 */ 111 112 113 114 111 public void addDependantFormat(Format format) { 112 dependant_formats.add(format); 113 } 114 /** Method to compare two classifiers for ordering. 115 115 * @param object The classifier we are comparing to, as an <strong>Object</strong>. 116 116 * @return An <i>int</i> specifying the classifier order, using values as set out in String. 117 117 * @see java.lang.String#compareTo 118 118 */ 119 120 121 122 123 124 125 126 119 public int compareTo(Object object) { 120 if(object instanceof Classifier) { 121 Classifier classifier = (Classifier) object; 122 return name.compareTo(classifier.getName()); 123 } 124 return name.compareTo(object.toString()); 125 } 126 /** This method produces a deep copy of this classifier. Note that this also creates a new copy of each of the super classes of classifiers as well. This is the way it should be, as each assigned classifier may have different values for the higher classifiers (such as BasPlug). 127 127 * @return A newly created <strong>Classifier</strong> with the same details and Arguments as this one. 128 128 * @see org.greenstone.gatherer.cdm.Argument 129 129 */ 130 131 132 133 134 135 136 137 138 139 140 141 142 143 130 public Classifier copy() { 131 Classifier copy = null; 132 if(super_classifier == null) { 133 copy = new Classifier(name, desc, null); 134 } 135 else { 136 copy = new Classifier(name, desc, super_classifier.copy()); 137 } 138 for(int i = 0; i < size(); i++) { 139 copy.addArgument(((Argument)get(i)).copy()); 140 } 141 return copy; 142 } 143 /** Method to determine if two classifiers are equal. 144 144 * @param object The classifier to test against, as an <strong>Object</strong>. 145 145 * @return <i>true</i> if the classifier names match, <i>false</i> otherwise. 146 146 * @see org.greenstone.gatherer.cdm.CustomClassifier 147 147 */ 148 149 150 151 152 153 154 155 156 157 148 public boolean equals(Object object) { 149 if(object instanceof CustomClassifier) { 150 CustomClassifier classifier = (CustomClassifier) object; 151 return (toString().equalsIgnoreCase(classifier.getCommand())); 152 } 153 else { 154 return(toString().equalsIgnoreCase(object.toString())); 155 } 156 } 157 /** Method to retrieve an argument by its name. 158 158 * @param name The name of the argument as a <strong>String</strong>. 159 159 * @return The <strong>Argument</strong> requested, or <i>null</i> if no such argument. 160 160 */ 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 161 public Argument getArgument(String name) { 162 // The name given may still include the '-' 163 if(name.startsWith("-")) { 164 name = name.substring(1); 165 } 166 ArrayList arguments = getArguments(); 167 for(int i = 0; i < arguments.size(); i++) { 168 Argument argument = (Argument)arguments.get(i); 169 if(argument.getName().equals(name)) { 170 return argument; 171 } 172 } 173 return null; 174 } 175 /** Method to retrieve all of the arguments available to a classifier, including both specific and general ones. 176 176 * @return A <strong>Hashtable</strong> of arguments, with <name> -> <argument> entries. 177 177 */ 178 179 180 181 182 183 184 185 186 187 188 189 190 191 178 public ArrayList getArguments() { 179 ArrayList all_arguments = new ArrayList(this); 180 if(super_classifier != null) { 181 ArrayList super_arguments = super_classifier.getArguments(); 182 for(int i = 0; i < super_arguments.size(); i++) { 183 Object argument = super_arguments.get(i); 184 if(!all_arguments.contains(argument)) { 185 all_arguments.add(argument); 186 } 187 } 188 } 189 return all_arguments; 190 } 191 /** Method to retrieve a classifiers custom argument information. 192 192 * @return The custom arguments as a <strong>String</strong>. 193 193 */ 194 195 196 197 194 public String getCustom() { 195 return custom; 196 } 197 /** Method to retrieve a classifiers name. 198 198 * @return A <strong>String</strong> containing the classifiers name. 199 199 */ 200 201 202 203 200 public String getName() { 201 return name; 202 } 203 /** Method to retrieve the position of this classifier as a special keyword or the form "CL#" where # is the classifiers order in the set of classifiers. Note that if this is called for a Classifier that has never been assigned a position of 'Search' is returned, as this is the only case where we will try to write a Classifier that does not have a manager. 204 204 * @return A <strong>String</strong> containing the special position keyword. 205 205 * @see org.greenstone.gatherer.cdm.ClassifierManager 206 206 */ 207 208 209 210 211 212 213 207 public String getPositionString() { 208 if(manager == null) { 209 return "Search"; 210 } 211 return "CL" + (manager.indexOf(this) + 1); 212 } 213 /** Method to set the value of custom. 214 214 * @param custom The new value of custom as a <strong>String</strong>. 215 215 */ 216 217 218 219 216 public void setCustom(String custom) { 217 this.custom = custom; 218 } 219 /** Method to set the value of desc. 220 220 * @param desc The new value of desc as a <strong>String</strong>. 221 221 */ 222 223 224 225 222 public void setDesc(String desc) { 223 this.desc = desc; 224 } 225 /** Method to set the value of manager. 226 226 * @param manager The new manager as a <strong>ClassifierManager</strong>. 227 227 */ 228 229 230 231 228 public void setManager(ClassifierManager manager) { 229 this.manager = manager; 230 } 231 /** Method to set the value of name. 232 232 * @param name The new value of name as a <strong>String</strong>. 233 233 */ 234 235 236 237 234 public void setName(String name) { 235 this.name = name; 236 } 237 /** Method to set the value of the super_classifier. 238 238 * @param super_classifier The new value of super_classifier as a <strong>Classifier</strong>, or <i>null</i> if this class has no inheritance. 239 239 */ 240 241 242 243 240 public void setSuper(Classifier super_classifier) { 241 this.super_classifier = super_classifier; 242 } 243 /** Method to print out this classifier as it would appear as a command within the collection configuration file. 244 244 * @return A <strong>String</strong> containing a single classifier command. 245 245 */ 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 246 public String toString() { 247 StringBuffer text = new StringBuffer("classify "); 248 text.append(name); 249 text.append(" "); 250 ArrayList arguments = getArguments(); 251 for(int i = 0; i < arguments.size(); i++) { 252 Argument argument = (Argument)arguments.get(i); 253 if(argument.isAssigned()) { 254 text.append(argument.toString()); 255 text.append(" "); 256 } 257 } 258 if(custom != null) { 259 text.append(custom); 260 } 261 return text.toString(); 262 } 263 263 } 264 264 -
trunk/gli/src/org/greenstone/gatherer/cdm/ClassifierManager.java
r4293 r4366 112 112 // #################################################################################### 113 113 public class ClassifierManager 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 114 implements MSMListener { 115 /** An interface to the Gatherer, the creator of this cdm module, for access to the Greenstone installation directory. */ 116 private Gatherer gatherer = null; 117 /** A reference to the CollectionDesignManager for access to other configuration managers. */ 118 private CollectionDesignManager manager = null; 119 /** The controls for editing the contents of this manager. */ 120 private Control controls = null; 121 /** A list of assigned classifiers. */ 122 private DynamicListModel assigned = null; 123 /** A list of known, but currently unassigned, classifiers. */ 124 private DynamicListModel reserve = null; 125 /** We may have somehow recieved a classifier command that are, in fact, custom classifiers which can refer to classifiers that haven't been parsed yet, so this holds a list of failed commands which are retried after the loading is complete. */ 126 private ArrayList unresolved_commands = null; 127 /** Constructor. 128 * @param gatherer A reference to the <strong>Gatherer</strong> for access to the Dictionary. 129 * @param manager A reference to the <strong>CollectionDesignManager</strong> itself. 130 * @see org.greenstone.gatherer.Gatherer 131 * @see org.greenstone.gatherer.cdm.DynamicListModel 132 * @see org.greenstone.gatherer.collection.CollectionManager 133 * @see org.greenstone.gatherer.msm.MetadataSetManager 134 * @see org.greenstone.gatherer.msm.MSMListener 135 */ 136 public ClassifierManager(Gatherer gatherer, CollectionDesignManager manager) { 137 this.assigned = new DynamicListModel(); 138 this.gatherer = gatherer; 139 this.manager = manager; 140 this.unresolved_commands = new ArrayList(); 141 loadClassifiers(); 142 saveClassifiers(); 143 // Register as a MSMListener. 144 Gatherer.c_man.getCollection().msm.addMSMListener(this); 145 } 146 /** Method to add a new classifier to reserve. 147 147 * @param classifier The new <strong>Classifier</strong>. 148 148 * @see org.greenstone.gatherer.cdm.DynamicListModel 149 149 */ 150 151 152 153 154 155 150 public void addClassifier(Classifier classifier) { 151 if(!reserve.contains(classifier)) { 152 reserve.addElement(classifier); 153 } 154 } 155 /** Method to assign a classifier. 156 156 * @param classifier The reserve <strong>Classifier</strong> to assign. 157 157 * @see org.greenstone.gatherer.cdm.DynamicListModel 158 158 */ 159 160 161 162 163 164 165 166 159 public void assignClassifier(Classifier classifier) { 160 if(!assigned.contains(classifier)) { 161 assigned.addElement(classifier); 162 classifier.setManager(this); 163 gatherer.c_man.configurationChanged(); 164 } 165 } 166 /** Method to assign a classifier. 167 167 * @param classifier The <strong>CustomClassifier</strong> to assign. 168 168 * @see org.greenstone.gatherer.cdm.DynamicListModel 169 169 */ 170 171 172 173 174 175 176 177 170 public void assignClassifier(CustomClassifier classifier) { 171 if(!assigned.contains(classifier)) { 172 assigned.addElement(classifier); 173 classifier.setManager(this); 174 gatherer.c_man.configurationChanged(); 175 } 176 } 177 /** Destructor. 178 178 * @see org.greenstone.gatherer.Gatherer 179 179 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 180 180 * @see org.greenstone.gatherer.cdm.DynamicListModel 181 181 */ 182 183 184 185 186 187 188 189 190 191 192 193 194 195 182 public void destroy() { 183 // Deregister as a listener 184 if(gatherer.c_man != null && gatherer.c_man.msm != null) { 185 gatherer.c_man.msm.removeMSMListener(this); 186 } 187 // Null globals 188 assigned = null; 189 controls = null; 190 gatherer = null; 191 manager = null; 192 reserve = null; 193 unresolved_commands = null; 194 } 195 /** Method to retrieve the classifier with the given index. 196 196 * @param index The index of the desired classifier as an <i>int</i>. 197 197 * @return The requested Classifier as an <strong>Object</strong> or <i>null</i> if no such classifier exists. 198 198 * @see org.greenstone.gatherer.cdm.DynamicListModel 199 199 */ 200 201 202 203 204 205 206 200 public Object getClassifier(int index) { 201 if(0 <= index && index < assigned.size()) { 202 return assigned.get(index); 203 } 204 return null; 205 } 206 /** Method to retrieve the named classifier. 207 207 * @param name The name of the desired classifier as a <strong>String</strong>. 208 208 * @return The requested <strong>Classifier</strong> or <i>null</i> if no such classifier exists. 209 209 * @see org.greenstone.gatherer.cdm.DynamicListModel 210 210 */ 211 212 213 214 215 216 217 218 219 220 221 211 public Classifier getClassifier(String name) { 212 for(int i = 0; i < reserve.size(); i++) { 213 Classifier classifier = (Classifier)reserve.get(i); 214 if(classifier.getName().equals(name)) { 215 return classifier; 216 } 217 } 218 // No success. 219 return null; 220 } 221 /** Method to retrieve the control for this manager. 222 222 * @return A <strong>JPanel</strong> containing the controls. 223 223 */ 224 225 226 227 228 229 230 224 public JPanel getControls() { 225 if(controls == null) { 226 controls = new Control(); 227 } 228 return controls; 229 } 230 /** Called whenever a metadata element changes significantly. 231 231 * @param event A <strong>MSMEvent</strong> choc' full of event informationy goodness. 232 232 */ 233 234 235 236 233 public void elementChanged(MSMEvent event) { 234 // Don't really care, as the elements dealt with here are all live references so changes like identifier change will propagate immediately. 235 } 236 /** Method to find the index of the given classifier within the assigned classifiers. 237 237 * @param classifier The <strong>Classifier</strong> whose index you wish to find. 238 238 * @return The index of the classifier as an <i>int</i>, which has a value of -1 if the classifier was not found. 239 239 * @see org.greenstone.gatherer.cdm.DynamicListModel 240 240 */ 241 242 243 244 245 246 247 248 249 250 251 */ 252 253 254 255 256 257 258 241 public int indexOf(Classifier classifier) { 242 for(int i = 0; i < assigned.size(); i++) { 243 Classifier sibling = (Classifier) assigned.get(i); 244 if(sibling.equals(classifier)) { 245 return i; 246 } 247 } 248 return -1; 249 } 250 /** Method to invalidate controls after a significant change in the system state. 251 */ 252 public void invalidateControls() { 253 if(controls != null) { 254 controls.destroy(); 255 } 256 controls = null; 257 } 258 /** Method to load the details of a single plug-in. 259 259 * @param classifier The classifier <strong>File</strong> you wish to load. 260 260 */ 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 261 public void loadClassifier(File classifier) { 262 ///ystem.err.println("Attempting to parse " + classifier.toString()); 263 Document document = null; 264 long start; 265 long end; 266 // Run classinfo on this classifier, and then send the results for parsing. 267 try { 268 String args[] = null; 269 if(Utility.isWindows()) { 270 args = new String[4]; 271 if(Gatherer.config.perl_path != null) { 272 args[0] = gatherer.config.perl_path + "Perl.exe"; 273 } 274 else { 275 args[0] = "Perl.exe"; 276 } 277 args[1] = gatherer.config.gsdl_path + "bin" + File.separator + "script" + File.separator + "classinfo.pl"; 278 args[2] = "-xml"; 279 args[3] = getClassifierName(classifier); 280 } 281 else { 282 args = new String[3]; 283 args[0] = "classinfo.pl"; 284 args[1] = "-xml"; 285 args[2] = getClassifierName(classifier); 286 } 287 287 288 288 // Create the process. 289 290 291 292 293 294 295 296 297 298 289 Runtime runtime = Runtime.getRuntime(); 290 Process process = runtime.exec(args); 291 InputStream input_stream = process.getErrorStream(); 292 BufferedReader error_in = new BufferedReader(new InputStreamReader(process.getErrorStream())); 293 String line = ""; 294 StringBuffer xml = new StringBuffer(""); 295 while((line = error_in.readLine()) != null) { 296 xml.append(line); 297 xml.append("\n"); 298 } 299 299 // Then read the xml from the piped input stream. 300 301 302 303 304 305 306 300 InputSource source = new InputSource(new StringReader(xml.toString())); 301 DOMParser parser = new DOMParser(); 302 parser.parse(source); 303 document = parser.getDocument(); 304 } 305 catch (Exception error) { 306 error.printStackTrace(); 307 307 //ystem.err.println("Error: Cannot parse " + getClassifierName(classifier)); 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 308 } 309 if(document != null) { 310 parse(document.getDocumentElement()); 311 } 312 } 313 /** Called whenever the metadata value associated to a certain record changes. */ 314 public void metadataChanged(MSMEvent event) { 315 FileNode record = event.getRecord(); 316 if(record != null) { 317 for(int i = 0; i < assigned.size(); i++) { 318 Object object = assigned.get(i); 319 if(object instanceof CustomClassifier) { 320 CustomClassifier classifier = (CustomClassifier) object; 321 classifier.process(record); 322 } 323 } 324 } 325 } 326 /** This method attempts to parse a classifier command from a command string taken from the collection configuration file. This process is quite complex as not only must the correct classifier be matched but also all of the parameters given must be legal. If such a command is found, the classifier is immediately assigned. 327 327 * @param command The command <strong>String</strong> that may include classifier information. 328 328 * @return <i>true</i> if a classifier command was parsed, <i>false</i> otherwise. … … 331 331 * @see org.greenstone.gatherer.cdm.CommandTokenizer 332 332 */ 333 public boolean parse(String command) { 334 String command_lc = command.toLowerCase(); 335 if(command_lc.startsWith("classify")) { 336 CommandTokenizer tokenizer = new CommandTokenizer(command); 337 if(tokenizer.countTokens() >= 2) { 338 tokenizer.nextToken(); // Throw away 'classifier' 339 String name = tokenizer.nextToken(); 340 // Try to locate the classifier with this name. 341 Classifier classifier = getClassifier(name); 342 // And if successful start to parse the arguments. 343 if(classifier != null) { 344 // Take a copy. 345 classifier = classifier.copy(); 346 String key = null; 347 while((key = tokenizer.nextToken()) != null) { 348 // Try to retrieve a matching argument. 349 Argument argument = classifier.getArgument(key); 350 if(argument != null) { 351 // Set as assigned. 352 argument.setAssigned(true); 353 // And if the argument is of a parameter type, parse a parameter. 354 if(argument.getType() != Argument.FLAG && tokenizer.hasMoreTokens()) { 355 String value = tokenizer.nextToken(); 356 value = value.replace(':', MSMUtils.NS_SEP); 357 argument.setValue(value); 358 } 359 } 360 // Argument cannot be matched. 361 else { 362 String cur_key = key; 363 String value = tokenizer.nextToken(); 364 if(value.startsWith("-")) { 365 key = value; 366 value = null; 367 } 368 else { 369 key = null; 370 } 371 String custom = classifier.getCustom(); 372 if(custom == null) { 373 if(value == null) { 374 classifier.setCustom(cur_key); 375 } 376 else { 377 classifier.setCustom(cur_key + " " + value); 378 } 379 } 380 else { 381 if(value == null) { 382 classifier.setCustom(custom + " " + cur_key); 383 } 384 else { 385 classifier.setCustom(custom + " " + cur_key + " " + value); 386 } 387 } 388 } 389 } 390 assignClassifier(classifier); 391 return true; 392 } 393 else { 394 ///ystem.err.println("Unknown classifier"); 395 } 333 public boolean parse(String command) { 334 String command_lc = command.toLowerCase(); 335 if(command_lc.startsWith("classify")) { 336 CommandTokenizer tokenizer = new CommandTokenizer(command); 337 if(tokenizer.countTokens() >= 2) { 338 tokenizer.nextToken(); // Throw away 'classifier' 339 String name = tokenizer.nextToken(); 340 // Try to locate the classifier with this name. 341 Classifier classifier = getClassifier(name); 342 // And if successful start to parse the arguments. 343 if(classifier != null) { 344 // Take a copy. 345 classifier = classifier.copy(); 346 String key = null; 347 while((key = tokenizer.nextToken()) != null) { 348 // Try to retrieve a matching argument. 349 Argument argument = classifier.getArgument(key); 350 if(argument != null) { 351 // Set as assigned. 352 argument.setAssigned(true); 353 // And if the argument is of a parameter type, parse a parameter. 354 if(argument.getType() != Argument.FLAG && tokenizer.hasMoreTokens()) { 355 String value = tokenizer.nextToken(); 356 value = value.replace(':', MSMUtils.NS_SEP); 357 argument.setValue(value); 358 } 359 } 360 // Argument cannot be matched. 361 else { 362 String cur_key = key; 363 String value = tokenizer.nextToken(); 364 if(value.startsWith("-")) { 365 key = value; 366 value = null; 367 } 368 else { 369 key = null; 370 } 371 String custom = classifier.getCustom(); 372 if(custom == null) { 373 if(value == null) { 374 classifier.setCustom(cur_key); 396 375 } 397 } 398 else if(command_lc.startsWith("customclassifier")) { 399 unresolved_commands.add(command); 400 return true; 401 } 402 return false; 403 } 404 /** This method removes an assigned classifier. I was tempted to call it unassign, but remove is more consistant. Note that there is no way to remove a classifier from the reserve. 376 else { 377 classifier.setCustom(cur_key + " " + value); 378 } 379 } 380 else { 381 if(value == null) { 382 classifier.setCustom(custom + " " + cur_key); 383 } 384 else { 385 classifier.setCustom(custom + " " + cur_key + " " + value); 386 } 387 } 388 } 389 } 390 assignClassifier(classifier); 391 return true; 392 } 393 else { 394 ///ystem.err.println("Unknown classifier"); 395 } 396 } 397 } 398 else if(command_lc.startsWith("customclassifier")) { 399 unresolved_commands.add(command); 400 return true; 401 } 402 return false; 403 } 404 /** This method removes an assigned classifier. I was tempted to call it unassign, but remove is more consistant. Note that there is no way to remove a classifier from the reserve. 405 405 * @param classifier The Classifier or CustomClassifier, as an <strong>Object</strong>, to remove. 406 406 * @see org.greenstone.gatherer.cdm.DynamicListModel 407 407 */ 408 409 410 411 412 408 public void removeClassifier(Object classifier) { 409 assigned.removeElement(classifier); 410 gatherer.c_man.configurationChanged(); 411 } 412 /** Method which attempts to reparse obvious classifier commands which previously referenced unresovable Classifiers. 413 413 * @see org.greenstone.gatherer.cdm.Classifier 414 414 * @see org.greenstone.gatherer.cdm.CommandTokenizer 415 415 * @see org.greenstone.gatherer.cdm.CustomClassifier 416 416 */ 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 417 public void reparseUnresolved() { 418 for(int i = 0; i < unresolved_commands.size(); i++) { 419 String command = (String) unresolved_commands.get(i); 420 CommandTokenizer tokenizer = new CommandTokenizer(command); 421 if(tokenizer.countTokens() >= 6) { 422 tokenizer.nextToken();// Loose customclassifier 423 // Get class name. 424 String class_name = tokenizer.nextToken(); 425 // Parse arguments. 426 String replaces = null; 427 String separations = null; 428 while(tokenizer.hasMoreTokens()) { 429 String arg_name = tokenizer.nextToken(); 430 if(arg_name.equalsIgnoreCase("-replaces")) { 431 replaces = tokenizer.nextToken(); 432 } 433 else { 434 separations = tokenizer.nextToken(); 435 } 436 } 437 try { 438 replaces = replaces.substring(2); 439 int index = Integer.parseInt(replaces); 440 Classifier original = (Classifier)getClassifier(index); 441 if(original != null) { 442 Class custom_classifier_class = Class.forName("org.greenstone.gatherer.cdm.custom." + class_name); 443 CustomClassifier custom_classifier = (CustomClassifier) custom_classifier_class.newInstance(); 444 custom_classifier.setGatherer(gatherer); 445 custom_classifier.recreate(original, separations); 446 assigned.add(indexOf(original), custom_classifier); 447 assigned.removeElement(original); 448 } 449 else { 450 ///ystem.err.println("Missing original."); 451 } 452 } 453 catch (Exception error) { 454 error.printStackTrace(); 455 } 456 } 457 } 458 // Regardless of if they work, clear the commands. 459 unresolved_commands.clear(); 460 } 461 /** Method to cache the current contents of reserve (known classifiers) to file. 462 462 * @see org.greenstone.gatherer.util.Utility 463 463 */ 464 465 466 467 468 469 470 471 472 473 474 464 public void saveClassifiers() { 465 try { 466 FileOutputStream file = new FileOutputStream(Utility.BASE_DIR + "classifiers.dat"); 467 ObjectOutputStream out = new ObjectOutputStream(file); 468 out.writeObject(reserve); 469 out.close(); 470 } 471 catch (Exception error) { 472 } 473 } 474 /** Called when a metadata set changed significantly. 475 475 * @param event A <strong>MSMEvent</strong> containing information about the set change. 476 476 */ 477 478 479 480 477 public void setChanged(MSMEvent event) { 478 // Again, we would only worry about this if we contained 'inanimate' references to elements or something, but our references are live, and controls are rebuilt everytime a pop-up is needed. 479 } 480 /** Method used to determine the number of classifiers that have been assigned. 481 481 * @return An <i>int</i> which is the number of classifiers. 482 482 */ 483 484 485 486 483 public int size() { 484 return assigned.size(); 485 } 486 /** Method to print out a block of classifier commands, much like you'd find in a collection configuration file. 487 487 * @return A <strong>String</strong> containing a series of classifier commands separated by new lines. 488 488 * @see org.greenstone.gatherer.cdm.Classifier 489 489 * @see org.greenstone.gatherer.cdm.CustomClassifier 490 490 */ 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 491 public String toString() { 492 StringBuffer text = new StringBuffer(); 493 for(int i = 0; i < assigned.size(); i++) { 494 Object object = assigned.get(i); 495 if(object instanceof Classifier) { 496 Classifier classifier = (Classifier) object; 497 text.append(classifier.toString()); 498 } 499 else if(object instanceof CustomClassifier) { 500 CustomClassifier classifier = (CustomClassifier) object; 501 text.append(classifier.getCommand()); 502 text.append("\n"); 503 text.append(classifier.getCustomCommand(i)); 504 } 505 text.append("\n"); 506 } 507 text.append("\n"); 508 return text.toString(); 509 } 510 /** Called when a significant change has occured to a value tree for a certain element, however we take no further action. 511 511 * @param event A <strong>MSMEvent</strong> containing information relevant to the event. 512 512 */ 513 514 515 513 public void valueChanged(MSMEvent event) { 514 } 515 /** Retrieve a phrase from the dictionary based on a certain key. 516 516 * @param key The search <strong>String</strong>. 517 517 * @return The matching phrase from the Dictionary. 518 518 */ 519 520 521 522 519 private String get(String key) { 520 return get(key, null); 521 } 522 /** Retrieve a phrase from the dictionary based on a certain key and certain arguments. 523 523 * @param key The search <strong>String</strong>. 524 524 * @param args A <strong>String[]</strong> used to complete and format the returned phrase. … … 527 527 * @see org.greenstone.gatherer.Gatherer 528 528 */ 529 530 531 532 533 534 535 529 private String get(String key, String args[]) { 530 if(key.indexOf(".") == -1) { 531 key = "CDM.ClassifierManager." + key; 532 } 533 return gatherer.dictionary.get(key, args); 534 } 535 /** Method to extract just the classifiers name from a file object. 536 536 * @param classifier The <strong>File</strong> which references a certain classifier. 537 537 * @return A <strong>String</strong> containing just the classifiers name, without extension. 538 538 */ 539 540 541 542 543 544 545 546 539 private String getClassifierName(File classifier) { 540 String name = classifier.getName(); 541 if(name.indexOf(".") != -1) { 542 name = name.substring(0, name.indexOf(".")); 543 } 544 return name; 545 } 546 /** Method to initially load information from the standard plug-ins within the gsdl Perl library. 547 547 * @see org.greenstone.gatherer.cdm.DynamicListModel 548 548 * @see org.greenstone.gatherer.util.Utility 549 549 */ 550 551 552 553 554 555 556 557 558 559 560 550 private void loadClassifiers() { 551 // Attempt to restore the cached file. 552 try { 553 FileInputStream file = new FileInputStream(Utility.BASE_DIR + "classifiers.dat"); 554 ObjectInputStream input = new ObjectInputStream(file); 555 reserve = (DynamicListModel) input.readObject(); 556 } 557 catch (Exception error) { 558 } 559 if(reserve == null) { 560 reserve = new DynamicListModel(); 561 561 // Retrieve the gsdl home directory... 562 563 564 565 566 567 562 String directory = gatherer.config.gsdl_path; 563 directory = directory + "perllib" + File.separator + "classify" + File.separator; 564 loadClassifiers(new File(directory)); 565 } 566 } 567 /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location. 568 568 * @param directory A <strong>File</strong> indicating the directory to be scanned for plug-ins. 569 569 * @see org.greenstone.gatherer.cdm.ParsingProgress 570 570 */ 571 572 573 571 private void loadClassifiers(File directory) { 572 File files[] = directory.listFiles(); 573 if(files != null) { 574 574 // Create a progress indicator. 575 576 577 578 579 580 581 582 583 584 585 586 587 588 575 ParsingProgress progress = new ParsingProgress(get("CDM.ClassifierManager.Parsing.Title"), get("CDM.ClassifierManager.Parsing.Message"), files.length); 576 for(int i = 0; i < files.length; i++) { 577 // We only want to check Perl Modules. 578 if(files[i].getName().endsWith(".pm")) { 579 loadClassifier(files[i]); 580 } 581 progress.inc(); 582 } 583 progress.dispose(); 584 progress.destroy(); 585 progress = null; 586 } 587 } 588 /** Parses a DOM tree model turning it into a Classifier and its associated arguments. 589 589 * @param root The <strong>Node</strong> at the root of the DOM model. 590 590 * @return A newly created <strong>Classifier</strong> based on the information parsed from the DOM model. 591 591 * @see org.greenstone.gatherer.cdm.Argument 592 592 */ 593 private Classifier parse(Node root) { 594 Classifier classifier = new Classifier(); 595 String node_name = null; 596 for(Node node = root.getFirstChild(); node != null; 597 node = node.getNextSibling()) { 598 node_name = node.getNodeName(); 599 if(node_name.equals("Name")) { 600 String name = MSMUtils.getValue(node); 601 // We can save ourselves some processing time if a classifier with this name already exists in our manager. If so retrieve it and return it. 602 Classifier existing = getClassifier(name); 603 if(existing != null) { 604 return existing; 605 } 606 classifier.setName(name); 593 private Classifier parse(Node root) { 594 Classifier classifier = new Classifier(); 595 String node_name = null; 596 for(Node node = root.getFirstChild(); node != null; 597 node = node.getNextSibling()) { 598 node_name = node.getNodeName(); 599 if(node_name.equals("Name")) { 600 String name = MSMUtils.getValue(node); 601 // We can save ourselves some processing time if a classifier with this name already exists in our manager. If so retrieve it and return it. 602 Classifier existing = getClassifier(name); 603 if(existing != null) { 604 return existing; 605 } 606 classifier.setName(name); 607 } 608 else if(node_name.equals("Desc")) { 609 classifier.setDesc(MSMUtils.getValue(node)); 610 } 611 // Parse the multitude of arguments. 612 else if(node_name.equals("Arguments")) { 613 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) { 614 node_name = arg.getNodeName(); 615 // An option. 616 if(node_name.equals("Option")) { 617 Argument argument = new Argument(); 618 // If its an option we parse the multitude of details an options might have. 619 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) { 620 node_name = det.getNodeName(); 621 if(node_name.equals("Name")) { 622 argument.setName(MSMUtils.getValue(det)); 623 } 624 else if(node_name.equals("Desc")) { 625 argument.setDesc(MSMUtils.getValue(det)); 626 } 627 else if(node_name.equals("Type")) { 628 argument.setType(MSMUtils.getValue(det)); 629 } 630 else if(node_name.equals("Default")) { 631 argument.setDefault(MSMUtils.getValue(det)); 632 } 633 else if(node_name.equals("List")) { 634 // Two final loops are required to parse lists. 635 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) { 636 if(value.getNodeName().equals("Value")) { 637 String key = null; 638 String desc = ""; 639 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { 640 node_name = subvalue.getNodeName(); 641 if(node_name.equals("Name")) { 642 key = MSMUtils.getValue(subvalue); 643 } 644 else if(node_name.equals("Desc")) { 645 desc = MSMUtils.getValue(subvalue); 646 } 647 } 648 if(key != null) { 649 argument.addOption(key, desc); 650 } 651 } 607 652 } 608 else if(node_name.equals("Desc")) { 609 classifier.setDesc(MSMUtils.getValue(node)); 653 } 654 else if(node_name.equals("Required")) { 655 String v = MSMUtils.getValue(det); 656 ///ystem.err.println("Required = " + v); 657 if(v.equalsIgnoreCase("yes")) { 658 ///ystem.err.println("Setting required to true."); 659 argument.setRequired(true); 610 660 } 611 // Parse the multitude of arguments. 612 else if(node_name.equals("Arguments")) { 613 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) { 614 node_name = arg.getNodeName(); 615 // An option. 616 if(node_name.equals("Option")) { 617 Argument argument = new Argument(); 618 // If its an option we parse the multitude of details an options might have. 619 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) { 620 node_name = det.getNodeName(); 621 if(node_name.equals("Name")) { 622 argument.setName(MSMUtils.getValue(det)); 623 } 624 else if(node_name.equals("Desc")) { 625 argument.setDesc(MSMUtils.getValue(det)); 626 } 627 else if(node_name.equals("Type")) { 628 argument.setType(MSMUtils.getValue(det)); 629 } 630 else if(node_name.equals("Default")) { 631 argument.setDefault(MSMUtils.getValue(det)); 632 } 633 else if(node_name.equals("List")) { 634 // Two final loops are required to parse lists. 635 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) { 636 if(value.getNodeName().equals("Value")) { 637 String key = null; 638 String desc = ""; 639 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { 640 node_name = subvalue.getNodeName(); 641 if(node_name.equals("Name")) { 642 key = MSMUtils.getValue(subvalue); 643 } 644 else if(node_name.equals("Desc")) { 645 desc = MSMUtils.getValue(subvalue); 646 } 647 } 648 if(key != null) { 649 argument.addOption(key, desc); 650 } 651 } 652 } 653 } 654 else if(node_name.equals("Required")) { 655 String v = MSMUtils.getValue(det); 656 ///ystem.err.println("Required = " + v); 657 if(v.equalsIgnoreCase("yes")) { 658 ///ystem.err.println("Setting required to true."); 659 argument.setRequired(true); 660 } 661 } 662 } 663 classifier.addArgument(argument); 664 } 665 // A super classifier class. 666 else if(node_name.equals("ClasInfo")) { 667 Classifier super_classifier = parse(arg); 668 classifier.setSuper(super_classifier); 669 } 670 } 671 } 672 } 673 if(classifier.getName() != null) { 674 addClassifier(classifier); 675 return classifier; 676 } 677 return null; 678 } 679 /** A class which provides controls for assigned and editing classifiers. */ 680 private class Control 681 extends JPanel { 682 /** Button for adding classifiers. */ 683 private JButton add = null; 684 /** Button for configuring the selected classifier. */ 685 private JButton configure = null; 686 /** Button to remove the selected classifier. */ 687 private JButton remove = null; 688 /** A combobox containing all of the known classifiers, including those that may have already been assigned. */ 689 private JComboBox classifier = null; 690 /** A list of assigned classifiers. */ 691 private JList classifier_list = null; 692 /** The text area containing instructions on the use of this control. */ 693 private JTextArea instructions = null; 694 /** Constructor. 695 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.AddListener 696 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.ConfigureListener 697 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.RemoveListener 661 } 662 } 663 classifier.addArgument(argument); 664 } 665 // A super classifier class. 666 else if(node_name.equals("ClasInfo")) { 667 Classifier super_classifier = parse(arg); 668 classifier.setSuper(super_classifier); 669 } 670 } 671 } 672 } 673 if(classifier.getName() != null) { 674 addClassifier(classifier); 675 return classifier; 676 } 677 return null; 678 } 679 /** A class which provides controls for assigned and editing classifiers. */ 680 private class Control 681 extends JPanel { 682 /** Button for adding classifiers. */ 683 private JButton add = null; 684 /** Button for configuring the selected classifier. */ 685 private JButton configure = null; 686 /** Button to remove the selected classifier. */ 687 private JButton remove = null; 688 /** A combobox containing all of the known classifiers, including those that may have already been assigned. */ 689 private JComboBox classifier = null; 690 /** A list of assigned classifiers. */ 691 private JList classifier_list = null; 692 /** The text area containing instructions on the use of this control. */ 693 private JTextArea instructions = null; 694 /** Constructor. 695 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.AddListener 696 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.ConfigureListener 697 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.RemoveListener 698 */ 699 public Control() { 700 Object classifiers[] = reserve.toArray(); 701 ArrayList classifier_model = new ArrayList(); 702 for(int i = 0; i < classifiers.length; i++) { 703 classifier_model.add(((Classifier)classifiers[i]).getName()); 704 } 705 // Now we add custom classifiers. 706 addCustomClassifiers(classifier_model); 707 Collections.sort(classifier_model); 708 // Create 709 add = new JButton(get("Add")); 710 JPanel button_pane = new JPanel(); 711 JPanel central_pane = new JPanel(); 712 configure = new JButton(get("Configure")); 713 JPanel header_pane = new JPanel(); 714 instructions = new JTextArea(get("Instructions")); 715 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 716 instructions.setEditable(false); 717 instructions.setLineWrap(true); 718 instructions.setRows(5); 719 instructions.setWrapStyleWord(true); 720 classifier = new JComboBox(classifier_model.toArray()); 721 classifier.setEditable(true); 722 JLabel classifier_label = new JLabel(get("Classifier")); 723 classifier_list = new JList(assigned); 724 JLabel classifier_list_label = new JLabel(get("Assigned")); 725 classifier_list_label.setHorizontalAlignment(JLabel.CENTER); 726 classifier_list_label.setOpaque(true); 727 JPanel classifier_list_pane = new JPanel(); 728 JPanel classifier_pane = new JPanel(); 729 remove = new JButton(get("Remove")); 730 JLabel title = new JLabel(get("Title")); 731 title.setHorizontalAlignment(JLabel.CENTER); 732 title.setOpaque(true); 733 JPanel temp = new JPanel(new BorderLayout()); 734 // Listeners 735 add.addActionListener(new AddListener()); 736 configure.addActionListener(new ConfigureListener()); 737 remove.addActionListener(new RemoveListener()); 738 classifier_list.addMouseListener(new ClickListener()); 739 // Layout 740 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0)); 741 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 742 header_pane.setLayout(new BorderLayout()); 743 header_pane.add(title, BorderLayout.NORTH); 744 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 745 classifier_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2)); 746 classifier_list_pane.setLayout(new BorderLayout()); 747 classifier_list_pane.add(classifier_list_label, BorderLayout.NORTH); 748 classifier_list_pane.add(new JScrollPane(classifier_list), BorderLayout.CENTER); 749 classifier_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 750 classifier_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 751 classifier_pane.setLayout(new GridLayout(1,2)); 752 classifier_pane.add(classifier_label); 753 classifier_pane.add(classifier); 754 button_pane.setLayout(new GridLayout(3,1)); 755 button_pane.add(add); 756 button_pane.add(configure); 757 button_pane.add(remove); 758 // Scope these mad bordering skillz. 759 temp.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Controls")), BorderFactory.createEmptyBorder(2,2,2,2)))); 760 temp.add(classifier_pane, BorderLayout.NORTH); 761 temp.add(button_pane, BorderLayout.SOUTH); 762 central_pane.setLayout(new BorderLayout()); 763 central_pane.add(classifier_list_pane, BorderLayout.CENTER); 764 central_pane.add(temp, BorderLayout.SOUTH); 765 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 766 setLayout(new BorderLayout()); 767 add(header_pane, BorderLayout.NORTH); 768 add(central_pane, BorderLayout.CENTER); 769 } 770 /** Method which acts like a destructor, tidying up references to persistant objects. 698 771 */ 699 public Control() { 700 Object classifiers[] = reserve.toArray(); 701 ArrayList classifier_model = new ArrayList(); 702 for(int i = 0; i < classifiers.length; i++) { 703 classifier_model.add(((Classifier)classifiers[i]).getName()); 704 } 705 // Now we add custom classifiers. 706 addCustomClassifiers(classifier_model); 707 Collections.sort(classifier_model); 708 // Create 709 add = new JButton(get("Add")); 710 JPanel button_pane = new JPanel(); 711 JPanel central_pane = new JPanel(); 712 configure = new JButton(get("Configure")); 713 JPanel header_pane = new JPanel(); 714 instructions = new JTextArea(get("Instructions")); 715 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 716 instructions.setEditable(false); 717 instructions.setLineWrap(true); 718 instructions.setRows(5); 719 instructions.setWrapStyleWord(true); 720 classifier = new JComboBox(classifier_model.toArray()); 721 classifier.setEditable(true); 722 JLabel classifier_label = new JLabel(get("Classifier")); 723 classifier_list = new JList(assigned); 724 JLabel classifier_list_label = new JLabel(get("Assigned")); 725 classifier_list_label.setHorizontalAlignment(JLabel.CENTER); 726 classifier_list_label.setOpaque(true); 727 JPanel classifier_list_pane = new JPanel(); 728 JPanel classifier_pane = new JPanel(); 729 remove = new JButton(get("Remove")); 730 JLabel title = new JLabel(get("Title")); 731 title.setHorizontalAlignment(JLabel.CENTER); 732 title.setOpaque(true); 733 JPanel temp = new JPanel(new BorderLayout()); 734 // Listeners 735 add.addActionListener(new AddListener()); 736 configure.addActionListener(new ConfigureListener()); 737 remove.addActionListener(new RemoveListener()); 738 classifier_list.addMouseListener(new ClickListener()); 739 // Layout 740 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0)); 741 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 742 header_pane.setLayout(new BorderLayout()); 743 header_pane.add(title, BorderLayout.NORTH); 744 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 745 classifier_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2)); 746 classifier_list_pane.setLayout(new BorderLayout()); 747 classifier_list_pane.add(classifier_list_label, BorderLayout.NORTH); 748 classifier_list_pane.add(new JScrollPane(classifier_list), BorderLayout.CENTER); 749 classifier_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 750 classifier_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 751 classifier_pane.setLayout(new GridLayout(1,2)); 752 classifier_pane.add(classifier_label); 753 classifier_pane.add(classifier); 754 button_pane.setLayout(new GridLayout(3,1)); 755 button_pane.add(add); 756 button_pane.add(configure); 757 button_pane.add(remove); 758 // Scope these mad bordering skillz. 759 temp.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Controls")), BorderFactory.createEmptyBorder(2,2,2,2)))); 760 temp.add(classifier_pane, BorderLayout.NORTH); 761 temp.add(button_pane, BorderLayout.SOUTH); 762 central_pane.setLayout(new BorderLayout()); 763 central_pane.add(classifier_list_pane, BorderLayout.CENTER); 764 central_pane.add(temp, BorderLayout.SOUTH); 765 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 766 setLayout(new BorderLayout()); 767 add(header_pane, BorderLayout.NORTH); 768 add(central_pane, BorderLayout.CENTER); 769 } 770 /** Method which acts like a destructor, tidying up references to persistant objects. 772 public void destroy() { 773 add = null; 774 classifier = null; 775 classifier_list = null; 776 configure = null; 777 instructions = null; 778 remove = null; 779 } 780 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called. 771 781 */ 772 public void destroy() { 773 add = null; 774 classifier = null; 775 classifier_list = null; 776 configure = null; 777 instructions = null; 778 remove = null; 779 } 780 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called. 781 */ 782 public void updateUI() { 783 if(instructions != null) { 784 instructions.setCaretPosition(0); 785 } 786 super.updateUI(); 787 } 788 /** Searches and adds a list of dynamically located CustomClassifiers. Note that the classes must be located under org.greenstone.gatherer.cdm.custom and have accompaning properties files which are used as dictionaries. 782 public void updateUI() { 783 if(instructions != null) { 784 instructions.setCaretPosition(0); 785 } 786 super.updateUI(); 787 } 788 /** Searches and adds a list of dynamically located CustomClassifiers. Note that the classes must be located under org.greenstone.gatherer.cdm.custom and have accompaning properties files which are used as dictionaries. 789 789 * @param classifier_model An <strong>ArrayList</strong> which will be used as the model for the combobox listing all known Classifiers. 790 790 */ 791 791 private void addCustomClassifiers(ArrayList classifier_model) { 792 792 //classifier_model.add("CustomAZList"); 793 793 // Search for classifiers under the org.greenstone.gatherer.cdm.custom directory. 794 795 796 797 798 799 800 801 802 803 804 805 806 807 794 File custom_directory = new File(Utility.BASE_DIR + "classes" + File.separator + "org" + File.separator + "greenstone" + File.separator + "gatherer" + File.separator + "cdm" + File.separator + "custom"); 795 if(custom_directory.exists()) { 796 File children[] = custom_directory.listFiles(); 797 for(int i = 0; i < children.length; i++) { 798 String temp = children[i].getName().toLowerCase(); 799 // There are a whole bunch of conditions about what files are custom classifier main classes. 800 if(temp.endsWith(".class") && temp.indexOf("$") == -1) { 801 // Determine the name of this custom classifier. 802 String name = children[i].getName(); 803 name = name.substring(0, name.indexOf(".")); 804 classifier_model.add(name); 805 } 806 } 807 } 808 808 // Search for any other CustomClassifiers within the jar file (if present) 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 809 File jar_file = new File("Gatherer.jar"); 810 if(jar_file.exists()) { 811 try { 812 JarFile jar = new JarFile(jar_file); 813 for(Enumeration entries = jar.entries(); entries.hasMoreElements(); ) { 814 String name = entries.nextElement().toString(); 815 if(name.startsWith("org/greenstone/gatherer/cdm/custom/") && name.endsWith(".class") && name.indexOf("$") == -1) { 816 name = name.substring(35, name.length() - 6); 817 if(!classifier_model.contains(name)) { 818 classifier_model.add(name); 819 } 820 } 821 name = null; 822 } 823 jar = null; 824 } 825 catch (Exception error) { 826 error.printStackTrace(); 827 } 828 } 829 jar_file = null; 830 } 831 /** This class listens for actions upon the add button in the controls, and if detected calls the assignClassifier() method. 832 832 */ 833 834 833 private class AddListener 834 implements ActionListener { 835 835 /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured on one of our target controls, so that we can add the selected Classifier. 836 836 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. … … 840 840 * @see org.greenstone.gatherer.cdm.CustomClassifier 841 841 */ 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 842 public void actionPerformed(ActionEvent event) { 843 String name = (String)classifier.getSelectedItem(); 844 Classifier target = getClassifier(name); 845 Classifier classifier = null; 846 CustomClassifier custom_classifier = null; 847 if(target != null) { 848 classifier = target.copy(); 849 } 850 else { 851 // Try to retrieve custom classifier for name. 852 try { 853 Class custom_class = Class.forName("org.greenstone.gatherer.cdm.custom." + name); 854 custom_classifier = (CustomClassifier)custom_class.newInstance(); 855 custom_classifier.setGatherer(gatherer); 856 } 857 catch (Exception error) { 858 gatherer.debug(error, "Error in ClassifierManager.AddListener.actionPerformed(): " + error); 859 } 860 // And if all else fails create a new classifier. 861 if(classifier == null && custom_classifier == null) { 862 classifier = new Classifier(name, "", null); 863 } 864 } 865 if(classifier != null) { 866 // Automatically chain to configuration. This ensures required arguments are filled out. 867 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, classifier); 868 if(ac.display()) { 869 assignClassifier(classifier); 870 } 871 ac.destroy(); 872 ac = null; 873 } 874 // Custom classifier 875 else { 876 if(custom_classifier.display(true)) { 877 assignClassifier(custom_classifier); 878 } 879 custom_classifier.destroy(); // Remove gui prompt or else. 880 custom_classifier = null; 881 } 882 } 883 } 884 /** Listens for double clicks apon the list and react as if the configure button was pushed. */ 885 private class ClickListener 886 extends MouseAdapter { 887 887 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt. 888 888 * @param event A <strong>MouseEvent</strong> containing information about the mouse click. 889 889 */ 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 890 public void mouseClicked(MouseEvent event) { 891 if(event.getClickCount() == 2 ) { 892 if(!classifier_list.isSelectionEmpty()) { 893 Object object = classifier_list.getSelectedValue(); 894 if(object instanceof Classifier) { 895 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (Classifier)object); 896 if(ac.display()) { 897 assigned.refresh(); 898 } 899 ac.destroy(); 900 ac = null; 901 } 902 else if(object instanceof CustomClassifier) { 903 CustomClassifier cc = (CustomClassifier)object; 904 if(cc.display(true)) { 905 assigned.refresh(); 906 } 907 cc.destroy(); // Remove gui prompt or else. 908 cc = null; 909 } 910 } 911 } 912 } 913 } 914 /** This class listens for actions upon the configure button in the controls, and if detected creates a new ArgumentConfiguration dialog box to allow for configuration. 915 915 */ 916 917 916 private class ConfigureListener 917 implements ActionListener { 918 918 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls. 919 919 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. … … 922 922 * @see org.greenstone.gatherer.cdm.CustomClassifier 923 923 */ 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 924 public void actionPerformed(ActionEvent event) { 925 if(!classifier_list.isSelectionEmpty()) { 926 Object object = classifier_list.getSelectedValue(); 927 if(object instanceof Classifier) { 928 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (Classifier)object); 929 if(ac.display()) { 930 assigned.refresh(); 931 } 932 ac.destroy(); 933 ac = null; 934 } 935 else if(object instanceof CustomClassifier) { 936 CustomClassifier cc = (CustomClassifier)object; 937 if(cc.display(true)) { 938 assigned.refresh(); 939 } 940 cc.destroy(); // Remove gui prompt or else. 941 cc = null; 942 } 943 } 944 } 945 } 946 /** This class listens for actions upon the remove button in the controls, and if detected calls the removeClassifier() method. 947 947 */ 948 949 948 private class RemoveListener 949 implements ActionListener { 950 950 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls, so we can remove the selected Classifier. 951 951 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. … … 953 953 * @see org.greenstone.gatherer.cdm.CustomClassifier 954 954 */ 955 956 957 958 959 960 961 962 963 964 955 public void actionPerformed(ActionEvent event) { 956 if(!classifier_list.isSelectionEmpty()) { 957 Object object = classifier_list.getSelectedValue(); 958 if(object instanceof Classifier || object instanceof CustomClassifier) { 959 removeClassifier(object); 960 } 961 } 962 } 963 } 964 } 965 965 } 966 966 -
trunk/gli/src/org/greenstone/gatherer/cdm/CollectionDesignManager.java
r4293 r4366 84 84 // #################################################################################### 85 85 public class CollectionDesignManager { 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 86 /** Whether this collection is to be made public or not. */ 87 public boolean public_col = false; 88 /** Whether this collection is a beta version or not. */ 89 public boolean beta = true; 90 /** A list of classifiers to use at build time. */ 91 public ClassifierManager classifiers = null; 92 /** A manager of collection level metadata. */ 93 public CollectionMetaManager collectionmetadatum = null; 94 /** E-mail address of the collection's creator. */ 95 public EmailAddress creator = null; 96 /** E-mail address of the collection's maintainer. */ 97 public EmailAddress maintainer = null; 98 /** The collection configuration file. */ 99 public File in_file = null; 100 /** A list of formating strings to use at build time. */ 101 public FormatManager formats = null; 102 /** The manager in charge of displaying this manager and the controls for other managers. */ 103 public GUI gui = null; 104 /** List of indexes to be built, and the default index. */ 105 public IndexManager indexes = null; 106 /** Contains instructions dealing with the collection language. */ 107 public LanguageManager languages = null; 108 /** A simple manager for the visual review of metadata sets. */ 109 public MetadataSetManager metadatasets = null; 110 /** A list of plugins to use at build time. */ 111 public PlugInManager plugins = null; 112 /** Contains: A list of subcollections, (defined on metadatadata), a list of which subcollection indexes to build and the default subcollection index. */ 113 public SubcollectionManager subcollections = null; 114 /** A list of whatever commands could not be parsed at all. */ 115 private ArrayList rest = null; 116 /** A reference to the Gatherer. */ 117 private Gatherer gatherer = null; 118 /** Constructor. 119 * @param gatherer The <strong>Gatherer</strong> that created this class. 120 * @see org.greenstone.gatherer.cdm.ClassifierManager 121 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 122 * @see org.greenstone.gatherer.cdm.FormatManager 123 * @see org.greenstone.gatherer.cdm.IndexManager 124 * @see org.greenstone.gatherer.cdm.LanguageManager 125 * @see org.greenstone.gatherer.cdm.MetadataSetManager 126 * @see org.greenstone.gatherer.cdm.PlugInManager 127 * @see org.greenstone.gatherer.cdm.SubcollectionManager 128 */ 129 public CollectionDesignManager() { 130 this.gatherer = Gatherer.self; 131 this.classifiers = new ClassifierManager(gatherer, this); 132 this.collectionmetadatum = new CollectionMetaManager(gatherer, this); 133 this.formats = new FormatManager(gatherer, this); 134 this.indexes = new IndexManager(gatherer, this); 135 this.languages = new LanguageManager(gatherer, this); 136 this.metadatasets = new MetadataSetManager(gatherer); 137 this.plugins = new PlugInManager(gatherer, this); 138 this.rest = new ArrayList(); 139 this.subcollections = new SubcollectionManager(gatherer); 140 } 141 /** In order to prevent Components that have registered themselves wasting memory, this method invalidates each of the sub-managers controls, causing them to unregister listeners. 142 * @see org.greenstone.gatherer.cdm.ClassifierManager 143 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 144 * @see org.greenstone.gatherer.cdm.FormatManager 145 * @see org.greenstone.gatherer.cdm.IndexManager 146 * @see org.greenstone.gatherer.cdm.LanguageManager 147 * @see org.greenstone.gatherer.cdm.MetadataSetManager 148 * @see org.greenstone.gatherer.cdm.PlugInManager 149 * @see org.greenstone.gatherer.cdm.SubcollectionManager 150 */ 151 public void destroy() { 152 // Remove visual the component from its parent. 153 if(gui.getParent() != null) { 154 gui.getParent().remove(gui); 155 } 156 // Remove references from persistant listeners. 157 if(classifiers != null && formats != null && gui != null && indexes != null && languages != null && metadatasets != null && plugins != null && subcollections != null) { 158 classifiers.invalidateControls(); 159 formats.invalidateControls(); 160 gui.invalidateControls(); 161 indexes.invalidateControls(); 162 languages.invalidateControls(); 163 metadatasets.invalidateControls(); 164 plugins.invalidateControls(); 165 subcollections.invalidateControls(); 166 } 167 // Null globals. 168 classifiers = null; 169 collectionmetadatum = null; 170 creator = null; 171 maintainer = null; 172 in_file = null; 173 formats = null; 174 gui = null; 175 indexes = null; 176 languages = null; 177 metadatasets = null; 178 plugins = null; 179 subcollections = null; 180 rest = null; 181 gatherer = null; 182 } 183 184 /** Display the GUI interface for the CollectionDesignManager in the centre of the indicated panel. 185 * @param target The <strong>JPanel</strong> you wish to display the GUI on. 186 * @see org.greenstone.gatherer.cdm.GUI 187 */ 188 public void display(JPanel target) { 189 this.gui = new GUI(gatherer, this); 190 target.add(gui, BorderLayout.CENTER); 191 } 192 /** When the tab on the JTabbedPane that contains the GUI is selected, this method is called to ensure that the controls are all up to date, in terms of references to metadata etc. 193 * @see org.greenstone.gatherer.cdm.GUI 194 */ 195 public void gainFocus() { 196 gui.updateUI(); 197 } 198 /** Retrieve the current set of indexes as defined by the user configuration. 199 * @return An <strong>ArrayList</strong> of indexes. 200 * @see org.greenstone.gatherer.cdm.Index 201 * @see org.greenstone.gatherer.cdm.IndexManager 202 */ 203 public ArrayList getIndexes() { 204 ArrayList result = new ArrayList(); 205 int size = indexes.size(); 206 for(int i = 0; i < size; i++) { 207 result.add(indexes.getIndex(i)); 208 } 209 return result; 210 } 211 /** Retrieve the name of the collection configuration file which is being used as the source of the information in this object. 212 * @return The files absolute path as a <strong>String</strong>. 213 */ 214 public String getFilename() { 215 return in_file.getAbsolutePath(); 216 } 217 /** Used to parse the given file as if it was a collection configuration file, passing any parsed commands to the relevant submanager. Currently ignores any comments, and marks any other lines as being unrecognizable if they can't be parsed. 218 * @param filename The name of the file you wish to attempt to pass as a <strong>String</strong>. 219 * @see org.greenstone.gatherer.cdm.ClassifierManager 220 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 221 * @see org.greenstone.gatherer.cdm.FormatManager 222 * @see org.greenstone.gatherer.cdm.IndexManager 223 * @see org.greenstone.gatherer.cdm.LanguageManager 224 * @see org.greenstone.gatherer.cdm.MetadataSetManager 225 * @see org.greenstone.gatherer.cdm.PlugInManager 226 * @see org.greenstone.gatherer.cdm.SubcollectionManager 227 * @see org.greenstone.gatherer.util.EmailAddress 228 */ 229 public void parse(String filename) { 230 try { 231 in_file = new File(filename); 232 FileReader in_reader = new FileReader(in_file); 233 BufferedReader in = new BufferedReader(in_reader); 234 String command = null; 235 while((command = in.readLine()) != null) { 236 if(command.length() > 0) { 237 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat. 238 while(command.trim().endsWith("\\")) { 239 command = command.substring(0, command.lastIndexOf("\\")); 240 String next_line = in.readLine(); 241 if(next_line != null) { 242 command = command + next_line; 243 } 244 } 245 // Now we've finished parsing a command line, see what manager wants a piece of it. 246 boolean found = false; 247 String command_lc = command.toLowerCase(); 248 if(command_lc.startsWith("creator")) { 249 creator = new EmailAddress(gatherer, command); 250 found = true; 251 } 252 if(command_lc.startsWith("maintainer")) { 253 maintainer = new EmailAddress(gatherer, command); 254 found = true; 255 } 256 if(command_lc.startsWith("public")) { 257 if(command_lc.endsWith("true")) { 258 public_col = true; 259 } 260 else { 261 public_col = false; 262 } 263 found = true; 264 } 265 if(command_lc.startsWith("beta")) { 266 if(command_lc.endsWith("false")) { 267 beta = false; 268 } 269 else { 270 beta = true; 271 } 272 found = true; 273 } 274 if(!found) { 275 found = indexes.parse(command); 276 } 277 if(!found) { 278 found = subcollections.parse(command, false); 279 } 280 if(!found) { 281 found = languages.parse(command); 282 } 283 if(!found) { 284 found = plugins.parse(command); 285 } 286 if(!found) { 287 found = classifiers.parse(command); 288 } 289 if(!found) { 290 found = formats.parse(command, false); 291 } 292 if(!found) { 293 found = collectionmetadatum.parse(command, false); 294 } 295 // Metadataset commands 296 if(command_lc.startsWith("metadataset")) { 297 // Nothing yet. Eventually used to import metadata. 298 found = true; 299 } 300 // Comments we ignore. 301 if(command_lc.startsWith("# these instructions are not recognized by the gatherer.")) { 302 // Ignore 303 found = true; 304 } 305 // We have been unable to parse this command, add it to rest. 306 if(!found) { 307 rest.add(command); 308 } 309 } 310 } 311 in.close(); 312 312 // Now attempt to finalize any commands that were not immediately parsed as they were waiting for unresolved references. 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 313 subcollections.reparseUnresolved(); 314 classifiers.reparseUnresolved(); 315 formats.reparseUnresolved(); 316 collectionmetadatum.reparseUnresolved(); 317 } 318 catch(Exception error) { 319 error.printStackTrace(); 320 } 321 } 322 /** Cause the current definitions within the Collection Design Manager to be written back out to whatever collect.cfg it is based upon, taking care to change reference pointers into something more sensible within the text file. 323 * @see org.greenstone.gatherer.cdm.ClassifierManager 324 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 325 * @see org.greenstone.gatherer.cdm.FormatManager 326 * @see org.greenstone.gatherer.cdm.IndexManager 327 * @see org.greenstone.gatherer.cdm.LanguageManager 328 * @see org.greenstone.gatherer.cdm.MetadataSetManager 329 * @see org.greenstone.gatherer.cdm.PlugInManager 330 * @see org.greenstone.gatherer.cdm.SubcollectionManager 331 * @see org.greenstone.gatherer.util.EmailAddress 332 */ 333 public void save() { 334 try { 335 335 // If the file already exists (it should) rename it. 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 336 if(in_file.exists()) { 337 String filename = in_file.getAbsolutePath(); 338 File backup = new File(filename + "~"); 339 backup.deleteOnExit(); 340 if(!in_file.renameTo(backup)) { 341 gatherer.debug("Error in CollectionDesignManager.parse(): FileRenamedException"); 342 } 343 in_file = new File(filename); // Just in case we moved it. 344 } 345 FileOutputStream out = new FileOutputStream(in_file); 346 347 StringBuffer text = new StringBuffer(""); 348 if(creator != null) { 349 text.append(creator.toString()); 350 text.append("\n"); 351 } 352 if(maintainer != null) { 353 text.append(maintainer.toString()); 354 text.append("\n"); 355 } 356 if(public_col) { 357 text.append("public true\n"); 358 } 359 else { 360 text.append("public false\n"); 361 } 362 if(beta) { 363 text.append("beta true\n"); 364 } 365 else { 366 text.append("beta false\n"); 367 } 368 if(text.length() > 0) { 369 text.append("\n"); 370 } 371 if(indexes.size() > 0) { 372 text.append(indexes.toString()); 373 } 374 if(subcollections.size() > 0) { 375 text.append(subcollections.toString()); 376 } 377 if(languages.size() > 0) { 378 text.append(languages.toString()); 379 } 380 if(plugins.size() > 0) { 381 text.append(plugins.toString()); 382 } 383 if(classifiers.size() > 0) { 384 text.append(classifiers.toString()); 385 } 386 if(formats.size() > 0) { 387 text.append(formats.toString()); 388 } 389 if(collectionmetadatum.size() > 0) { 390 text.append(collectionmetadatum.toString()); 391 } 392 text.append(metadatasets.toString()); 393 if(rest.size() > 0) { 394 // Write out rest at the bottom. 395 text.append("# These instructions are not recognized by the Gatherer.\n"); 396 for(int i = 0; i < rest.size(); i++) { 397 text.append(rest.get(i)); 398 text.append("\n"); 399 } 400 } 401 out.write(text.toString().getBytes()); 402 out.close(); 403 out = null; 404 } 405 catch(Exception error) { 406 error.printStackTrace(); 407 } 408 } 409 /** Method used during a global search and replace to highlight the appropriate record within the Collection Design Managers version of the Metadata Set Manager (view only). 410 * @param element The name of the desired element as a <strong>String</strong>. 411 * @see org.greenstone.gatherer.cdm.GUI 412 * @see org.greenstone.gatherer.cdm.MetadataSetManager 413 */ 414 public Rectangle setSelectedElement(String element) { 415 // First ensure that the metadata set controls are visible. 416 gui.setSelectedView("CDM.GUI.MetadataSets"); 417 // Then tell them to select the given element. 418 return metadatasets.setSelectedElement(element); 419 } 420 420 } -
trunk/gli/src/org/greenstone/gatherer/cdm/CollectionMeta.java
r4293 r4366 59 59 */ 60 60 public class CollectionMeta 61 62 63 64 65 66 67 68 69 70 71 72 73 61 implements Comparable { 62 /** A reference to the collection design manager for access to the language manager. */ 63 private CollectionDesignManager manager = null; 64 /** The language of this metadata. Should be a two letter code. */ 65 private Language language = null; 66 /** The name of the thing this metadata is assigned to, which may index an Index. */ 67 private Object name = null; 68 /** The value of this metadata. */ 69 private String value = null; 70 /** Constructor. 71 * @param name The object the metadata is assigned to as an <strong>Object</strong>. 72 * @param language The language of the metadata as a <strong>Language</strong>. Should be a two letter code. 73 * @param value The value of this metadata, as a <strong>String</strong>. 74 74 */ 75 76 77 78 79 80 81 75 public CollectionMeta(CollectionDesignManager manager, Object name, Language language, String value) { 76 this.language = language; 77 this.manager = manager; 78 this.name = name; 79 this.value = value; 80 } 81 /** Method to compare two collection metadata objects to calculate their respective ordering. 82 82 * @param object The other metadata to compare to, as an <strong>Object</strong>. 83 83 * @return An <i>int</i> which is less than 0 if this object proceeds the given object, 0 if they are equal and greater than 0 otherwise. 84 84 * @see org.greenstone.gatherer.cdm.Language 85 85 */ 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 86 public int compareTo(Object object) { 87 if(object instanceof CollectionMeta) { 88 CollectionMeta metadata = (CollectionMeta) object; 89 int result = name.toString().compareTo(metadata.getName().toString()); 90 if(result == 0) { 91 Language other_language = metadata.getLanguage(); 92 if(language != null && other_language != null) { 93 result = language.compareTo(metadata.getLanguage()); 94 if(result == 0) { 95 return value.compareTo(metadata.getValue()); 96 } 97 } 98 else if(language != null) { 99 return -1; 100 } 101 else if(other_language != null) { 102 return 1; 103 } 104 } 105 return result; 106 } 107 return toString().compareTo(object.toString()); 108 } 109 /** Method to compare two collection metadata objects for equality. 110 110 * @param object The other metadata to compare to, as an <strong>Object</strong>. 111 111 * @return A <i>boolean</i> value of <i>true</i> if the object are equal, <i>false</i> otherwise. 112 112 */ 113 114 115 116 117 118 119 113 public boolean equals(Object object) { 114 if(compareTo(object) == 0) { 115 return true; 116 } 117 return false; 118 } 119 /** Method to retrieve the value of language. 120 120 * @return The value of language as a <strong>Language</strong>. 121 121 */ 122 123 124 125 122 public Language getLanguage() { 123 return language; 124 } 125 /** Method to retrieve the value of name. 126 126 * @return The value of name as an <strong>Object</strong>. 127 127 */ 128 129 130 131 128 public Object getName() { 129 return name; 130 } 131 /** Method to retrieve the value of value (well great choice of name there). 132 132 * @return The value of value as a <strong>String</strong>. 133 133 */ 134 135 136 137 134 public String getValue() { 135 return value; 136 } 137 /** Method to print out this class as it would appear within the collection configuration file. 138 138 * @return A <strong>String</strong> containing the text value of this class. 139 139 */ 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 140 public String toString() { 141 String text = "collectionmeta "; 142 if(name instanceof Index) { 143 text = text + "."; 144 Index index = (Index)name; 145 text = text + index.toString(false) + " "; 146 } 147 else { 148 text = text + name.toString() + " "; 149 } 150 if(language != null && manager.languages.size() > 0 && !manager.languages.isDefaultLanguage(language)) { 151 text = text + "[l=" + language.getCode() + "] "; 152 } 153 text = text + "\"" + Utility.encodeGreenstone(value) + "\"\n"; 154 return text; 155 } 156 /** Used to update the contents of this collection level metadata to the given 'new' values. 157 157 * @param name The new name of the metadata, as a <strong>Object</strong>. 158 158 * @param language The new <strong>Language</strong> of the metadata. 159 159 * @param value And the value the metadata is assigned, as a <strong>String</strong>. 160 160 */ 161 162 163 164 165 166 161 public void update(Object name, Language language, String value) { 162 this.name = name; 163 this.language = language; 164 this.value = value; 165 } 166 /** Ensure this is a valid metadata entry by checking that the value is non-null and non-zero length (after having removed whitespace). 167 167 * @return <i>true</i> if this metadata has a valid value and should be added to the config, <i>false</i> otherwise. 168 168 */ 169 170 171 169 public boolean valid() { 170 return(value != null && value.trim().length() > 0); 171 } 172 172 } 173 173 -
trunk/gli/src/org/greenstone/gatherer/cdm/CollectionMetaManager.java
r4293 r4366 95 95 // #################################################################################### 96 96 public class CollectionMetaManager 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 97 extends DefaultListModel { 98 /** A reference to the cdm manager so we can access indexes and languages. */ 99 private CollectionDesignManager manager = null; 100 /** A reference to ourself so that inner classes can use us as a model. */ 101 private DefaultListModel model = null; 102 /** A reference to the Gatherer. */ 103 private Gatherer gatherer = null; 104 /** The language the most recent metadata returned was in. */ 105 private Language current_language = null; 106 /** We can't safely parse metadata commands until after all the other commands have been parsed, so we store commands here for now. */ 107 private ArrayList unresolved_commands = null; 108 /** Constructor. 109 * @param gatherer A reference to the <strong>Gatherer</strong>. 110 * @param manager A reference to the <strong>CollectionDesignManager</strong> for access to other configuration managers. 111 */ 112 public CollectionMetaManager(Gatherer gatherer, CollectionDesignManager manager) { 113 super(); 114 this.gatherer = gatherer; 115 this.manager = manager; 116 this.model = this; 117 this.unresolved_commands = new ArrayList(); 118 } 119 /** Method to add a new piece of metadata. 120 120 * @param metadata The new <strong>CollectionMeta</strong>. 121 121 */ 122 123 124 125 126 127 128 129 130 122 public void addMetadata(CollectionMeta metadata) { 123 CollectionMeta existing = getMetadata(metadata.getName().toString(), metadata.getLanguage(), false); 124 if(existing != null) { 125 removeElement(existing); 126 } 127 addElement(metadata); 128 gatherer.c_man.configurationChanged(); 129 } 130 /** Retrieve the collectionextra metadata in the default language, if present. 131 131 * @return A <strong>CollectionMeta</strong> containing the collectionextra in the default language if present, or else the first collectionextra of any language, otherwise <i>null</i>. 132 132 * @see org.greenstone.gatherer.cdm.Language 133 133 */ 134 135 136 137 138 139 140 141 142 134 public CollectionMeta getCollectionExtra() { 135 CollectionMeta result = getMetadata("collectionextra", manager.languages.getDefaultLanguage(), true); 136 if(result == null) { 137 result = new CollectionMeta(manager, "collectionextra", manager.languages.getDefaultLanguage(), ""); 138 addMetadata(result); 139 } 140 return result; 141 } 142 /** Retrieve the collectionname metadata in the default language, if present. 143 143 * @return A <strong>CollectionMeta</strong> containing the collectionname in the default language if present, or else the first collectionname of any language, otherwise <i>null</i>. 144 144 * @see org.greenstone.gatherer.cdm.Language 145 145 */ 146 147 148 149 146 public CollectionMeta getCollectionName() { 147 return getMetadata("collectionname", manager.languages.getDefaultLanguage(), true); 148 } 149 /** Retrieve the iconcollection metadata in the default language, if present. 150 150 * @return A <strong>CollectionMeta</strong> containing the iconcollection in the default language if present, or else the first iconcollection of any language, otherwise <i>null</i>. 151 151 * @see org.greenstone.gatherer.cdm.Language 152 152 */ 153 154 155 156 157 158 159 160 161 153 public CollectionMeta getIconCollection() { 154 CollectionMeta result = getMetadata("iconcollection", manager.languages.getDefaultLanguage(), true); 155 if(result == null) { 156 result = new CollectionMeta(manager, "iconcollection", manager.languages.getDefaultLanguage(), ""); 157 addMetadata(result); 158 } 159 return result; 160 } 161 /** Method to retrieve the list of metadata. 162 162 * @return An <strong>ArrayList</strong> containing the metadata. 163 163 */ 164 165 166 167 168 169 170 171 164 public ArrayList getMetadata() { 165 ArrayList metadata = new ArrayList(); 166 for(int i = 0; i < size(); i++) { 167 metadata.add(get(i)); 168 } 169 Collections.sort(metadata); 170 return metadata; 171 } 172 172 173 174 175 176 177 178 179 180 181 182 183 184 173 /** Retrieve all of the metadata for the given feature, regardless of language. */ 174 public ArrayList getMetadata(String name) { 175 ArrayList result = new ArrayList(); 176 for(int i = 0; i < size(); i++) { 177 CollectionMeta metadata = (CollectionMeta) get(i); 178 if(metadata.getName().equals(name)) { 179 result.add(metadata); 180 } 181 } 182 return result; 183 } 184 /** Method to retrieve a certain piece of metadata based on its name and language. 185 185 * @param name The name of the metadata as a <strong>String</strong>. 186 186 * @param language The <strong>Language</strong> of the metadata. … … 188 188 * @return The <strong>CollectionMeta</strong> requested, or <i>null</i> if no such metadata. 189 189 */ 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 190 public CollectionMeta getMetadata(String name, Language language, boolean partial) { 191 CollectionMeta partial_match = null; 192 for(int i = 0; i < size(); i++) { 193 CollectionMeta metadata = (CollectionMeta) get(i); 194 if(metadata.getName().equals(name)) { 195 if (metadata.getLanguage().equals(language)) { 196 return metadata; 197 } 198 partial_match = metadata; 199 } 200 } 201 if(partial) { 202 return partial_match; 203 } 204 return null; 205 } 206 /** Method that attempts to parse a collection metadata command from the given text. If a command is parsed successfully it is immediately added to the the collections metadata. 207 207 * @param command The command text we wish to parse, as a <strong>String</strong>. 208 208 * @return A <i>boolean</i> which is <i>true</i> if a collection metadata command was successfully parsed, <i>false</i> otherwise. … … 213 213 * @see org.greenstone.gatherer.util.Utility 214 214 */ 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 215 public boolean parse(String command, boolean finished) { 216 String temp = command.toLowerCase(); 217 if(temp.startsWith("collectionmeta")) { 218 if(finished) { 219 CommandTokenizer ct = new CommandTokenizer(command); 220 if(ct.countTokens() >= 3) { 221 ct.nextToken(); // Throw away collectionmeta 222 Object key = ct.nextToken(); 223 Language language = null; 224 String language_code = null; 225 String value = ct.nextToken(); 226 // Arg. Remember a language token will be '[l=<code>]' 227 if(value.startsWith("[") && value.endsWith("]")) { 228 language_code = value.substring(3, value.length() - 1); 229 ///ystem.err.println("Language code = " + language_code); 230 value = ct.nextToken(); 231 } 232 // Check if the key is an index, an if so retrieve it. 233 if(((String)key).startsWith(".")) { 234 String key_str = (String)key; 235 key_str = key_str.substring(1); 236 key = manager.indexes.getIndex(key_str); 237 } 238 // An if we have a language code, retrieve its object too. 239 if(language_code != null) { 240 language = manager.languages.getLanguage(language_code, false); 241 } 242 // Otherwise set language to the default language. 243 else { 244 language = manager.languages.getDefaultLanguage(); 245 } 246 if(key != null) { 247 // Trim any " 248 if(value.equals("\"\"")) { 249 value = ""; 250 } 251 else { 252 if(value.startsWith("\"")) { 253 value = value.substring(1); 254 } 255 if(value.endsWith("\"")) { 256 value = value.substring(0, value.length() - 1); 257 } 258 } 259 CollectionMeta meta = new CollectionMeta(manager, key, language, Utility.decodeGreenstone(value)); 260 addMetadata(meta); 261 } 262 return true; 263 } 264 } 265 else { 266 unresolved_commands.add(command); 267 return true; 268 } 269 } 270 return false; 271 } 272 /** Ensure that the values being showed are the most up to date. */ 273 public void refresh() { 274 fireContentsChanged(this, 0, size()); 275 } 276 277 /** Method to remove a piece of metadata. 278 278 * @param metadata The <strong>CollectionMeta</strong> you wish to remove. 279 279 */ 280 281 282 283 284 285 */ 286 287 288 289 290 291 292 293 280 public void removeMetadata(CollectionMeta metadata) { 281 removeElement(metadata); 282 gatherer.c_man.configurationChanged(); 283 } 284 /** Method which attempts to reparse obvious metadata commands which used unresovable references. 285 */ 286 public void reparseUnresolved() { 287 for(int i = 0; i < unresolved_commands.size(); i++) { 288 parse((String)unresolved_commands.get(i), true); 289 } 290 // Regardless of if they work, clear the commands. 291 unresolved_commands.clear(); 292 } 293 /** Sets the value of a certain metadata. 294 294 * @param name The name of the metadata as a <strong>String</strong>. 295 295 * @param language The <strong>Language</strong> to use. 296 296 * @param value The value of the metadata also as a <strong>String</strong>. 297 297 */ 298 299 300 301 298 public void setMetadata(String name, Language language, String value) { 299 addMetadata(new CollectionMeta(manager, name, language, value)); 300 } 301 /** Method to produce the list of metadata in a string such as you would find in the collection configuration file. 302 302 * @return A <strong>String</strong> containing the list of collection metadata. 303 303 */ 304 305 306 307 308 309 310 311 312 313 314 315 304 public String toString() { 305 StringBuffer text = new StringBuffer(""); 306 for(int i = 0; i < size(); i++) { 307 CollectionMeta data = (CollectionMeta) get(i); 308 if(data.valid()) { 309 text.append(data.toString()); 310 } 311 } 312 text.append("\n"); 313 return text.toString(); 314 } 315 /** Overloaded to call get with both a key and an empty argument array. 316 316 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 317 317 * @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. 318 318 */ 319 320 321 322 319 private String get(String key) { 320 return get(key, null); 321 } 322 /** 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> 323 323 * 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>. 324 324 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. … … 328 328 * @see org.greenstone.gatherer.Dictionary 329 329 */ 330 331 332 333 334 335 330 private String get(String key, String args[]) { 331 if(key.indexOf('.') == -1) { 332 key = "CDM.CollectionMetaManager." + key; 333 } 334 return gatherer.dictionary.get(key, args); 335 } 336 336 } -
trunk/gli/src/org/greenstone/gatherer/cdm/CommandTokenizer.java
r4293 r4366 56 56 */ 57 57 public class CommandTokenizer 58 58 extends StringTokenizer { 59 59 60 60 private int last_type = -1; 61 61 62 63 64 65 62 static final public int BRACKET_ENCLOSED = 0; 63 static final public int DOUBLE_QUOTE_ENCLOSED = 1; 64 static final public int NORMAL = 2; 65 static final public int QUOTE_ENCLOSED = 3; 66 66 67 68 69 70 71 72 67 /** Constructor. 68 * @param command The command <strong>String</strong> you wish to tokenize. 69 */ 70 public CommandTokenizer(String command) { 71 super(command); 72 } 73 73 74 75 76 74 public int getLastType() { 75 return last_type; 76 } 77 77 78 79 80 78 public boolean isComment() { 79 return (last_type == DOUBLE_QUOTE_ENCLOSED || last_type == QUOTE_ENCLOSED); 80 } 81 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 82 /** Method to retrieve the next token from the command, taking care to group tokens enclosed in speech marks. 83 * @return A <strong>String</strong> containing the next token from the command. 84 */ 85 public String nextToken() { 86 String result = null; 87 if(hasMoreTokens()) { 88 StringBuffer buffer = new StringBuffer(super.nextToken()); 89 switch(buffer.charAt(0)) { 90 case '\"': 91 while((buffer.length() == 1 || buffer.charAt(buffer.length() - 1) != '\"') && hasMoreTokens()) { 92 buffer.append(" "); 93 buffer.append(super.nextToken()); 94 ///ystem.err.println("Current Buffer = '" + buffer.toString() + "'"); 95 } 96 ///ystem.err.println("Final Buffer = '" + buffer.toString() + "'"); 97 last_type = DOUBLE_QUOTE_ENCLOSED; 98 break; 99 case '\'': 100 while((buffer.length() == 1 || buffer.charAt(buffer.length() - 1) != '\'') && hasMoreTokens()) { 101 buffer.append(" "); 102 buffer.append(super.nextToken()); 103 } 104 last_type = QUOTE_ENCLOSED; 105 break; 106 case '[': 107 while((buffer.length() == 1 || buffer.charAt(buffer.length() - 1) != ']') && hasMoreTokens()) { 108 buffer.append(" "); 109 buffer.append(super.nextToken()); 110 } 111 last_type = BRACKET_ENCLOSED; 112 break; 113 default: 114 last_type = NORMAL; 115 } 116 result = buffer.toString(); 117 } 118 return result; 119 } 120 /** Unfortunately the StringBuffer doesn't have a built in endsWith method, so I'll just have to implement my own. 121 * @param str The <strong>StringBuffer</strong> we are checking the end of. 122 * @param target The <strong>String</strong> fragment we are searching for. 123 * @return <i>true</i> if str ends with target, <i>false</i> otherwise. 124 */ 125 private boolean endsWith(StringBuffer str, String target) { 126 String temp = str.toString(); 127 if(temp.endsWith(target) != (str.lastIndexOf(target) == str.length() - target.length())) { 128 128 ///ystem.err.println("Holy error that'll crash the HFile creator if it happens twice, Batman!"); 129 129 ///ystem.err.println("String = '" + temp + "'"); 130 130 ///ystem.err.println("Target = '" + target + "'"); 131 132 133 131 } 132 return str.lastIndexOf(target) == str.length() - target.length(); 133 } 134 134 } 135 135 -
trunk/gli/src/org/greenstone/gatherer/cdm/CustomClassifier.java
r4293 r4366 44 44 */ 45 45 public interface CustomClassifier 46 47 48 46 extends Comparable { 47 /** Constructor. 48 * public CustomClassifier(Gatherer gatherer); 49 49 */ 50 50 /** Create a blank copy of this pseudo-classifier. 51 51 * @return A newly allocated <strong>CustomClassifier</strong>. 52 52 */ 53 54 55 56 53 public CustomClassifier copy(); 54 /** Destructor. */ 55 public void destroy(); 56 /** Show the controls for configuring this pseudo-classifier. 57 57 * @param show <i>true</i> if the component should immediately show itself, <i>false</i> if you just wish to initialize components. 58 58 */ 59 60 59 public boolean display(boolean show); 60 /** Method to return this pseudo-classifier represented as a String. 61 61 * @return A <strong>String</strong>. 62 62 */ 63 64 63 public String getCommand(); 64 /** Retrieve the custom command, a command line that overrides and replaces some other 'actual' classifier. 65 65 * @param index The number of the classifer this one is replacing. 66 66 */ 67 68 67 public String getCustomCommand(int index); 68 /** Get the name of this custom classifier. 69 69 * @return A <strong>String</strong> representing the name. 70 70 */ 71 72 73 74 75 76 77 78 71 public String getName(); 72 public void process(FileNode record); 73 /** Recreate a CustomAZList given several parameters including the real classifier created during custom design. 74 * @param classifier The real <strong>Classifier</strong>. 75 * @param separations A <strong>String</strong> representing the choosen separations. 76 */ 77 public void recreate(Classifier classifier, String separations); 78 /** Sets the value of Gatherer, for those classes loaded dynamically. 79 79 * @param gatherer A reference to the <strong>Gatherer</strong>. 80 80 */ 81 82 81 public void setGatherer(Gatherer gatherer); 82 /** Sets the Classifier manager in charge of this classifier. 83 83 * @param manager This classifiers <strong>ClassifierManager</strong>. 84 84 */ 85 85 public void setManager(ClassifierManager manager); 86 86 } -
trunk/gli/src/org/greenstone/gatherer/cdm/DefaultSubIndex.java
r4293 r4366 60 60 */ 61 61 public class DefaultSubIndex 62 63 64 62 extends SubIndex { 63 /** Copy constructor. 64 * @param subindex The <strong>SubIndex</strong> we are copying. 65 65 */ 66 67 68 69 70 71 66 public DefaultSubIndex(SubIndex subindex) { 67 for(int i = 0; i < subindex.size(); i++) { 68 add(subindex.get(i)); 69 } 70 } 71 /** Parsed data Constructor. 72 72 * @param raw A <strong>String</strong> containing a comma separated list of subcollection names. 73 73 * @param manager A reference to the <strong>SubcollectionManager</strong> via which we retrieve the required <strong>Subcollection</strong>s. 74 74 */ 75 76 77 78 75 public DefaultSubIndex(String raw, SubcollectionManager manager) { 76 super(raw, manager); 77 } 78 /** Method to extract just the subindex information from this class. 79 79 * @return A <strong>SubIndex</strong> based on the information in this class. 80 80 */ 81 82 83 84 81 public SubIndex getSubIndex() { 82 return new SubIndex(this); 83 } 84 /** Method to produce this class as a string much like you'd find in the collection configuration file. 85 85 * @return A <strong>String</string> with the default index entry. 86 86 */ 87 88 89 90 91 92 93 94 95 96 97 98 99 87 public String toString() { 88 String text = "defaultsubcollection "; 89 for(int i = 0; i < size(); i++) { 90 Object object = get(i); 91 Subcollection sub = (Subcollection) get(i); 92 text = text + sub.getName(); 93 if(i < size() - 1) { 94 text = text + ","; 95 } 96 } 97 text = text + "\n"; 98 return text; 99 } 100 100 } 101 101 -
trunk/gli/src/org/greenstone/gatherer/cdm/DynamicListModel.java
r4293 r4366 57 57 */ 58 58 public class DynamicListModel 59 60 59 extends DefaultListModel 60 implements ComboBoxModel { 61 61 62 62 private boolean auto_order = false; 63 63 64 64 private Object object = null; 65 65 66 67 66 public void addElement(Object element) { 67 if(auto_order) { 68 68 // Insert the object in its alphabetically correct place. 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 69 int position = 0; 70 boolean found = false; 71 while(!found && position < size()) { 72 ///ystem.err.print("Compare " + element.toString() + " to " + get(position).toString() + ": "); 73 int order = element.toString().compareTo(get(position).toString()); 74 if(order < 0) { 75 add(position, element); 76 found = true; 77 ///ystem.err.println("Greater than. Insert"); 78 } 79 else if(order == 0) { 80 found = true; 81 ///ystem.err.println("Equal. End."); 82 } 83 else { 84 position++; 85 ///ystem.err.println("Less than. Carry on."); 86 } 87 } 88 if(!found) { 89 super.addElement(element); 90 ///ystem.err.println("Out of elements. Insert"); 91 } 92 } 93 else { 94 super.addElement(element); 95 } 96 } 97 97 98 99 100 101 102 103 104 105 98 public DynamicListModel shallowCopy() { 99 DynamicListModel copy = new DynamicListModel(); 100 copy.setAutoOrder(auto_order); 101 for(int i = 0; i < size(); i++) { 102 copy.addElement(get(i)); 103 } 104 return copy; 105 } 106 106 107 108 109 110 111 112 113 114 107 public Object getSelectedItem() { 108 ///ystem.err.println("Get item: " + object); 109 return object; 110 } 111 /** Notify all controls that are based on this list model that its contents have changed, and they should repaint themselves. */ 112 public void refresh() { 113 fireContentsChanged(this, 0, size()); 114 } 115 115 116 117 118 116 public void setAutoOrder(boolean auto_order) { 117 this.auto_order = auto_order; 118 } 119 119 120 121 122 123 120 public void setSelectedItem(Object object) { 121 ///ystem.err.println("Set item: " + object); 122 this.object = object; 123 } 124 124 } -
trunk/gli/src/org/greenstone/gatherer/cdm/ElementWrapper.java
r4293 r4366 6 6 */ 7 7 public class ElementWrapper 8 9 10 11 12 8 implements Comparable { 9 /** The element as the data from this object. */ 10 private Node element = null; 11 /** Constructor. 12 * @param element The <strong>Node</strong> this object is wrapped around. 13 13 */ 14 15 16 17 14 public ElementWrapper(Node element) { 15 this.element = element; 16 } 17 /** Compare two objects for ordering. 18 18 * @param object The other <strong>Object</strong> to compare to. 19 19 * @return An <i>int</i> indicating the ordering as is String.compareTo 20 20 */ 21 22 23 24 25 26 27 21 public int compareTo(Object object) { 22 if(object == null) { 23 return 1; 24 } 25 return toString().compareTo(object.toString()); 26 } 27 /** Compare two objects for equality. 28 28 * @param object The <strong>Object</strong> to compare to. 29 29 * @return <i>true</i> if the objects are equal, <i>false</i> otherwise. 30 30 */ 31 32 33 34 35 36 37 31 public boolean equals(Object object) { 32 if(compareTo(object) == 0) { 33 return true; 34 } 35 return false; 36 } 37 /** Retrieve the name of the element. 38 38 * @return The fully qualified name as a <strong>String</strong>. 39 39 * @see org.greenstone.gatherer.msm.MSMUtils 40 40 */ 41 42 43 44 41 public String name() { 42 return MSMUtils.getFullName((Element)element); 43 } 44 /** Retrieve a textual representation of this object. 45 45 * @return A <strong>String</strong>. 46 46 * @see org.greenstone.gatherer.msm.MSMUtils 47 47 */ 48 49 50 48 public String toString() { 49 return MSMUtils.getIdentifier(element) + ": " + MSMUtils.getDescription(element); 50 } 51 51 } -
trunk/gli/src/org/greenstone/gatherer/cdm/Format.java
r4293 r4366 56 56 */ 57 57 public class Format { 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 58 /** If this format is of a true/false type, the current state. */ 59 private boolean state = false; 60 /** The type of this format command, either FLAG, FORMAT or PARAM. */ 61 private int type = -1; 62 /** The feature this format command affects. */ 63 private Object feature = null; 64 /** If this format refers to a specific part of a feature, this indicates the part. */ 65 private String part = null; 66 /** A command string, which may include a specific HTML chunk used to format a control. */ 67 private String value = null; 68 /** An element of the format type enumeration. A true/false option. */ 69 static final public int FLAG = 0; 70 /** An element of the format type enumeration. A string parameter. */ 71 static final public int VALUE = 1; 72 /** The default features as specified by the Greenstone Developers manual. */ 73 static final public String DEFAULT_FEATURES[] = {"", "DocumentArrowsBottom","DocumentButtons","DocumentContents","DocumentHeading","DocumentImages","DocumentText","DocumentUseHTML","Search"}; 74 /** The list of known feature parts. */ 75 static final public String DEFAULT_PARTS[] = {"","DateList","HList","Invisible","VList"}; 76 /** Constructor for a flag format command. 77 * @param feature The <Strong>Object</strong> this format affects. 78 * @param part The specific part of the control to format as a <strong>String</strong>. 79 * @param state A <i>boolean</i> indicating this formats state. 80 */ 81 public Format(Object feature, String part, boolean state) { 82 this.feature = feature; 83 this.part = part; 84 this.state = state; 85 this.type = FLAG; 86 } 87 /** Constructor for a format-string format command. 88 * @param feature The <Strong>Object</strong> this format affects. 89 * @param part The specific part of the control to format as a <strong>String</strong>. 90 * @param value The format <strong>String</strong> which may be a label name, or a piece of HTML formatting. 91 */ 92 public Format(Object feature, String part, String value) { 93 this.feature = feature; 94 this.part = part; 95 this.type = VALUE; 96 this.value = value; 97 } 98 /** Method to retrieve the value of feature, which may either be a String or a Classifier. 99 * @return The value of feature as an <strong>Object</strong>. 100 */ 101 public Object getFeature() { 102 return feature; 103 } 104 /** Method to retrieve the value of part. 105 * @return The value of part as a <Strong>String</strong>. 106 */ 107 public String getPart() { 108 return part; 109 } 110 /** Method to retrieve this formats special Greenstone position code. 111 * @return A <strong>String</strong> which distinctly indicates this classifiers position. 112 * @see org.greenstone.gatherer.cdm.Classifier 113 */ 114 public String getPosition() { 115 ///ystem.err.println("Retrieving the name of feature " + feature); 116 String position = null; 117 if(feature instanceof Classifier) { 118 position = ((Classifier)feature).getPositionString(); 119 } 120 else { 121 position = feature.toString(); 122 } 123 ///ystem.err.println("Result: " + position + part); 124 return position + part; 125 } 126 /** Method to retrieve the value of state. 127 * @param The value of state as a <i>boolean</i>. 128 */ 129 public boolean getState() { 130 return state; 131 } 132 /** Method to retrieve the value of type. 133 * @param The value of type as an <i>int</i>. 134 */ 135 public int getType() { 136 return type; 137 } 138 /** Retrieve the value of value. 139 * @return A <strong>String</strong> which is the value of value. 140 */ 141 public String getValue() { 142 return value; 143 } 144 /** Set the value of state. 145 * @param state The new value for state as a <i>boolean</i>. 146 */ 147 public void setState(boolean state) { 148 this.state = state; 149 } 150 /** Set the value of type. 151 * @param type The new value for type as an <i>int</i>. 152 */ 153 public void setType(int type) { 154 this.type = type; 155 } 156 /** Set the value of value. Hmmm. 157 * @param value The new value from value as a <strong>String</strong>. 158 */ 159 public void setValue(String value) { 160 this.value = value; 161 } 162 /** Method to translate this classes information into a line of text as you would expect in the collection configuration file. 163 * @return A <strong>String</strong> containing the format command. 164 */ 165 public String toString() { 166 String text = "format "; 167 text = text + getPosition() + " "; 168 switch(type) { 169 case FLAG: 170 if(state) { 171 text = text + "true"; 172 } 173 else { 174 text = text + "false"; 175 } 176 break; 177 default: 178 text = text + "\"" + value + "\""; 179 } 180 return text; 181 } 182 /** Retrieve the type of a certain named feature. 183 * @param name The name of the feature as a <strong>String</strong>. 184 * @return An <i>int</i> indicating the type, and hence the controls needed to edit this type. 185 */ 186 static public int getType(String name) { 187 int index = -1; 188 for(int i = 1; i < DEFAULT_FEATURES.length; i++) { 189 if(name.equalsIgnoreCase(DEFAULT_FEATURES[i])) { 190 index = i; 191 } 192 } 193 if(index == 1 || index == 3 || index == 5 || index == 7) { 194 return FLAG; 195 } 196 return VALUE; 197 } 198 198 } -
trunk/gli/src/org/greenstone/gatherer/cdm/FormatManager.java
r4293 r4366 94 94 */ 95 95 public class FormatManager 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 96 extends DynamicListModel { 97 /** The main manager so we can get to ClassifierManager which is needed to turn position reference such as "CL1" into the appropriate Classifier. */ 98 private CollectionDesignManager manager = null; 99 /** The controls used to edit the format commands. */ 100 private Control controls = null; 101 /** A reference to ourselves so inner classes can get at the model. */ 102 private DynamicListModel model = null; 103 /** A reference to the Gatherer. */ 104 private Gatherer gatherer = null; 105 /** We may have somehow recieved a format command which references a classifier that hasn't been parsed yet, so this variable holds a list of failed commands which are retried after the loading is complete. */ 106 private Vector unresolved_commands = null; 107 /** Constructor. 108 * @param gatherer A reference to the <strong>Gatherer</strong>. 109 * @param manager A reference to the <strong>CollectionDesignManager</strong> that created this manager. 110 */ 111 public FormatManager(Gatherer gatherer, CollectionDesignManager manager) { 112 super(); 113 this.gatherer = gatherer; 114 this.manager = manager; 115 this.model = this; 116 this.unresolved_commands = new Vector(); 117 } 118 /** Method to add a new format to this manager. 119 119 * @param format The <strong>Format</strong> to add. 120 120 */ 121 122 123 124 125 126 127 121 public void addFormat(Format format) { 122 if(formatExists(format) == null) { 123 addElement(format); 124 gatherer.c_man.configurationChanged(); 125 } 126 } 127 /** Method which determines if a certain format exists, in which case it must be removed before a new format of the same component is added. For general formatting statements this compares types, for 'custom' ones it compares list and part. 128 128 * @param format The <strong>Format</strong> whose uniqueness we want to check. 129 129 * @return The <strong>Format</strong> that matches the one given, or <i>null</i> if no such format exists. 130 130 */ 131 132 133 134 135 136 137 138 139 140 131 public Format formatExists(Format format) { 132 for(int i = 0; i < size(); i++) { 133 Format current = (Format) get(i); 134 if(format.getPosition().equals(current.getPosition())) { 135 return current; 136 } 137 } 138 return null; 139 } 140 /** Gets the format indicated by the index. 141 141 * @param index The location of the desired format, as an <i>int</i>. 142 142 */ 143 144 145 146 143 public Format getFormat(int index) { 144 return (Format)get(index); 145 } 146 /** Method to retrieve this managers controls. 147 147 * @return The <strong>Control</strong> for this collection. 148 148 */ 149 150 151 152 153 154 155 156 157 158 159 160 161 162 149 public Control getControls() { 150 if(controls == null) { 151 controls = new Control(); 152 } 153 return controls; 154 } 155 /** Method to invalidate controls when some significant change has occured within the collection. */ 156 public void invalidateControls() { 157 if(controls != null) { 158 controls.destroy(); 159 } 160 controls = null; 161 } 162 /** This method attempts to parse a format command from the given string. If a format is parsed, it is immediately added to this managers list of format commands. 163 163 * @param command The <strong>String</strong> we are trying to parse a command from. 164 164 * @param finished A <i>boolean</i> which is <i>true</i> if we are calling this after the the input has been completely parsed (i.e all legal Classifiers are know to exist), or <i>false</i> otherwise. … … 168 168 * @see org.greenstone.gatherer.cdm.Format 169 169 */ 170 public boolean parse(String command, boolean finished) { 171 String temp = command.toLowerCase(); 172 if(temp.startsWith("format")) { 173 if(finished) { 174 CommandTokenizer ct = new CommandTokenizer(command); 175 ct.nextToken(); // Throw away 'format' 176 String position = ct.nextToken(); 177 String value = ct.nextToken(); 178 // String speech marks. 179 if(value.startsWith("\"") && value.endsWith("\"")) { 180 if(value.equals("\"\"")) { 181 value = ""; 182 } 183 else { 184 value = value.substring(1, value.length() - 1); 185 } 186 } 187 // Ensure we parsed a good command. 188 if(position != null && value != null) { 189 // The trickiest bit of parsing format commands is figuring out how much of the position is feature, and how much part. Since the parts are far less likely to change or be customized in any way, we'll try to match them first (except "" of course). 190 String feature_name = null; 191 String part = null; 192 // If this works, then we're all finished. Yay. 193 for(int i = 1; i < Format.DEFAULT_PARTS.length; i++) { 194 if(position.endsWith(Format.DEFAULT_PARTS[i])) { 195 part = Format.DEFAULT_PARTS[i]; 196 feature_name = position.substring(0, position.length() - part.length()); 197 } 198 } 199 // Otherwise we can attempt to find the default features, but we have less chance of success. 200 if(feature_name == null || part == null) { 201 for(int i = 1; i < Format.DEFAULT_FEATURES.length; i++) { 202 if(position.startsWith(Format.DEFAULT_FEATURES[i])) { 203 feature_name = Format.DEFAULT_FEATURES[i]; 204 if(position.length() > feature_name.length()) { 205 part = position.substring(feature_name.length()); 206 } 207 else { 208 part = ""; 209 } 210 } 211 } 212 } 213 // Otherwise we can assume we are dealing with a classifier and split the position using... 214 // position ::= <classifier_position><part> 215 // classifier_position ::= <alphanum>[0-9]$ 216 // part ::= ^![0-9]<alpha> 217 // But I don't like my chances of this working if someone does a scary like CL4Part8B. I just have 218 // to hope no-one uses numbers, and no-one expects CustomFeatureCustomPart to parse properly. 219 if(feature_name == null || part == null) { 220 part = ""; 221 boolean found = false; 222 int index = position.length() - 1; 223 while(index >= 0 && !found) { 224 if(Character.isDigit(position.charAt(index))) { 225 found = true; 226 } 227 else { 228 part = position.charAt(index) + part; 229 index--; 230 } 231 } 232 if(found) { 233 feature_name = position.substring(0, index + 1); 234 } 235 // We ran out of string. No digits. Arg! 236 else { 237 part = null; 238 } 239 } 240 // And if all else fails, stick it all in feature. 241 if(feature_name == null || part == null) { 242 feature_name = position; 243 part = ""; 244 } 245 // Now try to retrieve a classifier with the feature name. 246 Object feature = null; 247 String feature_name_lc = feature_name.toLowerCase(); 248 if(feature_name_lc.startsWith("cl") && feature_name.length() >= 3) { 249 String raw_index = feature_name.substring(2); // Lose the 'CL' 250 int index = -1; 251 try { 252 index = Integer.parseInt(raw_index); 253 } 254 catch(NumberFormatException nfe) { 255 nfe.printStackTrace(); 256 } 257 feature = manager.classifiers.getClassifier(index - 1); 258 } 259 else { 260 ///ystem.err.println("name to short for classifier."); 261 } 262 if(feature == null) { 263 feature = feature_name; 264 } 265 ///ystem.err.println("Feature name = " + feature_name + "\nPart = " + part); 266 if(feature instanceof Classifier) { 267 ///ystem.err.println("Feature is a classifier."); 268 } 269 else { 270 ///ystem.err.println("Feature is a String."); 271 } 272 // Now we have a quick look at value. If its true or false we create a FLAG type 273 if(value.equalsIgnoreCase("true")) { 274 addFormat(new Format(feature, part, true)); 275 } 276 else if(value.equalsIgnoreCase("false")) { 277 addFormat(new Format(feature, part, false)); 278 } 279 // Otherwise add a plain old value based format 280 else { 281 addFormat(new Format(feature, part, value)); 282 } 283 return true; 284 } 285 // Somethings gone terribly, terribly wrong. 286 return false; 170 public boolean parse(String command, boolean finished) { 171 String temp = command.toLowerCase(); 172 if(temp.startsWith("format")) { 173 if(finished) { 174 CommandTokenizer ct = new CommandTokenizer(command); 175 ct.nextToken(); // Throw away 'format' 176 String position = ct.nextToken(); 177 String value = ct.nextToken(); 178 // String speech marks. 179 if(value.startsWith("\"") && value.endsWith("\"")) { 180 if(value.equals("\"\"")) { 181 value = ""; 182 } 183 else { 184 value = value.substring(1, value.length() - 1); 185 } 186 } 187 // Ensure we parsed a good command. 188 if(position != null && value != null) { 189 // The trickiest bit of parsing format commands is figuring out how much of the position is feature, and how much part. Since the parts are far less likely to change or be customized in any way, we'll try to match them first (except "" of course). 190 String feature_name = null; 191 String part = null; 192 // If this works, then we're all finished. Yay. 193 for(int i = 1; i < Format.DEFAULT_PARTS.length; i++) { 194 if(position.endsWith(Format.DEFAULT_PARTS[i])) { 195 part = Format.DEFAULT_PARTS[i]; 196 feature_name = position.substring(0, position.length() - part.length()); 197 } 198 } 199 // Otherwise we can attempt to find the default features, but we have less chance of success. 200 if(feature_name == null || part == null) { 201 for(int i = 1; i < Format.DEFAULT_FEATURES.length; i++) { 202 if(position.startsWith(Format.DEFAULT_FEATURES[i])) { 203 feature_name = Format.DEFAULT_FEATURES[i]; 204 if(position.length() > feature_name.length()) { 205 part = position.substring(feature_name.length()); 287 206 } 288 207 else { 289 unresolved_commands.add(command);208 part = ""; 290 209 } 291 return true; 292 } 293 return false; 294 } 295 /** Method to remove a format. 210 } 211 } 212 } 213 // Otherwise we can assume we are dealing with a classifier and split the position using... 214 // position ::= <classifier_position><part> 215 // classifier_position ::= <alphanum>[0-9]$ 216 // part ::= ^![0-9]<alpha> 217 // But I don't like my chances of this working if someone does a scary like CL4Part8B. I just have 218 // to hope no-one uses numbers, and no-one expects CustomFeatureCustomPart to parse properly. 219 if(feature_name == null || part == null) { 220 part = ""; 221 boolean found = false; 222 int index = position.length() - 1; 223 while(index >= 0 && !found) { 224 if(Character.isDigit(position.charAt(index))) { 225 found = true; 226 } 227 else { 228 part = position.charAt(index) + part; 229 index--; 230 } 231 } 232 if(found) { 233 feature_name = position.substring(0, index + 1); 234 } 235 // We ran out of string. No digits. Arg! 236 else { 237 part = null; 238 } 239 } 240 // And if all else fails, stick it all in feature. 241 if(feature_name == null || part == null) { 242 feature_name = position; 243 part = ""; 244 } 245 // Now try to retrieve a classifier with the feature name. 246 Object feature = null; 247 String feature_name_lc = feature_name.toLowerCase(); 248 if(feature_name_lc.startsWith("cl") && feature_name.length() >= 3) { 249 String raw_index = feature_name.substring(2); // Lose the 'CL' 250 int index = -1; 251 try { 252 index = Integer.parseInt(raw_index); 253 } 254 catch(NumberFormatException nfe) { 255 nfe.printStackTrace(); 256 } 257 feature = manager.classifiers.getClassifier(index - 1); 258 } 259 else { 260 ///ystem.err.println("name to short for classifier."); 261 } 262 if(feature == null) { 263 feature = feature_name; 264 } 265 ///ystem.err.println("Feature name = " + feature_name + "\nPart = " + part); 266 if(feature instanceof Classifier) { 267 ///ystem.err.println("Feature is a classifier."); 268 } 269 else { 270 ///ystem.err.println("Feature is a String."); 271 } 272 // Now we have a quick look at value. If its true or false we create a FLAG type 273 if(value.equalsIgnoreCase("true")) { 274 addFormat(new Format(feature, part, true)); 275 } 276 else if(value.equalsIgnoreCase("false")) { 277 addFormat(new Format(feature, part, false)); 278 } 279 // Otherwise add a plain old value based format 280 else { 281 addFormat(new Format(feature, part, value)); 282 } 283 return true; 284 } 285 // Somethings gone terribly, terribly wrong. 286 return false; 287 } 288 else { 289 unresolved_commands.add(command); 290 } 291 return true; 292 } 293 return false; 294 } 295 /** Method to remove a format. 296 296 * @param format The <strong>Format</strong> to remove. 297 297 */ 298 299 300 301 302 298 public void removeFormat(Format format) { 299 removeElement(format); 300 gatherer.c_man.configurationChanged(); 301 } 302 /** Method which attempts to reparse obvious format commands which previously referenced unresovable Classifiers. 303 303 */ 304 305 306 307 308 309 310 311 312 313 304 public void reparseUnresolved() { 305 for(int i = 0; i < unresolved_commands.size(); i++) { 306 if(!parse((String)unresolved_commands.get(i), true)) { 307 ///ystem.err.println("*** Error: Command " + unresolved_commands.get(i)); 308 } 309 } 310 // Regardless of if they work, clear the commands. 311 unresolved_commands.clear(); 312 } 313 /** Method to produce a block of text representing the format commands in this manager, ready to be used in the collection configuration file. 314 314 * @return A <strong>String</strong> containing a series of format commands. 315 315 */ 316 317 318 319 320 321 322 323 324 325 326 316 public String toString() { 317 StringBuffer text = new StringBuffer(""); 318 for(int i = 0; i < size(); i++) { 319 Format format = (Format) get(i); 320 text.append(format.toString()); 321 text.append("\n"); 322 } 323 text.append("\n"); 324 return text.toString(); 325 } 326 /** Overloaded to call get with both a key and an empty argument array. 327 327 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 328 328 * @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. 329 329 */ 330 331 332 333 330 private String get(String key) { 331 return get(key, null); 332 } 333 /** 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> 334 334 * 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>. 335 335 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. … … 339 339 * @see org.greenstone.gatherer.Dictionary 340 340 */ 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 341 private String get(String key, String args[]) { 342 if(key.indexOf('.') == -1) { 343 key = "CDM.FormatManager." + key; 344 } 345 return gatherer.dictionary.get(key, args); 346 } 347 private class Control 348 extends JPanel { 349 /** Do we ignore selection changing events (mainly because we're generating them!) */ 350 private boolean ignore = false; 351 private boolean new_entry = true; 352 private boolean ready = false; 353 private ButtonGroup button_group = null; 354 private CardLayout card_layout = null; 355 private Dimension LABEL_SIZE = new Dimension(175,25); 356 private Format current_format = null; 357 private String view_type = "custom"; 358 private JButton add = null; 359 private JButton insert = null; 360 private JButton preview = null; 361 private JButton remove = null; 362 private JComboBox feature = null; 363 private JComboBox part = null; 364 private JComboBox special = null; 365 private JLabel editor_label = null; 366 private JLabel feature_label = null; 367 private JLabel format_list_label = null; 368 private JLabel part_label = null; 369 private JLabel special_label = null; 370 private JLabel title = null; 371 private JLabel value_label = null; 372 private JList format_list = null; 373 private JPanel blank_pane = null; 374 private JPanel control_pane = null; 375 private JPanel editor_pane = null; 376 private JPanel feature_pane = null; 377 private JPanel format_list_pane = null; 378 private JPanel header_pane = null; 379 private JPanel inner_button_pane = null; 380 private JPanel inner_state_pane = null; 381 private JPanel inner_value_pane = null; 382 private JPanel options_pane = null; 383 private JPanel outer_button_pane = null; 384 private JPanel part_pane = null; 385 private JPanel special_pane = null; 386 private JPanel state_pane = null; 387 private JPanel value_pane = null; 388 private JPanel view_pane = null; 389 private JTextArea editor = null; 390 private JTextArea instructions = null; 391 private JTextField value = null; 392 private JToggleButton off = null; 393 private JToggleButton on = null; 394 private String BLANK = "blank"; 395 private String CUSTOM = "custom"; 396 private String FLAG = "flag"; 397 private String PARAM = "param"; 398 private Vector part_model = null; 399 private Vector special_model = null; 400 public Control() { 401 ArrayList feature_model = new ArrayList(); 402 402 // Add the set options 403 404 405 403 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) { 404 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i])); 405 } 406 406 // Now the classifiers. 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 407 for(int j = 0; j < manager.classifiers.size(); j++) { 408 feature_model.add(new Entry(manager.classifiers.getClassifier(j))); 409 } 410 Collections.sort(feature_model); 411 part_model = new Vector(); 412 part_model.add("");//get("Custom")); 413 part_model.add("DateList"); 414 part_model.add("HList"); 415 part_model.add("Invisible"); 416 part_model.add("VList"); 417 special_model = new Vector(); 418 special_model.add("[Text]"); 419 special_model.add("[link]"); 420 special_model.add("[/link]"); 421 special_model.add("[icon]"); 422 special_model.add("[num]"); 423 special_model.add("[parent():_]"); 424 special_model.add("[parent(Top):_]"); 425 special_model.add("[parent(All'_'):_]"); 426 Vector elements = gatherer.c_man.msm.getAssignedElements(); 427 for(int i = 0; i < elements.size(); i++) { 428 special_model.add("[" + ((ElementWrapper)elements.get(i)).toString() + "]"); 429 } 430 Collections.sort(special_model); 431 431 // Create 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 432 add = new JButton(get("Add")); 433 add.setEnabled(false); 434 blank_pane = new JPanel(); 435 button_group = new ButtonGroup(); 436 card_layout = new CardLayout(); 437 control_pane = new JPanel(); 438 editor = new JTextArea(); 439 editor.setCaretPosition(0); 440 editor.setLineWrap(true); 441 editor.setWrapStyleWord(true); 442 editor_label = new JLabel(get("Editor")); 443 editor_label.setHorizontalAlignment(JLabel.CENTER); 444 editor_pane = new JPanel(); 445 feature = new JComboBox(feature_model.toArray()); 446 feature.setEditable(true); 447 feature_label = new JLabel(get("Feature")); 448 feature_label.setPreferredSize(LABEL_SIZE); 449 format_list = new JList(model); 450 format_list_label = new JLabel(get("Assigned_Formats")); 451 format_list_pane = new JPanel(); 452 feature_pane = new JPanel(); 453 header_pane = new JPanel(); 454 inner_button_pane = new JPanel(); 455 inner_state_pane = new JPanel(); 456 inner_value_pane = new JPanel(); 457 insert = new JButton(get("Insert")); 458 instructions = new JTextArea(get("Instructions")); 459 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 460 instructions.setEditable(false); 461 instructions.setLineWrap(true); 462 instructions.setRows(4); 463 instructions.setWrapStyleWord(true); 464 off = new JToggleButton(get("Off")); 465 off.setSelected(false); 466 on = new JToggleButton(get("On")); 467 on.setSelected(true); 468 options_pane = new JPanel(); 469 outer_button_pane = new JPanel(); 470 part = new JComboBox(part_model); 471 part.setEditable(true); 472 part_label = new JLabel(get("Part")); 473 part_label.setPreferredSize(LABEL_SIZE); 474 part_pane = new JPanel(); 475 preview = new JButton(get("Preview")); 476 preview.setEnabled(false); 477 remove = new JButton(get("Remove")); 478 remove.setEnabled(false); 479 special = new JComboBox(special_model); 480 special_label = new JLabel(get("Special")); 481 special_label.setHorizontalAlignment(JLabel.CENTER); 482 special_pane = new JPanel(); 483 state_pane = new JPanel(); 484 title = new JLabel(get("Title")); 485 title.setHorizontalAlignment(JLabel.CENTER); 486 title.setOpaque(true); 487 value = new JTextField(); 488 value_label = new JLabel(get("Value")); 489 value_pane = new JPanel(); 490 view_pane = new JPanel(); 491 491 // Connect 492 493 494 495 496 497 498 499 500 501 502 503 504 492 add.addActionListener(new AddListener()); 493 button_group.add(on); 494 button_group.add(off); 495 editor.addKeyListener(new EditorListener()); 496 feature.addActionListener(new FeatureListener()); 497 format_list.addListSelectionListener(new FormatListListener()); 498 insert.addActionListener(new InsertListener()); 499 off.addActionListener(new StateListener()); 500 on.addActionListener(new StateListener()); 501 part.addActionListener(new PartListener()); 502 preview.addActionListener(new PreviewListener()); 503 remove.addActionListener(new RemoveListener()); 504 value.addKeyListener(new ValueListener()); 505 505 // Layout 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 506 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 507 508 header_pane.setLayout(new BorderLayout()); 509 header_pane.add(title, BorderLayout.NORTH); 510 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 511 512 format_list_label.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 513 514 format_list_pane.setLayout(new BorderLayout()); 515 format_list_pane.add(format_list_label, BorderLayout.NORTH); 516 format_list_pane.add(new JScrollPane(format_list), BorderLayout.CENTER); 517 518 feature_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 519 520 feature_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 521 feature_pane.setLayout(new BorderLayout()); 522 feature_pane.add(feature_label, BorderLayout.WEST); 523 feature_pane.add(feature, BorderLayout.CENTER); 524 525 part_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 526 527 part_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 528 part_pane.setLayout(new BorderLayout()); 529 part_pane.add(part_label, BorderLayout.WEST); 530 part_pane.add(part, BorderLayout.CENTER); 531 532 options_pane.setLayout(new GridLayout(2,1)); 533 options_pane.add(feature_pane); 534 options_pane.add(part_pane); 535 536 inner_state_pane = new JPanel(new GridLayout(1,2)); 537 inner_state_pane.add(on); 538 inner_state_pane.add(off); 539 540 state_pane.setLayout(new BorderLayout()); 541 state_pane.add(inner_state_pane, BorderLayout.NORTH); 542 state_pane.add(new JPanel(), BorderLayout.CENTER); 543 544 value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 545 546 inner_value_pane.setLayout(new BorderLayout()); 547 inner_value_pane.add(value_label, BorderLayout.WEST); 548 inner_value_pane.add(value, BorderLayout.CENTER); 549 550 value_pane.setLayout(new BorderLayout()); 551 value_pane.add(inner_value_pane, BorderLayout.NORTH); 552 value_pane.add(new JPanel(), BorderLayout.CENTER); 553 554 special_pane.setLayout(new GridLayout(3,1)); 555 special_pane.add(special_label); 556 special_pane.add(special); 557 special_pane.add(insert); 558 559 editor_pane.setLayout(new BorderLayout()); 560 editor_pane.add(editor_label, BorderLayout.NORTH); 561 editor_pane.add(new JScrollPane(editor), BorderLayout.CENTER); 562 editor_pane.add(special_pane, BorderLayout.EAST); 563 563 564 564 // Magic for view_pane card layout. 565 566 567 565 view_pane.setLayout(card_layout); 566 view_pane.add(editor_pane, CUSTOM); 567 view_pane.add(state_pane, FLAG); 568 568 //view_pane.add(value_pane, PARAM); 569 569 570 571 572 573 574 575 576 570 inner_button_pane.setLayout(new GridLayout(1,2)); 571 inner_button_pane.add(add); 572 inner_button_pane.add(remove); 573 574 outer_button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 575 outer_button_pane.setLayout(new GridLayout(1,1)); 576 outer_button_pane.add(inner_button_pane); 577 577 //outer_button_pane.add(preview); 578 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 579 control_pane.setLayout(new BorderLayout()); 580 control_pane.add(options_pane, BorderLayout.NORTH); 581 control_pane.add(view_pane, BorderLayout.CENTER); 582 control_pane.add(outer_button_pane, BorderLayout.SOUTH); 583 584 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 585 setLayout(new BorderLayout()); 586 add(header_pane, BorderLayout.NORTH); 587 add(format_list_pane, BorderLayout.CENTER); 588 add(control_pane, BorderLayout.SOUTH); 589 ready = true; 590 } 591 public void destroy() { 592 } 593 /** Overriden to ensure that the instructions pane is scrolled to the top. 594 594 */ 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 595 public void updateUI() { 596 if(ready) { 597 // Rebuild feature model. 598 ArrayList feature_model = new ArrayList(); 599 // Add the set options 600 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) { 601 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i])); 602 } 603 // Now the classifiers. 604 for(int j = 0; j < manager.classifiers.size(); j++) { 605 feature_model.add(new Entry(manager.classifiers.getClassifier(j))); 606 } 607 feature.setModel(new DefaultComboBoxModel(feature_model.toArray())); 608 if(instructions != null) { 609 instructions.setCaretPosition(0); 610 } 611 } 612 super.updateUI(); 613 } 614 615 616 617 /** Formats the formatting string so that it contains safe characters and isn't enclosed in speech marks etc. (Ironic eh?) 618 618 * @see java.lang.StringBuffer 619 619 */ 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 620 private String format(String raw) { 621 String safe = null; 622 if(raw != null && raw.length() > 0) { 623 StringBuffer temp = new StringBuffer(raw); 624 // Remove quotes at start and end. Look at my wiggly save three lines of code skills. 625 char start = ' '; 626 while(temp.length() > 0 && (start = temp.charAt(0)) == '\"' || start == '\'') { 627 temp.delete(0, 1); 628 } 629 int length = 0; 630 char end = ' '; 631 while((length = temp.length()) > 0 && (end = temp.charAt(length - 1)) == '\"' || end == '\'') { 632 temp.delete(length - 1, length); 633 } 634 // Now escape quotes within the format string 635 int quote_index = -1; 636 while((quote_index = temp.indexOf("\"", quote_index + 1)) != -1) { 637 temp.replace(quote_index, quote_index + 1, "\\\""); 638 quote_index = quote_index + 1; 639 } 640 // Done. 641 safe = temp.toString(); 642 } 643 return safe; 644 } 645 /** Remove safe characters from string replacing them with unsafe ones. 646 646 * @see java.lang.StringBuffer 647 647 */ 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 648 private String unformat(String safe) { 649 String raw = null; 650 if(safe != null && safe.length() > 0) { 651 StringBuffer temp = new StringBuffer(safe); 652 int quote_index = -1; 653 while((quote_index = temp.indexOf("\\\"")) != -1) { 654 temp.replace(quote_index, quote_index + 2, "\""); 655 } 656 raw = temp.toString(); 657 } 658 return raw; 659 } 660 /** Listens for clicks on the add button, and if the relevant details are provided adds a new format. */ 661 private class AddListener 662 implements ActionListener { 663 public void actionPerformed(ActionEvent event) { 664 ignore = true; 665 Entry entry = (Entry)feature.getSelectedItem(); 666 Object f = entry.getFeature(); 667 String p = (String)part.getSelectedItem(); 668 if(view_type.equals(FLAG)) { 669 current_format = new Format(f, p, on.isSelected()); 670 } 671 else { 672 current_format = new Format(f, p, format(Utility.stripNL(editor.getText()))); 673 } 674 addFormat(current_format); 675 add.setEnabled(false); 676 remove.setEnabled(true); 677 // Update list selection 678 format_list.setSelectedValue(current_format, true); 679 new_entry = false; 680 ignore = false; 681 } 682 } 683 private class EditorListener 684 extends KeyAdapter { 685 public void keyReleased(KeyEvent event) { 686 String safe = format(editor.getText()); 687 if(!ignore && current_format != null) { 688 // We have just performed an edit. Immediately update the format and model. 689 if(safe != null) { 690 current_format.setValue(safe); 691 } 692 else { 693 current_format.setValue(""); 694 } 695 gatherer.c_man.configurationChanged(); 696 model.refresh(); 697 } 698 Entry entry = (Entry) feature.getSelectedItem(); 699 String name = entry.toString(); 700 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && safe != null && safe.length() != 0 && new_entry) { 701 add.setEnabled(true); 702 } 703 else { 704 add.setEnabled(false); 705 } 706 } 707 } 708 /** This object provides a wrapping around an entry in the format target control, which is tranparent as to whether it is backed by a String or a Classifier. */ 709 private class Entry 710 implements Comparable { 711 private Classifier classifier = null; 712 private CustomClassifier custom_classifier = null; 713 private String text = null; 714 public Entry(Object object) { 715 if(object instanceof Classifier) { 716 classifier = (Classifier)object; 717 } 718 if(object instanceof CustomClassifier) { 719 custom_classifier = (CustomClassifier)object; 720 } 721 else if(object instanceof String) { 722 text = (String)object; 723 } 724 else { 725 text = ""; 726 } 727 } 728 public Entry(String text) { 729 this.text = text; 730 } 731 public int compareTo(Object object) { 732 if(object == null) { 733 return 1; 734 } 735 if(toString() == null) { 736 return -1; 737 } 738 else { 739 String object_str = object.toString(); 740 if(object_str == null) { 741 return 1; 742 } 743 return toString().compareTo(object_str); 744 } 745 } 746 public boolean equals(Object object) { 747 if(compareTo(object) == 0) { 748 return true; 749 } 750 return false; 751 } 752 public Classifier getClassifier() { 753 return classifier; 754 } 755 public CustomClassifier getCustomClassifier() { 756 return custom_classifier; 757 } 758 public Object getFeature() { 759 if(classifier != null) { 760 return classifier; 761 } 762 return text; 763 } 764 public String toString() { 765 if(classifier != null) { 766 String name = classifier.toString(); 767 return name.substring(9); 768 } 769 if(custom_classifier != null) { 770 String name = custom_classifier.toString(); 771 return name;//.substring(17); 772 } 773 return text; 774 } 775 } 776 private class FeatureListener 777 implements ActionListener { 778 public void actionPerformed(ActionEvent event) { 779 if(!ignore) { 780 current_format = null; 781 Entry entry = (Entry) feature.getSelectedItem(); 782 String name = entry.toString(); 783 int type = Format.getType(name); 784 switch(type) { 785 case Format.FLAG: 786 // Flags first. 787 part.setEnabled(false); 788 part_pane.remove(part); 789 card_layout.show(view_pane, FLAG); 790 view_type = FLAG; 791 add.setEnabled(true); // One of the options must be selected. 792 break; 793 default: 794 part.setEnabled(true); 795 part_pane.add(part, BorderLayout.CENTER); 796 card_layout.show(view_pane, CUSTOM); 797 view_type = CUSTOM; 798 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) { 799 add.setEnabled(true); 800 } 801 else { 802 add.setEnabled(false); 803 } 804 } 805 control_pane.updateUI(); 806 new_entry = true; 807 } 808 } 809 } 810 private class FormatListListener 811 implements ListSelectionListener { 812 public void valueChanged(ListSelectionEvent event) { 813 if(!ignore) { 814 if(!format_list.isSelectionEmpty()) { 815 ignore = true; 816 current_format = (Format)format_list.getSelectedValue(); 817 // Try to match the target, remembering the entries within are Entry's 818 Entry an_entry = new Entry(current_format.getFeature()); 819 feature.setSelectedItem(an_entry); 820 // Try to match the part. 821 part.setSelectedItem(current_format.getPart()); 822 // Now use type to determine what controls are visible, and what have initial values. 823 switch(current_format.getType()) { 824 case Format.FLAG: 825 // Flags first. 826 part.setEnabled(false); 827 part_pane.remove(part); 828 card_layout.show(view_pane, FLAG); 829 view_type = FLAG; 830 // Initial value 831 on.setSelected(current_format.getState()); 832 off.setSelected(!current_format.getState()); 833 add.setEnabled(false); // Can only update 834 break; 835 default: 836 part.setEnabled(true); 837 part_pane.add(part, BorderLayout.CENTER); 838 card_layout.show(view_pane, CUSTOM); 839 view_type = CUSTOM; 840 // Initial value 841 editor.setText(unformat(current_format.getValue())); 842 add.setEnabled(false); 843 } 844 control_pane.updateUI(); 845 ignore = false; 846 preview.setEnabled(true); 847 remove.setEnabled(true); 848 new_entry = false; 849 } 850 else { 851 preview.setEnabled(false); 852 remove.setEnabled(false); 853 } 854 } 855 } 856 } 857 private class InsertListener 858 implements ActionListener { 859 public void actionPerformed(ActionEvent event) { 860 editor.insert((String)special.getSelectedItem(), editor.getCaretPosition()); 861 } 862 } 863 private class PartListener 864 implements ActionListener { 865 public void actionPerformed(ActionEvent event) { 866 if(!ignore) { 867 current_format = null; 868 Entry entry = (Entry) feature.getSelectedItem(); 869 String name = entry.toString(); 870 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) { 871 add.setEnabled(true); 872 } 873 else { 874 add.setEnabled(false); 875 } 876 new_entry = true; 877 } 878 } 879 } 880 private class PreviewListener 881 implements ActionListener { 882 public void actionPerformed(ActionEvent event) { 883 } 884 } 885 private class RemoveListener 886 implements ActionListener { 887 public void actionPerformed(ActionEvent event) { 888 if(!format_list.isSelectionEmpty()) { 889 removeFormat((Format)format_list.getSelectedValue()); 890 // Change buttons 891 add.setEnabled(true); 892 remove.setEnabled(false); 893 } 894 } 895 } 896 private class StateListener 897 implements ActionListener { 898 public void actionPerformed(ActionEvent event) { 899 if(!ignore && current_format != null) { 900 // We have just performed an edit. Immediately update the format and model. 901 current_format.setState(on.isSelected()); 902 model.refresh(); 903 } 904 } 905 } 906 private class ValueListener 907 extends KeyAdapter { 908 public void keyReleased(KeyEvent event) { 909 if(!ignore && current_format != null) { 910 // We have just performed an edit. Immediately update the format and model. 911 current_format.setValue(value.getText()); 912 model.refresh(); 913 } 914 } 915 } 916 } 917 917 } -
trunk/gli/src/org/greenstone/gatherer/cdm/GUI.java
r4293 r4366 86 86 */ 87 87 public class GUI 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 88 extends JPanel { 89 /** A reference to the collection manager, as it is here that all of the other data members and managers reside. */ 90 private CollectionDesignManager manager = null; 91 /** The controls used to modify the general options. */ 92 private Control controls = null; 93 /** A tree to serve as a 'table of contents' for this design tool. We decided on a tree rather than a list, as it allows us to break sections into subsections if they become to complicated. */ 94 private DesignTree tree = null; 95 /** A reference to the Gatherer. */ 96 private Gatherer gatherer = null; 97 /** The title located just above the content tree. */ 98 private JLabel title = null; 99 /** The panel containing both the title and the tree. */ 100 private JPanel tree_pane = null; 101 /** The panel apon which is rendered the currently selected section screen. */ 102 private JPanel view = null; 103 /** The available subscreens. */ 104 static final public String CONTENTS[] = {"General", "Indexes", "Subcollections", "Languages", "Plugins", "Classifiers", "Formats", "MetadataSets"}; 105 /** The preferred size of the collection design module screen real-estate. */ 106 static final private Dimension SIZE = new Dimension(760, 500); 107 /** Constructor. 108 * @see DesignTree 109 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 110 */ 111 public GUI(Gatherer gatherer, CollectionDesignManager manager) { 112 super(); 113 // Assignments 114 this.gatherer = gatherer; 115 this.manager = manager; 116 // Creation 117 this.title = new JLabel(get("Design_Topics")); 118 this.tree = new DesignTree(); 119 this.tree_pane = new JPanel(); 120 this.view = getControls(); 121 // Connect 122 tree.addTreeSelectionListener(new TreeListener()); 123 tree_pane.setLayout(new BorderLayout()); 124 tree_pane.add(title, BorderLayout.NORTH); 125 tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER); 126 // Layout 127 setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); 128 setLayout(new BorderLayout()); 129 add(tree_pane, BorderLayout.WEST); 130 add(view, BorderLayout.CENTER); 131 } 132 /** Mark the current set of controls as invalid. If they are needed again in the future new controls will be generate. 133 133 * @see org.greenstone.gatherer.cdm.GUI.Control 134 134 */ 135 136 137 138 139 140 141 135 public void invalidateControls() { 136 if(controls != null) { 137 controls.destroy(); 138 } 139 controls = null; 140 } 141 /** Force the display to show a certain pane of controls. 142 142 * @param type A <strong>String</strong> giving the name of the submanager view we wish to display. 143 143 */ 144 145 146 147 144 public void setSelectedView(String type) { 145 tree.setSelectedView(type); 146 } 147 /** Overrides the normal updateUI to ensure that the current view also recieves an update message. 148 148 */ 149 150 151 152 153 154 155 149 public void updateUI() { 150 if(view != null) { 151 view.updateUI(); 152 } 153 super.updateUI(); 154 } 155 /**Overridden so we can exit when window is closed 156 156 * @param event A <strong>WindowsEvent</strong> that encapsulates all the information gathered about the event that called this method. 157 157 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 158 158 */ 159 160 161 162 163 164 165 159 protected void processWindowEvent(WindowEvent event) { 160 if(event.getID() == WindowEvent.WINDOW_CLOSING) { 161 manager.save(); 162 System.exit(0); 163 } 164 } 165 /** Overloaded to call get with both a key and an empty argument array. 166 166 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 167 167 * @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. 168 168 */ 169 170 171 172 169 private String get(String key) { 170 return get(key, null); 171 } 172 /** 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> 173 173 * 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>. 174 174 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. … … 178 178 * @see org.greenstone.gatherer.Dictionary 179 179 */ 180 181 182 183 184 185 186 180 private String get(String key, String args[]) { 181 if(key.indexOf('.') == -1) { 182 key = "CDM.GUI." + key; 183 } 184 return gatherer.dictionary.get(key, args); 185 } 186 /** Because the CollectionDesignManager has enough to do, this class is resposible for generating the controls for the general options, all of which are stored in the aforementioned manager. 187 187 * @return A <strong>JPanel</strong> containing controls for editing the general options. 188 188 */ 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 189 private JPanel getControls() { 190 if(controls == null) { 191 controls = new Control(); 192 } 193 return controls; 194 } 195 /** This class represents the visual component of the general options stored in the CollectionDesignManager. */ 196 private class Control 197 extends JPanel { 198 /** The collection metadata representing the extra or description of the collection. */ 199 private CollectionMeta collection_extra_data = null; 200 /** The collection metadata representing the name of the collection. */ 201 private CollectionMeta collection_name_data = null; 202 /** The collection metadata representing the icon file location of the collection. */ 203 private CollectionMeta icon_collection_data = null; 204 /** The default size of label on this control. */ 205 private Dimension LABEL_SIZE = new Dimension(200,25); 206 /** The checkbox controlling public access to the collection. */ 207 private JCheckBox access = null; 208 /** The checkbox controlling the state of the collection. */ 209 private JCheckBox beta = null; 210 /** The label denoting the collection extra area. */ 211 private JLabel collection_extra_label = null; 212 /** The label denoting the collection extra language (Default). */ 213 private JLabel collection_extra_language = null; 214 /** The label denoting the collection name area. */ 215 private JLabel collection_name_label = null; 216 /** The label denoting the name language (Default). */ 217 private JLabel collection_name_language = null; 218 /** The label denoting the collection icon area. */ 219 private JLabel icon_collection_label = null; 220 /** The label denoting the icon language (Default). */ 221 private JLabel icon_collection_language = null; 222 /** The label which serves as the title of this view. */ 223 private JLabel title = null; 224 /** The panel containing the access controls. */ 225 private JPanel access_pane = null; 226 /** The panel containing the state controls. */ 227 private JPanel beta_pane = null; 228 /** The central pane that will contain the view. */ 229 private JPanel central_pane = null; 230 /** A panel used to affect internal layout of the collection extra area. */ 231 private JPanel collection_extra_inner_pane = null; 232 /** The panel containing the collection extra area. */ 233 private JPanel collection_extra_pane = null; 234 /** A panel used to affect internal layout of the collection name area. */ 235 private JPanel collection_name_inner_pane = null; 236 /** The panel containing the collection name area. */ 237 private JPanel collection_name_pane = null; 238 /** A panel used to affect internal layout of the collection icon area. */ 239 private JPanel icon_collection_inner_pane = null; 240 /** The panel containing the icon area. */ 241 private JPanel icon_collection_pane = null; 242 /** A text area used to display the inline help for this manager. */ 243 private JTextArea instructions = null; 244 /** The text field used to edit the collections title. */ 245 private JTextField collection_name = null; 246 /** The text field used to edit the file name of the collections icon. */ 247 private JTextField icon_collection = null; 248 /** A text area used to modify the collection description. */ 249 private JTextArea collection_extra = null; 250 /** Constructor. 251 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 252 * @see org.greenstone.gatherer.cdm.CollectionMeta 253 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 254 * @see org.greenstone.gatherer.cdm.Language 255 * @see org.greenstone.gatherer.cdm.LanguageManager 256 */ 257 public Control() { 258 super(); 259 collection_extra_data = manager.collectionmetadatum.getCollectionExtra(); 260 collection_name_data = manager.collectionmetadatum.getCollectionName(); 261 icon_collection_data = manager.collectionmetadatum.getIconCollection(); 262 262 // Creation. 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 263 access = new JCheckBox(get("CDM.General.Access")); 264 access.setSelected(manager.public_col); 265 access_pane = new JPanel(); 266 beta = new JCheckBox(get("CDM.General.Beta")); 267 beta.setSelected(manager.beta); 268 beta_pane = new JPanel(); 269 central_pane = new JPanel(); 270 collection_extra = new JTextArea(); 271 collection_extra_inner_pane = new JPanel(); 272 collection_extra_label = new JLabel(get("CDM.General.Collection_Extra")); 273 collection_extra_label.setHorizontalAlignment(JLabel.CENTER); 274 collection_extra_language = new JLabel(); 275 collection_extra_pane = new JPanel(); 276 if(collection_extra_data != null) { 277 collection_extra.setText(collection_extra_data.getValue()); 278 Language language = null; 279 if((language = collection_extra_data.getLanguage()) != null) { 280 collection_extra_language.setText(language.toString()); 281 } 282 else { 283 collection_extra_language.setText(manager.languages.getDefaultLanguage().toString()); 284 } 285 } 286 collection_name = new JTextField(); 287 collection_name_inner_pane = new JPanel(); 288 collection_name_label = new JLabel(get("CDM.General.Collection_Name")); 289 collection_name_label.setPreferredSize(LABEL_SIZE); 290 collection_name_language = new JLabel(); 291 collection_name_pane = new JPanel(); 292 if(collection_name_data != null) { 293 collection_name.setText(collection_name_data.getValue()); 294 Language language = null; 295 if((language = collection_name_data.getLanguage()) != null) { 296 collection_name_language.setText(language.toString()); 297 } 298 else { 299 collection_name_language.setText(manager.languages.getDefaultLanguage().toString()); 300 } 301 } 302 icon_collection = new JTextField(); 303 icon_collection_inner_pane = new JPanel(); 304 icon_collection_label = new JLabel(get("CDM.General.Icon_Collection")); 305 icon_collection_label.setPreferredSize(LABEL_SIZE); 306 icon_collection_language = new JLabel(); 307 icon_collection_pane = new JPanel(); 308 if(icon_collection_data != null) { 309 icon_collection.setText(icon_collection_data.getValue()); 310 Language language = null; 311 if((language = icon_collection_data.getLanguage()) != null) { 312 icon_collection_language.setText(language.toString()); 313 } 314 else { 315 icon_collection_language.setText(manager.languages.getDefaultLanguage().toString()); 316 } 317 } 318 instructions = new JTextArea(get("CDM.General.Instructions")); 319 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 320 instructions.setEditable(false); 321 instructions.setLineWrap(true); 322 instructions.setRows(4); 323 instructions.setWrapStyleWord(true); 324 JPanel lower = new JPanel(); 325 title = new JLabel(get("CDM.General.Title")); 326 title.setHorizontalAlignment(JLabel.CENTER); 327 title.setOpaque(true); 328 JPanel upper = new JPanel(); 329 329 // Add listeners. 330 331 332 333 334 335 330 access.addActionListener(new AccessListener()); 331 beta.addActionListener(new BetaListener()); 332 collection_extra.addKeyListener(new ChangeListener(collection_extra_data, collection_extra, "collectionextra")); 333 collection_name.addKeyListener(new ChangeListener(collection_name_data, collection_name, "collectionname")); 334 collection_name.addKeyListener(new CollectionTitleListener()); 335 icon_collection.addKeyListener(new ChangeListener(icon_collection_data, icon_collection, "iconcollection")); 336 336 // Layout 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 337 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 338 339 upper.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 340 upper.setLayout(new BorderLayout()); 341 upper.add(title, BorderLayout.NORTH); 342 upper.add(new JScrollPane(instructions), BorderLayout.CENTER); 343 344 collection_name_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 345 collection_name_language.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); 346 347 collection_name_inner_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 348 collection_name_inner_pane.setLayout(new BorderLayout()); 349 collection_name_inner_pane.add(collection_name); 350 351 collection_name_pane.setLayout(new BorderLayout()); 352 collection_name_pane.add(collection_name_label, BorderLayout.WEST); 353 collection_name_pane.add(collection_name_inner_pane, BorderLayout.CENTER); 354 collection_name_pane.add(collection_name_language, BorderLayout.EAST); 355 356 icon_collection_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 357 icon_collection_language.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); 358 359 icon_collection_inner_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 360 icon_collection_inner_pane.setLayout(new BorderLayout()); 361 icon_collection_inner_pane.add(icon_collection, BorderLayout.CENTER); 362 363 icon_collection_pane.setLayout(new BorderLayout()); 364 icon_collection_pane.add(icon_collection_label, BorderLayout.WEST); 365 icon_collection_pane.add(icon_collection_inner_pane, BorderLayout.CENTER); 366 icon_collection_pane.add(icon_collection_language, BorderLayout.EAST); 367 368 access_pane.add(access); 369 370 beta_pane.add(beta); 371 372 lower.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 373 lower.setLayout(new GridLayout(6,1)); 374 lower.add(manager.creator.getControls()); 375 lower.add(manager.maintainer.getControls()); 376 lower.add(access_pane); 377 lower.add(beta_pane); 378 lower.add(collection_name_pane); 379 lower.add(icon_collection_pane); 380 381 collection_extra_inner_pane.setLayout(new BorderLayout()); 382 collection_extra_inner_pane.add(collection_extra_label, BorderLayout.WEST); 383 collection_extra_inner_pane.add(collection_extra_language, BorderLayout.EAST); 384 385 collection_extra.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 386 387 collection_extra_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 388 collection_extra_pane.setLayout(new BorderLayout()); 389 collection_extra_pane.add(collection_extra_inner_pane, BorderLayout.NORTH); 390 collection_extra_pane.add(new JScrollPane(collection_extra), BorderLayout.CENTER); 391 392 central_pane.setLayout(new BorderLayout()); 393 central_pane.add(lower, BorderLayout.NORTH); 394 central_pane.add(collection_extra_pane, BorderLayout.CENTER); 395 396 setLayout(new BorderLayout()); 397 add(upper, BorderLayout.NORTH); 398 add(central_pane, BorderLayout.CENTER); 399 } 400 /** Destructor. 401 401 */ 402 403 404 402 public void destroy() { 403 } 404 /** We override the updateUI method so that we can ensure we are scrolled to the top of the instructions box first. 405 405 */ 406 407 408 409 410 411 412 413 414 415 406 public void updateUI() { 407 if(instructions != null) { 408 instructions.setCaretPosition(0); 409 } 410 if(collection_extra != null) { 411 collection_extra.setCaretPosition(0); 412 } 413 super.updateUI(); 414 } 415 /** Detect when the collection access changes, and update the configuration as necessary. 416 416 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 417 417 */ 418 419 420 421 422 423 424 418 private class AccessListener 419 implements ActionListener { 420 public void actionPerformed(ActionEvent event) { 421 manager.public_col = access.isSelected(); 422 } 423 } 424 /** Detect when the collection state changes and update the configuration as necessary. 425 425 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 426 426 */ 427 428 429 430 431 432 433 434 435 427 private class BetaListener 428 implements ActionListener { 429 public void actionPerformed(ActionEvent event) { 430 manager.beta = beta.isSelected(); 431 } 432 } 433 /** This class listens for any changes its registered controls, including enter actions and keys typed, and updates the fields stored in CollectionDesignManager as necessary. */ 434 private class ChangeListener 435 extends KeyAdapter { 436 436 /** The collection metadata to alter. */ 437 437 private CollectionMeta metadata = null; 438 438 /** The control we are watching for changes. */ 439 439 private JTextComponent component = null; 440 440 /** The name of the metadata to monitor. */ 441 441 private String name = null; 442 442 /** Construstor. 443 * @param metadata The <strong>CollectionMeta</strong> to alter.444 * @param component The <strong>JTextComponent</strong> we are monitoring for changes.445 * @param name The name of the metadata as a <strong>String</strong>.443 * @param metadata The <strong>CollectionMeta</strong> to alter. 444 * @param component The <strong>JTextComponent</strong> we are monitoring for changes. 445 * @param name The name of the metadata as a <strong>String</strong>. 446 446 */ 447 448 449 450 451 452 453 454 455 456 447 public ChangeListener(CollectionMeta metadata, JTextComponent component, String name) { 448 this.component = component; 449 this.metadata = metadata; 450 if(metadata != null) { 451 this.name = metadata.getName().toString(); 452 } 453 else { 454 this.name = name; 455 } 456 } 457 457 /** Any extension of a KeyAdapter may override this method so we can be informed when a key has been released (ie after the character etc has been added). In this case we want to update the appropriate metadata, however there are three distinct cases to consider:<br> 1. A simple update of the value field is all that is necessary,<br> 2. The metadata exists but it currently has no language, so we must set the language to default and update the value, or<br> 3. The metadata doesn't exist so we'll create a new one with the specified name, default language and new value.<br>This final case is highly unlikely but still possible. 458 458 * @param event An <strong>Event</strong> containing information about the key release. … … 465 465 * @see org.greenstone.gatherer.collection.CollectionManager 466 466 */ 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 467 public void keyReleased(KeyEvent event) { 468 if(metadata != null) { 469 Language language = metadata.getLanguage(); 470 if(language == null) { 471 metadata.update(metadata.getName(), manager.languages.getDefaultLanguage(), component.getText()); 472 } 473 else { 474 metadata.update(metadata.getName(), metadata.getLanguage(), component.getText()); 475 } 476 } 477 else { 478 metadata = new CollectionMeta(manager, name, manager.languages.getDefaultLanguage(), component.getText()); 479 manager.collectionmetadatum.addMetadata(metadata); 480 } 481 gatherer.c_man.configurationChanged(); 482 } 483 } 484 /** Listens for changes to the collection name and updates the windows title bar appropriately and collection file. */ 485 private class CollectionTitleListener 486 extends KeyAdapter { 487 487 /** Any extension of a KeyAdapter may override this method so we can be informed when a key has been released (ie after the character etc has been added). When such a change occurs update the main guis title bar to reflect the change and fix the collection name. 488 488 * @param event An <strong>Event</strong> containing information about the key release. 489 489 */ 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 490 public void keyReleased(KeyEvent event) { 491 String name = collection_name.getText(); 492 if(name != null && name.length() > 0) { 493 gatherer.g_man.setTitle(Utility.PROGRAM_NAME + ":\"" + name + "\""); 494 gatherer.c_man.getCollection().setTitle(name); 495 } 496 } 497 } 498 } 499 /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */ 500 private class DesignTree 501 extends JTree { 502 private DesignNode root = null; 503 /** Constructor. Automatically generates all of the nodes, in the order of CONTENTS. 504 */ 505 public DesignTree() { 506 super(); 507 root = new DesignNode("Root"); 508 this.setModel(new DefaultTreeModel(root)); 509 509 // Now add the design categories. 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 510 for(int i = 0; i < CONTENTS.length; i++) { 511 root.add(new DesignNode(CONTENTS[i])); 512 } 513 expandRow(0); 514 setRootVisible(false); 515 setSelectionRow(0); 516 } 517 /** Set the current view to the one specified. 518 * @param type The name of the desired view as a <strong>String</strong>. 519 * @see org.greenstone.gatherer.Gatherer 520 * @see org.greenstone.gatherer.cdm.GUI.DesignNode 521 */ 522 public void setSelectedView(String type) { 523 type = gatherer.get(type); 524 for(int i = 0; i < root.getChildCount(); i++) { 525 DesignNode child = (DesignNode) root.getChildAt(i); 526 if(child.toString().equals(type)) { 527 TreePath path = new TreePath(child.getPath()); 528 setSelectionPath(path); 529 } 530 } 531 } 532 } 533 /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */ 534 private class DesignNode 535 extends DefaultMutableTreeNode { 536 /** Constructor. 537 * @param object The <strong>Object</strong> assigned to this node. 538 538 */ 539 540 541 542 539 public DesignNode(String object) { 540 super(object); 541 } 542 /** Retrieve a textual representation of the object. 543 543 * @return A <strong>String</strong>. 544 544 */ 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 545 public String toString() { 546 return get((String)getUserObject()); 547 } 548 } 549 /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */ 550 private class TreeListener 551 implements TreeSelectionListener { 552 /** Called whenever the selection changes, we must update the view so it matches the node selected. 553 * @param event A <strong>TreeSelectionEvent</strong> containing more information about the tree selection. 554 * @see org.greenstone.gatherer.cdm.ClassifierManager 555 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 556 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 557 * @see org.greenstone.gatherer.cdm.FormatManager 558 * @see org.greenstone.gatherer.cdm.LanguageManager 559 * @see org.greenstone.gatherer.cdm.MetadataSetManager 560 * @see org.greenstone.gatherer.cdm.SubcollectionManager 561 * @see org.greenstone.gatherer.cdm.PlugInManager 562 */ 563 public void valueChanged(TreeSelectionEvent event) { 564 if(!tree.isSelectionEmpty()) { 565 TreePath path = tree.getSelectionPath(); 566 DesignNode node = (DesignNode)path.getLastPathComponent(); 567 String type = (String)node.getUserObject(); 568 // Assure user 569 // Build and change panes. 570 remove(view); 571 view.hasFocus(); // Trigger pending information update events. 572 if(type.equals("General")) { 573 view = getControls(); 574 } 575 else if(type.equals("Indexes")) { 576 view = manager.indexes.getControls(); 577 } 578 else if(type.equals("Subcollections")) { 579 view = manager.subcollections.getControls(); 580 } 581 else if(type.equals("Languages")) { 582 view = manager.languages.getControls(); 583 } 584 else if(type.equals("Plugins")) { 585 view = manager.plugins.getControls(); 586 } 587 else if(type.equals("Classifiers")) { 588 view = manager.classifiers.getControls(); 589 } 590 else if(type.equals("Formats")) { 591 view = manager.formats.getControls(); 592 } 593 else if(type.equals("MetadataSets")) { 594 view = manager.metadatasets.getControls(); 595 } 596 add(view, BorderLayout.CENTER); 597 view.updateUI(); 598 // Ready 599 } 600 } 601 } 602 602 } -
trunk/gli/src/org/greenstone/gatherer/cdm/Index.java
r4293 r4366 57 57 import org.w3c.dom.Element; 58 58 /** This class encapsulates a single indexing pair. 59 * @author John Thompson, Greenstone Digital Library, University of Waikato60 * @version 2.261 */59 * @author John Thompson, Greenstone Digital Library, University of Waikato 60 * @version 2.2 61 */ 62 62 public class Index 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 63 implements Comparable { 64 /** A refernce to the main manager for access to other sub-managers. */ 65 private CollectionDesignManager manager = null; 66 /** The level of this index. */ 67 private int level = 0; 68 /** The sources for data this index is built apon, which are either fully qualified metadata element names or 'text'. */ 69 private Vector sources = null; 70 /** An element in the index level enumeration. */ 71 static public final int DOCUMENT = 0; 72 /** An element in the index level enumeration. */ 73 static public final int PARAGRAPH = 1; 74 /** An element in the index level enumeration. */ 75 static public final int SECTION = 2; 76 /** An values of items in the index level enumeration. */ 77 static public final String LEVEL[] = {"document","paragraph","section"}; 78 /** Constructor. 79 * @param level The level of this index as a <strong>String</string>. 80 * @param metadata The fully qualified name of the metadata this index is built on as a <strong>String</strong>, or <i>null</i> in which case the data defaults to "text". 81 */ 82 public Index(int level, Vector sources, CollectionDesignManager manager) { 83 this.level = level; 84 this.manager = manager; 85 this.sources = sources; 86 if(this.sources == null) { 87 this.sources = new Vector(); 88 this.sources.add("text"); 89 } 90 } 91 /** Method to compare two indexes. 92 92 * @param object The other index as an <strong>Object</strong>. 93 93 * @return An <i>int</i> which indicates how the indexes compare. 94 94 * @see java.lang.String 95 95 */ 96 97 98 99 100 101 102 103 96 public int compareTo(Object object) { 97 if(object instanceof Index) { 98 Index index = (Index) object; 99 return toString(false).compareTo(index.toString(false)); 100 } 101 return toString(false).compareTo(object.toString()); 102 } 103 /** Method to test for the equality of two indexes. 104 104 * @param object The other index as an <strong>Object</strong>. 105 105 * @return A <i>boolean</i> which is <i>true</i> if the two indexes are equal, <i>false</i> otherwise. 106 106 */ 107 108 109 110 111 112 113 107 public boolean equals(Object object) { 108 if(compareTo(object) == 0) { 109 return true; 110 } 111 return false; 112 } 113 /** Method to get the data source of this index. 114 114 * @return A <strong>String</string> which is a comma separated list of either fully qualified names of an assigned metadata elements, or "text". 115 115 */ 116 117 118 119 120 121 122 123 124 125 126 127 116 public String getData() { 117 String result = ""; 118 Collections.sort(sources, new IndexComparator()); 119 for(int i = 0; i < sources.size(); i++) { 120 result = result + sources.get(i).toString(); 121 if(i < sources.size() - 1) { 122 result = result + ","; 123 } 124 } 125 return result; 126 } 127 /** Method to get the value of level. 128 128 * @return The value of level as a <strong>String</strong>. 129 129 */ 130 131 132 133 130 public int getLevel() { 131 return level; 132 } 133 /** Method to get the value of metadata. 134 134 * @return The value of metadata as an <strong>Vector</strong>. 135 135 */ 136 137 138 139 136 public Vector getMetadata() { 137 return sources; 138 } 139 /** Method to retrieve this indexes name. 140 140 * @return A <strong>String</strong>. 141 141 */ 142 143 144 145 146 147 148 149 150 151 152 153 142 public String getName() { 143 if(manager != null) { 144 String name = LEVEL[level] + ":" + getData(); 145 Language language = manager.languages.getDefaultLanguage(); 146 CollectionMeta metadata = manager.collectionmetadatum.getMetadata(name, language, true); 147 if(metadata != null) { 148 return metadata.getValue(); 149 } 150 } 151 return ""; 152 } 153 /** Method to turn this object into a string representation ready to be placed in the collection configuration file. 154 154 * @return A <strong>String</strong> containing the information of this class. 155 155 */ 156 157 158 159 156 public String toString() { 157 return LEVEL[level] + ":" + getData() + " \"" + getName() + "\""; 158 } 159 /** Retrieve a textual representation of this index. 160 160 * @param show_name <i>true</i> if you want the name of this index, <i>false</i> for the gsdl index reference. 161 161 */ 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 162 public String toString(boolean show_name) { 163 if(show_name) { 164 return getName(); 165 } 166 return LEVEL[level] + ":" + getData(); 167 } 168 /** A custom comparator for comparing Indexes. */ 169 private class IndexComparator 170 implements Comparator { 171 /** Method to compare two objects, which may be either indexes or strings. 172 * @param object1 One object as an <strong>Object</strong>. 173 * @param object2 Another object as an <strong>Object</strong>. 174 * @return An <i>int</i> which indicates how they compare. 175 * @see java.lang.String 176 */ 177 public int compare(Object object1, Object object2) { 178 if(object1 instanceof Index && object2 instanceof Index) { 179 Index index1 = (Index)object1; 180 Index index2 = (Index)object2; 181 return index1.toString(false).compareTo(index2.toString(false)); 182 } 183 else if(object1 instanceof Index) { 184 Index index = (Index) object1; 185 return index.toString(false).compareTo(object2.toString()); 186 } 187 else if(object2 instanceof Index) { 188 Index index = (Index) object2; 189 return object1.toString().compareTo(index.toString(false)); 190 } 191 return object1.toString().compareTo(object2.toString()); 192 } 193 /** Method to test for the equality of two objects, which may be indexes or strings. 194 194 * @param object Another object as an <strong>Object</strong>. 195 195 * @return A <i>boolean</i> which is <i>true</i> if the two objects are equal, <i>false</i> otherwise. 196 196 */ 197 198 199 200 201 202 203 197 public boolean equals(Object object) { 198 if(compareTo(object) == 0) { 199 return true; 200 } 201 return false; 202 } 203 } 204 204 } -
trunk/gli/src/org/greenstone/gatherer/cdm/IndexManager.java
r4293 r4366 83 83 import org.w3c.dom.Element; 84 84 /** This class is resposible for storing the indexes which have been assigned to this collection and the default index, and providing methods for interacting with both these data pools. It also knows how to turn itself into a String as it would be displayed in the collection configuration file. 85 * @author John Thompson, Greenstone Digital Library, University of Waikato86 * @version 2.387 */85 * @author John Thompson, Greenstone Digital Library, University of Waikato 86 * @version 2.3 87 */ 88 88 public class IndexManager 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 89 extends DefaultListModel { 90 /** A reference to our creator, the collection design manager. */ 91 private CollectionDesignManager manager = null; 92 /** The controls for editing the indexes. */ 93 private Control controls = null; 94 /** A reference to ourselves so our inner methods have access. */ 95 private DefaultListModel model = null; 96 /** A reference to the Gatherer, for access to the Dictionary and messaging purposes. */ 97 private Gatherer gatherer = null; 98 /** The default index. */ 99 private Index default_index = null; 100 /** Constructor. 101 * @param gatherer A reference to the <strong>Gatherer</strong>. 102 * @param manager A reference to the <strong>CollectionDesignManager</strong>. 103 */ 104 public IndexManager(Gatherer gatherer, CollectionDesignManager manager) { 105 super(); 106 this.gatherer = gatherer; 107 this.manager = manager; 108 this.model = this; 109 } 110 /** Method to add a new index. 111 111 * @param index The <strong>Index</strong> to add. 112 112 * @see org.greenstone.gatherer.Gatherer 113 113 * @see org.greenstone.gatherer.collection.CollectionManager 114 114 */ 115 116 117 115 public void addIndex(Index index) { 116 if(!contains(index)) { 117 String index_str = index.toString(false).toLowerCase(); 118 118 // Add alphabetically. 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 119 for(int i = 0; i < size(); i++) { 120 Index sibling = (Index) get(i); 121 String sibling_str = sibling.toString(false).toLowerCase(); 122 int position = index_str.compareTo(sibling_str); 123 // Sibling is before index. 124 if(position > 0) { 125 // Carry on. 126 } 127 // Index is equal to, or before sibling. Insert it. 128 else if(position == 0 || position < 0) { 129 add(i, index); 130 gatherer.c_man.configurationChanged(); 131 return; 132 } 133 } 134 134 // If we got this far, we haven't inserted index, and we are out of model so. 135 136 137 138 139 140 141 142 135 addElement(index); 136 gatherer.c_man.configurationChanged(); 137 } 138 else { 139 JOptionPane.showMessageDialog(manager.gui, get("CDM.IndexManager.Index_Exists"), get("General.Warning"), JOptionPane.WARNING_MESSAGE); 140 } 141 } 142 /** Method to acquire the controls for editing the indexes. 143 143 * @return A <strong>JPanel</strong> containing the controls. 144 144 * @see org.greenstone.gatherer.cdm.IndexManager.Control 145 145 */ 146 147 148 149 150 151 152 146 public JPanel getControls() { 147 if(controls == null) { 148 controls = new Control(); 149 } 150 return controls; 151 } 152 /** Method to get the default index. 153 153 * @return The default <strong>Index</strong>. 154 154 */ 155 156 157 158 155 public Index getDefault() { 156 return default_index; 157 } 158 /** Method to retrieve a certain index, as referenced by an index number. 159 159 * @param index An <i>int</i> which indicates the position of the desired index. 160 160 * @return The <strong>Index</strong> at the given index, or <i>null</i> if no such index exists. 161 161 */ 162 163 164 165 166 167 168 162 public Index getIndex(int index) { 163 if(0 <= index && index < size()) { 164 return (Index)get(index); 165 } 166 return null; 167 } 168 /** Method to retrieve a certain index, given its name. 169 169 * @param name The name of the index as a <Strong>String</strong>. 170 170 * @return The <strong>Index</strong> that matches name, or <i>null</i> if no such index exists. 171 171 */ 172 173 174 175 176 177 178 179 180 181 182 183 184 172 public Index getIndex(String name) { 173 ///ystem.err.println("Searching for index " + name); 174 for(int i = 0; i < size(); i++) { 175 Index index = (Index) get(i); 176 if(index.toString(false).equals(name)) { 177 ///ystem.err.println("Found."); 178 return (Index)get(i); 179 } 180 } 181 ///ystem.err.println("No such index."); 182 return null; 183 } 184 /** A method to retrieve all of the indexes associated with this manager. 185 185 * @return A <strong>Vector</strong> of <strong>Index</strong>es. 186 186 */ 187 188 189 190 191 192 193 194 195 187 public Vector getIndexes() { 188 Vector indexes = new Vector(); 189 for(int i = 0; i < size(); i++) { 190 indexes.add(get(i)); 191 } 192 Collections.sort(indexes); 193 return indexes; 194 } 195 /** Mark the current set of controls, if any, as obsolete and deallocate them. If further need of the controls will cause new controls to be created. 196 196 * @see org.greenstone.gatherer.cdm.IndexManager.Control 197 197 */ 198 199 200 201 202 203 204 198 public void invalidateControls() { 199 if(controls != null) { 200 controls.destroy(); 201 } 202 controls = null; 203 } 204 /** Method that attempts to parse an index related command from the given command. If such a command is parsed, it is immediately registered with this manager. 205 205 * @param command The <strong>String</strong> to parse. 206 206 * @return A <i>boolean</i> which is <i>true</i> if a command was parsed, <i>false</i> otherwise. … … 208 208 * @see org.greenstone.gatherer.cdm.Index 209 209 */ 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 210 public boolean parse(String command) { 211 String temp = command.toLowerCase(); 212 if(temp.startsWith("indexes")) { 213 CommandTokenizer ct = new CommandTokenizer(command); 214 ct.nextToken(); // Throw away indexes. 215 while(ct.hasMoreTokens()) { 216 String entry = ct.nextToken(); 217 if(entry.indexOf(":") != -1) { 218 String level_str = entry.substring(0, entry.indexOf(":")); 219 String sources_raw = entry.substring(entry.indexOf(":") + 1); 220 Vector sources = new Vector(); 221 StringTokenizer st = new StringTokenizer(sources_raw, ","); 222 while(st.hasMoreTokens()) { 223 String token = st.nextToken(); 224 // We may have to replace old : with whatever namespace separator we are using. 225 token = token.replace(':', MSMUtils.NS_SEP); 226 sources.add(token); 227 } 228 for(int i = 0; i < Index.LEVEL.length; i++) { 229 if(level_str.equals(Index.LEVEL[i])) { 230 addIndex(new Index(i, sources, manager)); 231 } 232 } 233 } 234 else { 235 return false; 236 } 237 } 238 return true; 239 } 240 else if(temp.startsWith("defaultindex")) { 241 CommandTokenizer ct = new CommandTokenizer(command); 242 ct.nextToken(); 243 String entry = ct.nextToken(); 244 if(entry.indexOf(":") != -1) { 245 String level_str = entry.substring(0, entry.indexOf(":")); 246 String sources_raw = entry.substring(entry.indexOf(":") + 1); 247 Vector sources = new Vector(); 248 StringTokenizer st = new StringTokenizer(sources_raw, ","); 249 while(st.hasMoreTokens()) { 250 sources.add(st.nextToken()); 251 } 252 for(int i = 0; i < Index.LEVEL.length; i++) { 253 if(level_str.equals(Index.LEVEL[i])) { 254 setDefault(new Index(i, sources, manager)); 255 } 256 } 257 } 258 else { 259 return false; 260 } 261 return true; 262 } 263 return false; 264 } 265 266 public void removeAll() { 267 removeAllElements(); 268 default_index = null; 269 gatherer.c_man.configurationChanged(); 270 } 271 272 /** Method to remove a certain index. 273 273 * @param index The <Strong>Index</strong> to remove. 274 274 * @see org.greenstone.gatherer.Gatherer … … 277 277 * @see org.greenstone.gatherer.collection.CollectionManager 278 278 */ 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 279 public void removeIndex(Index index) { 280 if(index != null) { 281 String name = index.getName(); 282 if(name != null) { 283 Language default_language = manager.languages.getDefaultLanguage(); 284 CollectionMeta metadata = new CollectionMeta(manager, index, default_language, name); 285 manager.collectionmetadatum.removeMetadata(metadata); 286 } 287 removeElement(index); 288 if(default_index != null && default_index.equals(index)) { 289 default_index = null; 290 } 291 gatherer.c_man.configurationChanged(); 292 } 293 } 294 /** Method to set the default index. 295 295 * @param index The new default <strong>Index</strong>. 296 296 * @see org.greenstone.gatherer.Gatherer 297 297 * @see org.greenstone.gatherer.collection.CollectionManager 298 298 */ 299 300 301 302 303 304 305 306 299 public void setDefault(Index index) { 300 default_index = index; 301 if(index != null && !contains(index)) { 302 addElement(index); 303 } 304 gatherer.c_man.configurationChanged(); 305 } 306 /** Method to print out the contents of this class as a string. 307 307 * @return A <strong>String</strong> just as it would appear in the colleciton configuration file. 308 308 */ 309 310 311 312 313 314 315 316 317 318 319 320 309 public String toString() { 310 String text = ""; 311 // First the indexes. 312 if(size() > 0) { 313 text = "indexes "; 314 for(int i = 0; i < size(); i++) { 315 Index index = (Index) get(i); 316 text = text + index.toString(false); 317 if(i < size() - 1) { 318 text = text + " "; 319 } 320 } 321 321 // Now the default index if there is one, or just the first index 322 322 // if there isn't 323 324 325 326 327 328 329 330 323 if(default_index != null) { 324 text = text + "\ndefaultindex " + default_index.toString(false) + "\n"; 325 } 326 text = text + "\n"; 327 } 328 return text; 329 } 330 /** Overloaded to call get with both a key and an empty argument array. 331 331 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 332 332 * @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. 333 333 */ 334 335 336 337 334 private String get(String key) { 335 return get(key, null); 336 } 337 /** 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> 338 338 * 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>. 339 339 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. … … 343 343 * @see org.greenstone.gatherer.Dictionary 344 344 */ 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 345 private String get(String key, String args[]) { 346 if(key.indexOf('.') == -1) { 347 key = "CDM.IndexManager." + key; 348 } 349 return gatherer.dictionary.get(key, args); 350 } 351 /** This class creates a set of controls for editing the indexes. */ 352 private class Control 353 extends JPanel { 354 /** The default size of a label on this control. */ 355 private Dimension LABEL_SIZE = new Dimension(120,25); 356 /** The button used to add a new index. */ 357 private JButton add = null; 358 /** The button used to clear the default index. */ 359 private JButton clear_default = null; 360 /** The button used to remove an index. */ 361 private JButton remove = null; 362 /** The button used to set the default index. */ 363 private JButton set_default = null; 364 /** The combobox used to adjust what level the new index will built on. */ 365 private JComboBox level = null; 366 /** The label denoting the default index to be built for this collection. */ 367 private JLabel default_label = null; 368 /** The label denoting the list of assigned indexes. */ 369 private JLabel index_label = null; 370 /** The label denoting the control for choosing index level. */ 371 private JLabel level_label = null; 372 /** The label denoting the name of the index. */ 373 private JLabel name_label = null; 374 /** The label denoting the list of possible sources for the index. */ 375 private JLabel source_label = null; 376 /** The title shown at the top of the index controls. */ 377 private JLabel title = null; 378 /** The list of assigned indexes. */ 379 private JList index_list = null; 380 /** The list of possible sources on which the index could be built. */ 381 private JList source_list = null; 382 /** The panel used to display the list of assigned indexes. */ 383 private JPanel assigned_pane = null; 384 /** The panel which contains the buttons used to edit indexes. */ 385 private JPanel button_pane = null; 386 /** The panel on which all other panels are displayed. */ 387 private JPanel central_pane = null; 388 /** The control pane showing the editable controls. */ 389 private JPanel control_pane = null; 390 /** The edit pane contains the list of possible sources. */ 391 private JPanel edit_pane = null; 392 /** A panel used to correctly lay out the default index label. */ 393 private JPanel default_pane = null; 394 /** The panel containing the title label and instructions. */ 395 private JPanel header_pane = null; 396 /** The lvel panel contains the control for adjusting index level. */ 397 private JPanel level_pane = null; 398 /** The pane in which the name control is set. */ 399 private JPanel name_pane = null; 400 /** The scrollpane containing the instructions text area. */ 401 private JScrollPane instructions_scroll = null; 402 /** A text area containing inline instructions. */ 403 private JTextArea instructions = null; 404 /** A text field naming the default index. Non-editable. */ 405 private JTextField default_value = null; 406 /** A text field for controlling the name of the new index. */ 407 private JTextField name = null; 408 /** A model containing all of the available index sources. */ 409 private Vector source_model = null; 410 /** Constructor. 411 * @see org.greenstone.gatherer.Configuration 412 * @see org.greenstone.gatherer.Gatherer 413 * @see org.greenstone.gatherer.cdm.IndexManager.Control.AddListener 414 * @see org.greenstone.gatherer.cdm.IndexManager.Control.ClearDefaultListener 415 * @see org.greenstone.gatherer.cdm.IndexManager.Control.NameListener 416 * @see org.greenstone.gatherer.cdm.IndexManager.Control.RemoveListener 417 * @see org.greenstone.gatherer.cdm.IndexManager.Control.SetDefaultListener 418 * @see org.greenstone.gatherer.collection.CollectionManager 419 * @see org.greenstone.gatherer.gui.Coloring 420 * @see org.greenstone.gatherer.msm.MetadataSetManager 421 * @see org.greenstone.gatherer.util.ExclusiveListSelectionListener 422 */ 423 public Control() { 424 super(); 425 source_model = new Vector(); 426 source_model.add("text"); 427 source_model.addAll(gatherer.c_man.msm.getAssignedElements()); 428 428 //source_model.addAll(gatherer.c_man.msm.getElements()); 429 429 // Creation 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 430 add = new JButton(get("Add")); 431 add.setEnabled(false); 432 add.setMnemonic(KeyEvent.VK_A); 433 assigned_pane = new JPanel(); 434 button_pane = new JPanel(); 435 central_pane = new JPanel(); 436 control_pane = new JPanel(); 437 clear_default = new JButton(get("Clear_Default")); 438 clear_default.setMnemonic(KeyEvent.VK_C); 439 if(default_index == null) { 440 clear_default.setEnabled(false); 441 } 442 default_label = new JLabel(get("Default_Index")); 443 default_pane = new JPanel(); 444 if(default_index != null) { 445 default_value = new JTextField(default_index.toString(true)); 446 default_value.setCaretPosition(0); 447 } 448 else { 449 default_value = new JTextField(); 450 } 451 edit_pane = new JPanel(); 452 header_pane = new JPanel(); 453 index_label = new JLabel(get("Indexes")); 454 index_list = new JList(model); 455 instructions = new JTextArea(get("Instructions")); 456 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 457 instructions.setEditable(false); 458 instructions.setLineWrap(true); 459 instructions.setRows(5); 460 instructions.setWrapStyleWord(true); 461 level = new JComboBox(); 462 level.addItem(get("Document")); 463 level.addItem(get("Paragraph")); 464 level.addItem(get("Section")); 465 level.setEditable(false); 466 level_label = new JLabel(get("Level")); 467 level_label.setPreferredSize(LABEL_SIZE); 468 level_pane = new JPanel(); 469 name = new JTextField(); 470 name_label = new JLabel(get("Name")); 471 name_label.setPreferredSize(LABEL_SIZE); 472 name_pane = new JPanel(); 473 remove = new JButton(get("Remove")); 474 remove.setEnabled(false); 475 remove.setMnemonic(KeyEvent.VK_R); 476 set_default = new JButton(get("Set_Default")); 477 set_default.setEnabled(false); 478 set_default.setMnemonic(KeyEvent.VK_S); 479 source_label = new JLabel(get("Source")); 480 source_list = new JList(source_model); 481 title = new JLabel(get("Title")); 482 title.setHorizontalAlignment(JLabel.CENTER); 483 483 484 484 // Listeners 485 486 487 488 489 490 491 492 493 494 495 485 add.addActionListener(new AddListener()); 486 clear_default.addActionListener(new ClearDefaultListener()); 487 remove.addActionListener(new RemoveListener()); 488 set_default.addActionListener(new SetDefaultListener()); 489 name.addKeyListener(new NameListener()); 490 ListListener ll = new ListListener(); 491 index_list.addListSelectionListener(ll); 492 source_list.addListSelectionListener(ll); 493 ExclusiveListSelectionListener elsl = new ExclusiveListSelectionListener(); 494 elsl.add(index_list); 495 elsl.add(source_list); 496 496 497 497 // Layout 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 498 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0)); 499 500 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 501 502 header_pane.setLayout(new BorderLayout()); 503 header_pane.add(title, BorderLayout.NORTH); 504 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 505 506 name_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 507 508 name_pane.setBorder(BorderFactory.createEmptyBorder(4,2,4,2)); 509 name_pane.setLayout(new BorderLayout()); 510 name_pane.add(name_label, BorderLayout.WEST); 511 name_pane.add(name, BorderLayout.CENTER); 512 513 control_pane.setLayout(new BorderLayout()); 514 control_pane.add(source_label, BorderLayout.NORTH); 515 control_pane.add(new JScrollPane(source_list), BorderLayout.CENTER); 516 517 level_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 518 519 level_pane.setBorder(BorderFactory.createEmptyBorder(4,2,4,2)); 520 level_pane.setLayout(new BorderLayout()); 521 level_pane.add(level_label, BorderLayout.WEST); 522 level_pane.add(level, BorderLayout.CENTER); 523 524 edit_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 525 edit_pane.setLayout(new BorderLayout()); 526 edit_pane.add(name_pane, BorderLayout.NORTH); 527 edit_pane.add(control_pane, BorderLayout.CENTER); 528 edit_pane.add(level_pane, BorderLayout.SOUTH); 529 530 default_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 531 532 default_pane.setBorder 533 (BorderFactory.createCompoundBorder 534 (BorderFactory.createCompoundBorder 535 (BorderFactory.createEmptyBorder(2,2,2,2), 536 BorderFactory.createRaisedBevelBorder()), 537 BorderFactory.createEmptyBorder(2,2,2,2))); 538 default_pane.setLayout(new BorderLayout()); 539 default_pane.add(default_label, BorderLayout.WEST); 540 default_pane.add(default_value, BorderLayout.CENTER); 541 542 assigned_pane.setLayout(new BorderLayout()); 543 assigned_pane.add(index_label, BorderLayout.NORTH); 544 assigned_pane.add(new JScrollPane(index_list), BorderLayout.CENTER); 545 assigned_pane.add(default_pane, BorderLayout.SOUTH); 546 547 central_pane.setBorder(BorderFactory.createEmptyBorder(2,0,2,0)); 548 central_pane.setLayout(new GridLayout(1,2)); 549 central_pane.add(edit_pane); 550 central_pane.add(assigned_pane); 551 552 button_pane.setLayout(new GridLayout(2,2)); 553 button_pane.add(add); 554 button_pane.add(remove); 555 button_pane.add(clear_default); 556 button_pane.add(set_default); 557 558 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 559 setLayout(new BorderLayout()); 560 add(header_pane, BorderLayout.NORTH); 561 add(central_pane, BorderLayout.CENTER); 562 add(button_pane, BorderLayout.SOUTH); 563 } 564 /* Destructor, removes persistant listeners from the Dictionary. 565 565 */ 566 567 568 566 public void destroy() { 567 } 568 /** We override the updateUI method so that we can ensure we are scrolled to the top of the instructions box first. 569 569 * @see org.greenstone.gatherer.Gatherer 570 570 * @see org.greenstone.gatherer.collection.CollectionManager 571 571 * @see org.greenstone.gatherer.msm.MetadataSetManager 572 572 */ 573 574 575 576 573 public void updateUI() { 574 if(instructions != null) { 575 instructions.setCaretPosition(0); 576 } 577 577 // Reload the assigned indexes list. 578 579 580 581 582 578 if(source_model != null) { 579 source_model.clear(); 580 source_model.add("text"); 581 source_model.addAll(gatherer.c_man.msm.getAssignedElements()); 582 } 583 583 // Faster than a NPE its the - 'Super class'. 584 585 586 587 588 584 super.updateUI(); 585 } 586 /** Listens for actions apon the 'add' button in the IndexManager controls, and if detected calls the add method of the manager with a newly created index. */ 587 private class AddListener 588 implements ActionListener { 589 589 /** Method called when an action is performed on a registered component, and when it does we check if we have enough data to create a new index, and if so we create one. 590 * @param event An <strong>ActionEvent</strong> providing extra information about the event. 591 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 592 * @see org.greenstone.gatherer.cdm.CollectionMeta 593 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 594 * @see org.greenstone.gatherer.cdm.Index 595 * @see org.greenstone.gatherer.cdm.Language 596 * @see org.greenstone.gatherer.cdm.LanguageManager 590 * @param event An <strong>ActionEvent</strong> providing extra information about the event. 591 * @see org.greenstone.gatherer.cdm.CollectionDesignManager 592 * @see org.greenstone.gatherer.cdm.CollectionMeta 593 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 594 * @see org.greenstone.gatherer.cdm.Index 595 * @see org.greenstone.gatherer.cdm.Language 596 * @see org.greenstone.gatherer.cdm.LanguageManager 597 */ 598 public void actionPerformed(ActionEvent event) { 599 if(!source_list.isSelectionEmpty() && name.getText().length() != 0) { 600 Object object[] = source_list.getSelectedValues(); 601 Vector sources = new Vector(); 602 for(int i = 0; i < object.length; i++) { 603 sources.add(object[i]); 604 } 605 Index index = new Index(level.getSelectedIndex(), sources, manager); 606 // Before we add the index to the model, we have to add the collection metadata for this. 607 Language language = manager.languages.getDefaultLanguage(); 608 CollectionMeta metadata = new CollectionMeta(manager, index, language, name.getText()); 609 manager.collectionmetadatum.addMetadata(metadata); 610 // Finally add index. 611 addIndex(index); 612 } 613 } 614 } 615 /** Listens for actions apon the 'clear default' button in the IndexManager controls, and if detected calls the setDefault method of the manager with <i>null</i>. */ 616 private class ClearDefaultListener 617 implements ActionListener { 618 /** If called when an action occurs on a registered component, we clear the default index. 619 * @param event An <strong>ActionEvent</strong> containing extra information about the action that occured. 597 620 */ 598 public void actionPerformed(ActionEvent event) { 599 if(!source_list.isSelectionEmpty() && name.getText().length() != 0) { 600 Object object[] = source_list.getSelectedValues(); 601 Vector sources = new Vector(); 602 for(int i = 0; i < object.length; i++) { 603 sources.add(object[i]); 604 } 605 Index index = new Index(level.getSelectedIndex(), sources, manager); 606 // Before we add the index to the model, we have to add the collection metadata for this. 607 Language language = manager.languages.getDefaultLanguage(); 608 CollectionMeta metadata = new CollectionMeta(manager, index, language, name.getText()); 609 manager.collectionmetadatum.addMetadata(metadata); 610 // Finally add index. 611 addIndex(index); 612 } 613 } 614 } 615 /** Listens for actions apon the 'clear default' button in the IndexManager controls, and if detected calls the setDefault method of the manager with <i>null</i>. */ 616 private class ClearDefaultListener 617 implements ActionListener { 618 /** If called when an action occurs on a registered component, we clear the default index. 619 * @param event An <strong>ActionEvent</strong> containing extra information about the action that occured. 621 public void actionPerformed(ActionEvent event) { 622 setDefault(null); 623 clear_default.setEnabled(false); 624 default_value.setText(""); 625 } 626 } 627 /** Listens for actions apon the 'remove' button in the IndexManager controls, and if detected calls the remove method of the manager with the index selected for removal. */ 628 private class RemoveListener 629 implements ActionListener { 630 /** If called when an action occurs on a registered component, we remove the currently selected index, if there is one. 631 * @param event An <strong>ActionEvent</strong> containing extra information about the action that occured. 632 * @see org.greenstone.gatherer.cdm.Index 633 */ 634 public void actionPerformed(ActionEvent event) { 635 if(!index_list.isSelectionEmpty()) { 636 removeIndex((Index)index_list.getSelectedValue()); 637 if(default_index == null) { 638 clear_default.setEnabled(false); 639 default_value.setText(""); 640 } 641 } 642 } 643 } 644 /** Listens for actions apon the 'set default' button in the IndexManager controls, and if detected calls the setDefault method of the manager with the index selected for default. */ 645 private class SetDefaultListener 646 implements ActionListener { 647 /** If called when an action occurs on a registered component, we set the default index to the index currently selected. 648 * @param event An <strong>ActionEvent</strong> containing extra information about the action that occured. 649 * @see org.greenstone.gatherer.cdm.Index 650 */ 651 public void actionPerformed(ActionEvent event) { 652 setDefault((Index)index_list.getSelectedValue()); 653 if(default_index != null) { 654 clear_default.setEnabled(true); 655 default_value.setText(default_index.toString(true)); 656 default_value.setCaretPosition(0); 657 } 658 else { 659 clear_default.setEnabled(false); 660 default_value.setText(""); 661 } 662 } 663 } 664 /** Listens for key presses within the name field, and enabled or disables controls as appropriate. */ 665 private class NameListener 666 extends KeyAdapter { 667 /** Called when a key is released, this is the perfect time to enable the add button if the fields are appropriately set. 668 * @param event A <strong>KeyEvent</strong> containing information about the key released. 620 669 */ 621 public void actionPerformed(ActionEvent event) { 622 setDefault(null); 623 clear_default.setEnabled(false); 624 default_value.setText(""); 625 } 626 } 627 /** Listens for actions apon the 'remove' button in the IndexManager controls, and if detected calls the remove method of the manager with the index selected for removal. */ 628 private class RemoveListener 629 implements ActionListener { 630 /** If called when an action occurs on a registered component, we remove the currently selected index, if there is one. 631 * @param event An <strong>ActionEvent</strong> containing extra information about the action that occured. 632 * @see org.greenstone.gatherer.cdm.Index 670 public void keyReleased(KeyEvent event) { 671 if(source_list.isSelectionEmpty() || name.getText().length() == 0) { 672 add.setEnabled(false); 673 } 674 else { 675 add.setEnabled(true); 676 } 677 if(index_list.isSelectionEmpty()) { 678 remove.setEnabled(false); 679 set_default.setEnabled(false); 680 } 681 else { 682 remove.setEnabled(true); 683 set_default.setEnabled(true); 684 } 685 } 686 } 687 /** Listens for selections within the list on the IndexManager controls, and if a change is detected enables, or disables, controls appropriately. */ 688 private class ListListener 689 implements ListSelectionListener { 690 /** This method is called whenever the source list selection changes. When it does we need to check if the add button should now be enabled. 691 * @param event A <strong>ListSelectionEvent</strong> containing further information about the list selection. 633 692 */ 634 public void actionPerformed(ActionEvent event) { 635 if(!index_list.isSelectionEmpty()) { 636 removeIndex((Index)index_list.getSelectedValue()); 637 if(default_index == null) { 638 clear_default.setEnabled(false); 639 default_value.setText(""); 640 } 641 } 642 } 643 } 644 /** Listens for actions apon the 'set default' button in the IndexManager controls, and if detected calls the setDefault method of the manager with the index selected for default. */ 645 private class SetDefaultListener 646 implements ActionListener { 647 /** If called when an action occurs on a registered component, we set the default index to the index currently selected. 648 * @param event An <strong>ActionEvent</strong> containing extra information about the action that occured. 649 * @see org.greenstone.gatherer.cdm.Index 650 */ 651 public void actionPerformed(ActionEvent event) { 652 setDefault((Index)index_list.getSelectedValue()); 653 if(default_index != null) { 654 clear_default.setEnabled(true); 655 default_value.setText(default_index.toString(true)); 656 default_value.setCaretPosition(0); 657 } 658 else { 659 clear_default.setEnabled(false); 660 default_value.setText(""); 661 } 662 } 663 } 664 /** Listens for key presses within the name field, and enabled or disables controls as appropriate. */ 665 private class NameListener 666 extends KeyAdapter { 667 /** Called when a key is released, this is the perfect time to enable the add button if the fields are appropriately set. 668 * @param event A <strong>KeyEvent</strong> containing information about the key released. 669 */ 670 public void keyReleased(KeyEvent event) { 671 if(source_list.isSelectionEmpty() || name.getText().length() == 0) { 672 add.setEnabled(false); 673 } 674 else { 675 add.setEnabled(true); 676 } 677 if(index_list.isSelectionEmpty()) { 678 remove.setEnabled(false); 679 set_default.setEnabled(false); 680 } 681 else { 682 remove.setEnabled(true); 683 set_default.setEnabled(true); 684 } 685 } 686 } 687 /** Listens for selections within the list on the IndexManager controls, and if a change is detected enables, or disables, controls appropriately. */ 688 private class ListListener 689 implements ListSelectionListener { 690 /** This method is called whenever the source list selection changes. When it does we need to check if the add button should now be enabled. 691 * @param event A <strong>ListSelectionEvent</strong> containing further information about the list selection. 692 */ 693 public void valueChanged(ListSelectionEvent event) { 694 if(source_list.isSelectionEmpty() || name.getText().length() == 0) { 695 add.setEnabled(false); 696 } 697 else { 698 add.setEnabled(true); 699 } 700 if(index_list.isSelectionEmpty()) { 701 remove.setEnabled(false); 702 set_default.setEnabled(false); 703 } 704 else { 705 remove.setEnabled(true); 706 set_default.setEnabled(true); 707 } 708 } 709 } 710 } 693 public void valueChanged(ListSelectionEvent event) { 694 if(source_list.isSelectionEmpty() || name.getText().length() == 0) { 695 add.setEnabled(false); 696 } 697 else { 698 add.setEnabled(true); 699 } 700 if(index_list.isSelectionEmpty()) { 701 remove.setEnabled(false); 702 set_default.setEnabled(false); 703 } 704 else { 705 remove.setEnabled(true); 706 set_default.setEnabled(true); 707 } 708 } 709 } 710 } 711 711 } 712 712 -
trunk/gli/src/org/greenstone/gatherer/cdm/Language.java
r4293 r4366 56 56 */ 57 57 public class Language 58 59 60 61 62 63 64 65 66 67 68 58 implements Comparable { 59 /** Is this language the default one. */ 60 private boolean default_language = false; 61 /** The name of this language. */ 62 private String name = null; 63 /** The two character code for this language. */ 64 private String value = null; 65 /** Constructor. 66 * @param value A <strong>String</strong> representing the code for this language. 67 * @param name A <strong>String</strong> representing the name of this language. 68 * @param default_language A <i>boolean</i> which is <i>true</i> if this language is the default one. 69 69 */ 70 71 72 73 74 75 70 public Language(String value, String name, boolean default_language) { 71 this.default_language = default_language; 72 this.name = name; 73 this.value = value.substring(0, 2); 74 } 75 /** Copy constructor. 76 76 * @param language The <strong>Language</strong> we want to copy. 77 77 */ 78 79 80 81 82 83 78 public Language(Language language) { 79 this.default_language = language.isDefault(); 80 this.name = language.toString(); 81 this.value = language.getCode(); 82 } 83 /** Method to compare two languages for ordering purposes. 84 84 * @param object The other language as an <strong>Object</strong>. 85 85 * @return An <i>int</i> which indicates order using the same values as in String.compareTo(). 86 86 * @see java.lang.String#compareTo 87 87 */ 88 89 90 91 88 public int compareTo(Object object) { 89 return toString().compareTo(object.toString()); 90 } 91 /** Method to test for the equality of two languages. 92 92 * @param object The other language as an <strong>Object</strong>. 93 93 * @return <i>true</i> if the languages are equal, <i>false</i> otherwise. 94 94 */ 95 96 97 98 99 100 101 95 public boolean equals(Object object) { 96 if(compareTo(object) == 0) { 97 return true; 98 } 99 return false; 100 } 101 /** Method to retrieve the code of this language. 102 102 * @return A <strong>String</strong> representing the two letter code. 103 103 */ 104 105 106 107 104 public String getCode() { 105 return value; 106 } 107 /** Method to determine if this language is the default one. 108 108 * @return A <i>boolean</i> which is <i>true</i> if this language is the default one. 109 109 */ 110 111 112 113 110 public boolean isDefault() { 111 return default_language; 112 } 113 /** Method to set the value of default. 114 114 * @param value The new value of default as a <i>boolean</i>. 115 115 */ 116 117 118 119 116 public void setDefault(boolean value) { 117 this.default_language = default_language; 118 } 119 /** Method to display the language code. 120 120 * @return A <strong>String</strong> representing the language code. 121 121 */ 122 123 124 122 public String toString() { 123 return name; 124 } 125 125 } 126 126 -
trunk/gli/src/org/greenstone/gatherer/cdm/LanguageManager.java
r4293 r4366 85 85 import org.greenstone.gatherer.cdm.Language; 86 86 /** This class manages the language commands, remembering both a list of languages to build indexes in, plus the default language. 87 * @author John Thompson, Greenstone Digital Library, University of Waikato88 * @version 2.389 */87 * @author John Thompson, Greenstone Digital Library, University of Waikato 88 * @version 2.3 89 */ 90 90 public class LanguageManager 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 91 extends DefaultListModel { 92 /** A reference to the collection design manager. */ 93 private CollectionDesignManager manager = null; 94 /** The visual controls for this manager. */ 95 private Control controls = null; 96 /** A reference to the Gatherer. */ 97 private Gatherer gatherer = null; 98 /** A reference to this class as a model, for the inner controls class. */ 99 private ListModel model = null; 100 /** A hashtable of code->name mappings of known languages. */ 101 private LinkedHashMap known_languages = null; 102 /** The default language object. */ 103 private Language default_language = null; 104 /** Constructor. 105 * @param gatherer A reference to the <strong>Gatherer</strong>. 106 * @param manager A reference to the <strong>CollectionDesignManager</strong>. 107 */ 108 public LanguageManager(Gatherer gatherer, CollectionDesignManager manager) { 109 super(); 110 this.gatherer = gatherer; 111 this.known_languages = new LinkedHashMap(); 112 this.manager = manager; 113 this.model = this; 114 loadLanguages(); 115 } 116 /** Method to add a new language. 117 117 * @param language The <strong>Language</strong> to add. 118 118 * @see org.greenstone.gatherer.Gatherer 119 119 * @see org.greenstone.gatherer.collection.CollectionManager 120 120 */ 121 122 121 public void addLanguage(Language language) { 122 if(!contains(language)) { 123 123 // Add alphabetically. 124 125 126 127 128 129 130 131 132 133 134 135 136 137 124 for(int index = 0; index < size(); index++) { 125 Language sibling = (Language) get(index); 126 int position = language.compareTo(sibling); 127 // Sibling is before language. 128 if(position > 0) { 129 // Carry on. 130 } 131 // Language is equal to, or before sibling. Insert it. 132 else if(position == 0 || position < 0) { 133 add(index, language); 134 gatherer.c_man.configurationChanged(); 135 return; 136 } 137 } 138 138 // If we got this far, we haven't inserted language, and we are out of model so. 139 140 141 142 143 139 addElement(language); 140 gatherer.c_man.configurationChanged(); 141 } 142 } 143 /** Method to retrieve the control for this manager. 144 144 * @return A <strong>JPanel</strong> containing the controls. 145 145 */ 146 147 148 149 150 151 152 146 public JPanel getControls() { 147 if(controls == null) { 148 controls = new Control(); 149 } 150 return controls; 151 } 152 /** Method to retrieve the default language code. 153 153 * @return A <strong>Language</strong> containing a two letter code. 154 154 */ 155 156 157 155 public Language getDefaultLanguage() { 156 // If no default is set... 157 if(default_language == null) { 158 158 // And we have other assigned languages, use the first one of them. 159 160 161 159 if(size() >= 1) { 160 default_language = (Language) get(0); 161 } 162 162 // And we have nothing else, use English. 163 164 165 166 167 168 169 170 171 163 else { 164 default_language = getLanguage("en", false); 165 // Remember to add it. 166 addLanguage(default_language); 167 } 168 } 169 return default_language; 170 } 171 /** Method to retrieve a certain language object by its code. 172 172 * @param code The two letter code of a language, as a <strong>String</strong>. 173 173 * @param assigned_only If <i>true</i> only those languages currently having indexes built for them are checked, otherwise the known languages buffer is checked. 174 174 * @return The <strong>Language</strong> that matches the given code, or <i>null</i> if no such language exists. 175 175 */ 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 176 public Language getLanguage(String code, boolean assigned_only) { 177 if(assigned_only) { 178 for(int i = 0; i < size(); i++) { 179 Language pos = (Language)get(i); 180 ///ystem.err.println("Comparing " + pos.getCode() + " and " + code); 181 if(pos.getCode().equals(code)) { 182 return pos; 183 } 184 } 185 } 186 else { 187 if(known_languages.containsKey(code)) { 188 return new Language((Language)known_languages.get(code)); 189 } 190 } 191 return null; 192 } 193 /** Method to return a list of the known language codes. 194 194 * @return An <strong>ArrayList</strong> containing a series of alphabetically sorted two letter codes. 195 195 */ 196 197 198 199 200 201 202 203 204 205 206 207 208 196 public ArrayList getLanguageCodes() { 197 ArrayList result = new ArrayList(); 198 Iterator key_iter = known_languages.keySet().iterator(); 199 while(key_iter.hasNext()) { 200 result.add(known_languages.get(key_iter.next())); 201 } 202 //for(Enumeration keys = known_languages.keys(); keys.hasMoreElements(); ) { 203 // result.add(known_languages.get(keys.nextElement())); 204 //} 205 //Collections.sort(result); 206 return result; 207 } 208 /** Mark the current set of controls, if any, as obsolete and deallocate them. If further need of the controls will cause new controls to be created. 209 209 * @see org.greenstone.gatherer.cdm.IndexManager.Control 210 210 */ 211 212 213 214 215 216 217 218 211 public void invalidateControls() { 212 if(controls != null) { 213 controls.destroy(); 214 } 215 controls = null; 216 } 217 218 /** Determine if the given language is the current default language. 219 219 * @param language The <strong>Language</strong> to test. 220 220 * @return <i>true</i> if the language is the default one, <i>false</i> otherwise. 221 221 */ 222 223 224 225 222 public boolean isDefaultLanguage(Language language) { 223 return (language.equals(default_language)); 224 } 225 /** This method attempts to parse a language command from the given string. If such a command is parsed, it is immediately added to the list of languages. 226 226 * @param command The <strong>String</strong> containing a possible language command. 227 227 * @return A <i>boolean</i> which is <i>true</i> if a command was parsed, <i>false</i> otherwise. 228 228 * @see org.greenstone.gatherer.cdm.Language 229 229 */ 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 230 public boolean parse(String command) { 231 String command_lc = command.toLowerCase(); 232 if(command_lc.startsWith("languages")) { 233 StringTokenizer tokenizer = new StringTokenizer(command); 234 tokenizer.nextToken(); 235 while(tokenizer.hasMoreTokens()) { 236 String code = tokenizer.nextToken(); 237 if(known_languages.containsKey(code)) { 238 Language language = (Language)known_languages.get(code); 239 addElement(new Language(language)); 240 } 241 else { 242 addElement(new Language(code, code, false)); 243 } 244 } 245 return true; 246 } 247 if(command_lc.startsWith("defaultlanguage")) { 248 StringTokenizer tokenizer = new StringTokenizer(command); 249 tokenizer.nextToken(); 250 if(tokenizer.hasMoreTokens()) { 251 String code = tokenizer.nextToken(); 252 Language language = getLanguage(code, true); 253 if(language == null) { 254 language = new Language(code, (String)known_languages.get(code), true); 255 addElement(language); 256 } 257 else { 258 language.setDefault(true); 259 } 260 setDefault(language); 261 } 262 return true; 263 } 264 return false; 265 } 266 /** Method to cause the list appearance to update if the selection changes. */ 267 public void refreshAppearance() { 268 fireContentsChanged(this, 0, size()); 269 } 270 /** Method to remove a certain language. 271 271 * @param language The <strong>Language</strong> to remove. 272 272 * @see org.greenstone.gatherer.Gatherer 273 273 * @see org.greenstone.gatherer.collection.CollectionManager 274 274 */ 275 276 277 278 279 280 281 282 275 public void removeLanguage(Language language) { 276 removeElement(language); 277 if(default_language != null && default_language.equals(language)) { 278 default_language = null; 279 } 280 gatherer.c_man.configurationChanged(); 281 } 282 /** Method to set the default language. 283 283 * @param language The <strong>Language</strong> to use as a default, or <i>null</i> for no default. 284 284 * @see org.greenstone.gatherer.Gatherer 285 285 * @see org.greenstone.gatherer.collection.CollectionManager 286 286 */ 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 287 public void setDefault(Language language) { 288 // Unset existing. 289 Language old = null; 290 if(default_language != null) { 291 old = default_language; 292 default_language.setDefault(false); 293 default_language = null; 294 } 295 // Now set the new if its not null. 296 if(language != null) { 297 default_language = language; 298 default_language.setDefault(true); 299 } 300 // Now cause the model to refresh any lists that are listening. 301 int start = 0; 302 int end = size() - 1; 303 if(default_language != null && old != null) { 304 int index_default = indexOf(default_language); 305 int index_old = indexOf(old); 306 if(index_default < index_old) { 307 start = index_default; 308 end = index_old; 309 } 310 else { 311 start = index_old; 312 end = index_default; 313 } 314 } 315 else if(default_language != null) { 316 start = end = indexOf(default_language); 317 } 318 else { 319 start = end = indexOf(old); 320 } 321 fireContentsChanged(this, 0, size()); 322 gatherer.c_man.configurationChanged(); 323 } 324 /** Method to translate this object into a block of commands as you you expect to find in the collection configuration file. 325 325 * @return A <strong>String</string> containing a series of commands. 326 326 */ 327 328 329 330 331 332 333 334 335 336 337 338 339 340 327 public String toString() { 328 StringBuffer text = new StringBuffer(); 329 if(size() > 1) { 330 text.append("languages "); 331 for(int i = 0; i < size(); i++) { 332 Language language = (Language) get(i); 333 text.append(language.getCode()); 334 if(i < size() - 1) { 335 text.append(" "); 336 } 337 else { 338 text.append("\n"); 339 } 340 } 341 341 // Only bother with default language if there is more than one language. 342 343 344 345 346 347 348 349 350 351 342 if(default_language != null) { 343 text.append("defaultlanguage "); 344 text.append(default_language.getCode()); 345 text.append("\n"); 346 } 347 text.append("\n"); 348 } 349 return text.toString(); 350 } 351 /** Overloaded to call get with both a key and an empty argument array. 352 352 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 353 353 * @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. 354 354 */ 355 356 357 358 355 private String get(String key) { 356 return get(key, null); 357 } 358 /** 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> 359 359 * 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>. 360 360 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. … … 364 364 * @see org.greenstone.gatherer.Dictionary 365 365 */ 366 367 368 369 370 371 372 366 private String get(String key, String args[]) { 367 if(key.indexOf('.') == -1) { 368 key = "CDM.LanguageManager." + key; 369 } 370 return gatherer.dictionary.get(key, args); 371 } 372 /** This method loads a series of code->language mappings into known_languages, by reading from the 'languages.dat' file, which is essentially a verbatim copy of the ISO 639 Standard. 373 373 * @see org.greenstone.gatherer.cdm.Language 374 374 */ 375 private void loadLanguages() { 376 try { 377 File in_file = new File("languages.dat"); 378 FileReader in_reader = new FileReader(in_file); 379 BufferedReader in = new BufferedReader(in_reader); 380 String entry = null; 381 while((entry = in.readLine()) != null) { 382 if(!entry.startsWith("#")) { 383 StringTokenizer tokenizer = new StringTokenizer(entry); 384 String name = tokenizer.nextToken(); 385 String code = tokenizer.nextToken().toLowerCase(); 386 Language language = new Language(code, name, false); 387 known_languages.put(code, language); 388 } 389 } 390 in.close(); 391 } 392 catch (Exception error) { 393 error.printStackTrace(); 394 } 395 } 396 397 /** This class represents the visual component of the Language Manager. */ 398 private class Control 399 extends JPanel { 400 /** The button to add a new language support. */ 401 private JButton add = null; 402 /** The button to clear the current default language. */ 403 private JButton clear_default = null; 404 /** The button to remove a supported language. */ 405 private JButton remove = null; 406 /** The button to set the current language as the default one. */ 407 private JButton set_default = null; 408 /** A button to active the translation manager prompt. */ 409 private JButton translate = null; 410 /** A combobox listing the available supported languages. */ 411 private JComboBox selector = null; 412 /** The label denoting the default language. */ 413 private JLabel default_language_label = null; 414 /** The label denoting the language selector. */ 415 private JLabel selector_label = null; 416 /** The title label of this control view. */ 417 private JLabel title = null; 418 /** A list of currently supported languages. */ 419 private JList list = null; 420 /** The pane in which the control buttons are placed. */ 421 private JPanel button_pane = null; 422 /** The pane onto which all other panes are placed. */ 423 private JPanel central_pane = null; 424 /** The pane to the left containing editing controls. */ 425 private JPanel control_pane = null; 426 /** A panel used to correctly layout the default language box. */ 427 private JPanel default_language_pane = null; 428 /** The display pane contains the title and instruction controls. */ 429 private JPanel display_pane = null; 430 /** The pane which holds everything the display pane doesn't! */ 431 private JPanel lower_pane = null; 432 /** The pane containing the selector combobox. */ 433 private JPanel selector_pane = null; 434 /** A text area displaying inline instructions. */ 435 private JTextArea instructions = null; 436 /** A description of the language currently selected. */ 437 private JTextArea description = null; 438 /** The text field showing the currently name of the default language. Non-editable. */ 439 private JTextField default_language_value = null; 440 /** Constructor. 441 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.AddListener 442 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.ClearDefaultListener 443 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.ListListener 444 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.RemoveListener 445 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.SelectorListener 446 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.SetDefaultListener 447 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.TranslateListener 375 private void loadLanguages() { 376 try { 377 File in_file = new File("languages.dat"); 378 FileReader in_reader = new FileReader(in_file); 379 BufferedReader in = new BufferedReader(in_reader); 380 String entry = null; 381 while((entry = in.readLine()) != null) { 382 if(!entry.startsWith("#")) { 383 StringTokenizer tokenizer = new StringTokenizer(entry); 384 String name = tokenizer.nextToken(); 385 String code = tokenizer.nextToken().toLowerCase(); 386 Language language = new Language(code, name, false); 387 known_languages.put(code, language); 388 } 389 } 390 in.close(); 391 } 392 catch (Exception error) { 393 error.printStackTrace(); 394 } 395 } 396 397 /** This class represents the visual component of the Language Manager. */ 398 private class Control 399 extends JPanel { 400 /** The button to add a new language support. */ 401 private JButton add = null; 402 /** The button to clear the current default language. */ 403 private JButton clear_default = null; 404 /** The button to remove a supported language. */ 405 private JButton remove = null; 406 /** The button to set the current language as the default one. */ 407 private JButton set_default = null; 408 /** A button to active the translation manager prompt. */ 409 private JButton translate = null; 410 /** A combobox listing the available supported languages. */ 411 private JComboBox selector = null; 412 /** The label denoting the default language. */ 413 private JLabel default_language_label = null; 414 /** The label denoting the language selector. */ 415 private JLabel selector_label = null; 416 /** The title label of this control view. */ 417 private JLabel title = null; 418 /** A list of currently supported languages. */ 419 private JList list = null; 420 /** The pane in which the control buttons are placed. */ 421 private JPanel button_pane = null; 422 /** The pane onto which all other panes are placed. */ 423 private JPanel central_pane = null; 424 /** The pane to the left containing editing controls. */ 425 private JPanel control_pane = null; 426 /** A panel used to correctly layout the default language box. */ 427 private JPanel default_language_pane = null; 428 /** The display pane contains the title and instruction controls. */ 429 private JPanel display_pane = null; 430 /** The pane which holds everything the display pane doesn't! */ 431 private JPanel lower_pane = null; 432 /** The pane containing the selector combobox. */ 433 private JPanel selector_pane = null; 434 /** A text area displaying inline instructions. */ 435 private JTextArea instructions = null; 436 /** A description of the language currently selected. */ 437 private JTextArea description = null; 438 /** The text field showing the currently name of the default language. Non-editable. */ 439 private JTextField default_language_value = null; 440 /** Constructor. 441 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.AddListener 442 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.ClearDefaultListener 443 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.ListListener 444 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.RemoveListener 445 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.SelectorListener 446 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.SetDefaultListener 447 * @see org.greenstone.gatherer.cdm.LanguageManager.Control.TranslateListener 448 */ 449 public Control() { 450 super(); 451 // Creation. 452 title = new JLabel(get("Title")); 453 title.setHorizontalAlignment(JLabel.CENTER); 454 title.setOpaque(true); 455 456 central_pane = new JPanel(); 457 458 control_pane = new JPanel(); 459 460 instructions = new JTextArea(get("Instructions")); 461 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 462 instructions.setEditable(false); 463 instructions.setLineWrap(true); 464 instructions.setWrapStyleWord(true); 465 466 selector_pane = new JPanel(); 467 468 selector_label = new JLabel(get("Selector")); 469 selector = new JComboBox(getLanguageCodes().toArray()); 470 471 display_pane = new JPanel(); 472 473 list = new JList(model); 474 list.setCellRenderer(new ListRenderer()); 475 476 default_language_pane = new JPanel(); 477 478 default_language_label = new JLabel(get("Default_Language")); 479 default_language_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 480 if(default_language == null) { 481 default_language_value = new JTextField(); 482 } 483 else { 484 default_language_value = new JTextField(default_language.toString()); 485 } 486 default_language_value.setBackground(Color.white); 487 default_language_value.setEditable(false); 488 489 button_pane = new JPanel(); 490 491 add = new JButton(get("Add")); 492 add.setMnemonic(KeyEvent.VK_A); 493 if(selector.getSelectedItem() != null) { 494 add.setEnabled(true); 495 } 496 else { 497 add.setEnabled(false); 498 } 499 500 remove = new JButton(get("Remove")); 501 remove.setMnemonic(KeyEvent.VK_R); 502 remove.setEnabled(false); 503 504 clear_default = new JButton(get("Clear_Default")); 505 clear_default.setMnemonic(KeyEvent.VK_C); 506 clear_default.setEnabled(false); 507 508 set_default = new JButton(get("Set_Default")); 509 set_default.setMnemonic(KeyEvent.VK_S); 510 set_default.setEnabled(false); 511 512 lower_pane = new JPanel(); 513 translate = new JButton(get("Translate")); 514 translate.setMnemonic(KeyEvent.VK_T); 515 516 // Set up and connect listeners. 517 add.addActionListener(new AddListener()); 518 clear_default.addActionListener(new ClearDefaultListener()); 519 remove.addActionListener(new RemoveListener()); 520 selector.addActionListener(new SelectorListener()); 521 set_default.addActionListener(new SetDefaultListener()); 522 translate.addActionListener(new TranslateListener()); 523 list.addListSelectionListener(new ListListener()); 524 // Layout components. 525 selector_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); 526 selector_pane.setLayout(new BorderLayout()); 527 selector_pane.add(selector_label, BorderLayout.WEST); 528 selector_pane.add(selector, BorderLayout.CENTER); 529 530 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 531 532 control_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 533 control_pane.setLayout(new BorderLayout()); 534 control_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 535 control_pane.add(selector_pane, BorderLayout.SOUTH); 536 537 default_language_pane.setBorder 538 (BorderFactory.createCompoundBorder 539 (BorderFactory.createCompoundBorder 540 (BorderFactory.createEmptyBorder(5,5,5,5), 541 BorderFactory.createRaisedBevelBorder()), 542 BorderFactory.createEmptyBorder(2,2,2,2))); 543 default_language_pane.setLayout(new BorderLayout()); 544 default_language_pane.add(default_language_label, BorderLayout.WEST); 545 default_language_pane.add(default_language_value, BorderLayout.CENTER); 546 547 display_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,5)); 548 display_pane.setLayout(new BorderLayout()); 549 display_pane.add(new JScrollPane(list), BorderLayout.CENTER); 550 display_pane.add(default_language_pane, BorderLayout.SOUTH); 551 552 central_pane.setLayout(new GridLayout(1,2)); 553 central_pane.add(control_pane); 554 central_pane.add(display_pane); 555 556 button_pane.setLayout(new GridLayout(2,2)); 557 button_pane.add(add); 558 button_pane.add(remove); 559 button_pane.add(clear_default); 560 button_pane.add(set_default); 561 562 lower_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 563 lower_pane = new JPanel(new BorderLayout()); 564 lower_pane.add(button_pane, BorderLayout.NORTH); 565 lower_pane.add(translate, BorderLayout.CENTER); 566 567 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 568 setLayout(new BorderLayout()); 569 add(title, BorderLayout.NORTH); 570 add(central_pane, BorderLayout.CENTER); 571 add(lower_pane, BorderLayout.SOUTH); 572 } 573 /** Destructor. 448 574 */ 449 public Control() { 450 super(); 451 // Creation. 452 title = new JLabel(get("Title")); 453 title.setHorizontalAlignment(JLabel.CENTER); 454 title.setOpaque(true); 455 456 central_pane = new JPanel(); 457 458 control_pane = new JPanel(); 459 460 instructions = new JTextArea(get("Instructions")); 461 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 462 instructions.setEditable(false); 463 instructions.setLineWrap(true); 464 instructions.setWrapStyleWord(true); 465 466 selector_pane = new JPanel(); 467 468 selector_label = new JLabel(get("Selector")); 469 selector = new JComboBox(getLanguageCodes().toArray()); 470 471 display_pane = new JPanel(); 472 473 list = new JList(model); 474 list.setCellRenderer(new ListRenderer()); 475 476 default_language_pane = new JPanel(); 477 478 default_language_label = new JLabel(get("Default_Language")); 479 default_language_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 480 if(default_language == null) { 481 default_language_value = new JTextField(); 482 } 483 else { 484 default_language_value = new JTextField(default_language.toString()); 485 } 486 default_language_value.setBackground(Color.white); 487 default_language_value.setEditable(false); 488 489 button_pane = new JPanel(); 490 491 add = new JButton(get("Add")); 492 add.setMnemonic(KeyEvent.VK_A); 493 if(selector.getSelectedItem() != null) { 494 add.setEnabled(true); 495 } 496 else { 497 add.setEnabled(false); 498 } 499 500 remove = new JButton(get("Remove")); 501 remove.setMnemonic(KeyEvent.VK_R); 502 remove.setEnabled(false); 503 504 clear_default = new JButton(get("Clear_Default")); 505 clear_default.setMnemonic(KeyEvent.VK_C); 506 clear_default.setEnabled(false); 507 508 set_default = new JButton(get("Set_Default")); 509 set_default.setMnemonic(KeyEvent.VK_S); 510 set_default.setEnabled(false); 511 512 lower_pane = new JPanel(); 513 translate = new JButton(get("Translate")); 514 translate.setMnemonic(KeyEvent.VK_T); 515 516 // Set up and connect listeners. 517 add.addActionListener(new AddListener()); 518 clear_default.addActionListener(new ClearDefaultListener()); 519 remove.addActionListener(new RemoveListener()); 520 selector.addActionListener(new SelectorListener()); 521 set_default.addActionListener(new SetDefaultListener()); 522 translate.addActionListener(new TranslateListener()); 523 list.addListSelectionListener(new ListListener()); 524 // Layout components. 525 selector_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); 526 selector_pane.setLayout(new BorderLayout()); 527 selector_pane.add(selector_label, BorderLayout.WEST); 528 selector_pane.add(selector, BorderLayout.CENTER); 529 530 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 531 532 control_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 533 control_pane.setLayout(new BorderLayout()); 534 control_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 535 control_pane.add(selector_pane, BorderLayout.SOUTH); 536 537 default_language_pane.setBorder 538 (BorderFactory.createCompoundBorder 539 (BorderFactory.createCompoundBorder 540 (BorderFactory.createEmptyBorder(5,5,5,5), 541 BorderFactory.createRaisedBevelBorder()), 542 BorderFactory.createEmptyBorder(2,2,2,2))); 543 default_language_pane.setLayout(new BorderLayout()); 544 default_language_pane.add(default_language_label, BorderLayout.WEST); 545 default_language_pane.add(default_language_value, BorderLayout.CENTER); 546 547 display_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,5)); 548 display_pane.setLayout(new BorderLayout()); 549 display_pane.add(new JScrollPane(list), BorderLayout.CENTER); 550 display_pane.add(default_language_pane, BorderLayout.SOUTH); 551 552 central_pane.setLayout(new GridLayout(1,2)); 553 central_pane.add(control_pane); 554 central_pane.add(display_pane); 555 556 button_pane.setLayout(new GridLayout(2,2)); 557 button_pane.add(add); 558 button_pane.add(remove); 559 button_pane.add(clear_default); 560 button_pane.add(set_default); 561 562 lower_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 563 lower_pane = new JPanel(new BorderLayout()); 564 lower_pane.add(button_pane, BorderLayout.NORTH); 565 lower_pane.add(translate, BorderLayout.CENTER); 566 567 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 568 setLayout(new BorderLayout()); 569 add(title, BorderLayout.NORTH); 570 add(central_pane, BorderLayout.CENTER); 571 add(lower_pane, BorderLayout.SOUTH); 572 } 573 /** Destructor. 575 public void destroy() { 576 } 577 /** We override the updateUI method so that we can ensure we are scrolled to the top of the instructions box first. 574 578 */ 575 public void destroy() { 576 } 577 /** We override the updateUI method so that we can ensure we are scrolled to the top of the instructions box first. 578 */ 579 public void updateUI() { 580 if(instructions != null) { 581 instructions.setCaretPosition(0); 582 } 583 super.updateUI(); 584 } 585 /** Listens for actions apon the 'add' button in the LanguageManager controls, and if detected calls the add method of the manager with a newly created language. */ 586 private class AddListener 587 implements ActionListener { 579 public void updateUI() { 580 if(instructions != null) { 581 instructions.setCaretPosition(0); 582 } 583 super.updateUI(); 584 } 585 /** Listens for actions apon the 'add' button in the LanguageManager controls, and if detected calls the add method of the manager with a newly created language. */ 586 private class AddListener 587 implements ActionListener { 588 588 /** Add a new language support. 589 * @param event An <strong>ActionEvent</strong>. 590 * @see org.greenstone.gatherer.cdm.Language 589 * @param event An <strong>ActionEvent</strong>. 590 * @see org.greenstone.gatherer.cdm.Language 591 */ 592 public void actionPerformed(ActionEvent event) { 593 Language language = (Language)selector.getSelectedItem(); 594 if(language != null) { 595 addLanguage(new Language(language)); 596 } 597 } 598 } 599 /** Listens for actions apon the 'clear default' button in the LanguageManager controls, and if detected calls the setDefault method of the manager with <i>null</i>. */ 600 private class ClearDefaultListener 601 implements ActionListener { 602 /** Clear the default index. 603 * @param event An <strong>ActionEvent</strong>. 591 604 */ 592 public void actionPerformed(ActionEvent event) { 593 Language language = (Language)selector.getSelectedItem(); 594 if(language != null) { 595 addLanguage(new Language(language)); 596 } 597 } 598 } 599 /** Listens for actions apon the 'clear default' button in the LanguageManager controls, and if detected calls the setDefault method of the manager with <i>null</i>. */ 600 private class ClearDefaultListener 601 implements ActionListener { 602 /** Clear the default index. 603 * @param event An <strong>ActionEvent</strong>. 605 public void actionPerformed(ActionEvent event) { 606 setDefault(null); 607 clear_default.setEnabled(false); 608 default_language_value.setText(""); 609 } 610 } 611 /** Listens for actions apon the 'remove' button in the LanguageManager controls, and if detected calls the remove method of the manager with the language selected for removal. */ 612 private class RemoveListener 613 implements ActionListener { 614 /** Remove the currently selected language, if any. 615 * @param event An <strong>ActionEvent</strong>. 616 * @see org.greenstone.gatherer.cdm.Language 617 */ 618 public void actionPerformed(ActionEvent event) { 619 if(!list.isSelectionEmpty()) { 620 removeLanguage((Language)list.getSelectedValue()); 621 if(default_language == null) { 622 clear_default.setEnabled(false); 623 default_language_value.setText(""); 624 } 625 } 626 } 627 } 628 /** Listens for selections wihtin the combobox on the LanguageManager controls, and if a change is detected enables, or disables, controls appropriately. */ 629 private class SelectorListener 630 implements ActionListener { 631 /** Enable or disable controls depeding on selection. 632 * @param event An <strong>ActionEvent</strong>. 604 633 */ 605 public void actionPerformed(ActionEvent event) { 606 setDefault(null); 607 clear_default.setEnabled(false); 608 default_language_value.setText(""); 609 } 610 } 611 /** Listens for actions apon the 'remove' button in the LanguageManager controls, and if detected calls the remove method of the manager with the language selected for removal. */ 612 private class RemoveListener 613 implements ActionListener { 614 /** Remove the currently selected language, if any. 615 * @param event An <strong>ActionEvent</strong>. 616 * @see org.greenstone.gatherer.cdm.Language 634 public void actionPerformed(ActionEvent event) { 635 if(selector.getSelectedItem() != null) { 636 add.setEnabled(true); 637 //description.setText((String)known_languages.get(code)); 638 } 639 else { 640 add.setEnabled(false); 641 //description.setText(""); 642 } 643 } 644 } 645 /** Listens for actions apon the 'set default' button in the LanguageManager controls, and if detected calls the <i>setDefault()</i> method of the manager with the language selected for default. */ 646 private class SetDefaultListener 647 implements ActionListener { 648 /** Set the default index to the one currently selected, if any. 649 * @param event An <strong>ActionEvent</strong>. 650 * @see org.greenstone.gatherer.cdm.Language 651 */ 652 public void actionPerformed(ActionEvent event) { 653 if(!list.isSelectionEmpty()) { 654 setDefault((Language)list.getSelectedValue()); 655 clear_default.setEnabled(true); 656 default_language_value.setText(default_language.toString()); 657 } 658 } 659 } 660 /** Listens for actions apon the 'translate' button in the LanguageManager controls, and if detected creates a new <strong>TranslationManager</strong> to allow the user to tranlate. */ 661 private class TranslateListener 662 implements ActionListener { 663 /** Create a new translation manager, but remember to clean up after it disposes. 664 * @param event An <strong>ActionEvent</strong>. 665 * @see org.greenstone.gatherer.cdm.TranslationManager 666 */ 667 public void actionPerformed(ActionEvent event) { 668 TranslationManager tm = new TranslationManager(gatherer, manager); 669 tm.destroy(); 670 } 671 } 672 /** Listens for selections within the list on the LanguageManager controls, and if a change is detected enables, or disables, controls appropriately. */ 673 private class ListListener 674 implements ListSelectionListener { 675 /** Enable or disable controls depending on the current list selection. 676 * @param event A <strong>ListSelectionEvent</strong>. 617 677 */ 618 public void actionPerformed(ActionEvent event) { 619 if(!list.isSelectionEmpty()) { 620 removeLanguage((Language)list.getSelectedValue()); 621 if(default_language == null) { 622 clear_default.setEnabled(false); 623 default_language_value.setText(""); 624 } 625 } 626 } 627 } 628 /** Listens for selections wihtin the combobox on the LanguageManager controls, and if a change is detected enables, or disables, controls appropriately. */ 629 private class SelectorListener 630 implements ActionListener { 631 /** Enable or disable controls depeding on selection. 632 * @param event An <strong>ActionEvent</strong>. 633 */ 634 public void actionPerformed(ActionEvent event) { 635 if(selector.getSelectedItem() != null) { 636 add.setEnabled(true); 637 //description.setText((String)known_languages.get(code)); 638 } 639 else { 640 add.setEnabled(false); 641 //description.setText(""); 642 } 643 } 644 } 645 /** Listens for actions apon the 'set default' button in the LanguageManager controls, and if detected calls the <i>setDefault()</i> method of the manager with the language selected for default. */ 646 private class SetDefaultListener 647 implements ActionListener { 648 /** Set the default index to the one currently selected, if any. 649 * @param event An <strong>ActionEvent</strong>. 650 * @see org.greenstone.gatherer.cdm.Language 651 */ 652 public void actionPerformed(ActionEvent event) { 653 if(!list.isSelectionEmpty()) { 654 setDefault((Language)list.getSelectedValue()); 655 clear_default.setEnabled(true); 656 default_language_value.setText(default_language.toString()); 657 } 658 } 659 } 660 /** Listens for actions apon the 'translate' button in the LanguageManager controls, and if detected creates a new <strong>TranslationManager</strong> to allow the user to tranlate. */ 661 private class TranslateListener 662 implements ActionListener { 663 /** Create a new translation manager, but remember to clean up after it disposes. 664 * @param event An <strong>ActionEvent</strong>. 665 * @see org.greenstone.gatherer.cdm.TranslationManager 666 */ 667 public void actionPerformed(ActionEvent event) { 668 TranslationManager tm = new TranslationManager(gatherer, manager); 669 tm.destroy(); 670 } 671 } 672 /** Listens for selections within the list on the LanguageManager controls, and if a change is detected enables, or disables, controls appropriately. */ 673 private class ListListener 674 implements ListSelectionListener { 675 /** Enable or disable controls depending on the current list selection. 676 * @param event A <strong>ListSelectionEvent</strong>. 677 */ 678 public void valueChanged(ListSelectionEvent event) { 679 if(list.isSelectionEmpty()) { 680 remove.setEnabled(false); 681 set_default.setEnabled(false); 682 } 683 else { 684 remove.setEnabled(true); 685 set_default.setEnabled(true); 686 } 687 refreshAppearance(); 688 } 689 } 690 /** Our list cel renderer which renders the default cell just a little different. */ 691 private class ListRenderer 692 extends DefaultListCellRenderer { 678 public void valueChanged(ListSelectionEvent event) { 679 if(list.isSelectionEmpty()) { 680 remove.setEnabled(false); 681 set_default.setEnabled(false); 682 } 683 else { 684 remove.setEnabled(true); 685 set_default.setEnabled(true); 686 } 687 refreshAppearance(); 688 } 689 } 690 /** Our list cel renderer which renders the default cell just a little different. */ 691 private class ListRenderer 692 extends DefaultListCellRenderer { 693 693 /** Method to produce the component used to display an entry in the list. 694 694 * @param list The <strong>JList</strong> the component will be placed in. … … 700 700 * @see org.greenstone.gatherer.cdm.Language 701 701 */ 702 703 704 705 706 707 708 709 710 711 712 713 714 702 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 703 Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 704 Language language = (Language) value; 705 if(language.isDefault() && !isSelected) { 706 component.setBackground(Gatherer.config.getColor("coloring.workspace_selection_background", false)); 707 } 708 if(component instanceof JLabel) { 709 ((JLabel)component).setOpaque(true); 710 } 711 return component; 712 } 713 } 714 } 715 715 } 716 716 -
trunk/gli/src/org/greenstone/gatherer/cdm/MetadataSetManager.java
r4293 r4366 78 78 import org.w3c.dom.NodeList; 79 79 /** Unlike its namesake in the msm package, this class really only knows how to produce a simple visual representation of the currently imported metadata sets. It is also read-only, so should be fairly straight forward. 80 * @author John Thompson, Greenstone Digital Library, University of Waikato81 * @version 2.382 * @see org.greenstone.gatherer.msm.MetadataSetManager83 */80 * @author John Thompson, Greenstone Digital Library, University of Waikato 81 * @version 2.3 82 * @see org.greenstone.gatherer.msm.MetadataSetManager 83 */ 84 84 public class MetadataSetManager 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 85 extends DynamicListModel 86 implements MSMListener { 87 /** The visual contols used to review the metadata sets. */ 88 private Control controls = null; 89 /** A reference to the Gatherer. */ 90 private Gatherer gatherer = null; 91 /** A reference to ourselves so our inner classes can refer to us. */ 92 private DynamicListModel model = null; 93 /** Constructor. 94 * @param gatherer A reference to the <strong>Gatherer</strong>. 95 * @see org.greenstone.gatherer.collection.CollectionManager 96 * @see org.greenstone.gatherer.msm.MetadataSetManager 97 */ 98 public MetadataSetManager(Gatherer gatherer) { 99 this.gatherer = gatherer; 100 this.model = this; 101 Gatherer.c_man.getCollection().msm.addMSMListener(this); 102 loadMetadataSets(); 103 } 104 /** Destructor. Remove any references of this class from persistant objects. 105 105 * @see org.greenstone.gatherer.Gatherer 106 106 * @see org.greenstone.gatherer.collection.CollectionManager 107 107 * @see org.greenstone.gatherer.msm.MetadataSetManager 108 108 */ 109 110 111 112 109 public void destroy() { 110 gatherer.c_man.msm.removeMSMListener(this); 111 } 112 /** Called when an element is changed within a set in the MSM, prompting us to refresh our list of elements being shown. 113 113 * @param event A <strong>MSMEvent</strong> which encapsulates relevant data about the change. 114 114 * @see org.greenstone.gatherer.cdm.MetadataSetManager.Control 115 115 */ 116 117 118 119 120 121 122 116 public void elementChanged(MSMEvent event) { 117 // Get the controls to refresh element list. 118 if(controls != null) { 119 controls.refreshElementList(); 120 } 121 } 122 /** A method for retrieve the controls for this manager. 123 123 * @see org.greenstone.gatherer.Dictionary 124 124 * @see org.greenstone.gatherer.gui.Coloring 125 125 * @see org.greenstone.gatherer.msm.MetadataSetManager 126 126 */ 127 128 129 130 131 132 133 127 public Control getControls() { 128 if(controls == null) { 129 controls = new Control(); 130 } 131 return controls; 132 } 133 /** A method to invalidate the current set of controls, as they become obselete. 134 134 * @see org.greenstone.gatherer.cdm.MetadataSetManager.Control 135 135 */ 136 137 138 139 140 141 142 136 public void invalidateControls() { 137 if(controls != null) { 138 controls.destroy(); 139 } 140 controls = null; 141 } 142 /** Called when a metadata value has undergone significant change. 143 143 * @param event A <strong>MSMEvent</strong> which encapsulates relevant data about the change. 144 144 */ 145 146 147 148 145 public void metadataChanged(MSMEvent event) { 146 // Couldn't care less. 147 } 148 /** Called when a set is added or removed from the MSM. 149 149 * @param event A <strong>MSMEvent</strong> which encapsulates relevant data about the change. 150 150 */ 151 152 153 154 155 156 151 public void setChanged(MSMEvent event) { 152 // Reload model. 153 clear(); 154 loadMetadataSets(); 155 } 156 /** Select the selected element, given its name, and return the bounds of the selection. Used during search and replace. 157 157 * @param element The elements fully qualified name as a <strong>String</strong>. 158 158 * @return The bounds of the selection as a <strong>Rectangle</strong>. 159 159 * @see org.greenstone.gatherer.cdm.MetadataSetManager.Control 160 160 */ 161 162 163 164 165 166 167 161 public Rectangle setSelectedElement(String element) { 162 if(controls != null) { 163 return controls.setSelectedElement(element); 164 } 165 return null; 166 } 167 /** Prints out the contents of this manager, as they would appear in the collection configuration file. 168 168 * @return A <strong>String</strong> containing a block of commands. 169 169 * @see org.greenstone.gatherer.cdm.MetadataSetManager.SetWrapper 170 170 */ 171 172 173 174 175 176 177 178 179 180 171 public String toString() { 172 String text = ""; 173 for(int i = 0; i < size(); i++) { 174 SetWrapper set = (SetWrapper)get(i); 175 text = text + set.toString() + "\n"; 176 } 177 text = text + "\n"; 178 return text; 179 } 180 /** Called when a significant change has occured to a value tree for a certain element, however we take no further action. 181 181 * @param event A <strong>MSMEvent</strong> containing information relevant to the event. 182 182 */ 183 184 185 183 public void valueChanged(MSMEvent event) { 184 } 185 /** Method to retrieve a phrase from the dictionary based on a key. 186 186 * @param key A <strong>String</strong> used to find the correct phrase. 187 187 * @param args A <strong>String[]</strong> of arguments used in formatting and filling out the phrase. 188 188 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. 189 189 */ 190 191 192 193 190 private String get(String key) { 191 return get(key, null); 192 } 193 /** Method to retrieve a phrase from the dictionary based on a key. 194 194 * @param key A <strong>String</strong> used to find the correct phrase. 195 195 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. … … 197 197 * @see org.greenstone.gatherer.Gatherer 198 198 */ 199 200 201 202 203 204 205 199 private String get(String key, String args[]) { 200 if(key.indexOf(".") == -1) { 201 key = "CDM.MetadataSetManager." + key; 202 } 203 return gatherer.dictionary.get(key, args); 204 } 205 /** Retrieves the current list of loaded metadata sets, and builds a list model around them. 206 206 * @see org.greenstone.gatherer.Gatherer 207 207 * @see org.greenstone.gatherer.cdm.MetadataSetManager.SetWrapper … … 210 210 * @see org.greenstone.gatherer.msm.MetadataSetManager 211 211 */ 212 private void loadMetadataSets() { 213 // We initialize the set_model with wrapped metadata sets. Note that when we call getSets() we also end up adding ourselves as a listener to the metadata set manager. 214 Vector sets = gatherer.c_man.getCollection().msm.getSets(); 215 for(int i = 0; i < sets.size(); i++) { 216 addElement(new SetWrapper((MetadataSet)sets.get(i))); 217 } 218 } 219 /** This class creates and lays-out the various controls for reviewing the metadata sets, and their commands as they would appear in the collection configuration file. */ 220 private class Control 221 extends JPanel { 222 /** The label denoting the element list. */ 223 private JLabel element_label = null; 224 /** The label denoting the set list. */ 225 private JLabel set_label = null; 226 /** The title of these controls. */ 227 private JLabel title = null; 228 /** The list of elements for the choosen set. */ 229 private JList element_list = null; 230 /** The list of sets in this collection. */ 231 private JList set_list = null; 232 /** The panel onto which all other panels will be placed. */ 233 private JPanel central_pane = null; 234 /** The panel onto which the element list will be placed. */ 235 private JPanel element_pane = null; 236 /** The panel containing the title and instructions. */ 237 private JPanel header_pane = null; 238 /** The panel containing the set list. */ 239 private JPanel set_pane = null; 240 /** The text area of inline instructions. */ 241 private JTextArea instructions = null; 242 /** The element model for the currently selected set. */ 243 private Vector element_model = null; 244 /* Constructor. 245 * @see org.greenstone.gatherer.Coloring; 246 * @see org.greenstone.gatherer.Dictionary 247 * @see org.greenstone.gatherer.cdm.MetadataSetManager.ListListener 248 * @see org.greenstone.gatherer.msm.MetadataSetManager 212 private void loadMetadataSets() { 213 // We initialize the set_model with wrapped metadata sets. Note that when we call getSets() we also end up adding ourselves as a listener to the metadata set manager. 214 Vector sets = gatherer.c_man.getCollection().msm.getSets(); 215 for(int i = 0; i < sets.size(); i++) { 216 addElement(new SetWrapper((MetadataSet)sets.get(i))); 217 } 218 } 219 /** This class creates and lays-out the various controls for reviewing the metadata sets, and their commands as they would appear in the collection configuration file. */ 220 private class Control 221 extends JPanel { 222 /** The label denoting the element list. */ 223 private JLabel element_label = null; 224 /** The label denoting the set list. */ 225 private JLabel set_label = null; 226 /** The title of these controls. */ 227 private JLabel title = null; 228 /** The list of elements for the choosen set. */ 229 private JList element_list = null; 230 /** The list of sets in this collection. */ 231 private JList set_list = null; 232 /** The panel onto which all other panels will be placed. */ 233 private JPanel central_pane = null; 234 /** The panel onto which the element list will be placed. */ 235 private JPanel element_pane = null; 236 /** The panel containing the title and instructions. */ 237 private JPanel header_pane = null; 238 /** The panel containing the set list. */ 239 private JPanel set_pane = null; 240 /** The text area of inline instructions. */ 241 private JTextArea instructions = null; 242 /** The element model for the currently selected set. */ 243 private Vector element_model = null; 244 /* Constructor. 245 * @see org.greenstone.gatherer.Coloring; 246 * @see org.greenstone.gatherer.Dictionary 247 * @see org.greenstone.gatherer.cdm.MetadataSetManager.ListListener 248 * @see org.greenstone.gatherer.msm.MetadataSetManager 249 */ 250 public Control() { 251 // Create visual components 252 central_pane = new JPanel(); 253 element_label = new JLabel(get("Elements")); 254 element_label.setHorizontalAlignment(JLabel.CENTER); 255 element_label.setOpaque(true); 256 element_list = new JList(); 257 element_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 258 element_pane = new JPanel(); 259 header_pane = new JPanel(); 260 instructions = new JTextArea(get("Instructions")); 261 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 262 instructions.setEditable(false); 263 instructions.setLineWrap(true); 264 instructions.setRows(5); 265 instructions.setWrapStyleWord(true); 266 set_label = new JLabel(get("Sets")); 267 set_label.setHorizontalAlignment(JLabel.CENTER); 268 set_label.setOpaque(true); 269 set_list = new JList(model); 270 set_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 271 set_pane = new JPanel(); 272 title = new JLabel(get("Title")); 273 title.setHorizontalAlignment(JLabel.CENTER); 274 title.setOpaque(true); 275 // Add listeners 276 set_list.addListSelectionListener(new ListListener()); 277 // Layout 278 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 279 280 header_pane.setLayout(new BorderLayout()); 281 header_pane.add(title, BorderLayout.NORTH); 282 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 283 284 set_pane.setLayout(new BorderLayout()); 285 set_pane.add(set_label, BorderLayout.NORTH); 286 set_pane.add(new JScrollPane(set_list), BorderLayout.CENTER); 287 288 element_pane.setLayout(new BorderLayout()); 289 element_pane.add(element_label, BorderLayout.NORTH); 290 element_pane.add(new JScrollPane(element_list), BorderLayout.CENTER); 291 292 central_pane.setLayout(new GridLayout(2,1)); 293 central_pane.add(set_pane); 294 central_pane.add(element_pane); 295 296 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 297 setLayout(new BorderLayout()); 298 add(header_pane, BorderLayout.NORTH); 299 add(central_pane, BorderLayout.CENTER); 300 } 301 /** Destructor. 249 302 */ 250 public Control() { 251 // Create visual components 252 central_pane = new JPanel(); 253 element_label = new JLabel(get("Elements")); 254 element_label.setHorizontalAlignment(JLabel.CENTER); 255 element_label.setOpaque(true); 256 element_list = new JList(); 257 element_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 258 element_pane = new JPanel(); 259 header_pane = new JPanel(); 260 instructions = new JTextArea(get("Instructions")); 261 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 262 instructions.setEditable(false); 263 instructions.setLineWrap(true); 264 instructions.setRows(5); 265 instructions.setWrapStyleWord(true); 266 set_label = new JLabel(get("Sets")); 267 set_label.setHorizontalAlignment(JLabel.CENTER); 268 set_label.setOpaque(true); 269 set_list = new JList(model); 270 set_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 271 set_pane = new JPanel(); 272 title = new JLabel(get("Title")); 273 title.setHorizontalAlignment(JLabel.CENTER); 274 title.setOpaque(true); 275 // Add listeners 276 set_list.addListSelectionListener(new ListListener()); 277 // Layout 278 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 279 280 header_pane.setLayout(new BorderLayout()); 281 header_pane.add(title, BorderLayout.NORTH); 282 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 283 284 set_pane.setLayout(new BorderLayout()); 285 set_pane.add(set_label, BorderLayout.NORTH); 286 set_pane.add(new JScrollPane(set_list), BorderLayout.CENTER); 287 288 element_pane.setLayout(new BorderLayout()); 289 element_pane.add(element_label, BorderLayout.NORTH); 290 element_pane.add(new JScrollPane(element_list), BorderLayout.CENTER); 291 292 central_pane.setLayout(new GridLayout(2,1)); 293 central_pane.add(set_pane); 294 central_pane.add(element_pane); 295 296 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 297 setLayout(new BorderLayout()); 298 add(header_pane, BorderLayout.NORTH); 299 add(central_pane, BorderLayout.CENTER); 300 } 301 /** Destructor. 302 */ 303 public void destroy() { 304 } 305 /** Update the element list. */ 306 public void refreshElementList() { 307 element_list.updateUI(); 308 } 309 /** Select the selected element, given its name, and return the bounds of the selection. Used during search and replace. 303 public void destroy() { 304 } 305 /** Update the element list. */ 306 public void refreshElementList() { 307 element_list.updateUI(); 308 } 309 /** Select the selected element, given its name, and return the bounds of the selection. Used during search and replace. 310 310 * @param element The elements fully qualified name as a <strong>String</strong>. 311 311 * @return The bounds of the selection as a <strong>Rectangle</strong>. … … 313 313 * @see org.greenstone.gatherer.cdm.MetadataSetManager.Control.ElementWrapper 314 314 */ 315 316 315 public Rectangle setSelectedElement(String element) { 316 Rectangle bounds = null; 317 317 // Parse off namespace 318 318 String namespace = element.substring(0, element.indexOf(".")); 319 319 // Force the set list to show the correct entry. 320 321 322 323 324 325 326 320 ListModel s_model = set_list.getModel(); 321 for(int i = 0; i < s_model.getSize(); i++) { 322 SetWrapper sw = (SetWrapper) s_model.getElementAt(i); 323 if(sw.getSet().getNamespace().equals(namespace)) { 324 set_list.setSelectedValue(sw, true); 325 } 326 } 327 327 // Force the element list to show that name. 328 329 330 331 332 333 334 335 328 ListModel e_model = element_list.getModel(); 329 for(int i = 0; i < e_model.getSize(); i++) { 330 ElementWrapper ew = (ElementWrapper) e_model.getElementAt(i); 331 if(ew.name().equals(element)) { 332 element_list.setSelectedValue(ew, true); 333 bounds = element_list.getCellBounds(i, i); 334 } 335 } 336 336 // Return the bounds of the selected row. 337 338 339 337 return bounds; 338 } 339 /** Overriden to ensure the instruction area is scrolled to top. 340 340 */ 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 341 public void updateUI() { 342 if(instructions != null) { 343 instructions.setCaretPosition(0); 344 } 345 super.updateUI(); 346 } 347 348 private class ListListener 349 implements ListSelectionListener { 350 public void valueChanged(ListSelectionEvent event) { 351 if(!set_list.isSelectionEmpty()) { 352 MetadataSet set = ((SetWrapper)set_list.getSelectedValue()).getSet(); 353 NodeList elements = set.getElements(); 354 element_model = new Vector(); 355 for(int i = 0; i < elements.getLength(); i++) { 356 element_model.add(new ElementWrapper(elements.item(i))); 357 } 358 Collections.sort(element_model); 359 element_list.setListData(element_model); 360 } 361 } 362 } 363 } 364 /** Provides a convience wrapper around a metadata set, that allows it to appear in the <strong>JList</strong> the same way it would in the collection configuration file. */ 365 private class SetWrapper { 366 private MetadataSet set = null; 367 public SetWrapper(MetadataSet set) { 368 this.set = set; 369 } 370 public MetadataSet getSet() { 371 return set; 372 } 373 public String toString() { 374 String namespace = set.getNamespace(); 375 375 // Watch out for the greenstone set, with its namespace of "" 376 377 378 379 380 381 376 if(namespace == null || namespace.length() == 0) { 377 namespace = "greenstone"; 378 } 379 return "metadataset " + namespace + " \"" + set.getFile().getName() + "\""; 380 } 381 } 382 382 } -
trunk/gli/src/org/greenstone/gatherer/cdm/ParsingProgress.java
r4293 r4366 60 60 import javax.swing.JProgressBar; 61 61 /** This class provides a progress bar to be displayed whenever the module must reload the plugin and classifier information, either automatically or if the user indicates a reload is needed. 62 * @author John Thompson, Greenstone Digital Library, University of Waikato63 * @version 2.364 */62 * @author John Thompson, Greenstone Digital Library, University of Waikato 63 * @version 2.3 64 */ 65 65 public class ParsingProgress 66 67 68 69 70 71 72 73 74 75 76 66 extends JDialog { 67 /** The content pane within the dialog box. */ 68 private JPanel content_pane = null; 69 /** The progress bar itself. */ 70 private JProgressBar progress = null; 71 /** The default size of the progress dialog. */ 72 static final Dimension SIZE = new Dimension(400,75); 73 /** Constructor. 74 * @param title The title to show on the dialog, as a <strong>String</strong>. 75 * @param message The message to show on the dialog, as a <strong>String</strong>. 76 * @param max The total number of plugins/classifiers that have to be parsed before the progress bar can be disposed of, as an <i>int</i>. 77 77 */ 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 78 public ParsingProgress(String title, String message, int max) { 79 super(); 80 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 81 this.setSize(SIZE); 82 this.setTitle(title); 83 // Creation 84 this.content_pane = (JPanel) getContentPane(); 85 this.progress = new JProgressBar(); 86 this.progress.setMaximum(max); 87 // Layout 88 this.content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 89 this.content_pane.setLayout(new BorderLayout()); 90 this.content_pane.add(new JLabel(message), BorderLayout.NORTH); 91 this.content_pane.add(progress, BorderLayout.CENTER); 92 // Center and display 93 Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); 94 setLocation((screen_size.width - SIZE.width) / 2, 95 (screen_size.height - SIZE.height) / 2); 96 show(); 97 } 98 public void destroy() { 99 } 100 /** Method which increments the progress count by one, which should be called after every successful parsing of a classifier or plugin. 101 101 */ 102 103 104 102 public void inc() { 103 progress.setValue(progress.getValue() + 1); 104 } 105 105 } -
trunk/gli/src/org/greenstone/gatherer/cdm/PlugIn.java
r4293 r4366 63 63 // #################################################################################### 64 64 public class PlugIn 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 65 extends ArrayList 66 implements ArgumentContainer, Comparable, Serializable { 67 /** A reference to the plugin that this one inherits from. */ 68 private PlugIn super_plugin = null; 69 /** A string of custom arguments to pass to the plugin. */ 70 private String custom = null; 71 /** A description of this plugin. */ 72 private String desc = null; 73 /** The name of the plugin as it would appear in the collect.cfg file. */ 74 private String name = null; 75 /** Default Constructor. 76 */ 77 public PlugIn() { 78 super(); 79 } 80 /** Constructor. 81 81 * @param name The name of this plugin as a <strong>String</strong>. 82 82 * @param desc A description of this plugin as a <strong>String</strong>. 83 83 * @param super_plugin The super class of this plugin, as a <strong>PlugIn</strong>. 84 84 */ 85 86 87 88 89 90 91 85 public PlugIn(String name, String desc, PlugIn super_plugin) { 86 super(); 87 this.desc = desc; 88 this.name = name; 89 this.super_plugin = super_plugin; 90 } 91 /** Method to add an argument to this plugin. Only adds the argument if it isn't already present. 92 92 * @param argument The <strong>Argument</strong> to add. 93 93 */ 94 95 96 97 98 99 100 94 public void addArgument(Argument argument) { 95 if(!contains(argument)) { 96 add(argument); 97 argument.setOwner(name); 98 } 99 } 100 /** Method to compare two plugins for ordering. 101 101 * @param object The plugin we are comparing to, as an <strong>Object</strong>. 102 102 * @return An <i>int</i> specifying the plugin order, using values as set out in <strong>String</strong>. 103 103 * @see java.lang.String#compareTo 104 104 */ 105 106 107 108 109 110 111 112 105 public int compareTo(Object object) { 106 if(object != null && object instanceof PlugIn) { 107 PlugIn plugin = (PlugIn) object; 108 return name.compareTo(plugin.getName()); 109 } 110 return -1; 111 } 112 /** This method produces a deep copy of this plugin. Note that this also creates a new copy of each of the super classes of plugins as well. This is the way it should be, as each assigned plugin may have different values for the higher plugins (such as BasPlug). 113 113 * @return A newly created <strong>PlugIn</strong> with the same details and <strong>Argument</strong>s as this one. 114 114 */ 115 116 117 118 119 120 121 122 123 124 125 126 127 128 115 public PlugIn copy() { 116 PlugIn copy = null; 117 if(super_plugin == null) { 118 copy = new PlugIn(name, desc, null); 119 } 120 else { 121 copy = new PlugIn(name, desc, super_plugin.copy()); 122 } 123 for(int i = 0; i < size(); i++) { 124 copy.addArgument(((Argument)get(i)).copy()); 125 } 126 return copy; 127 } 128 /** Method to determine if two plugins are equal. 129 129 * @param object The plugin to test against, as an <strong>Object</strong>. 130 130 * @return <i>true</i> if the plugin names match, <i>false</i> otherwise. 131 131 */ 132 133 134 135 136 137 138 132 public boolean equals(Object object) { 133 if(object != null && compareTo(object) == 0) { 134 return true; 135 } 136 return false; 137 } 138 /** Method to retrieve an argument by its name. 139 139 * @param name The name of the argument as a <strong>String</strong>. 140 140 * @return The <strong>Argument</strong> requested, or <i>null</i> if no such argument. 141 141 */ 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 142 public Argument getArgument(String name) { 143 // The name given may still include the '-' 144 if(name.startsWith("-")) { 145 name = name.substring(1); 146 } 147 ArrayList arguments = getArguments(); 148 for(int i = 0; i < arguments.size(); i++) { 149 Argument argument = (Argument)arguments.get(i); 150 if(argument.getName().equals(name)) { 151 return argument; 152 } 153 } 154 return null; 155 } 156 /** Method to retrieve all of the arguments available to a plugin, including both specific and general ones. 157 157 * @return A <strong>Hashtable</strong> of arguments, with <name> -> <argument> entries. 158 158 */ 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 159 public ArrayList getArguments() { 160 ArrayList all_arguments = new ArrayList(this); 161 Collections.sort(all_arguments); 162 if(super_plugin != null) { 163 ArrayList super_arguments = super_plugin.getArguments(); 164 for(int i = 0; i < super_arguments.size(); i++) { 165 Object argument = super_arguments.get(i); 166 if(!all_arguments.contains(argument)) { 167 all_arguments.add(argument); 168 } 169 } 170 } 171 return all_arguments; 172 } 173 /** Method to retrieve a plugins custom argument information. 174 174 * @return The custom arguments as a <strong>String</strong>. 175 175 */ 176 177 178 179 176 public String getCustom() { 177 return custom; 178 } 179 /** Method to retrieve a plugins name. 180 180 * @return A <strong>String</strong> containing the plugins name. 181 181 */ 182 183 184 185 186 187 188 182 public String getName() { 183 return name; 184 } 185 public void setCustom(String custom) { 186 this.custom = custom; 187 } 188 /** Method to set the value of desc. 189 189 * @param desc The new value of desc as a <strong>String</strong>. 190 190 */ 191 192 193 194 191 public void setDesc(String desc) { 192 this.desc = desc; 193 } 194 /** Method to set the value of name. 195 195 * @param name The new value of name as a <strong>String</strong>. 196 196 */ 197 198 199 200 197 public void setName(String name) { 198 this.name = name; 199 } 200 /** Method to set the value of the super_plugin. 201 201 * @param super_plugin The new value of super_plugin as a <strong>PlugIn</strong>, or <i>null</i> if this class has no inheritance. 202 202 */ 203 204 205 206 203 public void setSuper(PlugIn super_plugin) { 204 this.super_plugin = super_plugin; 205 } 206 /** Method to print out this plugin as it would appear as a command within the collection configuration file. 207 207 * @return A <strong>String</strong> containing a single plugin command. 208 208 */ 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 209 public String toString() { 210 String text = "plugin " + name + " "; 211 ArrayList arguments = getArguments(); 212 for(int i = 0; i < arguments.size(); i++) { 213 Argument argument = (Argument)arguments.get(i); 214 if(argument.isAssigned()) { 215 text = text + argument.toString(); 216 if(i < arguments.size() - 1) { 217 text = text + " "; 218 } 219 } 220 } 221 // Now print custom arguments if any. 222 if(custom != null) { 223 text = text + " " + custom; 224 } 225 return text; 226 } 227 227 } 228 228 -
trunk/gli/src/org/greenstone/gatherer/cdm/PlugInManager.java
r4293 r4366 103 103 /** This class is resposible for maintaining a list of known plug-ins, and importing new plugins using the parser. */ 104 104 public class PlugInManager { 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 105 /** A reference to the main manager for this module. */ 106 private CollectionDesignManager manager = null; 107 /** The controls for editing the contents of this manager. */ 108 private Control controls = null; 109 /** A list of assigned plugins. */ 110 private DynamicListModel assigned = null; 111 /** A list of those plugins that have not yet been assigned. Begins as a copy of reserve. */ 112 private DynamicListModel available = null; 113 /** A list of known, but currently unassigned, plug-ins. */ 114 private DynamicListModel reserve = null; 115 /** A reference to the Gatherer. */ 116 private Gatherer gatherer = null; 117 /** The current index of the separator. */ 118 private JPanel separator = null; 119 /** The default size for a label. */ 120 static final private Dimension LABEL_SIZE = new Dimension(140, 20); 121 /** Constructor. 122 */ 123 public PlugInManager(Gatherer gatherer, CollectionDesignManager manager) { 124 this.assigned = new DynamicListModel(); 125 this.gatherer = gatherer; 126 this.manager = manager; 127 this.separator = getSeparator(); 128 // Add the movement separator 129 assigned.addElement(separator); 130 loadPlugIns(); 131 savePlugIns(); 132 } 133 /** Method to add a new plugin to reserve. 134 134 * @param plugin The new <strong>PlugIn</strong>. 135 135 */ 136 137 138 139 140 141 142 136 public void addPlugIn(PlugIn plugin) { 137 if(!reserve.contains(plugin)) { 138 reserve.addElement(plugin); 139 available.addElement(plugin); 140 } 141 } 142 /** Method to assign a plugin. 143 143 * @param plugin The reserve <strong>PlugIn</strong> to assign. 144 144 */ 145 146 147 148 149 150 151 152 153 145 public void assignPlugIn(PlugIn plugin) { 146 if(!assigned.contains(plugin)) { 147 if(plugin.getName().equals("RecPlug") || plugin.getName().equals("ArcPlug")) { 148 assigned.addElement(plugin); // Adds after separator 149 } 150 else { 151 int index = assigned.indexOf(separator); 152 assigned.add(index, plugin); 153 } 154 154 // Remove from available 155 156 157 158 159 155 available.removeElement(plugin); 156 gatherer.c_man.configurationChanged(); 157 } 158 } 159 /** Method to retrieve the control for this manager. 160 160 * @return A <strong>JPanel</strong> containing the controls. 161 161 */ 162 163 164 165 166 167 168 162 public JPanel getControls() { 163 if(controls == null) { 164 controls = new Control(); 165 } 166 return controls; 167 } 168 /** Method to retrieve the named plugin. 169 169 * @param name The name of the desired plugin as a <strong>String</strong>. 170 170 * @return The requested <strong>PlugIn</strong> or <i>null</i> if no such plugin exists. 171 171 */ 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 */ 187 188 189 190 191 192 193 172 public PlugIn getPlugIn(String name) { 173 for(int i = 0; i < reserve.size(); i++) { 174 Object object = reserve.get(i); 175 if(object instanceof PlugIn) { 176 PlugIn plugin = (PlugIn) object; 177 if(plugin.getName().equals(name)) { 178 return plugin; 179 } 180 } 181 } 182 // No success. 183 return null; 184 } 185 /** Method to invalidate controls after a significant change in the system state. 186 */ 187 public void invalidateControls() { 188 if(controls != null) { 189 controls.destroy(); 190 } 191 controls = null; 192 } 193 /** Method to load the details of a single plug-in. 194 194 * @param plugin The plugin <strong>File</strong> you wish to load. 195 195 */ 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 196 public void loadPlugIn(File plugin) { 197 Document document = null; 198 // Run pluginfo on this plugin, and then send the results for parsing. 199 try { 200 String args[] = null; 201 if(Utility.isWindows()) { 202 args = new String[4]; 203 if(gatherer.config.perl_path != null) { 204 args[0] = gatherer.config.perl_path + "Perl.exe"; 205 } 206 else { 207 args[0] = "Perl.exe"; 208 } 209 args[1] = gatherer.config.gsdl_path + "bin" + File.separator + "script" + File.separator + "pluginfo.pl"; 210 args[2] = "-xml"; 211 args[3] = getPlugInName(plugin); 212 } 213 else { 214 args = new String[3]; 215 args[0] = "pluginfo.pl"; 216 args[1] = "-xml"; 217 args[2] = getPlugInName(plugin); 218 } 219 for(int i = 0; i < args.length; i++) { 220 ///ystem.out.print(args[i] + " "); 221 } 222 222 ///ystem.out.print("\n"); 223 223 // Create the process. 224 225 224 Runtime runtime = Runtime.getRuntime(); 225 Process process = runtime.exec(args); 226 226 //InputStream input_stream = process.getErrorStream(); 227 228 229 230 231 232 233 234 235 227 BufferedReader error_in = new BufferedReader(new InputStreamReader(process.getErrorStream())); 228 String line = ""; 229 StringBuffer xml = new StringBuffer(""); 230 while((line = error_in.readLine()) != null) { 231 xml.append(line); 232 xml.append("\n"); 233 } 234 line = null; 235 error_in = null; 236 236 ///ystem.out.println("\n"); 237 237 // Then read the xml from the piped input stream. 238 239 240 241 242 243 244 245 246 247 248 249 250 238 InputSource source = new InputSource(new StringReader(xml.toString())); 239 DOMParser parser = new DOMParser(); 240 parser.parse(source); 241 document = parser.getDocument(); 242 } 243 catch (Exception error) { 244 error.printStackTrace(); 245 } 246 if(document != null) { 247 parse(document.getDocumentElement()); 248 } 249 } 250 /** Method to move a plugin higher in the list order. 251 251 * @param plugin The <strong>PlugIn</strong> you want to move. 252 252 * @param direction <i>true</i> to move the plugin up, <i>false</i> to move it down. 253 253 * @param all <i>true</i> to move to move all the way, <i>false</i> for a single step. 254 254 */ 255 public void movePlugIn(PlugIn plugin, boolean direction, boolean all) { 256 // Can't ever move RecPlug or ArcPlug. 257 if(plugin.getName().equals("ArcPlug") || plugin.getName().equals("RecPlug")) { 258 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.Fixed"), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 259 return; 260 } 261 if(all) { 262 if(direction) { 263 assigned.removeElement(plugin); 264 assigned.add(0, plugin); 265 gatherer.c_man.configurationChanged(); 255 public void movePlugIn(PlugIn plugin, boolean direction, boolean all) { 256 // Can't ever move RecPlug or ArcPlug. 257 if(plugin.getName().equals("ArcPlug") || plugin.getName().equals("RecPlug")) { 258 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.Fixed"), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 259 return; 260 } 261 if(all) { 262 if(direction) { 263 assigned.removeElement(plugin); 264 assigned.add(0, plugin); 265 gatherer.c_man.configurationChanged(); 266 } 267 else { 268 assigned.removeElement(plugin); 269 int index = assigned.size() - 1; 270 boolean found = false; 271 while(!found) { 272 Object temp = assigned.get(index); 273 if(temp instanceof PlugIn) { 274 PlugIn current = (PlugIn) temp; 275 if(current.getName().equals("ArcPlug") || current.getName().equals("RecPlug")) { 276 index--; 277 } 278 else { 279 found = true; 280 } 281 } 282 else { 283 found = true; 284 } 285 } 286 assigned.add(index, plugin); 287 gatherer.c_man.configurationChanged(); 288 } 289 } 290 else { 291 // Try to move the plugin one step in the desired direction. 292 int index = assigned.indexOf(plugin); 293 ///ystem.err.println("Index of " + plugin + " = " + index); 294 if(direction) { 295 index--; 296 if(index < 0) { 297 String args[] = new String[1]; 298 args[0] = plugin.getName(); 299 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.At_Top", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 300 return; 301 } 302 assigned.removeElement(plugin); 303 assigned.add(index, plugin); 304 gatherer.c_man.configurationChanged(); 305 } 306 else { 307 index++; 308 Object object = assigned.get(index); 309 if(index == assigned.size()) { 310 String args[] = new String[1]; 311 args[0] = plugin.getName(); 312 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.At_Bottom", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 313 // Still not going to move RecPlug or ArcPlug. 314 return; 315 } 316 else if(!(object instanceof PlugIn) || ((PlugIn)object).getName().equals("ArcPlug") || ((PlugIn)object).getName().equals("RecPlug")) { 317 String args[] = new String[1]; 318 args[0] = plugin.getName(); 319 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.Cannot", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 320 // Still not going to move RecPlug or ArcPlug. 321 return; 322 } 323 assigned.removeElement(plugin); 324 assigned.add(index, plugin); 325 gatherer.c_man.configurationChanged(); 326 } 327 } 328 } 329 330 /** This method attempts to parse a plugin command from a command string taken from the collection configuration file. This process is quite complex as not only must the correct plugin be matched by also all of the parameters given must be legal. If such a command is found, the plugin is immediately assigned. 331 * @param command The coomand <strong>String</strong> that may include plugin information. 332 * @return <i>true</i> if a plugin command was parsed, <i>false</i> otherwise. 333 */ 334 public boolean parse(String command) { 335 String command_lc = command.toLowerCase(); 336 if(command_lc.startsWith("plugin")) { 337 CommandTokenizer tokenizer = new CommandTokenizer(command); 338 if(tokenizer.countTokens() >= 2) { 339 tokenizer.nextToken(); // Throw away 'plugin' 340 String name = tokenizer.nextToken(); 341 // Try to locate the plugin with this name. 342 PlugIn plugin = getPlugIn(name); 343 // And if successful start to parse the arguments. 344 if(plugin != null) { 345 // Take a copy. 346 plugin = plugin.copy(); 347 String key = null; 348 while(tokenizer.hasMoreTokens()) { 349 if(key == null) { 350 key = tokenizer.nextToken(); 351 } 352 // Try to retrieve a matching argument. 353 Argument argument = plugin.getArgument(key); 354 if(argument != null) { 355 // Set as assigned. 356 argument.setAssigned(true); 357 // And if the argument is of a parameter type, parse a parameter. 358 if(argument.getType() != Argument.FLAG && tokenizer.hasMoreTokens()) { 359 argument.setValue(tokenizer.nextToken()); 360 } 361 key = null; 362 } 363 // Argument cannot be matched. 364 else { 365 String cur_key = key; 366 String value = tokenizer.nextToken(); 367 if(value.startsWith("-")) { 368 key = value; 369 value = null; 370 } 371 else { 372 key = null; 373 } 374 String custom = plugin.getCustom(); 375 if(custom == null) { 376 if(value == null) { 377 plugin.setCustom(cur_key); 266 378 } 267 379 else { 268 assigned.removeElement(plugin); 269 int index = assigned.size() - 1; 270 boolean found = false; 271 while(!found) { 272 Object temp = assigned.get(index); 273 if(temp instanceof PlugIn) { 274 PlugIn current = (PlugIn) temp; 275 if(current.getName().equals("ArcPlug") || current.getName().equals("RecPlug")) { 276 index--; 277 } 278 else { 279 found = true; 280 } 281 } 282 else { 283 found = true; 284 } 285 } 286 assigned.add(index, plugin); 287 gatherer.c_man.configurationChanged(); 380 plugin.setCustom(cur_key + " " + value); 288 381 } 289 } 290 else { 291 // Try to move the plugin one step in the desired direction. 292 int index = assigned.indexOf(plugin); 293 ///ystem.err.println("Index of " + plugin + " = " + index); 294 if(direction) { 295 index--; 296 if(index < 0) { 297 String args[] = new String[1]; 298 args[0] = plugin.getName(); 299 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.At_Top", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 300 return; 301 } 302 assigned.removeElement(plugin); 303 assigned.add(index, plugin); 304 gatherer.c_man.configurationChanged(); 382 } 383 else { 384 if(value == null) { 385 plugin.setCustom(custom + " " + cur_key); 305 386 } 306 387 else { 307 index++; 308 Object object = assigned.get(index); 309 if(index == assigned.size()) { 310 String args[] = new String[1]; 311 args[0] = plugin.getName(); 312 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.At_Bottom", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 313 // Still not going to move RecPlug or ArcPlug. 314 return; 315 } 316 else if(!(object instanceof PlugIn) || ((PlugIn)object).getName().equals("ArcPlug") || ((PlugIn)object).getName().equals("RecPlug")) { 317 String args[] = new String[1]; 318 args[0] = plugin.getName(); 319 JOptionPane.showMessageDialog(manager.gui, get("CDM.Move.Cannot", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE); 320 // Still not going to move RecPlug or ArcPlug. 321 return; 322 } 323 assigned.removeElement(plugin); 324 assigned.add(index, plugin); 325 gatherer.c_man.configurationChanged(); 388 plugin.setCustom(custom + " " + cur_key + " " + value); 326 389 } 327 } 328 } 329 330 /** This method attempts to parse a plugin command from a command string taken from the collection configuration file. This process is quite complex as not only must the correct plugin be matched by also all of the parameters given must be legal. If such a command is found, the plugin is immediately assigned. 331 * @param command The coomand <strong>String</strong> that may include plugin information. 332 * @return <i>true</i> if a plugin command was parsed, <i>false</i> otherwise. 333 */ 334 public boolean parse(String command) { 335 String command_lc = command.toLowerCase(); 336 if(command_lc.startsWith("plugin")) { 337 CommandTokenizer tokenizer = new CommandTokenizer(command); 338 if(tokenizer.countTokens() >= 2) { 339 tokenizer.nextToken(); // Throw away 'plugin' 340 String name = tokenizer.nextToken(); 341 // Try to locate the plugin with this name. 342 PlugIn plugin = getPlugIn(name); 343 // And if successful start to parse the arguments. 344 if(plugin != null) { 345 // Take a copy. 346 plugin = plugin.copy(); 347 String key = null; 348 while(tokenizer.hasMoreTokens()) { 349 if(key == null) { 350 key = tokenizer.nextToken(); 351 } 352 // Try to retrieve a matching argument. 353 Argument argument = plugin.getArgument(key); 354 if(argument != null) { 355 // Set as assigned. 356 argument.setAssigned(true); 357 // And if the argument is of a parameter type, parse a parameter. 358 if(argument.getType() != Argument.FLAG && tokenizer.hasMoreTokens()) { 359 argument.setValue(tokenizer.nextToken()); 360 } 361 key = null; 362 } 363 // Argument cannot be matched. 364 else { 365 String cur_key = key; 366 String value = tokenizer.nextToken(); 367 if(value.startsWith("-")) { 368 key = value; 369 value = null; 370 } 371 else { 372 key = null; 373 } 374 String custom = plugin.getCustom(); 375 if(custom == null) { 376 if(value == null) { 377 plugin.setCustom(cur_key); 378 } 379 else { 380 plugin.setCustom(cur_key + " " + value); 381 } 382 } 383 else { 384 if(value == null) { 385 plugin.setCustom(custom + " " + cur_key); 386 } 387 else { 388 plugin.setCustom(custom + " " + cur_key + " " + value); 389 } 390 } 391 } 392 } 393 // Slight tweak. If the plugin is the RecPlug we want to set use_metadata_files by default. 394 if(plugin.getName().equalsIgnoreCase("RecPlug")) { 395 Argument argument = plugin.getArgument("-use_metadata_files"); 396 if(argument != null) { 397 argument.setAssigned(true); 398 } 399 } 400 // Second tweak. If the plugin is the HTMLPlug we want to modify the block expression so our backup files are ignored. 401 if(plugin.getName().equalsIgnoreCase("HTMLPlug")) { 402 Argument argument = plugin.getArgument("-block_exp"); 403 if(argument != null) { 404 argument.setValue(argument.getDefaultValue() + "|(~$)"); 405 } 406 } 407 // Add the plugin to our reserve 408 assignPlugIn(plugin); 409 return true; 410 } 411 else { 412 //ystem.err.println("Unknown plugin"); 413 } 414 } 415 } 416 return false; 417 } 418 /** This method removes an assigned plugin. I was tempted to call it unassign, by remove is more consistant. Note that there is no way to remove a plugin from the reserve. 390 } 391 } 392 } 393 // Slight tweak. If the plugin is the RecPlug we want to set use_metadata_files by default. 394 if(plugin.getName().equalsIgnoreCase("RecPlug")) { 395 Argument argument = plugin.getArgument("-use_metadata_files"); 396 if(argument != null) { 397 argument.setAssigned(true); 398 } 399 } 400 // Second tweak. If the plugin is the HTMLPlug we want to modify the block expression so our backup files are ignored. 401 if(plugin.getName().equalsIgnoreCase("HTMLPlug")) { 402 Argument argument = plugin.getArgument("-block_exp"); 403 if(argument != null) { 404 argument.setValue(argument.getDefaultValue() + "|(~$)"); 405 } 406 } 407 // Add the plugin to our reserve 408 assignPlugIn(plugin); 409 return true; 410 } 411 else { 412 //ystem.err.println("Unknown plugin"); 413 } 414 } 415 } 416 return false; 417 } 418 /** This method removes an assigned plugin. I was tempted to call it unassign, by remove is more consistant. Note that there is no way to remove a plugin from the reserve. 419 419 * @param plugin The <strong>PlugIn</strong> to remove. 420 420 */ 421 422 423 424 425 426 427 */ 428 429 430 431 432 433 434 435 436 437 438 421 public void removePlugIn(PlugIn plugin) { 422 assigned.removeElement(plugin); 423 available.addElement(plugin); 424 gatherer.c_man.configurationChanged(); 425 } 426 /** Method to cache the current contents of reserve (known plugins) to file. 427 */ 428 public void savePlugIns() { 429 try { 430 FileOutputStream file = new FileOutputStream(Utility.BASE_DIR + "plugins.dat"); 431 ObjectOutputStream out = new ObjectOutputStream(file); 432 out.writeObject(reserve); 433 out.close(); 434 } 435 catch (Exception error) { 436 } 437 } 438 /** Method used to determine the number of plugins that have been assigned. 439 439 * @return An <i>int</i> which is the number of plugins. 440 440 */ 441 442 443 444 441 public int size() { 442 return assigned.size(); 443 } 444 /** Method to print out a block of plugin commands, much like you'd find in a collection configuration file. 445 445 * @return A <strong>String</strong> containing a series of plugin commands separated by new lines. 446 446 */ 447 448 449 450 451 452 453 454 455 456 457 458 459 447 public String toString() { 448 String text = ""; 449 for(int i = 0; i < assigned.size(); i++) { 450 Object object = assigned.get(i); 451 if(object instanceof PlugIn) { 452 PlugIn plugin = (PlugIn) object; 453 text = text + plugin.toString() + "\n"; 454 } 455 } 456 text = text + "\n"; 457 return text; 458 } 459 /* Retrieve a phrase from the dictionary based on a certain key. 460 460 * @param key The search <strong>String</strong>. 461 461 * @return The matching phrase from the Dictionary. 462 462 */ 463 464 465 466 463 private String get(String key) { 464 return get(key, null); 465 } 466 /* Retrieve a phrase from the dictionary based on a certain key and arguments. 467 467 * @param key The search <strong>String</strong>. 468 468 * @param args A <strong>String[]</strong> of arguments used to complete and format the choosen phrase. 469 469 * @return The matching phrase from the Dictionary. 470 470 */ 471 472 473 474 475 476 477 471 private String get(String key, String args[]) { 472 if(key.indexOf(".") == -1) { 473 key = "CDM.PlugInManager." + key; 474 } 475 return gatherer.dictionary.get(key, args); 476 } 477 /** Method to extract just the plugins name from a file object. 478 478 * @param plugin The <strong>File</strong> which references a certain plugin. 479 479 * @return A <strong>String</strong> containing just the plugins name, without extension. 480 480 */ 481 482 483 484 485 486 487 488 481 private String getPlugInName(File plugin) { 482 String name = plugin.getName(); 483 if(name.indexOf(".") != -1) { 484 name = name.substring(0, name.indexOf(".")); 485 } 486 return name; 487 } 488 /** Method to retrieve the CData value from a node. Requires a search for the #text node. 489 489 * @param node The <strong>Node</strong> whose value we wish to find. 490 490 * @return The value of node as a <strong>String</strong>, or <i>null</i> if no value exists. 491 491 */ 492 493 494 492 private String getValue(Node node) { 493 if(node.hasChildNodes()) { 494 Node text = node.getFirstChild(); 495 495 //ystem.err.println("Value of " + node.getNodeName() + " = " + text.getNodeValue()); 496 497 498 499 500 501 */ 502 503 504 505 506 507 508 509 510 511 512 513 514 515 496 return text.getNodeValue(); 497 } 498 return node.getNodeValue(); 499 } 500 /** Method to initially load information from the standard plug-ins within the gsdl Perl library. 501 */ 502 private void loadPlugIns() { 503 // Attempt to restore the cached file. 504 try { 505 FileInputStream file = new FileInputStream(Utility.BASE_DIR + "plugins.dat"); 506 ObjectInputStream input = new ObjectInputStream(file); 507 reserve = (DynamicListModel) input.readObject(); 508 available = reserve.shallowCopy(); 509 } 510 catch (Exception error) { 511 } 512 if(reserve == null) { 513 available = new DynamicListModel(); 514 reserve = new DynamicListModel(); 515 reserve.setAutoOrder(true); 516 516 // Retrieve the gsdl home directory... 517 518 519 520 521 522 517 String directory = gatherer.config.gsdl_path; 518 directory = directory + "perllib" + File.separator + "plugins" + File.separator; 519 loadPlugIns(new File(directory)); 520 } 521 } 522 /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location. 523 523 * @param directory A <strong>File</strong> indicating the directory to be scanned for plug-ins. 524 524 */ 525 526 527 525 private void loadPlugIns(File directory) { 526 File files[] = directory.listFiles(); 527 if(files != null) { 528 528 // Create a progress indicator. 529 ParsingProgress progress = new ParsingProgress(get("CDM.PlugInManager.Parsing.Title"), get("CDM.PlugInManager.Parsing.Message"), files.length); 530 for(int i = 0; i < files.length; i++) { 531 // We only want to check Perl Modules. 532 if(files[i].getName().endsWith(".pm")) { 533 loadPlugIn(files[i]); 534 } 535 progress.inc(); 529 ParsingProgress progress = new ParsingProgress(get("CDM.PlugInManager.Parsing.Title"), get("CDM.PlugInManager.Parsing.Message"), files.length); 530 for(int i = 0; i < files.length; i++) { 531 // We only want to check Perl Modules. 532 if(files[i].getName().endsWith(".pm")) { 533 loadPlugIn(files[i]); 534 } 535 progress.inc(); 536 } 537 progress.dispose(); 538 } 539 } 540 541 private PlugIn parse(Node root) { 542 PlugIn plugin = new PlugIn(); 543 String node_name = null; 544 for(Node node = root.getFirstChild(); node != null; 545 node = node.getNextSibling()) { 546 node_name = node.getNodeName(); 547 if(node_name.equals("Name")) { 548 String name = getValue(node); 549 // We can save ourselves some processing time if a plugin with this name already exists in our manager. If so retrieve it and return it. 550 PlugIn existing = getPlugIn(name); 551 if(existing != null) { 552 return existing; 553 } 554 plugin.setName(name); 555 } 556 else if(node_name.equals("Desc")) { 557 plugin.setDesc(getValue(node)); 558 } 559 // Parse the multitude of arguments. 560 else if(node_name.equals("Arguments")) { 561 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) { 562 node_name = arg.getNodeName(); 563 // An option. 564 if(node_name.equals("Option")) { 565 Argument argument = new Argument(); 566 // If its an option we parse the multitude of details an options might have. 567 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) { 568 node_name = det.getNodeName(); 569 if(node_name.equals("Name")) { 570 argument.setName(getValue(det)); 571 } 572 else if(node_name.equals("Desc")) { 573 argument.setDesc(getValue(det)); 574 } 575 else if(node_name.equals("Type")) { 576 argument.setType(getValue(det)); 577 } 578 else if(node_name.equals("Default")) { 579 argument.setDefault(getValue(det)); 580 } 581 else if(node_name.equals("List")) { 582 // Two final loops are required to parse lists. 583 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) { 584 if(value.getNodeName().equals("Value")) { 585 String key = null; 586 String desc = ""; 587 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { 588 node_name = subvalue.getNodeName(); 589 if(node_name.equals("Name")) { 590 key = getValue(subvalue); 591 } 592 else if(node_name.equals("Desc")) { 593 desc = getValue(subvalue); 594 } 595 } 596 if(key != null) { 597 argument.addOption(key, desc); 598 } 599 } 536 600 } 537 progress.dispose(); 538 } 539 } 540 541 private PlugIn parse(Node root) { 542 PlugIn plugin = new PlugIn(); 543 String node_name = null; 544 for(Node node = root.getFirstChild(); node != null; 545 node = node.getNextSibling()) { 546 node_name = node.getNodeName(); 547 if(node_name.equals("Name")) { 548 String name = getValue(node); 549 // We can save ourselves some processing time if a plugin with this name already exists in our manager. If so retrieve it and return it. 550 PlugIn existing = getPlugIn(name); 551 if(existing != null) { 552 return existing; 553 } 554 plugin.setName(name); 601 } 602 else if(node_name.equals("Required")) { 603 String v = getValue(det); 604 if(v != null && v.equals("yes")) { 605 argument.setRequired(true); 555 606 } 556 else if(node_name.equals("Desc")) { 557 plugin.setDesc(getValue(node)); 558 } 559 // Parse the multitude of arguments. 560 else if(node_name.equals("Arguments")) { 561 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) { 562 node_name = arg.getNodeName(); 563 // An option. 564 if(node_name.equals("Option")) { 565 Argument argument = new Argument(); 566 // If its an option we parse the multitude of details an options might have. 567 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) { 568 node_name = det.getNodeName(); 569 if(node_name.equals("Name")) { 570 argument.setName(getValue(det)); 571 } 572 else if(node_name.equals("Desc")) { 573 argument.setDesc(getValue(det)); 574 } 575 else if(node_name.equals("Type")) { 576 argument.setType(getValue(det)); 577 } 578 else if(node_name.equals("Default")) { 579 argument.setDefault(getValue(det)); 580 } 581 else if(node_name.equals("List")) { 582 // Two final loops are required to parse lists. 583 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) { 584 if(value.getNodeName().equals("Value")) { 585 String key = null; 586 String desc = ""; 587 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { 588 node_name = subvalue.getNodeName(); 589 if(node_name.equals("Name")) { 590 key = getValue(subvalue); 591 } 592 else if(node_name.equals("Desc")) { 593 desc = getValue(subvalue); 594 } 595 } 596 if(key != null) { 597 argument.addOption(key, desc); 598 } 599 } 600 } 601 } 602 else if(node_name.equals("Required")) { 603 String v = getValue(det); 604 if(v != null && v.equals("yes")) { 605 argument.setRequired(true); 606 } 607 } 608 } 609 plugin.addArgument(argument); 610 } 611 // A super plugin class. 612 else if(node_name.equals("PlugInfo")) { 613 PlugIn super_plugin = parse(arg); 614 plugin.setSuper(super_plugin); 615 } 616 } 617 } 618 } 619 if(plugin.getName() != null) { 620 addPlugIn(plugin); 621 return plugin; 622 } 623 return null; 624 } 625 626 /** A class which provodes controls for assigned and editing plugins. */ 627 private class Control 628 extends JPanel { 629 /** Button for adding plugins. */ 630 private JButton add = null; 631 /** Button for configuring the selected plugin. */ 632 private JButton configure = null; 633 /** Buttom to move an assinged plugin as low in the order as possible. */ 634 private JButton move_bottom = null; 635 /** Button to move an assigned plugin one position lower in the order. */ 636 private JButton move_down = null; 637 /** Button to move an assigned plugin as high in the order as possible. */ 638 private JButton move_top = null; 639 /** Button to move an assigned plugin one position higher in the order. */ 640 private JButton move_up = null; 641 /** Button to remove the selected plugin. */ 642 private JButton remove = null; 643 /** A combobox containing all of the known plugins, including those that may have already been assigned. */ 644 private JComboBox plugin = null; 645 /** The label next to the plugin combobox. */ 646 private JLabel plugin_label = null; 647 /** The label above the assigned plugin list. */ 648 private JLabel plugin_list_label = null; 649 /** The title of this view. */ 650 private JLabel title = null; 651 /** A list of assigned plugins. */ 652 private JList plugin_list = null; 653 /** The area where the add, configure and remove buttons are placed. */ 654 private JPanel button_pane = null; 655 /** The region which divides the central portion of the view into list and controls. */ 656 private JPanel central_pane = null; 657 /** The area where title label and instructions sit. */ 658 private JPanel header_pane = null; 659 /** The area where movement buttons are placed. */ 660 private JPanel movement_pane = null; 661 /** The small region containing the plugin combobox and its label. */ 662 private JPanel plugin_pane = null; 663 /** The pane containing the assigned plugin list and its label. */ 664 private JPanel plugin_list_pane = null; 665 /** The text area containing instructions on the use of this control. */ 666 private JTextArea instructions = null; 667 /** Constructor. 607 } 608 } 609 plugin.addArgument(argument); 610 } 611 // A super plugin class. 612 else if(node_name.equals("PlugInfo")) { 613 PlugIn super_plugin = parse(arg); 614 plugin.setSuper(super_plugin); 615 } 616 } 617 } 618 } 619 if(plugin.getName() != null) { 620 addPlugIn(plugin); 621 return plugin; 622 } 623 return null; 624 } 625 626 /** A class which provodes controls for assigned and editing plugins. */ 627 private class Control 628 extends JPanel { 629 /** Button for adding plugins. */ 630 private JButton add = null; 631 /** Button for configuring the selected plugin. */ 632 private JButton configure = null; 633 /** Buttom to move an assinged plugin as low in the order as possible. */ 634 private JButton move_bottom = null; 635 /** Button to move an assigned plugin one position lower in the order. */ 636 private JButton move_down = null; 637 /** Button to move an assigned plugin as high in the order as possible. */ 638 private JButton move_top = null; 639 /** Button to move an assigned plugin one position higher in the order. */ 640 private JButton move_up = null; 641 /** Button to remove the selected plugin. */ 642 private JButton remove = null; 643 /** A combobox containing all of the known plugins, including those that may have already been assigned. */ 644 private JComboBox plugin = null; 645 /** The label next to the plugin combobox. */ 646 private JLabel plugin_label = null; 647 /** The label above the assigned plugin list. */ 648 private JLabel plugin_list_label = null; 649 /** The title of this view. */ 650 private JLabel title = null; 651 /** A list of assigned plugins. */ 652 private JList plugin_list = null; 653 /** The area where the add, configure and remove buttons are placed. */ 654 private JPanel button_pane = null; 655 /** The region which divides the central portion of the view into list and controls. */ 656 private JPanel central_pane = null; 657 /** The area where title label and instructions sit. */ 658 private JPanel header_pane = null; 659 /** The area where movement buttons are placed. */ 660 private JPanel movement_pane = null; 661 /** The small region containing the plugin combobox and its label. */ 662 private JPanel plugin_pane = null; 663 /** The pane containing the assigned plugin list and its label. */ 664 private JPanel plugin_list_pane = null; 665 /** The text area containing instructions on the use of this control. */ 666 private JTextArea instructions = null; 667 /** Constructor. 668 */ 669 public Control() { 670 Object plugins[] = reserve.toArray(); 671 Vector plugin_model = new Vector(); 672 for(int i = 0; i < plugins.length; i++) { 673 plugin_model.add(((PlugIn)plugins[i]).getName()); 674 } 675 Collections.sort(plugin_model); 676 // Create 677 add = new JButton(get("Add")); 678 add.setMnemonic(KeyEvent.VK_A); 679 button_pane = new JPanel(); 680 central_pane = new JPanel(); 681 configure = new JButton(get("Configure")); 682 configure.setMnemonic(KeyEvent.VK_C); 683 header_pane = new JPanel(); 684 instructions = new JTextArea(get("Instructions")); 685 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 686 instructions.setEditable(false); 687 instructions.setLineWrap(true); 688 instructions.setRows(5); 689 instructions.setWrapStyleWord(true); 690 move_bottom = new JButton(); 691 JLabel move_bottom_label = new JLabel(get("Move_Bottom")); 692 move_bottom_label.setHorizontalAlignment(JLabel.CENTER); 693 move_bottom_label.setPreferredSize(LABEL_SIZE); 694 move_bottom.setLayout(new BorderLayout()); 695 move_bottom.add(new JLabel(Utility.getImage("arrow-bottom.gif")), BorderLayout.WEST); 696 move_bottom.add(move_bottom_label, BorderLayout.CENTER); 697 move_bottom.add(new JLabel(Utility.getImage("arrow-bottom.gif")), BorderLayout.EAST); 698 move_bottom.setMnemonic(KeyEvent.VK_B); 699 move_down = new JButton(); 700 JLabel move_down_label = new JLabel(get("Move_Down")); 701 move_down_label.setHorizontalAlignment(JLabel.CENTER); 702 move_down_label.setPreferredSize(LABEL_SIZE); 703 move_down.setLayout(new BorderLayout()); 704 move_down.add(new JLabel(Utility.getImage("arrow-down.gif")), BorderLayout.WEST); 705 move_down.add(move_down_label, BorderLayout.CENTER); 706 move_down.add(new JLabel(Utility.getImage("arrow-down.gif")), BorderLayout.EAST); 707 move_down.setMnemonic(KeyEvent.VK_D); 708 move_top = new JButton(); 709 JLabel move_top_label = new JLabel(get("Move_Top")); 710 move_top_label.setHorizontalAlignment(JLabel.CENTER); 711 move_top_label.setPreferredSize(LABEL_SIZE); 712 move_top.setLayout(new BorderLayout()); 713 move_top.add(new JLabel(Utility.getImage("arrow-top.gif")), BorderLayout.WEST); 714 move_top.add(move_top_label, BorderLayout.CENTER); 715 move_top.add(new JLabel(Utility.getImage("arrow-top.gif")), BorderLayout.EAST); 716 move_top.setMnemonic(KeyEvent.VK_T); 717 move_up = new JButton(); 718 JLabel move_up_label = new JLabel(get("Move_Up")); 719 move_up_label.setHorizontalAlignment(JLabel.CENTER); 720 move_up_label.setPreferredSize(LABEL_SIZE); 721 move_up.setLayout(new BorderLayout()); 722 move_up.add(new JLabel(Utility.getImage("arrow-up.gif")), BorderLayout.WEST); 723 move_up.add(move_up_label, BorderLayout.CENTER); 724 move_up.add(new JLabel(Utility.getImage("arrow-up.gif")), BorderLayout.EAST); 725 move_up.setMnemonic(KeyEvent.VK_U); 726 movement_pane = new JPanel(); 727 plugin = new JComboBox(available); 728 //plugin.setEditable(true); 729 plugin.setSelectedIndex(0); 730 plugin_label = new JLabel(get("PlugIn")); 731 plugin_list = new JList(assigned); 732 plugin_list.setCellRenderer(new ListRenderer()); 733 plugin_list_label = new JLabel(get("Assigned")); 734 plugin_list_label.setHorizontalAlignment(JLabel.CENTER); 735 plugin_list_label.setOpaque(true); 736 plugin_list_pane = new JPanel(); 737 plugin_pane = new JPanel(); 738 remove = new JButton(get("Remove")); 739 remove.setMnemonic(KeyEvent.VK_R); 740 title = new JLabel(get("Title")); 741 title.setHorizontalAlignment(JLabel.CENTER); 742 title.setOpaque(true); 743 // Listeners 744 add.addActionListener(new AddListener()); 745 configure.addActionListener(new ConfigureListener()); 746 MoveListener ml = new MoveListener(); 747 move_bottom.addActionListener(ml); 748 move_down.addActionListener(ml); 749 move_top.addActionListener(ml); 750 move_up.addActionListener(ml); 751 remove.addActionListener(new RemoveListener()); 752 plugin_list.addMouseListener(new ClickListener()); 753 // Layout 754 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0)); 755 756 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 757 758 header_pane.setLayout(new BorderLayout()); 759 header_pane.add(title, BorderLayout.NORTH); 760 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 761 762 plugin_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2)); 763 764 movement_pane.setLayout(new GridLayout(4,1)); 765 movement_pane.add(move_top); 766 movement_pane.add(move_up); 767 movement_pane.add(move_down); 768 movement_pane.add(move_bottom); 769 770 plugin_list_pane.setLayout(new BorderLayout()); 771 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH); 772 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER); 773 plugin_list_pane.add(movement_pane, BorderLayout.EAST); 774 775 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 776 777 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 778 plugin_pane.setLayout(new GridLayout(1,2)); 779 plugin_pane.add(plugin_label); 780 plugin_pane.add(plugin); 781 782 // Scope these mad bordering skillz. 783 JPanel temp = new JPanel(new BorderLayout()); 784 temp.setBorder 785 (BorderFactory.createCompoundBorder 786 (BorderFactory.createEmptyBorder(5,0,5,0), 787 BorderFactory.createCompoundBorder 788 (BorderFactory.createTitledBorder(get("Controls")), 789 BorderFactory.createEmptyBorder(2,2,2,2)))); 790 791 temp.add(plugin_pane, BorderLayout.NORTH); 792 temp.add(button_pane, BorderLayout.SOUTH); 793 794 central_pane.setLayout(new BorderLayout()); 795 central_pane.add(plugin_list_pane, BorderLayout.CENTER); 796 central_pane.add(temp, BorderLayout.SOUTH); 797 798 button_pane.setLayout(new GridLayout(3,1)); 799 button_pane.add(add); 800 button_pane.add(configure); 801 button_pane.add(remove); 802 803 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 804 setLayout(new BorderLayout()); 805 add(header_pane, BorderLayout.NORTH); 806 add(central_pane, BorderLayout.CENTER); 807 //add(button_pane, BorderLayout.SOUTH); 808 } 809 /** Method which acts like a destructor, tidying up references to persistant objects. 668 810 */ 669 public Control() { 670 Object plugins[] = reserve.toArray(); 671 Vector plugin_model = new Vector(); 672 for(int i = 0; i < plugins.length; i++) { 673 plugin_model.add(((PlugIn)plugins[i]).getName()); 674 } 675 Collections.sort(plugin_model); 676 // Create 677 add = new JButton(get("Add")); 678 add.setMnemonic(KeyEvent.VK_A); 679 button_pane = new JPanel(); 680 central_pane = new JPanel(); 681 configure = new JButton(get("Configure")); 682 configure.setMnemonic(KeyEvent.VK_C); 683 header_pane = new JPanel(); 684 instructions = new JTextArea(get("Instructions")); 685 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 686 instructions.setEditable(false); 687 instructions.setLineWrap(true); 688 instructions.setRows(5); 689 instructions.setWrapStyleWord(true); 690 move_bottom = new JButton(); 691 JLabel move_bottom_label = new JLabel(get("Move_Bottom")); 692 move_bottom_label.setHorizontalAlignment(JLabel.CENTER); 693 move_bottom_label.setPreferredSize(LABEL_SIZE); 694 move_bottom.setLayout(new BorderLayout()); 695 move_bottom.add(new JLabel(Utility.getImage("arrow-bottom.gif")), BorderLayout.WEST); 696 move_bottom.add(move_bottom_label, BorderLayout.CENTER); 697 move_bottom.add(new JLabel(Utility.getImage("arrow-bottom.gif")), BorderLayout.EAST); 698 move_bottom.setMnemonic(KeyEvent.VK_B); 699 move_down = new JButton(); 700 JLabel move_down_label = new JLabel(get("Move_Down")); 701 move_down_label.setHorizontalAlignment(JLabel.CENTER); 702 move_down_label.setPreferredSize(LABEL_SIZE); 703 move_down.setLayout(new BorderLayout()); 704 move_down.add(new JLabel(Utility.getImage("arrow-down.gif")), BorderLayout.WEST); 705 move_down.add(move_down_label, BorderLayout.CENTER); 706 move_down.add(new JLabel(Utility.getImage("arrow-down.gif")), BorderLayout.EAST); 707 move_down.setMnemonic(KeyEvent.VK_D); 708 move_top = new JButton(); 709 JLabel move_top_label = new JLabel(get("Move_Top")); 710 move_top_label.setHorizontalAlignment(JLabel.CENTER); 711 move_top_label.setPreferredSize(LABEL_SIZE); 712 move_top.setLayout(new BorderLayout()); 713 move_top.add(new JLabel(Utility.getImage("arrow-top.gif")), BorderLayout.WEST); 714 move_top.add(move_top_label, BorderLayout.CENTER); 715 move_top.add(new JLabel(Utility.getImage("arrow-top.gif")), BorderLayout.EAST); 716 move_top.setMnemonic(KeyEvent.VK_T); 717 move_up = new JButton(); 718 JLabel move_up_label = new JLabel(get("Move_Up")); 719 move_up_label.setHorizontalAlignment(JLabel.CENTER); 720 move_up_label.setPreferredSize(LABEL_SIZE); 721 move_up.setLayout(new BorderLayout()); 722 move_up.add(new JLabel(Utility.getImage("arrow-up.gif")), BorderLayout.WEST); 723 move_up.add(move_up_label, BorderLayout.CENTER); 724 move_up.add(new JLabel(Utility.getImage("arrow-up.gif")), BorderLayout.EAST); 725 move_up.setMnemonic(KeyEvent.VK_U); 726 movement_pane = new JPanel(); 727 plugin = new JComboBox(available); 728 //plugin.setEditable(true); 729 plugin.setSelectedIndex(0); 730 plugin_label = new JLabel(get("PlugIn")); 731 plugin_list = new JList(assigned); 732 plugin_list.setCellRenderer(new ListRenderer()); 733 plugin_list_label = new JLabel(get("Assigned")); 734 plugin_list_label.setHorizontalAlignment(JLabel.CENTER); 735 plugin_list_label.setOpaque(true); 736 plugin_list_pane = new JPanel(); 737 plugin_pane = new JPanel(); 738 remove = new JButton(get("Remove")); 739 remove.setMnemonic(KeyEvent.VK_R); 740 title = new JLabel(get("Title")); 741 title.setHorizontalAlignment(JLabel.CENTER); 742 title.setOpaque(true); 743 // Listeners 744 add.addActionListener(new AddListener()); 745 configure.addActionListener(new ConfigureListener()); 746 MoveListener ml = new MoveListener(); 747 move_bottom.addActionListener(ml); 748 move_down.addActionListener(ml); 749 move_top.addActionListener(ml); 750 move_up.addActionListener(ml); 751 remove.addActionListener(new RemoveListener()); 752 plugin_list.addMouseListener(new ClickListener()); 753 // Layout 754 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0)); 755 756 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 757 758 header_pane.setLayout(new BorderLayout()); 759 header_pane.add(title, BorderLayout.NORTH); 760 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 761 762 plugin_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2)); 763 764 movement_pane.setLayout(new GridLayout(4,1)); 765 movement_pane.add(move_top); 766 movement_pane.add(move_up); 767 movement_pane.add(move_down); 768 movement_pane.add(move_bottom); 769 770 plugin_list_pane.setLayout(new BorderLayout()); 771 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH); 772 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER); 773 plugin_list_pane.add(movement_pane, BorderLayout.EAST); 774 775 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 776 777 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 778 plugin_pane.setLayout(new GridLayout(1,2)); 779 plugin_pane.add(plugin_label); 780 plugin_pane.add(plugin); 781 782 // Scope these mad bordering skillz. 783 JPanel temp = new JPanel(new BorderLayout()); 784 temp.setBorder 785 (BorderFactory.createCompoundBorder 786 (BorderFactory.createEmptyBorder(5,0,5,0), 787 BorderFactory.createCompoundBorder 788 (BorderFactory.createTitledBorder(get("Controls")), 789 BorderFactory.createEmptyBorder(2,2,2,2)))); 790 791 temp.add(plugin_pane, BorderLayout.NORTH); 792 temp.add(button_pane, BorderLayout.SOUTH); 793 794 central_pane.setLayout(new BorderLayout()); 795 central_pane.add(plugin_list_pane, BorderLayout.CENTER); 796 central_pane.add(temp, BorderLayout.SOUTH); 797 798 button_pane.setLayout(new GridLayout(3,1)); 799 button_pane.add(add); 800 button_pane.add(configure); 801 button_pane.add(remove); 802 803 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 804 setLayout(new BorderLayout()); 805 add(header_pane, BorderLayout.NORTH); 806 add(central_pane, BorderLayout.CENTER); 807 //add(button_pane, BorderLayout.SOUTH); 808 } 809 /** Method which acts like a destructor, tidying up references to persistant objects. 810 */ 811 public void destroy() { 812 } 813 /** This method is overridden in some control classes to allow for the updating of collection configuration when the focus is lost to another view. In this case however the updates are asynchronous and so no 'has the configuration changed without the user updating' check is needed. 811 public void destroy() { 812 } 813 /** This method is overridden in some control classes to allow for the updating of collection configuration when the focus is lost to another view. In this case however the updates are asynchronous and so no 'has the configuration changed without the user updating' check is needed. 814 814 * @return <i>true</i> if this control has focus, <i>false</i> otherwise. 815 815 */ 816 817 818 819 816 public boolean hasFocus() { 817 return super.hasFocus(); 818 } 819 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called. 820 820 */ 821 822 823 824 825 826 827 821 public void updateUI() { 822 if(instructions != null) { 823 instructions.setCaretPosition(0); 824 } 825 super.updateUI(); 826 } 827 /** This class listens for actions upon the add button in the controls, and if detected calls the <i>assignPlugIn()</i> method. 828 828 */ 829 830 829 private class AddListener 830 implements ActionListener { 831 831 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls. 832 832 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. 833 833 */ 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 834 public void actionPerformed(ActionEvent event) { 835 PlugIn new_plugin = null; 836 Object object = plugin.getSelectedItem(); 837 if(object instanceof PlugIn) { 838 PlugIn target = (PlugIn)object; 839 new_plugin = target.copy(); 840 } 841 else { 842 new_plugin = new PlugIn(object.toString(), "", null); 843 } 844 object = null; 845 // Automatically chain to configuration. This ensures required arguments are filled out. 846 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, new_plugin); 847 if(ac.display()) { 848 assignPlugIn(new_plugin); 849 plugin_list.setSelectedValue(new_plugin, true); 850 } 851 ac = null; 852 new_plugin = null; 853 plugin.setSelectedIndex(0); 854 } 855 } 856 /** This class listens for actions upon the configure button in the controls, and if detected creates a new <strong>ArgumentConfiguration</strong> dialog box to allow for configuration. 857 857 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration 858 858 */ 859 860 859 private class ConfigureListener 860 implements ActionListener { 861 861 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls. 862 862 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. 863 863 */ 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 864 public void actionPerformed(ActionEvent event) { 865 if(!plugin_list.isSelectionEmpty()) { 866 Object object = plugin_list.getSelectedValue(); 867 if(object instanceof PlugIn) { 868 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (PlugIn)object); 869 if(ac.display()) { 870 assigned.refresh(); 871 } 872 } 873 } 874 } 875 } 876 /** A special list renderer which is able to render separating lines as well. */ 877 private class ListRenderer 878 extends DefaultListCellRenderer { 879 879 /** Return a component that has been configured to display the specified value. That component's paint method is then called to "render" the cell. If it is necessary to compute the dimensions of a list because the list cells do not have a fixed size, this method is called to generate a component on which getPreferredSize can be invoked. 880 880 * @param list - The <strong>JList</strong> we're painting. … … 889 889 * @see javax.swing.ListSelectionModel 890 890 */ 891 892 893 894 895 896 897 898 899 900 901 902 891 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 892 if(value instanceof JPanel) { 893 return (JPanel) value; 894 } 895 else { 896 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 897 } 898 } 899 } 900 /** Listens for double clicks apon the list and react as if the configure button was pushed. */ 901 private class ClickListener 902 extends MouseAdapter { 903 903 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt. 904 904 * @param event A <strong>MouseEvent</strong> containing information about the mouse click. 905 905 */ 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 906 public void mouseClicked(MouseEvent event) { 907 if(event.getClickCount() == 2 ) { 908 if(!plugin_list.isSelectionEmpty()) { 909 Object object = plugin_list.getSelectedValue(); 910 if(object instanceof PlugIn) { 911 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (PlugIn)object); 912 if(ac.display()) { 913 assigned.refresh(); 914 } 915 } 916 } 917 } 918 } 919 } 920 /** Listens for actions apon the move buttons in the manager controls, and if detected calls the <i>movePlugIn()</i> method of the manager with the appropriate details. */ 921 private class MoveListener 922 implements ActionListener { 923 923 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls. 924 924 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. 925 925 */ 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 926 public void actionPerformed(ActionEvent event) { 927 if(!plugin_list.isSelectionEmpty()) { 928 Object object = plugin_list.getSelectedValue(); 929 if(object instanceof PlugIn) { 930 PlugIn plugin = (PlugIn) object; 931 if(event.getSource() == move_top) { 932 movePlugIn(plugin, true, true); 933 } 934 else if(event.getSource() == move_up) { 935 movePlugIn(plugin, true, false); 936 } 937 else if(event.getSource() == move_down) { 938 movePlugIn(plugin, false, false); 939 } 940 else { 941 movePlugIn(plugin, false, true); 942 } 943 plugin_list.setSelectedValue(plugin, true); 944 } 945 } 946 } 947 } 948 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugIn()</i> method. 949 949 */ 950 951 950 private class RemoveListener 951 implements ActionListener { 952 952 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls. 953 953 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action. 954 954 */ 955 956 957 958 959 960 961 962 963 964 965 955 public void actionPerformed(ActionEvent event) { 956 if(!plugin_list.isSelectionEmpty()) { 957 Object object = plugin_list.getSelectedValue(); 958 if(object instanceof PlugIn) { 959 removePlugIn((PlugIn)object); 960 } 961 } 962 } 963 } 964 } 965 /** Creates a list separator. 966 966 * Code courtesy of Farwell, Paul. Contact <a href="mailto:[email protected]">[email protected]</a> 967 967 */ 968 969 970 971 972 973 974 975 976 977 978 979 968 static private JPanel getSeparator() { 969 // we put the separator inside a panel to control 970 // its appearance 971 JPanel _sepPanel = new JPanel(); 972 _sepPanel.setOpaque(false); 973 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3)); 974 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS)); 975 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4))); 976 _sepPanel.add(new JPopupMenu.Separator()); 977 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4))); 978 return _sepPanel; 979 } 980 980 } 981 981 -
trunk/gli/src/org/greenstone/gatherer/cdm/SubIndex.java
r4293 r4366 60 60 */ 61 61 public class SubIndex 62 63 64 65 66 67 68 69 62 extends Vector 63 implements Comparable { 64 /** Default Constructor. 65 */ 66 public SubIndex() { 67 super(); 68 } 69 /** Copy Constructor. 70 70 * @param original The <strong>Vector</strong> used to initially fill the SubIndex. 71 71 */ 72 73 74 75 72 public SubIndex(Vector original) { 73 super(original); 74 } 75 /** Parsed data Constructor. 76 76 * @param raw A <strong>String</strong> containing a comma separated list of subcollection names. 77 77 * @param manager A reference to the <strong>SubcollectionManager</strong> via which we retrieve the required Subcollections. 78 78 */ 79 80 81 82 83 84 85 86 87 88 89 90 79 public SubIndex(String raw, SubcollectionManager manager) { 80 super(); 81 StringTokenizer tokenizer = new StringTokenizer(raw, ","); 82 while(tokenizer.hasMoreTokens()) { 83 String name = tokenizer.nextToken(); 84 Subcollection sub = manager.getSubcollection(name); 85 if(sub != null) { 86 add(sub); 87 } 88 } 89 } 90 /** Method to compare two subindexes. 91 91 * @param object The other subindex to compare to, as an <strong>Object</strong>. 92 92 * @return An <i>int</i> which is greater than, equal to or less than zero if the this object is before, equal to, or after the target object respectively. 93 93 */ 94 95 96 97 98 94 public int compareTo(Object object) { 95 SubIndex index = (SubIndex) object; 96 return toString().compareTo(index.toString()); 97 } 98 /** Method to determine if this subindex contains a certain subcollection. 99 99 * @param name The name, as a <strong>String</strong>, of the subcollection you are checking for. 100 100 * @return A <i>boolean</i> which is <i>true</i> if this index contains the named subcollection, <i>false</i> otherwise. 101 101 */ 102 103 104 105 106 107 108 109 110 111 102 public boolean containsSubcollection(String name) { 103 for(int i = 0; i < size(); i++) { 104 Subcollection sub = (Subcollection) get(i); 105 if(sub.getName().equals(name)) { 106 return true; 107 } 108 } 109 return false; 110 } 111 /** Method to check two subindexes for equality. 112 112 * @param object The other subindex to compare to, as an <strong>Object</strong>. 113 113 * @return A <i>boolean</i> which is <i>true</i> if the subindexes are equal, <i>false</i> otherwise. 114 114 */ 115 116 117 118 119 120 121 115 public boolean equals(Object object) { 116 if(compareTo(object) == 0) { 117 return true; 118 } 119 return false; 120 } 121 /** Method to generate a comma separated list from a subindex vector. 122 122 * @return A <strong>String</strong> representing this subindex. 123 123 */ 124 125 126 127 128 129 130 131 132 133 134 135 124 public String toString() { 125 String text = ""; 126 for(int i = 0; i < size(); i++) { 127 Object object = get(i); 128 Subcollection sub = (Subcollection) get(i); 129 text = text + sub.getName(); 130 if(i < size() - 1) { 131 text = text + ","; 132 } 133 } 134 return text; 135 } 136 136 } -
trunk/gli/src/org/greenstone/gatherer/cdm/SubIndexes.java
r4293 r4366 58 58 */ 59 59 public class SubIndexes 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 60 extends DefaultListModel { 61 public void changed() { 62 fireContentsChanged(this, 0, getSize() - 1); 63 } 64 /** Method to display the contents of this class as an entry in the collection configuration file. The line contains the key word, then a space separated series of index entries. 65 * @return A <strong>String</strong> representing this collection of indexes. 66 */ 67 public String toString() { 68 String text = "indexsubcollections "; 69 for(int i = 0; i < size(); i++) { 70 text = text + ((SubIndex)get(i)).toString(); 71 if(i < size() - 1) { 72 text = text + " "; 73 } 74 } 75 text = text + "\n"; 76 return text; 77 } 78 78 } -
trunk/gli/src/org/greenstone/gatherer/cdm/Subcollection.java
r4293 r4366 53 53 import org.w3c.dom.Element; 54 54 /** This class encapsulates one subcollection entry in the collection configuration file. 55 * @author John Thompson, Greenstone Digital Library, University of Waikato56 * @version 2.357 */55 * @author John Thompson, Greenstone Digital Library, University of Waikato 56 * @version 2.3 57 */ 58 58 public class Subcollection 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 59 implements Comparable { 60 /** A <i>boolean</i> which is <i>true</i> if the condition is an include one, <i>false</i> otherwise. */ 61 private boolean include = true; 62 /** Either the fully qualified name of the metadata whose value should be matched against the given expression, or <i>null</i> if you wish to match against the file name. */ 63 private String element = null; 64 /** A String containing a Perl expression which is used as the filter for this subcollection. */ 65 private String exp = null; 66 /** A series of flags to be used when matching the expression. */ 67 private String flags = null; 68 /** A String which is a unique identifier of a subcollection. */ 69 private String name = null; 70 /** Constructor. 71 * @param name A <strong>String</strong> which is a unique identifier of a subcollection. 72 * @param pattern A <strong>String</strong> containing the inclusion, the element to filter, a Perl expression which is used as the filter for this subcollection, and a series of argument flags. 73 */ 74 public Subcollection(String name, String pattern) { 75 this.name = name; 76 // Have to do some work on pattern. 77 // Remove quote marks. 78 if(pattern.startsWith("\"") || pattern.startsWith("'")) { 79 pattern = pattern.substring(1, pattern.length() - 1); 80 } 81 // Test for exclusion 82 if(pattern.startsWith("!")) { 83 this.include = false; 84 pattern = pattern.substring(1, pattern.length()); 85 } 86 else { 87 this.include = true; 88 } 89 if(pattern.indexOf("/") != -1) { 90 90 // Now element (which may be Filename) 91 92 93 94 91 this.element = pattern.substring(0, pattern.indexOf("/")); 92 if(this.element.toLowerCase().equals("filename")) { 93 this.element = null; 94 } 95 95 // Extract Perl expression 96 97 98 99 100 101 102 103 104 105 96 if(pattern.indexOf("/") != pattern.lastIndexOf("/")) { 97 this.exp = pattern.substring(pattern.indexOf("/") + 1, 98 pattern.lastIndexOf("/")); 99 this.flags = pattern.substring(pattern.lastIndexOf("/") + 1); 100 } 101 else { 102 this.exp = pattern.substring(pattern.indexOf("/") + 1); 103 } 104 } 105 else { 106 106 // Hmmm. Well I don't know what this is but it isn't a proper subcollection definition. Can't really 'return null' or something. So I'll just not set include, element, exp and flag expressions. 107 108 109 107 } 108 } 109 /** Constructor. 110 110 * @param name A <strong>String</strong> which is a unique identifier of a subcollection. 111 111 * @param include A <i>boolean</i> which is <i>true</i> if the condition is an include one, <i>false</i> otherwise. 112 112 * @param exp A <strong>String</strong> containing a Perl expression which is used as the filter for this subcollection. 113 113 */ 114 115 116 117 118 119 120 121 114 public Subcollection(String name, boolean include, String exp, String flags) { 115 this.element = null; 116 this.exp = exp; 117 this.flags = flags; 118 this.include = include; 119 this.name = name; 120 } 121 /** Constructor. 122 122 * @param name A <strong>String</strong> which is a unique identifier of a subcollection. 123 123 * @param include A <i>boolean</i> which is <i>true</i> if the condition is an include one, <i>false</i> otherwise. … … 126 126 * @param flags A <strong>String</strong> of flags to be taken into account when matching the expression. 127 127 */ 128 129 130 131 132 133 134 135 128 public Subcollection(String name, boolean include, String element, String exp, String flags) { 129 this.element = element; 130 this.exp = exp; 131 this.flags = flags; 132 this.include = include; 133 this.name = name; 134 } 135 /** Method to compare two subcollections. 136 136 * @param object The other subcollection to compare to, as an <strong>Object</strong>. 137 137 * @return An <i>int</i> which is greater than, equal to or less than zero if the this object is before, equal to, or after the target object respectively. 138 138 */ 139 140 141 142 143 139 public int compareTo(Object object) { 140 Subcollection sub = (Subcollection) object; 141 return getName().compareTo(sub.getName()); 142 } 143 /** Method to check two subcollections for equality. 144 144 * @param object The other subcollection to compare to, as an <strong>Object</strong>. 145 145 * @return A <i>boolean</i> which is <i>true</i> if the subcollections are equal, <i>false</i> otherwise. 146 146 */ 147 148 149 150 151 152 153 147 public boolean equals(Object object) { 148 if(compareTo(object) == 0) { 149 return true; 150 } 151 return false; 152 } 153 /** Method to get the value of element. 154 154 * @return The value of element as an <strong>String</strong>. 155 155 */ 156 157 158 159 156 public String getElement() { 157 return element; 158 } 159 /** Method to get the value of expression. 160 160 * @return The value of exp as a <strong>String</strong>. 161 161 */ 162 163 164 165 162 public String getExpression() { 163 return exp; 164 } 165 /** Method to get the value of flags. 166 166 * @return The value of flags as a <strong>String</strong>. 167 167 */ 168 169 170 171 168 public String getFlags() { 169 return flags; 170 } 171 /** Method to get the value of include. 172 172 * @param boolean The value of include as a <i>boolean</i>. 173 173 */ 174 175 176 177 174 public boolean getInclude() { 175 return include; 176 } 177 /** Method to get the value of name. 178 178 * @param String The value of name as a <strong>String</string>. 179 179 */ 180 181 182 183 180 public String getName() { 181 return name; 182 } 183 /** Method to get the name of the source of the strings used in pattern matching. 184 184 * @return A <strong>String</strong> which is either the fully qualified name of a metadata element, or "Filename". 185 185 */ 186 187 188 189 190 191 192 186 public String getSource() { 187 if(element != null) { 188 return element; 189 } 190 return "Filename"; 191 } 192 /** Method to display the contents of this class as it would appear in the collection configuration file. 193 193 * @return A <strong>String</strong> which could be used as a subcollection entry in collect.cfg. 194 194 */ 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 195 public String toString() { 196 String text = "subcollection "; 197 text = text + name + " "; 198 text = text + "\""; 199 if(!include) { 200 text = text + "!"; 201 } 202 if(element != null) { 203 text = text + element; 204 } 205 else { 206 text = text + "Filename"; 207 } 208 text = text + "/"; 209 text = text + exp; 210 text = text + "/"; 211 if(flags != null) { 212 text = text + flags; 213 } 214 text = text + "\"\n"; 215 return text; 216 } 217 218 /** Update certain fields of this subcollection. 219 219 * @param name A <strong>String</strong> which is a unique identifier of a subcollection. 220 220 * @param source Either the fully qualified name of the metadata whose value should be matched against the given expression, or <i>null</i> if you wish to match against the file name. … … 223 223 * @param flags A <strong>String</strong> of flags to be taken into account when matching the expression. 224 224 */ 225 226 227 228 229 230 231 232 233 225 public void update(String name, String source, String exp, boolean include, String flags) { 226 this.name = name; 227 this.element = source; 228 this.exp = exp; 229 this.include = include; 230 this.flags = flags; 231 } 232 233 /** Method to update the name of a metadata element, if it changes. 234 234 * @param old_name The current name of the element as a <strong>String</strong>. 235 235 * @param new_name The new name of the element as a <strong>String</strong>. 236 236 */ 237 238 239 240 241 237 public void updateElement(String old_name, String new_name) { 238 if(element.equals(old_name)) { 239 element = new_name; 240 } 241 } 242 242 } 243 243 -
trunk/gli/src/org/greenstone/gatherer/cdm/SubcollectionManager.java
r4293 r4366 87 87 import org.w3c.dom.Element; 88 88 /** This class maintains a list of subcollections within our collection, and also records which of these subcollections we are building indexes for. 89 * @author John Thompson, Greenstone Digital Library, University of Waikato90 * @version 2.191 */89 * @author John Thompson, Greenstone Digital Library, University of Waikato 90 * @version 2.1 91 */ 92 92 public class SubcollectionManager 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 93 extends DefaultListModel { 94 /** The controls used to edit the settings of this manager. */ 95 private Control controls = null; 96 /** A reference to this class so we can use it as a model in our inner classes. */ 97 private DefaultListModel model = null; 98 /** The default index for the subcollections. */ 99 private DefaultSubIndex default_index = null; 100 /** A reference to the Gatherer. */ 101 private Gatherer gatherer = null; 102 /** A hashtable of subcollections, with mappings from name to subcollection. */ 103 private Hashtable subcollections = null; 104 /** A vector-type structure of subcollection indexes. */ 105 private SubIndexes subindexes = null; 106 /** A vector containing all of the subcollection commands that are unresolved, or in other words require that all 'subcollection <name> "<exp>"' to have been previously parsed before we can be certain their references will make sense. */ 107 private ArrayList unresolved = null; 108 /** Constructor. 109 * @param gatherer A reference to the <Strong>Gatherer</strong>. 110 * @see org.greenstone.gatherer.cdm.SubIndexes 111 */ 112 public SubcollectionManager(Gatherer gatherer) { 113 super(); 114 this.gatherer = gatherer; 115 this.model = this; 116 this.subcollections = new Hashtable(); 117 this.subindexes = new SubIndexes(); 118 this.unresolved = new ArrayList(); 119 } 120 /** Method to add a subindex. 121 121 * @param subindex A <strong>SubIndex</strong>. 122 122 * @see org.greenstone.gatherer.Gatherer 123 123 * @see org.greenstone.gatherer.collection.CollectionManager 124 124 */ 125 126 127 128 129 130 131 125 public void addSubIndex(SubIndex subindex) { 126 if(!subindexes.contains(subindex)) { 127 subindexes.addElement(subindex); 128 gatherer.c_man.configurationChanged(); 129 } 130 } 131 /** Method to add a new subcollection. Adds it to both the underlying list model and the hashtable mapping names to subcollections. 132 132 * @param sub The <strong>Subcollection</strong> to add. 133 133 * @return A <i>boolean</i> indicating if the addition was successful (<i>true</i>) or not (<i>false</i>). … … 135 135 * @see org.greenstone.gatherer.collection.CollectionManager 136 136 */ 137 138 139 140 141 142 143 144 145 146 137 public boolean addSubcollection(Subcollection sub) { 138 if(!contains(sub)) { 139 addElement(sub); 140 subcollections.put(sub.getName(), sub); 141 gatherer.c_man.configurationChanged(); 142 return true; 143 } 144 return false; 145 } 146 /** A method to add a new subcollection, by specifying its name, metadata element if applicable and Perl expression to filter pages into or out of this collection. 147 147 * @param name A <>>String</>> which is a unique identifier of a subcollection. 148 148 * @param include A <i>boolean</i> indicating whether this is an inclusion filter (<i>true</i>) or an exclusion one (<i>false</i>). … … 153 153 * @see org.greenstone.gatherer.cdm.Subcollection 154 154 */ 155 156 157 158 159 160 161 162 163 164 165 155 public boolean addSubcollection(String name, boolean include, String element, String exp, String flags) { 156 Subcollection sub = null; 157 if(element != null) { 158 sub = new Subcollection(name, include, element, exp, flags); 159 } 160 else { 161 sub = new Subcollection(name, include, exp, flags); 162 } 163 return addSubcollection(sub); 164 } 165 /** Refresh all derived components using this manager as a model. 166 166 */ 167 168 169 170 167 public void changed() { 168 fireContentsChanged(this, 0, getSize() - 1); 169 } 170 /** Method to retrieve the controls for this manager. 171 171 * @return A <Strong>Control</strong> object which contains the controls used to edit the subcollection data. 172 172 */ 173 174 175 176 177 178 179 173 public Control getControls() { 174 if(controls == null) { 175 controls = new Control(); 176 } 177 return controls; 178 } 179 /** Method to retrieve the default index. 180 180 * @return A <strong>DefaultSubIndex</strong> object. 181 181 */ 182 183 184 185 182 public DefaultSubIndex getDefaultSubIndex() { 183 return default_index; 184 } 185 /** Method to retrieve a certain subcollection by its name. 186 186 * @param name A <strong>String</strong> which is used as the key for finding the matching subcollection. 187 187 * @return The requested <strong>Subcollection</strong> or <i>null</i> if no such subcollection exists. 188 188 */ 189 190 191 192 189 public Subcollection getSubcollection(String name) { 190 return (Subcollection) subcollections.get(name); 191 } 192 /** Method to get all of the subindexes set. 193 193 * @return A <strong>SubIndexes</strong> object containing all the defined indexes. 194 194 */ 195 196 197 198 195 public SubIndexes getSubIndexes() { 196 return subindexes; 197 } 198 /** Method to get all of the subcollections defined. 199 199 * @return A <strong>Vector</strong> of subcollections. 200 200 */ 201 202 203 204 205 206 207 208 201 public Vector getSubcollections() { 202 Vector subcollections = new Vector(); 203 for(int i = 0; i < size(); i++) { 204 subcollections.add(get(i)); 205 } 206 return subcollections; 207 } 208 /** Mark the current controls, if any, as invalid and deallocate them. Any further use of the controls will requires them being rebuilt. 209 209 * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control 210 210 */ 211 212 213 214 215 216 217 211 public void invalidateControls() { 212 if(controls != null) { 213 controls.destroy(); 214 controls = null; 215 } 216 } 217 /** This method attempts to parse a subcollection related command from the given command string. If such a string is successfully parsed, it is immediately added to the data within this collection. 218 218 * @param command The <strong>String</strong> to be parsed. 219 219 * @param finished This <i>boolean</i> is usually <i>false</i> when called, unless this parse call is being made -after- the entire collection configuration file has been read in in which case it is <i>true</i>. … … 224 224 * @see org.greenstone.gatherer.cdm.SubIndex 225 225 */ 226 227 228 229 230 231 232 233 234 235 236 237 238 226 public boolean parse(String command, boolean finished) { 227 String temp = command.toLowerCase(); 228 CommandTokenizer tokenizer = new CommandTokenizer(command); 229 tokenizer.nextToken(); // Throw away head. 230 if(temp.startsWith("subcollection")) { 231 if(tokenizer.countTokens() >= 2) { 232 String name = tokenizer.nextToken(); 233 String pattern = tokenizer.nextToken(); 234 addSubcollection(new Subcollection(name, pattern)); 235 return true; 236 } 237 } 238 else if(temp.startsWith("indexsubcollections")) { 239 239 // These entries depend on what subcollections have previously been set, so we cannot safely parse them until after the file is complete. 240 241 242 243 244 245 246 247 248 249 250 240 if(!finished) { 241 unresolved.add(command); 242 } 243 else { 244 while(tokenizer.hasMoreTokens()) { 245 addSubIndex(new SubIndex(tokenizer.nextToken(), this)); 246 } 247 } 248 return true; 249 } 250 else if(temp.startsWith("defaultsubcollection")) { 251 251 // These entries depend on what subcollections have previously been set, so we cannot safely parse them until after the file is complete. 252 253 254 255 256 257 258 259 260 261 262 263 264 252 if(!finished) { 253 unresolved.add(command); 254 } 255 else { 256 if(tokenizer.hasMoreTokens()) { 257 setDefaultSubIndex(new DefaultSubIndex(tokenizer.nextToken(), this)); 258 } 259 } 260 return true; 261 } 262 return false; 263 } 264 /** Method to remove a certain subindex. 265 265 * @param subindex The <strong>SubIndex</strong> you wish to remove. 266 266 * @see org.greenstone.gatherer.Gatherer 267 267 * @see org.greenstone.gatherer.collection.CollectionManager 268 268 */ 269 270 271 272 273 269 public void removeSubIndex(SubIndex subindex) { 270 subindexes.removeElement(subindex); 271 gatherer.c_man.configurationChanged(); 272 } 273 /** Method to remove all of the subindexes that contain a certain subcollection. 274 274 * @param sub The <strong>Subcollection</strong> that you wish to remove. 275 275 * @see org.greenstone.gatherer.Gatherer … … 277 277 * @see org.greenstone.gatherer.collection.CollectionManager 278 278 */ 279 280 281 282 283 284 285 286 287 288 279 public void removeSubIndexes(Subcollection sub) { 280 for(int i = subindexes.size() - 1; i >= 0; i--) { 281 SubIndex subindex = (SubIndex)subindexes.get(i); 282 if(subindex.containsSubcollection(sub.getName())) { 283 subindexes.removeElement(subindex); 284 } 285 } 286 gatherer.c_man.configurationChanged(); 287 } 288 /** Method to remove the given subcollection. 289 289 * @param sub The <strong>Subcollection</strong> you want to remove. 290 290 * @see org.greenstone.gatherer.Gatherer 291 291 * @see org.greenstone.gatherer.collection.CollectionManager 292 292 */ 293 294 295 296 297 298 299 */ 300 301 302 303 304 305 306 293 public void removeSubcollection(Subcollection sub) { 294 removeElement(sub); 295 subcollections.remove(sub); 296 gatherer.c_man.configurationChanged(); 297 } 298 /** Method to retry the parsing of commands that were previously unable to be parsed as they referenced subcollections that may not have been instantiated. 299 */ 300 public void reparseUnresolved() { 301 for(int i = 0; i < unresolved.size(); i++) { 302 parse((String)unresolved.get(i), true); 303 } 304 unresolved.clear(); 305 } 306 /** Method to set the default subcollection index. 307 307 * @param subcollection The <strong>Subcollection</strong> to use as the default index. 308 308 * @see org.greenstone.gatherer.Gatherer 309 309 * @see org.greenstone.gatherer.collection.CollectionManager 310 310 */ 311 312 313 314 315 316 317 318 311 public void setDefaultSubIndex(DefaultSubIndex subindex) { 312 this.default_index = subindex; 313 if(subindex != null) { 314 addSubIndex(subindex.getSubIndex()); 315 } 316 gatherer.c_man.configurationChanged(); 317 } 318 /** This method causes the contents of this manager to be converted to a string, which is accomplished by calling <i>toString()</i> on each subcollection, then printing out the contents of the index vector. 319 319 * @return A <strong>String</strong> containing a block of configuration commands. 320 320 * @see org.greenstone.gatherer.cdm.SubIndex 321 321 * @see org.greenstone.gatherer.cdm.Subcollection 322 322 */ 323 324 325 326 327 328 329 330 331 332 333 323 public String toString() { 324 String text = ""; 325 // Retrieve the subcollection names and sort them. 326 if(subcollections.size() > 0) { 327 Vector names = new Vector(subcollections.keySet()); 328 Collections.sort(names); 329 for(int i = 0; i < names.size(); i++) { 330 Subcollection sub = 331 (Subcollection) subcollections.get(names.get(i)); 332 text = text + sub.toString(); 333 } 334 334 // Now add a entry, separated by spaces for each subcollection 335 335 // index. 336 337 338 336 if(subindexes.size() > 0) { 337 text = text + subindexes.toString(); 338 } 339 339 // Finally add the default subcollection index if necessary. 340 341 342 343 344 345 346 347 348 349 340 if(default_index != null) { 341 text = text + default_index.toString(); 342 } 343 text = text + "\n"; 344 } 345 // Otherwise if there were no subcollections, there aren't going to be 346 // subcollection indexes, nor a default subcollection index are there. 347 return text; 348 } 349 /** Method to retrieve a phrase from the dictionary based on a key. 350 350 * @param key A <strong>String</strong> used to find the correct phrase. 351 351 * @param args A <strong>String[]</strong> of arguments used in formatting and filling out the phrase. 352 352 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. 353 353 */ 354 355 356 357 354 private String get(String key) { 355 return get(key, null); 356 } 357 /** Method to retrieve a phrase from the dictionary based on a key. 358 358 * @param key A <strong>String</strong> used to find the correct phrase. 359 359 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. … … 361 361 * @see org.greenstone.gatherer.Gatherer 362 362 */ 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 363 private String get(String key, String args[]) { 364 if(key.indexOf(".") == -1) { 365 key = "CDM.SubcollectionManager." + key; 366 } 367 return gatherer.dictionary.get(key, args); 368 } 369 /** This class creates a JPanel containing serveral more controls used for editing subcollection information. */ 370 private class Control 371 extends JPanel { 372 /** <i>true</i> if the current selection has changed. */ 373 private boolean changed = false; 374 /** Button to add a subcollection. */ 375 private JButton add = null; 376 /** Button to add a subindex. */ 377 private JButton add_index = null; 378 /** Button to clear the default subindex. */ 379 private JButton clear_default = null; 380 /** Button to remove a subcollection. */ 381 private JButton remove = null; 382 /** Button to remove a subindex. */ 383 private JButton remove_index = null; 384 /** Button to set the default subindex. */ 385 private JButton set_default = null; 386 /** Button to cause an update of the subcollections. */ 387 private JButton update = null; 388 /** A combobox allowing you to choose which field the data for matching should be taken from. Includes text body and filename, as well as all assigned metadata elements. */ 389 private JComboBox source = null; 390 /** The list of assigned subcollections. */ 391 private JList subcollection_list = null; 392 /** The list of subcollections available for the creation of subindexes. */ 393 private JList subcollection_list_2 = null; 394 /** The list of assigned subindexes. */ 395 private JList subindexes_list = null; 396 /** The label denoting the list of subindexes. */ 397 private JLabel subindexes_label = null; 398 /** The panel containing the subcollection controls. */ 399 private JPanel subcollection_pane = null; 400 /** The panel containing the subindex controls. */ 401 private JPanel subindex_pane = null; 402 /** The tabbed pane used to store the subcollection and subindex controls. */ 403 private JTabbedPane tabbed_pane = null; 404 /** The area used to display inline instructions. */ 405 private JTextArea instructions = null; 406 /** The field displaying the name of the default subindex. */ 407 private JTextField default_value = null; 408 /** A field used for specifying flags for the PERL expression matching, such as 'i' for case insensitive. */ 409 private JTextField flags = null; 410 /** The pattern the source text must match. */ 411 private JTextField match = null; 412 /** The name of this subcollection. */ 413 private JTextField name = null; 414 /** When this button is selected the filter matching files are excluded. */ 415 private JToggleButton exclude = null; 416 /** When this button is selected the filter matching files are included. */ 417 private JToggleButton include = null; 418 /** The existing subcollection whose details you are reviewing, if any. */ 419 private Subcollection current = null; 420 /** Constructor, creates the outer parts of the view, then calls two methods in turn to create the subcollection controls and subindex controls. 421 */ 422 public Control() { 423 423 // Create 424 425 426 427 428 429 430 431 432 433 434 435 436 424 JPanel border_pane = new JPanel(); 425 JPanel header_pane = new JPanel(); 426 instructions = new JTextArea(get("Instructions")); 427 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 428 instructions.setEditable(false); 429 instructions.setLineWrap(true); 430 instructions.setRows(5); 431 instructions.setWrapStyleWord(true); 432 tabbed_pane = new JTabbedPane(); 433 JLabel title = new JLabel(get("Title")); 434 title.setHorizontalAlignment(JLabel.CENTER); 435 createSubcollection(); 436 createSubindex(); 437 437 // Add listeners 438 438 // Layout 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 439 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 440 header_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 441 header_pane.setLayout(new BorderLayout()); 442 header_pane.add(title, BorderLayout.NORTH); 443 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 444 tabbed_pane.addTab(get("Subcollection_Controls"), subcollection_pane); 445 tabbed_pane.addTab(get("Subindex_Controls"), subindex_pane); 446 border_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 447 border_pane.setLayout(new BorderLayout()); 448 border_pane.add(tabbed_pane, BorderLayout.CENTER); 449 setLayout(new BorderLayout()); 450 add(header_pane, BorderLayout.NORTH); 451 add(border_pane, BorderLayout.CENTER); 452 } 453 /** Create the subcollection controls. 454 454 * @see org.greenstone.gatherer.Gatherer 455 455 * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.AddListener … … 459 459 * @see org.greenstone.gatherer.msm.MetadataSetManager 460 460 */ 461 461 public void createSubcollection() { 462 462 // Create 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 463 add = new JButton(get("Add")); 464 add.setMnemonic(KeyEvent.VK_A); 465 JPanel button_pane = new JPanel(); 466 JPanel button_pane_1 = new JPanel(); 467 JPanel button_pane_3 = new JPanel(); 468 exclude = new JToggleButton(get("Exclude")); 469 exclude.setMnemonic(KeyEvent.VK_X); 470 flags = new JTextField(); 471 JLabel flags_label = new JLabel(get("Flags")); 472 include = new JToggleButton(get("Include")); 473 include.setMnemonic(KeyEvent.VK_I); 474 JLabel inclusive_label = new JLabel(get("Inclusive")); 475 JPanel inclusive_pane = new JPanel(); 476 match = new JTextField(); 477 JLabel match_label = new JLabel(get("Match")); 478 name = new JTextField(); 479 JLabel name_label = new JLabel(get("Name")); 480 remove = new JButton(get("Remove")); 481 remove.setMnemonic(KeyEvent.VK_R); 482 Vector source_model = gatherer.c_man.msm.getAssignedElements(); 483 source_model.add(0, "Filename"); 484 source = new JComboBox(source_model); 485 JLabel source_label = new JLabel(get("Source")); 486 subcollection_list = new JList(model); 487 subcollection_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 488 subcollection_pane = new JPanel(); 489 ButtonGroup bg = new ButtonGroup(); 490 bg.add(include); 491 bg.add(exclude); 492 include.setSelected(true); 493 JPanel subcollection_list_pane = new JPanel(); 494 JLabel subcollection_list_label = new JLabel(get("Assigned")); 495 495 496 496 // Add listeners 497 498 499 497 add.addActionListener(new AddListener()); 498 remove.addActionListener(new RemoveListener()); 499 subcollection_list.addListSelectionListener(new ListListener()); 500 500 501 501 // Layout 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 502 inclusive_pane.setLayout(new GridLayout()); 503 inclusive_pane.add(include); 504 inclusive_pane.add(exclude); 505 button_pane_1.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 506 button_pane_1.setLayout(new GridLayout(5, 2)); 507 button_pane_1.add(name_label); 508 button_pane_1.add(name); 509 button_pane_1.add(source_label); 510 button_pane_1.add(source); 511 button_pane_1.add(match_label); 512 button_pane_1.add(match); 513 button_pane_1.add(inclusive_label); 514 button_pane_1.add(inclusive_pane); 515 button_pane_1.add(flags_label); 516 button_pane_1.add(flags); 517 button_pane_3.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 518 button_pane_3.setLayout(new GridLayout(1,2)); 519 button_pane_3.add(add); 520 button_pane_3.add(remove); 521 button_pane.setLayout(new BorderLayout()); 522 button_pane.add(button_pane_1, BorderLayout.CENTER); 523 button_pane.add(button_pane_3, BorderLayout.SOUTH); 524 subcollection_list_pane.setLayout(new BorderLayout()); 525 subcollection_list_pane.add(subcollection_list_label, BorderLayout.NORTH); 526 subcollection_list_pane.add(new JScrollPane(subcollection_list), BorderLayout.CENTER); 527 subcollection_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 528 subcollection_pane.setLayout(new BorderLayout()); 529 subcollection_pane.add(subcollection_list_pane, BorderLayout.CENTER); 530 subcollection_pane.add(button_pane, BorderLayout.SOUTH); 531 } 532 /** Create the subindex controls. 533 533 * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.AddSubIndexListener 534 534 * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.ClearDefaultListener … … 538 538 * @see org.greenstone.gatherer.util.ExclusiveListListener 539 539 */ 540 540 public void createSubindex() { 541 541 // Creation 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 542 add_index = new JButton(get("Add_Subindex")); 543 add_index.setMnemonic(KeyEvent.VK_A); 544 JPanel button_pane_2 = new JPanel(); 545 clear_default = new JButton(get("Clear_Default_Subindex")); 546 clear_default.setMnemonic(KeyEvent.VK_C); 547 if(default_index == null) { 548 clear_default.setEnabled(false); 549 } 550 JLabel default_label = new JLabel(get("Default_Subindex")); 551 JPanel default_pane = new JPanel(); 552 if(default_index == null) { 553 default_value = new JTextField(); 554 } 555 else { 556 default_value = new JTextField(default_index.getSubIndex().toString()); 557 } 558 JPanel subindex_inner_pane_1 = new JPanel(); 559 JPanel subindex_inner_pane_2 = new JPanel(); 560 remove_index = new JButton(get("Remove_Subindex")); 561 remove_index.setMnemonic(KeyEvent.VK_R); 562 set_default = new JButton(get("Set_Default_Subindex")); 563 set_default.setMnemonic(KeyEvent.VK_S); 564 JLabel subcollection_label = new JLabel(get("Subcollection")); 565 subcollection_list_2 = new JList(model); 566 JPanel list_2_pane = new JPanel(); 567 subindex_pane = new JPanel(); 568 JLabel subindexes_label = new JLabel(get("Subindexes")); 569 subindexes_list = new JList(subindexes); 570 subindexes_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 571 JPanel subindexes_pane = new JPanel(); 572 572 573 573 // Add listeners 574 575 576 577 578 579 580 581 582 583 584 585 586 587 574 ChangeListener cl = new ChangeListener(); 575 ExclusiveListSelectionListener ell = new ExclusiveListSelectionListener(); 576 ell.add(subcollection_list_2); 577 ell.add(subindexes_list); 578 add_index.addActionListener(new AddSubIndexListener()); 579 clear_default.addActionListener(new ClearDefaultListener()); 580 exclude.addActionListener(cl); 581 include.addActionListener(cl); 582 remove_index.addActionListener(new RemoveSubIndexListener()); 583 set_default.addActionListener(new SetDefaultListener()); 584 source.addActionListener(cl); 585 flags.addKeyListener(cl); 586 match.addKeyListener(cl); 587 name.addKeyListener(cl); 588 588 589 589 // Layout 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 590 list_2_pane.setLayout(new BorderLayout()); 591 list_2_pane.add(subcollection_label, BorderLayout.NORTH); 592 list_2_pane.add(new JScrollPane(subcollection_list_2), BorderLayout.CENTER); 593 subindexes_pane.setLayout(new BorderLayout()); 594 subindexes_pane.add(subindexes_label, BorderLayout.NORTH); 595 subindexes_pane.add(new JScrollPane(subindexes_list), BorderLayout.CENTER); 596 subindex_inner_pane_2.setLayout(new GridLayout(2,1)); 597 subindex_inner_pane_2.add(list_2_pane); 598 subindex_inner_pane_2.add(subindexes_pane); 599 default_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 600 default_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder()), BorderFactory.createEmptyBorder(2,2,2,2))); 601 default_pane.setLayout(new BorderLayout()); 602 default_pane.add(default_label, BorderLayout.WEST); 603 default_pane.add(default_value, BorderLayout.CENTER); 604 subindex_inner_pane_1.setLayout(new BorderLayout()); 605 subindex_inner_pane_1.add(subindex_inner_pane_2, BorderLayout.CENTER); 606 subindex_inner_pane_1.add(default_pane, BorderLayout.SOUTH); 607 button_pane_2.setLayout(new GridLayout(2,2)); 608 button_pane_2.add(add_index); 609 button_pane_2.add(remove_index); 610 button_pane_2.add(clear_default); 611 button_pane_2.add(set_default); 612 subindex_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 613 subindex_pane.setLayout(new BorderLayout()); 614 subindex_pane.add(subindex_inner_pane_1, BorderLayout.CENTER); 615 subindex_pane.add(button_pane_2, BorderLayout.SOUTH); 616 } 617 /** Method to unregister any listeners to avoid memory leaks. 618 618 */ 619 620 621 619 public void destroy() { 620 } 621 /** We have overriden this method to provide completely different functionality, given that our JPanel will never actually have focus. We call this method in GUI on the view we are about to replace, and this method checks if a change has occured and if so updates the current object. 622 622 * @return A <i>boolean</i> which is always <i>false</i>. 623 623 * @see org.greenstone.gatherer.msm.ElementWrapper 624 624 */ 625 625 public boolean hasFocus() { 626 626 // If we have a current metadata open, and something has changed then save the change. 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 627 if(changed && current != null) { 628 String n = name.getText(); 629 String s = null; 630 Object o = source.getSelectedItem(); 631 if(o instanceof ElementWrapper) { 632 ElementWrapper e = (ElementWrapper)o; 633 s = e.toString(); 634 } 635 String e = match.getText(); 636 String f = flags.getText(); 637 if(n != null && n.length() > 0 && (s == null || s.length() > 0) && e != null && e.length() > 0) { 638 current.update(n, s, e, include.isSelected(), f); 639 int index = model.indexOf(current); 640 changed(); 641 subindexes.changed(); 642 if(default_index != null) { 643 default_value.setText(default_index.getSubIndex().toString()); 644 } 645 } 646 changed = false; 647 } 648 return false; 649 } 650 /** Overriden to ensure the instructions are scrolled to top. 651 651 */ 652 653 654 655 656 657 658 659 660 652 public void updateUI() { 653 if(instructions != null) { 654 instructions.setCaretPosition(0); 655 } 656 super.updateUI(); 657 } 658 /** Listens for actions apon the 'add' button in the SubcollectionManager controls, and if detected calls the addSubcollection method of the manager with a newly created subcollection. */ 659 private class AddListener 660 implements ActionListener { 661 661 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we wish to retrieve information from the various edit controls, and if we have sufficient data to build a new subcollection do so. 662 662 * @param event An <strong>ActionEvent</strong> containing information about the event. … … 664 664 * @see org.greenstone.gatherer.msm.ElementWrapper 665 665 */ 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 666 public void actionPerformed(ActionEvent event) { 667 String n = name.getText(); 668 String s = null; 669 Object o = source.getSelectedItem(); 670 if(o instanceof ElementWrapper) { 671 ElementWrapper e = (ElementWrapper)o; 672 s = e.toString(); 673 } 674 String e = match.getText(); 675 String f = flags.getText(); 676 if(n != null && n.length() > 0 && (s == null || s.length() > 0) && e != null && e.length() > 0) { 677 Subcollection sub = new Subcollection(n, include.isSelected(), s, e, f); 678 addSubcollection(sub); 679 } 680 changed = false; 681 } 682 } 683 /** Listens for actions apon the 'add subindex' button in the SubcollectionManager controls, and if detected calls the addSubindex method of the manager with a newly created subindex. */ 684 private class AddSubIndexListener 685 implements ActionListener { 686 686 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a series of subcollections selected, and if so build an new subindex based apon them. 687 687 * @param event An <strong>ActionEvent</strong> containing information about the event. 688 688 * @see org.greenstone.gatherer.cdm.SubIndex 689 689 */ 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 690 public void actionPerformed(ActionEvent event) { 691 if(!subcollection_list_2.isSelectionEmpty()) { 692 Vector selected = new Vector(); 693 Object raw[] = subcollection_list_2.getSelectedValues(); 694 for(int i = 0; i < raw.length; i++) { 695 selected.add(raw[i]); 696 } 697 addSubIndex(new SubIndex(selected)); 698 } 699 } 700 } 701 /** This class listens for any key entry in a text field, selection change in a combobox or button click, and when detected sets the changed flag to true. Its also convenient to use this class to test if the add button should be active yet. */ 702 public class ChangeListener 703 extends KeyAdapter 704 implements ActionListener { 705 705 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to record that somethings changed, then validate the controls. 706 706 * @param event An <strong>ActionEvent</strong> containing information about the event. 707 707 */ 708 709 710 711 708 public void actionPerformed(ActionEvent event) { 709 changed = true; 710 validate(); 711 } 712 712 /** Any inheritor of KeyAdapter can override this method so we can be informed when an key has been type in one of our target controls. In this case we want to record that somethings changed, then validate the controls. 713 713 * @param event An <strong>KeyEvent</strong> containing information about the event. 714 714 */ 715 716 717 718 715 public void keyPressed(KeyEvent event) { 716 changed = true; 717 validate(); 718 } 719 719 /** Method to validate the current editor values, and enable or disable controls based on said values. */ 720 721 722 723 724 725 726 727 728 729 730 731 720 private void validate() { 721 if(changed && name.getText().length() > 0 && match.getText().length() > 0) { 722 add.setEnabled(true); 723 } 724 else { 725 add.setEnabled(false); 726 } 727 } 728 } 729 /** Listens for actions apon the 'clear default subindex' button in the SubcollectionManager controls, and if detected calls the setDefaultSubIndex() method of the manager with <i>null</i>. */ 730 private class ClearDefaultListener 731 implements ActionListener { 732 732 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to clear the default subindex. 733 733 * @param event An <strong>ActionEvent</strong> containing information about the event. 734 734 */ 735 736 737 738 739 740 741 742 743 735 public void actionPerformed(ActionEvent event) { 736 setDefaultSubIndex(null); 737 clear_default.setEnabled(false); 738 default_value.setText(""); 739 } 740 } 741 /** This class listens for selections in the list on the subcollections pane of the SubcollectionManager, and updates the controls as necessary to reflect selection. */ 742 private class ListListener 743 implements ListSelectionListener { 744 744 /** Any implementation of ListSelectionListener must include this method so we can be informed when the selection changes. In this case we want to execute any changes the users made to the entry, then update the controls with details of the new selection. 745 745 * @param event A <strong>ListSelectionEvent</strong> containing information related to this event. … … 747 747 * @see org.greenstone.gatherer.msm.ElementWrapper 748 748 */ 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 749 public void valueChanged(ListSelectionEvent event) { 750 // If we have a previous collection and the users changed something, but not added, then update subcollection. 751 if(changed && current != null) { 752 String n = name.getText(); 753 String s = null; 754 Object o = source.getSelectedItem(); 755 if(o instanceof ElementWrapper) { 756 ElementWrapper e = (ElementWrapper)o; 757 s = e.toString(); 758 } 759 String e = match.getText(); 760 String f = flags.getText(); 761 if(n != null && n.length() > 0 && (s == null || s.length() > 0) && e != null && e.length() > 0) { 762 current.update(n, s, e, include.isSelected(), f); 763 int index = model.indexOf(current); 764 changed(); 765 subindexes.changed(); 766 if(default_index != null) { 767 default_value.setText(default_index.getSubIndex().toString()); 768 } 769 } 770 } 771 // Now load the new entry. 772 if(!subcollection_list.isSelectionEmpty()) { 773 current = (Subcollection) subcollection_list.getSelectedValue(); 774 flags.setText(current.getFlags()); 775 include.setSelected(current.getInclude()); 776 exclude.setSelected(!current.getInclude()); 777 match.setText(current.getExpression()); 778 name.setText(current.getName()); 779 String s = current.getSource(); 780 int pos = 0; 781 Object value = source.getItemAt(pos); 782 while(value != null) { 783 if(value instanceof ElementWrapper) { 784 ElementWrapper e = (ElementWrapper) value; 785 if(e.toString().equals(s)) { 786 source.setSelectedIndex(pos); 787 value = null; 788 } 789 else { 790 pos++; 791 value = source.getItemAt(pos); 792 } 793 } 794 else if(value.toString().equals(s)) { 795 source.setSelectedIndex(pos); 796 value = null; 797 } 798 else { 799 pos++; 800 value = source.getItemAt(pos); 801 } 802 } 803 // Can't add one thats already there. 804 add.setEnabled(false); 805 // You can remove it though... 806 remove.setEnabled(true); 807 } 808 else { 809 flags.setText(""); 810 include.setSelected(true); 811 match.setText(""); 812 name.setText(""); 813 source.setSelectedIndex(0); 814 remove.setEnabled(false); 815 } 816 // Have to do this after so we don't get called when nothings actually changed. 817 changed = false; 818 } 819 } 820 /** Listens for actions apon the 'remove' button in the SubcollectionManager controls, and if detected calls the remove method of the manager with the SubIndex selected for removal. */ 821 private class RemoveListener 822 implements ActionListener { 823 823 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subcolleciton selected, and if so remove both it and any subindexes based on it. 824 824 * @param event An <strong>ActionEvent</strong> containing information about the event. 825 825 * @see org.greenstone.gatherer.cdm.Subcollection 826 826 */ 827 828 829 830 831 832 833 834 835 836 837 827 public void actionPerformed(ActionEvent event) { 828 if(!subcollection_list.isSelectionEmpty()) { 829 Subcollection sub_col = (Subcollection)subcollection_list.getSelectedValue(); 830 removeSubIndexes(sub_col); 831 removeSubcollection(sub_col); 832 } 833 } 834 } 835 /** Listens for actions apon the 'remove subindex' button in the SubcollectionManager controls, and if detected calls the removeSubIndex method of the manager with the SubIndex selected for removal. */ 836 private class RemoveSubIndexListener 837 implements ActionListener { 838 838 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subindex selected, and if so remove it. 839 839 * @param event An <strong>ActionEvent</strong> containing information about the event. 840 840 * @see org.greenstone.gatherer.cdm.SubIndex 841 841 */ 842 843 844 845 846 847 848 849 850 842 public void actionPerformed(ActionEvent event) { 843 if(!subindexes_list.isSelectionEmpty()) { 844 removeSubIndex((SubIndex)subindexes_list.getSelectedValue()); 845 } 846 } 847 } 848 /** Listens for actions apon the 'set default subindex' button in the SubcollectionManager controls, and if detected calls the setDefaultSubIndex method of the manager with the SubIndex selected for default. */ 849 private class SetDefaultListener 850 implements ActionListener { 851 851 /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subindex selected, and if so set it as default. 852 852 * @param event An <strong>ActionEvent</strong> containing information about the event. … … 854 854 * @see org.greenstone.gatherer.cdm.SubIndex 855 855 */ 856 857 858 859 860 861 862 863 864 856 public void actionPerformed(ActionEvent event) { 857 if(!subindexes_list.isSelectionEmpty()) { 858 setDefaultSubIndex(new DefaultSubIndex((SubIndex)subindexes_list.getSelectedValue())); 859 clear_default.setEnabled(true); 860 default_value.setText(default_index.getSubIndex().toString()); 861 } 862 } 863 } 864 } 865 865 } 866 866 -
trunk/gli/src/org/greenstone/gatherer/cdm/TranslationManager.java
r4293 r4366 87 87 import org.greenstone.gatherer.cdm.LanguageManager; 88 88 /** This class provides a graphical interface to allow a user to quickly and conviently (ie all in one place) translate the text fragments associated with general metadata and indexes into each of the assigned languages in the collection. It should provide clear controls for the editing of these text fragments, plus clear indicate what languages still need further translation, which it will do through a combination of coloring and other visual indicators. 89 * @author John Thompson, Greenstone Digital Library, University of Waikato90 * @version 2.391 */89 * @author John Thompson, Greenstone Digital Library, University of Waikato 90 * @version 2.3 91 */ 92 92 public class TranslationManager 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 93 extends JDialog { 94 /** A reference to our creator, the CollectionDesignManager. */ 95 private CollectionDesignManager manager = null; 96 /** The currently selected collection metadata. */ 97 private CollectionMeta current = null; 98 /** A listener for selection changes in the feature list. */ 99 private FeatureListener features_listener = null; 100 /** A reference to the Gatherer, for access to the Dictionary and messaging purposes. */ 101 private Gatherer gatherer = null; 102 /** The button used to close the translation dialog. */ 103 private JButton close_button = null; 104 /** A reference to the dialog so that our inner classes can interact with it. */ 105 private JDialog dialog = null; 106 /** The label denoting the features list. */ 107 private JLabel features_label = null; 108 /** The label denoting the language table. */ 109 private JLabel languages_label = null; 110 /** The label denoting the original text area. */ 111 private JLabel original_label = null; 112 /** The label denoting the original language field */ 113 private JLabel original_language_label = null; 114 /** The label denoting the translated text area. */ 115 private JLabel translation_label = null; 116 /** The label denoting the translated language field. */ 117 private JLabel translation_language_label = null; 118 /** A list of the various features that have collection metadata associated with them. */ 119 private JList features_list = null; 120 /** The base panel. */ 121 private JPanel content_pane = null; 122 /** The right panel. */ 123 private JPanel control_pane = null; 124 /** The panel the features list is shown in. */ 125 private JPanel features_pane = null; 126 /** The panel the languages table is shown in. */ 127 private JPanel languages_pane = null; 128 /** The panel the original text is shown in. */ 129 private JPanel original_pane = null; 130 /** The panel the original language field is shown in. */ 131 private JPanel original_header_pane = null; 132 /** The panel the translation text is shown in. */ 133 private JPanel translation_pane = null; 134 /** The panel the translation language field is shown in. */ 135 private JPanel translation_header_pane = null; 136 /** The table used to show the languages supported by the collection. */ 137 private JTable languages_table = null; 138 /** The text area displaying the original text (in the default language). */ 139 private JTextArea original = null; 140 /** The text area displaying the translated text (in the selected language). */ 141 private JTextArea translation = null; 142 /** The language currently selected. */ 143 private Language current_language = null; 144 /** The proxy model to the language manager which serves as the basis for the table. */ 145 private TableProxy languages_model = null; 146 /** The default size of the features list. */ 147 static final private Dimension FEATURE_SIZE = new Dimension(200, 400); 148 /** The default size of the translation dialog. */ 149 static final private Dimension SIZE = new Dimension(800, 450); 150 /** Constructor. 151 * @param gatherer A reference to the <strong>Gatherer</strong>. 152 * @param manager A reference to the <strong>CollectionDesignManager</strong>. 153 * @see org.greenstone.gatherer.Configuration 154 * @see org.greenstone.gatherer.cdm.CollectionMeta 155 * @see org.greenstone.gatherer.cdm.TranslationManager.TableProxy 156 */ 157 public TranslationManager(Gatherer gatherer, CollectionDesignManager manager) { 158 super(); 159 this.dialog = this; 160 this.gatherer = gatherer; 161 this.manager = manager; 162 this.languages_model = new TableProxy(); 163 // Create the features model. Basically we want one string entry for each unique name in the metadata. 164 ArrayList features_model = new ArrayList(); 165 for(int i = 0; i < manager.collectionmetadatum.size(); i++) { 166 CollectionMeta metadata = (CollectionMeta) manager.collectionmetadatum.get(i); 167 Object object = metadata.getName(); 168 String name = null; 169 if(object instanceof Index) { 170 name = ((Index)object).toString(false); 171 } 172 else { 173 name = object.toString(); 174 } 175 if(!features_model.contains(name)) { 176 features_model.add(name); 177 } 178 } 179 Collections.sort(features_model); 180 // Create components... 181 setModal(true); 182 setSize(SIZE); 183 close_button = new JButton(get("General.Close")); 184 content_pane = (JPanel) getContentPane(); 185 control_pane = new JPanel(); 186 features_label = new JLabel(get("Affected_Features")); 187 features_label.setHorizontalAlignment(JLabel.CENTER); 188 features_list = new JList(features_model.toArray()); 189 features_listener = new FeatureListener(); 190 features_pane = new JPanel(); 191 features_pane.setPreferredSize(FEATURE_SIZE); 192 languages_table = new JTable(languages_model); 193 languages_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 194 languages_label = new JLabel(get("Assigned_Languages")); 195 languages_label.setHorizontalAlignment(JLabel.CENTER); 196 languages_pane = new JPanel(); 197 original = new JTextArea(); 198 original.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 199 original.setEditable(false); 200 original.setFont(new Font("Arial Unicode MS", Font.PLAIN, 10)); 201 original_header_pane = new JPanel(); 202 original_label = new JLabel(get("Default_Text")); 203 original_label.setHorizontalAlignment(JLabel.CENTER); 204 original_language_label = new JLabel(""); 205 original_pane = new JPanel(); 206 translation = new JTextArea(); 207 translation.setEnabled(false); 208 translation.setFont(new Font("Arial Unicode MS", Font.PLAIN, 10)); 209 translation_header_pane = new JPanel(); 210 translation_label = new JLabel(get("Translation")); 211 translation_label.setHorizontalAlignment(JLabel.CENTER); 212 translation_language_label = new JLabel(""); 213 translation_pane = new JPanel(); 214 // Add listeners... 215 close_button.addActionListener(new CloseListener()); 216 translation.addKeyListener(new TranslationListener()); 217 features_list.addListSelectionListener(features_listener); 218 features_list.addListSelectionListener(languages_model); 219 ListSelectionModel languages_selection_model = languages_table.getSelectionModel(); 220 languages_selection_model.addListSelectionListener(new TableListener()); 221 // Layout... 222 features_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 223 features_pane.setLayout(new BorderLayout()); 224 features_pane.add(features_label, BorderLayout.NORTH); 225 features_pane.add(new JScrollPane(features_list), BorderLayout.CENTER); 226 languages_label.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 227 languages_pane.setLayout(new BorderLayout()); 228 languages_pane.add(languages_label, BorderLayout.NORTH); 229 languages_pane.add(new JScrollPane(languages_table), BorderLayout.CENTER); 230 original_header_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 231 original_header_pane.setLayout(new BorderLayout()); 232 original_header_pane.add(original_label, BorderLayout.WEST); 233 original_header_pane.add(original_language_label, BorderLayout.EAST); 234 original_pane.setLayout(new BorderLayout()); 235 original_pane.add(original_header_pane, BorderLayout.NORTH); 236 original_pane.add(new JScrollPane(original), BorderLayout.CENTER); 237 translation_header_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 238 translation_header_pane.setLayout(new BorderLayout()); 239 translation_header_pane.add(translation_label, BorderLayout.WEST); 240 translation_header_pane.add(translation_language_label, BorderLayout.EAST); 241 translation_pane.setLayout(new BorderLayout()); 242 translation_pane.add(translation_header_pane, BorderLayout.NORTH); 243 translation_pane.add(new JScrollPane(translation), BorderLayout.CENTER); 244 control_pane.setLayout(new GridLayout(3,1)); 245 control_pane.add(languages_pane); 246 control_pane.add(original_pane); 247 control_pane.add(translation_pane); 248 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 249 content_pane.setLayout(new BorderLayout()); 250 content_pane.add(features_pane, BorderLayout.WEST); 251 content_pane.add(control_pane, BorderLayout.CENTER); 252 content_pane.add(close_button, BorderLayout.SOUTH); 253 // Display... 254 Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); 255 for (int i = 0; i < languages_model.getColumnCount() - 1; i++) { 256 TableColumn column = languages_table.getColumnModel().getColumn(i); 257 column.setMinWidth(0); 258 column.setMaxWidth(screen_size.width); 259 column.setPreferredWidth((SIZE.width - FEATURE_SIZE.width) / 6); 260 } 261 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 262 show(); 263 } 264 /** Destructor 265 265 */ 266 267 268 269 266 public void destroy() { 267 dialog = null; 268 } 269 /** Retrieve a phrase from the dictionary based on a certain key. 270 270 * @param key The search <strong>String</strong>. 271 271 * @return The matching phrase from the Dictionary as a <strong>String</strong>. 272 272 */ 273 274 275 276 273 private String get(String key) { 274 return get(key, null); 275 } 276 /** Retrieve a phrase from the dictionary based on a certain key and certain arguments. 277 277 * @param key The search <strong>String</strong>. 278 278 * @param args A <strong>String[]</strong> used to complete and format the returned phrase. … … 281 281 * @see org.greenstone.gatherer.Gatherer 282 282 */ 283 284 285 286 287 288 289 290 291 292 293 294 */ 295 296 297 298 299 300 301 302 303 304 305 306 307 283 private String get(String key, String args[]) { 284 if(key.indexOf(".") == -1) { 285 key = "CDM.TranslationManager." + key; 286 } 287 return gatherer.dictionary.get(key, args); 288 } 289 /** Listens for actions apon the close button. */ 290 private class CloseListener 291 implements ActionListener { 292 /** Closes the dialog. Any implementation of an ActionListener must include this method so we can be informed when an action has occured. 293 * @param event An <Strong>ActionEvent</strong> containing details about the action. 294 */ 295 public void actionPerformed(ActionEvent event) { 296 dialog.dispose(); 297 } 298 } 299 /** Listens to selections within the feature list. */ 300 private class FeatureListener 301 implements ListSelectionListener { 302 /** Called whenever the selection changes in the registered list, which requires us to create a new TableProxy. 303 * @param event A <strong>ListSelectionEvent</strong> containing more information about the list selection change. 304 * @see org.greenstone.gatherer.cdm.CollectionMeta 305 * @see org.greenstone.gatherer.cdm.Language 306 */ 307 public void valueChanged(ListSelectionEvent event) { 308 308 // Try to find the default language value for this feature. 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 309 String name = (String) features_list.getSelectedValue(); 310 Language language = manager.languages.getDefaultLanguage(); 311 CollectionMeta metadata = manager.collectionmetadatum.getMetadata(name, language, true); 312 if(metadata != null && metadata.getValue() != null && metadata.getValue().length() != 0) { 313 if(!metadata.getLanguage().equals(language)) { 314 original_label.setText(get("Existing_Text")); 315 } 316 else { 317 original_label.setText(get("Default_Text")); 318 } 319 original.setText(metadata.getValue()); 320 original_language_label.setText(metadata.getLanguage().toString()); 321 } 322 else { 323 original_label.setText(get("No_Text")); 324 original.setText(""); 325 } 326 } 327 } 328 /** Listens for selections within the languages table. */ 329 private class TableListener 330 implements ListSelectionListener { 331 /** Called whenever the selection changes in the registered list, which requires us to update several parts of the dialog. 332 * @param event A <strong>ListSelectionEvent</strong> containing more information about the list selection change. 333 * @see org.greenstone.gatherer.cdm.CollectionMeta 334 * @see org.greenstone.gatherer.cdm.Language 335 */ 336 public void valueChanged(ListSelectionEvent event) { 337 if (event.getValueIsAdjusting()) return; 338 ListSelectionModel lsm = (ListSelectionModel)event.getSource(); 339 if (!lsm.isSelectionEmpty()) { 340 int row = lsm.getMinSelectionIndex(); 341 current = languages_model.getMetadata(row); 342 if(current != null) { 343 if (current.getValue() != null) { 344 translation.setText(current.getValue()); 345 } 346 else { 347 translation.setText(""); 348 } 349 current_language = current.getLanguage(); 350 } 351 else { 352 current_language = (Language) languages_model.getValueAt(row, 1); 353 translation.setText(""); 354 } 355 features_listener.valueChanged(null); 356 translation_language_label.setText(current_language.toString()); 357 translation.setEnabled(true); 358 } 359 } 360 } 361 /** A AbstractTableModel extension based upon a vector of CollectionMeta, which can also listen to a list for selection changes. */ 362 private class TableProxy 363 extends AbstractTableModel 364 implements ListSelectionListener { 365 /** Called when the contents of a cell changes. 366 */ 367 public void cellChanged() { 368 fireTableDataChanged(); 369 } 370 /** Returns the most specific superclass for all the cell values in the column. 371 371 * @param column An <i>int</i> specifying which columns class to be determined. 372 372 * @return The columns <strong>Class</strong>. 373 373 */ 374 374 public Class getColumnClass(int column) { 375 376 377 375 return String.class; 376 } 377 /** Returns the number of columns in the model. 378 378 * @return An <i>int</i> specifying the number of columns. 379 379 */ 380 381 382 383 384 385 380 public int getColumnCount() { 381 if(!features_list.isSelectionEmpty()) { 382 return 2; 383 } 384 return 0; 385 } 386 386 /** Returns the name of the column at columnIndex. 387 387 * @param column An <i>int</i> specifying the column whose name you are interesting in. 388 388 * @return The name of the column as a <strong>String</strong>. 389 389 */ 390 391 392 393 394 395 396 397 398 399 400 401 402 390 public String getColumnName(int column) { 391 String name = null; 392 switch(column) { 393 case 0: 394 name = get("Language"); 395 break; 396 case 1: 397 name = get("Text"); 398 break; 399 } 400 return name; 401 } 402 /** Retrieve the collection metadata from the specified row. 403 403 * @param row An <i>int</i> indicating the row in question. 404 404 * @see org.greenstone.gatherer.cdm.Language 405 405 * @see org.greenstone.gatherer.cdm.LanguageManager 406 406 */ 407 408 409 410 411 407 public CollectionMeta getMetadata(int row) { 408 String name = (String) features_list.getSelectedValue(); 409 Language language = (Language) getValueAt(row, 0); 410 return manager.collectionmetadatum.getMetadata(name, language, false); 411 } 412 412 /** Returns the number of rows in the model. 413 413 * @return An <i>int</i> specifying the number of rows in the table, which is exactly the number of languages assigned to this collection. 414 414 * @see org.greenstone.gatherer.cdm.LanguageManager 415 415 */ 416 417 418 419 416 public int getRowCount() { 417 return manager.languages.size(); 418 } 419 /** Returns the value for the cell at column and row. 420 420 * @param row The desired row as an <i>int</i>. 421 421 * @param column The desired column as an <i>int</i>. … … 425 425 * @see org.greenstone.gatherer.cdm.LanguageManager 426 426 */ 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 427 public Object getValueAt(int row, int column) { 428 if(!features_list.isSelectionEmpty()) { 429 // First of all find the Language that matches the row and the current feature name, because in any case you'll need them. 430 Language language = (Language)manager.languages.get(row); 431 String feature_name = (String)features_list.getSelectedValue(); 432 // Also try to locate the collection level metadata that matches. 433 CollectionMeta metadata = manager.collectionmetadatum.getMetadata(feature_name, language, false); 434 // Now we do subtly different things depending on the column. 435 switch(column) { 436 case 0: // The Language. Easiest case. 437 return language; 438 case 1: // The value of the metadata. Needs us to retrieve the value for a certain feature and language. 439 if(metadata != null) { 440 return metadata.getValue(); 441 } 442 } 443 // If all else fails, return an empty string and hope the class caster doesn't explode. 444 } 445 return ""; 446 } 447 447 /** Returns true if the cell at row and column is editable. 448 448 * @param row The row of the cell as an <i>int</i>. 449 449 * @param column The column of the cell also as an <i>int</i>. 450 450 */ 451 452 453 451 public boolean isCellEditable(int rowIndex, int columnIndex) { 452 return false; 453 } 454 454 /** Sets the value in the cell at columnIndex and rowIndex to aValue. 455 455 */ 456 457 458 456 public void setValueAt(Object aValue, int rowIndex, int columnIndex) { 457 } 458 /** Any implementation of <i>ListSelectionListener</i> must include this method so we can be informed when the list selection has changed. 459 459 * @param event A <strong>ListSelectionEvent</strong> containing information about the selection change. 460 460 */ 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 461 public void valueChanged(ListSelectionEvent event) { 462 fireTableStructureChanged(); 463 } 464 } 465 /** This provides a custom renderer that colours the background of the default language cell green, and the foreground of currently unready metadata rows red. */ 466 private class TableRenderer 467 extends DefaultTableCellRenderer { 468 /** Retrieve the component used to rubberstamp the table based on the given parameters. 469 * @param table The <strong>JTable</strong> to rendered. 470 * @param value The <strong>Object</strong> whose representation will be shown in the table row. 471 * @param isSelected <i>true</i> iff the column/row is selected. 472 * @param hasFocus <i>true</i> iff the column/row is in focus. 473 * @param row An <i>int</i> specifying the target row. 474 * @param column An <i>int</i> specifying the target column. 475 * @see org.greenstone.gatherer.cdm.Language 476 */ 477 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 478 Component component = null; 479 479 // Create the component. 480 481 482 483 484 485 486 487 488 489 490 480 if(value instanceof String || value instanceof Language) { 481 JLabel label = new JLabel(value.toString()); 482 label.setOpaque(true); 483 component = label; 484 } 485 else { 486 JCheckBox check = new JCheckBox("", ((Boolean)value).booleanValue()); 487 JPanel pane = new JPanel(new BorderLayout()); 488 pane.add(check, BorderLayout.CENTER); 489 component = pane; 490 } 491 491 // Check for default property 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 492 Language language = (Language)languages_model.getValueAt(row, 1); 493 if(language.isDefault()) { 494 if(isSelected) { 495 component.setBackground(Gatherer.config.getColor("coloring.collection_selection_background", false)); 496 } 497 else { 498 component.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 499 } 500 } 501 else { 502 if(isSelected) { 503 component.setBackground(table.getSelectionBackground()); 504 } 505 else { 506 component.setForeground(Color.white); 507 } 508 } 509 509 // Check for readiness 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 510 Boolean ready = (Boolean)languages_model.getValueAt(row, 0); 511 if(!ready.booleanValue()) { 512 component.setForeground(Color.red); 513 } 514 else { 515 component.setForeground(Color.black); 516 } 517 return component; 518 } 519 } 520 /** Listens for actions within the translation text field and immediately updates the collection metadata as necessary. */ 521 private class TranslationListener 522 extends KeyAdapter { 523 /** This method is called when a key has been released, and is perfect for us to synchronize the contents of the translation field with the collection metadata associated with the selected feature and language. 524 * @param event A <strong>KeyEvent</strong> containing more detail about the key released. 525 * @see org.greenstone.gatherer.cdm.CollectionMeta 526 * @see org.greenstone.gatherer.cdm.CollectionMetaManager 527 * @see org.greenstone.gatherer.cdm.Index 528 */ 529 public void keyReleased(KeyEvent event) { 530 if(current == null) { 531 String name = (String)features_list.getSelectedValue(); 532 Index index = manager.indexes.getIndex(name); 533 if(index == null) { 534 current = new CollectionMeta(manager, name, current_language, translation.getText()); 535 } 536 else { 537 current = new CollectionMeta(manager, index, current_language, translation.getText()); 538 } 539 manager.collectionmetadatum.addMetadata(current); 540 } 541 else { 542 current.update(current.getName(), current.getLanguage(), translation.getText()); 543 } 544 languages_model.cellChanged(); 545 } 546 } 547 547 } -
trunk/gli/src/org/greenstone/gatherer/checklist/CheckList.java
r4293 r4366 66 66 // #################################################################################### 67 67 public class CheckList 68 69 70 71 72 73 74 75 76 77 78 79 68 extends JList { 69 /** The border used when a list row is not in focus. */ 70 protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); 71 /** Constructor. */ 72 public CheckList() { 73 super(); 74 setCellRenderer(new CellRenderer()); 75 setModel(new DefaultListModel()); 76 setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 77 addMouseListener(new CheckListListener()); 78 } 79 /** Constructor. 80 80 * @param list_data An <strong>ArrayList</strong> of entries for this checklist. 81 81 * @see org.greenstone.gatherer.checklist.CheckList.Entry 82 82 */ 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 83 public CheckList(ArrayList list_data) { 84 super(); 85 // Create a new model. 86 DefaultListModel model = new DefaultListModel(); 87 for(int i = 0; i < list_data.size(); i++) { 88 Entry entry = new Entry(list_data.get(i)); 89 String name = entry.toString(); 90 int index = 0; 91 boolean found = false; 92 while(index < model.size() && !found) { 93 Object sibling = model.getElementAt(index); 94 if(name.compareTo(sibling.toString()) <= 0) { 95 model.add(index, entry); 96 found = true; 97 } 98 index++; 99 } 100 if(!found) { 101 model.addElement(entry); 102 } 103 } 104 setModel(model); 105 setCellRenderer(new CellRenderer()); 106 addMouseListener(new CheckListListener()); 107 setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 108 } 109 110 /** Constructor. */ 111 public CheckList(ListModel data_model) { 112 super(data_model); 113 setCellRenderer(new CellRenderer()); 114 addMouseListener(new CheckListListener()); 115 setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 116 } 117 118 public void addEntry(Entry entry) { 119 DefaultListModel model = (DefaultListModel) getModel(); 120 String name = entry.toString(); 121 int index = 0; 122 boolean found = false; 123 while(index < model.size() && !found) { 124 Object sibling = model.getElementAt(index); 125 if(name.compareTo(sibling.toString()) <= 0) { 126 model.add(index, entry); 127 found = true; 128 } 129 index++; 130 sibling = null; 131 } 132 if(!found) { 133 model.addElement(entry); 134 } 135 name = null; 136 model = null; 137 } 138 139 public Entry get(int index) { 140 DefaultListModel model = (DefaultListModel) getModel(); 141 return (Entry) model.get(index); 142 } 143 144 /** Retrieve the currently ticked entries from this list. 145 145 * @return An <strong>ArrayList</strong> containing only those entries from the initial list that are checked. 146 146 * @see org.greenstone.gatherer.checklist.CheckList.Entry 147 147 */ 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 148 public ArrayList getSelected() { 149 ArrayList result = new ArrayList(); 150 DefaultListModel model = (DefaultListModel) getModel(); 151 int size = model.size(); 152 for(int i = 0; i < size; i++) { 153 Entry entry = (Entry) model.get(i); 154 if(entry.isSelected()) { 155 result.add(entry.getObject()); 156 } 157 } 158 return result; 159 } 160 161 public boolean isSelected(int index) { 162 DefaultListModel model = (DefaultListModel) getModel(); 163 Entry entry = (Entry) model.get(index); 164 return entry.isSelected(); 165 } 166 167 public int getEntryCount() { 168 return getModel().getSize(); 169 } 170 171 /** Checks the entries in the list whose name appear in the given array. 172 172 * @param names The name of entries to be checked as a <strong>String[]</strong>. 173 173 * @see org.greenstone.gatherer.checklist.CheckList.Entry 174 174 */ 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 175 public void setSelected(String names[]) { 176 DefaultListModel model = (DefaultListModel) getModel(); 177 int size = model.size(); 178 for(int i = 0; i < size; i++) { 179 Entry entry = (Entry) model.get(i); 180 for(int j = 0; names != null && j < names.length; j++) { 181 if(entry.toString().equals(names[j])) { 182 entry.setSelected(true); 183 } 184 } 185 } 186 } 187 /** A custom list cell renderer for producing rows which contain clickable check boxes. */ 188 protected class CellRenderer 189 implements ListCellRenderer { 190 /** Return a component that has been configured to display the specified value. That component's paint method is then called to "render" the cell. If it is necessary to compute the dimensions of a list because the list cells do not have a fixed size, this method is called to generate a component on which getPreferredSize can be invoked. 191 * @param list The </strong>JList</strong> we're painting. 192 * @param value The value returned by list.getModel().getElementAt(index), as an <strong>Object</strong>. 193 * @param index The cells index as an <i>int</i>. 194 * @param is_selected <i>true</i> if the specified cell was selected, <i>false</i> otherwise. 195 * @param cell_has_focus <i>true</i> if and only if the specified cell has the focus. 196 * @return A <strong>Component</strong> whose paint() method will render the specified value. 197 197 */ 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 198 public Component getListCellRendererComponent(JList list, Object value, int index, boolean is_selected, boolean cell_has_focus) { 199 JCheckBox checkbox = (JCheckBox) value; 200 checkbox.setBackground(is_selected ? list.getSelectionBackground() : list.getBackground()); 201 checkbox.setForeground(is_selected ? list.getSelectionForeground() : list.getForeground()); 202 checkbox.setEnabled(list.isEnabled()); 203 checkbox.setFont(list.getFont()); 204 checkbox.setFocusPainted(false); 205 checkbox.setBorderPainted(true); 206 checkbox.setBorder((is_selected) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); 207 return checkbox; 208 } 209 } 210 /** Listens for clicks apon the checks within the list, and updates as necessary. */ 211 private class CheckListListener 212 extends MouseAdapter { 213 private Entry previous_checkbox = null; 214 /** Called whenever the mouse is clicked over our list. We find the nearest checkbox and change its state. 215 * @param e A <strong>MouseEvent</strong> containing everything you ever wanted to know about the mouse event but were afraid to ask. 216 */ 217 public void mousePressed(MouseEvent e) { 218 JList list = (JList) e.getSource(); 219 int index = list.locationToIndex(e.getPoint()); 220 Entry checkbox = (Entry)list.getModel().getElementAt(index); 221 221 // If this is the same checkbox as was recently selected, change the tick. 222 223 224 225 226 227 222 if(list.isSelectedIndex(index) && checkbox == previous_checkbox) { 223 if(!checkbox.isFixed()) { 224 checkbox.setSelected(!checkbox.isSelected()); 225 } 226 checkbox.grabFocus(); 227 } 228 228 // Otherwise the selection has just changed, so select the new checkbox 229 230 231 232 233 234 235 236 229 else if(list.isSelectedIndex(index)) { 230 previous_checkbox = checkbox; 231 } 232 else { 233 previous_checkbox = null; 234 } 235 } 236 } 237 237 } 238 238 -
trunk/gli/src/org/greenstone/gatherer/checklist/Entry.java
r4293 r4366 9 9 /** An Entry encapsulates a single row of the list, both its check box and the object the row represents. */ 10 10 public class Entry 11 12 13 14 15 16 17 18 19 20 21 22 23 24 11 extends JCheckBox { 12 /** Is this checkboxes state fixed. */ 13 private boolean fixed = false; 14 /** The original object this row is based on, and whose toString() is used to generate the check box label. */ 15 private Object object = null; 16 /** A sundry string for storing original values (rather than strings retrieved from dictionary). */ 17 private String property; 18 /** Constructor. */ 19 public Entry(Object object) { 20 super(object.toString()); 21 this.object = object; 22 // We also build a special tool tip for metadata sets. 23 if(object instanceof MetadataSet) { 24 MetadataSet set = (MetadataSet) object; 25 25 // Build tooltip 26 27 28 29 30 31 32 33 34 35 36 37 26 StringBuffer tip = new StringBuffer(Gatherer.dictionary.get("NewCollectionPrompt.Set_Contains")); 27 tip.append(":\n"); 28 for(int i = 0; i < set.size(); i++) { 29 Element element = set.getElement(i); 30 tip.append(MSMUtils.getIdentifier(element)); 31 if(i < set.size() - 1) { 32 tip.append(", "); 33 } 34 } 35 setToolTipText(Utility.formatHTMLWidth(tip.toString(), 60)); 36 } 37 } 38 38 39 40 41 42 43 39 /** Constructor. */ 40 public Entry(String text, boolean is_selected) { 41 super(text); 42 setSelected(is_selected); 43 } 44 44 45 45 /** Retrieve the object associated with this entry. 46 46 * @return An <strong>Object</strong>. 47 47 */ 48 49 50 51 52 53 48 public Object getObject() { 49 if(object == null) { 50 return getText(); 51 } 52 return object; 53 } 54 54 55 56 57 55 public String getProperty() { 56 return property; 57 } 58 58 59 60 61 59 public boolean isFixed() { 60 return fixed; 61 } 62 62 63 64 65 63 public void setFixed(boolean fixed) { 64 this.fixed = fixed; 65 } 66 66 67 68 69 67 public void setProperty(String property) { 68 this.property = property; 69 } 70 70 71 72 73 74 75 76 71 public String toString() { 72 if(object == null) { 73 return getText(); 74 } 75 return object.toString(); 76 } 77 77 } -
trunk/gli/src/org/greenstone/gatherer/collection/BuildOptions.java
r4293 r4366 15 15 /** Build options uses the argument list found in config.xml and the current settings from the loaded collection configuration to represent the options the user wants to use during import and build. If there are no arguments stored in config.xml, or if the user indicates the argument are out of date, this class tries to parse new ones. */ 16 16 public class BuildOptions { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 17 /** The root element of the build argument tree. */ 18 private Element build_arguments_element; 19 /** The root element of the build values tree. */ 20 private Element build_values_element; 21 /** The root element of the import argument tree. */ 22 private Element import_arguments_element; 23 /** The root element of the import values tree. */ 24 private Element import_values_element; 25 /** A cache of previously created build arguments. */ 26 private Hashtable build_arguments_cache = new Hashtable(); 27 /** A cache of previously created import arguments. */ 28 private Hashtable import_arguments_cache = new Hashtable(); 29 /** The name of an argument element. */ 30 static final private String ARGUMENT = "Argument"; 31 /** The name of the enabled attribute. */ 32 static final private String ENABLED = "enabled"; 33 /** The name of the 'false' value. */ 34 static final private String FALSE = "false"; 35 /** The name of a name attribute. */ 36 static final private String NAME = "name"; 37 /** The name of an option element. */ 38 static final private String OPTION = "Option"; 39 /** The name of the 'true' value. */ 40 static final private String TRUE = "true"; 41 /** When constructing a BuildOptions object we first try to retrieve the valid arguments from config.xml. If that fails we then try to parse the arguments noticed when calling import.pl -xml and buildcol.pl -xml. If that also fails, we load the default arguments from the xml template library. */ 42 public BuildOptions(Element build_values_element, Element import_values_element) { 43 // Try to retrieve the arguments for import and build. 44 build_arguments_element = Gatherer.config.getArguments("buildcol.pl"); 45 import_arguments_element = Gatherer.config.getArguments("import.pl"); 46 // If that fails try to reconstruct the arguments 47 if(build_arguments_element == null || import_arguments_element == null) { 48 build_arguments_element = loadArguments("buildcol.pl"); 49 import_arguments_element = loadArguments("import.pl"); 50 50 // And if that too fails, load the default argument templates 51 if(build_arguments_element == null || import_arguments_element == null) { 52 Document build_document = Utility.parse("xml" + File.separator + "buildcol.xml", true); 53 build_arguments_element = build_document.getDocumentElement(); 54 build_document = null; 55 Document import_document = Utility.parse("xml" + File.separator + "import.xml", true); 56 import_arguments_element = import_document.getDocumentElement(); 57 import_document = null; 58 ///atherer.println("Loaded default BO arguments from templates."); 51 if(build_arguments_element == null || import_arguments_element == null) { 52 Document build_document = Utility.parse("xml" + File.separator + "buildcol.xml", true); 53 build_arguments_element = build_document.getDocumentElement(); 54 build_document = null; 55 Document import_document = Utility.parse("xml" + File.separator + "import.xml", true); 56 import_arguments_element = import_document.getDocumentElement(); 57 import_document = null; 58 ///atherer.println("Loaded default BO arguments from templates."); 59 } 60 else { 61 ///atherer.println("Loaded BO arguments from scripts."); 62 } 63 // By now we should definately have the arguments. However the reason we are here is because they are not stored in the config.xml file, to make sure they are stored now. 64 Gatherer.config.setArguments(build_arguments_element); 65 Gatherer.config.setArguments(import_arguments_element); 66 } 67 else { 68 Gatherer.println("Loaded BO arguments from config.xml"); 69 } 70 // Now take a note of the values too. 71 this.build_values_element = build_values_element; 72 this.import_values_element = import_values_element; 73 } 74 75 /** Retrieve the arguments associated with a certain type of action, ie import or build. */ 76 public String[] getArguments(boolean build) { 77 String arguments[] = null; 78 NodeList argument_list = null; 79 if(build) { 80 argument_list = build_values_element.getElementsByTagName(ARGUMENT); 81 } 82 else { 83 argument_list = import_values_element.getElementsByTagName(ARGUMENT); 84 } 85 for(int i = 0; i < argument_list.getLength(); i++) { 86 String[] temp = new String[2]; 87 Element argument = (Element) argument_list.item(i); 88 ArrayTools.add(arguments, argument.getAttribute(NAME)); 89 String value = MSMUtils.getValue(argument); 90 if(value != null && value.length() > 0) { 91 ArrayTools.add(arguments, value); 92 } 93 } 94 return arguments; 95 } 96 97 /** Retrieve the indexth argument belonging to build. */ 98 public Argument getBuildArgument(int index) { 99 Argument argument = null; 100 // Try to find the argument in the cache. 101 SoftReference reference = (SoftReference) build_arguments_cache.get(new Integer(index)); 102 if(reference != null) { 103 argument = (Argument) reference.get(); 104 } 105 // Otherwise generate a new argument. 106 if(argument == null) { 107 argument = getArgument(build_arguments_element, index); 108 } 109 return argument; 110 } 111 112 /** Retrieve the number of arguments involved in building. */ 113 public int getBuildArgumentCount() { 114 NodeList build_options = build_arguments_element.getElementsByTagName(OPTION); 115 return build_options.getLength(); 116 } 117 118 /** Retrieve the value of a certain build argument. */ 119 public String getBuildValue(String name) { 120 return getValue(build_values_element, name, false); 121 } 122 123 /** Determine if the named argument value is enabled or disabled. */ 124 public boolean getBuildValueEnabled(String name) { 125 boolean result = false; 126 String value = getValue(build_values_element, name, true); 127 if(value != null && value.length() > 0) { 128 result = (value.equalsIgnoreCase(TRUE)); 129 } 130 return result; 131 } 132 133 /** Retrieve all of the build values as a String array ready to added to the buildcol.pl call. */ 134 public String[] getBuildValues() { 135 return getValues(build_values_element); 136 } 137 138 /** Retrieve the indexth argument belonging to import. */ 139 public Argument getImportArgument(int index) { 140 Argument argument = null; 141 // Try to find the argument in the cache. 142 SoftReference reference = (SoftReference) import_arguments_cache.get(new Integer(index)); 143 if(reference != null) { 144 argument = (Argument) reference.get(); 145 } 146 // Otherwise generate a new argument. 147 if(argument == null) { 148 argument = getArgument(import_arguments_element, index); 149 } 150 return argument; 151 } 152 153 /** Retrieve the number of arguments involved in importing. */ 154 public int getImportArgumentCount() { 155 NodeList import_options = import_arguments_element.getElementsByTagName(OPTION); 156 return import_options.getLength(); 157 } 158 159 /** Retrieve the value of a certain build argument. */ 160 public String getImportValue(String name) { 161 return getValue(import_values_element, name, false); 162 } 163 164 /** Determine if the named argument value is enabled or disabled. */ 165 public boolean getImportValueEnabled(String name) { 166 boolean result = false; 167 String value = getValue(import_values_element, name, true); 168 if(value != null && value.length() > 0) { 169 result = (value.equalsIgnoreCase(TRUE)); 170 } 171 return result; 172 } 173 174 /** Retrieve all of the import arguments in a form ready to be sent out to a shell process. */ 175 public String[] getImportValues() { 176 return getValues(import_values_element); 177 } 178 179 /** Set the value of a build argument. */ 180 public void setBuildValue(String name, boolean enable, String value) { 181 setValue(build_values_element, name, enable, value); 182 } 183 184 /** Set the value of a build argument. */ 185 public void setImportValue(String name, boolean enable, String value) { 186 setValue(import_values_element, name, enable, value); 187 } 188 189 /** Remove the given build value. */ 190 public void removeBuildValue(String name) { 191 removeValue(build_values_element, name); 192 } 193 194 /** Remove the given import value. */ 195 public void removeImportValue(String name) { 196 removeValue(import_values_element, name); 197 } 198 199 /** Retrieve the indexth element from the given set of arguments. */ 200 private Argument getArgument(Element arguments_element, int index) { 201 Argument argument = null; 202 try { 203 NodeList option_list = arguments_element.getElementsByTagName(OPTION); 204 if(0 <= index && index < option_list.getLength()) { 205 // Retrieve the appropriate argument 206 Element option = (Element) option_list.item(index); 207 // Iterate through this option elements children, making note of any details we find. 208 argument = new Argument(); 209 for(Node node = option.getFirstChild(); node != null; node = node.getNextSibling()) { 210 String node_name = node.getNodeName(); 211 if(node_name.equals("Name")) { 212 argument.setName(MSMUtils.getValue(node)); 213 } 214 else if(node_name.equals("Desc")) { 215 argument.setDesc(MSMUtils.getValue(node)); 216 } 217 else if(node_name.equals("Type")) { 218 argument.setType(MSMUtils.getValue(node)); 219 } 220 else if(node_name.equals("Default")) { 221 argument.setDefault(MSMUtils.getValue(node)); 222 } 223 else if(node_name.equals("List")) { 224 // Two final loops are required to parse lists. 225 for(Node value = node.getFirstChild(); value != null; value = value.getNextSibling()) { 226 if(value.getNodeName().equals("Value")) { 227 String key = null; 228 String desc = ""; 229 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { 230 node_name = subvalue.getNodeName(); 231 if(node_name.equals("Name")) { 232 key = MSMUtils.getValue(subvalue); 233 } 234 else if(node_name.equals("Desc")) { 235 desc = MSMUtils.getValue(subvalue); 236 } 59 237 } 60 else{61 ///atherer.println("Loaded BO arguments from scripts.");238 if(key != null) { 239 argument.addOption(key, desc); 62 240 } 63 // By now we should definately have the arguments. However the reason we are here is because they are not stored in the config.xml file, to make sure they are stored now. 64 Gatherer.config.setArguments(build_arguments_element); 65 Gatherer.config.setArguments(import_arguments_element); 66 } 67 else { 68 Gatherer.println("Loaded BO arguments from config.xml"); 69 } 70 // Now take a note of the values too. 71 this.build_values_element = build_values_element; 72 this.import_values_element = import_values_element; 73 } 74 75 /** Retrieve the arguments associated with a certain type of action, ie import or build. */ 76 public String[] getArguments(boolean build) { 77 String arguments[] = null; 78 NodeList argument_list = null; 79 if(build) { 80 argument_list = build_values_element.getElementsByTagName(ARGUMENT); 81 } 82 else { 83 argument_list = import_values_element.getElementsByTagName(ARGUMENT); 84 } 85 for(int i = 0; i < argument_list.getLength(); i++) { 86 String[] temp = new String[2]; 87 Element argument = (Element) argument_list.item(i); 88 ArrayTools.add(arguments, argument.getAttribute(NAME)); 89 String value = MSMUtils.getValue(argument); 90 if(value != null && value.length() > 0) { 91 ArrayTools.add(arguments, value); 92 } 93 } 94 return arguments; 95 } 96 97 /** Retrieve the indexth argument belonging to build. */ 98 public Argument getBuildArgument(int index) { 99 Argument argument = null; 100 // Try to find the argument in the cache. 101 SoftReference reference = (SoftReference) build_arguments_cache.get(new Integer(index)); 102 if(reference != null) { 103 argument = (Argument) reference.get(); 104 } 105 // Otherwise generate a new argument. 106 if(argument == null) { 107 argument = getArgument(build_arguments_element, index); 108 } 109 return argument; 110 } 111 112 /** Retrieve the number of arguments involved in building. */ 113 public int getBuildArgumentCount() { 114 NodeList build_options = build_arguments_element.getElementsByTagName(OPTION); 115 return build_options.getLength(); 116 } 117 118 /** Retrieve the value of a certain build argument. */ 119 public String getBuildValue(String name) { 120 return getValue(build_values_element, name, false); 121 } 122 123 /** Determine if the named argument value is enabled or disabled. */ 124 public boolean getBuildValueEnabled(String name) { 125 boolean result = false; 126 String value = getValue(build_values_element, name, true); 127 if(value != null && value.length() > 0) { 128 result = (value.equalsIgnoreCase(TRUE)); 129 } 130 return result; 131 } 132 133 /** Retrieve all of the build values as a String array ready to added to the buildcol.pl call. */ 134 public String[] getBuildValues() { 135 return getValues(build_values_element); 136 } 137 138 /** Retrieve the indexth argument belonging to import. */ 139 public Argument getImportArgument(int index) { 140 Argument argument = null; 141 // Try to find the argument in the cache. 142 SoftReference reference = (SoftReference) import_arguments_cache.get(new Integer(index)); 143 if(reference != null) { 144 argument = (Argument) reference.get(); 145 } 146 // Otherwise generate a new argument. 147 if(argument == null) { 148 argument = getArgument(import_arguments_element, index); 149 } 150 return argument; 151 } 152 153 /** Retrieve the number of arguments involved in importing. */ 154 public int getImportArgumentCount() { 155 NodeList import_options = import_arguments_element.getElementsByTagName(OPTION); 156 return import_options.getLength(); 157 } 158 159 /** Retrieve the value of a certain build argument. */ 160 public String getImportValue(String name) { 161 return getValue(import_values_element, name, false); 162 } 163 164 /** Determine if the named argument value is enabled or disabled. */ 165 public boolean getImportValueEnabled(String name) { 166 boolean result = false; 167 String value = getValue(import_values_element, name, true); 168 if(value != null && value.length() > 0) { 169 result = (value.equalsIgnoreCase(TRUE)); 170 } 171 return result; 172 } 173 174 /** Retrieve all of the import arguments in a form ready to be sent out to a shell process. */ 175 public String[] getImportValues() { 176 return getValues(import_values_element); 177 } 178 179 /** Set the value of a build argument. */ 180 public void setBuildValue(String name, boolean enable, String value) { 181 setValue(build_values_element, name, enable, value); 182 } 183 184 /** Set the value of a build argument. */ 185 public void setImportValue(String name, boolean enable, String value) { 186 setValue(import_values_element, name, enable, value); 187 } 188 189 /** Remove the given build value. */ 190 public void removeBuildValue(String name) { 191 removeValue(build_values_element, name); 192 } 193 194 /** Remove the given import value. */ 195 public void removeImportValue(String name) { 196 removeValue(import_values_element, name); 197 } 198 199 /** Retrieve the indexth element from the given set of arguments. */ 200 private Argument getArgument(Element arguments_element, int index) { 201 Argument argument = null; 202 try { 203 NodeList option_list = arguments_element.getElementsByTagName(OPTION); 204 if(0 <= index && index < option_list.getLength()) { 205 // Retrieve the appropriate argument 206 Element option = (Element) option_list.item(index); 207 // Iterate through this option elements children, making note of any details we find. 208 argument = new Argument(); 209 for(Node node = option.getFirstChild(); node != null; node = node.getNextSibling()) { 210 String node_name = node.getNodeName(); 211 if(node_name.equals("Name")) { 212 argument.setName(MSMUtils.getValue(node)); 213 } 214 else if(node_name.equals("Desc")) { 215 argument.setDesc(MSMUtils.getValue(node)); 216 } 217 else if(node_name.equals("Type")) { 218 argument.setType(MSMUtils.getValue(node)); 219 } 220 else if(node_name.equals("Default")) { 221 argument.setDefault(MSMUtils.getValue(node)); 222 } 223 else if(node_name.equals("List")) { 224 // Two final loops are required to parse lists. 225 for(Node value = node.getFirstChild(); value != null; value = value.getNextSibling()) { 226 if(value.getNodeName().equals("Value")) { 227 String key = null; 228 String desc = ""; 229 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { 230 node_name = subvalue.getNodeName(); 231 if(node_name.equals("Name")) { 232 key = MSMUtils.getValue(subvalue); 233 } 234 else if(node_name.equals("Desc")) { 235 desc = MSMUtils.getValue(subvalue); 236 } 237 } 238 if(key != null) { 239 argument.addOption(key, desc); 240 } 241 } 242 } 243 } 244 else if(node_name.equals("Required")) { 245 String v = MSMUtils.getValue(node); 246 if(v != null && v.equals("yes")) { 247 argument.setRequired(true); 248 } 249 } 250 } 251 } 252 } 253 catch (Exception error) { 254 Gatherer.println("Error in BuildOptions.getArgument(): " + error); 255 Gatherer.printStackTrace(error); 256 } 257 return argument; 258 } 259 260 private String getValue(Element arguments_element, String name, boolean is_enabled) { 261 String result = null; 262 try { 263 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT); 264 for(int i = 0; result == null && i < arguments.getLength(); i++) { 265 Element argument_element = (Element) arguments.item(i); 266 // Is this the argument we want. 267 if(argument_element.getAttribute(NAME).equalsIgnoreCase(name)) { 268 // Are we simply determining if this argument is enabled 269 if(is_enabled) { 270 result = argument_element.getAttribute(ENABLED); 271 } 272 else { 273 String argument_value = MSMUtils.getValue(argument_element); 274 if(argument_value != null) { 275 result = argument_value; 276 } 277 argument_value = null; 278 } 279 } 280 argument_element = null; 281 } 282 arguments = null; 283 } 284 catch (Exception error) { 285 Gatherer.printStackTrace(error); 286 } 287 return result; 288 } 289 290 private String[] getValues(Element arguments_element) { 291 ArrayList values = new ArrayList(); 292 try { 293 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT); 294 for(int i = 0; i < arguments.getLength(); i++) { 295 Element argument_element = (Element) arguments.item(i); 296 // Determine if this argument is enabled. 297 if(argument_element.getAttribute(ENABLED).equalsIgnoreCase(TRUE)) { 298 // First store the name of the argument prefixed with a '-' 299 values.add("-" + argument_element.getAttribute(NAME)); 300 // Now retrieve the value. 301 String argument_value = MSMUtils.getValue(argument_element); 302 // If there is a value, tokenize it by commas only. 303 if(argument_value != null && argument_value.length() > 0) { 304 values.add(argument_value); 305 } 306 argument_value = null; 307 } 308 argument_element = null; 309 } 310 arguments = null; 311 } 312 catch (Exception error) { 313 Gatherer.printStackTrace(error); 314 } 315 return ArrayTools.arrayListToStringArray(values); 316 } 317 318 private Element loadArguments(String filename) { 319 Element arguments_element = null; 320 // Run the required program. 321 /** @todo - I never finished implementing this. Have to test that the perl script handles being loaded in this way. 322 try { 323 String args[] = new String[3]; 324 args[0] = Gatherer.config.perl_path; 325 args[1] = filename; 326 args[2] = "-xml"; 241 } 242 } 243 } 244 else if(node_name.equals("Required")) { 245 String v = MSMUtils.getValue(node); 246 if(v != null && v.equals("yes")) { 247 argument.setRequired(true); 248 } 249 } 250 } 251 } 252 } 253 catch (Exception error) { 254 Gatherer.println("Error in BuildOptions.getArgument(): " + error); 255 Gatherer.printStackTrace(error); 256 } 257 return argument; 258 } 259 260 private String getValue(Element arguments_element, String name, boolean is_enabled) { 261 String result = null; 262 try { 263 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT); 264 for(int i = 0; result == null && i < arguments.getLength(); i++) { 265 Element argument_element = (Element) arguments.item(i); 266 // Is this the argument we want. 267 if(argument_element.getAttribute(NAME).equalsIgnoreCase(name)) { 268 // Are we simply determining if this argument is enabled 269 if(is_enabled) { 270 result = argument_element.getAttribute(ENABLED); 271 } 272 else { 273 String argument_value = MSMUtils.getValue(argument_element); 274 if(argument_value != null) { 275 result = argument_value; 276 } 277 argument_value = null; 278 } 279 } 280 argument_element = null; 281 } 282 arguments = null; 283 } 284 catch (Exception error) { 285 Gatherer.printStackTrace(error); 286 } 287 return result; 288 } 289 290 private String[] getValues(Element arguments_element) { 291 ArrayList values = new ArrayList(); 292 try { 293 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT); 294 for(int i = 0; i < arguments.getLength(); i++) { 295 Element argument_element = (Element) arguments.item(i); 296 // Determine if this argument is enabled. 297 if(argument_element.getAttribute(ENABLED).equalsIgnoreCase(TRUE)) { 298 // First store the name of the argument prefixed with a '-' 299 values.add("-" + argument_element.getAttribute(NAME)); 300 // Now retrieve the value. 301 String argument_value = MSMUtils.getValue(argument_element); 302 // If there is a value, tokenize it by commas only. 303 if(argument_value != null && argument_value.length() > 0) { 304 values.add(argument_value); 305 } 306 argument_value = null; 307 } 308 argument_element = null; 309 } 310 arguments = null; 311 } 312 catch (Exception error) { 313 Gatherer.printStackTrace(error); 314 } 315 return ArrayTools.arrayListToStringArray(values); 316 } 317 318 private Element loadArguments(String filename) { 319 Element arguments_element = null; 320 // Run the required program. 321 /** @todo - I never finished implementing this. Have to test that the perl script handles being loaded in this way. 322 try { 323 String args[] = new String[3]; 324 args[0] = Gatherer.config.perl_path; 325 args[1] = filename; 326 args[2] = "-xml"; 327 327 // Create the process. 328 328 Runtime runtime = Runtime.getRuntime(); … … 334 334 Document document = parser.getDocument(); 335 335 arguments_element = document.getDocumentElement(); 336 337 336 } 337 catch (Exception error) { 338 338 Gatherer.println("Error in BuildOptions.loadArguments(): " + error); 339 339 Gatherer.printStackTrace(error); 340 }341 */342 return arguments_element;343 }344 345 /** Set the state of some build or import argument. Note that value may be either a single String, and ArrayList of Strings or null. If enable is false then any existing argument for the named argument is disabled. */346 public void setValue(Element arguments_element, String name, boolean enable, String value) {347 ///ystem.err.println("Set value: " + (arguments_element == build_values_element ? "Build" : "Import") + ", " + name + ", " + enable + ", " + value);348 try {349 Document document = arguments_element.getOwnerDocument();350 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT);351 boolean found = false;352 for(int i = 0; i < arguments.getLength(); i++) {353 Element argument_element = (Element) arguments.item(i);354 // If this the argument named.355 if(argument_element.getAttribute(NAME).equalsIgnoreCase(name)) {356 found = true;357 // Set whether this argument is enabled358 argument_element.setAttribute(ENABLED, (enable ? TRUE : FALSE));359 // Now we set the value, depending or what it is.360 if(value == null) {361 // Nothing to do.362 }363 else {364 // Remove existing text nodes.365 while(argument_element.hasChildNodes()) {366 argument_element.removeChild(argument_element.getFirstChild());367 }368 argument_element.appendChild(document.createTextNode((String)value));369 }370 }371 argument_element = null;372 340 } 341 */ 342 return arguments_element; 343 } 344 345 /** Set the state of some build or import argument. Note that value may be either a single String, and ArrayList of Strings or null. If enable is false then any existing argument for the named argument is disabled. */ 346 public void setValue(Element arguments_element, String name, boolean enable, String value) { 347 ///ystem.err.println("Set value: " + (arguments_element == build_values_element ? "Build" : "Import") + ", " + name + ", " + enable + ", " + value); 348 try { 349 Document document = arguments_element.getOwnerDocument(); 350 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT); 351 boolean found = false; 352 for(int i = 0; i < arguments.getLength(); i++) { 353 Element argument_element = (Element) arguments.item(i); 354 // If this the argument named. 355 if(argument_element.getAttribute(NAME).equalsIgnoreCase(name)) { 356 found = true; 357 // Set whether this argument is enabled 358 argument_element.setAttribute(ENABLED, (enable ? TRUE : FALSE)); 359 // Now we set the value, depending or what it is. 360 if(value == null) { 361 // Nothing to do. 362 } 363 else { 364 // Remove existing text nodes. 365 while(argument_element.hasChildNodes()) { 366 argument_element.removeChild(argument_element.getFirstChild()); 367 } 368 argument_element.appendChild(document.createTextNode((String)value)); 369 } 370 } 371 argument_element = null; 372 } 373 373 // If we haven't found an instance of this argument, but should have, then add it. 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 374 if(!found && (enable || value != null)) { 375 Element argument_element = document.createElement(ARGUMENT); 376 argument_element.setAttribute(NAME, name); 377 argument_element.setAttribute(ENABLED, (enable ? TRUE : FALSE)); 378 // Now we set the value, depending or what it is. 379 if(value == null) { 380 // Nothing to do. 381 } 382 else { 383 argument_element.appendChild(document.createTextNode((String)value)); 384 } 385 arguments_element.appendChild(argument_element); 386 } 387 arguments = null; 388 document = null; 389 389 // Make sure the collection knows to save. 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 390 Gatherer.c_man.getCollection().setSaved(false); 391 } 392 catch (Exception error) { 393 Gatherer.printStackTrace(error); 394 } 395 } 396 397 /** Remove the named value from the given arguments element. */ 398 private void removeValue(Element arguments_element, String name) { 399 try { 400 NodeList arguments = arguments_element.getElementsByTagName(ARGUMENT); 401 boolean found = false; 402 for(int i = 0; !found && i < arguments.getLength(); i++) { 403 Element argument_element = (Element) arguments.item(i); 404 // Is this the argument we want. 405 if(argument_element.getAttribute(NAME).equalsIgnoreCase(name)) { 406 arguments_element.removeChild(argument_element); 407 found = true; 408 } 409 argument_element = null; 410 } 411 arguments = null; 412 } 413 catch (Exception error) { 414 Gatherer.printStackTrace(error); 415 } 416 } 417 417 } -
trunk/gli/src/org/greenstone/gatherer/collection/Collection.java
r4293 r4366 56 56 */ 57 57 public class Collection { 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 58 /** A reference to the BuildOptions. */ 59 public BuildOptions build_options; 60 /** A reference to the Collection Design Manager. */ 61 public CollectionDesignManager cdm; 62 /** A reference to the Greenstone Directory Metadata Manager. */ 63 public GDMManager gdm; 64 /** A reference to the Metadata Set Manager. */ 65 public MetadataSetManager msm; 66 /** <i>true</i> if the currently loaded collection has been saved since the last significant change, <i>false</i> otherwise. */ 67 private boolean saved = false; 68 /** The collectio configuration file for this collection. */ 69 private CollectionConfiguration collect_cfg; 70 /** The document around which this collection class is based. */ 71 private Document document; 72 /** The file the collection is in (the file may not actually exist, such in the case of a legacy collection)! */ 73 private File file; 74 /** The name of the argument element. */ 75 static final private String ARGUMENT = "Argument"; 76 /** The name of the build element. */ 77 static final private String BUILD = "Build"; 78 /** The name of the built attribute. */ 79 static final private String BUILT = "built"; 80 /** The name of the build config element. */ 81 static final private String BUILD_CONFIG = "BuildConfig"; 82 /** The name of the collection xml template. */ 83 static final private String COLLECTION_XML_TEMPLATE = "xml" + File.separator + "template.col"; 84 /** The name of the directory mappings element. */ 85 static final private String DIRECTORY_MAPPINGS = "DirectoryMappings"; 86 /** The name of the file attribute. */ 87 static final private String FILE = "file"; 88 /** The name of the import element. */ 89 static final private String IMPORT = "Import"; 90 /** The name of the imported attribute. */ 91 static final private String IMPORTED = "imported"; 92 /** The name of the mapping element. */ 93 static final private String MAPPING = "Mapping"; 94 /** The name of the name attribute. */ 95 static final private String NAME = "name"; 96 /** The name of the true value. */ 97 static final private String TRUE = "true"; 98 /** Constructor. */ 99 public Collection(File collection_xml) { 100 this.file = collection_xml; 101 // Try to load this collections details. 102 document = Utility.parse(collection_xml, false); 103 // If that fails load the default settings for a collection. 104 if(document == null) { 105 document = Utility.parse(COLLECTION_XML_TEMPLATE, true); 106 } 107 // Point the Configuration class at our gatherer config arguments. 108 Gatherer.config.setCollectionConfiguration(document); 109 // We also attempt to parse the collection configuration file. 110 collect_cfg = new CollectionConfiguration(new File(collection_xml.getParentFile(), Utility.CONFIG_DIR)); 111 // Finally create all of the child managers that are directly dependant on a collection 112 build_options = new BuildOptions(getBuildValues(), getImportValues()); 113 } 114 /** Add a special directory mapping. */ 115 public boolean addDirectoryMapping(String name, File file) { 116 boolean result = false; 117 try { 118 Element document_element = document.getDocumentElement(); 119 Element directory_mappings_element = (Element) MSMUtils.getNodeFromNamed(document_element, DIRECTORY_MAPPINGS); 120 120 // Ensure the name isn't already in use. 121 122 123 124 125 126 127 128 129 121 boolean found = false; 122 NodeList mappings = directory_mappings_element.getElementsByTagName(MAPPING); 123 for(int i = 0; !found && i < mappings.getLength(); i++) { 124 Element mapping_element = (Element) mappings.item(i); 125 if(mapping_element.getAttribute(NAME).equalsIgnoreCase(name)) { 126 found = true; 127 } 128 mapping_element = null; 129 } 130 130 // Otherwise add the mapping. 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 131 if(!found) { 132 Element mapping_element = document.createElement(MAPPING); 133 mapping_element.setAttribute(NAME, name); 134 mapping_element.setAttribute(FILE, file.toString()); 135 directory_mappings_element.appendChild(mapping_element); 136 result = true; 137 mapping_element = null; 138 } 139 mappings = null; 140 directory_mappings_element = null; 141 document_element = null; 142 saved = false; 143 } 144 catch (Exception error) { 145 Gatherer.printStackTrace(error); 146 } 147 return result; 148 } 149 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 150 /** Destructor. 151 * @see org.greenstone.gatherer.collection.CollectionModel */ 152 public void destroy() { 153 cdm.destroy(); 154 gdm.destroy(); 155 msm.destroy(); 156 Gatherer.config.setCollectionConfiguration(null); 157 cdm = null; 158 document = null; 159 gdm = null; 160 msm = null; 161 } 162 /** Determine whether this collection has been successfully built in the past. 163 * @return <i>true</i> if the collection has been built, <i>false</i> otherwise. 164 */ 165 public boolean getBuilt() { 166 return get(BUILT); 167 } 168 /** Calculates the number of documents in this collection. */ 169 public int getDocumentCount() { 170 return getCount((TreeNode)Gatherer.c_man.getRecordSet().getRoot(), false); 171 } 172 /** Retrieve the description of this collection. 173 * @return The description as a <strong>String</strong>. 174 */ 175 public String getDescription() { 176 return collect_cfg.getDescription(); 177 } 178 /** Retrieve a specific directory mapping associated with this collection. 179 * @param name The name of the mapping to retrieve as a <strong>String</strong>. 180 * @return The <strong>File</strong> this name maps to, or <i>null</i> if no such mapping. 181 */ 182 public File getDirectoryMapping(String name) { 183 File result = null; 184 try { 185 Element document_element = document.getDocumentElement(); 186 Element directory_mappings_element = (Element) MSMUtils.getNodeFromNamed(document_element, DIRECTORY_MAPPINGS); 187 187 // Ensure the name isn't already in use. 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 188 boolean found = false; 189 NodeList mappings = directory_mappings_element.getElementsByTagName(MAPPING); 190 for(int i = 0; !found && i < mappings.getLength(); i++) { 191 Element mapping_element = (Element) mappings.item(i); 192 if(mapping_element.getAttribute(NAME).equalsIgnoreCase(name)) { 193 result = new File(MSMUtils.getValue(mapping_element)); 194 found = true; 195 } 196 mapping_element = null; 197 } 198 mappings = null; 199 directory_mappings_element = null; 200 document_element = null; 201 } 202 catch(Exception error) { 203 Gatherer.printStackTrace(error); 204 } 205 return result; 206 } 207 /** Retrieve the special directory mappings associated with this collection. 208 * @return A <strong>HashMap</strong> containing mappings from names to directories. 209 */ 210 public HashMap getDirectoryMappings() { 211 HashMap special_directories = new HashMap(); 212 try { 213 Element document_element = document.getDocumentElement(); 214 Element directory_mappings_element = (Element) MSMUtils.getNodeFromNamed(document_element, DIRECTORY_MAPPINGS); 215 215 // Ensure the name isn't already in use. 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 216 boolean found = false; 217 NodeList mappings = directory_mappings_element.getElementsByTagName(MAPPING); 218 for(int i = 0; !found && i < mappings.getLength(); i++) { 219 Element mapping_element = (Element) mappings.item(i); 220 String name = mapping_element.getAttribute(NAME); 221 File file = new File(mapping_element.getAttribute(FILE)); 222 special_directories.put(name, file); 223 file = null; 224 name = null; 225 mapping_element = null; 226 } 227 mappings = null; 228 directory_mappings_element = null; 229 document_element = null; 230 } 231 catch(Exception error) { 232 Gatherer.printStackTrace(error); 233 } 234 return special_directories; 235 } 236 /** Retrieve the authors email for this collection. 237 * @return The email as a <strong>String</strong>. 238 */ 239 public String getEmail() { 240 return collect_cfg.getCreator(); 241 } 242 /** Counts the number of folders used in the current record set. */ 243 public int getFolderCount() { 244 return getCount((TreeNode)Gatherer.c_man.getRecordSet().getRoot(), true); 245 } 246 /** Determine if this collection has had an import action run upon it since the last major change. 247 * @return <i>true</i> if an import has occured, <i>false</i> otherwise. 248 */ 249 public boolean getImported() { 250 return get(IMPORTED); 251 } 252 /** Retrieve the short name for this collection. 253 * @return The name as a <strong>String</strong>. 254 */ 255 public String getName() { 256 return file.getParentFile().getName(); 257 } 258 /** Determine if this collection has been saved since the last major change. 259 * @return <i>true</i> if it has been saved recently, <i>false</i> otherwise. 260 */ 261 public boolean getSaved() { 262 return saved; 263 } 264 /** Retrieve the title of this collection. 265 * @return The title as a <strong>String</strong>. 266 */ 267 public String getTitle() { 268 return collect_cfg.getName(); 269 } 270 /** Remove a previously defined special directory mapping. 271 * @param name The name of the mapping to remove as a <strong>String</strong>. 272 * @return The <strong>File</strong> of the mapping removed. 273 */ 274 public File removeDirectoryMapping(String name) { 275 File file = null; 276 try { 277 Element document_element = document.getDocumentElement(); 278 Element directory_mappings_element = (Element) MSMUtils.getNodeFromNamed(document_element, DIRECTORY_MAPPINGS); 279 279 // Ensure the name isn't already in use. 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 280 boolean found = false; 281 NodeList mappings = directory_mappings_element.getElementsByTagName(MAPPING); 282 for(int i = 0; !found && i < mappings.getLength(); i++) { 283 Element mapping_element = (Element) mappings.item(i); 284 if(mapping_element.getAttribute(NAME).equalsIgnoreCase(name)) { 285 file = new File(MSMUtils.getValue(mapping_element)); 286 directory_mappings_element.removeChild(mapping_element); 287 found = true; 288 } 289 mapping_element = null; 290 } 291 mappings = null; 292 directory_mappings_element = null; 293 document_element = null; 294 saved = false; 295 } 296 catch(Exception error) { 297 Gatherer.printStackTrace(error); 298 } 299 return file; 300 } 301 /** Save this xml document to the given file. */ 302 public void save() { 303 Utility.export(document, file); 304 } 305 /** Set the value of built to the given value. 306 * @param value The new value for built, <i>true</i> if the collection has been built successfully, <i>false</i> otherwise. 307 */ 308 public void setBuilt(boolean value) { 309 set(BUILT, value); 310 saved = false; 311 } 312 /** Set the value of imported to the given value. 313 * @param value The new value for imported, <i>true</i> if the collection has been imported successfully, <i>false</i> otherwise. 314 */ 315 public void setImported(boolean value) { 316 set(IMPORTED, value); 317 saved = false; 318 } 319 /** Set the value of saved to the given value. 320 * @param value The new value for saved, <i>true</i> if the collection has been saved recently, <i>false</i> otherwise. 321 */ 322 public void setSaved(boolean value) { 323 saved = value; 324 } 325 /** Set the value of title to the given value. 326 * @param title The new <strong>String</strong> title. 327 */ 328 public void setTitle(String title) { 329 collect_cfg.setName(title); 330 } 331 /** Method called to return a textual representation of a class, which in this case is the collections title. 332 * @return A <strong>String</strong> containing the collections title. 333 */ 334 public String toString() { 335 return collect_cfg.getName(); 336 } 337 /** Get the value of a collection argument. */ 338 private boolean get(String name) { 339 boolean result = false; 340 try { 341 Element document_element = document.getDocumentElement(); 342 NodeList arguments = document_element.getElementsByTagName(ARGUMENT); 343 boolean found = false; 344 for(int i = 0; !found && i < arguments.getLength(); i++) { 345 Element argument_element = (Element) arguments.item(i); 346 if(argument_element.getParentNode() == document_element) { 347 if(argument_element.getAttribute(NAME).equalsIgnoreCase(BUILT)) { 348 String value = MSMUtils.getValue(argument_element); 349 if(value.equalsIgnoreCase(TRUE)) { 350 result = true; 351 } 352 found = true; 353 value = null; 354 } 355 } 356 argument_element = null; 357 } 358 arguments = null; 359 document_element = null; 360 } 361 catch (Exception error) { 362 Gatherer.printStackTrace(error); 363 } 364 return result; 365 } 366 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 367 /** Method to retrieve the current build options associated with this Collection. */ 368 private Element getBuildValues() { 369 Element build_values_element = null; 370 try { 371 Element document_element = document.getDocumentElement(); 372 Element build_config_element = (Element) MSMUtils.getNodeFromNamed(document_element, BUILD_CONFIG); 373 build_values_element = (Element) MSMUtils.getNodeFromNamed(build_config_element, BUILD); 374 build_config_element = null; 375 document_element = null; 376 } 377 catch (Exception error) { 378 Gatherer.printStackTrace(error); 379 } 380 return build_values_element; 381 } 382 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 383 /** Count either documents or folders, depending on the state of the given boolean. */ 384 private int getCount(TreeNode node, boolean folders) { 385 int count = 0; 386 File file = ((FileNode)node).getFile(); 387 if(file.isFile() && !folders) { 388 count++; 389 } 390 else if(file.isDirectory() && folders) { 391 count++; 392 } 393 for(int i = 0; i < node.getChildCount(); i++) { 394 count = count + getCount(node.getChildAt(i), folders); 395 } 396 return count; 397 } 398 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 399 /** Method to retrieve the current import options associated with this Collection. */ 400 public Element getImportValues() { 401 Element import_values_element = null; 402 try { 403 Element document_element = document.getDocumentElement(); 404 Element build_config_element = (Element) MSMUtils.getNodeFromNamed(document_element, BUILD_CONFIG); 405 import_values_element = (Element) MSMUtils.getNodeFromNamed(build_config_element, IMPORT); 406 build_config_element = null; 407 document_element = null; 408 } 409 catch (Exception error) { 410 Gatherer.printStackTrace(error); 411 } 412 return import_values_element; 413 } 414 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 415 /** Set the value of a collection argument. */ 416 private void set(String name, boolean value) { 417 try { 418 Element document_element = document.getDocumentElement(); 419 NodeList arguments = document_element.getElementsByTagName(ARGUMENT); 420 boolean found = false; 421 for(int i = 0; !found && i < arguments.getLength(); i++) { 422 Element argument_element = (Element) arguments.item(i); 423 if(argument_element.getParentNode() == document_element) { 424 if(argument_element.getAttribute(NAME).equalsIgnoreCase(BUILT)) { 425 // Strip any current value nodes. 426 while(argument_element.hasChildNodes()) { 427 argument_element.removeChild(argument_element.getFirstChild()); 428 } 429 // Append new value 430 argument_element.appendChild(document.createTextNode(value ? "true" : "false")); 431 } 432 } 433 argument_element = null; 434 } 435 arguments = null; 436 document_element = null; 437 } 438 catch (Exception error) { 439 Gatherer.printStackTrace(error); 440 } 441 } 442 442 } -
trunk/gli/src/org/greenstone/gatherer/collection/CollectionConfiguration.java
r4293 r4366 10 10 public class CollectionConfiguration { 11 11 12 13 14 15 16 17 18 19 12 private Element creator_element; 13 private Element description_element; 14 private Element maintainer_element; 15 private Element name_element; 16 private String creator; 17 private String description; 18 private String maintainer; 19 private String name; 20 20 21 22 23 24 21 public CollectionConfiguration(File file) { 22 try { 23 String filename = file.getName().toLowerCase(); 24 if(filename.endsWith(".xml")) { 25 25 26 } 27 else if(filename.endsWith(".cfg")) { 28 FileReader fr = new FileReader(file); 29 BufferedReader br = new BufferedReader(fr); 30 String command = null; 31 while((command = br.readLine()) != null) { 32 if(command.length() > 0) { 33 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat. 34 while(command.trim().endsWith("\\")) { 35 command = command.substring(0, command.lastIndexOf("\\")); 36 String next_line = br.readLine(); 37 if(next_line != null) { 38 command = command + next_line; 39 } 40 next_line = null; 41 } 42 CommandTokenizer tokenizer = new CommandTokenizer(command); 43 if(tokenizer.countTokens() >= 2) { 44 String temp = tokenizer.nextToken(); 45 if(temp.equalsIgnoreCase("creator")) { 46 creator = tokenizer.nextToken(); 47 } 48 else if(temp.equalsIgnoreCase("maintainer")) { 49 maintainer = tokenizer.nextToken(); 50 } 51 else if(temp.equalsIgnoreCase("collectionmeta")) { 52 temp = tokenizer.nextToken(); 53 if(temp.equalsIgnoreCase("collectionname")) { 54 temp = tokenizer.nextToken(); 55 if(temp.startsWith("[") && temp.endsWith("]")) { 56 String language = temp.substring(temp.indexOf("=") + 1, temp.length() - 1); 57 if(name == null || language.equalsIgnoreCase(Gatherer.dictionary.getLanguage())) { 58 name = Utility.decodeGreenstone(tokenizer.nextToken()); 59 } 60 } 61 else if(name == null) { 62 name = Utility.decodeGreenstone(temp); 63 } 64 if(name != null && name.startsWith("\"") && name.endsWith("\"")) { 65 name = name.substring(1, name.length() - 1); 66 } 26 67 } 27 else if(filename.endsWith(".cfg")) { 28 FileReader fr = new FileReader(file); 29 BufferedReader br = new BufferedReader(fr); 30 String command = null; 31 while((command = br.readLine()) != null) { 32 if(command.length() > 0) { 33 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat. 34 while(command.trim().endsWith("\\")) { 35 command = command.substring(0, command.lastIndexOf("\\")); 36 String next_line = br.readLine(); 37 if(next_line != null) { 38 command = command + next_line; 39 } 40 next_line = null; 41 } 42 CommandTokenizer tokenizer = new CommandTokenizer(command); 43 if(tokenizer.countTokens() >= 2) { 44 String temp = tokenizer.nextToken(); 45 if(temp.equalsIgnoreCase("creator")) { 46 creator = tokenizer.nextToken(); 47 } 48 else if(temp.equalsIgnoreCase("maintainer")) { 49 maintainer = tokenizer.nextToken(); 50 } 51 else if(temp.equalsIgnoreCase("collectionmeta")) { 52 temp = tokenizer.nextToken(); 53 if(temp.equalsIgnoreCase("collectionname")) { 54 temp = tokenizer.nextToken(); 55 if(temp.startsWith("[") && temp.endsWith("]")) { 56 String language = temp.substring(temp.indexOf("=") + 1, temp.length() - 1); 57 if(name == null || language.equalsIgnoreCase(Gatherer.dictionary.getLanguage())) { 58 name = Utility.decodeGreenstone(tokenizer.nextToken()); 59 } 60 } 61 else if(name == null) { 62 name = Utility.decodeGreenstone(temp); 63 } 64 if(name != null && name.startsWith("\"") && name.endsWith("\"")) { 65 name = name.substring(1, name.length() - 1); 66 } 67 } 68 else if(temp.equalsIgnoreCase("collectionextra")) { 69 temp = tokenizer.nextToken(); 70 ///ystem.err.println("Read: " + temp); 71 if(temp.startsWith("[") && temp.endsWith("]")) { 72 String language = temp.substring(temp.indexOf("=") + 1, temp.length() - 1); 73 ///ystem.err.println("Try to match " + language + " to " + Gatherer.dictionary.getLanguage()); 74 if(description == null || language.equalsIgnoreCase(Gatherer.dictionary.getLanguage())) { 75 description = Utility.decodeGreenstone(tokenizer.nextToken()); 76 ///ystem.err.println("Found language match, or first match: " + description); 77 } 78 } 79 else if(description == null) { 80 description = Utility.decodeGreenstone(temp); 81 ///ystem.err.println("Found first match: " + description); 82 } 83 if(description != null && description.startsWith("\"") && description.endsWith("\"")) { 84 description = description.substring(1, description.length() - 1); 85 } 86 } 87 } 88 temp = null; 89 } 90 tokenizer = null; 91 } 92 } 93 command = null; 94 br.close(); 95 br = null; 96 fr.close(); 97 fr = null; 68 else if(temp.equalsIgnoreCase("collectionextra")) { 69 temp = tokenizer.nextToken(); 70 ///ystem.err.println("Read: " + temp); 71 if(temp.startsWith("[") && temp.endsWith("]")) { 72 String language = temp.substring(temp.indexOf("=") + 1, temp.length() - 1); 73 ///ystem.err.println("Try to match " + language + " to " + Gatherer.dictionary.getLanguage()); 74 if(description == null || language.equalsIgnoreCase(Gatherer.dictionary.getLanguage())) { 75 description = Utility.decodeGreenstone(tokenizer.nextToken()); 76 ///ystem.err.println("Found language match, or first match: " + description); 77 } 78 } 79 else if(description == null) { 80 description = Utility.decodeGreenstone(temp); 81 ///ystem.err.println("Found first match: " + description); 82 } 83 if(description != null && description.startsWith("\"") && description.endsWith("\"")) { 84 description = description.substring(1, description.length() - 1); 85 } 98 86 } 87 } 88 temp = null; 89 } 90 tokenizer = null; 91 } 92 } 93 command = null; 94 br.close(); 95 br = null; 96 fr.close(); 97 fr = null; 98 } 99 99 ///ystem.err.println("Parsed collect.cfg"); 100 100 ///ystem.err.println("name = " + name); … … 102 102 ///ystem.err.println("maintainer = " + maintainer); 103 103 ///ystem.err.println("description = " + description); 104 105 106 107 108 109 104 } 105 catch(Exception error) { 106 Gatherer.println("Error in CollectionConfiguration.<init>(): " + error); 107 Gatherer.printStackTrace(error); 108 } 109 } 110 110 111 112 113 114 115 116 117 118 119 120 111 public String getCreator() { 112 String result = ""; 113 if(creator_element != null) { 114 result = MSMUtils.getValue(creator_element); 115 } 116 else if(creator != null) { 117 result = creator; 118 } 119 return result; 120 } 121 121 122 123 124 125 126 127 128 129 130 131 122 public String getDescription() { 123 String result = ""; 124 if(description_element != null) { 125 result = MSMUtils.getValue(description_element); 126 } 127 else if(description != null) { 128 result = description; 129 } 130 return result; 131 } 132 132 133 134 135 136 137 138 139 140 141 142 133 public String getMaintainer() { 134 String result = ""; 135 if(maintainer_element != null) { 136 result = MSMUtils.getValue(maintainer_element); 137 } 138 else if(maintainer != null) { 139 result = maintainer; 140 } 141 return result; 142 } 143 143 144 145 146 147 148 149 150 151 152 153 144 public String getName() { 145 String result = "Error"; 146 if(name_element != null) { 147 result = MSMUtils.getValue(name_element); 148 } 149 else if(name != null) { 150 result = name; 151 } 152 return result; 153 } 154 154 155 156 157 155 public void setName(String name) { 156 /** @todo */ 157 } 158 158 } -
trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java
r4351 r4366 76 76 */ 77 77 public class CollectionManager 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 78 implements GShellListener, MSMListener { 79 /** A reference to the metadata set manager. 80 * @deprecated 81 */ 82 public MetadataSetManager msm; 83 /** A reference to the undo manager. Although only one instance is shared between all collections, the undo queues are emptied between each. */ 84 public UndoManager undo; 85 /** Are we currently in the process of building? */ 86 private boolean building = false; 87 /** Are we currently in the process of importing? */ 88 private boolean importing = false; 89 /** The collection this manager is managing! */ 90 private Collection collection = null; 91 /** The collection_model. */ 92 private FileSystemModel collection_model = null; 93 /** The workspace model. This becomes invalid on a collection change. */ 94 private FileSystemModel workspace_model = null; 95 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */ 96 private FMTreeModelListener fm_tree_model_listener = null; 97 /** The monitor resposible for parsing the build process. */ 98 private GShellProgressMonitor build_monitor = null; 99 /** The monitor resposible for parsing the copy process. */ 100 private GShellProgressMonitor copy_monitor = null; 101 /** The monitor resposible for parsing the import process. */ 102 private GShellProgressMonitor import_monitor = null; 103 104 private int special_case = -1; 105 /** The name of the standard lock file. */ 106 static final public String LOCK_FILE = "gatherer.lck"; 107 static final public int NO_SPECIAL_CASE = -1; 108 static final public int SPECIAL_DLS = 0; 109 /** Constructor. */ 110 public CollectionManager() { 111 // Initialisation. 112 this.building = false; 113 this.importing = false; 114 this.collection = null; 115 this.undo = new UndoManager(); 116 } 117 /** Add a special directory mapping. 118 118 * @param name The name for this directory mapping as a <strong>String</strong>. 119 119 * @param file The directory this mapping maps to as a <strong>File</strong>. 120 120 */ 121 122 121 public void addDirectoryMapping(String name, File file) { 122 if(ready()) { 123 123 // Update the information stored in the collection 124 124 collection.addDirectoryMapping(name, file); 125 125 // Now update the tree 126 127 128 129 130 131 132 126 FileSystemModel model = (FileSystemModel) Gatherer.g_man.collection_pane.getWorkspaceTree().getModel(); 127 FileNode parent = (FileNode) model.getRoot(); 128 FileNode target = new FileNode(file, name); 129 SynchronizedTreeModelTools.insertNodeInto(model, parent, target); 130 } 131 } 132 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor. 133 133 * @see org.greenstone.gatherer.Configuration 134 134 * @see org.greenstone.gatherer.Gatherer … … 141 141 * @see org.greenstone.gatherer.util.Utility 142 142 */ 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 143 public void buildCollection() { 144 Gatherer.println("Building collection"); 145 building = true; 146 147 String args[]; 148 if(Utility.isWindows()) { 149 args = new String[4]; 150 args[0] = Gatherer.config.perl_path; 151 args[1] = "-S"; 152 args[2] = Gatherer.config.getScriptPath() + "buildcol.pl"; 153 args[3] = collection.getName(); 154 } 155 else { 156 args = new String[2]; 157 args[0] = Gatherer.config.getScriptPath() + "buildcol.pl"; 158 args[1] = collection.getName(); 159 } 160 args = ArrayTools.add(args, collection.build_options.getBuildValues()); 161 GShell shell = new GShell(args, GShell.BUILD, Message.BUILDING, this, build_monitor, GShell.GSHELL_BUILD); 162 shell.addGShellListener(Gatherer.g_man.create_pane); 163 shell.start(); 164 } 165 /** Used to determine whether the currently active collection has been built. 166 166 * @return A boolean indicating the built status of the collection. 167 167 */ 168 169 170 171 172 173 174 175 * @see org.greenstone.gatherer.Gatherer 176 * @see org.greenstone.gatherer.collection.Collection 177 * @see org.greenstone.gatherer.util.Utility 178 */ 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 168 public boolean built() { 169 if(collection != null) { 170 return collection.getBuilt(); 171 } 172 return false; 173 } 174 /** Called to close the current collection and remove its lock file. 175 * @see org.greenstone.gatherer.Gatherer 176 * @see org.greenstone.gatherer.collection.Collection 177 * @see org.greenstone.gatherer.util.Utility 178 */ 179 public void closeCollection() { 180 // Remove the lock on this file, then remove the collection. 181 File lock_file = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + LOCK_FILE); 182 lock_file.delete(); 183 collection.msm.destroy(); 184 collection = null; 185 collection_model = null; 186 workspace_model = null; 187 undo.clear(); 188 Gatherer.config.setCollectionConfiguration(null); 189 Gatherer.g_man.collectionChanged(false); 190 } 191 192 /** Method that is called whenever something has changed in the configuration of this collection. */ 193 public void configurationChanged() { 194 if(collection != null) { 195 collection.setSaved(false); 196 } 197 } 198 199 /** Used to set the current collection to the given collection. Note that this call should -always- be proceeded by a ready call, and if the colection is ready and the saved flag is unset then the user should be prompted to save. Also note that this method creates yet another GShell to run buildcol.pl. */ 200 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets) { 201 special_case = NO_SPECIAL_CASE; 202 try { 203 203 // Create a progress monitor. 204 204 ProgressMonitor progress = new ProgressMonitor(Gatherer.g_man, get("Creating_New"), "mkcol.pl", 0, 7); 205 205 // Create the new collection. 206 207 206 makeCollection(description, email, name, title); 207 progress.setProgress(1); 208 208 209 209 // ACTIVE_DIR/gimport/ 210 210 String a_dir = Utility.getCollectionDir(Gatherer.config.gsdl_path) + name + File.separator; 211 211 212 212 // Has to be done before creating greenstone directory metadata manager. 213 214 215 216 217 218 213 File gimport_dir_temp = new File(Utility.getImportDir(a_dir)+"temp.dat"); 214 File gimport_dir = gimport_dir_temp.getParentFile(); 215 gimport_dir.mkdirs(); 216 if(progress != null) { 217 progress.setNote(get("Gimport_Created")); 218 } 219 219 220 220 // ACTIVE_DIR/gcache/ 221 222 223 224 225 226 221 File gcache_dir_temp = new File(Utility.getCacheDir(a_dir)+"temp.dat"); 222 File gcache_dir = gcache_dir_temp.getParentFile(); 223 gcache_dir.mkdirs(); 224 if(progress != null) { 225 progress.setNote(get("Gcache_Created")); 226 } 227 227 228 228 // ACTIVE_DIR/log/ 229 230 231 232 233 234 229 File log_dir_temp = new File(Utility.getLogDir(a_dir)+"temp.dat"); 230 File log_dir = log_dir_temp.getParentFile(); 231 log_dir.mkdirs(); 232 if(progress != null) { 233 progress.setNote(get("Log_Created")); 234 } 235 235 236 236 progress.setProgress(2); 237 237 238 238 // Now create the collection object around the directory. 239 240 241 242 239 collection = new Collection(new File(a_dir, name + ".col")); 240 collection.msm = new MetadataSetManager(); 241 msm = collection.msm; // Legacy 242 collection.msm.load(); 243 243 // Import default metadata sets if any. 244 245 246 247 244 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) { 245 MetadataSet metadata_set = (MetadataSet) metadata_sets.get(i); 246 collection.msm.importMDS(metadata_set.getFile(), false); 247 } 248 248 249 249 // Before we create the CollectionDesignManager we have to check if we are basing it upon some other collection. 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 250 if(base_collection_directory != null) { 251 // If so our first step is to correctly generate an import profile for our collection from this base collection. 252 // Try to import any existing metadata sets for this collection. Look in base_collection_directory/metadata and import any metadata sets found. 253 File base_metadata = new File(base_collection_directory, Utility.METADATA_DIR); 254 if(base_metadata.exists()) { 255 File[] possible_metadata_sets = base_metadata.listFiles(); 256 for(int i = 0; possible_metadata_sets != null && i < possible_metadata_sets.length; i++) { 257 String filename = possible_metadata_sets[i].getName(); 258 if(filename.endsWith(".mds")) { 259 ///ystem.err.println("Found a metadata set. Importing: " + possible_metadata_sets[i].getAbsolutePath()); 260 collection.msm.importMDS(possible_metadata_sets[i], false); 261 } 262 } 263 } 264 // If no sets were imported, then create a new metadata with this new collections name. Note that if this is one of the 'big five' collections then I already know the importing mappings as well. 265 boolean skip_import_phase = false; 266 if(collection.msm.getSets().size() == 0) { 267 CollectionConfiguration col_con = new CollectionConfiguration(new File(base_collection_directory, Utility.CONFIG_DIR)); 268 // If the source collection is one of the 'big five' then we know what the metadata set is. 269 String collection_name = col_con.getName(); 270 // Demo collection - part of the DLS 271 if(collection_name.equals(Utility.COLLECTION_DEMO) || collection_name.equals(Utility.COLLECTION_DLS)) { 272 special_case = SPECIAL_DLS; 273 String demo_directory = (new File(base_collection_directory.getParentFile(), Utility.COLLECTION_DEMO_DIRECTORY)).getAbsolutePath(); 274 String dls_directory = (new File(base_collection_directory.getParentFile(), Utility.COLLECTION_DLS_DIRECTORY)).getAbsolutePath(); 275 // Add the dls.mds 276 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.DLS_MDS), false); 277 // Add the mappings for the dls (even if its not present). 278 collection.msm.profiler.addAction(dls_directory, "AZList", "dls.AZList"); 279 collection.msm.profiler.addAction(dls_directory, "Keyword", "dls.Keyword"); 280 collection.msm.profiler.addAction(dls_directory, "Language", "dls.Language"); 281 collection.msm.profiler.addAction(dls_directory, "Organization", "dls.Organization"); 282 collection.msm.profiler.addAction(dls_directory, "Subject", "dls.Subject"); 283 collection.msm.profiler.addAction(dls_directory, "Title", "dls.Title"); 284 // Add the mappings for the demo dls (even if its not present). 285 collection.msm.profiler.addAction(demo_directory, "AZList", "dls.AZList"); 286 collection.msm.profiler.addAction(demo_directory, "Keyword", "dls.Keyword"); 287 collection.msm.profiler.addAction(demo_directory, "Language", "dls.Language"); 288 collection.msm.profiler.addAction(demo_directory, "Organization", "dls.Organization"); 289 collection.msm.profiler.addAction(demo_directory, "Subject", "dls.Subject"); 290 collection.msm.profiler.addAction(demo_directory, "Title", "dls.Title"); 291 // Skip the import phase 292 skip_import_phase = true; 293 } 294 // Prompt the user so that they can choose at least one initial metadata set. We're sneaky here and just create a ncm_prompt 295 else { 296 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt(); 297 // If cancelled then they really do mean to start a collection with no metadata sets. 298 if(!ncm_prompt.isCancelled()) { 299 ArrayList initial_sets = ncm_prompt.getSets(); 300 for(int i = 0; initial_sets != null && i < initial_sets.size(); i++) { 301 MetadataSet metadata_set = (MetadataSet) initial_sets.get(i); 302 collection.msm.importMDS(metadata_set.getFile(), false); 303 metadata_set = null; 304 } 305 initial_sets = null; 306 } 307 ncm_prompt.dispose(); 308 ncm_prompt = null; 309 } 310 } 311 // Do a dry metadata import run over the entire base collection, recording profile mappings. We do this by finding the archive files, and then iterating over them using the GreenstoneArchiveParser to retrieve metadata from them. We then process the importing of new metadata elements using the selectElement prompt used in a file action metadata import. However the big change is that we don't actually import any metadata, just create importing profiles. 312 File base_archives = new File(base_collection_directory, Utility.ARCHIVE_DIR); 313 if(!skip_import_phase && base_archives.exists()) { 314 ///ystem.err.println("Found archives directory."); 315 } 316 // Failing that we do a dummy run over the files in the collection. 317 else if(!skip_import_phase) { 318 ///ystem.err.println("No archives directory. Searching files for metadata.xml information."); 319 // Find the import directory 320 File base_import = new File(base_collection_directory, Utility.IMPORT_DIR); 321 if(!base_import.exists()) { 322 base_import = new File(base_collection_directory, Utility.OLD_IMPORT_DIR); 323 } 324 if(base_import.exists()) { 325 searchForMetadata(base_import); 326 } 327 // And if that fails then we must have been asked by Satan himself to build the very digital collections of hell, because they don't match any goodly greenstone collection I have ever seen, so you can't blame me if I can't import them. 328 } 329 // Now we update our collect.cfg 330 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_DIR), new File(a_dir, Utility.CONFIG_DIR), description, email, title); 331 } 332 333 collection.cdm = new CollectionDesignManager(); 334 collection.gdm = new GDMManager(); 335 336 progress.setProgress(3); 337 337 338 338 // Has to be done after creating metadata set manager. 339 340 341 342 343 344 345 346 347 339 File gmeta_dir_temp = new File(getCollectionMetadata()+"temp.dat"); 340 File gmeta_dir = gmeta_dir_temp.getParentFile(); 341 gmeta_dir.mkdirs(); 342 if(progress != null) { 343 progress.setNote("GMeta created"); 344 } 345 progress.setProgress(4); 346 347 progress.setProgress(6); 348 348 // Register ourselves as being interested in what the msm has to say. 349 349 collection.msm.addMSMListener(this); 350 350 // Create a lock file. 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 351 File lock_file = new File(a_dir, LOCK_FILE); 352 FileOutputStream out = new FileOutputStream(lock_file); 353 out.write(LOCK_FILE.getBytes()); 354 out.close(); 355 out = null; 356 progress.setProgress(7); 357 String args[] = new String[1]; 358 args[0] = name; 359 progress.setNote(get("Session_Ready", args)); 360 progress.close(); 361 } 362 catch (Exception error) { 363 Gatherer.printStackTrace(error); 364 } 365 // Done. 366 if(Gatherer.g_man != null) { 367 workspace_model = null; 368 Gatherer.g_man.collectionChanged(ready()); 369 } 370 } 371 372 public void createLockFile(File destination) { 373 try { 374 Document default_lockfile = Utility.parse("xml" + File.separator + LOCK_FILE, true); 375 String user_name = System.getProperty("user.name"); 376 Element person_element = (Element) MSMUtils.getNodeFromNamed(default_lockfile.getDocumentElement(), "User"); 377 person_element.appendChild(default_lockfile.createTextNode(user_name)); 378 person_element = null; 379 user_name = null; 380 String machine_name = Utility.getMachineName(); 381 Element machine_element = (Element) MSMUtils.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine"); 382 machine_element.appendChild(default_lockfile.createTextNode(machine_name)); 383 machine_element = null; 384 machine_name = null; 385 String date_time = Utility.getDateString(); 386 Element date_element = (Element) MSMUtils.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date"); 387 date_element.appendChild(default_lockfile.createTextNode(date_time)); 388 date_element = null; 389 date_time = null; 390 Utility.export(default_lockfile, destination); 391 } 392 catch (Exception error) { 393 Gatherer.printStackTrace(error); 394 } 395 } 396 397 /** Method that is called whenever an element within a set is changed or modified. We want to mark the collection so that it needs saving again. 398 398 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 399 399 * @see org.greenstone.gatherer.collection.Collection 400 400 */ 401 402 403 404 405 401 public void elementChanged(MSMEvent event) { 402 // This means the state of the collections has changed, so we should set saved to false. 403 collection.setSaved(false); 404 } 405 /** Used to retrieve the build options associated with the currently loaded collection. If none yet exist, default ones are created. 406 406 * @return A <strong>BuildOptions</strong> object containing the build options for the current collection. 407 407 * @see org.greenstone.gatherer.collection.Collection 408 408 */ 409 410 411 412 413 409 public BuildOptions getBuildOptions() { 410 return collection.build_options; 411 } 412 413 /** Retrieve the current collection. 414 414 * @return The <strong>Collection</strong> itself. 415 415 */ 416 417 418 419 416 public Collection getCollection() { 417 return collection; 418 } 419 /** Constructs the absolute filename of the collection archive directory, which should resemble "$GSDLHOME/collect/<col_name>/archive/" 420 420 * @return A <strong>String</strong> containing the filename. 421 421 * @see org.greenstone.gatherer.Configuration … … 424 424 * @see org.greenstone.gatherer.util.Utility 425 425 */ 426 427 428 429 426 public String getCollectionArchive() { 427 return Utility.getArchiveDir(Gatherer.config.gsdl_path, collection.getName()); 428 } 429 /** Constructs the absolute filename of the collection building directory, which should resemble "$GSDLHOME/collect/<col_name>/building/" 430 430 * @return A <strong>String</strong> containing the filename. 431 431 * @see org.greenstone.gatherer.Configuration … … 434 434 * @see org.greenstone.gatherer.util.Utility 435 435 */ 436 437 438 439 436 public String getCollectionBuild() { 437 return Utility.getBuildDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 438 } 439 /** Constructs the absolute filename of the collection cache directory, which should resemble "$GSDLHOME/collect/<col_name>/gcache/" 440 440 * @return A <strong>String</strong> containing the filename. 441 441 * @see org.greenstone.gatherer.Configuration … … 444 444 * @see org.greenstone.gatherer.util.Utility 445 445 */ 446 447 448 449 446 public String getCollectionCache() { 447 return Utility.getCacheDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 448 } 449 /** Constructs the absolute filename of the collection config file, which should resemble "$GSDLHOME/collect/<col_name>/etc/collect.cfg" 450 450 * @return A <strong>String</strong> containing the filename. 451 451 * @see org.greenstone.gatherer.Configuration … … 454 454 * @see org.greenstone.gatherer.util.Utility 455 455 */ 456 457 458 459 460 456 public String getCollectionConfig() { 457 return Utility.getConfigDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 458 } 459 460 /** Constructs the absolute filename of the collection directory, which should resemble "$GSDLHOME/collect/<col_name>" 461 461 * @return A <strong>String</strong> containing the directory name. 462 462 * @see org.greenstone.gatherer.Configuration … … 465 465 * @see org.greenstone.gatherer.util.Utility 466 466 */ 467 468 469 470 471 467 public String getCollectionDirectory() { 468 return Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator; 469 } 470 471 /** Constructs the absolute filename of the collection etc directory, which should resemble "$GSDLHOME/collect/<col_name>/etc/" 472 472 * @return A <strong>String</strong> containing the filename. 473 473 * @see org.greenstone.gatherer.Configuration … … 476 476 * @see org.greenstone.gatherer.util.Utility 477 477 */ 478 479 480 481 478 public String getCollectionEtc() { 479 return Utility.getEtcDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 480 } 481 /** Constructs the absolute filename of the collection file, which should resemble "$GSDLHOME/collect/<col_name>/<col_name>.col" 482 482 * @return A <strong>String</strong> containing the filename. 483 483 * @see org.greenstone.gatherer.Configuration … … 486 486 * @see org.greenstone.gatherer.util.Utility 487 487 */ 488 489 490 491 488 public String getCollectionFilename() { 489 return Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + collection.getName() + ".col"; 490 } 491 /** Constructs the absolute filename of the collection import directory, which should resemble "$GSDLHOME/collect/<col_name>/gimport/" 492 492 * @return A <strong>String</strong> containing the filename. 493 493 * @see org.greenstone.gatherer.Configuration … … 496 496 * @see org.greenstone.gatherer.util.Utility 497 497 */ 498 499 500 501 498 public String getCollectionImport() { 499 return Utility.getImportDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 500 } 501 /** Constructs the absolute filename of the collection index directory, which should resemble "$GSDLHOME/collect/<col_name>/index/" 502 502 * @return A <strong>String</strong> containing the filename. 503 503 * @see org.greenstone.gatherer.Configuration … … 506 506 * @see org.greenstone.gatherer.util.Utility 507 507 */ 508 509 510 511 508 public String getCollectionIndex() { 509 return Utility.getIndexDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 510 } 511 /** Constructs the absolute filename of the collection log directory, which should resemble "$GSDLHOME/collect/<col_name>/log/" 512 512 * @return A <strong>String</strong> containing the filename. 513 513 * @see org.greenstone.gatherer.Configuration … … 516 516 * @see org.greenstone.gatherer.util.Utility 517 517 */ 518 519 520 521 518 public String getCollectionLog() { 519 return Utility.getLogDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 520 } 521 /** Constructs the absolute filename of the collection metadata directory, which should resemble "$GSDLHOME/collect/<col_name>/metadata/" 522 522 * @return A <strong>String</strong> containing the filename. 523 523 * @see org.greenstone.gatherer.Configuration … … 526 526 * @see org.greenstone.gatherer.util.Utility 527 527 */ 528 529 530 531 532 533 534 535 536 528 public String getCollectionMetadata() { 529 return Utility.getMetadataDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 530 } 531 532 public String getCollectionOldImport() { 533 return Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + Utility.OLD_IMPORT_DIR; 534 } 535 536 /** This method either returns the title of the current collection, or a placeholder string of 'No Collection'. 537 537 * @return A <strong>String</strong> which represents what we wish to display for a collection title. 538 538 * @see org.greenstone.gatherer.collection.Collection 539 539 */ 540 541 542 543 544 545 546 547 548 549 540 public String getCollectionTitle() { 541 if(collection != null) { 542 return collection.getTitle(); 543 } 544 return get("Collection.No_Collection"); 545 } 546 547 /** Retrieve the record set (tree model) associated with the current collection. */ 548 public TreeModel getRecordSet() { 549 if(collection_model == null && collection != null) { 550 550 // Check if the gimport directory exists, and if so use it. 551 552 553 554 555 556 557 558 559 560 561 551 File gimport_directory = new File(getCollectionImport()); 552 if(gimport_directory.exists()) { 553 // Generate a new FileSystemModel based on the collection gimport directory. 554 collection_model = new FileSystemModel(new FileNode(gimport_directory, false)); 555 } 556 else { 557 ///ystem.err.println("No gimport directory exists."); 558 // Otherwise default to the import directory. If it doesn't exist, there aint much more I can do. 559 // Generate a new FileSystemModel based on the collection import directory. 560 collection_model = new FileSystemModel(new FileNode(new File(getCollectionOldImport()), false)); 561 } 562 562 // Ensure that the manager is a change listener for the tree. 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 563 if(fm_tree_model_listener == null) { 564 fm_tree_model_listener = new FMTreeModelListener(); 565 } 566 collection_model.addTreeModelListener(fm_tree_model_listener); 567 } 568 return collection_model; 569 } 570 /** Create and return the model behind the workspace tree. Quite an extensive method, as it must first map known greenstone collections, then the local file system and finally any public or private download workspaces. */ 571 public TreeModel getWorkspace() { 572 if(workspace_model != null) { 573 return workspace_model; 574 } 575 // Create the workspace tree. 576 FileNode workspace_root = new FileNode("ABS_ROOT"); 577 workspace_model = new FileSystemModel(workspace_root); 578 // Create and add Greenstone collections node. 579 // Starting at the collection directory of gsdl... 580 File start = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path)); 581 FileNode world_root = new FileNode(get("Tree.World")); 582 workspace_root.insert(world_root); 583 // For each of the children directories, which are collections... 584 File cols[] = start.listFiles(); 585 ArrayTools.sort(cols); 586 // We add their import and gimport directories, but only if its not our current collection. 587 for(int i = 0; cols != null && i < cols.length; i++) { 588 if(collection == null || !(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName()).equals(cols[i].getAbsolutePath())) { 589 File dirs[] = cols[i].listFiles(); 590 ArrayTools.sort(dirs); 591 File import_dir = new File(cols[i], "gimport"); 592 if(!import_dir.exists()) { 593 import_dir = new File(cols[i], "import"); 594 } 595 if(import_dir.exists()) { 596 FileNode collection_root = new FileNode(import_dir, cols[i].getName(), true); 597 world_root.insert(collection_root); 598 collection_root = null; 599 } 600 import_dir = null; 601 } 602 } 603 // Create Local File space. 604 // Get all the available roots mounted on the system. 605 File roots[] = File.listRoots(); 606 // If there is just one root use it as the tree root (linux) 607 if(roots != null) { 608 FileNode file_root; 609 String name = get("Tree.Root"); 610 if(roots.length == 1) { 611 file_root = new FileNode(roots[0], name); 612 workspace_root.insert(file_root); 613 } 614 614 // Otherwise build a dummy node which has these nodes as 615 615 // children. 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 616 else { 617 file_root = new FileNode(name); 618 workspace_root.insert(file_root); 619 // Hopefully this does an alphabetic sort. 620 ArrayTools.sort(roots); 621 for(int i = 0; i < roots.length; i++) { 622 FileNode child_root = new FileNode(roots[i]); 623 file_root.insert(child_root); 624 child_root = null; 625 } 626 } 627 name = null; 628 file_root = null; 629 } 630 // If mirroring is enabled show the public and private caches. 631 if(Gatherer.config.get("workflow.mirror", false)) { 632 632 // Add Public workspace 633 634 633 FileNode public_root = new FileNode(new File(Utility.CACHE_DIR), get("Tree.Public")); 634 workspace_root.insert(public_root); 635 635 // Add Private workspace if a collection has been loaded. 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 636 if(ready()) { 637 FileNode private_root = new FileNode(new File(getCollectionCache()), get("Tree.Private")); 638 workspace_root.insert(private_root); 639 } 640 } 641 // Finally we retrieve and map any predefined special directories. 642 if(ready()) { 643 HashMap mappings = collection.getDirectoryMappings(); 644 for(Iterator names = mappings.keySet().iterator(); names.hasNext(); ) { 645 String name = (String) names.next(); 646 File file = (File) mappings.get(name); 647 FileNode special_root = new FileNode(file, name); 648 //workspace_root.insert(special_root); 649 SynchronizedTreeModelTools.insertNodeInto(workspace_model, workspace_root, special_root); 650 } 651 } 652 return workspace_model; 653 } 654 /** This method when called, creates a new GShell in order to run the import.pl script. 655 655 * @see org.greenstone.gatherer.Configuration 656 656 * @see org.greenstone.gatherer.Gatherer … … 662 662 * @see org.greenstone.gatherer.util.Utility 663 663 */ 664 665 664 public void importCollection() { 665 if(!saved()) { 666 666 // Force save. 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 667 try { 668 SaveCollectionTask save_task = new SaveCollectionTask(collection); 669 save_task.setImportAfter(true); 670 save_task.start(); 671 } 672 catch(Exception error) { 673 Gatherer.printStackTrace(error); 674 } 675 } 676 else { 677 importing = true; 678 String args[]; 679 if(Utility.isWindows()) { 680 args = new String[6]; 681 args[0] = Gatherer.config.perl_path; 682 args[1] = "-S"; 683 args[2] = Gatherer.config.getScriptPath() + "import.pl"; 684 args[3] = "-importdir"; 685 args[4] = getCollectionImport(); 686 args[5] = collection.getName(); 687 } 688 else { 689 args = new String[4]; 690 args[0] = Gatherer.config.getScriptPath() + "import.pl"; 691 args[1] = "-importdir"; 692 args[2] = getCollectionImport(); 693 args[3] = collection.getName(); 694 } 695 args = ArrayTools.add(args, collection.build_options.getImportValues()); 696 GShell shell = new GShell(args, GShell.IMPORT, Message.BUILDING, this, import_monitor, GShell.GSHELL_IMPORT); 697 shell.addGShellListener(Gatherer.g_man.create_pane); 698 shell.start(); 699 } 700 } 701 /** Attempts to load the given collection. Currently uses simple serialization of the collection class. 702 702 * @param location The path to the collection as a <strong>String</strong>. 703 703 * @see org.greenstone.gatherer.Configuration … … 709 709 * @see org.greenstone.gatherer.util.Utility 710 710 */ 711 712 713 714 715 716 717 718 711 public boolean loadCollection(String location) { 712 ///ystem.err.println("Load Collection '" + location + "'"); 713 String[] args2 = new String[1]; 714 args2[0] = location; 715 boolean result = false; 716 // Check we have actually been given a .col file. 717 if(location.endsWith(".col")) { 718 File collection_file = new File(location); 719 719 // Ensure that the directory exists. 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 720 File collection_directory = collection_file.getParentFile(); 721 if(collection_directory.exists()) { 722 String name = collection_directory.getName(); 723 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE); 724 // Now determine if a lock already exists on this collection. 725 int choice = LockFileDialog.YES_OPTION; 726 if(lock_file.exists()) { 727 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, name, lock_file); 728 choice = dialog.getChoice(); 729 } 730 if(choice == LockFileDialog.YES_OPTION) { 731 try { 732 if(lock_file.exists()) { 733 lock_file.delete(); 734 } 735 // Create a lock file. 736 createLockFile(lock_file); 737 // Open the collection file 738 collection = new Collection(collection_file); 739 collection.msm = new MetadataSetManager(); 740 msm = collection.msm; // Legacy 741 collection.msm.load(); 742 collection.cdm = new CollectionDesignManager(); 743 collection.gdm = new GDMManager(); 744 // Tell everyone that it worked. 745 Gatherer.println(get("Loading_Successful", name)); 746 // Now we need to hook up classes that depend on messages from the metadata set manager to keep their content fresh. 747 collection.msm.addMSMListener(this); 748 // We're done. Let everyone know. 749 if(Gatherer.g_man != null) { 750 workspace_model = null; 751 Gatherer.g_man.collectionChanged(ready()); 752 } 753 result = true; 754 } catch (Exception error) { 755 // There is obviously no existing collection present. 756 Gatherer.printStackTrace(error); 757 JOptionPane.showMessageDialog(Gatherer.g_man, get("Cannot_Open", args2), get("General.Error"), JOptionPane.ERROR_MESSAGE); 758 } 759 } 760 lock_file = null; 761 } 762 else { 763 JOptionPane.showMessageDialog(Gatherer.g_man, get("CollectionManager.File_Not_Found", args2), get("General.Error"), JOptionPane.ERROR_MESSAGE); 764 } 765 collection_directory = null; 766 } 767 else { 768 JOptionPane.showMessageDialog(Gatherer.g_man, get("Not_Col_File", args2), get("General.Error"), JOptionPane.ERROR_MESSAGE); 769 Gatherer.println("Not a Gatherer Collection."); 770 } 771 args2 = null; 772 return result; 773 } 774 775 public void makeCollection(String description, String email, String name, String title) { 776 // Run the mkcol command. 777 String command[]; 778 if(Utility.isWindows()) { 779 if(description == null || email == null || title == null) { 780 command = new String[4]; 781 } 782 else { 783 command = new String[10]; 784 } 785 command[0] = Gatherer.config.perl_path; 786 command[1] = "-S"; 787 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl"; 788 if(description == null || email == null || title == null) { 789 command[3] = name; 790 } 791 else { 792 command[3] = "-title"; 793 command[4] = title; 794 command[5] = "-creator"; 795 command[6] = email; 796 command[7] = "-about"; 797 command[8] = description; 798 command[9] = name; 799 } 800 } 801 else { 802 if(description == null || email == null || title == null) { 803 command = new String[2]; 804 command[0] = "mkcol.pl"; 805 command[1] = name; 806 } 807 else { 808 command = new String[8]; 809 command[0] = "mkcol.pl"; 810 command[1] = "-title"; 811 command[2] = title; 812 command[3] = "-creator"; 813 command[4] = email; 814 command[5] = "-about"; 815 command[6] = description; 816 command[7] = name; 817 } 818 } 819 GShell process = new GShell(command, GShell.NEW, Message.COLLECT, this, null, GShell.GSHELL_NEW); 820 process.addGShellListener(this); 821 process.run(); // Don't bother threading this... yet 822 } 823 824 /** Any implementation of GShellListener must include this method to allow the GShell to send messages to listeners. However in this case the CollectionManager is in no way interested in what the messages are, just the import events which have a specific type and are handled elsewhere. Thus we can safely ignore this event. 825 825 * @param event A <strong>GShellEvent</strong> which contains a the message. 826 826 */ 827 828 829 827 public synchronized void message(GShellEvent event) { 828 } 829 /** Called whenever the metadata value changes in some way, such as the addition of a new value. We want to mark the collection so that it needs saving again. 830 830 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 831 831 * @see org.greenstone.gatherer.collection.Collection 832 832 */ 833 834 835 836 837 833 public void metadataChanged(MSMEvent event) { 834 // Again this change means we need to save the collection again. 835 collection.setSaved(false); 836 } 837 /** This call is fired whenever a process within a GShell created by this class begins. 838 838 * @param event A <strong>GShellEvent</strong> containing information about the GShell process. 839 839 * @see org.greenstone.gatherer.Gatherer … … 841 841 * @see org.greenstone.gatherer.shell.GShell 842 842 */ 843 844 845 846 847 848 843 public synchronized void processBegun(GShellEvent event) { 844 ///ystem.err.println("ProcessBegun " + event.getType()); 845 // If this is one of the types where we wish to lock user control 846 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true); 847 } 848 /** This call is fired whenever a process within a GShell created by this class ends. 849 849 * @param event A <strong>GShellEvent</strong> containing information about the GShell process. 850 850 * @see org.greenstone.gatherer.Gatherer … … 852 852 * @see org.greenstone.gatherer.shell.GShell 853 853 */ 854 855 856 857 858 859 854 public synchronized void processComplete(GShellEvent event) { 855 ///ystem.err.println("ProcessComplete " + event.getType()); 856 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false); 857 ///ystem.err.println("Recieved process complete event - " + event); 858 // If we were running an import, now run a build. 859 if(event.getType() == GShell.IMPORT && event.getStatus() != GShell.ERROR) { 860 860 // Finish import. 861 862 863 864 865 861 collection.setImported(true); 862 buildCollection(); 863 } 864 // If we were running a build, now is when we move files across. 865 else if(event.getType() == GShell.BUILD && event.getStatus() != GShell.ERROR) { 866 866 ///ystem.err.println("Installing collection."); 867 868 867 installCollection(); 868 collection.setBuilt(true); 869 869 // If we have a local library running (that we know about) then we ask it to add our newly create collection 870 870 ///ystem.err.println("Check if we should reset local server."); 871 872 873 874 871 if(Gatherer.config.exec_file != null) { 872 ///ystem.err.println("Local Library Found!"); 873 Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.ADD_COMMAND + collection.getName()); 874 } 875 875 //else { 876 876 ///ystem.err.println("GLI can't recognize a local library."); 877 877 //} 878 878 // Signal collection changed. 879 880 881 882 883 884 885 886 887 879 workspace_model = null; 880 Gatherer.g_man.collectionChanged(ready()); 881 JOptionPane.showMessageDialog(Gatherer.g_man, get("Preview_Ready"), get("Preview_Ready_Title"), JOptionPane.INFORMATION_MESSAGE); 882 } 883 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.ERROR) { 884 JOptionPane.showMessageDialog(Gatherer.g_man, get("Preview_Ready_Failed"), get("Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE); 885 } 886 } 887 /** Determine if the manager is ready for actions apon its collection. 888 888 * @return A <i>boolean</i> which is <i>true</i> to indicate a collection has been loaded and thus the collection is ready for editing, <i>false</i> otherwise. 889 889 */ 890 891 892 893 894 895 896 897 * @see org.greenstone.gatherer.collection.Collection 898 */ 899 900 901 890 public boolean ready() { 891 if(collection != null) { 892 return true; 893 } 894 return false; 895 } 896 /** Called to refresh the models upon which the trees are based. 897 * @see org.greenstone.gatherer.collection.Collection 898 */ 899 public void refreshTrees() { 900 } 901 /** This method associates the collection build monitor with the build monitor created in CreatePane. 902 902 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor. 903 903 */ 904 905 906 907 904 public void registerBuildMonitor(GShellProgressMonitor monitor) { 905 build_monitor = monitor; 906 } 907 /** This method associates the collection copy monitor with the copy monitor created in CreatePane. 908 908 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the copy monitor. 909 909 */ 910 911 912 913 910 public void registerCopyMonitor(GShellProgressMonitor monitor) { 911 copy_monitor = monitor; 912 } 913 /** This method associates the collection import monitor with the import monitor created in CreatePane. 914 914 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor. 915 915 */ 916 917 918 919 916 public void registerImportMonitor(GShellProgressMonitor monitor) { 917 import_monitor = monitor; 918 } 919 /** Remove a previously assigned special directory mapping. 920 920 * @param name The symbolic name of the special directory mapping to remove as a <strong>String</strong>. 921 921 * @return The <strong>File</strong> of the mapping removed. 922 922 */ 923 924 925 923 public File removeDirectoryMapping(FileNode target) { 924 File file = null; 925 if(ready()) { 926 926 // Remove from collection, remembering file 927 927 file = collection.removeDirectoryMapping(target.toString()); 928 928 // Update tree. 929 930 931 932 933 934 929 FileSystemModel model = (FileSystemModel) Gatherer.g_man.collection_pane.getWorkspaceTree().getModel(); 930 SynchronizedTreeModelTools.removeNodeFromParent(model, target); 931 } 932 return file; 933 } 934 /** Used to check whether all open collections have a 'saved' state. 935 935 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved. 936 936 * @see org.greenstone.gatherer.collection.Collection 937 937 */ 938 939 940 941 942 943 944 945 938 public boolean saved() { 939 boolean result = true; 940 if(collection != null) { 941 result = collection.getSaved(); 942 } 943 return result; 944 } 945 /** Saves a collection by serializing it to file. 946 946 * @param close_after <i>true</i> to cause the Gatherer to close the collection once save is complete, <i>false</i> otherwise. 947 947 * @param exit_after <i>true</i> to cause the Gatherer to exit once save is complete, <i>false</i> otherwise. … … 952 952 * @see org.greenstone.gatherer.collection.Collection 953 953 */ 954 955 956 957 958 959 960 961 962 963 954 public void saveCollection(boolean close_after, boolean exit_after) { 955 try { 956 SaveCollectionTask save_task = new SaveCollectionTask(collection, close_after, exit_after); 957 save_task.start(); 958 } 959 catch(Exception error) { 960 Gatherer.printStackTrace(error); 961 } 962 } 963 /** Saves the current collection to a new filename, then restores the original collection. Finally opens the collection copy. 964 964 * @param name The name collection name. 965 965 */ 966 967 968 969 970 971 972 973 974 975 976 977 966 public void saveCollectionAs(String name) { 967 // We need to do this in a separate thread so create a SaveCollectionAsTask 968 try { 969 SaveCollectionTask save_task = new SaveCollectionTask(collection, name); 970 save_task.start(); 971 } 972 catch(Exception error) { 973 Gatherer.printStackTrace(error); 974 } 975 } 976 977 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. We want to mark the collection so that it needs saving again. 978 978 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 979 979 * @see org.greenstone.gatherer.collection.Collection 980 980 */ 981 982 983 984 985 986 987 988 989 990 981 public void setChanged(MSMEvent event) { 982 // Invalidate saved 983 collection.setSaved(false); 984 } 985 986 /** Updates the given workspace tree model to reference the private cache of the currently loaded collection. */ 987 public void updatePrivateWorkspace(DefaultTreeModel model) { 988 // Add Private workspace if a collection has been loaded. 989 if(ready() && !Gatherer.config.get("workflow.mirror", true)) { 990 FileNode root = (FileNode)model.getRoot(); 991 991 // Remove old private workspace 992 993 992 FileNode old = (FileNode)model.getChild(root, 2); 993 model.removeNodeFromParent(old); 994 994 // Create and insert new. 995 996 997 998 999 995 FileNode private_workspace = new FileNode(new File(getCollectionCache()), get("Tree.Private")); 996 model.insertNodeInto(private_workspace, root, 2); 997 } 998 } 999 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. We want to mark the collection so that it needs saving again. 1000 1000 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 1001 1001 * @see org.greenstone.gatherer.collection.Collection 1002 1002 */ 1003 1004 1005 1006 1003 public void valueChanged(MSMEvent event) { 1004 collection.setSaved(false); 1005 } 1006 /** Used to retrive a value from the dictionary based on the key. 1007 1007 * @param key A <strong>String</strong> indicating what value to retrieve. 1008 1008 * @return A <strong>String</strong> representing the value. 1009 1009 */ 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1010 private String get(String key) { 1011 return get(key, (String[])null); 1012 } 1013 1014 /** Used to retrive a value from the dictionary based on the key. */ 1015 private String get(String key, String arg) { 1016 String[] args = new String[1]; 1017 args[0] = arg; 1018 return get(key, args); 1019 } 1020 1021 /** Used to retrive a value from the dictionary based on the key, and an array of arguments. 1022 1022 * @param key A <strong>String</strong> indicating what value to retrieve. 1023 1023 * @param args A <strong>String[]</strong> of arguments to be inserted into the phrase. 1024 1024 * @return A <strong>String</strong> representing the value. 1025 1025 */ 1026 1027 1028 1029 1030 1031 1032 1033 * @see org.greenstone.gatherer.Gatherer 1034 * @see org.greenstone.gatherer.util.Utility 1035 */ 1036 1037 1026 private String get(String key, String args[]) { 1027 if(key.indexOf('.') == -1) { 1028 key = "CollectionManager." + key; 1029 } 1030 return Gatherer.dictionary.get(key, args); 1031 } 1032 /** Install collection by moving its files from building to index after a successful build. 1033 * @see org.greenstone.gatherer.Gatherer 1034 * @see org.greenstone.gatherer.util.Utility 1035 */ 1036 private void installCollection() { 1037 Gatherer.println("Build complete. Moving files."); 1038 1038 1039 1039 try { 1040 1040 // We have to ensure that the local library 1041 1042 1043 1044 1041 if(Gatherer.config.exec_file != null) { 1042 ///ystem.err.println("Local Library Found!"); 1043 Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName()); 1044 } 1045 1045 1046 1047 1048 1046 File index_dir = new File(getCollectionIndex(), "temp.txt"); 1047 index_dir = index_dir.getParentFile(); 1048 Gatherer.println("Index = " + index_dir.getAbsolutePath()); 1049 1049 1050 1051 1052 1053 1054 1055 1056 1050 if(index_dir.exists()) { 1051 Utility.delete(index_dir); 1052 } 1053 1054 if(index_dir.exists()) { 1055 throw(new Exception("Index directory cannot be removed.")); 1056 } 1057 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1058 File build_dir = new File(getCollectionBuild(), "temp.txt"); 1059 build_dir = build_dir.getParentFile(); 1060 Gatherer.println("Build = " + build_dir.getAbsolutePath()); 1061 build_dir.renameTo(index_dir); 1062 1063 File new_build = new File(getCollectionBuild(), "temp.txt"); 1064 new_build = new_build.getParentFile(); 1065 1066 if(new_build.exists()) { 1067 throw(new Exception("Build directory cannot be moved.")); 1068 } 1069 1070 new_build.mkdir(); 1071 } 1072 catch (Exception exception) { 1073 JOptionPane.showMessageDialog(Gatherer.g_man, "Exception detected during collection install.\nMost likely caused by Windows or Local Library holding locks on files:\n" + exception.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 1074 } 1075 } 1076 /** Creates and dispatches a message given the initial details. 1077 1077 * @param level An <i>int</i> indicating the message level for this message. 1078 1078 * @param message A <strong>String</strong> which contains the payload of this message. … … 1080 1080 * @see org.greenstone.gatherer.Message 1081 1081 */ 1082 private void message(int level, String message) { 1083 Message msg = new Message(Message.COLLECT, level, message); 1084 if(Gatherer.g_man != null) { 1085 Gatherer.log.add(msg); 1086 } else { 1087 Gatherer.println(msg.toString()); 1088 } 1089 } 1090 1091 private boolean searchArchivesForMetadata(File archive_directory) { 1092 /** @todo - ensure GreenstoneArchiveParser works as expected. */ 1093 return true; 1094 } 1095 1096 private boolean searchForMetadata(File current_file) { 1097 boolean cancelled = false; 1098 if(current_file.isFile() && current_file.getName().equals(Utility.METADATA_XML)) { 1099 cancelled = collection.msm.searchForMetadata(null, new FileNode(current_file), false, true); // A dummy run only. 1100 } 1101 else { 1102 File[] children_files = current_file.listFiles(); 1103 for(int i = 0; !cancelled && children_files != null && i < children_files.length; i++) { 1104 cancelled = searchForMetadata(children_files[i]); 1082 private void message(int level, String message) { 1083 Message msg = new Message(Message.COLLECT, level, message); 1084 if(Gatherer.g_man != null) { 1085 Gatherer.log.add(msg); 1086 } else { 1087 Gatherer.println(msg.toString()); 1088 } 1089 } 1090 1091 private boolean searchArchivesForMetadata(File archive_directory) { 1092 /** @todo - ensure GreenstoneArchiveParser works as expected. */ 1093 return true; 1094 } 1095 1096 private boolean searchForMetadata(File current_file) { 1097 boolean cancelled = false; 1098 if(current_file.isFile() && current_file.getName().equals(Utility.METADATA_XML)) { 1099 cancelled = collection.msm.searchForMetadata(null, new FileNode(current_file), false, true); // A dummy run only. 1100 } 1101 else { 1102 File[] children_files = current_file.listFiles(); 1103 for(int i = 0; !cancelled && children_files != null && i < children_files.length; i++) { 1104 cancelled = searchForMetadata(children_files[i]); 1105 } 1106 } 1107 return cancelled; 1108 } 1109 1110 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title) { 1111 boolean first_name = true; 1112 boolean first_extra = true; 1113 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath(); 1114 1115 HashMap mappings = collection.msm.profiler.getActions(collection_path); 1116 1117 // Now read in base_cfg line by line, parsing important onces and/or replacing them with information pertinent to our collection. Each line is then written back out to the new collect.cfg file. 1118 try { 1119 BufferedReader in = new BufferedReader(new FileReader(base_cfg)); 1120 BufferedWriter out = new BufferedWriter(new FileWriter(new_cfg, false)); // Overwrite whats there. 1121 String command = null; 1122 while((command = in.readLine()) != null) { 1123 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat. 1124 while(command.trim().endsWith("\\")) { 1125 command = command.substring(0, command.lastIndexOf("\\")); 1126 String next_line = in.readLine(); 1127 if(next_line != null) { 1128 command = command + next_line; 1129 } 1130 } 1131 ///ystem.err.println("Read: " + command); 1132 // Now we've finished parsing a line, determine what to do with it. 1133 String command_lc = command.toLowerCase(); 1134 // We replace the creator string with our own. 1135 if(command_lc.startsWith(Utility.CFG_CREATOR)) { 1136 write(out, Utility.CFG_CREATOR + " " + email); 1137 } 1138 else if(command_lc.startsWith(Utility.CFG_MAINTAINER)) { 1139 write(out, Utility.CFG_MAINTAINER + " " + email); 1140 } 1141 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONNAME)) { 1142 if(first_name) { 1143 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONNAME + " \"" + title + "\""); 1144 first_name = false; 1145 } 1146 } 1147 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA)) { 1148 if(first_extra) { 1149 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA + " \"" + description + "\""); 1150 first_extra = false; 1151 } 1152 } 1153 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_ICONCOLLECTION)) { 1154 write(out, Utility.CFG_COLLECTIONMETA_ICONCOLLECTION + " \"\""); 1155 } 1156 1157 // Just before we try more general parsing there are the special cases to check. These are explicit changes required by some collections to produce sensible results. 1158 else if(special_case == SPECIAL_DLS && command_lc.equals("classify hierarchy -hfile azlist.txt -metadata azlist -sort title -buttonname title -hlist_at_top")) { 1159 write(out, "classify AZList -metadata dls.Title -buttonname Title"); 1160 } 1161 else if(command_lc.startsWith(Utility.CFG_CLASSIFY)) { 1162 StringTokenizer tokenizer = new StringTokenizer(command); 1163 StringBuffer text = new StringBuffer(tokenizer.nextToken()); 1164 // Read in the classifier command watching for hfile, metadata and sort arguments. 1165 String buttonname = null; 1166 String hfile = null; 1167 String new_metadata = null; 1168 String old_metadata = null; 1169 while(tokenizer.hasMoreTokens()) { 1170 String token = tokenizer.nextToken(); 1171 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) { 1172 if(tokenizer.hasMoreTokens()) { 1173 text.append(" "); 1174 text.append(token); 1175 token = tokenizer.nextToken(); 1176 hfile = token; 1177 } 1178 } 1179 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) { 1180 if(tokenizer.hasMoreTokens()) { 1181 text.append(" "); 1182 text.append(token); 1183 String temp_metadata = tokenizer.nextToken(); 1184 String replacement = (String) mappings.get(temp_metadata); 1185 if(replacement != null) { 1186 token = replacement; 1187 old_metadata = temp_metadata; 1188 new_metadata = replacement; 1105 1189 } 1106 } 1107 return cancelled; 1108 } 1109 1110 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title) { 1111 boolean first_name = true; 1112 boolean first_extra = true; 1113 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath(); 1114 1115 HashMap mappings = collection.msm.profiler.getActions(collection_path); 1116 1117 // Now read in base_cfg line by line, parsing important onces and/or replacing them with information pertinent to our collection. Each line is then written back out to the new collect.cfg file. 1118 try { 1119 BufferedReader in = new BufferedReader(new FileReader(base_cfg)); 1120 BufferedWriter out = new BufferedWriter(new FileWriter(new_cfg, false)); // Overwrite whats there. 1121 String command = null; 1122 while((command = in.readLine()) != null) { 1123 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat. 1124 while(command.trim().endsWith("\\")) { 1125 command = command.substring(0, command.lastIndexOf("\\")); 1126 String next_line = in.readLine(); 1127 if(next_line != null) { 1128 command = command + next_line; 1129 } 1130 } 1131 ///ystem.err.println("Read: " + command); 1132 // Now we've finished parsing a line, determine what to do with it. 1133 String command_lc = command.toLowerCase(); 1134 // We replace the creator string with our own. 1135 if(command_lc.startsWith(Utility.CFG_CREATOR)) { 1136 write(out, Utility.CFG_CREATOR + " " + email); 1137 } 1138 else if(command_lc.startsWith(Utility.CFG_MAINTAINER)) { 1139 write(out, Utility.CFG_MAINTAINER + " " + email); 1140 } 1141 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONNAME)) { 1142 if(first_name) { 1143 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONNAME + " \"" + title + "\""); 1144 first_name = false; 1145 } 1146 } 1147 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA)) { 1148 if(first_extra) { 1149 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA + " \"" + description + "\""); 1150 first_extra = false; 1151 } 1152 } 1153 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_ICONCOLLECTION)) { 1154 write(out, Utility.CFG_COLLECTIONMETA_ICONCOLLECTION + " \"\""); 1155 } 1156 1157 // Just before we try more general parsing there are the special cases to check. These are explicit changes required by some collections to produce sensible results. 1158 else if(special_case == SPECIAL_DLS && command_lc.equals("classify hierarchy -hfile azlist.txt -metadata azlist -sort title -buttonname title -hlist_at_top")) { 1159 write(out, "classify AZList -metadata dls.Title -buttonname Title"); 1160 } 1161 else if(command_lc.startsWith(Utility.CFG_CLASSIFY)) { 1162 StringTokenizer tokenizer = new StringTokenizer(command); 1163 StringBuffer text = new StringBuffer(tokenizer.nextToken()); 1164 // Read in the classifier command watching for hfile, metadata and sort arguments. 1165 String buttonname = null; 1166 String hfile = null; 1167 String new_metadata = null; 1168 String old_metadata = null; 1169 while(tokenizer.hasMoreTokens()) { 1170 String token = tokenizer.nextToken(); 1171 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) { 1172 if(tokenizer.hasMoreTokens()) { 1173 text.append(" "); 1174 text.append(token); 1175 token = tokenizer.nextToken(); 1176 hfile = token; 1177 } 1178 } 1179 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) { 1180 if(tokenizer.hasMoreTokens()) { 1181 text.append(" "); 1182 text.append(token); 1183 String temp_metadata = tokenizer.nextToken(); 1184 String replacement = (String) mappings.get(temp_metadata); 1185 if(replacement != null) { 1186 token = replacement; 1187 old_metadata = temp_metadata; 1188 new_metadata = replacement; 1189 } 1190 else { 1191 token = temp_metadata; 1192 } 1193 temp_metadata = null; 1194 replacement = null; 1195 } 1196 } 1197 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) { 1198 if(tokenizer.hasMoreTokens()) { 1199 text.append(" "); 1200 text.append(token); 1201 String temp_metadata = tokenizer.nextToken(); 1202 String replacement = (String) mappings.get(temp_metadata); 1203 if(replacement != null) { 1204 token = replacement; 1205 } 1206 else { 1207 token = temp_metadata; 1208 } 1209 temp_metadata = null; 1210 replacement = null; 1211 } 1212 } 1213 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) { 1214 buttonname = token; 1215 } 1216 text.append(' '); 1217 text.append(token); 1218 token = null; 1219 } 1220 tokenizer = null; 1221 1222 // If we replaced the metadata argument and didn't encounter a buttonname, then add one now pointing back to the old metadata name in order to accomodate macro files which required such names (buttonname is metadata name by default)! 1223 if(old_metadata != null && new_metadata != null && buttonname == null) { 1224 text.append(' '); 1225 text.append(Utility.CFG_CLASSIFY_BUTTONNAME); 1226 text.append(' '); 1227 text.append(old_metadata); 1228 } 1229 command = text.toString(); 1230 // Replace the hfile if we found it 1231 if(hfile != null && new_metadata != null) { 1232 command = command.replaceAll(hfile, new_metadata + ".txt"); 1233 } 1234 buttonname = null; 1235 hfile = null; 1236 new_metadata = null; 1237 old_metadata = null; 1238 write(out, command); 1239 } 1240 else { 1241 // There is still one special case, that of the format command. In such a command we have to search for [<target>] to ensure we don't change parts of the format which have nothing to do with the metadata elements. 1242 boolean format_command = command_lc.startsWith(Utility.CFG_FORMAT); 1243 // Replace mapping strings 1244 if(mappings != null) { 1245 for(Iterator keys = mappings.keySet().iterator(); keys.hasNext(); ) { 1246 String target = (String) keys.next(); 1247 String replacement = (String) mappings.get(target); 1248 if(format_command) { 1249 target = "\\[" + target + "\\]"; 1250 replacement = "{Or}{[" + replacement + "]," + target + "}"; 1251 } 1252 command = command.replaceAll(target, replacement); 1253 } 1254 } 1255 write(out, command); 1256 } 1190 else { 1191 token = temp_metadata; 1257 1192 } 1258 in.close(); 1259 in = null; 1260 out.flush(); 1261 out.close(); 1262 out = null; 1263 } 1264 catch(Exception error) { 1265 Gatherer.printStackTrace(error); 1266 } 1267 // All done, I hope. 1268 } 1269 1270 private void write(BufferedWriter out, String message) 1271 throws Exception { 1272 ///ystem.err.println("Writing: " + message); 1273 out.write(message, 0, message.length()); 1274 out.newLine(); 1275 } 1276 1277 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */ 1278 private class FMTreeModelListener 1279 implements TreeModelListener { 1280 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false. 1193 temp_metadata = null; 1194 replacement = null; 1195 } 1196 } 1197 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) { 1198 if(tokenizer.hasMoreTokens()) { 1199 text.append(" "); 1200 text.append(token); 1201 String temp_metadata = tokenizer.nextToken(); 1202 String replacement = (String) mappings.get(temp_metadata); 1203 if(replacement != null) { 1204 token = replacement; 1205 } 1206 else { 1207 token = temp_metadata; 1208 } 1209 temp_metadata = null; 1210 replacement = null; 1211 } 1212 } 1213 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) { 1214 buttonname = token; 1215 } 1216 text.append(' '); 1217 text.append(token); 1218 token = null; 1219 } 1220 tokenizer = null; 1221 1222 // If we replaced the metadata argument and didn't encounter a buttonname, then add one now pointing back to the old metadata name in order to accomodate macro files which required such names (buttonname is metadata name by default)! 1223 if(old_metadata != null && new_metadata != null && buttonname == null) { 1224 text.append(' '); 1225 text.append(Utility.CFG_CLASSIFY_BUTTONNAME); 1226 text.append(' '); 1227 text.append(old_metadata); 1228 } 1229 command = text.toString(); 1230 // Replace the hfile if we found it 1231 if(hfile != null && new_metadata != null) { 1232 command = command.replaceAll(hfile, new_metadata + ".txt"); 1233 } 1234 buttonname = null; 1235 hfile = null; 1236 new_metadata = null; 1237 old_metadata = null; 1238 write(out, command); 1239 } 1240 else { 1241 // There is still one special case, that of the format command. In such a command we have to search for [<target>] to ensure we don't change parts of the format which have nothing to do with the metadata elements. 1242 boolean format_command = command_lc.startsWith(Utility.CFG_FORMAT); 1243 // Replace mapping strings 1244 if(mappings != null) { 1245 for(Iterator keys = mappings.keySet().iterator(); keys.hasNext(); ) { 1246 String target = (String) keys.next(); 1247 String replacement = (String) mappings.get(target); 1248 if(format_command) { 1249 target = "\\[" + target + "\\]"; 1250 replacement = "{Or}{[" + replacement + "]," + target + "}"; 1251 } 1252 command = command.replaceAll(target, replacement); 1253 } 1254 } 1255 write(out, command); 1256 } 1257 } 1258 in.close(); 1259 in = null; 1260 out.flush(); 1261 out.close(); 1262 out = null; 1263 } 1264 catch(Exception error) { 1265 Gatherer.printStackTrace(error); 1266 } 1267 // All done, I hope. 1268 } 1269 1270 private void write(BufferedWriter out, String message) 1271 throws Exception { 1272 ///ystem.err.println("Writing: " + message); 1273 out.write(message, 0, message.length()); 1274 out.newLine(); 1275 } 1276 1277 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */ 1278 private class FMTreeModelListener 1279 implements TreeModelListener { 1280 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false. 1281 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree. 1282 */ 1283 public void treeNodesChanged(TreeModelEvent event) { 1284 if(collection != null) { 1285 collection.setSaved(false); 1286 } 1287 } 1288 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false. 1281 1289 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree. 1282 1290 */ 1283 public void treeNodesChanged(TreeModelEvent event) {1284 1285 1286 1287 1288 1291 public void treeNodesInserted(TreeModelEvent event) { 1292 if(collection != null) { 1293 collection.setSaved(false); 1294 } 1295 } 1296 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false. 1289 1297 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree. 1290 1298 */ 1291 public void treeNodesInserted(TreeModelEvent event) {1292 1293 1294 1295 1296 1299 public void treeNodesRemoved(TreeModelEvent event) { 1300 if(collection != null) { 1301 collection.setSaved(false); 1302 } 1303 } 1304 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false. 1297 1305 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree. 1298 1306 */ 1299 public void treeNodesRemoved(TreeModelEvent event) { 1300 if(collection != null) { 1301 collection.setSaved(false); 1302 } 1303 } 1304 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false. 1305 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree. 1306 */ 1307 public void treeStructureChanged(TreeModelEvent event) { 1308 if(collection != null) { 1309 collection.setSaved(false); 1310 } 1311 } 1312 } 1307 public void treeStructureChanged(TreeModelEvent event) { 1308 if(collection != null) { 1309 collection.setSaved(false); 1310 } 1311 } 1312 } 1313 1313 } -
trunk/gli/src/org/greenstone/gatherer/collection/DeleteCollectionPrompt.java
r4293 r4366 50 50 */ 51 51 public class DeleteCollectionPrompt 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 52 extends JDialog { 53 /** The currently selected collection for deletion. */ 54 private Collection collection = null; 55 /** The model behind the list. */ 56 private DefaultListModel list_model = null; 57 /** A reference to ourself so any inner-classes get dispose of us. */ 58 private DeleteCollectionPrompt prompt = null; 59 /** The close button, which exits the prompt without deleting anything. */ 60 private JButton close_button = null; 61 /** The ok button which causes the selected collection to be deleted. */ 62 private JButton ok_button = null; 63 /** The confirmation check box. */ 64 private JCheckBox confirmation = null; 65 /** The label above details. */ 66 private JLabel details_label = null; 67 /** The label above the list. */ 68 private JLabel list_label = null; 69 /** The list of available collections. */ 70 private JList list = null; 71 /** The text area used to display details about the collection selected. */ 72 private JTextArea details = null; 73 /** A string array used to pass arguments to the phrase retrieval method. */ 74 private String args[] = null; 75 /** The size of the delete prompt screen. */ 76 public static final Dimension SIZE = new Dimension(500, 500); 77 /** Constructor. 78 * @see org.greenstone.gatherer.collection.DeleteCollectionPrompt.CloseButtonListener 79 * @see org.greenstone.gatherer.collection.DeleteCollectionPrompt.CollectionListListener 80 * @see org.greenstone.gatherer.collection.DeleteCollectionPrompt.ConfirmationCheckBoxListener 81 * @see org.greenstone.gatherer.collection.DeleteCollectionPrompt.OKButtonListener 82 */ 83 public DeleteCollectionPrompt() { 84 super(); 85 this.close_button = new JButton(get("General.Close", null)); 86 this.confirmation = new JCheckBox(get("Confirm_Delete", null)); 87 this.details = new JTextArea(); 88 this.details_label = new JLabel(get("Collection_Details", null)); 89 this.list = new JList(); 90 this.list_label = new JLabel(get("Collection_List", null)); 91 this.list_model = new DefaultListModel(); 92 this.ok_button = new JButton(get("General.OK", null)); 93 this.prompt = this; 94 this.setModal(true); 95 this.setSize(SIZE); 96 this.setTitle(get("Title", null)); 97 close_button.addActionListener(new CloseButtonListener()); 98 confirmation.addActionListener(new ConfirmationCheckBoxListener()); 99 confirmation.setEnabled(false); 100 confirmation.setSelected(false); 101 details.setFont(Gatherer.config.getFont("general.tooltip_font", false)); 102 details.setText(get("No_Collection", null)); 103 list.addListSelectionListener(new CollectionListListener()); 104 list.clearSelection(); 105 list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 106 list.setModel(list_model); 107 ok_button.addActionListener(new OKButtonListener()); 108 ok_button.setEnabled(false); 109 scanForCollections(); 110 } 111 /** Destructor. */ 112 public void destroy() { 113 close_button = null; 114 confirmation = null; 115 details = null; 116 details_label = null; 117 list_model.clear(); 118 list_model = null; 119 list = null; 120 ok_button = null; 121 prompt = null; 122 } 123 /** This method causes the modal prompt to be displayed. */ 124 public void display() { 125 // Central pane 126 JPanel list_pane = new JPanel(new BorderLayout()); 127 list_pane.add(list_label, BorderLayout.NORTH); 128 list_pane.add(new JScrollPane(list), BorderLayout.CENTER); 129 list_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); 130 131 JPanel details_pane = new JPanel(new BorderLayout()); 132 details_pane.add(details_label, BorderLayout.NORTH); 133 details_pane.add(new JScrollPane(details), BorderLayout.CENTER); 134 details_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 135 136 JPanel central_pane = new JPanel(new GridLayout(2, 1)); 137 central_pane.add(list_pane); 138 central_pane.add(details_pane); 139 central_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 140 141 // Lower pane 142 JPanel confirmation_pane = new JPanel(new BorderLayout()); 143 confirmation_pane.add(confirmation, BorderLayout.CENTER); 144 confirmation_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 145 146 JPanel button_pane = new JPanel(new GridLayout(1, 2)); 147 button_pane.add(ok_button); 148 button_pane.add(close_button); 149 button_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 150 151 JPanel lower_pane = new JPanel(new BorderLayout()); 152 lower_pane.add(confirmation_pane, BorderLayout.NORTH); 153 lower_pane.add(button_pane, BorderLayout.SOUTH); 154 lower_pane.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); 155 156 // Final. 157 JPanel content_pane = (JPanel)this.getContentPane(); 158 content_pane.setLayout(new BorderLayout()); 159 content_pane.add(central_pane, BorderLayout.CENTER); 160 content_pane.add(lower_pane, BorderLayout.SOUTH); 161 162 // Center and display. 163 Dimension screen_size = Gatherer.config.screen_size; 164 this.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 165 this.show(); 166 } 167 /** Shows a delete complete prompt. 168 168 * @param success A <strong>boolean</strong> indicating if the collection was successfully deleted. 169 169 * @see org.greenstone.gatherer.collection.Collection 170 170 */ 171 172 173 174 175 176 177 178 179 180 181 171 public void resultPrompt(boolean success) { 172 args = new String[1]; 173 args[0] = collection.getName(); 174 if(success) { 175 JOptionPane.showMessageDialog(prompt,get("Successful_Delete", args),get("Successful_Title", null),JOptionPane.INFORMATION_MESSAGE); 176 } 177 else { 178 JOptionPane.showMessageDialog(prompt,get("Failed_Delete", args),get("Failed_Title", null),JOptionPane.WARNING_MESSAGE); 179 } 180 } 181 /** Retrieves a phrase from the <strong>Dictionary</strong> in <strong>Gatherer</strong>. 182 182 * @param key A <strong>String</strong> used to match against a specific phrase in the dictionary. 183 183 * @see org.greenstone.gatherer.Dictionary 184 184 * @see org.greenstone.gatherer.Gatherer 185 185 */ 186 187 188 189 190 191 192 186 private String get(String key, String args[]) { 187 if(key.indexOf(".") == -1) { 188 key = "DeleteCollectionPrompt." + key; 189 } 190 return Gatherer.dictionary.get(key, args); 191 } 192 /** Attempts to load the given collection. Currently uses simple 193 193 * serialization of the collection class. 194 194 * @param location The path to the serialized collection. 195 195 */ 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 196 public Collection loadCollection(File file) { 197 Collection col = null; 198 try { 199 FileInputStream fis = new FileInputStream(file); 200 BufferedInputStream bin = new BufferedInputStream(fis); 201 ObjectInputStream input = new ObjectInputStream(bin); 202 col = (Collection) input.readObject(); 203 input.close(); 204 input = null; 205 bin.close(); 206 bin = null; 207 fis.close(); 208 fis = null; 209 } 210 catch (Exception error) { 211 error.printStackTrace(); 212 } 213 return col; 214 } 215 /** Method to scan the collect directory retrieving and reloading each collection it finds, while building the list of known collections. 216 216 * @see org.greenstone.gatherer.Configuration 217 217 * @see org.greenstone.gatherer.Gatherer … … 219 219 * @see org.greenstone.gatherer.util.Utility 220 220 */ 221 222 223 224 225 221 private void scanForCollections() { 222 // Start at the collect dir. 223 String collect_directory_name = Utility.getCollectionDir(Gatherer.config.gsdl_path); 224 File collect_directory = new File(collect_directory_name); 225 if(collect_directory.exists()) { 226 226 // Now for each child directory see if it contains a .col file and 227 227 // if so try to load it.. 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 228 File collections[] = collect_directory.listFiles(); 229 ArrayTools.sort(collections); 230 for(int i = 0; collections != null && i < collections.length; i++) { 231 File children[] = collections[i].listFiles(); 232 for(int j = 0; children != null && j < children.length; j++) { 233 if(children[j].getAbsolutePath().endsWith(".col")) { 234 Collection col = loadCollection(children[j]); 235 // If we've actually loaded 236 if(col != null) { 237 list_model.addElement(col); 238 } 239 } 240 } 241 } 242 } 243 // Otherwise the collect directory doesn't actually exist, so there ain't much we can do. 244 } 245 /** A button listener implementation, which listens for actions on the close button and disposes of the dialog when detected. */ 246 private class CloseButtonListener 247 implements ActionListener { 248 /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned. 249 * @param event An <strong>ActionEvent</strong> containing all the relevant information garnered from the event itself. 250 250 */ 251 252 253 254 255 256 */ 257 258 259 260 251 public void actionPerformed(ActionEvent event) { 252 prompt.dispose(); 253 } 254 } 255 /** This private class listens for selection events in from the list and then displays the appropriate details for that collection. 256 */ 257 private class CollectionListListener 258 implements ListSelectionListener { 259 /** Any implementation of ListSelectionListener must include this method so we can be informed when the list selection changes. 260 * @param event A <strong>ListSelectionEvent</strong> containing all the relevant information garnered from the event itself. 261 261 */ 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 262 public void valueChanged(ListSelectionEvent event) { 263 if(!list.isSelectionEmpty()) { 264 confirmation.setEnabled(true); 265 collection = (Collection) list.getSelectedValue(); 266 args = new String[4]; 267 args[0] = collection.getTitle(); 268 args[1] = collection.getName(); 269 args[2] = collection.getEmail(); 270 args[3] = collection.getDescription(); 271 details.setText(get("Details", args)); 272 } 273 else { 274 confirmation.setEnabled(false); 275 details.setText(get("No_Collection", null)); 276 } 277 } 278 } 279 /** A check box listener so we can tell if the users selected confirm. */ 280 private class ConfirmationCheckBoxListener 281 implements ActionListener { 282 /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned. 283 * @param event An <strong>ActionEvent</strong> containing all the relevant information garnered from the event itself. 284 284 */ 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 285 public void actionPerformed(ActionEvent event) { 286 if(confirmation.isSelected()) { 287 ok_button.setEnabled(true); 288 } 289 else { 290 ok_button.setEnabled(false); 291 } 292 } 293 } 294 /** The OK button listener implementation. */ 295 private class OKButtonListener 296 implements ActionListener { 297 /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned. 298 * @param event An <strong>ActionEvent</strong> containing all the relevant information garnered from the event itself. 299 * @see org.greenstone.gatherer.Configuration 300 * @see org.greenstone.gatherer.Gatherer 301 * @see org.greenstone.gatherer.util.Utility 302 */ 303 public void actionPerformed(ActionEvent event) { 304 304 // Delete the selected collection. 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 305 File delete_me = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator); 306 if(Utility.delete(delete_me)) { 307 resultPrompt(true); 308 list_model.removeElement(collection); 309 details.setText(get("No_Collection", null)); 310 confirmation.setEnabled(false); 311 confirmation.setSelected(false); 312 ok_button.setEnabled(false); 313 collection = null; 314 } 315 else { 316 resultPrompt(false); 317 } 318 } 319 } 320 320 } 321 321 -
trunk/gli/src/org/greenstone/gatherer/collection/Job.java
r4293 r4366 50 50 */ 51 51 public class Job 52 52 implements ActionListener { 53 53 54 54 private boolean clobber; 55 55 private boolean debug; 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 56 private boolean higher_directories; 57 private boolean no_parents; 58 private boolean other_hosts; 59 private boolean page_requisites; 60 private boolean quiet; 61 62 private GProgressBar progress; 63 64 private GURL initial = null; 65 private GURL url = null; 66 67 private TreeModel model; 68 69 private int depth; 70 private int previous_state; 71 private int state; 72 73 private String current_url; 74 private String destination; 75 private String proxy_pass; 76 private String proxy_user; 77 78 private Vector encountered_urls; 79 private Vector failed_urls; 80 81 private WGet mummy; 82 83 public static int COMPLETE = 0; 84 public static int PAUSED = 1; 85 85 public static int RUNNING = 2; 86 86 public static int STOPPED = 3; 87 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 88 /** 89 */ 90 public Job(TreeModel model, boolean clobber, boolean debug, boolean no_parents, boolean other_hosts, boolean page_requisites, boolean quiet, GURL initial, int depth, String destination, String proxy_pass, String proxy_user, WGet mummy, boolean simple) { 91 this.model = model; 92 93 this.debug = debug; 94 this.clobber = clobber; 95 this.no_parents = no_parents; 96 this.other_hosts = other_hosts; 97 this.page_requisites = page_requisites; 98 this.quiet = quiet; 99 this.initial = initial; 100 this.depth = depth; 101 this.destination = destination; 102 this.proxy_pass = proxy_pass; 103 this.proxy_user = proxy_user; 104 this.mummy = mummy; 105 106 progress = new GProgressBar(this, initial.toString(), simple); 107 108 encountered_urls = new Vector(); 109 failed_urls = new Vector(); 110 111 previous_state = STOPPED; 112 state = STOPPED; 113 } 114 115 /** Depending on which button on the progress bar was pushed, 116 * this method will affect the state of the Job and perhaps make 117 * calls to wget.class if necessary. 118 * @param event The ActionEvent fired from within the GProgressBar 119 * which we must respond to. 120 */ 121 public void actionPerformed(ActionEvent event) { 122 // The action button is used to alternately start or stop the 123 // job. If the current state of the job is paused then this 124 // restart is logically equivelent to a resume. 125 if(event.getSource() == progress.action) { 126 previous_state = state; 127 if(state == RUNNING) { 128 state = PAUSED; 129 } 130 else { 131 state = RUNNING; 132 mummy.resumeThread(); 133 } 134 } 135 else if (event.getSource() == progress.cancel) { 136 state = STOPPED; // Should already be stopped. 137 mummy.deleteJob(this); 138 } 139 } 140 141 /** Called by the WGet native code to inform us of a new download starting. 142 * @param url The url that is being downloaded, as a String. 143 */ 144 public void addDownload(String raw_url) { 145 if(!encountered_urls.contains(raw_url)) { 146 encountered_urls.add(raw_url); 147 } 148 // Regardless create a new GURL 149 current_url = raw_url; 150 url = new GURL(raw_url); 151 progress.addDownload(raw_url); 152 } 153 154 /** Used to advise the Job of a newly parsed link. Its up to Job 155 * to decide if it already knows about this url, and if not to 156 * update its progress bar. 157 * @param url The url in question as a String. 158 * @param type Whether the link is an internal or external link. 159 * @return A boolean indicating if the url was added. 160 */ 161 public boolean addLink(String raw_url, int type) { 162 ///ystem.out.println("addLink("+url+", "+type+")"); 163 if(!encountered_urls.contains(raw_url)) { 164 164 // Add it to the urls we've seen. 165 165 encountered_urls.add(raw_url); 166 166 // Add it the to links for the current GURL. 167 167 168 168 // Add it to the progress file count. 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 169 progress.increaseFileCount(); 170 return true; 171 } 172 // Regardless add it to the children links of the current GURL 173 initial.addLink(raw_url); 174 175 // We've seen it before. Don't count it again. 176 return false; 177 } 178 179 public void callWGet() { 180 // Build parameter string 181 String command = "wget "; 182 183 // Parse arguments into array. 184 // Always: 185 // rewrite links to be local if possible - NOOOOOO, 186 // output a debug file and debug messages, 187 // run quietly. 188 //command = command + "-k "; 189 190 if(destination != null) { 191 command = command + "-P " + destination + " "; 192 } 193 194 if(depth < 0) { 195 195 // Infinite recursion 196 197 198 196 command = command + "-r "; 197 } 198 else if (depth == 0) { 199 199 // Just this page. 200 201 200 } 201 else if (depth > 0) { 202 202 // Recursion to the specified depth. 203 204 205 206 207 208 209 210 211 212 213 203 command = command + "-r -l" + depth + " "; 204 } 205 206 if(!clobber || previous_state == Job.PAUSED) { 207 command = command + "-nc -c "; 208 } 209 210 if(proxy_user != null) { 211 command = command + "--proxy-user=" + proxy_user 212 + " --proxy-passwd=" + proxy_pass + " "; 213 } 214 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 215 if(page_requisites) { 216 command = command + "-p "; 217 } 218 219 if(other_hosts) { 220 command = command + "-H "; 221 } 222 223 // Finally tell it the site to download. 224 command = command + initial.toString(); 225 226 if(previous_state == Job.COMPLETE) { 227 progress.mirrorBegun(true); 228 } 229 else { 230 progress.mirrorBegun(false); 231 } 232 233 // Run it 234 try { 235 Gatherer.println("Cmd: " + command); 236 Runtime rt = Runtime.getRuntime(); 237 Process prcs = rt.exec(command); 238 InputStreamReader isr = 239 new InputStreamReader( prcs.getErrorStream() ); 240 BufferedReader br = new BufferedReader( isr ); 241 241 // Capture the standard error stream and seach for two particular 242 242 // occurances. 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 243 String line; 244 boolean ignore_for_robots = false; 245 while ((line = br.readLine()) != null) { 246 Gatherer.println(line); 247 248 // The first magic special test is to see if we've just 249 // asked for the robots.txt file. If so we ignore 250 // the next add and then the next complete/error. 251 if(line.lastIndexOf("robots.txt;") != -1) { 252 Gatherer.println("***** Requesting robot.txt"); 253 ignore_for_robots = true; 254 } 255 // If line contains "=> `" display text as the 256 // currently downloading url. Unique to add download. 257 else if(line.lastIndexOf("=> `") != -1) { 258 if(!ignore_for_robots) { 259 // Add download 260 String new_url = 261 line.substring(line.indexOf("`") + 1, 262 line.lastIndexOf("'")); 263 // Remove the destination guff 264 if(destination != null) { 265 new_url = new_url.substring(destination.length()); 266 } 267 addDownload("http:/" + new_url); 268 } 269 } 270 // If line contains "saved [<size>]" set currently 271 // downloading url to "Download Complete". 272 else if(line.lastIndexOf(") - `") != -1) { 273 if(!ignore_for_robots) { 274 // Download complete 275 downloadComplete(); 276 } 277 else { 278 ignore_for_robots = false; 279 } 280 } 281 // The already there line begins "File `..." However this 282 // is only true in english, so instead I looked and there 283 // are few (if any at all) other messages than those above 284 // and not overwriting messages that use " `" so we'll 285 // look for that. Note this method is not guarenteed to be 286 // unique like the previous two. 287 else if(line.lastIndexOf(" `") != -1) { 288 // Not Overwriting 289 Gatherer.println("Already there."); 290 String new_url = 291 line.substring(line.indexOf("`") + 1, 292 line.lastIndexOf("'")); 293 // Remove the destination guff 294 if(destination != null) { 295 new_url = new_url.substring(destination.length()); 296 } 297 addDownload("http:/" + new_url); 298 downloadWarning(); 299 } 300 // Any other important message starts with the time in the form hh:mm:ss 301 else if(line.length() > 7) { 302 if(line.charAt(2) == ':' && line.charAt(5) == ':') { 303 if(!ignore_for_robots) { 304 Gatherer.println("Error."); 305 downloadFailed(); 306 } 307 else { 308 ignore_for_robots = false; 309 } 310 } 311 } 312 } 313 313 // Now display final message based on exit value 314 315 314 prcs.waitFor(); 315 } catch (Exception ioe) { 316 316 //message(Utility.ERROR, ioe.toString()); 317 318 319 320 321 322 323 324 325 326 } 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 317 Gatherer.printStackTrace(ioe); 318 } 319 // If we've got to here and the state isn't STOPPED then the 320 // job is complete. 321 if(state == Job.RUNNING) { 322 progress.mirrorComplete(); 323 previous_state = state; 324 state = Job.COMPLETE; 325 } 326 } 327 328 /** The most important part of the Job class, this method is 329 * responsible for calling the WGet native methods used to 330 * mirror the indicated url. By this stage all the variables 331 * necessary should be set and we need only build up the 332 * parameter string and make the call. 333 */ 334 public void callWGetNative() { 335 Vector args = new Vector(); 336 337 // Let the GProgressBar know we're starting, just in case 338 // the user hasn't told us to. If this is the second time the 339 // urls downloaded and the first attempt was successful (ie 340 // the previous job was complete), then we have the case where 341 // the user is forcing us to remirror. Reset all the values etc 342 // if this is the case then reset the variables. 343 // Note that this can cause the result line to look something 344 // like this. 345 // Downloaded 12 of 12 files (8 warnings, 0 errors). 346 // The warnings would be something like, 'File already downloaded' 347 // but the total number of files and the file successfully 348 // downloaded will be correct. 349 if(previous_state == Job.COMPLETE) { 350 progress.mirrorBegun(true); 351 } 352 else { 353 progress.mirrorBegun(false); 354 } 355 356 // Parse arguments into array. 357 args.add(Utility.BASE_DIR + "wget"); 358 //args.add("-k"); 359 args.add("-d"); 360 args.add("-o"); 361 args.add("debug.txt"); 362 362 363 364 365 366 367 368 363 if(destination != null) { 364 args.add("-P"); 365 args.add(destination); 366 } 367 368 if(depth < 0) { 369 369 // Infinite recursion 370 371 372 370 args.add("-r"); 371 } 372 else if (depth == 0) { 373 373 // Just this page. 374 375 374 } 375 else if (depth > 0) { 376 376 // Recursion to the specified depth. 377 378 379 380 381 382 383 384 385 386 387 388 389 390 377 args.add("-r"); 378 args.add("-l"); 379 args.add("" + depth + ""); // Hacky 380 } 381 382 if(!clobber || previous_state == PAUSED) { 383 args.add("-nc"); 384 args.add("-c"); 385 } 386 387 if(proxy_user != null) { 388 args.add("--proxy-user=" + proxy_user); 389 args.add("--proxy-passwd=" + proxy_pass); 390 } 391 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 392 if(page_requisites) { 393 args.add("-p"); 394 } 395 396 if(quiet) { 397 args.add("-q"); 398 } 399 400 if(other_hosts) { 401 args.add("-H"); 402 } 403 404 args.add(initial.toString()); 405 406 Gatherer.println("Calling wget "); 407 for(Enumeration e = args.elements(); e.hasMoreElements();) { 408 Gatherer.println(e.nextElement() + " "); 409 } 410 Gatherer.println(""); 411 412 // Run home to mummy. 413 int value = mummy.wget(args.size(), args.toArray(), debug); 414 415 // If we've got to here and the state isn't STOPPED then the job is complete. 416 if(state == RUNNING) { 417 progress.mirrorComplete(); 418 previous_state = state; 419 state = COMPLETE; 420 } 421 } 422 423 /** Called by the WGet native code when the current download is 424 * completed. In turn all download listeners are informed. 425 */ 426 public void downloadComplete() { 427 progress.downloadComplete(); 428 /* @todo 429 model.add(url.getURL(), destination); 430 */ 431 url = null; 432 current_url = null; 433 } 434 434 435 436 437 438 439 440 441 442 435 /** Called by the WGet native code when the requested download returns 436 * a status code other than 200. 437 */ 438 public void downloadFailed() { 439 ///ystem.out.println("downloadFailed("+current_url+")"); 440 failed_urls.add(current_url); // Its the current url thats failed. 441 progress.downloadFailed(); 442 } 443 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 /**466 467 468 public GProgressBar getProgressBar() {469 470 }471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 444 /** 445 */ 446 public void downloadWarning() { 447 progress.downloadWarning(); 448 } 449 450 /** 451 * @return A String representing the currently downloading url. 452 */ 453 public String getCurrent() { 454 return current_url; 455 } 456 457 /** 458 * @return A String representing the initial urls host (root node 459 * of tree that we are mirroring). 460 */ 461 public String getHost() { 462 return url.getHost(); 463 } 464 465 /** 466 * @return Returns the progress bar associated with this job. 467 */ 468 public GProgressBar getProgressBar() { 469 return progress; 470 } 471 472 /** Called to discover if the user wanted this thread to run or if 473 * it is paused. 474 * @return An int representing the current Job state. 475 */ 476 public int getState() { 477 return state; 478 } 479 480 /** Returns the current state of the stop flag for this job. 481 * @return A boolean representing whether the user has requested to 482 * stop. 483 */ 484 public boolean hasSignalledStop() { 485 if(state == Job.STOPPED || state == Job.PAUSED || 486 state == Job.COMPLETE) { 487 return true; 488 } 489 return false; 490 } 491 492 /** A convinence call. 493 * @return A String representing the url of the initial url (root node of the mirrored tree). 494 */ 495 public String toString() { 496 return initial.toString(); 497 } 498 499 /** Called by the WGet native code to signal the current progress of 500 * downloading. 501 * @param current A long representing the number of bytes that have 502 * been downloaded since the last update. 503 * @param expected A long representing the total number of bytes 504 * expected for this download. 505 */ 506 public void updateProgress(long current, long expected) { 507 progress.updateProgress(current, expected); 508 } 509 509 } 510 510 -
trunk/gli/src/org/greenstone/gatherer/collection/LoadCollectionBox.java
r4293 r4366 49 49 public class LoadCollectionBox { 50 50 51 52 53 51 private JFileChooser chooser = null; 52 public LoadCollectionBox() { 53 } 54 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 55 public String getFileName() { 56 String defaultPath = Utility.getCollectionDir(Gatherer.config.gsdl_path); 57 chooser = new JFileChooser(new File(defaultPath)); 58 chooser.setDialogTitle(Gatherer.dictionary.get("SaveCollectionBox.Title")); 59 ExtensionFileFilter filter1 = new ExtensionFileFilter(".col", Gatherer.dictionary.get("General.File_Description")); 60 chooser.setFileFilter(filter1); 61 chooser.setAcceptAllFileFilterUsed(false); 62 int return_val = chooser.showOpenDialog(null); 63 if(return_val == JFileChooser.APPROVE_OPTION) { 64 return chooser.getSelectedFile().getAbsolutePath(); 65 } 66 else { 67 return null; 68 } 69 } 70 70 } -
trunk/gli/src/org/greenstone/gatherer/collection/SaveCollectionBox.java
r4293 r4366 42 42 /** Provides a prompt allowing the user some choice in whether a collection saves. */ 43 43 public class SaveCollectionBox 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 44 extends JDialog 45 implements ActionListener{ 46 /** What option the user has choosen. */ 47 private int result = 0; 48 /** Button to cancel prompt, no save. */ 49 private JButton cancel = null; 50 /** Button for no save. */ 51 private JButton no = null; 52 /** Button to save. */ 53 private JButton yes = null; 54 /** A reference to ourselves so our inner classes can dispose of us. */ 55 private SaveCollectionBox myself = null; 56 /** Result value if the user has choosen cancel. */ 57 static final public int SAVE_CANCEL = 0; 58 /** Result value if the user has choosen no. */ 59 static final public int SAVE_NO = 1; 60 /** Result value if the user has choosen yes. */ 61 static final public int SAVE_YES = 2; 62 /** Construtor. */ 63 public SaveCollectionBox() { 64 super(Gatherer.g_man); 65 this.myself = this; 66 result = SAVE_CANCEL; 67 // Dialog setup 68 this.setModal(true); 69 this.setTitle(get("Title")); 70 this.setSize(360,100); 71 } 72 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured. In this case we see what button the users clicked, set the appropriate result then dispose of the dialog. */ 73 public void actionPerformed(ActionEvent event) { 74 if(event.getSource() == yes) { 75 result = SAVE_YES; 76 this.dispose(); 77 } else if(event.getSource() == no) { 78 result = SAVE_NO; 79 this.dispose(); 80 } else if(event.getSource() == cancel) { 81 result = SAVE_CANCEL; 82 this.dispose(); 83 } 84 } 85 /** Destructor. */ 86 public void destroy() { 87 cancel = null; 88 no.removeActionListener(this); 89 no = null; 90 yes.removeActionListener(this); 91 yes = null; 92 myself = null; 93 rootPane = null; 94 } 95 /** Displays the prompt by first building the controls, then waits for a user selection. 96 96 * @param name The name of the current collection as a <strong>String</strong>. 97 97 */ 98 99 100 101 98 public int getUserOption(String name) { 99 JPanel content_pane = (JPanel) this.getContentPane(); 100 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 101 content_pane.setLayout(new GridLayout(2,1)); 102 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 103 String args[] = new String[1]; 104 args[0] = name; 105 JLabel save_label = new JLabel(get("Label", args)); 106 content_pane.add(save_label); 107 JPanel button_pane = new JPanel(new GridLayout(1,3)); 108 content_pane.add(button_pane); 109 // We add both mnemonics and key listener so that the 'y' of yes is underlined, but pressing just [Y] (rather than [CTL]-[Y]) performs a systematic click. 110 yes = new JButton(get("General.Yes")); 111 yes.addActionListener(this); 112 KeyListenerImpl key_listener = new KeyListenerImpl(); 113 yes.addKeyListener(key_listener); 114 yes.setMnemonic(KeyEvent.VK_Y); 115 button_pane.add(yes); 116 no = new JButton(get("General.No")); 117 no.addActionListener(this); 118 no.addKeyListener(key_listener); 119 no.setMnemonic(KeyEvent.VK_N); 120 button_pane.add(no); 121 cancel = new JButton(get("General.Cancel")); 122 cancel.addActionListener(this); 123 cancel.addKeyListener(key_listener); 124 cancel.setMnemonic(KeyEvent.VK_C); 125 button_pane.add(cancel); 126 // Center on screen. 127 Dimension dlgSize = getSize(); 128 Dimension frmSize = Gatherer.g_man.getSize(); 129 Point loc = Gatherer.g_man.getLocation(); 130 setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); 131 show(); 132 // Deallocate things we allocated here. 133 yes.removeKeyListener(key_listener); 134 no.removeKeyListener(key_listener); 135 cancel.removeKeyListener(key_listener); 136 key_listener = null; 137 content_pane = null; 138 args = null; 139 save_label = null; 140 button_pane = null; 141 dlgSize = null; 142 frmSize = null; 143 loc = null; 144 return result; 145 } 146 /** Method to retrieve a phrase from the dictionary based of a key. 147 147 * @param key A <strong>String</strong> used to find the correct phrase. 148 148 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. 149 149 */ 150 151 152 153 150 private String get(String key) { 151 return get(key, null); 152 } 153 /** Method to retrieve a phrase from the dictionary based of a key and including arguments. 154 154 * @param key A <strong>String</strong> used to find the correct phrase. 155 155 * @param args A <strong>String[]</strong> of arguments used in formatting and filling out the phrase. 156 156 * @return A <strong>String</strong> containing the correct phrase with the correct formatting. 157 157 */ 158 159 160 161 162 163 164 165 166 167 168 158 private String get(String key, String args[]) { 159 if(key.indexOf('.') == -1) { 160 key = "SaveCollectionBox." + key; 161 } 162 return Gatherer.dictionary.get(key, args); 163 } 164 /** Listens for key presses when the focus is on one of the buttons. Note that the key pressed needn't be the one associated with the button in focus. */ 165 private class KeyListenerImpl 166 extends KeyAdapter { 167 /** Any extension of KeyAdapter can override this method so that we can be informed whenever a key is released (ie after a keyTyped event). In this case we map the key press to an appropriate click on one of the buttons (not necessarily the one the key release was detected on). 168 * @param event A <strong>KeyEvent</strong> containing details about the key release event. 169 169 */ 170 171 172 173 174 175 176 177 178 179 180 181 170 public void keyReleased(KeyEvent event) { 171 if(event.getKeyCode() == KeyEvent.VK_Y) { 172 yes.doClick(10); 173 } 174 else if(event.getKeyCode() == KeyEvent.VK_N) { 175 no.doClick(10); 176 } 177 else if(event.getKeyCode() == KeyEvent.VK_C) { 178 cancel.doClick(10); 179 } 180 } 181 } 182 182 } -
trunk/gli/src/org/greenstone/gatherer/collection/SaveCollectionTask.java
r4293 r4366 50 50 */ 51 51 public class SaveCollectionTask 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 52 extends Thread { 53 private boolean close_after = false; 54 /** Should we exit the Gatherer once this save is complete. */ 55 private boolean exit_after = false; 56 /** Do we run the import scripts once this save is complete. */ 57 private boolean import_after = false; 58 /** The current collection. */ 59 private Collection collection = null; 60 /** The filename of the collection we are saving. */ 61 private String name = null; 62 static final private int CLOSE_COLLECTION = 0; 63 static final private int COLLECTION_COPIED = 1; 64 static final private int COLLECTION_SAVED = 2; 65 static final private int COLLECTION_CFG_SAVED = 3; 66 static final private int COPY_COLLECTION = 4; 67 static final private int MAKE_COLLECTION = 5; 68 static final private int METADATA_SAVED = 6; 69 static final private int METADATA_XML_SAVED = 7; 70 static final private int OPEN_COLLECTION = 8; 71 static final private int RESTORE_COLLECTION = 9; 72 /** Constructor. 73 * @param name The filename of the collection we are saving as. 74 */ 75 public SaveCollectionTask(Collection collection) { 76 this.collection = collection; 77 } 78 /** Constructor. 79 * @param exit_after <i>true</i> to cause the Gatherer to exit once save is complete, <i>false</i> otherwise. 80 */ 81 public SaveCollectionTask(Collection collection, boolean close_after, boolean exit_after) { 82 this.close_after = close_after; 83 this.collection = collection; 84 this.exit_after = exit_after; 85 } 86 /** Constructor. 87 * @param name The filename of the collection we are saving as. 88 */ 89 public SaveCollectionTask(Collection collection, String name) { 90 this.collection = collection; 91 this.name = name; 92 } 93 /** <p>This method, when created in a forked process by start(), performs the actions necessary to save a collection. If this is a regular save it saves the state of the current collection, ensuring all files, metadata files, and configuration files are written to disk. If, instead, this is a "Save As" action then there as several more steps involved:</p> 94 * <p>1. Perform a regular collection save on what we will later refer to as the origin collection.</p> 95 * <p>2. Call CollectionManager.makeCollection() to create the collection structure to save into (copied collection).</p> 96 * <p>3. Copy files from the origin to the copied collections (except special collection files ending with ~).</p> 97 * <p>4. Restore origin collection by undoing file copy actions and removing the ~ from the end of collection files.</p> 98 * <p>5. Close the origin collection.</p> 99 * <p>6. Open the copied collection.</p> 100 * <p>Of course all of this takes a while, and depends on several other bits of code to work properly.</p>. 101 * @see org.greenstone.gatherer.collection.Collection 102 * @see org.greenstone.gatherer.collection.CollectionModel 103 * @see org.greenstone.gatherer.gui.GUIManager 104 * @see org.greenstone.gatherer.gui.GConfigPane 105 * @see org.greenstone.gatherer.msm.MetadataSetManager 106 * @see org.greenstone.gatherer.util.Utility 107 */ 108 public void run() { 109 // Change cursor to hourglass. 110 Gatherer.g_man.wait(true); 111 // Create progress monitor box. It will display itself as necessary. 112 ProgressMonitor spd = new ProgressMonitor(Gatherer.g_man, Gatherer.dictionary.get("SaveProgressDialog.Title", collection.getName()), null, 0, 100); 113 spd.setMillisToDecideToPopup(100); 114 spd.setMillisToPopup(100); 115 // 0. Force all remaining metadata.xml files to load. 116 // 1. Perform a regular collection save on what we will later refer to as the origin collection. 117 ///ystem.err.println("1. Save origin."); 118 String tmp_loc = Gatherer.c_man.getCollectionFilename(); 119 String args[] = new String[1]; 120 args[0] = collection.getName() + ".col"; 121 try { 122 122 // Block until all of the metadata files have been read in. 123 123 collection.gdm.waitUntilComplete(); 124 124 // Write out the metadata xml files. The destroy below is meant to do this, but never does. 125 125 collection.gdm.save(); 126 126 127 128 127 spd.setProgress(getValue(METADATA_XML_SAVED)); 128 File file = new File(tmp_loc); 129 129 // Create backup 130 131 132 133 134 135 136 130 if(file.exists()) { 131 File backup = new File(tmp_loc + "~"); 132 backup.deleteOnExit(); 133 if(!file.renameTo(backup)) { 134 Gatherer.println("Error in CollectionManager.load(): FileNotRenamedException"); 135 } 136 } 137 137 // Carry on. 138 139 138 collection.save(); 139 spd.setProgress(getValue(COLLECTION_SAVED)); 140 140 // Write out the collection configuration file. 141 142 141 Gatherer.g_man.config_pane.saveConfiguration(); 142 spd.setProgress(getValue(COLLECTION_CFG_SAVED)); 143 143 // Write out the metadata files. 144 145 144 Gatherer.c_man.msm.save(); 145 spd.setProgress(getValue(METADATA_SAVED)); 146 146 // Clean-up 147 147 spd.setProgress(100); 148 148 ///atherer.g_man.collectionChanged(Gatherer.c_man.ready()); 149 150 151 152 153 154 155 149 collection.setSaved(true); 150 } 151 catch (Exception error) { 152 Gatherer.printStackTrace(error); 153 } 154 // Now we check whether we've finished, or is this a Save As action, in which case we've got miles to go. 155 if(name != null) { 156 156 // 2. Call CollectionManager.makeCollection() to create the collection structure to save into (copied collection). 157 157 ///ystem.err.println("2. Make copy."); 158 159 158 Gatherer.c_man.createCollection(null, null, name, null, null, null); 159 spd.setProgress(getValue(MAKE_COLLECTION)); 160 160 // 3. Copy files from the origin to the copied collections (except special collection files ending with ~). 161 161 // As part of this we must rename origin.col to copy.col 162 162 ///ystem.err.println("3. Copy origin."); 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 163 ArrayList files = new ArrayList(); 164 File collection_file = new File(Gatherer.c_man.getCollectionFilename()); 165 File copied_dir = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path) + name); 166 files.add(collection_file.getParentFile()); 167 while(files.size() > 0) { 168 File file = (File) files.get(0); 169 files.remove(0); 170 if(file.isDirectory()) { 171 File children[] = file.listFiles(); 172 for(int i = 0; i < children.length; i++) { 173 files.add(children[i]); 174 } 175 children = null; 176 } 177 else { 178 // Rename origin.col file. 179 if(file.equals(collection_file)) { 180 File copied_file = new File(copied_dir, name + ".col"); 181 copy(collection_file, copied_file); 182 copied_file = null; 183 } 184 // Exclude *~ files from certain locations and lock.tmp file. 185 else if(!file.getName().endsWith("~") && !file.getName().equals(CollectionManager.LOCK_FILE)) { 186 StringTokenizer origin = new StringTokenizer(file.getAbsolutePath(), File.separator); 187 StringTokenizer destin = new StringTokenizer(copied_dir.getAbsolutePath(), File.separator); 188 while(destin.hasMoreTokens()) { 189 origin.nextToken(); 190 destin.nextToken(); 191 } 192 File copied_file = new File(copied_dir.getAbsolutePath()); 193 while(origin.hasMoreTokens()) { 194 copied_file = new File(copied_file, origin.nextToken()); 195 } 196 copy(file, copied_file); 197 copied_file = null; 198 origin = null; 199 destin = null; 200 } 201 } 202 file = null; 203 } 204 spd.setProgress(getValue(COPY_COLLECTION)); 205 205 // 4. Restore origin collection by undoing file copy actions and removing the ~ from the end of collection files. 206 206 ///ystem.err.println("4. Restore origin."); 207 207 Gatherer.c_man.undo.undoAll(); 208 208 ///ystem.err.println(" - UndoAll complete."); 209 209 // Recurse collection tree restoring *~ 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 210 files.clear(); // Should be empty anyway. 211 files.add(collection_file.getParentFile()); 212 while(files.size() > 0) { 213 File file = (File) files.get(0); 214 files.remove(0); 215 if(file.isDirectory()) { 216 File children[] = file.listFiles(); 217 for(int i = 0; i < children.length; i++) { 218 files.add(children[i]); 219 } 220 children = null; 221 } 222 else if(file.getName().endsWith("~")) { 223 String filename = file.getAbsolutePath(); 224 File original_file = new File(filename.substring(0, filename.length() - 1)); 225 ///ystem.err.println("Renaming " + filename); 226 if(!file.renameTo(original_file)) { 227 Gatherer.println("Error in SaveCollectionTask.run(): FileRenameException"); 228 } 229 } 230 } 231 231 ///ystem.err.println(" - Restore *~ complete."); 232 232 spd.setProgress(getValue(RESTORE_COLLECTION)); 233 233 // 5. Close the origin collection. 234 234 ///ystem.err.println("5. Close origin."); 235 236 237 235 collection.setSaved(true); 236 Gatherer.c_man.closeCollection(); 237 spd.setProgress(getValue(CLOSE_COLLECTION)); 238 238 // 6. Open the copied collection. 239 239 ///ystem.err.println("6. Open copy."); 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 240 Gatherer.c_man.loadCollection(copied_dir.getAbsolutePath() + File.separator + name + ".col"); 241 spd.setProgress(getValue(OPEN_COLLECTION)); 242 copied_dir = null; 243 } 244 spd.close(); 245 spd = null; 246 tmp_loc = null; 247 args = null; 248 // Reset undo queue. 249 if(Gatherer.c_man.ready()) { 250 Gatherer.c_man.undo.clear(); 251 } 252 Gatherer.g_man.wait(false); 253 254 // Now we are finished action any necessary action. 255 if(import_after) { 256 Gatherer.c_man.importCollection(); 257 } 258 if(close_after) { 259 Gatherer.c_man.closeCollection(); 260 } 261 ///ystem.err.println("Save Complete."); 262 if(exit_after) { 263 Gatherer.self.exit(); 264 } 265 } 266 267 public void setImportAfter(boolean state) { 268 import_after = true; 269 } 270 271 private void copy(File source, File destination) { 272 ///ystem.err.println("Copy: " + source.getAbsolutePath()); 273 ///ystem.err.println(" to: " + destination.getAbsolutePath()); 274 try { 275 destination.getParentFile().mkdirs(); 276 FileInputStream f_in = new FileInputStream(source); 277 FileOutputStream f_out = new FileOutputStream(destination); 278 byte data[] = new byte[Utility.BUFFER_SIZE]; 279 int data_size = 0; 280 while((data_size = f_in.read(data, 0, Utility.BUFFER_SIZE)) != -1) { 281 f_out.write(data, 0, data_size); 282 } 283 f_in.close(); 284 f_out.close(); 285 } 286 catch (Exception exception) { 287 Gatherer.printStackTrace(exception); 288 } 289 } 290 291 private int getValue(int reference) { 292 double multiplier; 293 if(name == null) { 294 multiplier = 1.0; 295 } 296 else { 297 multiplier = 0.25; 298 } 299 switch(reference) { 300 // Standard Save 301 case COLLECTION_SAVED: 302 return (int)((double)70 * multiplier); 303 case COLLECTION_CFG_SAVED: 304 case METADATA_SAVED: 305 case METADATA_XML_SAVED: 306 return (int)((double)10 * multiplier); 307 // Save As 308 case MAKE_COLLECTION: 309 return 5; 310 case COPY_COLLECTION: 311 return 30; 312 case RESTORE_COLLECTION: 313 return 30; 314 case CLOSE_COLLECTION: 315 return 10; 316 case OPEN_COLLECTION: 317 return 10; 318 } 319 return 0; 320 } 321 321 } 322 322 -
trunk/gli/src/org/greenstone/gatherer/file/FileAlreadyExistsException.java
r4293 r4366 4 4 5 5 public class FileAlreadyExistsException 6 6 extends Exception { 7 7 8 9 10 8 public FileAlreadyExistsException() { 9 super(); 10 } 11 11 } -
trunk/gli/src/org/greenstone/gatherer/file/FileAssociation.java
r4293 r4366 55 55 */ 56 56 public class FileAssociation { 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 57 /** The file extension, most likely three or four letters. */ 58 private String extension; 59 /** The command string, which may include %1 which should be replaced with the target files name. */ 60 private String command; 61 public FileAssociation(String extension, String command) { 62 this.command = command; 63 this.extension = extension; 64 } 65 /** Retrieve the command from this association. 66 * @return The command as a <strong>String</strong>. 67 */ 68 public String getCommand() { 69 return command; 70 } 71 /** Retrieve the extension from this association. 72 * @return The extension as a <strong>String</strong>. 73 */ 74 public String getExtension() { 75 return extension; 76 } 77 /** Set the command for this file association. 78 * @param command The new command as a <strong>String</strong>. 79 */ 80 public void setCommand(String command) { 81 this.command = command; 82 } 83 83 } -
trunk/gli/src/org/greenstone/gatherer/file/FileAssociationManager.java
r4293 r4366 49 49 /** Provides a manager for relating a filetype to a command to open that filetype, and an optional icon. Under windows this will attempt to leverage the Windows Registry. If this fails, or under other platforms, the user will be prompted to enter the required details manually. This information will then be stored for any further sessions of the Gatherer. Of course the ability to edit these settings must also be provided. */ 50 50 public class FileAssociationManager 51 51 extends HashMap { 52 52 53 53 static private final File ASSOC_FILE = new File(Utility.BASE_DIR, "associations.xml"); 54 54 55 56 57 58 59 60 55 public FileAssociationManager() { 56 // Attempt to load any previous file associations 57 if(ASSOC_FILE.exists()) { 58 load(); 59 } 60 } 61 61 62 63 64 62 public void destroy() { 63 save(); 64 } 65 65 66 67 68 69 70 71 72 73 74 75 76 66 public String getCommand(File file) { 67 String command = null; 68 String filename = file.toString(); 69 // Determine extension 70 int index = -1; 71 if((index = filename.lastIndexOf(".")) != -1) { 72 String extension = filename.substring(index + 1); 73 command = getCommand(file, extension); 74 } 75 return command; 76 } 77 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 78 public String getCommand(File file, String extension) { 79 // We first check to see if we already know the answer to how to run this program. 80 String command = (String) get(extension); 81 if(command != null) { 82 command = command + " " + file.getAbsolutePath(); 83 } 84 // If not, and we are on windows, try the registry. 85 if(command == null && Utility.isWindows()) { 86 command = WinRegistry.openCommand(file.getAbsolutePath()); 87 } 88 // Failing all that prompt the user to select the program to open this with. 89 if(command == null) { 90 FileAssociationDialog dialog = new FileAssociationDialog(this); 91 command = dialog.display(extension); 92 dialog.destroy(); 93 dialog = null; 94 if(command != null) { 95 put(extension, command); 96 command = command + " " + file.getAbsolutePath(); 97 } 98 } 99 ///ystem.err.println("Get command: " + extension + " = " + command); 100 return command; 101 } 102 102 103 104 105 103 public String getCommandImmediately(String extension) { 104 return (String) get(extension); 105 } 106 106 107 108 109 110 111 112 107 public void edit() { 108 FileAssociationDialog dialog = new FileAssociationDialog(this); 109 dialog.display(null); 110 dialog.destroy(); 111 dialog = null; 112 } 113 113 114 115 116 117 114 private void load() { 115 // Read in and parse the document. 116 Document document = Utility.parse(ASSOC_FILE, false); 117 if(document != null) { 118 118 // Recover all the entry nodes of the association root. 119 120 121 122 123 124 125 126 127 128 129 130 131 119 for(Node entry_n = document.getDocumentElement().getFirstChild(); entry_n != null; entry_n = entry_n.getNextSibling()) { 120 if(entry_n.getNodeName().equals("Entry")) { 121 Element entry_e = (Element) entry_n; 122 String extension = entry_e.getAttribute("extension"); 123 String command = MSMUtils.getValue(entry_e); 124 put(extension, command); 125 extension = null; 126 command = null; 127 entry_e = null; 128 } 129 } 130 } 131 } 132 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 133 private void save() { 134 // Only save if there is something to save. 135 if(size() > 0) { 136 StringBuffer assoc = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); 137 assoc.append("<!DOCTYPE Associations [\n"); 138 assoc.append(" <!ELEMENT Associations (Entry*)>\n"); 139 assoc.append(" <!ELEMENT Entry (#PCDATA)>\n"); 140 assoc.append(" <!ATTLIST Entry\n"); 141 assoc.append(" extension CDATA #REQUIRED>\n"); 142 assoc.append("]>\n"); 143 assoc.append("\n"); 144 assoc.append("<Associations>\n"); 145 for(Iterator keys = keySet().iterator(); keys.hasNext(); ) { 146 String extension = (String) keys.next(); 147 String command = (String) get(extension); 148 assoc.append("\t<Entry extension=\""); 149 assoc.append(extension); 150 assoc.append("\">"); 151 assoc.append(command); 152 assoc.append("</Entry>\n"); 153 } 154 assoc.append("</Associations>"); 155 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 156 String assoc_str = assoc.toString(); 157 assoc = null; 158 try { 159 FileOutputStream fos = new FileOutputStream(ASSOC_FILE); 160 OutputStreamWriter osw = new OutputStreamWriter(fos, Charset.forName("UTF-8")); 161 int position = 0; 162 int BLOCK_SIZE = 1024; 163 // Write x block sized chunks to file. 164 while(position + BLOCK_SIZE < assoc_str.length()) { 165 osw.write(assoc_str, position, BLOCK_SIZE); 166 position = position + BLOCK_SIZE; 167 } 168 // Write the remainder of the buffer. 169 if(position < assoc_str.length()) { 170 osw.write(assoc_str, position, assoc_str.length() - position); 171 } 172 osw.flush(); 173 osw.close(); 174 osw = null; 175 fos.close(); 176 fos = null; 177 assoc_str = null; 178 } 179 catch (Exception error) { 180 Gatherer.printStackTrace(error); 181 } 182 } 183 } 184 184 185 186 187 188 189 190 191 192 193 185 public void setCommand(String extension, String command) { 186 ///ystem.err.println("Set command: " + extension + " = " + command); 187 if(command == null || command.length() == 0) { 188 remove(extension); 189 } 190 else { 191 put(extension, command); 192 } 193 } 194 194 } 195 195 -
trunk/gli/src/org/greenstone/gatherer/file/FileFilter.java
r4293 r4366 2 2 3 3 public class FileFilter { 4 5 6 7 8 9 4 public boolean exclude; 5 public String filter; 6 public FileFilter(String filter, boolean exclude) { 7 this.exclude = exclude; 8 this.filter = filter; 9 } 10 10 } -
trunk/gli/src/org/greenstone/gatherer/file/FileJob.java
r4293 r4366 35 35 */ 36 36 public class FileJob { 37 38 37 /** true to mark that this file has already been copied, false otherwise. */ 38 public boolean done = false; 39 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 40 public boolean folder_level = false; 41 /** true if this should generate an undo event, false for a redo one. */ 42 public boolean undo = true; 43 /** true if this job should generate an undo event of any kind. */ 44 public boolean undoable = false; 45 /** The type of this movement as an byte. */ 46 public byte type = 0; 47 /** The DragComponent source of this file, most likely a GTree. */ 48 public DragComponent source = null; 49 /** The DragComponent to move the file to, again most likely a GTree. */ 50 public DragComponent target = null; 51 /** The unique identifier shared by all jobs created by the same action. */ 52 private long id = 0; 53 /** The path to the destination node as a string. Used because any reference based path or variables quickly becomes obsolete. */ 54 private TreePath destination_path = null; 55 /** The path to the origin node as a string. Used because any reference based path or variables quickly becomes obsolete. */ 56 private TreePath origin_path = null; 57 /** An element of the job type enumeration indicating a copy action. */ 58 static final public byte COPY = 1; 59 /** An element of the job type enumeration indicating a delete action. */ 60 static final public byte DELETE = 2; 61 /** An element of the job type enumeration indicating a move action. */ 62 static final public byte MOVE = 3; 63 /** Constructor. 64 * @param id A unique identifier for this job (and others created with a single gesture) as a long. 65 * @param source The DragComponent source of this file, most likely a GTree. 66 * @param orig The FileNode you wish to mode. 67 * @param target The DragComponent to move the file to, again most likely a GTree. 68 * @param dest The files new FileNode parent within the target. 69 * @param type The type of this movement as an int, either COPY or DELETE. 70 * @param undo true if this job some create an undo job when actioned, false for a redo job. 71 * @param undoable true if this job can generate undo or redo jobs, false otherwise. 72 */ 73 public FileJob(long id, DragComponent source, FileNode orig, DragComponent target, FileNode dest, byte type, boolean undo, boolean undoable) { 74 this.id = id; 75 this.source = source; 76 this.target = target; 77 this.type = type; 78 this.undo = undo; 79 this.undoable = undoable; 80 ///ystem.err.println("New Job: " + type + ", " + source + ", " + target); 81 // Dont store FileNodes which can go stale. Store paths instead, which are used to locate current 'fresh' versions of nodes. 82 if(dest != null) { 83 this.destination_path = new TreePath(dest.getPath()); 84 84 ///ystem.err.println("Destination Path: " + destination_path); 85 86 87 85 } 86 if(orig != null) { 87 this.origin_path = new TreePath(orig.getPath()); 88 88 ///ystem.err.println("Origin Path: " + origin_path); 89 90 91 92 93 94 95 96 97 98 89 } 90 } 91 /** Retrieve the destination node. Watch out for stale versions by always attempting to load the node at destination_path first. */ 92 public FileNode getDestination() { 93 FileNode destination = null; 94 if(destination_path != null) { 95 if(target != null) { 96 FileSystemModel model = (FileSystemModel)target.getTreeModel(); 97 destination = model.getNode(destination_path); 98 } 99 99 // If the above fails, a stale copy may be better than nothing. 100 101 102 103 104 105 106 107 108 109 110 111 112 113 100 else { 101 destination = (FileNode) destination_path.getLastPathComponent(); 102 } 103 } 104 return destination; 105 } 106 /** Retrieve the origin node. Watch out for stale versions by always attempting to load the node at origin_path first. */ 107 public FileNode getOrigin() { 108 FileNode origin = null; 109 if(origin_path != null) { 110 if(source != null) { 111 FileSystemModel model = (FileSystemModel)source.getTreeModel(); 112 origin = model.getNode(origin_path); 113 } 114 114 // If the above fails, a stale copy may be better than nothing. 115 116 117 118 119 120 121 122 123 124 115 else { 116 origin = (FileNode) origin_path.getLastPathComponent(); 117 } 118 } 119 return origin; 120 } 121 /** Retrieve the id for this job. */ 122 public long ID() { 123 return id; 124 } 125 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 126 public String toString() { 127 StringBuffer text = new StringBuffer(""); 128 switch(type) { 129 case COPY: 130 text.append("copy "); 131 break; 132 case DELETE: 133 text.append("delete "); 134 break; 135 case MOVE: 136 text.append("move "); 137 break; 138 default: 139 text.append("unknown "); 140 } 141 FileNode origin = getOrigin(); 142 if(origin != null) { 143 text.append(origin.getFile().getAbsolutePath()); 144 } 145 else { 146 text.append("ERROR!"); 147 } 148 text.append(" -> "); 149 FileNode destination = getDestination(); 150 if(destination != null) { 151 text.append(destination.getFile().getAbsolutePath()); 152 } 153 else { 154 text.append("Recycle Bin"); 155 } 156 return text.toString(); 157 } 158 158 } -
trunk/gli/src/org/greenstone/gatherer/file/FileManager.java
r4293 r4366 51 51 */ 52 52 public class FileManager { 53 54 55 56 57 58 59 60 61 53 /** Not only the queue of files to be moved, but also the object that moves them. */ 54 private FileQueue queue = null; 55 /** Constructor. 56 * @see org.greenstone.gatherer.file.FileQueue 57 */ 58 public FileManager() { 59 queue = new FileQueue(false); 60 queue.start(); 61 } 62 62 63 64 65 66 67 68 69 70 71 63 /** Given the arguments, determine what action should be carried out by the file queue, and add all of the necessary file jobs. */ 64 public void action(DragComponent source, FileNode[] source_nodes, DragComponent target, FileNode target_node) { 65 byte type = 0; 66 // If source and target are the same we are moving 67 if(source == target) { 68 type = FileJob.MOVE; 69 } 70 // If source and target are different 71 else { 72 72 // If target is the UndoManager, we're deleting 73 74 75 76 77 78 79 80 81 82 83 73 if(target instanceof UndoManager) { 74 // If the source is the workspace then display an error message. Workspace is read only. 75 if(source.toString().equals("Workspace")) { 76 JOptionPane.showMessageDialog(Gatherer.g_man, Gatherer.dictionary.get("FileActions.Read_Only"), Gatherer.dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE); 77 return; 78 } 79 // Normal delete. Go ahead. 80 else { 81 type = FileJob.DELETE; 82 } 83 } 84 84 // Otherwise we are copying 85 86 87 88 89 90 91 85 else { 86 type = FileJob.COPY; 87 } 88 } 89 Task task = new Task(System.currentTimeMillis(), source, source_nodes, target, target_node, type); 90 SwingUtilities.invokeLater(task); 91 } 92 92 93 94 95 96 93 /** Retrieves the file queue object. */ 94 public FileQueue getQueue() { 95 return queue; 96 } 97 97 98 99 100 101 102 103 104 105 106 107 98 public void newFolder(DragTree tree, FileNode parent_node) { 99 // Ask the user for the directories name. 100 NewFolderPrompt new_folder_prompt = new NewFolderPrompt(parent_node); 101 String name = new_folder_prompt.display(); 102 new_folder_prompt.dispose(); 103 new_folder_prompt = null; 104 // And if the name is non-null... 105 if(name != null) { 106 FileSystemModel model = (FileSystemModel) tree.getModel(); 107 File folder_file = new File(parent_node.getFile(), name); 108 108 //... check if it already exists. 109 110 111 109 if(folder_file.exists()) { 110 JOptionPane.showMessageDialog(Gatherer.g_man, Gatherer.dictionary.get("FileActions.Folder_Already_Exists", name), Gatherer.dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE); 111 } 112 112 // Otherwise create it. 113 114 115 116 117 118 119 120 121 122 123 113 else { 114 folder_file.mkdirs(); 115 FileNode folder_node = new FileNode(folder_file); 116 SynchronizedTreeModelTools.insertNodeInto(model, parent_node, folder_node); 117 folder_node = null; 118 } 119 folder_file = null; 120 model = null; 121 } 122 name = null; 123 } 124 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 125 private class Task 126 implements Runnable { 127 private byte type; 128 private DragComponent source; 129 private DragComponent target; 130 private FileNode target_node; 131 private FileNode[] source_nodes; 132 private long id; 133 public Task(long id, DragComponent source, FileNode[] source_nodes, DragComponent target, FileNode target_node, byte type) { 134 this.id = id; 135 this.source = source; 136 this.source_nodes = source_nodes; 137 this.target = target; 138 this.target_node = target_node; 139 this.type = type; 140 } 141 public void run() { 142 142 // Reset, and calculate progress bar size. 143 143 queue.calculateSize(source_nodes); 144 144 // Now we queue the job(s). Note that this may fail if a read only file is encountered and we have been asked to delete. 145 146 147 148 149 145 for(int i = 0; source_nodes != null && i < source_nodes.length; i++) { 146 queue.addJob(id, source, source_nodes[i], target, target_node, type, true, true, true); 147 } 148 } 149 } 150 150 } 151 151 -
trunk/gli/src/org/greenstone/gatherer/file/FileNode.java
r4293 r4366 10 10 11 11 public class FileNode 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 12 implements MutableTreeNode { 13 14 static final private int FALSE = 0; 15 static final private int TRUE = 1; 16 static final private int UNKNOWN = 2; 17 18 private ArrayList children; 19 private boolean children_readonly = true; 20 private boolean readonly = true; 21 private File file; 22 private FileSystemModel model; 23 private int allows_children = UNKNOWN; 24 private MutableTreeNode parent; 25 private String title; 26 27 public FileNode(File file) { 28 ///ystem.err.println("New FileNode(" + file.getAbsolutePath() + ")"); 29 this.file = file; 30 } 31 32 public FileNode(File file, boolean readonly) { 33 this(file); 34 this.children_readonly = readonly; 35 this.readonly = readonly; 36 } 37 38 public FileNode(File file, FileSystemModel model) { 39 this(file); 40 this.model = model; 41 } 42 43 public FileNode(File file, FileSystemModel model, boolean readonly) { 44 this(file, readonly); 45 this.model = model; 46 } 47 48 public FileNode(File file, FileSystemModel model, String title) { 49 this(file, model); 50 this.title = title; 51 } 52 53 public FileNode(File file, String title) { 54 this(file); 55 this.title = title; 56 } 57 58 public FileNode(File file, String title, boolean readonly) { 59 this(file, readonly); 60 this.title = title; 61 } 62 63 public FileNode(File file, FileSystemModel model, String title, boolean readonly) { 64 this(file, model, readonly); 65 this.title = title; 66 } 67 68 /** The special 'dummy' root node, that is not based on a particular file, but instead holds several special directory mappings. */ 69 public FileNode(String title) { 70 this.children = new ArrayList(); 71 this.title = title; 72 } 73 74 /** Returns the children of the receiver as an Enumeration. */ 75 public Enumeration children() { 76 return new FileEnumeration(); 77 } 78 78 79 79 /** Compare two filenodes for equality. */ 80 80 public boolean equals(FileNode node) { 81 82 83 84 85 86 87 88 89 90 81 boolean result = false; 82 if(node != null) { 83 if(file != null) { 84 result = (file.equals(node.getFile())); 85 } 86 else { 87 result = toString().equals(node.toString()); 88 } 89 } 90 return result; 91 91 } 92 92 93 93 /** Returns true if the receiver allows children. We have to cache the result of this call to prevent unceasing missing disk messages being thrown if the removable media was, um, removed after directory mapped. */ 94 94 public boolean getAllowsChildren() { 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 95 if(readonly) { 96 if(allows_children == UNKNOWN) { 97 // If the file is non-null but doesn't exist (as is the case for removable media), return true anyway. 98 if(file != null) { 99 if(FileSystemView.getFileSystemView().isFileSystemRoot(file)) { 100 allows_children = TRUE; 101 } 102 else if(file.exists() && file.isDirectory()) { 103 allows_children = TRUE; 104 } 105 // Any mapped directories always allow children. 106 else if(getParent() != null && getParent().getParent() == null) { 107 allows_children = TRUE; 108 } 109 else { 110 allows_children = FALSE; 111 } 112 } 113 // Allows children is always true for dummy nodes. 114 else { 115 allows_children = TRUE; 116 } 117 } 118 return (allows_children == TRUE); 119 } 120 else { 121 return (file == null || file.isDirectory()); 122 } 123 123 } 124 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 125 /** Returns the child TreeNode at index childIndex. */ 126 public TreeNode getChildAt(int index) { 127 TreeNode result = null; 128 map(); 129 if(0 <= index && index < children.size()) { 130 result = (TreeNode) children.get(index); 131 } 132 else { 133 result = new DefaultMutableTreeNode("Error"); 134 } 135 return result; 136 } 137 138 /** Returns the number of children TreeNodes the receiver contains. */ 139 public int getChildCount() { 140 int size = 0; 141 // We don't automatically map if this is a system root, or we risk the 50,000 Disk not found error messages of death. 142 if(FileSystemView.getFileSystemView().isFileSystemRoot(file)) { 143 size = 1; // Size is always non-zero for a system root 144 } 145 else { 146 map(); 147 } 148 ///ystem.err.println(this + ".getChildCount() = " + children.size()); 149 if(children != null) { 150 size = children.size(); 151 } 152 return size; 153 } 154 155 public File getFile() { 156 return file; 157 } 158 159 /** Returns the index of node in the receivers children. */ 160 public int getIndex(TreeNode node) { 161 map(); 162 return children.indexOf(node); 163 } 164 165 /** Returns the parent TreeNode of the receiver. */ 166 public TreeNode getParent() { 167 return parent; 168 } 169 170 /** Retrieves the tree path from the root node to this node. */ 171 public TreeNode[] getPath() { 172 int count = 0; 173 TreeNode current = this; 174 while(current != null) { 175 count++; 176 current = current.getParent(); 177 } 178 TreeNode[] path = new TreeNode[count]; 179 current = this; 180 while(current != null) { 181 path[count - 1] = current; 182 count--; 183 current = current.getParent(); 184 } 185 return path; 186 } 187 188 public void insert(MutableTreeNode child) { 189 insert(child, children.size()); 190 } 191 192 /** Adds child to the receiver at index. */ 193 public void insert(MutableTreeNode child, int index) { 194 ///ystem.err.println("Insert " + child + " in " + this + " at index " + index); 195 //map(); 196 try { 197 children.add(index, child); 198 198 // Set parent and model. 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 199 FileNode new_child = (FileNode) child; 200 new_child.setModel(model); 201 new_child.setParent(this); 202 new_child.setReadOnly(readonly); 203 } 204 catch(Exception error) { 205 error.printStackTrace(); 206 } 207 } 208 209 /** Returns true if the receiver is a leaf. */ 210 public boolean isLeaf() { 211 return !getAllowsChildren(); 212 } 213 214 public boolean isReadOnly() { 215 return readonly; 216 } 217 218 public void map() { 219 // Only map if there are no children. 220 if(children == null && file != null && getAllowsChildren()) { 221 221 ///ystem.err.println("Map: " + this); 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 222 children = new ArrayList(); 223 File[] files = file.listFiles(); 224 if(files != null && files.length > 0) { 225 ArrayTools tools = new ArrayTools(); 226 // Apply the filters set in the model. 227 FileFilter[] filters = model.getFilters(); 228 for(int i = 0; filters != null && i < filters.length; i++) { 229 files = tools.filter(files, filters[i].filter, filters[i].exclude); 230 } 231 // Sort the remaining files. 232 tools.sort(files, true); 233 // Now add them to children. 234 for(int i = 0; i < files.length; i++) { 235 FileNode child = new FileNode(files[i], model, children_readonly); 236 child.setParent(this); 237 children.add(child); 238 } 239 } 240 model.nodeStructureChanged(this); 241 } 242 else { 243 ///ystem.err.println("Can't map: " + this + ". No file or doesn't allow children."); 244 } 245 } 246 247 /** Removes the child at index from the receiver. */ 248 public void remove(int index) { 249 if(0 <= index && index < children.size()) { 250 children.remove(index); 251 } 252 } 253 254 /** Removes node from the receiver. */ 255 public void remove(MutableTreeNode node){ 256 int index = getIndex(node); 257 if(index != -1) { 258 children.remove(index); 259 } 260 } 261 262 /** Removes the receiver from its parent. */ 263 public void removeFromParent() { 264 parent.remove(this); 265 parent = null; 266 } 267 268 public void setChildrenReadOnly(boolean children_readonly) { 269 this.children_readonly = children_readonly; 270 } 271 272 public void setFile(File file) { 273 this.file = file; 274 } 275 276 public void setModel(FileSystemModel model) { 277 this.model = model; 278 } 279 280 public void setParent(MutableTreeNode parent) { 281 this.parent = parent; 282 } 283 284 public void setReadOnly(boolean readonly) { 285 this.readonly = readonly; 286 } 287 288 /** Resets the user object of the receiver to object. */ 289 public void setUserObject(Object object) { 290 try { 291 file = (File) object; 292 title = null; 293 } 294 catch(Exception error) { 295 error.printStackTrace(); 296 } 297 } 298 299 public String toString() { 300 if(title == null) { 301 if(FileSystemView.getFileSystemView().isFileSystemRoot(file)) { 302 title = file.getAbsolutePath(); 303 } 304 else { 305 title = file.getName(); 306 } 307 } 308 return title; 309 } 310 311 /** Unmap this nodes children. */ 312 public void unmap() { 313 // You cannot unmap nodes that have no file basis. 314 if(file != null) { 315 315 ///ystem.err.println("Unmap: " + this); 316 317 318 316 children = null; 317 } 318 else { 319 319 ///ystem.err.println("No file for " + this + " - can't unmap."); 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 320 } 321 } 322 323 private class FileEnumeration 324 implements Enumeration { 325 private int index = 0; 326 /** Tests if this enumeration contains more elements. */ 327 public boolean hasMoreElements() { 328 return (index < children.size()); 329 } 330 /** Returns the next element of this enumeration if this enumeration object has at least one more element to provide. */ 331 public Object nextElement() { 332 Object result = null; 333 if(index < children.size()) { 334 result = children.get(index); 335 index++; 336 } 337 return result; 338 } 339 } 340 340 } -
trunk/gli/src/org/greenstone/gatherer/file/FileOpenActionListener.java
r4293 r4366 48 48 */ 49 49 public class FileOpenActionListener 50 51 52 53 54 55 56 57 50 extends MouseAdapter 51 implements TreeExpansionListener { 52 /** This flag gets toggled to <i>true</i> if the listener determines that the next mouse clicked event it will recieve is actually caused by the tree expanding or collapsing. */ 53 private boolean ignore = false; 54 /** The constructor. */ 55 public FileOpenActionListener() { 56 } 57 /** Any subclass of MouseAdapter can override this method to respond to mouse click events. In this case we want to start an external application if someone double clicks on an appropriate file record. 58 58 * @param event A <strong>MouseEvent</strong> containing further information about the mouse click performed. 59 59 * @see org.greenstone.gatherer.Gatherer … … 61 61 * @see org.greenstone.gatherer.tree.GTree 62 62 */ 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 63 public void mouseClicked(MouseEvent event) { 64 ///ystem.err.println("Mouse clicked"); 65 if(!ignore) { 66 if(event.getClickCount() >= 2) { 67 // Find the file we're clicking on. 68 JTree tree = (JTree)event.getSource(); 69 TreePath path = tree.getClosestPathForLocation(event.getX(), event.getY()); 70 if(path != null) { 71 FileNode record = (FileNode)path.getLastPathComponent(); 72 ///ystem.err.println("Double clicked on " + record); 73 File file = record.getFile(); 74 if(file != null && file.isFile()) { 75 ///ystem.err.println("Running " + file); 76 Gatherer.self.spawnApplication(file); 77 } 78 } 79 } 80 } 81 else { 82 82 ///ystem.err.println("Caused by tree expansion / collapse. Ignoring."); 83 84 85 86 83 ignore = false; 84 } 85 } 86 /** Any implementation of TreeExpansionListener must include this method so that we can be informed when a tree node has been collapsed, thus indicating that any mouse click events about to be recieved are most likely related to this event. 87 87 * @param event A <strong>TreeExpansionEvent</strong> containing information about the node collapsed. 88 88 */ 89 90 91 92 93 89 public void treeCollapsed(TreeExpansionEvent event) { 90 ///ystem.err.println("Tree Collapsed"); 91 ignore = true; 92 } 93 /** Any implementation of TreeExpansionListener must include this method so that we can be informed when a tree node has been expanded, thus indicating that any mouse click events about to be recieved are most likely related to this event. 94 94 * @param event A <strong>TreeExpansionEvent</strong> containing information about the node expanded. 95 95 */ 96 97 98 99 96 public void treeExpanded(TreeExpansionEvent event) { 97 ///ystem.err.println("Tree Expanded"); 98 ignore = true; 99 } 100 100 } -
trunk/gli/src/org/greenstone/gatherer/file/FileQueue.java
r4293 r4366 58 58 */ 59 59 public class FileQueue 60 60 extends Thread 61 61 implements TreeSelectionListener { 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 62 /** When someone requests the movement queue to be dumped this cancel flag is set to true. */ 63 private boolean cancel_action = false; 64 /** A temporary mapping from currently existing FileNode folder to their equivelent FileNode folder within the undo managers tree. */ 65 private HashMap completed_folder_mappings = new HashMap(); 66 /** true to cause this file queue to return from run() as soon as there are no jobs left on the queue. Useful for undo jobs which must occur before a specific action. */ 67 private boolean return_immediately = false; 68 /** We are only allowed to wait under specific circumstances. */ 69 private boolean wait_allowed = true; 70 /** true if the user has selected yes to all from a file 'clash' dialog. */ 71 private boolean yes_to_all = false; 72 /** A temporary mapping from currently existing FileNodes to the potential FileNode folder within the undo managers tree. */ 73 private HashMap recycle_folder_mappings = new HashMap(); 74 /** A label explaining the current moving files status. */ 75 private SmudgyLabel file_status = null; 76 /** A label explaining the status of this job. */ 77 private JLabel job_status = null; 78 /** A list containing a queue of waiting movement jobs. */ 79 //private LinkedList queue; 80 private ArrayList queue; 81 /** A progress bar which shows how many bytes, out of the total size of bytes, has been moved. */ 82 private LongProgressBar progress = null; 83 /** The last piece of text shown on the file status label, just incase we are displaying a very temporary message. */ 84 private String previous = null; 85 /** Constructor. 86 86 * @param return_immediately true to cause this file queue to return from run() as soon as there are no jobs left on the queue. 87 87 * @see org.greenstone.gatherer.Configuration … … 89 89 * @see org.greenstone.gatherer.gui.LongProgressBar 90 90 */ 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 91 public FileQueue(boolean return_immediately) { 92 this.return_immediately = return_immediately; 93 this.queue = new ArrayList();//LinkedList(); 94 String args[] = new String[2]; 95 args[0] = "0"; 96 args[1] = "0"; 97 file_status = new SmudgyLabel(get("Selected", args)); 98 job_status = new JLabel(get("No_Activity")); 99 progress = new LongProgressBar(); 100 progress.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 101 progress.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 102 progress.setString(get("No_Activity")); 103 progress.setStringPainted(true); 104 args = null; 105 } 106 107 /** Requeue an existing job into the queue. 108 108 * @param job A previously created FileJob. 109 109 */ 110 111 112 113 114 115 116 110 synchronized public void addJob(FileJob job, int position) { 111 job.done = true; // Ensure that the requeued job is marked as done. 112 queue.add(position, job); 113 notify(); 114 } 115 116 /** Add a new job to the queue, specifiying as many arguments as is necessary to complete this type of job (ie delete needs no target information). 117 117 * @param id A long id unique to all jobs created by a single action. 118 118 * @param source The DragComponent source of this file, most likely a DragTree. … … 124 124 * @param undoable true if this job can generate undo or redo jobs at all, false otherwise. 125 125 */ 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 126 public void addJob(long id, DragComponent source, FileNode child, DragComponent target, FileNode parent, byte type, boolean undo, boolean undoable, boolean folder_level) { 127 addJob(id, source, child, target, parent, type, undo, undoable, folder_level, -1); 128 } 129 synchronized public void addJob(long id, DragComponent source, FileNode child, DragComponent target, FileNode parent, byte type, boolean undo, boolean undoable, boolean folder_level, int position) { 130 FileJob job = new FileJob(id, source, child, target, parent, type, undo, undoable); 131 job.folder_level = folder_level; 132 ///ystem.err.println("Adding job: " + job); 133 if(position != -1 && position <= queue.size() + 1) { 134 queue.add(position, job); 135 } 136 else { 137 queue.add(job); 138 } 139 notify(); 140 } 141 142 public void calculateSize(FileNode[] files) { 143 progress.reset(); 144 progress.setString(get("FileActions.Calculating_Size")); 145 progress.setIndeterminate(true); 146 Vector remaining = new Vector(); 147 for(int i = 0; i < files.length; i++) { 148 remaining.add(files[i]); 149 } 150 while(remaining.size() > 0) { 151 FileNode node = (FileNode)remaining.remove(0); 152 if(node.isLeaf()) { 153 progress.addMaximum(node.getFile().length()); 154 } 155 else { 156 for(int i = 0; i < node.getChildCount(); i++) { 157 remaining.add(node.getChildAt(i)); 158 } 159 } 160 } 161 progress.setString(get("No_Activity")); 162 progress.setIndeterminate(false); 163 } 164 165 /** This method is called to cancel the job queue at the next available moment. */ 166 public void cancelAction() { 167 cancel_action = true; 168 } 169 /** Access to the file state label. */ 170 public JLabel getFileStatus() { 171 return file_status; 172 } 173 /** Access to the job state label. */ 174 public JLabel getJobStatus() { 175 return job_status; 176 } 177 /** Access to the progress bar. */ 178 public LongProgressBar getProgress() { 179 return progress; 180 } 181 /** Prevent the progress bar updating momentarily, while the progress bar size is re-adjusted. */ 182 public void pause() { 183 progress.setIndeterminate(true); 184 } 185 /** The run method exists in every thread, and here it is used to work its way through the queue of Jobs. If no jobs are waiting and it cans, it waits until a job arrives. If a job is present then it is either COPIED or DELETED, with the records being copied or removed as necessary, and directories being recursed through. Finally the user can press cancel to cause the loop to prematurely dump the job queue then wait. 186 186 * @see org.greenstone.gatherer.Gatherer 187 187 * @see org.greenstone.gatherer.collection.CollectionManager … … 193 193 * @see org.greenstone.gatherer.util.Utility 194 194 */ 195 public void run() { 196 super.setName("FileQueue"); 197 while(!Gatherer.self.exit) { 195 public void run() { 196 super.setName("FileQueue"); 197 while(!Gatherer.self.exit) { 198 try { 199 // Retrieve the next job 200 int position = queue.size() - 1; 201 FileJob job = removeJob(position); 202 if(job != null) { 203 ///ystem.err.println("Found job: " + job); 204 // The user can cancel this individual action at several places, so keep track if the state is 'ready' for the next step. 205 boolean ready = true; 206 FileNode origin_node = job.getOrigin(); 207 FileNode destination_node = job.getDestination(); 208 FileSystemModel source_model = (FileSystemModel)job.source.getTreeModel(); 209 FileSystemModel target_model = (FileSystemModel)job.target.getTreeModel(); 210 if(destination_node == null) { 211 // Retrieve the root node of the target model instead. A delete, or course, has no target file so all deleted files are added to the root of the Recycle Bin model. 212 destination_node = (FileNode) target_model.getRoot(); 213 } 214 // Extract common job details. 215 File source_file = origin_node.getFile(); 216 File target_file = null; 217 // Determine the target file for a copy or move. 218 if(job.type == FileJob.COPY || job.type == FileJob.MOVE) { 219 target_file = new File(destination_node.getFile(), source_file.getName()); 220 } 221 // To copy a file, copy it then add any metadata found at the source. If this file was already in our collection then we must ensure the lastest version of its metadata.xml has been saved to disk. To copy a directory simply create the directory at the destination, then add all of its children files as new jobs. 222 if((job.type == FileJob.COPY || job.type == FileJob.MOVE) && !job.done) { 223 ///ystem.err.println("Copy/Move: " + origin_node); 224 FileNode new_node = null; 225 // Check if file exists, and action as necessary. Be aware the user can choose to cancel the action all together (where upon ready becomes false). 226 if(target_file.exists()) { 227 // We've previously been told 228 if(yes_to_all) { 229 // Remove the old file and tree entry. 230 target_file.delete(); 231 ready = true; 232 } 233 else { 234 ///atherer.println("Opps! This filename already exists. Give the user some options."); 235 Object[] options = { get("General.Yes"), get("Yes_To_All"), get("General.No"), get("General.Cancel") }; 236 int result = JOptionPane.showOptionDialog(Gatherer.g_man, get("File_Exists", target_file.getName()), get("General.Warning"), JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); 237 switch(result) { 238 case 1: // Yes To All 239 yes_to_all = true; 240 case 0: // Yes 241 // Remove the old file and tree entry. 242 if(destination_node != null) { 243 TreePath destination_path = new TreePath(destination_node.getPath()); 244 FileNode temp_target_node = new FileNode(target_file, target_model, true); 245 TreePath target_path = destination_path.pathByAddingChild(temp_target_node); 246 SynchronizedTreeModelTools.removeNodeFromParent(target_model, target_model.getNode(target_path)); 247 target_path = null; 248 temp_target_node = null; 249 destination_path = null; 250 } 251 target_file.delete(); 252 ready = true; 253 break; 254 case 3: // No To All 255 cancel_action = true; 256 case 2: // No 257 default: 258 ready = false; 259 } 260 } 261 } 262 // We proceed with the copy/move if the ready flag is still set. If it is that means there is no longer any existing file of the same name. 263 if(ready) { 264 // update status area 265 String args[] = new String[1]; 266 args[0] = "" + (queue.size() + 1) + ""; 267 job_status.setText(get("Jobs", args)); 268 if(job.type == FileJob.COPY) { 269 args[0] = Utility.formatPath("FileActions.Copying", source_file.getAbsolutePath(), file_status.getSize().width); 270 file_status.setText(get("Copying", args)); 271 } 272 else { 273 args[0] = Utility.formatPath("FileActions.Moving", source_file.getAbsolutePath(), file_status.getSize().width); 274 file_status.setText(get("Moving", args)); 275 } 276 args = null; 277 file_status.setToolTipText(Utility.formatHTMLWidth(file_status.getText(), 80)); 278 // If source is a file 279 if(source_file.isFile()) { 280 // copy the file. If anything goes wrong the copy file should throw the appropriate exception. No matter what exception is thrown (bar an IOException) we display some message, perhaps take some action, then cancel the remainder of the pending file jobs. No point in being told your out of hard drive space for each one of six thousand files eh? 198 281 try { 199 // Retrieve the next job 200 int position = queue.size() - 1; 201 FileJob job = removeJob(position); 202 if(job != null) { 203 ///ystem.err.println("Found job: " + job); 204 // The user can cancel this individual action at several places, so keep track if the state is 'ready' for the next step. 205 boolean ready = true; 206 FileNode origin_node = job.getOrigin(); 207 FileNode destination_node = job.getDestination(); 208 FileSystemModel source_model = (FileSystemModel)job.source.getTreeModel(); 209 FileSystemModel target_model = (FileSystemModel)job.target.getTreeModel(); 210 if(destination_node == null) { 211 // Retrieve the root node of the target model instead. A delete, or course, has no target file so all deleted files are added to the root of the Recycle Bin model. 212 destination_node = (FileNode) target_model.getRoot(); 213 } 214 // Extract common job details. 215 File source_file = origin_node.getFile(); 216 File target_file = null; 217 // Determine the target file for a copy or move. 218 if(job.type == FileJob.COPY || job.type == FileJob.MOVE) { 219 target_file = new File(destination_node.getFile(), source_file.getName()); 220 } 221 // To copy a file, copy it then add any metadata found at the source. If this file was already in our collection then we must ensure the lastest version of its metadata.xml has been saved to disk. To copy a directory simply create the directory at the destination, then add all of its children files as new jobs. 222 if((job.type == FileJob.COPY || job.type == FileJob.MOVE) && !job.done) { 223 ///ystem.err.println("Copy/Move: " + origin_node); 224 FileNode new_node = null; 225 // Check if file exists, and action as necessary. Be aware the user can choose to cancel the action all together (where upon ready becomes false). 226 if(target_file.exists()) { 227 // We've previously been told 228 if(yes_to_all) { 229 // Remove the old file and tree entry. 230 target_file.delete(); 231 ready = true; 232 } 233 else { 234 ///atherer.println("Opps! This filename already exists. Give the user some options."); 235 Object[] options = { get("General.Yes"), get("Yes_To_All"), get("General.No"), get("General.Cancel") }; 236 int result = JOptionPane.showOptionDialog(Gatherer.g_man, get("File_Exists", target_file.getName()), get("General.Warning"), JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); 237 switch(result) { 238 case 1: // Yes To All 239 yes_to_all = true; 240 case 0: // Yes 241 // Remove the old file and tree entry. 242 if(destination_node != null) { 243 TreePath destination_path = new TreePath(destination_node.getPath()); 244 FileNode temp_target_node = new FileNode(target_file, target_model, true); 245 TreePath target_path = destination_path.pathByAddingChild(temp_target_node); 246 SynchronizedTreeModelTools.removeNodeFromParent(target_model, target_model.getNode(target_path)); 247 target_path = null; 248 temp_target_node = null; 249 destination_path = null; 250 } 251 target_file.delete(); 252 ready = true; 253 break; 254 case 3: // No To All 255 cancel_action = true; 256 case 2: // No 257 default: 258 ready = false; 259 } 260 } 261 } 262 // We proceed with the copy/move if the ready flag is still set. If it is that means there is no longer any existing file of the same name. 263 if(ready) { 264 // update status area 265 String args[] = new String[1]; 266 args[0] = "" + (queue.size() + 1) + ""; 267 job_status.setText(get("Jobs", args)); 268 if(job.type == FileJob.COPY) { 269 args[0] = Utility.formatPath("FileActions.Copying", source_file.getAbsolutePath(), file_status.getSize().width); 270 file_status.setText(get("Copying", args)); 271 } 272 else { 273 args[0] = Utility.formatPath("FileActions.Moving", source_file.getAbsolutePath(), file_status.getSize().width); 274 file_status.setText(get("Moving", args)); 275 } 276 args = null; 277 file_status.setToolTipText(Utility.formatHTMLWidth(file_status.getText(), 80)); 278 // If source is a file 279 if(source_file.isFile()) { 280 // copy the file. If anything goes wrong the copy file should throw the appropriate exception. No matter what exception is thrown (bar an IOException) we display some message, perhaps take some action, then cancel the remainder of the pending file jobs. No point in being told your out of hard drive space for each one of six thousand files eh? 281 try { 282 copyFile(source_file, target_file, progress); 283 } 284 // If we can't find the source file, then the most likely reason is that the file system has changed since the last time it was mapped. Warn the user that the requested file can't be found, then force a refresh of the source folder involved. 285 catch(FileNotFoundException fnf_exception) { 286 Gatherer.printStackTrace(fnf_exception); 287 cancel_action = true; 288 // Show warning. 289 JOptionPane.showMessageDialog(Gatherer.g_man, get("File_Not_Found_Message", source_file.getName()), get("File_Not_Found_Title"), JOptionPane.ERROR_MESSAGE); 290 // Force refresh of source folder. 291 source_model.refresh(new TreePath(((FileNode)origin_node.getParent()).getPath())); 292 } 293 catch(FileAlreadyExistsException fae_exception) { 294 Gatherer.printStackTrace(fae_exception); 295 cancel_action = true; 296 // Show warning. 297 JOptionPane.showMessageDialog(Gatherer.g_man, get("File_Already_Exists_Message", target_file.getName()), get("File_Already_Exists_Title"), JOptionPane.ERROR_MESSAGE); 298 // Nothing else can be done by the Gatherer. 299 } 300 catch(InsufficientSpaceException is_exception) { 301 Gatherer.printStackTrace(is_exception); 302 cancel_action = true; 303 // Show warning. The message body of the expection explains how much more space is required for this file copy. 304 JOptionPane.showMessageDialog(Gatherer.g_man, get("Insufficient_Space_Message", is_exception.getMessage()), get("Insufficient_Space_Title"), JOptionPane.ERROR_MESSAGE); 305 // Nothing else can be done by the Gatherer. In fact if we are really out of space I'm not even sure we can quit safely. 306 } 307 catch(UnknownFileErrorException ufe_exception) { 308 Gatherer.printStackTrace(ufe_exception); 309 cancel_action = true; 310 // Show warning 311 JOptionPane.showMessageDialog(Gatherer.g_man, get("Unknown_File_Error_Message"), get("Unknown_File_Error_Title"), JOptionPane.ERROR_MESSAGE); 312 // Nothing else we can do. 313 } 314 catch(IOException exception) { 315 // Can't really do much about this. 316 Gatherer.printStackTrace(exception); 317 } 318 // If not cancelled 319 if(!cancel_action) { 320 // Step one is to create a dummy FileNode. Its important it has the correct structure so getPath works. 321 FileNode new_record = new FileNode(target_file); 322 SynchronizedTreeModelTools.insertNodeInto(target_model, destination_node, new_record); 323 new_node = new_record; 324 325 // create undo job 326 if(job.undoable) { 327 job.undoable = false; 328 if(job.type == FileJob.COPY) { 329 // A copy is undone with a delete, so it doesn't really matter where the file originally came from (we're not moving it back there, but into the recycle bin). You may also notice we don't make use of the target parent record. This is because no undo action needs this information, and even if it did it could simply ask for records parent! 330 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_COPY, null, null, job.target, new_record, job.undo); 331 } 332 else { 333 // Movements however do need a source and source parent so the file can be moved back to the correct place. 334 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_MOVE, job.source, (FileNode)origin_node.getParent(), job.target, new_record, job.undo); 335 } 336 } 337 new_record = null; 338 } 339 } 340 // Else 341 else if(source_file.isDirectory()) { 342 // create new record 343 FileNode directory_record = new FileNode(target_file); 344 ///ystem.err.println("Directory record = " + directory_record + " (" + target_file.getAbsolutePath() + ")"); 345 SynchronizedTreeModelTools.insertNodeInto(target_model, destination_node, directory_record); 346 // Why is this not happening eh? 347 directory_record.setParent(destination_node); 348 if(!target_file.exists()) { 349 // make the directory 350 target_file.mkdirs(); 351 new_node = directory_record; 352 // create undo job 353 if(job.undoable) { 354 job.undoable = false; 355 if(job.type == FileJob.COPY) { 356 // A copy is undone with a delete, so it doesn't really matter where the file originally came from (we're not moving it back there, but into the recycle bin). You may also notice we don't make use of the target parent record. This is because no undo action needs this information, and even if it did it could simply ask for records parent! 357 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_COPY, null, null, job.target, directory_record, job.undo); 358 } 359 else { 360 // Movements however do need a source and source parent so the file can be moved back to the correct place. 361 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_MOVE, job.source, (FileNode)origin_node.getParent(), job.target, directory_record, job.undo); 362 } 363 } 364 } 365 // Else inform the users that a directory already exists and files will be copied into it 366 else { 367 JOptionPane.showMessageDialog(null, get("Directory_Exists", target_file.toString()), get("General.Warning"), JOptionPane.WARNING_MESSAGE); 368 } 369 // Queue non-filtered child files for copying. If this directory already existed, the child records will have to generate the undo jobs, as we don't want to entirely delete this directory if it already existed. 370 FileNode child_record = null; 371 // In order to have a sane copy proceedure (rather than always copying last file first as it used to) we always add the child node at the position the parent was removed from. Consider the file job 'a' at the end of the queue which generates three new jobs 'b', 'c' and 'd'. The resulting flow should look like this. 372 // -- Starting queue ...[a] 373 // remove(position) = 'a' ... 374 // add(position, 'b') ...[b] 375 // add(position, 'c') ...[c][b] 376 // add(position, 'd') ...[d][c][b] 377 // Next loop 378 // remove(position) = 'b' ...[d][c] 379 for(int i = 0; i < origin_node.getChildCount(); i++) { 380 child_record = (FileNode) origin_node.getChildAt(i); 381 addJob(job.ID(), job.source, child_record, job.target, directory_record, job.type, job.undo, false, false, position); 382 } 383 child_record = null; 384 directory_record = null; 385 } 386 // The file wasn't found! 387 else { 388 cancel_action = true; 389 // Show warning. 390 JOptionPane.showMessageDialog(Gatherer.g_man, get("File_Not_Found_Message", source_file.getName()), get("File_Not_Found_Title"), JOptionPane.ERROR_MESSAGE); 391 // Force refresh of source folder. 392 source_model.refresh(new TreePath(((FileNode)origin_node.getParent()).getPath())); 393 } 282 copyFile(source_file, target_file, progress); 283 } 284 // If we can't find the source file, then the most likely reason is that the file system has changed since the last time it was mapped. Warn the user that the requested file can't be found, then force a refresh of the source folder involved. 285 catch(FileNotFoundException fnf_exception) { 286 Gatherer.printStackTrace(fnf_exception); 287 cancel_action = true; 288 // Show warning. 289 JOptionPane.showMessageDialog(Gatherer.g_man, get("File_Not_Found_Message", source_file.getName()), get("File_Not_Found_Title"), JOptionPane.ERROR_MESSAGE); 290 // Force refresh of source folder. 291 source_model.refresh(new TreePath(((FileNode)origin_node.getParent()).getPath())); 292 } 293 catch(FileAlreadyExistsException fae_exception) { 294 Gatherer.printStackTrace(fae_exception); 295 cancel_action = true; 296 // Show warning. 297 JOptionPane.showMessageDialog(Gatherer.g_man, get("File_Already_Exists_Message", target_file.getName()), get("File_Already_Exists_Title"), JOptionPane.ERROR_MESSAGE); 298 // Nothing else can be done by the Gatherer. 299 } 300 catch(InsufficientSpaceException is_exception) { 301 Gatherer.printStackTrace(is_exception); 302 cancel_action = true; 303 // Show warning. The message body of the expection explains how much more space is required for this file copy. 304 JOptionPane.showMessageDialog(Gatherer.g_man, get("Insufficient_Space_Message", is_exception.getMessage()), get("Insufficient_Space_Title"), JOptionPane.ERROR_MESSAGE); 305 // Nothing else can be done by the Gatherer. In fact if we are really out of space I'm not even sure we can quit safely. 306 } 307 catch(UnknownFileErrorException ufe_exception) { 308 Gatherer.printStackTrace(ufe_exception); 309 cancel_action = true; 310 // Show warning 311 JOptionPane.showMessageDialog(Gatherer.g_man, get("Unknown_File_Error_Message"), get("Unknown_File_Error_Title"), JOptionPane.ERROR_MESSAGE); 312 // Nothing else we can do. 313 } 314 catch(IOException exception) { 315 // Can't really do much about this. 316 Gatherer.printStackTrace(exception); 317 } 318 // If not cancelled 319 if(!cancel_action) { 320 // Step one is to create a dummy FileNode. Its important it has the correct structure so getPath works. 321 FileNode new_record = new FileNode(target_file); 322 SynchronizedTreeModelTools.insertNodeInto(target_model, destination_node, new_record); 323 new_node = new_record; 324 325 // create undo job 326 if(job.undoable) { 327 job.undoable = false; 328 if(job.type == FileJob.COPY) { 329 // A copy is undone with a delete, so it doesn't really matter where the file originally came from (we're not moving it back there, but into the recycle bin). You may also notice we don't make use of the target parent record. This is because no undo action needs this information, and even if it did it could simply ask for records parent! 330 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_COPY, null, null, job.target, new_record, job.undo); 331 } 332 else { 333 // Movements however do need a source and source parent so the file can be moved back to the correct place. 334 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_MOVE, job.source, (FileNode)origin_node.getParent(), job.target, new_record, job.undo); 335 } 336 } 337 new_record = null; 338 } 339 } 340 // Else 341 else if(source_file.isDirectory()) { 342 // create new record 343 FileNode directory_record = new FileNode(target_file); 344 ///ystem.err.println("Directory record = " + directory_record + " (" + target_file.getAbsolutePath() + ")"); 345 SynchronizedTreeModelTools.insertNodeInto(target_model, destination_node, directory_record); 346 // Why is this not happening eh? 347 directory_record.setParent(destination_node); 348 if(!target_file.exists()) { 349 // make the directory 350 target_file.mkdirs(); 351 new_node = directory_record; 352 // create undo job 353 if(job.undoable) { 354 job.undoable = false; 355 if(job.type == FileJob.COPY) { 356 // A copy is undone with a delete, so it doesn't really matter where the file originally came from (we're not moving it back there, but into the recycle bin). You may also notice we don't make use of the target parent record. This is because no undo action needs this information, and even if it did it could simply ask for records parent! 357 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_COPY, null, null, job.target, directory_record, job.undo); 358 } 359 else { 360 // Movements however do need a source and source parent so the file can be moved back to the correct place. 361 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_MOVE, job.source, (FileNode)origin_node.getParent(), job.target, directory_record, job.undo); 362 } 363 } 364 } 365 // Else inform the users that a directory already exists and files will be copied into it 366 else { 367 JOptionPane.showMessageDialog(null, get("Directory_Exists", target_file.toString()), get("General.Warning"), JOptionPane.WARNING_MESSAGE); 368 } 369 // Queue non-filtered child files for copying. If this directory already existed, the child records will have to generate the undo jobs, as we don't want to entirely delete this directory if it already existed. 370 FileNode child_record = null; 371 // In order to have a sane copy proceedure (rather than always copying last file first as it used to) we always add the child node at the position the parent was removed from. Consider the file job 'a' at the end of the queue which generates three new jobs 'b', 'c' and 'd'. The resulting flow should look like this. 372 // -- Starting queue ...[a] 373 // remove(position) = 'a' ... 374 // add(position, 'b') ...[b] 375 // add(position, 'c') ...[c][b] 376 // add(position, 'd') ...[d][c][b] 377 // Next loop 378 // remove(position) = 'b' ...[d][c] 379 for(int i = 0; i < origin_node.getChildCount(); i++) { 380 child_record = (FileNode) origin_node.getChildAt(i); 381 addJob(job.ID(), job.source, child_record, job.target, directory_record, job.type, job.undo, false, false, position); 382 } 383 child_record = null; 384 directory_record = null; 385 } 386 // The file wasn't found! 387 else { 388 cancel_action = true; 389 // Show warning. 390 JOptionPane.showMessageDialog(Gatherer.g_man, get("File_Not_Found_Message", source_file.getName()), get("File_Not_Found_Title"), JOptionPane.ERROR_MESSAGE); 391 // Force refresh of source folder. 392 source_model.refresh(new TreePath(((FileNode)origin_node.getParent()).getPath())); 393 } 394 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 395 // We can't have been cancelled, and we must have created a new FileNode during the above phase, before we can handle metadata. 396 if(!cancel_action && new_node != null) { 397 /* Time to handle any existing metadata. */ 398 // If the directory came from inside our collection... 399 if (job.source.toString().equals("Collection")) { 400 ///ystem.err.println("Move within collection..."); 401 GDMManager gdm = Gatherer.c_man.getCollection().gdm; 402 // we just retrieve the metadata attached to the origin node... 403 ArrayList existing_metadata = gdm.getMetadata(source_file); 404 ///ystem.err.println("Existing metadata for " + origin_node + ": " + gdm.toString(existing_metadata)); 405 // then assign this remainder to the new folder. 406 ///ystem.err.println("New metadata: " + gdm.toString(existing_metadata)); 407 gdm.addMetadata(new_node, existing_metadata); 408 existing_metadata = null; 409 gdm = null; 410 } 411 // If it came from the recycle bin retrieve the metadata from there, once again remembering to account for inherited metadata 412 else if (job.source.toString().equals("Undo")) { 413 GDMManager gdm = Gatherer.c_man.getCollection().gdm; 414 // Retrieve metadata from the recycle bin 415 ArrayList existing_metadata = Gatherer.c_man.undo.getMetadata(source_file); 416 // then assign this remainder to the new folder. 417 gdm.addMetadata(new_node, existing_metadata); 418 existing_metadata = null; 419 gdm = null; 420 } 421 // Otherwise if it came from the workspace use the MSMs parsers to search for folder level metadata (such as metadata.xml or marc records). 422 else if (job.source.toString().equals("Workspace")) { 423 cancel_action = Gatherer.c_man.getCollection().msm.searchForMetadata(new_node, origin_node, source_file.isDirectory()); 424 } 425 } 426 new_node = null; 427 428 } 429 } 430 // If we haven't been cancelled, and we've been asked to delete a directory/file, or perhaps as part of a move, we delete the file. This involves removing any existing metadata and then copying the file to the recycled bin (for a delete only), then deleting the file. When deleting a directory record from the tree (or from the filesystem for that matter) we must ensure that all of the descendant records have already been removed. If we fail to do this the delete will fail, or you will be bombarded with hundreds of 'Parent node of null not allowed' error messages. Also be aware that if the user has cancelled just this action, because of say a name clash, then we shouldn't do any deleting of any sort dammit. 431 if(!cancel_action && ready && (job.type == FileJob.DELETE || job.type == FileJob.MOVE)) { 432 ///ystem.err.print("Delete/Move: " + origin_node + " -> "); 433 // If the source is an empty directory or a file. Don't do anything to the root node of a tree. 434 File[] child_list = source_file.listFiles(); 435 if(source_file.isFile() || (child_list != null && (child_list.length == 0 || (child_list.length == 1 && child_list[0].getName().equals(Utility.METADATA_XML))) && origin_node.getParent() != null)) { 436 ///ystem.err.println("File or empty directory."); 437 // Delete any metadata.xml still in the directory. 438 if(child_list != null && child_list.length == 1) { 439 child_list[0].delete(); 440 } 441 442 ///ystem.err.println("Origin is file or is directory and is empty."); 443 // update status area 444 String args[] = new String[1]; 445 args[0] = "" + (queue.size() + 1) + ""; 446 job_status.setText(get("Jobs", args)); 447 args[0] = Utility.formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width); 448 file_status.setText(get("Deleting", args)); 449 args = null; 450 file_status.setToolTipText(Utility.formatHTMLWidth(file_status.getText(), 80)); 451 // Remove its metadata 452 ArrayList metadatum = null; 453 if(job.source == Gatherer.c_man.undo) { 454 Gatherer.c_man.undo.addMetadata(target_file, metadatum); 455 } 456 else { 457 metadatum= Gatherer.c_man.getCollection().gdm.removeMetadata(origin_node.getFile()); 458 } 459 // determine it parent node 460 FileNode parent_record = (FileNode)origin_node.getParent(); 461 // Remove from model 462 SynchronizedTreeModelTools.removeNodeFromParent(source_model, origin_node); 463 // If we are deleting 464 File recycled_file = null; 465 FileNode recycled_parent = null; 466 if(job.type == FileJob.DELETE) { 467 // See if this record already has a previous recycle folder noted in the recycle folder mappings. 468 recycled_parent = (FileNode) recycle_folder_mappings.get(origin_node); 469 if(recycled_parent != null) { 470 recycle_folder_mappings.remove(origin_node); 471 } 472 else { 473 recycled_parent = (FileNode) Gatherer.c_man.undo.getTreeModel().getRoot(); 474 } 475 // This may be a directory which we have already added previously. 476 if(completed_folder_mappings.containsKey(origin_node)) { 477 FileNode recycled_record = (FileNode) completed_folder_mappings.get(origin_node); 478 // Replace the temporary directory record in the undo tree with this one. 479 SynchronizedTreeModelTools.replaceNode(Gatherer.c_man.undo.getTreeModel(), recycled_record, origin_node); 480 origin_node.setFile(recycled_record.getFile()); 481 } 482 else { 483 // copy the file to the recycle bin 484 recycled_file = new File(recycled_parent.getFile(), origin_node.toString()); 485 // If the file already exists, delete it. 486 if(recycled_file.exists()) { 487 recycled_file.delete(); 488 } 489 recycled_file.deleteOnExit(); 490 copyFile(source_file, recycled_file, progress); 491 origin_node.setFile(recycled_file); 492 // Add the node to the appropriate place in the UndoManagers tree model. Unfortunately this one does have the possibility of altering the GUI (if the removeNodeFromParent above hasn't occured yet), so we must put it on the queue. 493 SynchronizedTreeModelTools.insertNodeInto(Gatherer.c_man.undo.getTreeModel(), recycled_parent, origin_node); 494 } 495 Gatherer.c_man.undo.addMetadata(source_file, metadatum); 496 } 497 // delete the source file 498 Utility.delete(source_file); 499 // create undo job as necessary. File move would have been handled above. 500 if(job.undoable) { 501 job.undoable = false; 502 // The target is null, indicating that we moved this file to the recycle bin. The UndoManager will replace null with 'this'. 503 Gatherer.c_man.undo.addUndo(job.ID(), UndoManager.FILE_DELETE, job.source, parent_record, null, origin_node, job.undo); 504 } 505 recycled_parent = null; 506 recycled_file = null; 507 //metadatum = null; 508 } 509 // Else the source is a directory and it has children remaining 510 else if(child_list != null && child_list.length > 0) { 511 ///ystem.err.print("Nonempty directory -> "); 512 ///ystem.err.println("Directory is non-empty. Remove children first."); 513 FileNode recycle_folder_record = null; 514 // Don't worry about all this for true file move actions. 515 if(job.type == FileJob.DELETE) { 516 // See if this record already has a previous recycle folder noted in the recycle folder mappings. 517 FileNode parent = (FileNode) recycle_folder_mappings.get(origin_node); 518 if(parent != null) { 519 recycle_folder_mappings.remove(origin_node); 520 } 521 else { 522 parent = (FileNode) Gatherer.c_man.undo.getTreeModel().getRoot(); 523 } 524 // We must add the folder node to our undo manager tree now, so we'll have something to add children to. We use a copy of the directory file record. 525 File recycle_folder = new File(parent.getFile(), origin_node.toString()); 526 recycle_folder.deleteOnExit(); 527 recycle_folder.mkdirs(); 528 recycle_folder_record = new FileNode(recycle_folder); 529 // Add this node to the undo tree model. 530 SynchronizedTreeModelTools.insertNodeInto(Gatherer.c_man.undo.getTreeModel(), parent, recycle_folder_record); 531 // We add an entry to the complete mappings to ensure this directory isn't added again 532 completed_folder_mappings.put(origin_node, recycle_folder_record); 533 ///ystem.err.println("Added completed directories mapping " + origin_node); 534 // queue all of its children, (both filtered and non-filtered), but for deleting only. Don't queue jobs for a current move event, as they would be queued as part of copying. I have no idea way, per sec, however the children within the origin node are always invalid during deletion (there are several copies of some nodes?!?). I'll check that each child is only added once. 535 ///ystem.err.println("Directory has " + origin_node.getChildCount() + " children."); 536 ///ystem.err.println("Directory actually has " + child_list.length + " children."); 537 origin_node.unmap(); 538 origin_node.map(); 539 ///ystem.err.println("Directory has " + origin_node.getChildCount() + " children."); 540 ///ystem.err.println("Directory actually has " + child_list.length + " children."); 541 for(int i = 0; i < origin_node.getChildCount(); i++) { 542 FileNode child_record = (FileNode) origin_node.getChildAt(i); 543 addJob(job.ID(), job.source, child_record, job.target, destination_node, FileJob.DELETE, job.undo, false, false, position); 544 if(recycle_folder_record != null) { 545 recycle_folder_mappings.put(child_record, recycle_folder_record); 546 } 547 } 548 } 549 // Requeue a delete job -after- the children have been dealt with. Remember I've reversed the direction of the queue so sooner is later. Te-he. Also have to remember that we have have followed this path to get here for a move job: Copy Directory -> Queue Child Files -> Delete Directory (must occur after child files) -> Queue Directory. 550 // One special case. Do not requeue root nodes. Don't requeue jobs marked as done. 551 if(origin_node.getParent() != null && !job.done) { 552 ///ystem.err.println("Requeue"); 553 job.type = FileJob.DELETE; // You only requeue jobs that are deletes, as directories must be inspected before children, but deleted after. 554 addJob(job, position); 555 } 556 else { 557 ///ystem.err.println("I've already done this job twice. I refuse to requeue it again!!!"); 558 } 559 } 560 } 561 job = null; 562 source_file = null; 563 target_file = null; 564 origin_node = null; 565 // We can only break out of the while loop if we are out of files, or if the action was cancelled. 566 if(cancel_action) { 567 // Empty queue 568 clearJobs(); 569 cancel_action = false; 570 } 571 // Debugging pause. 572 ///ystem.err.println("Job complete."); 573 } 574 else { 575 synchronized(this) { 576 ///ystem.err.println("Queue size = " + queue.size()); 577 // Force the trees to refresh (but only if there is something on screen!) 578 if(Gatherer.g_man != null) { 579 Gatherer.g_man.refreshTrees(); 580 } 581 // Reset status area 582 job_status.setText(get("No_Selection")); 583 file_status.setText(get("No_Activity")); 584 progress.reset(); 585 progress.setString(get("No_Activity")); 586 yes_to_all = false; 587 completed_folder_mappings.clear(); 588 recycle_folder_mappings.clear(); 589 // Now wait if applicable. 590 if(return_immediately) { 591 return; 592 } 593 ///ystem.err.println("Waiting"); 594 wait(); 595 } 596 } 597 } 598 catch (Exception error) { 599 Gatherer.printStackTrace(error); 600 } 601 } 602 } 603 /** A second lock is necessary to prevent the thread waiting, because its notify occured momentarily before it waited. If the flag is set then the thread can't wait, but loops around. This may chew processor time so should only be used if you are certain files are about to be placed on the queue. 604 604 * @param wait_allowed The new state of the wait_allowed flag, as a boolean. 605 605 */ 606 607 608 609 606 public void setWaitAllowed(boolean wait_allowed) { 607 this.wait_allowed = wait_allowed; 608 } 609 /** Restore the progress bar so that it updates normally. 610 610 * @see org.greenstone.gatherer.gui.LongProgressBar 611 611 */ 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 612 synchronized public void unpause() { 613 progress.setIndeterminate(false); 614 if(previous != null) { 615 progress.setString(previous); 616 previous = null; 617 } 618 } 619 /** Called when the user makes some selection in one of the trees we are listening to. From this we update the status details. */ 620 public void valueChanged(TreeSelectionEvent event) { 621 JTree tree = (JTree) event.getSource(); 622 if(tree.getSelectionCount() > 0) { 623 TreePath selection[] = tree.getSelectionPaths(); 624 int file_count = 0; 625 int dir_count = 0; 626 for(int i = 0; i < selection.length; i++) { 627 TreeNode record = (TreeNode) selection[i].getLastPathComponent(); 628 if(record.isLeaf()) { 629 file_count++; 630 } 631 else { 632 dir_count++; 633 } 634 record = null; 635 } 636 selection = null; 637 String args[] = new String[2]; 638 args[0] = "" + file_count + ""; 639 args[1] = "" + dir_count + ""; 640 job_status.setText(get("Selected", args)); 641 args = null; 642 } 643 tree = null; 644 } 645 646 synchronized private void clearJobs() { 647 queue.clear(); 648 } 649 650 /** Copy a file from the source location to the destination location. 651 651 * @param source The source File. 652 652 * @param destination The destination File. 653 653 * @see org.greenstone.gatherer.Gatherer 654 654 */ 655 656 657 658 659 660 655 public void copyFile(File source, File destination, LongProgressBar progress) 656 throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, UnknownFileErrorException { 657 if(source.isDirectory()) { 658 destination.mkdirs(); 659 } 660 else { 661 661 // Check if the origin file exists. 662 663 664 662 if(!source.exists()) { 663 throw(new FileNotFoundException()); 664 } 665 665 // Check if the destination file does not exist. 666 667 668 669 670 666 if(destination.exists()) { 667 throw(new FileAlreadyExistsException()); 668 } 669 File dirs = destination.getParentFile(); 670 dirs.mkdirs(); 671 671 // Copy the file. 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 672 FileInputStream f_in = new FileInputStream(source); 673 FileOutputStream f_out = new FileOutputStream(destination); 674 byte data[] = new byte[Utility.BUFFER_SIZE]; 675 int data_size = 0; 676 while((data_size = f_in.read(data, 0, Utility.BUFFER_SIZE)) != -1 && !cancel_action) { 677 long destination_size = destination.length(); 678 try { 679 f_out.write(data, 0, data_size); 680 } 681 // If an IO exception occurs, we can do some maths to determine if the number of bytes written to the file was less than expected. If so we assume a InsufficientSpace exception. If not we just throw the exception again. 682 catch (IOException io_exception) { 683 if(destination_size + (long) data_size > destination.length()) { 684 // Determine the difference (which I guess is in bytes). 685 long difference = (destination_size + (long) data_size) - destination.length(); 686 // Transform that into a human readable string. 687 String message = Utility.formatFileLength(difference); 688 throw(new InsufficientSpaceException(message)); 689 } 690 else { 691 throw(io_exception); 692 } 693 } 694 if(progress != null) { 695 progress.addValue(data_size); 696 } 697 } 698 698 // Flush and close the streams to ensure all bytes are written. 699 700 699 f_in.close(); 700 f_out.close(); 701 701 // We have now, in theory, produced an exact copy of the source file. Check this by comparing sizes. 702 703 704 702 if(!cancel_action && source.length() != destination.length()) { 703 throw(new UnknownFileErrorException()); 704 } 705 705 // If we were cancelled, ensure that none of the destination file exists. 706 707 708 709 710 711 706 if(cancel_action) { 707 destination.delete(); 708 } 709 } 710 } 711 /** Retrieve a phrase from the dictionary based on the key. 712 712 * @param key The key index as a String. 713 713 * @return The desired phrase also as a String. 714 714 */ 715 716 717 718 715 private String get(String key) { 716 return get(key, (String[])null); 717 } 718 /** Retrieve a phrase from the dictionary based on the key, and taking into account the given argument. 719 719 * @param key The key index as a String. 720 720 * @param args A String which is the argument to be added to the phrase. 721 721 * @return The desired phrase also as a String. 722 722 */ 723 724 725 726 727 728 723 private String get(String key, String arg) { 724 String args[] = new String[1]; 725 args[0] = arg; 726 return get(key, args); 727 } 728 /** Retrieve a phrase from the dictionary based on the key, and taking into account the given arguments. 729 729 * @param key The key index as a String. 730 730 * @param args A String[] of arguments to be added to the phrase. … … 733 733 * @see org.greenstone.gatherer.Gatherer 734 734 */ 735 736 737 738 739 740 741 742 743 744 745 746 747 748 735 private String get(String key, String args[]) { 736 if(key.indexOf('.') == -1) { 737 key = "FileActions." + key; 738 } 739 return Gatherer.dictionary.get(key, args); 740 } 741 742 private FileJob removeJob(int position) { 743 FileJob job = null; 744 if(queue.size() > 0) { 745 job = (FileJob) queue.remove(position); 746 } 747 return job; 748 } 749 749 } -
trunk/gli/src/org/greenstone/gatherer/file/FileSystemModel.java
r4293 r4366 14 14 15 15 public class FileSystemModel 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 16 extends DefaultTreeModel 17 implements TreeExpansionListener, TreeWillExpandListener { 18 19 private DragTree tree; 20 private FileFilter current_filter; 21 private FileFilter[] filters; 22 static private FileFilter[] default_filters = { new FileFilter("\\..*", true), new FileFilter("metadata\\.xml", true) }; 23 24 public FileSystemModel(FileNode root) { 25 super(root); 26 current_filter = null; 27 filters = null; 28 tree = null; 29 root.setModel(this); 30 root.map(); 31 } 32 33 public FileFilter[] getFilters() { 34 if(filters == null) { 35 if(current_filter != null) { 36 filters = new FileFilter[default_filters.length + 1]; 37 filters[default_filters.length] = current_filter; 38 } 39 else { 40 filters = new FileFilter[default_filters.length]; 41 } 42 System.arraycopy(default_filters, 0, filters, 0, default_filters.length); 43 } 44 return filters; 45 } 46 46 47 48 49 50 51 52 53 47 /** Retrieve the node denoted by the given tree path. Note that this isn't equivelent to saying path.lastPathComponent, as the references within the path may be stale. */ 48 public FileNode getNode(TreePath path) { 49 ///ystem.err.println("**** getNode(" + path + ") ****"); 50 FileNode current = (FileNode)root; 51 // Special case for the root node. Check the first path component is the root node. 52 FileNode first_node = (FileNode)path.getPathComponent(0); 53 if(current.equals(first_node)) { 54 54 ///ystem.err.println("First path component matches root node."); 55 55 // For each path with this tree path 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 56 for(int i = 1; current != null && i < path.getPathCount(); i++) { 57 // Retrieve the stale path 58 FileNode stale_node = (FileNode) path.getPathComponent(i); 59 ///ystem.err.print("Searching for '" + stale_node + "': "); 60 // Locate the fresh node by searching current's children. Remember to ensure that current is mapped. 61 current.map(); 62 boolean found = false; 63 for(int j = 0; !found && j < current.getChildCount(); j++) { 64 FileNode child_node = (FileNode) current.getChildAt(j); 65 ///ystem.err.print(child_node + " "); 66 if(stale_node.equals(child_node)) { 67 found = true; 68 current = child_node; 69 ///ystem.err.println("Found!"); 70 } 71 child_node = null; 72 } 73 // If no match is found, then set current to null and exit. 74 if(!found) { 75 current = null; 76 ///ystem.err.println("Not Found!"); 77 } 78 // Repeat as necessary 79 } 80 } 81 ///ystem.err.println("Returning node: " + new TreePath(current.getPath())); 82 return current; 83 } 84 85 public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) { 86 ///ystem.err.println("insertNodeInto(" + newChild + ", " + parent + ", " + index + ")"); 87 super.insertNodeInto(newChild, parent, index); 88 } 89 90 public void mapDirectory(File directory, String title) { 91 FileNode node = new FileNode(directory, this, title, true); 92 SynchronizedTreeModelTools.insertNodeInto(this, (FileNode)root, node); 93 } 94 95 /** Used to refresh the contents of the FileNode indicated by the tree path. While the refresh itself is a piece of the proverbial, the restoring of expanded folders afterwards is a nightmare, mainly because all of the tree paths are no longer valid (remember that refresh involves throwing away the node currents children and remapping them). 96 * @param path The TreePath to the node to be refreshed. 97 */ 98 public void refresh(TreePath path) { 99 // If no path is set, take the path to the root node (ie update the whole tree) 100 if(path == null) { 101 101 ///ystem.err.println("Refresh entire tree."); 102 103 104 102 path = new TreePath(((FileNode)root).getPath()); 103 } 104 else { 105 105 ///ystem.err.println("Refresh: " + path.getLastPathComponent()); 106 107 108 106 } 107 // Only a valid action if this model is currently being displayed in a tree. 108 if(tree != null) { 109 109 // Retrieve the error node. 110 110 FileNode node = (FileNode) path.getLastPathComponent(); 111 111 // If this error node is a dummy node (ie has no associated file) we can't unmap it, so we iterate through its children refreshing each in turn. 112 113 114 115 116 117 118 112 if(node.getFile() == null) { 113 for(int i = 0; i < node.getChildCount(); i++) { 114 FileNode child = (FileNode) node.getChildAt(i); 115 refresh(new TreePath(child.getPath())); 116 child = null; 117 } 118 } 119 119 // Otherwise we refresh this node. 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 120 else { 121 // Record all of the expanded paths under this node. How come getExpandedDescendants returns more results each time. 122 Enumeration old_tree_paths = tree.getExpandedDescendants(path); 123 // Refresh the tree structure. 124 node.unmap(); 125 node.map(); 126 // Fire the appropriate event. 127 nodeStructureChanged(node); 128 // Then (painfully) restore the expanded paths. Note that the paths stored in the enumeration no longer exist, so I have to restore by node matching. 129 //counter = 0; 130 ///ystem.err.println("After:"); 131 while(old_tree_paths != null && old_tree_paths.hasMoreElements()) { 132 //counter++; 133 FileNode current_node = node; 134 TreePath old_tree_path = (TreePath) old_tree_paths.nextElement(); 135 ///ystem.err.println(counter + ". " + old_tree_path); 136 TreePath new_tree_path = new TreePath(path.getPath()); // Why isn't there a treepath copy constructor! 137 boolean not_found = false; 138 while(!not_found && old_tree_path.getPathCount() > new_tree_path.getPathCount()) { 139 // Retrieve the new node in the old tree path 140 FileNode old_path_node = (FileNode) old_tree_path.getPathComponent(new_tree_path.getPathCount()); 141 // Ensure the current node is mapped 142 current_node.map(); 143 // Now attempt to match it to a child of the new paths last node. 144 boolean found = false; 145 for(int i = 0; !found && i < current_node.getChildCount(); i++) { 146 FileNode target = (FileNode) current_node.getChildAt(i); 147 ///ystem.err.println("Comparing " + old_path_node.toString() + " with " + target); 148 if(target.toString().equals(old_path_node.toString())) { 149 current_node = target; 150 found = true; 151 } 152 target = null; 153 } 154 old_path_node = null; 155 // If we found a match, we add that to the new Tree Path and continue. 156 if(found) { 157 ///ystem.err.println("Found a match!"); 158 new_tree_path = new_tree_path.pathByAddingChild(current_node); 159 } 160 // We also have to record if we were unable to find a match in the current nodes children. 161 else { 162 ///ystem.err.println("Node not found."); 163 not_found = true; 164 } 165 } 166 old_tree_path = null; 167 // If we've got this far, and haven't hit a not found, then we can assume we have the appropriate path, and ask the program to expand the new tree path 168 if(!not_found) { 169 tree.expandPath(new_tree_path); 170 ///ystem.err.println(" -> Expanded " + new_tree_path); 171 } 172 else { 173 ///ystem.err.println(" -> Cannot Expand " + new_tree_path); 174 } 175 new_tree_path = null; 176 current_node = null; 177 } 178 node = null; 179 } 180 } 181 else { 182 182 ///ystem.err.println("No Tree!"); 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 183 } 184 } 185 186 public void setFilter(String pattern) { 187 if(pattern != null) { 188 current_filter = new FileFilter(pattern, false); 189 } 190 else { 191 current_filter = null; 192 } 193 filters = null; 194 } 195 196 public void setTree(DragTree tree) { 197 this.tree = tree; 198 } 199 200 public String toString() { 201 if(tree != null) { 202 return tree.toString(); 203 } 204 return "FileSystemModel"; 205 } 206 207 /** Called whenever an item in the tree has been collapsed. */ 208 public void treeCollapsed(TreeExpansionEvent event) { 209 // Deallocate the affected nodes children. Don't need to do this in a swing worker, as the nodes children are currently not visable. 210 TreePath path = event.getPath(); 211 FileNode node = (FileNode) path.getLastPathComponent(); 212 node.unmap(); 213 // Fire the appropriate event. 214 nodeStructureChanged(node); 215 } 216 217 /** Called whenever an item in the tree has been expanded. */ 218 public void treeExpanded(TreeExpansionEvent event) { 219 } 220 221 /** Invoked whenever a node in the tree is about to be collapsed. */ 222 public void treeWillCollapse(TreeExpansionEvent event) 223 throws ExpandVetoException { 224 // Veto the event if the user is attempting to collapse the root node (regardless of whether it is visible. 225 TreePath path = event.getPath(); 226 if(path.getPathCount() == 1) { 227 throw new ExpandVetoException(event, "Cannot collapse root node!"); 228 } 229 } 230 230 231 232 233 234 235 236 237 238 239 240 241 242 243 231 /** Invoked whenever a node in the tree is about to be expanded. */ 232 public void treeWillExpand(TreeExpansionEvent event) 233 throws ExpandVetoException { 234 // Set the wait cursor. 235 Gatherer.g_man.wait(true); 236 // Allocate the children. Don't need to do this in a swing worker, as the nodes children are currently not visable. 237 TreePath path = event.getPath(); 238 FileNode node = (FileNode) path.getLastPathComponent(); 239 node.map(); 240 nodeStructureChanged(node); 241 // Restore the cursor. 242 Gatherer.g_man.wait(false); 243 } 244 244 } -
trunk/gli/src/org/greenstone/gatherer/file/InsufficientSpaceException.java
r4293 r4366 4 4 5 5 public class InsufficientSpaceException 6 6 extends Exception { 7 7 8 9 10 8 public InsufficientSpaceException(String message) { 9 super(message); 10 } 11 11 } -
trunk/gli/src/org/greenstone/gatherer/file/UnknownFileErrorException.java
r4293 r4366 4 4 5 5 public class UnknownFileErrorException 6 6 extends Exception { 7 7 8 9 10 8 public UnknownFileErrorException() { 9 super(); 10 } 11 11 } -
trunk/gli/src/org/greenstone/gatherer/gui/browser/GBrowserEvent.java
r4293 r4366 53 53 */ 54 54 public class GBrowserEvent 55 55 extends AWTEvent { 56 56 57 58 57 /** The url payload of this event. */ 58 private GURL url = null; 59 59 60 60 private JProgressBar progress = null; 61 61 62 63 62 /** The message payload of this event. */ 63 private String message = null; 64 64 65 66 67 68 69 70 71 72 73 74 75 65 /** This contructor is used to pass events about GURLs out to its 66 * listeners. The source and id are needed by the super class of AWTEvents 67 * but the url is specific to this class. 68 * @param source the class Object that fired this event. 69 * @param id a numeric identifier for this event. 70 * @param url the GURL about which this event is concerned. 71 */ 72 public GBrowserEvent(Object source, int id, GURL url) { 73 super(source, id); 74 this.url = url; 75 } 76 76 77 78 79 80 81 82 83 84 85 86 87 88 77 /** This contructor is used to pass events about GURLs out to its 78 * listeners. The source and id are needed by the super class of AWTEvents 79 * but the url and message are specific to this class. 80 * @param source the class Object that fired this event. 81 * @param id a numeric identifier for this event. 82 * @param url the GURL about which this event is concerned. 83 * @param message a message String, the reason for this event. 84 */ 85 public GBrowserEvent(Object source, int id, GURL url, String message) { 86 super(source, id); 87 this.url = url; 88 } 89 89 90 91 92 93 94 95 96 97 98 99 100 90 /** This constructor is used to pass message events out to its listeners. 91 * It is much the same as the first constructor, however instead of a 92 * GURL as payload, we instead have a message String. 93 * @param source the class Object that fired this event. 94 * @param id a numeric identifier for this event. 95 * @param message a message String, the reason for this event. 96 */ 97 public GBrowserEvent(Object source, int id, String message) { 98 super(source, id); 99 this.message = message; 100 } 101 101 102 103 104 105 106 107 108 102 /** Used to get the GURL contained within this event. 103 * @return a GURL specifying the url this event is concerned with, or null 104 * if this is a message type event. 105 */ 106 public GURL getURL() { 107 return url; 108 } 109 109 110 111 112 113 114 115 116 110 /** Used to get the String message contained within this event. 111 * @return a String containing the message for this event, or null if this 112 * is a GURL type event. 113 */ 114 public String getMessage() { 115 return message; 116 } 117 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 118 /** Returns a boolean showing whether it is legal to request this GURLs 119 * progress bar. All GURLs have progress bars, but only those spawned in 120 * GHTMLPanes etc will actually have their progress bars updated. Webclient 121 * does not yet support this functionality but will eventually via the 122 * DocumentProgressEvent. 123 * @return A boolean specifying whether this GURL supports ProgressBar 124 * requests. 125 */ 126 public boolean hasProgress() { 127 if(progress != null) { 128 return true; 129 } 130 return false; 131 } 132 132 } 133 133 -
trunk/gli/src/org/greenstone/gatherer/gui/browser/GBrowserListener.java
r4293 r4366 47 47 48 48 public interface GBrowserListener 49 50 51 52 53 54 55 49 extends EventListener { 50 public void actionFailed(GBrowserEvent event); 51 public void controlsChanged(); 52 public void hyperlinkClick(GBrowserEvent event); 53 public void renderBegun(GBrowserEvent event); 54 public void renderComplete(GBrowserEvent event); 55 public void statusChanged(GBrowserEvent event); 56 56 } -
trunk/gli/src/org/greenstone/gatherer/gui/browser/GHTMLPane.java
r4293 r4366 71 71 */ 72 72 public class GHTMLPane 73 74 73 extends JPanel 74 implements HTMLViewPane, HyperlinkListener, PropertyChangeListener { 75 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 76 /** follow_links - does the class automatically follow hyperlinks. */ 77 boolean follow_links = true; 78 /** ready - has the view finished rendering the current URL. */ 79 boolean ready = true; 80 /** the list of listeners associated with this class. */ 81 EventListenerList listeners = null; 82 /** gatherer - a reference to the main class of Gatherer for messaging. */ 83 Gatherer gatherer = null; 84 /** view - the JEditorPane the page content will be rendered on to. */ 85 JEditorPane view = null; 86 Object control = null; 87 /** current_page - the URL of the currently shown page. */ 88 GURL current_url = null; 89 /** back_pages - Vector of pages that have been visited. */ 90 Vector back_pages = null; 91 /** Vector of pages that have been backed up from. 92 * Reset whenever a new url 'path' is entered. 93 */ 94 Vector forward_pages = null; 95 96 97 /** Constructor - collects initial settings, lays out view, renders initial 98 * URL. 99 * @param gatherer - the main class of Gatherer 100 */ 101 public GHTMLPane(Gatherer gatherer, Object control) { 102 this.gatherer = gatherer; 103 this.control = control; 104 this.setLayout(new BorderLayout()); 105 106 listeners = new EventListenerList(); 107 108 back_pages = new Vector(); 109 forward_pages = new Vector(); 110 111 gatherer.debug("\t\tCreating GHTMLPane"); 112 view = new JEditorPane(); 113 view.setEditable(false); 114 view.addHyperlinkListener(this); 115 view.addPropertyChangeListener(this); 116 this.add(new JScrollPane(view), BorderLayout.CENTER); 117 } 118 119 public void addGBrowserListener(GBrowserListener listener) { 120 listeners.add(GBrowserListener.class, listener); 121 } 122 123 public void afterDisplay() { 124 // Nothing to do. Only realy for embedded apps. 125 } 126 127 public boolean back() { 128 // Add current page to forward 129 forward_pages.add(0,current_url); 130 // Back up one page. 131 current_url = (GURL) back_pages.lastElement(); 132 back_pages.remove(back_pages.size() - 1); 133 this.setPage(current_url); 134 return true; 135 } 136 137 public boolean canBack() { 138 if(back_pages.size() > 0 && ready) { 139 return true; 140 } 141 return false; 142 } 143 144 public boolean canForward() { 145 if(forward_pages.size() > 0 && ready) { 146 return true; 147 } 148 return false; 149 } 150 151 public boolean canStop() { 152 if(!ready) { 153 return true; 154 } 155 return false; 156 } 157 158 // This code courtesy of Horst ([email protected]) who provides this solution 159 // to the crap null exception I keep getting when I try to grab the image 160 // of a rendered HTML. 161 public BufferedImage capture() { 162 if(ready) { 163 163 // Paint to an off screen buffer 164 164 Dimension size = view.getSize(); 165 165 //Graphics offgraphics = offscreen.getGraphics(); 166 167 168 169 166 BufferedImage offscreen = 167 new BufferedImage(size.width, size.height, 168 BufferedImage.TYPE_INT_RGB); 169 Graphics2D offgraphics = (Graphics2D) offscreen.getGraphics(); 170 170 // For some reason the clip rect is null for the created image 171 171 // (usually it is set to the size of the image) so set it. 172 173 174 172 offgraphics.setClip(0, 0, size.width, size.height); 173 offgraphics.setColor(getBackground()); 174 offgraphics.fillRect(0, 0, size.width, size.height); 175 175 // This will display components in the HTML Page 176 177 178 179 180 176 view.paint(offgraphics); 177 return offscreen; 178 } 179 return null; 180 } 181 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 182 public GURL current() { 183 return current_url; 184 } 185 186 public void followHyperlinks(boolean value) { 187 follow_links = value; 188 } 189 190 public boolean forward() { 191 // Add current page to back 192 back_pages.add(current_url); 193 // Show forward page. 194 current_url = (GURL) forward_pages.firstElement(); 195 forward_pages.remove(0); 196 setPage(current_url); 197 return true; 198 } 199 200 /** Since you can't add an interface to a component hierarchy this method is required to get a reference of the actual rendering component. 201 * @return This HTMLViewPane implementation as a component. 202 */ 203 public Component getContent() { 204 return this; 205 } 206 207 public JProgressBar getProgress() { 208 // Currently not available. Will have to do some magic using the 209 // view.getStream() function. 210 return null; 211 } 212 213 /** When a hyperlink event is fired we handle it in one of several ways, 214 * based on the event type. Enters and exits get fired off as messages to 215 * the listeners so they can update their status bars. Clicks also fire 216 * status changes, but their event messages include the GURLs incase we 217 * to do something special to them. Furthermore if follow hyperlinks is 218 * set the browser is responsible for loading up the newly designated url. 219 * @param event A HyperlinkEvent containing useful data about the event, 220 * such as the url in question and the position of the mouse. It also is 221 * marked as one of several types of events including ENTERED and EXITED 222 * (for mouseovers) and ACTIVATED (clicked). 223 */ 224 public void hyperlinkUpdate(HyperlinkEvent event) { 225 Object event_type = event.getEventType(); 226 GURL new_url = new GURL(event.getURL()); 227 // The mouse has been moved over a hyperlink. 228 if (event_type == HyperlinkEvent.EventType.ENTERED) { 229 fireStatusChange(new GURL(event.getURL())); 230 } 231 else if (event_type == HyperlinkEvent.EventType.EXITED) { 232 fireStatusChange(null); 233 } 234 else if (event_type == HyperlinkEvent.EventType.ACTIVATED) { 235 fireHyperlinkClick(new_url); 236 if(follow_links) { 237 show(new_url); 238 } 239 } 240 } 241 242 public boolean isReady() { 243 return ready; 244 } 245 246 public void propertyChange(PropertyChangeEvent evt) { 247 if(evt.getPropertyName() == "page") { 248 ready = true; 249 fireRenderComplete(current_url); 250 } 251 } 252 253 public void removeGBrowserListener(GBrowserListener listener) { 254 listeners.remove(GBrowserListener.class, listener); 255 } 256 257 public void setEnabled(boolean value) { 258 ready = value; 259 } 260 261 public boolean show(String filename) { 262 return show(new GURL("file:"+filename)); 263 } 264 265 /** 266 * 267 * Precondition: ready = true <BR> 268 */ 269 public boolean show(GURL url) { 270 if(current_url == null) { 271 fireRenderBegun(url); 272 current_url = url; 273 setPage(current_url); 274 return true; 275 } 276 else { 277 277 // If we are not already showing this url 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 278 if(!current_url.toString().equals(url.toString())) { 279 fireRenderBegun(url); 280 // Save a copy of the current page 281 back_pages.add(current_url); 282 current_url = url; 283 setPage(current_url); 284 return true; 285 } 286 } 287 return false; 288 } 289 290 public boolean stop() { 291 // Unfortunately a JEditorPane is not sophisticated enough to support 292 // this. Pretty much once you send the page to be rendered only killing 293 // the thread will stop it, and I'm not sure you can restart the thread 294 // later. 295 return false; 296 } 297 298 protected void fireActionFailed(GURL url) { 299 GBrowserEvent event = new GBrowserEvent(this,0,url); 300 Object[] concerned = listeners.getListenerList(); 301 for(int i = 0; i < concerned.length ; i++) { 302 if(concerned[i] == GBrowserListener.class) { 303 ((GBrowserListener)concerned[i+1]).actionFailed(event); 304 } 305 } 306 } 307 308 protected void fireControlsChanged() { 309 Object[] concerned = listeners.getListenerList(); 310 for(int i = 0; i < concerned.length ; i++) { 311 if(concerned[i] == GBrowserListener.class) { 312 ((GBrowserListener)concerned[i+1]).controlsChanged(); 313 } 314 } 315 } 316 317 protected void fireHyperlinkClick(GURL url) { 318 GBrowserEvent event = new GBrowserEvent(this,0,url); 319 Object[] concerned = listeners.getListenerList(); 320 for(int i = 0; i < concerned.length ; i++) { 321 if(concerned[i] == GBrowserListener.class) { 322 ((GBrowserListener)concerned[i+1]).hyperlinkClick(event); 323 } 324 } 325 } 326 327 protected void fireRenderBegun(GURL url) { 328 GBrowserEvent event = new GBrowserEvent(this,0,url); 329 Object[] concerned = listeners.getListenerList(); 330 for(int i = 0; i < concerned.length ; i++) { 331 if(concerned[i] == GBrowserListener.class) { 332 ((GBrowserListener)concerned[i+1]).renderBegun(event); 333 } 334 } 335 } 336 337 protected void fireRenderComplete(GURL url) { 338 GBrowserEvent event = new GBrowserEvent(this,0,url); 339 Object[] concerned = listeners.getListenerList(); 340 for(int i = 0; i < concerned.length ; i++) { 341 if(concerned[i] == GBrowserListener.class) { 342 ((GBrowserListener)concerned[i+1]).renderComplete(event); 343 } 344 } 345 } 346 347 protected void fireStatusChange(GURL url) { 348 GBrowserEvent event = new GBrowserEvent(this,0,url); 349 Object[] concerned = listeners.getListenerList(); 350 for(int i = 0; i < concerned.length ; i++) { 351 if(concerned[i] == GBrowserListener.class) { 352 ((GBrowserListener)concerned[i+1]).statusChanged(event); 353 } 354 } 355 } 356 357 private void setPage(GURL url) { 358 try { 359 view.setPage(url.getURL()); 360 } 361 catch (IOException e) { 362 Utility.showError(gatherer, "GHTMLPane.setPage()", e); 363 } 364 catch (Exception e) { 365 Utility.showError(gatherer, "Arrggg...", e); 366 } 367 } 368 368 } 369 369 -
trunk/gli/src/org/greenstone/gatherer/gui/browser/HTMLViewPane.java
r4293 r4366 55 55 /** HTMLViewPane provides the interface that all implementations must adhere to. The methods shown are used by Gatherer to interact with the rendered HTML pane, and in turn recieve messages back. */ 56 56 public interface HTMLViewPane { 57 58 57 /** Add a GBrowserListener to this browser. 58 * @param listener an object which implements the GBrowserListener interface. 59 59 */ 60 60 public void addGBrowserListener(GBrowserListener listener); 61 61 62 62 public void afterDisplay(); 63 63 64 64 /** … … 70 70 * @return value - whether or not the operation was successful 71 71 */ 72 public boolean back();73 /**72 public boolean back(); 73 /** 74 74 * CanBack is used to determine if there are further pages to 'back' to, and 75 75 * based on the result whether the functionality should be available 76 76 */ 77 public boolean canBack();78 /**77 public boolean canBack(); 78 /** 79 79 * CanForward performs approximately the same duty as canBack except, of 80 80 * course, in the opposite direction. 81 81 */ 82 public boolean canForward();83 82 public boolean canForward(); 83 /** 84 84 * CanStop determines whether the stop functionality is available. In general 85 85 * you have to be doing something before you can stop it! 86 86 */ 87 88 /**87 public boolean canStop(); 88 /** 89 89 * Capture is used to grab a buffered image of the current graphics context of 90 90 * the HTML pane. Note that this may not actually be feasible in embedded … … 94 94 * panes graphics buffer. 95 95 */ 96 public BufferedImage capture();97 /**96 public BufferedImage capture(); 97 /** 98 98 * When called this method returns the URL of the resource currently being 99 99 * displayed (or rendered). If no resource has been requested the return URL … … 101 101 * @return url - the GURL of the currently displayed resource. 102 102 */ 103 public GURL current();104 /**103 public GURL current(); 104 /** 105 105 * This method is used to tell the HTML pane whether it should attempt to 106 106 * follow hyperlinks when a user clicks them. Whether this is set or not the … … 109 109 * @param on - whether followHyperlinks is; on (= true) or off. 110 110 */ 111 public void followHyperlinks(boolean value);112 /**111 public void followHyperlinks(boolean value); 112 /** 113 113 * Forward is a call for the HTMLViewPane implementation to move forward one 114 114 * page in its browser history. It should react appropriately to calls to this … … 116 116 * @return value - whether of not the operation was successfull 117 117 */ 118 public boolean forward();119 118 public boolean forward(); 119 /** In order to add the class that implements this interface to a component hierarchy you require some way to get at the component. This is that way. 120 120 * @return a component reference of this class (as compared to the interface one you started with). 121 121 */ 122 123 /**122 public Component getContent(); 123 /** 124 124 * If called, getProgressBar should return a reference to a JProgress bar. The 125 125 * HTML pane is then resposible for updating the progress bar for its lifetime … … 129 129 * towards displaying a url or null. 130 130 */ 131 public JProgressBar getProgress();132 /** Before attempting to issue futher instructions to the HTML pane, Gatherer must first ensure that its ready. A pane should always be responsive but there are foreseeable times when an interrupt to go to a new page may cause the renderer to become unstable, or perhaps a user is filling out a form and thus shouldn't be pre-empted.131 public JProgressBar getProgress(); 132 /** Before attempting to issue futher instructions to the HTML pane, Gatherer must first ensure that its ready. A pane should always be responsive but there are foreseeable times when an interrupt to go to a new page may cause the renderer to become unstable, or perhaps a user is filling out a form and thus shouldn't be pre-empted. 133 133 * @return <i>true</i> if the pane is ready for further instructions, <i>false</i> if not. 134 134 */ 135 public boolean isReady();136 /**135 public boolean isReady(); 136 /** 137 137 * SetEnabled is used to temporarily prevent further user interaction with the 138 138 * pane. This may be caused by certain user requests in other parts of Gatherer … … 143 143 * @param on - what state the component should be in; on (= true) or off. 144 144 */ 145 public void setEnabled(boolean value);146 145 public void setEnabled(boolean value); 146 /** Remove a GBrowserListener from this browser. 147 147 * @param listener an object which implements the GBrowserListener interface. 148 148 */ 149 150 149 public void removeGBrowserListener(GBrowserListener listener); 150 /** 151 151 * Show, in both of its forms, allows Gatherer to display a certain URL or file 152 152 * in the HTML pane. … … 155 155 * @return whether a page has been queued to be rendered. 156 156 */ 157 public boolean show(String filename);158 /**157 public boolean show(String filename); 158 /** 159 159 * Show, in both of its forms, allows Gatherer to display a certain URL or file 160 160 * in the HTML pane. … … 163 163 * @return whether a page has been queued to be rendered. 164 164 */ 165 public boolean show(GURL url);166 /**165 public boolean show(GURL url); 166 /** 167 167 * Stop() kills the current rendering. 168 168 * @return whether the rendering was successfully killed. 169 169 */ 170 public boolean stop();170 public boolean stop(); 171 171 } 172 172 -
trunk/gli/src/org/greenstone/gatherer/gui/combobox/GComboBox.java
r4293 r4366 55 55 */ 56 56 public class GComboBox 57 57 extends JComboBox { 58 58 59 59 private JTextField text; 60 60 61 62 63 64 61 public GComboBox() { 62 super(); 63 text = (JTextField) this.getEditor().getEditorComponent(); 64 } 65 65 66 67 68 69 66 public GComboBox(ComboBoxModel model) { 67 super(model); 68 text = (JTextField) this.getEditor().getEditorComponent(); 69 } 70 70 71 72 73 71 public void addDocumentListener(DocumentListener listener) { 72 if(text != null) text.getDocument().addDocumentListener(listener); 73 } 74 74 75 76 77 75 public void addKeyListener(KeyListener listener) { 76 if(text != null) text.addKeyListener(listener); 77 } 78 78 79 80 81 82 83 84 85 79 public String getText() { 80 if(text != null) { 81 return text.getText(); 82 } 83 GComboBoxModel model = (GComboBoxModel)this.getModel(); 84 return (String)model.getSelectedItem(); 85 } 86 86 87 88 89 87 public void setText(String value) { 88 if(text != null) text.setText(value); 89 } 90 90 91 92 93 91 public void removeDocumentListener(DocumentListener listener) { 92 if(text != null) text.getDocument().removeDocumentListener(listener); 93 } 94 94 95 96 97 95 public void removeKeyListener(KeyListener listener) { 96 if(text != null) text.removeKeyListener(listener); 97 } 98 98 } -
trunk/gli/src/org/greenstone/gatherer/gui/combobox/GComboBoxModel.java
r4293 r4366 53 53 54 54 public class GComboBoxModel 55 56 55 extends AbstractListModel 56 implements MutableComboBoxModel { 57 57 58 59 58 // Data. 59 private boolean isMetadataElement = false; 60 60 61 61 private Hashtable name_to_element = new Hashtable(); 62 62 63 63 private Object selected_item = null; 64 64 65 65 private Vector contents = new Vector(); 66 66 67 68 67 public GComboBoxModel() { 68 } 69 69 70 71 72 73 70 public GComboBoxModel(Vector contents) { 71 this.contents = contents; 72 Collections.sort(contents); 73 } 74 74 75 76 77 78 79 80 81 82 83 75 public GComboBoxModel(Vector elements, boolean isMetadataElement) { 76 this.isMetadataElement = isMetadataElement; 77 for(int i = 0; i < elements.size(); i++) { 78 ElementWrapper element = (ElementWrapper)elements.get(i); 79 contents.add(element.toString()); 80 name_to_element.put(element.toString(), element); 81 } 82 Collections.sort(contents); 83 } 84 84 85 86 87 88 89 90 91 92 85 private boolean contains(Vector vector, Object key) { 86 for(int i = 0; i < vector.size(); i++) { 87 if(vector.get(i).toString().equals(key.toString())) { 88 return true; 89 } 90 } 91 return false; 92 } 93 93 94 95 96 97 98 99 100 101 94 // Methods. 95 public void addElement(Object obj) { 96 if(!contents.contains(obj)) { 97 contents.add(obj); 98 Collections.sort(contents); 99 refresh(); 100 } 101 } 102 102 103 104 105 103 public Object getElementAt(int index) { 104 return contents.get(index); 105 } 106 106 107 108 109 107 public Object getSelectedItem() { 108 return selected_item; 109 } 110 110 111 112 113 114 115 116 111 public ElementWrapper getSelectedMetadata() { 112 if(isMetadataElement) { 113 return (ElementWrapper)name_to_element.get(selected_item); 114 } 115 return null; 116 } 117 117 118 119 120 118 public int getSize() { 119 return contents.size(); 120 } 121 121 122 123 124 125 122 public void insertElementAt(Object obj, int index) { 123 contents.add(index, obj); 124 fireIntervalAdded(this, index, index); 125 } 126 126 127 128 129 127 public void refresh() { 128 fireContentsChanged(this, 0, getSize()); 129 } 130 130 131 132 133 134 135 131 public void removeElement(Object obj) { 132 int index = contents.indexOf(obj); 133 contents.remove(index); 134 fireIntervalRemoved(this, index, index); 135 } 136 136 137 138 139 140 137 public void removeElementAt(int index) { 138 contents.remove(index); 139 fireIntervalRemoved(this, index, index); 140 } 141 141 142 143 144 145 146 142 public void setSelectedItem(Object obj) { 143 selected_item = obj; 144 int index = contents.indexOf(obj); 145 fireContentsChanged(this, index, index); 146 } 147 147 } -
trunk/gli/src/org/greenstone/gatherer/gui/messages/MessageListModel.java
r4293 r4366 47 47 import org.greenstone.gatherer.util.Utility; 48 48 class MessageListModel 49 49 extends AbstractListModel { 50 50 51 51 Hashtable mappings; 52 52 53 53 int current_index; 54 54 55 55 int current_message; 56 56 57 57 int sizes[][] = new int[6][4]; 58 58 59 59 Vector data; 60 60 61 61 // Contructor 62 62 public MessageListModel() { 63 64 65 66 67 68 69 70 71 63 current_index = 0; 64 current_message = 0; 65 data = new Vector(); 66 mappings = new Hashtable(); 67 for(int i = 0; i < 6; i++) { 68 for(int j = 0; j < 4; j++) { 69 sizes[i][j] = 0; 70 } 71 } 72 72 } 73 73 74 74 // Add a new message in the correct place 75 75 public synchronized void add(Message message) { 76 77 78 79 80 76 // Add the element to the model 77 data.add(message); 78 // Increment the count for that type of message 79 sizes[message.source][message.level]++; 80 // An announce to the world that I have changed! 81 81 fireContentsChanged(this, 0, getSize() - 1); 82 82 } … … 84 84 // Return the size of the list 85 85 public int getSize() { 86 87 88 86 int size = 0; 87 // Each each source... 88 for(int i = 0; i < 6; i++) { 89 89 // and each level... 90 91 92 93 94 95 96 97 98 99 90 for(int j = 0; j < 4; j++) { 91 // If both this source and level are active, add its size 92 /** @todo reenabled 93 * if(gatherer.config.source[i] && gatherer.config.level[j]) { 94 * size = size + sizes[i][j]; 95 * } 96 */ 97 } 98 } 99 return size; 100 100 } 101 101 102 103 104 105 102 public int getTotalSize() { 103 int size = 0; 104 // Each each source... 105 for(int i = 0; i < 6; i++) { 106 106 // and each level... 107 108 109 110 111 112 107 for(int j = 0; j < 4; j++) { 108 size = size + sizes[i][j]; 109 } 110 } 111 return size; 112 } 113 113 114 115 116 114 public String getSizeStr() { 115 return "" + getSize() + ""; 116 } 117 117 118 119 120 118 public String getTotalSizeStr() { 119 return "" + getTotalSize() + ""; 120 } 121 121 122 122 // Return a message from the list 123 123 public Object getElementAt( int index ) { 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 124 Integer index_int = new Integer(index); 125 // Maybe we've already found this message. 126 if(mappings.containsKey(index_int)) { 127 current_message = ((Integer)mappings.get(index_int)).intValue(); 128 } 129 // Otherwise we're going to have to search for it. 130 else { 131 while(index != current_index) { 132 // Asking for an index lower than the one we're at. 133 if(index < current_index) { 134 Message temp; 135 /* 136 do { 137 current_message--; 138 temp = (Message)data.get(current_message); 139 } while(!(gatherer.config.source[temp.source] && 140 gatherer.config.level[temp.level])); 141 // We are now at a new visible message. 142 */ 143 current_index--; 144 } 145 else { 146 Message temp; 147 /* 148 do { 149 current_message++; 150 temp = (Message)data.get(current_message); 151 } while(!(gatherer.config.source[temp.source] && 152 gatherer.config.level[temp.level])); 153 // We are now at a new visible message. 154 current_index++; 155 */ 156 } 157 } 158 158 // Now that we've found the blighter, add a mapping so its quicker 159 159 // next time. 160 161 162 160 mappings.put(new Integer(index),new Integer(current_message)); 161 } 162 return ((Message)data.get(current_message)).toString(); 163 163 } 164 164 165 166 167 165 public void refresh() { 166 if(getSize() > 0) { 167 current_index = 0; 168 168 // Find the first message that meets the requirements. Its index 0 y'know. 169 170 169 current_message = -1; 170 Message temp; 171 171 /* 172 do {173 174 175 } while(!(gatherer.config.source[temp.source] &&176 172 do { 173 current_message++; 174 temp = (Message)data.get(current_message); 175 } while(!(gatherer.config.source[temp.source] && 176 gatherer.config.level[temp.level])); 177 177 */ 178 178 // Reset mappings. 179 180 179 mappings.clear(); 180 mappings.put(new Integer(current_index), new Integer(current_message)); 181 181 // Let everyone who cares know we've changed. 182 183 184 185 182 fireContentsChanged(this, 0, getSize() - 1); 183 } 184 // Else don't bother doning anything on an empty list! 185 } 186 186 } 187 187 -
trunk/gli/src/org/greenstone/gatherer/gui/messages/MessageListRenderer.java
r4293 r4366 45 45 */ 46 46 class MessageListRenderer 47 48 47 extends JLabel 48 implements ListCellRenderer { 49 49 50 50 // Constructor … … 54 54 // We just reconfigure the JLabel each time we're called. 55 55 public Component getListCellRendererComponent( 56 57 Object value, // value to display58 int index, // cell index59 boolean isSelected, // is the cell selected60 boolean cellHasFocus) { // the list and the cell have the focus61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 56 JList list, 57 Object value, // value to display 58 int index, // cell index 59 boolean isSelected, // is the cell selected 60 boolean cellHasFocus) { // the list and the cell have the focus 61 // Get the message 62 String s = value.toString(); 63 setText(s); 64 // Now parse out the level 65 StringTokenizer tokenizer = new StringTokenizer(s); 66 // Throw away source. 67 tokenizer.nextToken(); 68 // Now add color details based on level 69 String level = tokenizer.nextToken(); 70 if(level.equals(get("Main"))) { 71 setForeground(Color.black); 72 } else if (level.equals(get("Event"))) { 73 setForeground(Color.blue); 74 } else if (level.equals(get("Information"))) { 75 setForeground(Color.gray); 76 } else { 77 setForeground(Color.red); 78 } 79 setEnabled(list.isEnabled()); 80 setFont(list.getFont()); 81 return this; 82 82 } 83 83 84 85 86 87 88 89 84 private String get(String key) { 85 if(key.indexOf('.') == -1) { 86 key = "Level." + key; 87 } 88 return Gatherer.dictionary.get(key); 89 } 90 90 } -
trunk/gli/src/org/greenstone/gatherer/gui/messages/MessagePane.java
r4293 r4366 48 48 */ 49 49 public class MessagePane 50 50 extends JPanel 51 51 implements ActionListener { 52 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 // Toggle config setting 276 277 // Inform model something has changed. 278 279 280 281 282 283 // Toggle config setting 284 285 // Inform model something has changed. 286 287 288 289 290 291 // Toggle config setting 292 293 // Inform model something has changed. 294 295 296 297 298 299 // Toggle config setting 300 301 // Inform model something has changed. 302 303 304 305 306 307 // Toggle config setting 308 309 // Inform model something has changed. 310 311 312 313 314 315 // Toggle config setting 316 317 // Inform model something has changed. 318 319 320 321 322 323 // Toggle config setting 324 325 // Inform model something has changed. 326 327 328 329 330 331 // Toggle config setting 332 333 // Inform model something has changed. 334 335 336 337 338 339 // Toggle config setting 340 341 // Inform model something has changed. 342 343 344 345 346 347 // Toggle config setting 348 349 // Inform model something has changed. 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 53 GridBagConstraints grid_constraints = null; 54 55 GridBagLayout grid_layout = null; 56 57 JButton save_messages = null; 58 59 JLabel level_label = null; 60 JLabel message_count = null; 61 JLabel source_label = null; 62 63 JPanel central_pane = null; 64 JPanel corner_pane = null; 65 JPanel count_pane = null; 66 JPanel horizontal_pane = null; 67 JPanel level_pane = null; 68 JPanel source_pane = null; 69 JPanel vertical_pane = null; 70 71 JScrollPane scroll_pane = null; 72 73 JToggleButton level_event = null; 74 JToggleButton level_error = null; 75 JToggleButton level_information = null; 76 JToggleButton level_main = null; 77 JToggleButton source_browser = null; 78 JToggleButton source_build = null; 79 JToggleButton source_general = null; 80 JToggleButton source_mirroring = null; 81 JToggleButton source_metaedit = null; 82 JToggleButton source_session = null; 83 84 JList message_list = null; 85 86 MessageListModel model = null; 87 88 MessageListRenderer renderer = null; 89 90 String args[]; 91 92 public MessagePane() { 93 } 94 95 public void display() { 96 // Setup main layout managers 97 grid_layout = new GridBagLayout(); 98 grid_constraints = new GridBagConstraints(); 99 this.setLayout(grid_layout); 100 101 // Vertical Buttons 102 source_pane = new JPanel(new BorderLayout()); 103 source_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,3)); 104 105 vertical_pane = new JPanel(new GridLayout(6,1)); 106 107 Gatherer.println("\tCreating source_label"); 108 source_label = new JLabel(get("Source")); 109 source_label.setHorizontalAlignment(JLabel.CENTER); 110 111 Gatherer.println("\tCreating source_general"); 112 source_general = new JToggleButton(get("General"), Gatherer.config.get("messages.source_general", false)); 113 source_general.setIcon(Utility.OFF_ICON); 114 source_general.setSelectedIcon(Utility.ON_ICON); 115 source_general.setHorizontalTextPosition(JToggleButton.CENTER); 116 source_general.setVerticalTextPosition(JToggleButton.BOTTOM); 117 source_general.addActionListener(this); 118 119 Gatherer.println("\tCreating source_browser"); 120 source_browser = new JToggleButton(get("Browser"), Gatherer.config.get("messages.source_browser", false)); 121 source_browser.setIcon(Utility.OFF_ICON); 122 source_browser.setSelectedIcon(Utility.ON_ICON); 123 source_browser.setHorizontalTextPosition(JToggleButton.CENTER); 124 source_browser.setVerticalTextPosition(JToggleButton.BOTTOM); 125 source_browser.addActionListener(this); 126 127 Gatherer.println("\tCreating source_mirroring"); 128 source_mirroring = new JToggleButton(get("Mirroring"), Gatherer.config.get("messages.source_mirroring", false)); 129 source_mirroring.setIcon(Utility.OFF_ICON); 130 source_mirroring.setSelectedIcon(Utility.ON_ICON); 131 source_mirroring.setHorizontalTextPosition(JToggleButton.CENTER); 132 source_mirroring.setVerticalTextPosition(JToggleButton.BOTTOM); 133 source_mirroring.addActionListener(this); 134 135 Gatherer.println("\tCreating source_session"); 136 source_session = new JToggleButton(get("Session"), Gatherer.config.get("messages.source_session", false)); 137 source_session.setIcon(Utility.OFF_ICON); 138 source_session.setSelectedIcon(Utility.ON_ICON); 139 source_session.setHorizontalTextPosition(JToggleButton.CENTER); 140 source_session.setVerticalTextPosition(JToggleButton.BOTTOM); 141 source_session.addActionListener(this); 142 143 Gatherer.println("\tCreating source_metaedit"); 144 source_metaedit = new JToggleButton(get("Metaedit"), Gatherer.config.get("messages.source_metaedit", false)); 145 source_metaedit.setIcon(Utility.OFF_ICON); 146 source_metaedit.setSelectedIcon(Utility.ON_ICON); 147 source_metaedit.setHorizontalTextPosition(JToggleButton.CENTER); 148 source_metaedit.setVerticalTextPosition(JToggleButton.BOTTOM); 149 source_metaedit.addActionListener(this); 150 151 Gatherer.println("\tCreating source_build"); 152 source_build = new JToggleButton(get("Build"), Gatherer.config.get("messages.source_build", false)); 153 source_build.setIcon(Utility.OFF_ICON); 154 source_build.setSelectedIcon(Utility.ON_ICON); 155 source_build.setHorizontalTextPosition(JToggleButton.CENTER); 156 source_build.setVerticalTextPosition(JToggleButton.BOTTOM); 157 source_build.addActionListener(this); 158 159 source_pane.add(source_label, BorderLayout.NORTH); 160 vertical_pane.add(source_general); 161 vertical_pane.add(source_browser); 162 vertical_pane.add(source_mirroring); 163 vertical_pane.add(source_session); 164 vertical_pane.add(source_metaedit); 165 vertical_pane.add(source_build); 166 source_pane.add(vertical_pane, BorderLayout.CENTER); 167 168 // Central pane 169 central_pane = new JPanel(new BorderLayout()); 170 171 Gatherer.println("\tCreating message_list"); 172 model = new MessageListModel(); 173 renderer = new MessageListRenderer(); 174 message_list = new JList(); 175 message_list.setModel(model); 176 message_list.setCellRenderer(renderer); 177 178 scroll_pane = new JScrollPane(message_list); 179 central_pane.add(scroll_pane, BorderLayout.CENTER); 180 181 Gatherer.println("\tCreating message_count"); 182 count_pane = new JPanel(new BorderLayout()); 183 args = new String[2]; 184 args[0] = model.getSizeStr(); 185 args[1] = model.getTotalSizeStr(); 186 message_count = new JLabel(get("Message_Count",args)); 187 message_count.setHorizontalAlignment(JLabel.RIGHT); 188 189 central_pane.add(scroll_pane, BorderLayout.CENTER); 190 central_pane.add(message_count, BorderLayout.SOUTH); 191 192 // Corner area 193 corner_pane = new JPanel(new BorderLayout()); 194 corner_pane.setBorder(BorderFactory.createEmptyBorder(3,5,5,5)); 195 196 Gatherer.println("\tCreating save_message"); 197 save_messages = new JButton(get("Save_Messages"), 198 new ImageIcon(Utility.RES_DIR+"save.gif")); 199 save_messages.addActionListener(this); 200 201 corner_pane.add(save_messages, BorderLayout.CENTER); 202 203 // Horizontal buttons. 204 level_pane = new JPanel(new BorderLayout()); 205 206 horizontal_pane = new JPanel(new GridLayout(1,4)); 207 208 Gatherer.println("\tCreating level_label"); 209 level_label = new JLabel(get("Level")); 210 level_label.setHorizontalAlignment(JLabel.CENTER); 211 212 Gatherer.println("\tCreating level_main"); 213 level_main = new JToggleButton(get("Main"), Gatherer.config.get("messages.level_main", false)); 214 level_main.setIcon(Utility.OFF_ICON); 215 level_main.setSelectedIcon(Utility.ON_ICON); 216 level_main.addActionListener(this); 217 218 Gatherer.println("\tCreating level_event"); 219 level_event = new JToggleButton(get("Event"), Gatherer.config.get("messages.level_event", false)); 220 level_event.setIcon(Utility.OFF_ICON); 221 level_event.setSelectedIcon(Utility.ON_ICON); 222 level_event.addActionListener(this); 223 224 Gatherer.println("\tCreating level_information"); 225 level_information = new JToggleButton(get("Information"), Gatherer.config.get("messages.level_information", false)); 226 level_information.setIcon(Utility.OFF_ICON); 227 level_information.setSelectedIcon(Utility.ON_ICON); 228 level_information.addActionListener(this); 229 230 Gatherer.println("\tCreating level_error"); 231 level_error = new JToggleButton(get("Error"), Gatherer.config.get("messages.level_error", false)); 232 level_error.setIcon(Utility.OFF_ICON); 233 level_error.setSelectedIcon(Utility.ON_ICON); 234 level_error.addActionListener(this); 235 236 level_pane.add(level_label, BorderLayout.NORTH); 237 horizontal_pane.add(level_main); 238 horizontal_pane.add(level_event); 239 horizontal_pane.add(level_information); 240 horizontal_pane.add(level_error); 241 level_pane.add(horizontal_pane, BorderLayout.CENTER); 242 243 // Terrifying gridbag layout 244 grid_constraints.fill = GridBagConstraints.BOTH; 245 grid_layout.setConstraints(source_pane, grid_constraints); 246 this.add(source_pane); 247 grid_constraints.weightx = 1.0; 248 grid_constraints.weighty = 1.0; 249 grid_constraints.gridwidth = GridBagConstraints.REMAINDER; 250 grid_layout.setConstraints(central_pane, grid_constraints); 251 this.add(central_pane); 252 grid_constraints.weightx = 0.0; 253 grid_constraints.weighty = 0.0; 254 grid_constraints.gridwidth = 1; 255 grid_layout.setConstraints(corner_pane, grid_constraints); 256 this.add(corner_pane); 257 grid_constraints.gridwidth = GridBagConstraints.REMAINDER; 258 grid_layout.setConstraints(level_pane, grid_constraints); 259 this.add(level_pane); 260 } 261 262 public void actionPerformed(ActionEvent event) { 263 Object source = event.getSource(); 264 265 // Eventually this will dump the messages to a text file, but for now 266 // I'm using it in testing. 267 if(source == save_messages) { 268 for(int i = 0; i < 20; i++) { 269 model.add(Message.random()); 270 message_list.ensureIndexIsVisible(model.getSize() - 1); 271 } 272 } 273 // We've been asked to toggle whether we show general messages. 274 else if(source == source_general) { 275 // Toggle config setting 276 Gatherer.config.set("messages.source_general", false, !Gatherer.config.get("messages.source_general", false)); 277 // Inform model something has changed. 278 model.refresh(); 279 message_list.ensureIndexIsVisible(model.getSize() - 1); 280 } 281 // We've been asked to toggle whether we show browser messages. 282 else if(source == source_browser) { 283 // Toggle config setting 284 Gatherer.config.set("messages.source_browser", false, !Gatherer.config.get("messages.source_browser", false)); 285 // Inform model something has changed. 286 model.refresh(); 287 message_list.ensureIndexIsVisible(model.getSize() - 1); 288 } 289 // We've been asked to toggle whether we show general messages. 290 else if(source == source_mirroring) { 291 // Toggle config setting 292 Gatherer.config.set("messages.source_mirroring", false, !Gatherer.config.get("messages.source_mirroring", false)); 293 // Inform model something has changed. 294 model.refresh(); 295 message_list.ensureIndexIsVisible(model.getSize() - 1); 296 } 297 // We've been asked to toggle whether we show session messages. 298 else if(source == source_session) { 299 // Toggle config setting 300 Gatherer.config.set("messages.source_session", false, !Gatherer.config.get("messages.source_session", false)); 301 // Inform model something has changed. 302 model.refresh(); 303 message_list.ensureIndexIsVisible(model.getSize() - 1); 304 } 305 // We've been asked to toggle whether we show metaedit messages. 306 else if(source == source_metaedit) { 307 // Toggle config setting 308 Gatherer.config.set("messages.source_metaedit", false, !Gatherer.config.get("messages.source_metaedit", false)); 309 // Inform model something has changed. 310 model.refresh(); 311 message_list.ensureIndexIsVisible(model.getSize() - 1); 312 } 313 // We've been asked to toggle whether we show build messages. 314 else if(source == source_build) { 315 // Toggle config setting 316 Gatherer.config.set("messages.source_build", false, !Gatherer.config.get("messages.source_build", false)); 317 // Inform model something has changed. 318 model.refresh(); 319 message_list.ensureIndexIsVisible(model.getSize() - 1); 320 } 321 // We've been asked to toggle whether we show main level messages. 322 else if(source == level_main) { 323 // Toggle config setting 324 Gatherer.config.set("messages.level_main", false, !Gatherer.config.get("messages.level_main", false)); 325 // Inform model something has changed. 326 model.refresh(); 327 message_list.ensureIndexIsVisible(model.getSize() - 1); 328 } 329 // We've been asked to toggle whether we show event level messages. 330 else if(source == level_event) { 331 // Toggle config setting 332 Gatherer.config.set("messages.level_event", false, !Gatherer.config.get("messages.level_event", false)); 333 // Inform model something has changed. 334 model.refresh(); 335 message_list.ensureIndexIsVisible(model.getSize() - 1); 336 } 337 // We've been asked to toggle whether we show information level messages. 338 else if(source == level_information) { 339 // Toggle config setting 340 Gatherer.config.set("messages.level_information", false, !Gatherer.config.get("messages.level_information", false)); 341 // Inform model something has changed. 342 model.refresh(); 343 message_list.ensureIndexIsVisible(model.getSize() - 1); 344 } 345 // We've been asked to toggle whether we show error level messages. 346 else if(source == level_error) { 347 // Toggle config setting 348 Gatherer.config.set("messages.level_error", false, !Gatherer.config.get("messages.level_error", false)); 349 // Inform model something has changed. 350 model.refresh(); 351 message_list.ensureIndexIsVisible(model.getSize() - 1); 352 } 353 // Pretty much no matter what happens we need to get the new size 354 args = new String[2]; 355 args[0] = model.getSizeStr(); 356 args[1] = model.getTotalSizeStr(); 357 message_count.setText(get("Message_Count",args)); 358 } 359 360 /** The callback for the message listener. Here all of the messages are 361 * displayed onto the list. 362 * @param message The Message to be displayed. 363 */ 364 public void show(Message message) { 365 model.add(message); 366 message_list.ensureIndexIsVisible(model.getSize() - 1); 367 } 368 369 370 private String get(String key) { 371 return get(key, null); 372 } 373 374 private String get(String key, String args[]) { 375 if(key.indexOf('.') == -1) { 376 key = "Messages." + key; 377 } 378 return Gatherer.dictionary.get(key, args); 379 } 380 380 } 381 381 -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/Autofilter.java
r4293 r4366 58 58 */ 59 59 public class Autofilter { 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 60 /** <i>true</i> if the filter should be applied, <i>false</i> to indicate the filter is turned off. */ 61 public boolean active; 62 /** <i>true</i> if the matching for the first expression should be case sensitive, <i>false</i> otherwise. */ 63 public boolean casesense_one; 64 /** <i>true</i> if the matching for the second expression should be case sensitive, <i>false</i> otherwise. */ 65 public boolean casesense_two; 66 /** Used to determine the operation intended when applying two filters, and set using the values of the OPERATION_TYPE enumeration. */ 67 public boolean operation; 68 /** Used to determine how the column this filter is applied to should be sorted, and set using the values of the SORT_TYPE enumeration. */ 69 public boolean sort; 70 /** The method to be used for the first filter expression, set from the values of the METHOD_LIST enumeration. */ 71 public int method_one; 72 /** The method to be used for the second filter expression, set from the values of the METHOD_LIST enumeration. */ 73 public int method_two; 74 /** The value to be matched against for the first expression. */ 75 public String value_one; 76 /** The value to be matched against for the second expression. */ 77 public String value_two; 78 /** An element of the SORT_TYPE enumeration, indicates lowest to highest value column ordering. */ 79 public static final boolean ASCENDING = true; 80 /** An element of the OPERATION_TYPE enumeration, indicates that both filter expressions must be met (conjunction). */ 81 public static final boolean AND = true; 82 /** An element of the SORT_TYPE enumeration, indicates highest to lowest value column ordering. */ 83 public static final boolean DESCENDING = false; 84 /** An element of the OPERATION_TYPE enumeration, indicates that either (or both) filter expressions must be met (disjunction). */ 85 public static final boolean OR = false; 86 /** An enumeration of symbolic names of various matching methods. */ 87 public static final String METHOD_LIST[] = {"eqeq", "!eq", "<", "<eq", ">", ">eq", "^", "!^", "$", "!$", "?", "!?"}; 88 /** Default Constructor. */ 89 public Autofilter() { 90 operation = OR; 91 sort = ASCENDING; 92 } 93 /** Determine if this filter is currently active. 94 * @return <i>true</i> if it is active, <i>false</i> otherwise. 95 */ 96 public boolean active() { 97 return active; 98 } 99 /** Determine if this list of values (for a certain cell) passes the filter. 100 * @param values An <strong>ArrayList</strong> of values sourced from a single cell in the associated column. 101 * @return <i>true</i> if the values match and should be displayed, <i>false</i> otherwise. 102 */ 103 public boolean filter(ArrayList values) { 104 boolean result = false; 105 if(value_one != null) { 106 result = filter(values, method_one, value_one, casesense_one); 107 if(result) { 108 if(operation == AND && value_two != null) { 109 result = filter(values, method_two, value_two, casesense_two); 110 } 111 } 112 else if(operation == OR && value_two != null) { 113 result = filter(values, method_two, value_two, casesense_two); 114 } 115 } 116 return result; 117 } 118 /** Set the current activity state of this filter. 119 * @param active The new state of this filter, <i>true</i> to activate, <i>false</i> otherwise. 120 */ 121 public void setActive(boolean active) { 122 this.active = active; 123 } 124 /** Set one of the filter expressions using the given information. 125 * @param number The number of the filter you wish to set as an <i>int</i>. Either 1 or 2. 126 * @param method An <i>int</i> indicating the method to be used when matching. 127 * @param value The <strong>String</strong> to be matched against. 128 * @param casesense <i>true</i> if this expression should be case sensitive, <i>false</i> otherwise. 129 */ 130 public void setFilter(int number, int method, String value, boolean casesense) { 131 if(!casesense && value != null) { 132 value = value.toLowerCase(); 133 } 134 if(number == 1) { 135 casesense_one = casesense; 136 method_one = method; 137 value_one = value; 138 } 139 else { 140 casesense_two = casesense; 141 method_two = method; 142 value_two = value; 143 } 144 } 145 /** Set the operation to be used to join the two filters (if a second filter is set). 146 * @param operation <i>true</i> for conjunct filters, <i>false</i> for disjunct. 147 */ 148 public void setOperation(boolean operation) { 149 this.operation = operation; 150 } 151 /** Set the sort order of this column. 152 * @param sort <i>true</i> for ascending sort, <i>false</i> for descending. 153 */ 154 public void setSort(boolean sort) { 155 this.sort = sort; 156 } 157 /** Decide whether a row should be displayed or filtered. The result depends on the selector set with setFilterType. 158 * @param values An <strong>ArrayList</strong> of values to be checked against the filter. 159 * @param method The method of matching to be used, as an <i>int</i>. 160 * @param target The <strong>String</Strong> to match against. 161 * @param casesense <i>true</i> if the match is to be case sensitive, <i>false</i> otherwise. 162 * @return <i>true</i> to display the row, <i>false</i> to hide it. 163 */ 164 public boolean filter(ArrayList values, int method, String target, boolean casesense) { 165 boolean result = false; 166 // There are several special cases when the filter always returns turn, such as when the target is the wildcard character. 167 if(target == null || target.length() == 0 || target.equals("*")) { 168 result = true; 169 } 170 else { 171 171 // For each value in the list... 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 172 for(int i = 0; i < values.size(); i++) { 173 boolean pass; 174 String source; 175 // Account for case sensitivity. 176 if(casesense) { 177 source = values.get(i).toString(); 178 } 179 else { 180 source = values.get(i).toString().toLowerCase(); 181 } 182 // Perform the match, based on the selected method. 183 switch(method) { 184 case 1: // !EQ 185 pass = !(source.equals(target)); 186 break; 187 case 2: // < 188 pass = (source.compareTo(target) < 0); 189 break; 190 case 3: // <eq 191 pass = (source.compareTo(target) <= 0); 192 break; 193 case 4: // > 194 pass = (source.compareTo(target) > 0); 195 break; 196 case 5: // >eq 197 pass = (source.compareTo(target) >= 0); 198 break; 199 case 6: // ^ 200 pass = source.startsWith(target); 201 break; 202 case 7: // !^ 203 pass = !(source.startsWith(target)); 204 break; 205 case 8: // $ 206 pass = source.endsWith(target); 207 break; 208 case 9: // !$ 209 pass = !(source.endsWith(target)); 210 break; 211 case 10: // ? 212 pass = (source.indexOf(target) != -1); 213 break; 214 case 11: // !? 215 pass = (source.indexOf(target) == -1); 216 break; 217 default: // EQEQ 218 pass = source.equals(target); 219 break; 220 } 221 result = result || pass; 222 } 223 } 224 return result; 225 } 226 /** Produce a textual representation of this autofilter. 227 * @return A <strong>String</strong> displaying details of this autofilter. 228 */ 229 public String toString() { 230 String result = "One: " + method_one + " - " + value_one + " - " + casesense_one; 231 if(value_two != null) { 232 result = result + "\n" + "Operation: " + (operation?"AND":"OR") + "\n"; 233 result = result + "Two: " + method_two + " - " + value_two + " - " + casesense_two; 234 } 235 return result; 236 } 237 237 } 238 238 -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/AutofilterDialog.java
r4293 r4366 49 49 */ 50 50 public final class AutofilterDialog 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 51 extends JDialog { 52 /** The filter being edited. */ 53 private Autofilter filter; 54 /** A reference to ourselves so our inner classes can reference us. */ 55 private AutofilterDialog self; 56 /** The value returned from the filter dialog prompt. Used to determine if a prompt was set or unset, and what subsequent action to take. */ 57 private byte return_value = 0; 58 /** The button used to cancel the prompt. */ 59 private JButton cancel_button = null; 60 /** The button used to accept and commit any changes to the autofilter. */ 61 private JButton ok_button = null; 62 /** A check box used to specify that the given filters should be applied conjunctly. */ 63 private JCheckBox and_check = null; 64 /** The check box used to specify whether the first filter is case sensitive. */ 65 private JCheckBox first_case = null; 66 /** The none check is used to disable the second filter. */ 67 private JCheckBox none_check = null; 68 /** The check box used to specify that the given filters should be applied disjunctly. */ 69 private JCheckBox or_check = null; 70 /** The check box used to specify whether the second filter is case sensitive. */ 71 private JCheckBox second_case = null; 72 /** Used to specify the order of the resulting set: Ascending or Descending. */ 73 private JComboBox order = null; 74 /** The method used to match the first filter: Contains, Doesn't contain etc. */ 75 private JComboBox first_method = null; 76 /** The value to be matched for the first filter. */ 77 private JComboBox first_value = null; 78 /** The method used to match the first filter. Options as for the first method. */ 79 private JComboBox second_method = null; 80 /** The value to be matched for the second filter. */ 81 private JComboBox second_value = null; 82 /** Used for the most basic filter where an Equals, Case sensitive method is automatically used. */ 83 private JComboBox value = null; 84 /** The label which displays the name of the currently selected column (the column that will be associated with the autofilter this dialog produces). */ 85 private JLabel name; 86 /** The autofilter prompt contains two different panes, one for basic functionality and one for advanced. This containiner component is used to allow access to each via a 'tabbed' interface. */ 87 private JTabbedPane control = null; 88 /** A reference back to the MetaAudit dialog that spawned this prompt. Used to make sure that any open dialog window is always in front of the audit pane. */ 89 private MetaAuditFrame dialog; 90 /** The default size for the autofilter control. */ 91 static final private Dimension SIZE = new Dimension(640, 220); 92 /** Constructor. 93 * @param dialog A reference to the <strong>MetaAuditFrame</strong> that spawned this dialog. 94 * @see org.greenstone.gatherer.gui.metaaudit.Autofilter 95 * @see org.greenstone.gatherer.gui.metaaudit.AutofilterDialog.ButtonListener 96 * @see org.greenstone.gatherer.gui.metaaudit.AutofilterDialog.CheckListener 97 */ 98 public AutofilterDialog(MetaAuditFrame dialog) { 99 super(Gatherer.g_man); 100 this.dialog = dialog; 101 this.self = this; 102 setModal(true); 103 setSize(SIZE); 104 setTitle(get("Title")); 105 // Creation 106 JPanel content_pane = (JPanel) getContentPane(); 107 JPanel name_pane = new JPanel(); 108 JLabel name_label = new JLabel(get("Name")); 109 JTextField name_template = new JTextField(); 110 name = new JLabel(); 111 name.setBorder(name_template.getBorder()); 112 control = new JTabbedPane(); 113 JPanel value_pane = new JPanel(); 114 JPanel inner_value_pane = new JPanel(); 115 JLabel value_label = new JLabel(get("eqeq")); 116 value = new JComboBox(); 117 value.setEditable(false); 118 JPanel custom_pane = new JPanel(); 119 JPanel first_pane = new JPanel(); 120 first_method = new JComboBox(); 121 first_value = new JComboBox(); 122 first_value.setEditable(true); 123 first_value.addItem(""); 124 first_value.setSelectedItem(""); 125 first_case = new JCheckBox(get("Case_Sensitive")); 126 JPanel operator_pane = new JPanel(); 127 JLabel operator_label = new JLabel(get("Operator")); 128 ButtonGroup operator_group = new ButtonGroup(); 129 and_check = new JCheckBox(get("AND")); 130 none_check = new JCheckBox(get("None")); 131 or_check = new JCheckBox(get("OR")); 132 none_check.setSelected(true); 133 operator_group.add(none_check); 134 operator_group.add(and_check); 135 operator_group.add(or_check); 136 JPanel second_pane = new JPanel(); 137 second_method = new JComboBox(); 138 second_method.setEnabled(false); 139 second_value = new JComboBox(); 140 second_value.setEditable(true); 141 second_value.setEnabled(false); 142 second_value.addItem(""); 143 second_value.setSelectedItem(""); 144 second_case = new JCheckBox(get("Case_Sensitive")); 145 JPanel lower_pane = new JPanel(); 146 JPanel order_pane = new JPanel(); 147 JLabel order_label = new JLabel(get("Order")); 148 order = new JComboBox(); 149 order.addItem(get("Ascending")); 150 order.addItem(get("Descending")); 151 // Assign values to method comboboxes. 152 for(int i = 0; i < Autofilter.METHOD_LIST.length; i++) { 153 first_method.addItem(get(Autofilter.METHOD_LIST[i])); 154 second_method.addItem(get(Autofilter.METHOD_LIST[i])); 155 } 156 JPanel button_pane = new JPanel(); 157 cancel_button = new JButton(get("Clear")); 158 cancel_button.setMnemonic(KeyEvent.VK_C); 159 ok_button = new JButton(get("Set")); 160 ok_button.setMnemonic(KeyEvent.VK_S); 161 // Connection 162 and_check.addActionListener(new CheckListener(true)); 163 cancel_button.addActionListener(new ButtonListener(false)); 164 none_check.addActionListener(new CheckListener(false)); 165 ok_button.addActionListener(new ButtonListener(true)); 166 or_check.addActionListener(new CheckListener(true)); 167 // Layout 168 name_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 169 170 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 171 name_pane.setLayout(new BorderLayout()); 172 name_pane.add(name_label, BorderLayout.WEST); 173 name_pane.add(name, BorderLayout.CENTER); 174 175 value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 176 177 inner_value_pane.setLayout(new BorderLayout()); 178 inner_value_pane.add(value_label, BorderLayout.WEST); 179 inner_value_pane.add(value, BorderLayout.CENTER); 180 181 value_pane.setBorder(BorderFactory.createEmptyBorder(5,10,5,10)); 182 value_pane.setLayout(new BorderLayout()); 183 value_pane.add(inner_value_pane, BorderLayout.NORTH); 184 185 first_pane.setLayout(new BorderLayout()); 186 first_pane.add(first_method, BorderLayout.WEST); 187 first_pane.add(first_value, BorderLayout.CENTER); 188 first_pane.add(first_case, BorderLayout.EAST); 189 190 operator_pane.setLayout(new GridLayout(1,4)); 191 operator_pane.add(operator_label); 192 operator_pane.add(none_check); 193 operator_pane.add(and_check); 194 operator_pane.add(or_check); 195 196 second_pane.setLayout(new BorderLayout()); 197 second_pane.add(second_method, BorderLayout.WEST); 198 second_pane.add(second_value, BorderLayout.CENTER); 199 second_pane.add(second_case, BorderLayout.EAST); 200 201 order_pane.setLayout(new GridLayout(1,2)); 202 order_pane.add(order_label); 203 order_pane.add(order); 204 205 custom_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); 206 custom_pane.setLayout(new GridLayout(3,1)); 207 custom_pane.add(first_pane); 208 custom_pane.add(operator_pane); 209 custom_pane.add(second_pane); 210 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 211 control.add(get("Filter_By_Value"), value_pane); 212 control.add(get("Custom_Filter"), custom_pane); 213 214 button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 215 button_pane.setLayout(new GridLayout(1,2)); 216 button_pane.add(ok_button); 217 button_pane.add(cancel_button); 218 219 lower_pane.setLayout(new BorderLayout()); 220 lower_pane.add(order_pane, BorderLayout.CENTER); 221 lower_pane.add(button_pane, BorderLayout.SOUTH); 222 223 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 224 content_pane.setLayout(new BorderLayout()); 225 content_pane.add(name_pane, BorderLayout.NORTH); 226 content_pane.add(control, BorderLayout.CENTER); 227 content_pane.add(lower_pane, BorderLayout.SOUTH); 228 229 Dimension screen_size = Gatherer.config.screen_size; 230 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 231 screen_size = null; 232 } 233 /** Destructor. */ 234 public void destroy() { 235 dispose(); 236 cancel_button = null; 237 ok_button = null; 238 and_check = null; 239 first_case = null; 240 none_check = null; 241 or_check = null; 242 second_case = null; 243 order = null; 244 first_method = null; 245 first_value = null; 246 second_method = null; 247 second_value = null; 248 value = null; 249 control = null; 250 dialog = null; 251 self = null; 252 name = null; 253 } 254 /** Display the modal dialog box, allowing the user to define the filter. When the user presses one of the buttons, dispose and return to the caller providing an indication of which button was pressed. 255 255 * @return An <i>int</i> which indicates which button was pressed to dismiss the dialog. 256 256 */ 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 257 public Autofilter display(Autofilter filter, ArrayList raw_values, String column_name) { 258 this.filter = filter; 259 name.setText(column_name); 260 // Prune values so it only contains unique entries, then order. 261 TreeSet values = new TreeSet(raw_values); 262 value.setModel(new DefaultComboBoxModel(values.toArray())); 263 String star = "*"; 264 value.insertItemAt(star, 0); 265 value.setSelectedItem(star); 266 first_value.setModel(new DefaultComboBoxModel(values.toArray())); 267 second_value.setModel(new DefaultComboBoxModel(values.toArray())); 268 // Restore previous values. 269 if(filter != null && filter.value_one != null) { 270 value.setSelectedItem(filter.value_one); 271 first_method.setSelectedIndex(filter.method_one); 272 first_value.setSelectedItem(filter.value_one); 273 first_case.setSelected(filter.casesense_one); 274 if(filter.value_two == null) { 275 none_check.setSelected(true); 276 } 277 else { 278 if(filter.operation) { 279 and_check.setSelected(true); 280 } 281 else { 282 or_check.setSelected(true); 283 } 284 second_method.setSelectedIndex(filter.method_two); 285 second_value.setSelectedItem(filter.value_two); 286 second_case.setSelected(filter.casesense_two); 287 } 288 if(filter.sort) { 289 order.setSelectedIndex(0); 290 } 291 else { 292 order.setSelectedIndex(1); 293 } 294 } 295 // Display 296 show(); 297 dialog.toFront(); 298 return this.filter; 299 } 300 /** Use the given key to retrieve a phrase from the dictionary. 301 301 * @param key A <strong>String</strong> which uniquely maps to an entry in the Dictionary. 302 302 * @return The <strong>String</strong> to key mapped to. 303 303 * @see org.greenstone.gatherer.Dictionary 304 304 */ 305 private final String get(String key) { 306 if(key.indexOf(".") == -1) { 307 key = "Autofilter." + key; 308 } 309 return Gatherer.dictionary.get(key, (String[])null); 310 } 311 /** Listens for actions on the button it is attached to, and when notified sets the return_value and disposes of the dialog. */ 312 private final class ButtonListener 313 implements ActionListener { 314 private boolean return_filter; 315 /** Does an action on this button cause a filter to be returned. */ 316 /** Constructor takes an associated return value as an argument. 317 * @param return_filter <i>true</i> if we update then return the filter, <i>false</i> to clear existing filter. 305 private final String get(String key) { 306 if(key.indexOf(".") == -1) { 307 key = "Autofilter." + key; 308 } 309 return Gatherer.dictionary.get(key, (String[])null); 310 } 311 /** Listens for actions on the button it is attached to, and when notified sets the return_value and disposes of the dialog. */ 312 private final class ButtonListener 313 implements ActionListener { 314 private boolean return_filter; 315 /** Does an action on this button cause a filter to be returned. */ 316 /** Constructor takes an associated return value as an argument. 317 * @param return_filter <i>true</i> if we update then return the filter, <i>false</i> to clear existing filter. 318 */ 319 public ButtonListener(boolean return_filter) { 320 this.return_filter = return_filter; 321 } 322 /** When any registered component is actioned apon, set the value and hide the dialog. We hide rather than dispose, because hide assures the data values will be retained. 323 * @param event An <strong>ActionEvent</strong> containing information about the action that caused this method call. 324 * @see org.greenstone.gatherer.gui.metaaudit.Autofilter 325 */ 326 public void actionPerformed(ActionEvent event) { 327 if(return_filter) { 328 if(control.getSelectedIndex() == 0) { 329 filter.setFilter(1, 0, (String)value.getSelectedItem(), true); 330 filter.setFilter(2, 0, null, true); 331 } 332 else { 333 filter.setFilter(1, first_method.getSelectedIndex(), (String)first_value.getSelectedItem(), first_case.isSelected()); 334 if(!none_check.isSelected()) { 335 if(and_check.isSelected()) { 336 filter.setOperation(Autofilter.AND); 337 } 338 else { 339 filter.setOperation(Autofilter.OR); 340 } 341 filter.setFilter(2, second_method.getSelectedIndex(), (String)second_value.getSelectedItem(), second_case.isSelected()); 342 } 343 } 344 if(order.getSelectedIndex() == 0) { 345 filter.setSort(Autofilter.ASCENDING); 346 } 347 else { 348 filter.setSort(Autofilter.DESCENDING); 349 } 350 } 351 else { 352 filter = null; 353 } 354 hide(); 355 } 356 } 357 /** Listens for actions on the check box it is attached to, and when notified sets the state of the second method and value to the specified state. */ 358 private final class CheckListener 359 implements ActionListener { 360 /** The default desire state any check button this listens to. */ 361 private boolean desired_state = false; 362 /** The constructor takes an associated desired state. 363 * @param desired_state The state that should be set when this is actioned, as a <i>boolean</i>. 318 364 */ 319 public ButtonListener(boolean return_filter) { 320 this.return_filter = return_filter; 321 } 322 /** When any registered component is actioned apon, set the value and hide the dialog. We hide rather than dispose, because hide assures the data values will be retained. 323 * @param event An <strong>ActionEvent</strong> containing information about the action that caused this method call. 324 * @see org.greenstone.gatherer.gui.metaaudit.Autofilter 325 */ 326 public void actionPerformed(ActionEvent event) { 327 if(return_filter) { 328 if(control.getSelectedIndex() == 0) { 329 filter.setFilter(1, 0, (String)value.getSelectedItem(), true); 330 filter.setFilter(2, 0, null, true); 331 } 332 else { 333 filter.setFilter(1, first_method.getSelectedIndex(), (String)first_value.getSelectedItem(), first_case.isSelected()); 334 if(!none_check.isSelected()) { 335 if(and_check.isSelected()) { 336 filter.setOperation(Autofilter.AND); 337 } 338 else { 339 filter.setOperation(Autofilter.OR); 340 } 341 filter.setFilter(2, second_method.getSelectedIndex(), (String)second_value.getSelectedItem(), second_case.isSelected()); 342 } 343 } 344 if(order.getSelectedIndex() == 0) { 345 filter.setSort(Autofilter.ASCENDING); 346 } 347 else { 348 filter.setSort(Autofilter.DESCENDING); 349 } 350 } 351 else { 352 filter = null; 353 } 354 hide(); 355 } 356 } 357 /** Listens for actions on the check box it is attached to, and when notified sets the state of the second method and value to the specified state. */ 358 private final class CheckListener 359 implements ActionListener { 360 /** The default desire state any check button this listens to. */ 361 private boolean desired_state = false; 362 /** The constructor takes an associated desired state. 363 * @param desired_state The state that should be set when this is actioned, as a <i>boolean</i>. 364 */ 365 public CheckListener(boolean desired_state) { 366 this.desired_state = desired_state; 367 } 368 /** Whenever our registered components are actioned apon, set the state of the second method and value to the desired state. */ 369 public void actionPerformed(ActionEvent event) { 370 second_method.setEnabled(desired_state); 371 second_value.setEnabled(desired_state); 372 } 373 } 365 public CheckListener(boolean desired_state) { 366 this.desired_state = desired_state; 367 } 368 /** Whenever our registered components are actioned apon, set the state of the second method and value to the desired state. */ 369 public void actionPerformed(ActionEvent event) { 370 second_method.setEnabled(desired_state); 371 second_value.setEnabled(desired_state); 372 } 373 } 374 374 } 375 375 -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/Filter.java
r4293 r4366 63 63 */ 64 64 public class Filter 65 66 67 68 69 70 71 65 implements TableModelFilter { 66 /** An array of autofilters. */ 67 private Autofilter filters[]; 68 /** The registered TableModelFilterListeners. */ 69 private Vector listeners = new Vector (); 70 /** Constructor. 71 * @param columns The number of columns this Filter will be expected to filter, as an <i>int</i>. 72 72 */ 73 74 75 76 73 public Filter(int columns) { 74 filters = new Autofilter[columns]; 75 } 76 /** Add a TableModelFilterListener to the TableModelFilter. 77 77 * @param listener The <strong>TableModelFilterListener</strong> to add, if it isn't already registered. 78 78 */ 79 80 81 82 83 84 79 public synchronized void addTableModelFilterListener(TableModelFilterListener listener) { 80 if (!listeners.contains(listener)) { 81 listeners.addElement(listener); 82 } 83 } 84 /** Remove a filter from a column. 85 85 * @param column The column number to remove the filter from, as an <i>int</i>. 86 86 */ 87 88 89 90 91 92 87 public void clearFilter(int column) { 88 if(filters[column] != null) { 89 filters[column].setActive(false); 90 } 91 } 92 /** Determine if a certain row should be shown in the table by checking it against all the current filters. 93 93 * @param model The <strong>TableModel</strong> the row is from. 94 94 * @param row The row number as an <i>int</i> 95 95 * @return <i>true</i> if the rows data matches all autofilters set and should be displayed, <i>false</i> otherwise. 96 96 */ 97 98 99 100 101 102 103 104 105 106 107 97 public boolean filter(TableModel model, int row) { 98 boolean result = true; 99 for(int i = 0; result && i < filters.length; i++) { 100 if(filters[i] != null && filters[i].active()) { 101 ArrayList values = (ArrayList) model.getValueAt(row, i); 102 result = result && filters[i].filter(values); 103 } 104 } 105 return result; 106 } 107 /** Retrieve the autofilter associated with a certain column. 108 108 * @param column The column number as an <i>int</i>. 109 109 * @return The <strong>Autofilter</strong> assigned to that column. 110 110 */ 111 112 113 114 115 116 117 118 119 111 public Autofilter getFilter(int column) { 112 Autofilter filter = filters[column]; 113 if(filter == null) { 114 filter = new Autofilter(); 115 filters[column] = filter; 116 } 117 return filter; 118 } 119 /** Determine if a certain column is filtered. 120 120 * @param column The column number as an <i>int</i>. 121 121 * @return <i>true</i> if there is an active autofilter assigned to this column, <i>false</i> otherwise. 122 122 */ 123 124 125 126 127 128 129 130 123 public boolean isFiltered(int column) { 124 boolean result = false; 125 if(filters[column] != null) { 126 result = filters[column].active(); 127 } 128 return result; 129 } 130 /** Remove a TableModelFilterListener from the TableModelFilter. 131 131 * @param listener The <strong>TableModelFilterListener</strong> to remove. 132 132 */ 133 134 135 136 137 138 139 140 141 142 133 public synchronized void removeTableModelFilterListener(TableModelFilterListener listener) { 134 listeners.removeElement(listener); 135 } 136 /** Called whenever the filters assigned changes significantly, thus prompting a reload of the table model data. */ 137 public void fireFilterChanged() { 138 ///ystem.err.println("Filter Changed!"); 139 Vector tmp; 140 synchronized(this) { 141 tmp = (Vector) listeners.clone(); 142 } 143 143 144 145 146 147 148 149 144 TableModelFilterEvent event = new TableModelFilterEvent (this); 145 for (Enumeration enum = tmp.elements(); 146 enum.hasMoreElements(); ) { 147 ((TableModelFilterListener)enum.nextElement()).filterChanged(event); 148 } 149 } 150 150 } -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/HeaderListener.java
r4293 r4366 60 60 61 61 public class HeaderListener 62 63 64 65 66 67 68 62 extends MouseAdapter { 63 private MetaAuditFrame parent_frame; 64 private MetaAuditTable table; 65 public HeaderListener(MetaAuditFrame parent_frame, MetaAuditTable table) { 66 this.parent_frame = parent_frame; 67 this.table = table; 68 } 69 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 70 public void mouseClicked(MouseEvent event) { 71 // Determine the column this was clicked on. 72 int column = table.getColumnModel().getColumnIndexAtX(event.getX()); 73 int clicked_column = table.convertColumnIndexToModel(column); 74 // Display the currently assigned filter. 75 Filter filter_model = table.getFilter(); 76 Autofilter filter = filter_model.getFilter(clicked_column); 77 MetaAuditModel model = table.getOriginalModel(); 78 ArrayList default_values = model.getColumnValues(clicked_column); 79 String column_name = model.getColumnName(clicked_column); 80 filter = parent_frame.autofilter_dialog.display(filter, default_values, column_name); 81 if(filter == null) { 82 filter_model.clearFilter(clicked_column); 83 } 84 else { 85 filter.setActive(true); 86 TableModelSorter sorter = table.getSorter(); 87 sorter.setSortColumn(clicked_column); 88 sorter.setSortAscending(filter.sort); 89 } 90 filter_model.fireFilterChanged(); 91 } 92 92 } 93 93 -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/HeaderRenderer.java
r4293 r4366 47 47 */ 48 48 public class HeaderRenderer 49 50 51 52 53 54 55 56 57 58 59 49 extends JToggleButton 50 implements TableCellRenderer { 51 /** The constructor sets up a margin around the button, and adds the filter icons. 52 * @see org.greenstone.gatherer.util.Utility 53 */ 54 public HeaderRenderer() { 55 setMargin(new Insets(0,0,0,0)); 56 setIcon(Utility.getImage("filter.gif")); 57 setSelectedIcon(Utility.getImage("filter-on.gif")); 58 } 59 /** Called to create the component to be used as the 'rubber-stamp' for the table cell given the follwing arguments. 60 60 * @param table The <strong>JTable</strong> that is asking the renderer to draw; can be <i>null</i>. 61 61 * @param value The value of the cell to be rendered as an <strong>Object</strong>. It is up to the specific renderer to interpret and draw the value. For example, if value is the string "true", it could be rendered as a string or it could be rendered as a check box that is checked. null is a valid value. … … 66 66 * @see org.greenstone.gatherer.gui.metaaudit.MetaAuditTable 67 67 */ 68 69 70 71 72 73 74 75 68 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 69 setText(value.toString()); 70 MetaAuditTable temp = (MetaAuditTable) table; 71 if(temp != null) { // And it may if this is called during table init. 72 setSelected(temp.isFiltered(column)); 73 } 74 return this; 75 } 76 76 } -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/MetaAuditFrame.java
r4293 r4366 78 78 */ 79 79 public class MetaAuditFrame 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 80 extends JDialog 81 implements TreeSelectionListener { 82 public AutofilterDialog autofilter_dialog; 83 /** Whether the selection has changed since the last time we built the model (because it has been hidden and theres no point refreshing a model you can't see!). */ 84 private boolean invalid = true; 85 /** An array holding the most recent list of paths selected (plus some nulls for those paths removed). */ 86 private FileNode records[]; 87 /** A reference to ourselves so that inner classes can interact with us. */ 88 private MetaAuditFrame self; 89 /** The table in which the metadata is shown. */ 90 private MetaAuditTable table; 91 /** The default size for the metaaudit dialog. */ 92 static final private Dimension SIZE = new Dimension(640,480); 93 /** The tolerance used to determine between a column resize, and a request for an AutoFilterDialog. */ 94 static final private int TOLERANCE = 3; 95 /** Constructor.*/ 96 public MetaAuditFrame(TreeSynchronizer tree_sync, FileNode records[]) { 97 super(); 98 // Arguments 99 this.autofilter_dialog = new AutofilterDialog(this); 100 this.records = records; 101 this.self = this; 102 this.table = new MetaAuditTable(this); 103 // Creation 104 setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); 105 setSize(SIZE); 106 setTitle(get("Title")); 107 108 JPanel content_pane = (JPanel) getContentPane(); 109 JPanel button_pane = new JPanel(); 110 111 JButton close_button = new JButton(get("Close")); 112 close_button.setMnemonic(KeyEvent.VK_C); 113 // Connection 114 close_button.addActionListener(new CloseListener()); 115 tree_sync.addTreeSelectionListener(this); 116 // Layout 117 button_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 118 button_pane.setLayout(new BorderLayout()); 119 button_pane.add(close_button, BorderLayout.CENTER); 120 120 121 122 123 124 121 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 122 content_pane.setLayout(new BorderLayout()); 123 content_pane.add(new JScrollPane(table), BorderLayout.CENTER); 124 content_pane.add(button_pane, BorderLayout.SOUTH); 125 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 126 Dimension screen_size = Gatherer.config.screen_size; 127 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 128 screen_size = null; 129 close_button = null; 130 button_pane = null; 131 content_pane = null; 132 } 133 /** Destructor. */ 134 public void destroy() { 135 records = null; 136 self = null; 137 table = null; 138 } 139 /** Display the dialog on screen. */ 140 public void display() { 141 if(invalid) { 142 rebuildModel(); 143 } 144 show(); 145 } 146 /** This method is called whenever the selection within the collection tree changes. This causes the table to be rebuilt with a new model. 147 * @param event A <strong>TreeSelectionEvent</strong> containing information about the event. 148 */ 149 public void valueChanged(TreeSelectionEvent event) { 150 if(event.getSource() instanceof JTree) { 151 TreePath paths[] = Gatherer.g_man.metaedit_pane.collection_tree.getSelectionPaths(); 152 if(paths != null) { 153 records = new FileNode[paths.length]; 154 for(int i = 0; i < paths.length; i++) { 155 records[i] = (FileNode) paths[i].getLastPathComponent(); 156 } 157 if(isVisible()) { 158 rebuildModel(); 159 } 160 else { 161 invalid = true; 162 } 163 } 164 } 165 } 166 167 public void wait(boolean waiting) { 168 Component glass_pane = getGlassPane(); 169 if(waiting) { 170 170 // Show wait cursor. 171 172 173 174 171 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 172 glass_pane.setVisible(true); 173 } 174 else { 175 175 // Hide wait cursor. 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 176 glass_pane.setVisible(false); 177 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 178 } 179 glass_pane = null; 180 } 181 182 /** Retrieve a phrase from the Dictionary. 183 * @param key A <strong>String</strong> which uniquely maps to a phrase. 184 * @return The desired phrase as a </strong>String</strong>. 185 * @see org.greenstone.gatherer.Dictionary 186 */ 187 private String get(String key) { 188 return Gatherer.dictionary.get("MetaAudit." + key); 189 } 190 /** Rebuild the metaaudit table model using the current collection tree selection. 191 */ 192 private void rebuildModel() { 193 // Build and set model 194 table.newModel(records); 195 // Done. 196 invalid = false; 197 } 198 /** Listens for actions apon the close button, and if detected closes the dialog (storing its current size first). */ 199 private class CloseListener 200 implements ActionListener { 201 /** Any implementation of ActionListener must include this method so that we can be informed when an action has been performed on our target control(s). 202 * @param event An <strong>ActionEvent</strong> containing information about the event. 203 */ 204 public void actionPerformed(ActionEvent event) { 205 self.hide(); 206 } 207 } 208 208 } 209 209 -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/MetaAuditModel.java
r4293 r4366 51 51 */ 52 52 public class MetaAuditModel 53 53 extends AbstractTableModel { 54 54 55 56 55 private ArrayList elements; 56 private MetadataTableCache data; 57 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 58 public MetaAuditModel(FileNode records[]) { 59 this.elements = new ArrayList(Gatherer.c_man.getCollection().msm.getElements()); 60 Collections.sort(this.elements); 61 this.data = new MetadataTableCache(elements.size() + 1); 62 // Work our way through the record array provided, adding files, skipping blanks and recursively searching directories. 63 for(int i = 0; records != null && i < records.length; i++) { 64 if(records[i] != null) { 65 addRecord(records[i]); 66 } 67 } 68 } 69 public Class getColumnClass(int column) { 70 return ArrayList.class; 71 } 72 public int getColumnCount() { 73 return elements.size() + 1; 74 } 75 public String getColumnName(int column) { 76 String result; 77 if(column == 0) { 78 result = Gatherer.dictionary.get("AuditTable.File"); 79 } 80 else { 81 result = elements.get(column - 1).toString(); 82 } 83 return result; 84 } 85 public ArrayList getColumnValues(int column) { 86 ArrayList column_values = new ArrayList(); 87 if(0 <= column && column < getColumnCount()) { 88 for(int i = 0; i < data.size(); i++) { 89 ArrayList values = (ArrayList) data.get(i, column); 90 for(int j = 0; j < values.size(); j++) { 91 column_values.add(values.get(j).toString()); 92 } 93 } 94 } 95 return column_values; 96 } 97 public int getRowCount() { 98 return data.size(); 99 } 100 public Object getValueAt(int row, int column) { 101 Object result = ""; 102 if(0 <= row && row < data.size() && 0 <= column && column < elements.size() + 1) { 103 result = data.get(row, column); 104 } 105 return result; 106 } 107 107 108 109 108 public void reset() { 109 } 110 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 111 private void addRecord(FileNode record) { 112 if(record.isLeaf()) { 113 File file = record.getFile(); 114 data.newRow(); 115 data.put(0, file.getName()); 116 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(file); 117 for(int i = 0; i < metadatum.size(); i++) { 118 Metadata metadata = (Metadata) metadatum.get(i); 119 data.put(elements.indexOf(metadata.getElement()) + 1, metadata.getValueNode()); 120 } 121 } 122 else { 123 for(int i = 0; i < record.getChildCount(); i++) { 124 addRecord((FileNode)record.getChildAt(i)); 125 } 126 } 127 } 128 128 129 130 131 132 133 134 135 136 137 129 private class MetadataTableCache { 130 private ArrayList data; 131 private ArrayList current_row[]; 132 private int columns; 133 private RowComparator row_comparator = new RowComparator(); 134 public MetadataTableCache(int columns) { 135 this.columns = columns; 136 this.data = new ArrayList(); 137 } 138 138 139 140 141 142 139 public ArrayList get(int row, int column) { 140 ArrayList array_list[] = (ArrayList[]) data.get(row); 141 return array_list[column]; 142 } 143 143 144 145 146 147 148 149 150 151 152 153 154 155 144 public void newRow() { 145 if(current_row != null) { 146 for(int i = 0; i < columns; i++) { 147 Collections.sort(current_row[i], row_comparator); 148 } 149 } 150 current_row = new ArrayList[columns]; 151 for(int i = 0; i < columns; i++) { 152 current_row[i] = new ArrayList(); 153 } 154 data.add(current_row); 155 } 156 156 157 158 159 157 public void put(int column, Object value) { 158 current_row[column].add(value); 159 } 160 160 161 162 163 164 161 public int size() { 162 return data.size(); 163 } 164 } 165 165 166 167 168 169 170 171 172 173 174 166 private class RowComparator 167 implements Comparator { 168 public int compare(Object o1, Object o2) { 169 return o1.toString().compareTo(o2.toString()); 170 } 171 public boolean equals(Object obj) { 172 return toString().equals(obj.toString()); 173 } 174 } 175 175 } 176 176 -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/MetaAuditRenderer.java
r4293 r4366 52 52 53 53 public class MetaAuditRenderer 54 54 extends DefaultTableCellRenderer { 55 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 56 public Component getTableCellRendererComponent(JTable table, Object value, boolean is_selected, boolean has_focus, int row, int column) { 57 ArrayList array = (ArrayList) value; 58 JLabel label = (JLabel) super.getTableCellRendererComponent(table, "", is_selected, has_focus, row, column); 59 JPanel panel = new JPanel(); 60 panel.setBackground(label.getBackground()); 61 panel.setBorder(label.getBorder()); 62 if(array != null) { 63 panel.setLayout(new GridLayout(array.size(), 1)); 64 for(int i = 0; i < array.size(); i++) { 65 JLabel entry = new JLabel(array.get(i).toString()); 66 entry.setFont(label.getFont()); 67 panel.add(entry); 68 } 69 } 70 return panel; 71 } 72 72 } -
trunk/gli/src/org/greenstone/gatherer/gui/metaaudit/MetaAuditTable.java
r4293 r4366 55 55 */ 56 56 public class MetaAuditTable 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 57 extends JTable 58 implements TableModelSorterListener { 59 60 private boolean initial_state; 61 62 private boolean ready; 63 64 private Filter filter; 65 66 private HeaderRenderer header_renderer; 67 68 private MetaAuditFrame parent_frame; 69 70 private MetaAuditModel model; 71 72 private SortedTableHelper helper; 73 74 static final private int MARGIN = 10; 75 76 public MetaAuditTable(MetaAuditFrame parent_frame) { 77 super(); 78 this.header_renderer = new HeaderRenderer(); 79 this.parent_frame = parent_frame; 80 getTableHeader().addMouseListener(new HeaderListener(parent_frame, this)); 81 setAutoResizeMode(AUTO_RESIZE_OFF); 82 setDefaultRenderer(ArrayList.class, new MetaAuditRenderer()); 83 ready = true; 84 } 85 86 public Filter getFilter() { 87 return filter; 88 } 89 90 public MetaAuditModel getOriginalModel() { 91 return model; 92 } 93 94 public TableModelSorter getSorter() { 95 return helper.getTableModelSorter(); 96 } 97 98 public boolean isFiltered(int column) { 99 if(filter == null) { 100 return false; 101 } 102 return filter.isFiltered(column); 103 } 104 105 public void newModel(FileNode records[]) { 106 if(records == null || records.length == 0) { 107 return; 108 } 109 wait(true); 110 initial_state = true; 111 ///ystem.err.println("Build new Model - should only be called once per click!"); 112 model = new MetaAuditModel(records); 113 setModel(model); 114 filter = new Filter(model.getColumnCount()); 115 helper = new SortedTableHelper (this, filter, new DefaultTableModelSorter(), header_renderer, null); 116 helper.prepareTable(); 117 // Add renderer to columns. 118 TableColumnModel column_model = getColumnModel(); 119 for(int i = 0; i < column_model.getColumnCount(); i++) { 120 column_model.getColumn(i).setHeaderRenderer(header_renderer); 121 } 122 wait(false); 123 initial_state = false; 124 } 125 126 public void tableChanged(TableModelEvent e) { 127 wait(true); 128 super.tableChanged(e); 129 if(ready) { 130 130 // Create storage area 131 132 133 131 ArrayList heights[] = new ArrayList[dataModel.getRowCount()]; 132 int widths_value[] = new int[dataModel.getColumnCount()]; 133 ArrayList widths[] = new ArrayList[dataModel.getColumnCount()]; 134 134 // Iterate through cell values finding tallest and widest for each row and column respectively. 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 135 for(int j = 0; j < dataModel.getRowCount(); j++) { 136 for(int k = 0; k < dataModel.getColumnCount(); k++) { 137 Object object = dataModel.getValueAt(j, k); 138 if(object instanceof ArrayList) { 139 ArrayList data = (ArrayList) object; 140 if(heights[j] == null || data.size() > heights[j].size()) { 141 heights[j] = data; 142 } 143 String longest = ""; 144 for(int i = 0; i < data.size(); i++) { 145 String temp = data.get(i).toString(); 146 if(temp.length() > longest.length()) { 147 longest = temp; 148 } 149 temp = null; 150 } 151 if(widths[k] == null || longest.length() > widths_value[k]) { 152 widths_value[k] = longest.length(); 153 widths[k] = data; 154 } 155 longest = null; 156 data = null; 157 } 158 } 159 } 160 160 // Now assign the dimensions we have determined to be the largest. 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 161 TableCellRenderer renderer = getDefaultRenderer(ArrayList.class); 162 for(int l = 0; l < heights.length; l++) { 163 if(heights[l] != null) { 164 Component component = renderer.getTableCellRendererComponent(this, heights[l], false, false, 0, 0); 165 Dimension size = component.getPreferredSize(); 166 setRowHeight(l, size.height); 167 size = null; 168 component = null; 169 } 170 } 171 heights = null; 172 for(int m = 0; m < widths.length; m++) { 173 if(widths[m] != null) { 174 Component component = renderer.getTableCellRendererComponent(this, widths[m], false, false, 0, 0); 175 Dimension size = component.getPreferredSize(); 176 int width = size.width + MARGIN; 177 if(width > (2 * parent_frame.getSize().width) / 3) { 178 width = (2 * parent_frame.getSize().width) / 3; 179 } 180 Component header = header_renderer.getTableCellRendererComponent(this, dataModel.getColumnName(m), false, false, 0, 0); 181 if(header.getPreferredSize().width + MARGIN > width) { 182 width = header.getPreferredSize().width + MARGIN; 183 } 184 columnModel.getColumn(m).setPreferredWidth(width); 185 size = null; 186 component = null; 187 } 188 } 189 widths = null; 190 renderer = null; 191 } 192 wait(initial_state); 193 } 194 195 public void sortOrderChanged(TableModelSorterEvent event) { 196 } 197 198 private void wait(boolean waiting) { 199 if(parent_frame != null) { 200 parent_frame.wait(waiting); 201 } 202 } 203 203 } 204 204 -
trunk/gli/src/org/greenstone/gatherer/gui/table/GTable.java
r4293 r4366 61 61 62 62 public class GTable 63 63 extends JTable { 64 64 65 66 67 68 69 70 71 72 73 65 private boolean building = false; 66 private JLabel label = null; 67 public GTable() { 68 // First draw the background image - tiled 69 label = new JLabel("<HTML><FONT SIZE=5>Building Collection</FONT></HTML>"); 70 //label.setHorizontalAlignment(JLabel.CENTER); 71 //label.setVerticalAlignment(JLabel.CENTER); 72 //label.setSize(new Dimension(505, 200)); 73 } 74 74 75 76 77 78 75 public synchronized void setBuilding(boolean building) { 76 this.building = building; 77 repaint(); 78 } 79 79 80 81 82 83 84 80 public void paint(Graphics g) { 81 // Now let the regular paint code do it's work 82 super.paint(g); 83 //if(building) { 84 label.paint(g); 85 85 //} 86 86 } 87 87 } -
trunk/gli/src/org/greenstone/gatherer/gui/table/GTableCellEditor.java
r4293 r4366 58 58 */ 59 59 public class GTableCellEditor 60 60 extends DefaultCellEditor { 61 61 62 62 private Gatherer gatherer = null; 63 63 64 64 private GComboBox editor = null; 65 65 66 66 private GComboBoxModel editor_model = null; 67 67 68 69 70 71 72 73 74 75 76 68 /** The default constructor simply add the standard new argument, a 69 * reference to the Gatherer class. 70 * @param gatherer A reference to Gatherer for communication and dictionary 71 * purposes. 72 */ 73 public GTableCellEditor(Gatherer gatherer) { 74 super(new JTextField()); 75 this.gatherer = gatherer; 76 } 77 77 78 79 80 81 82 83 84 78 /** This method returns the currently selected value of our editor 79 * GCombobox. 80 * @return An Object which is the selected items value. 81 */ 82 public Object getCellEditorValue() { 83 return editor.getSelectedItem(); 84 } 85 85 86 87 88 89 90 91 86 /** Returns a reference to the editor GComboBox. 87 * @return A Component reference to our editor. 88 */ 89 public Component getComponent() { 90 return editor; 91 } 92 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 93 /** This method is called to retrieve the correct editor component for 94 * a certain row and column within the target JTable. It is here that 95 * we create the custom GComboBox for each row of the 'values' column 96 * using a different model for each. If the call comes from any other 97 * column (which it shouldn't but what the hell) we let the super 98 * handle it which returns a plain old JTextField as the editing component. 99 * @param table The JTable which called this method. 100 * @param value The initial value of the cell this editor will be used 101 * for, as an Object. Format this to suit the editor you wish to return. 102 * @param isSelected A Boolean specifying whether the selected cell 103 * is currently highlighted in such a way so as to allow the user to 104 * know it is being edited. 105 * @param row An Int specifying the cells row. 106 * @param column An Int specifying the cells column. 107 * @return A Component which will be placed in the table as the method 108 * of editing the specified cell. 109 */ 110 public Component getTableCellEditorComponent(JTable table, Object value, 111 boolean isSelected, int row, 112 int column) { 113 GTableModel model = (GTableModel)table.getModel(); 114 // Create a new GComboBox with a model based on the selected 115 // records metadata. 116 Metadata selected_metadata = model.getMetadataAtRow(row); 117 //editor_model = gatherer.c_man.getCollection().getValueModel 118 // (selected_metadata.element); 119 editor = new GComboBox(editor_model); 120 editor.setEditable(true); 121 // Make sure that value is the currently selected option in the 122 // editor. 123 editor.setSelectedItem(value); 124 // Done. 125 return editor; 126 } 127 127 } 128 128 -
trunk/gli/src/org/greenstone/gatherer/gui/table/GTableModel.java
r4350 r4366 46 46 */ 47 47 public class GTableModel 48 49 50 51 52 53 54 55 56 57 58 59 60 61 48 extends AbstractTableModel 49 implements MSMListener { 50 /** The source for the data currently shown in the table. May be any of the other three metadata lists, or the union of all of them. */ 51 private ArrayList current_metadata = new ArrayList(); 52 /** The metadata currently assigned at the file level. */ 53 private ArrayList file_metadata = null; // new ArrayList(); 54 /** The metadata currently assigned at the folder level. */ 55 private ArrayList inherited_metadata = null; // new ArrayList(); 56 /** The metadata currently unassigned. */ 57 private ArrayList unassigned_metadata = null; // new ArrayList(); 58 /** The file nodes this model is built upon. */ 59 private FileNode[] file_nodes; 60 /** The current view. */ 61 //private int current_view; 62 62 63 63 //private JToggleButton assigned_metadata_view; … … 67 67 //private JProgressBar activity_bar; 68 68 /** The table this model is being displayed in. */ 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 69 private JTable table; 70 71 private ModelBuilder builder; 72 73 static final public int SHOW_ALL = 0; 74 static final public int SHOW_ASSIGNED = 1; 75 static final public int SHOW_FILE = 2; 76 static final public int SHOW_INHERITED = 3; 77 static final public int SHOW_UNASSIGNED = 4; 78 static final private String[] COLUMN_NAMES = {"", Gatherer.dictionary.get("Metadata.Element"), Gatherer.dictionary.get("Metadata.Value")}; 79 80 public GTableModel(JTable table) { 81 this.builder = null; 82 this.file_metadata = current_metadata; 83 this.inherited_metadata = current_metadata; 84 this.table = table; 85 this.unassigned_metadata = current_metadata; 86 //changeView(); 87 // Register this model with the msm manager. 88 if(Gatherer.c_man.ready()) { 89 Gatherer.c_man.getCollection().msm.addMSMListener(this); 90 } 91 } 92 93 public GTableModel(JTable table, FileNode[] file_nodes) { 94 this(table); 95 this.file_nodes = file_nodes; 96 if(file_nodes != null && file_nodes.length > 0) { 97 97 // Create model builder 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 98 builder = new ModelBuilder(); 99 builder.start(); 100 } 101 } 102 103 /** The default constructor creates a new empty model with the current view set to an emtpy 'all metadata'. */ 104 /* 105 public GTableModel(JTable table, JToggleButton assigned_metadata_view, JToggleButton unassigned_metadata_view, JProgressBar activity_bar) { 106 this.activity_bar = activity_bar; 107 this.assigned_metadata_view = assigned_metadata_view; 108 this.builder = null; 109 this.table = table; 110 this.unassigned_metadata_view = unassigned_metadata_view; 111 changeView(); 112 // Register this model with the msm manager. 113 if(Gatherer.c_man.ready()) { 114 Gatherer.c_man.getCollection().msm.addMSMListener(this); 115 } 116 } 117 */ 118 118 119 119 /** This constuctor starts by creating an empty model then, using a separate model builder thread, builds a model from the given files. */ … … 130 130 */ 131 131 132 133 134 135 136 132 public void changeView() { 133 changeView(SHOW_ALL); 134 } 135 136 /* 137 137 public void changeView() { 138 138 boolean show_assigned = assigned_metadata_view.isSelected(); … … 151 151 152 152 /** Change the current view by changing the model that the table is currently based on. */ 153 154 155 156 157 153 public void changeView(int view) { 154 //current_view = view; 155 /* 156 switch(view) { 157 case SHOW_ASSIGNED: 158 158 ///ystem.err.println("Show assigned"); 159 159 current_metadata = new ArrayList(); … … 162 162 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR); 163 163 break; 164 164 case SHOW_FILE: 165 165 ///ystem.err.println("Show file"); 166 166 current_metadata = file_metadata; 167 167 break; 168 168 case SHOW_INHERITED: 169 169 ///ystem.err.println("Show inherited"); 170 170 current_metadata = inherited_metadata; 171 171 break; 172 172 case SHOW_UNASSIGNED: 173 173 ///ystem.err.println("Show unassigned"); 174 174 current_metadata = unassigned_metadata; 175 175 break; 176 176 default: 177 177 // SHOW_ALL 178 178 System.err.println("Show all"); … … 183 183 current_metadata.addAll(unassigned_metadata); 184 184 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR); 185 186 187 188 189 190 191 192 193 194 185 } 186 // Inform everyone that the model has changed. 187 fireTableDataChanged(); 188 */ 189 } 190 191 /** Called whenever an element in a set is added, edited or removed from the metadata set manager. We listen for this just incase the name of one of the elements currently shown changes. After that the table will be up to date, as the element references are otherwise live. */ 192 public void elementChanged(MSMEvent event) { 193 MSMAction profile = event.getProfile(); 194 if(profile != null) { 195 195 // Retrieve the elements new name (thats hopefully what will be returned by getValue()). 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 196 String name = profile.getTarget(); 197 int row = 0; 198 for( ; row < current_metadata.size(); row++) { 199 if(getValueAt(row, 0).equals(name)) { 200 fireTableCellUpdated(row, 0); 201 } 202 } 203 name = null; 204 } 205 profile = null; 206 event = null; 207 } 208 209 /** Returns the number of columns in this table. */ 210 public int getColumnCount() { 211 return COLUMN_NAMES.length; 212 } 213 214 /** Returns the data class of the specified column. */ 215 public Class getColumnClass(int col) { 216 if(col == 2) { 217 return JButton.class; 218 } 219 return String.class; 220 } 221 222 /** Retrieves the name of the specified column. */ 223 public String getColumnName(int col) { 224 return COLUMN_NAMES[col]; 225 } 226 227 /* Called to retrieve the Metadata record that matches a certain row. Usually caused by the user selectiong a row in the table. It synchronized so that the model doesn't up and change while we're trying to retrieve the indicated element. */ 228 public synchronized Metadata getMetadataAtRow(int row) { 229 if(0 <= row && row < current_metadata.size()) { 230 return (Metadata) current_metadata.get(row); 231 } 232 return null; 233 } 234 235 /** Returns the number of rows in this table. */ 236 public int getRowCount() { 237 return current_metadata.size(); 238 } 239 240 /** Returns the cell value at a given row and column as an Object. */ 241 public Object getValueAt(int row, int col) { 242 Object result = ""; 243 if(0 <= row && row < current_metadata.size()) { 244 Metadata m = (Metadata) current_metadata.get(row); 245 if(0 <= col && col < COLUMN_NAMES.length) { 246 switch(col) { 247 case 0: 248 if(!m.isFileLevel()) { 249 result = m.getFile(); 250 } 251 break; 252 case 1: 253 ElementWrapper element = m.getElement(); 254 if(element != null) { 255 result = element.toString(); 256 } 257 break; 258 case 2: 259 result = m.getValue(); 260 break; 261 case 3: 262 result = (m.isFileLevel() ? "true" : "false"); 263 break; 264 } 265 } 266 } 267 return result; 268 } 269 269 270 271 272 273 274 275 276 277 278 270 /** Allows access to this models current view. */ 271 public int getView() { 272 return SHOW_ALL; //current_view; 273 } 274 275 /** Determine if the given metadata is common to all selected file nodes given the context of the current view. */ 276 public boolean isCommon(int row) { 277 if(0 <= row && row < current_metadata.size()) { 278 Metadata entry = (Metadata) current_metadata.get(row); 279 279 ///ystem.err.println("There are " + file_nodes.length + " selected files."); 280 280 ///ystem.err.println("This metadata is attached to " + entry.getCount() + " of them."); 281 282 283 284 285 286 287 288 289 290 291 292 293 281 if(entry.getCount() == file_nodes.length) { 282 return true; 283 } 284 } 285 return false; 286 } 287 288 /** Whenever there is a metadata change we must determine if there are any changes to our various lists of metadata, and also if there is any need to refresh the table currently being viewed. */ 289 public void metadataChanged(MSMEvent event) { 290 FileNode file_node = event.getRecord(); 291 ///ystem.err.println("File node effected"); 292 // First check whether this record is one of those that we have showing. 293 if(file_nodes != null && file_node != null && ArrayTools.contains(file_nodes, file_node)) { 294 294 // Brute force solution... rebuild the table. 295 296 297 298 299 300 295 if(file_nodes != null && file_nodes.length > 0) { 296 current_metadata.clear(); 297 // Create model builder 298 builder = new ModelBuilder(); 299 builder.start(); 300 } 301 301 302 302 /* 303 303 // If old metadata has been removed then remove it. Update the unassigned list if necessary (first checking if there are other metadata entries with the same element, and furthermore if the were inherited metadata being supressed). 304 304 if(old_data != null) { 305 306 307 308 309 310 311 312 313 305 System.err.println("Remove: " + old_data); 306 // Determine the level of this metadata 307 ArrayList modified_metadata = null; 308 if(old_data.isFileLevel()) { 309 modified_metadata = file_metadata; 310 } 311 else { 312 modified_metadata = inherited_metadata; 313 } 314 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 315 int index = -1; 316 if((index = modified_metadata.indexOf(old_data)) != -1) { 317 Metadata data = (Metadata) modified_metadata.get(index); 318 data.dec(); 319 // If we just removed the last occurance of this metadata, delete it 320 if(data.getCount() == 0) { 321 remove(modified_metadata, data); 322 // If there are no further occurances of the element of this entry, add entry to unassigned_metadata. 323 boolean found = false; 324 ElementWrapper element = data.getElement(); 325 // Search the current metadata 326 for(int i = 0; i < modified_metadata.size(); i++) { 327 Metadata sibling = (Metadata) modified_metadata.get(i); 328 if(sibling.getElement().equals(element)) { 329 found = true; 330 } 331 } 332 // And the inherited metadata if we haven't already 333 if(modified_metadata != inherited_metadata) { 334 for(int i = 0; !found && i < inherited_metadata.size(); i++) { 335 Metadata sibling = (Metadata) inherited_metadata.get(i); 336 if(sibling.getElement().equals(element)) { 337 found = true; 338 } 339 } 340 } 341 // If we get this far and still haven't found a match then we do something very tricky. We retrieve all of the metadata for the parent file and notice any inherited nodes that were previously overwritten by the metadata entry we removed. The reason we use the parent is that we can't just assume that the GDMDocument changes occured before this method was called. Thus our about-to-be-removed overwritting metadata may still be in effect if we just called getMetadata for this file. 342 if(!found) { 343 ArrayList parent_metadatum = Gatherer.c_man.getCollection().gdm.getMetadata((file_node.getFile()).getParentFile()); 344 // For each piece of parent metadata 345 for(int i = parent_metadatum.size(); i > 0; i--) { 346 Metadata parent_metadata = (Metadata) parent_metadatum.get(i - 1); 347 // If we found some previously suppressed metadata then add it and say we found something. 348 if(!inherited_metadata.contains(parent_metadata)) { 349 ///ystem.err.println("Restoring overwritten inherited metadata: " + parent_metadata); 350 found = true; 351 parent_metadata.setCount(1); 352 parent_metadata.setFileLevel(false); // Has to be folder level doesn't it. 353 add(inherited_metadata, parent_metadata); 354 } 355 } 356 // Run this getMetadata again to restore all of the file level and source file entries (they would have changed to reflect the parents call. 357 Gatherer.c_man.getCollection().gdm.getMetadata(file_node.getFile()); 358 } 359 // And only then, if we haven't found a substitute, may we add a null metadata. 360 if(!found) { 361 add(unassigned_metadata, new Metadata(data.getElement())); 362 } 363 } 364 } 365 365 } 366 366 // If new metadata has been assigned, remove its entry from the unassigned metadata list. Furthermore add the new entry to either the file or folder metadata list depending on its level. Remember to rebuild the common metadata lists. 367 367 if(new_data != null) { 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 368 ///ystem.err.println("Add: " + new_data); 369 // Create dummy 'unassigned' metadata from the new metadata and remove it from the unassigned list (if its even there) 370 remove(unassigned_metadata, new Metadata(new_data.getElement())); 371 // Determine if we are adding the new node at the file or folder level. 372 ArrayList modified_metadata = null; 373 if(new_data.isFileLevel()) { 374 modified_metadata = file_metadata; 375 // If its file metadata, and its set to overwrite, then we need to remove any row it would overwrite. 376 if(new_data.accumulates()) { 377 // All good. 378 } 379 else { 380 // Remove any entries in the inherited_metadata that would be overwritten 381 ElementWrapper element = new_data.getElement(); 382 for(int i = 0; i < inherited_metadata.size(); i++) { 383 Metadata sibling = (Metadata) inherited_metadata.get(i); 384 if(sibling.getElement().equals(element)) { 385 inherited_metadata.remove(sibling); 386 } 387 } 388 } 389 } 390 else { 391 modified_metadata = inherited_metadata; 392 } 393 // And add it. 394 int index = -1; 395 if((index = modified_metadata.indexOf(new_data)) == -1) { 396 add(modified_metadata, new_data); 397 new_data.setCount(1); 398 } 399 else { 400 Metadata data = (Metadata) modified_metadata.get(index); 401 data.inc(); 402 data = null; 403 } 404 modified_metadata = null; 405 405 } 406 406 // As a current visible table is most likely affected, update the table component. We do this by calling changeView so as to also update any compound list. 407 407 //changeView(current_view); 408 408 */ 409 410 409 } 410 else { 411 411 ///ystem.err.println("FileNodes = " + file_nodes); 412 412 ///ystem.err.println("FileNode = " + file_node); 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 413 } 414 } 415 416 /** Called whenever a set is added or removed from the metadata set manager. */ 417 public void setChanged(MSMEvent event) { 418 // No, don't care. Still don't care. Couldn't care less. 419 } 420 421 public Rectangle setSelectedMetadata(Metadata data) { 422 Rectangle bounds = null; 423 if (builder != null) { 424 if(builder.complete) { 425 for(int i = 0; i < getRowCount(); i++) { 426 if(getMetadataAtRow(i).equals(data)) { 427 ///ystem.err.println("Found matching metadata at row " + i + " (of " + getRowCount() + " rows)"); 428 ///ystem.err.println("Table shows " + table.getRowCount() + " rows!"); 429 bounds = table.getCellRect(i, 0, true); 430 table.scrollRectToVisible(bounds); 431 table.setRowSelectionInterval(i, i); 432 } 433 } 434 } 435 else { 436 builder.selected_metadata = data; 437 } 438 } 439 return bounds; 440 } 441 442 public int size() { 443 return file_nodes.length; 444 } 445 446 /** Called when the value tree of a certain element has changed significantly but, although we display metadata values, we only care about those coming through metadataChanged() events. */ 447 public void valueChanged(MSMEvent event) { 448 // Don't care. Tra-la-la-la-la. 449 } 450 451 /** Alphabetically inserts the new_metadata in the target vector. */ 452 private int add(ArrayList target, Metadata new_metadata) { 453 int i = 0; 454 while(i < target.size()) { 455 Metadata current = (Metadata)target.get(i); 456 if(current.compareTo(new_metadata) > 0) { 457 target.add(i, new_metadata); 458 return i; 459 } 460 i++; 461 } 462 target.add(new_metadata); 463 return i; 464 } 465 466 /** Remove a certain piece of data from a certain vector. */ 467 private void remove(ArrayList target, Metadata old_metadata) { 468 if(target != null) { 469 for(int i = 0; i < target.size(); i++) { 470 Metadata current_metadata = (Metadata) target.get(i); 471 if(current_metadata.equals(old_metadata)) { 472 target.remove(current_metadata); 473 return; 474 } 475 } 476 } 477 } 478 479 private class ModelBuilder 480 extends Thread { 481 482 public boolean had_warning = false; 483 484 public boolean complete = false; 485 486 public Metadata selected_metadata = null;; 487 487 488 489 488 public void run() { 489 Vector elements = Gatherer.c_man.getCollection().msm.getElements(); 490 490 // Show some visual indication that building is occuring. 491 491 //assigned_metadata_view.setEnabled(false); … … 496 496 //long start = System.currentTimeMillis(); 497 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 498 ArrayList known_elements = new ArrayList(); // Elements that have metadata assigned. 499 for(int i = 0; i < file_nodes.length; i++) { 500 File current_file = file_nodes[i].getFile(); 501 // The current assigned metadata for this file. 502 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getAllMetadata(current_file); 503 for(int j = 0; j < metadatum.size(); j++) { 504 Metadata data = (Metadata) metadatum.get(j); 505 // Determine the target list by the metadata level 506 ArrayList modified_metadata; 507 if(data.isFileLevel()) { 508 modified_metadata = file_metadata; 509 } 510 else { 511 showInheritedMetadataWarning(); 512 modified_metadata = inherited_metadata; 513 } 514 int index = -1; 515 // Don't show hidden metadata 516 // If the given metadata already exists in our list of modified metadata then increment its commonality count. 517 if((index = modified_metadata.indexOf(data)) != -1) { 518 data = (Metadata) modified_metadata.get(index); 519 ///ystem.err.println("Increasing count:" + element); 520 data.inc(); 521 // If the table shown is stale, refresh it. 522 //if((modified_metadata == file_metadata && current_view == SHOW_FILE) || (modified_metadata == inherited_metadata && current_view == SHOW_INHERITED)) { 523 // fireTableRowsUpdated(index, index); 524 //} 525 // We may have to update a compound list 526 //else if(current_view == SHOW_ALL || current_view == SHOW_ASSIGNED) { 527 if((index = current_metadata.indexOf(data)) == -1) { 528 fireTableRowsUpdated(index, index); 529 } 530 //} 531 } 532 // Ensure the metadata's element is in our list of showable elements. 533 else if(contains(elements, data.getElement())) { 534 ///ystem.err.println("Setting count to one: " + element); 535 data.setCount(1); 536 index = add(modified_metadata, data); // Add to assigned metadata. 537 known_elements.add(data.getElement()); 538 // If the table shown is stale, refresh it. 539 //if((modified_metadata == file_metadata && current_view == SHOW_FILE) || (modified_metadata == inherited_metadata && current_view == SHOW_INHERITED)) { 540 // fireTableRowsInserted(index, index); 541 //} 542 // We may have to update a compound list 543 //else if(current_view == SHOW_ALL || current_view == SHOW_ASSIGNED) { 544 if((index = current_metadata.indexOf(data)) == -1) { 545 index = add(current_metadata, data); 546 fireTableRowsInserted(index, index); 547 } 548 else { 549 fireTableRowsUpdated(index, index); 550 } 551 //} 552 } 553 else { 554 ///ystem.err.println("No.\n***** Cannot match element so it must be hidden, right? *****"); 555 } 556 } 557 //activity_bar.setValue(activity_bar.getValue() + 1); 558 Gatherer.g_man.metaedit_pane.validateMetadataTable(); 559 } 560 560 // Add entries for the currently unassigned metadata. You can determine these as the difference between elements and known_elements. 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 561 for(int k = 0; k < elements.size(); k++) { 562 // For each key. 563 ElementWrapper element = (ElementWrapper) elements.get(k); 564 if(!known_elements.contains(element)) { 565 Metadata data = new Metadata(element); 566 int index = add(unassigned_metadata, data); 567 // Inform everyone that the model has changed, but only if it affects the current view. 568 //if(current_view == SHOW_UNASSIGNED) { 569 // fireTableRowsInserted(index, index); 570 //} 571 //else if(current_view == SHOW_ALL) { 572 if((index = current_metadata.indexOf(data)) == -1) { 573 index = add(current_metadata, data); 574 fireTableRowsInserted(index, index); 575 } 576 else { 577 fireTableRowsUpdated(index, index); 578 } 579 //} 580 } 581 //activity_bar.setValue(activity_bar.getValue() + 1); 582 Gatherer.g_man.metaedit_pane.validateMetadataTable(); 583 } 584 584 //long end = System.currentTimeMillis(); 585 585 ///ystem.err.println("Took " + (end - start) + "ms to build table."); … … 588 588 //activity_bar.setValue(activity_bar.getMaximum()); 589 589 //activity_bar.setString(Gatherer.dictionary.get("MetaEdit.Ready")); 590 591 590 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR); 591 fireTableDataChanged(); 592 592 // Finally complete 593 593 complete = true; 594 594 // If in the in between we've been asked to select a certain metadata, we action that now. 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 595 if(selected_metadata != null) { 596 setSelectedMetadata(selected_metadata); 597 } 598 } 599 600 /** For some reason contains doesn't always work. It appear not to use equals() properly, as ElementWrappers need to compare themselves by their string content as other data members are nearly always different even between to ElementWrappers generated from the same DOM Element. */ 601 private boolean contains(Vector elements, ElementWrapper element) { 602 if(element != null) { 603 for(int i = 0; i < elements.size(); i++) { 604 if(element.equals(elements.get(i))) { 605 return true; 606 } 607 } 608 } 609 return false; 610 } 611 612 private void showInheritedMetadataWarning() { 613 if(!had_warning) { 614 had_warning = true; 615 Runnable task = new Runnable() { 616 public void run() { 617 WarningDialog dialog = new WarningDialog("warning.InheritedMetadata", false); 618 dialog.display(); 619 dialog.dispose(); 620 dialog = null; 621 } 622 }; 623 SwingUtilities.invokeLater(task); 624 } 625 } 626 } 627 627 } -
trunk/gli/src/org/greenstone/gatherer/gui/table/TableCellRenderer.java
r4293 r4366 53 53 54 54 public class TableCellRenderer 55 55 extends DefaultTableCellRenderer { 56 56 57 57 private GTableModel model = null; 58 58 59 60 61 62 59 public TableCellRenderer(GTableModel model) { 60 super(); 61 this.model = model; 62 } 63 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 64 /** Returns the default table cell renderer. 65 * @param table The <strong>JTable</strong>. 66 * @param value The value to assign to the cell at [row, column] as an <strong>Object</strong>. 67 * @param isSelected <i>true</i> if cell is selected. 68 * @param row The row of the cell to render as an <i>int</i>. 69 * @param column The column of the cell to render as an <i>int</i>. 70 * @param hasFocus <i>true</i> if cell has focus, render cell appropriately. For example, put a special border on the cell, if the cell can be edited, render in the color used to indicate editing. 71 * @return The default table cell renderer <strong>Component</strong>. 72 */ 73 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 74 JLabel renderer = null; 75 if(value instanceof File) { 76 if(value != null) { 77 if(isSelected && hasFocus) { 78 Metadata data = model.getMetadataAtRow(row); 79 Gatherer.g_man.metaedit_pane.setSelection((File)value); 80 Gatherer.g_man.metaedit_pane.setSelectedMetadata(data); 81 } 82 renderer = new JLabel(Utility.getImage("upfolder.gif")); 83 } 84 } 85 if(renderer == null) { 86 renderer = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 87 } 88 // Set up the component 89 renderer.setOpaque(true); 90 // Foreground 91 if(model.isCommon(row)) { 92 renderer.setForeground(Color.black); 93 } 94 else { 95 renderer.setForeground(Color.gray); 96 } 97 // Background 98 if(column <= 1) { 99 if(isSelected) { 100 renderer.setBackground(Gatherer.config.getColor("coloring.workspace_heading_background", true)); 101 } 102 else { 103 renderer.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", true)); 104 } 105 } 106 else { 107 if(isSelected) { 108 renderer.setBackground(Gatherer.config.getColor("coloring.workspace_heading_background", true)); 109 } 110 else { 111 renderer.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", true)); 112 } 113 } 114 // Finally the 3rd column of cells never paint focus. 115 if(column == 2) { 116 renderer.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); 117 } 118 return renderer; 119 } 120 120 } -
trunk/gli/src/org/greenstone/gatherer/gui/tree/DragTree.java
r4313 r4366 21 21 22 22 public class DragTree 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 23 extends JTree 24 implements Autoscroll, DragGestureListener, DragSourceListener, DropTargetListener, DragComponent, TreeSelectionListener { 25 /** The normal background color. */ 26 private Color background_color; 27 /** The normal foreground color. */ 28 private Color foreground_color; 29 /** The Group this component belongs to. */ 30 private DragGroup group; 31 /** The image to use for the disabled background. */ 32 private ImageIcon disabled_background; 33 /** The image to use for a normal background. */ 34 private ImageIcon normal_background; 35 /** The icon to use for multiple node drag'n'drops. We decided against using the windows paradigm or a block of x horizontal lines for x files. */ 36 private ImageIcon multiple_icon = new ImageIcon("resource"+File.separator+"multiple.gif"); 37 /** The default drag action, although its not that important as we provide custom icons during drags. */ 38 private int drag_action = DnDConstants.ACTION_MOVE; 39 /** The location of the last ghost drawn, so that we can repair the 'spoilt' area. */ 40 private Point pt_last = null; 41 /** The region borderer by the lower cue line. */ 42 private Rectangle lower_cue_line; 43 /** The region covered by the drag ghost icon. */ 44 private Rectangle ra_ghost = new Rectangle(); 45 /** The region borderer by the upper cue line. */ 46 private Rectangle upper_cue_line; 47 /** The identifying name of this Tree. */ 48 private String name; 49 /** The last tree path the drag was hovered over. */ 50 private TreePath previous_path = null; 51 static private final Color TRANSPARENT_COLOR = new Color(0,0,0,0); 52 /** The distance from the edge of the current view within the scroll bar which if entered causes the view to scroll. */ 53 static private final int AUTOSCROLL_MARGIN = 12; 54 55 public DragTree(String name, String background_name) { 56 super(); 57 init(name, background_name); 58 } 59 60 public DragTree(String name, TreeModel model, String background_name) { 61 super(model); 62 init(name, background_name); 63 // Connection 64 setModel(model); 65 } 66 67 public void init(String name, String background_name) { 68 // Init 69 this.name = name; 70 if(background_name != null) { 71 this.disabled_background = new ImageIcon("background.gif"); 72 this.normal_background = new ImageIcon(background_name); 73 } 74 75 // Creation 76 this.putClientProperty("JTree.lineStyle", "Angled"); 77 this.setAutoscrolls(true); 78 this.setEditable(false); 79 this.setLargeModel(true); 80 this.setOpaque(true); 81 this.setSelectionModel(new DragTreeSelectionModel(this)); 82 // Connection 83 addTreeSelectionListener(this); 84 85 DragTreeCellRenderer renderer = new DragTreeCellRenderer(); 86 //make the renderer paint nodes as transparent when not selected 87 //renderer.setBackgroundNonSelectionColor(new Color(0,0,0,0)); 88 setCellRenderer(renderer); 89 89 90 90 // Drag'n'drop Setup 91 91 // Drag source setup. 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 92 DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, drag_action, this); 93 // Drop destination setup. 94 new DropTarget(this, drag_action, this, true); 95 } 96 97 // Autoscroll Interface - Scroll because the mouse cursor is in our scroll zone.<br> 98 // The following code was borrowed from the book:<br> 99 // Java Swing<br> 100 // By Robert Eckstein, Marc Loy & Dave Wood<br> 101 // Paperback - 1221 pages 1 Ed edition (September 1998)<br> 102 // O'Reilly & Associates; ISBN: 156592455X<br> 103 // The relevant chapter of which can be found at:<br> 104 // http://www.oreilly.com/catalog/jswing/chapter/dnd.beta.pdf<br> 105 // But I've probably tortured it beyond all recognition anyway. 106 public void autoscroll(Point pt) { 107 // Figure out which row we're on. 108 int row = getRowForLocation(pt.x, pt.y); 109 // If we are not on a row then ignore this autoscroll request 110 if (row < 0) return; 111 Rectangle bounds = getBounds();// Yes, scroll up one row 112 // Now decide if the row is at the top of the screen or at the bottom. We do this to make the previous row (or the next row) visible as appropriate. If we're at the absolute top or bottom, just return the first or last row respectively. 113 // Is row at top of screen? 114 if(pt.y + bounds.y <= AUTOSCROLL_MARGIN) { 115 115 // Yes, scroll up one row 116 117 118 119 120 121 122 123 116 if(row <= 0) { 117 row = 0; 118 } 119 else { 120 row = row - 1; 121 } 122 } 123 else { 124 124 // No, scroll down one row 125 126 127 128 129 130 131 132 133 */ 134 135 136 137 138 139 125 if(row < getRowCount() - 1) { 126 row = row + 1; 127 } 128 } 129 this.scrollRowToVisible(row); 130 } 131 132 /** In order for the appearance to be consistant, given we may be in the situation where the pointer has left our focus but the ghost remains, this method allows other members of the GGroup to tell this component to clear its ghost. 133 */ 134 public void clearGhost() { 135 // Erase the last ghost image and cue line 136 paintImmediately(ra_ghost.getBounds()); 137 } 138 139 /** Any implementation of DragSourceListener must include this method so we can be notified when the drag event ends (somewhere else), which will in turn remove actions. 140 140 * @param event A <strong>DragSourceDropEvent</strong> containing all the information about the end of the drag event. 141 141 */ 142 143 142 public void dragDropEnd(DragSourceDropEvent event) { 143 if(event.getDropSuccess()) { 144 144 // Do whatever I do when the drop is successful. 145 146 147 145 } 146 } 147 /** Any implementation of DragSourceListener must include this method so we can be notified when the drag focus enters this component. 148 148 * @param event A <strong>DragSourceDragEvent</strong> containing all the information 149 149 * about the drag event. 150 150 */ 151 152 153 154 151 public void dragEnter(DragSourceDragEvent event) { 152 // Handled elsewhere. 153 } 154 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus enters this component, which in this case is to grab focus from within our group. 155 155 * @param event A <strong>DropTargetDragEvent</strong> containing all the information about the drag event. 156 156 */ 157 158 159 160 157 public void dragEnter(DropTargetDragEvent event) { 158 group.grabFocus(this); 159 } 160 /** Any implementation of DragSourceListener must include this method so we can be notified when the drag focus leaves this component. 161 161 * @param event A <strong>DragSourceEvent</strong> containing all the information about the drag event. 162 162 */ 163 164 165 166 167 163 public void dragExit(DragSourceEvent event) { 164 clearGhost(); 165 } 166 167 /** Any implementation of DropTargetListener must include this method 168 168 * so we can be notified when the drag focus leaves this component. 169 169 * @param event A DropTargetEvent containing all the information 170 170 * about the drag event. 171 171 */ 172 173 174 175 176 172 public void dragExit(DropTargetEvent event) { 173 clearGhost(); 174 } 175 176 /** Any implementation of DragGestureListener must include this method 177 177 * so we can be notified when a drag action has been noticed, thus a 178 178 * drag action has begun. … … 180 180 * the drag event. 181 181 */ 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 182 public void dragGestureRecognized(DragGestureEvent event) { 183 // Disable editing, unless you want to have the edit box pop-up part way through dragging. 184 this.setEditable(false); 185 // We need this to find one of the selected nodes. 186 Point origin = event.getDragOrigin(); 187 TreePath path = this.getPathForLocation(origin.x, origin.y); 188 // Taking into account our delayed model of selection, it is possible the user has performed a select and drag in one click. Here we utilize the Windows paradigm like so: If the node at the origin of the drag and drop is already in our selection then we perform multiple drag and drop. Otherwise we recognise that this is a distinct drag-drop and move only the origin node. 189 if(!isPathSelected(path)) { 190 ((DragTreeSelectionModel)selectionModel).setImmediate(true); 191 setSelectionPath(path); 192 } 193 // Now update the selection stored as far as the group is concerned. 194 group.setSelection(getSelectionPaths()); 195 group.setSource(this); 196 // First grab ghost. 197 group.grabFocus(this); 198 // Ghost Image stuff. 199 if(path != null) { 200 Rectangle rect = this.getPathBounds(path); 201 group.mouse_offset = new Point(origin.x - rect.x, origin.y - rect.y); 202 202 // Create the ghost image. 203 203 // Retrieve the selected files. 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 204 int selection_count = getSelectionCount(); 205 if(selection_count > 0) { 206 JLabel label; 207 if(selection_count == 1) { 208 TreePath node_path = getSelectionPath(); 209 FileNode node = (FileNode) path.getLastPathComponent(); 210 label = new JLabel(node.toString(), ((DefaultTreeCellRenderer)getCellRenderer()).getLeafIcon(), JLabel.CENTER); 211 } 212 else { 213 String title = getSelectionCount() + " files"; 214 label = new JLabel(title, ((DefaultTreeCellRenderer)getCellRenderer()).getClosedIcon(), JLabel.CENTER); 215 title = null; 216 } 217 // The layout manager normally does this. 218 Dimension label_size = label.getPreferredSize(); 219 label.setSize(label_size); 220 label.setBackground(TRANSPARENT_COLOR); 221 label.setOpaque(true); 222 // Get a buffered image of the selection for dragging a ghost image. 223 group.image_ghost = new BufferedImage(label_size.width, label_size.height, BufferedImage.TYPE_INT_ARGB_PRE); 224 label_size = null; 225 // Get a graphics context for this image. 226 Graphics2D g2 = group.image_ghost.createGraphics(); 227 // Make the image ghostlike 228 g2.setComposite(AlphaComposite.getInstance (AlphaComposite.SRC, 0.5f)); 229 // Ask the cell renderer to paint itself into the BufferedImage 230 label.paint(g2); 231 g2 = null; 232 label = null; 233 try { 234 event.startDrag(new Cursor(Cursor.DEFAULT_CURSOR), group.image_ghost, new Point(5,5), new StringSelection("dummy"), this); 235 //dragging = true; 236 } 237 catch(Exception error) { 238 error.printStackTrace(); 239 } 240 } 241 } 242 } 243 244 /** Implementation side-effect. 245 245 * @param event A DragSourceDragEvent containing all the information about the drag event. 246 246 */ 247 248 249 250 247 public void dragOver(DragSourceDragEvent event) { 248 } 249 250 /** Any implementation of DropTargetListener must include this method 251 251 * so we can be notified when the drag moves in this component. 252 252 * @param event A DropTargetDragEvent containing all the information 253 253 * about the drag event. 254 254 */ 255 256 257 258 259 260 261 262 263 255 public void dragOver(DropTargetDragEvent event) { 256 // Draw the mouse ghost 257 Graphics2D g2 = (Graphics2D) getGraphics(); 258 Point pt = event.getLocation(); 259 if(pt_last != null && pt.equals(pt_last)) { 260 return; 261 } 262 pt_last = pt; 263 if(!DragSource.isDragImageSupported()) { 264 264 // Erase the last ghost image and cue line 265 265 paintImmediately(ra_ghost.getBounds()); 266 266 // Remember where you are about to draw the new ghost image 267 267 ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight()); 268 268 // Draw the ghost image 269 270 271 272 273 269 g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null); 270 } 271 // Now we highlight the target node if it is a valid drop target. Of course we don't bother if we are still over a node which has already been identified as to whether its a drop target. 272 TreePath target_path = this.getPathForLocation(pt.x, pt.y); 273 if(previous_path == null || target_path != null && !target_path.equals(previous_path)) { 274 274 // Immediately clear the old cue lines. 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 275 if(upper_cue_line != null && lower_cue_line != null) { 276 paintImmediately(upper_cue_line.getBounds()); 277 paintImmediately(lower_cue_line.getBounds()); 278 } 279 if(isValidDrop(target_path)) { 280 ///ystem.err.println("Valid. Painting cues."); 281 // Get the drop target's bounding rectangle 282 Rectangle raPath = getPathBounds(target_path); 283 // Cue line bounds (2 pixels beneath the drop target) 284 upper_cue_line = new Rectangle(0, raPath.y + (int)raPath.getHeight(), getWidth(), 2); 285 lower_cue_line = new Rectangle(0, raPath.y, getWidth(), 2); 286 g2.setColor(((DefaultTreeCellRenderer)cellRenderer).getBackgroundSelectionColor()); // The cue line color 287 g2.fill(upper_cue_line); // Draw the cue line 288 g2.fill(lower_cue_line); 289 } 290 else { 291 upper_cue_line = null; 292 lower_cue_line = null; 293 } 294 } 295 } 296 297 /** Any implementation of DropTargetListener must include this method 298 298 * so we can be notified when the drag ends, ie the transferable is 299 299 * dropped. … … 301 301 * about the end of the drag event. 302 302 */ 303 304 305 306 307 303 public void drop(DropTargetDropEvent event) { 304 ///start = System.currentTimeMillis(); 305 ///ystem.err.println("Drop target drop: " + this); 306 event.acceptDrop(drag_action); 307 if(!name.equals(Utility.WORKSPACE_TREE)) { 308 308 // Determine what node we dropped over. 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 309 Point pt = event.getLocation(); 310 TreePath target_path = this.getPathForLocation(pt.x, pt.y); 311 FileNode target = null; 312 if(target_path != null) { 313 if(isValidDrop(target_path)) { 314 target = (FileNode) target_path.getLastPathComponent(); 315 } 316 else { 317 // Warn that this is an invalid drop. 318 JOptionPane.showMessageDialog(Gatherer.g_man, Gatherer.dictionary.get("FileActions.InvalidTarget"), Gatherer.dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE); 319 } 320 } 321 else { 322 target = (FileNode) getModel().getRoot(); 323 } 324 target_path = null; 325 pt = null; 326 if(target != null) { 327 ///ystem.err.println("Valid drop."); 328 TreePath[] selection = group.getSelection(); 329 if(target != null) { 330 FileNode[] source_nodes = new FileNode[selection.length]; 331 for(int i = 0; i < source_nodes.length; i++) { 332 source_nodes[i] = (FileNode) selection[i].getLastPathComponent(); 333 } 334 Gatherer.f_man.action(group.getSource(), source_nodes, this, target); 335 source_nodes = null; 336 } 337 group.setSelection(null); 338 group.setSource(null); 339 selection = null; 340 target = null; 341 } 342 } 343 else { 344 344 // Warn that this is an invalid drop. 345 346 347 348 349 350 351 352 345 JOptionPane.showMessageDialog(Gatherer.g_man, Gatherer.dictionary.get("FileActions.ReadOnlyTarget"), Gatherer.dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE); 346 } 347 // Clear up the group.image_ghost 348 paintImmediately(ra_ghost.getBounds()); 349 event.getDropTargetContext().dropComplete(true); 350 } 351 352 /** Any implementation of DragSourceListener must include this method 353 353 * so we can be notified when the action to be taken upon drop changes. 354 354 * @param event A DragSourceDragEvent containing all the information 355 355 * about the drag event. 356 356 */ 357 358 359 360 357 public void dropActionChanged(DragSourceDragEvent event) { 358 } 359 360 /** Any implementation of DropTargetListener must include this method 361 361 * so we can be notified when the action to be taken upon drop changes. 362 362 * @param event A DropTargetDragEvent containing all the information 363 363 * about the drag event. 364 364 */ 365 366 367 368 365 public void dropActionChanged(DropTargetDragEvent event) { 366 } 367 368 /** Used to notify this component that it has gained focus. It should 369 369 * make some effort to inform the user of this. 370 370 */ 371 372 373 374 375 376 377 371 public void gainFocus() { 372 ///ystem.err.println("Gained focus: " + this); 373 ((DragTreeCellRenderer)cellRenderer).gainFocus(); 374 repaint(); 375 } 376 377 /** Autoscroll Interface... 378 378 * The following code was borrowed from the book: 379 379 * Java Swing … … 387 387 * the tree is in. This makes it a bit messy. 388 388 */ 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 389 public Insets getAutoscrollInsets() 390 { 391 Rectangle raOuter = this.getBounds(); 392 Rectangle raInner = this.getParent().getBounds(); 393 return new Insets(raInner.y - raOuter.y + AUTOSCROLL_MARGIN, 394 raInner.x - raOuter.x + AUTOSCROLL_MARGIN, 395 raOuter.height - raInner.height - raInner.y + raOuter.y + AUTOSCROLL_MARGIN, 396 raOuter.width - raInner.width - raInner.x + raOuter.x + AUTOSCROLL_MARGIN); 397 } 398 399 public String getSelectionDetails() { 400 return ((DragTreeSelectionModel)selectionModel).getDetails(); 401 } 402 403 public FileSystemModel getTreeModel() { 404 return (FileSystemModel) getModel(); 405 } 406 407 /** This method is used to inform this component when it loses focus, 408 408 * and should indicate this somehow. 409 409 */ 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 410 public void loseFocus() { 411 ///ystem.err.println("Lost focus: " + this); 412 ((DragTreeCellRenderer)cellRenderer).loseFocus(); 413 repaint(); 414 } 415 416 public void mapDirectory(File file, String title) { 417 try { 418 ((FileSystemModel)treeModel).mapDirectory(file, title); 419 } 420 catch (Exception error) { 421 error.printStackTrace(); 422 } 423 } 424 425 public void paint(Graphics g) { 426 if(disabled_background != null) { 427 int height = getSize().height; 428 int offset = 0; 429 ImageIcon background; 430 if(isEnabled()) { 431 background = normal_background; 432 } 433 else { 434 background = disabled_background; 435 } 436 while((height - offset) > 0) { 437 g.drawImage(background.getImage(), 0, offset, null); 438 offset = offset + background.getIconHeight(); 439 } 440 background = null; 441 } 442 super.paint(g); 443 } 444 445 public void refresh(TreePath path) { 446 ((FileSystemModel)treeModel).refresh(path); 447 } 448 449 public void setBackgroundNonSelectionColor(Color color) { 450 background_color = color; 451 if(isEnabled()) { 452 setBackground(color); 453 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(color); 454 } 455 else { 456 setBackground(Color.lightGray); 457 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(Color.lightGray); 458 } 459 repaint(); 460 } 461 462 public void setBackgroundSelectionColor(Color color) { 463 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundSelectionColor(color); 464 repaint(); 465 } 466 467 /** Override the normal setEnabled so the Tree exhibits a little more 468 468 * change, which in this instance is the background colour changing. 469 469 * @param state Whether this GTree should be in an enabled state. 470 470 */ 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 471 public void setEnabled(boolean state) { 472 super.setEnabled(state); 473 clearSelection(); 474 // Change some colors 475 if(state) { 476 setBackground(background_color); 477 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(background_color); 478 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(foreground_color); 479 } 480 else { 481 setBackground(Color.lightGray); 482 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(Color.lightGray); 483 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(Color.black); 484 } 485 repaint(); 486 } 487 488 public void setGroup(DragGroup group) { 489 this.group = group; 490 } 491 492 /** Determines whether the following selection attempts should go through the normal delayed selection model, or should happen immediately.*/ 493 public void setImmediate(boolean state) { 494 ((DragTreeSelectionModel)selectionModel).setImmediate(state); 495 } 496 497 public void setModel(TreeModel model) { 498 super.setModel(model); 499 if(model instanceof FileSystemModel) { 500 FileSystemModel file_system_model = (FileSystemModel) model; 501 file_system_model.setTree(this); 502 addTreeExpansionListener(file_system_model); 503 addTreeWillExpandListener(file_system_model); 504 file_system_model = null; 505 } 506 } 507 508 /** Ensure that that file node denoted by the given file is selected. */ 509 public void setSelection(File file) { 510 // We know the file exists, and thus that it must exists somewhere in our tree hierarchy. 511 // 1. Retrieve the root node of our tree. 512 FileNode current = (FileNode) getModel().getRoot(); 513 // 2. Find that node in the file parents, keeping track of each intermediate file. 514 ArrayList files = new ArrayList(); 515 while(file != null && !current.toString().equals(file.getName())) { 516 files.add(0, file); 517 file = file.getParentFile(); 518 } 519 if(file == null) { 520 return; 521 } 522 // 3. While there are still remaining intermediate files. 523 while(files.size() > 0) { 524 file = (File) files.remove(0); 525 525 // 3a. Find the next file in the current nodes children. 526 527 528 529 530 531 532 533 534 535 526 boolean found = false; 527 current.map(); 528 for(int i = 0; !found && i < current.getChildCount(); i++) { 529 FileNode child = (FileNode) current.getChildAt(i); 530 if(child.toString().equals(file.getName())) { 531 // 3b. Make the current node this node (if found) and continue. 532 found = true; 533 current = child; 534 } 535 } 536 536 // 3c. If not found then return as this node can't exists somehow. 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 537 if(!found) { 538 return; 539 } 540 } 541 // 4. We should now have the desired node. Remember to make the selection immediate. 542 TreePath path = new TreePath(current.getPath()); 543 setImmediate(true); 544 setSelectionPath(path); 545 setImmediate(false); 546 } 547 548 public void setTextNonSelectionColor(Color color) { 549 foreground_color = color; 550 if(isEnabled()) { 551 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(color); 552 } 553 else { 554 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(Color.black); 555 } 556 repaint(); 557 } 558 559 public void setTextSelectionColor(Color color) { 560 ((DefaultTreeCellRenderer)cellRenderer).setTextSelectionColor(color); 561 repaint(); 562 } 563 564 public String toString() { 565 return name; 566 } 567 568 public void valueChanged(TreeSelectionEvent e) { 569 if(group == null) { 570 570 ///ystem.err.println("Oh my god, this tree has no group: " + this); 571 572 573 574 575 576 577 578 579 580 581 571 } 572 else { 573 group.grabFocus(this); 574 } 575 Gatherer.g_man.menu_bar.setMetaAuditSuffix(getSelectionDetails()); 576 } 577 578 private boolean isValidDrop(TreePath target_path) { 579 boolean valid = false; 580 if(target_path != null) { 581 FileNode target_node = (FileNode) target_path.getLastPathComponent(); 582 582 // We can only continue testing if the node is a folder. 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 583 if(!target_node.isLeaf()) { 584 // Now we check if the node is readonly. 585 if(!target_node.isReadOnly()) { 586 // Finally we check the target path against the paths in the selection to ensure we are not adding to our own ancestors! 587 TreePath[] selection = group.getSelection(); 588 boolean failed = false; 589 for(int i = 0; !failed && selection != null && i < selection.length; i++) { 590 failed = selection[i].isDescendant(target_path); 591 } 592 // Having finally completed all the tests, we can highlight the drop target. 593 if(!failed) { 594 valid = true; 595 } 596 else { 597 ///ystem.err.println("Invalid. Target is descendant of itself."); 598 } 599 } 600 else { 601 ///ystem.err.println("Read only."); 602 } 603 } 604 else { 605 ///ystem.err.println("Leaf node. Children not allowed."); 606 } 607 previous_path = target_path; 608 } 609 else { 610 if(target_path == null) { 611 previous_path = null; 612 } 613 } 614 return valid; 615 } 616 616 } -
trunk/gli/src/org/greenstone/gatherer/gui/tree/DragTreeCellRenderer.java
r4293 r4366 42 42 43 43 public class DragTreeCellRenderer 44 44 extends DefaultTreeCellRenderer { 45 45 46 47 48 46 private boolean in_focus = false; 47 private Color selection_background; 48 private Color selection_foreground; 49 49 50 51 52 53 54 50 public DragTreeCellRenderer() { 51 super(); 52 selection_background = getBackgroundSelectionColor(); 53 selection_foreground = getTextSelectionColor(); 54 } 55 55 56 57 58 59 56 public void gainFocus() { 57 setBackgroundSelectionColor(selection_background); 58 setTextSelectionColor(selection_foreground); 59 } 60 60 61 62 63 64 61 public void loseFocus() { 62 setBackgroundSelectionColor(Color.lightGray); 63 setTextSelectionColor(Color.black); 64 } 65 65 } 66 66
Note:
See TracChangeset
for help on using the changeset viewer.