Changeset 4364
- Timestamp:
- 2003-05-27T15:40:47+12:00 (21 years ago)
- Location:
- trunk/gli/src/org/greenstone/gatherer
- Files:
-
- 36 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/sarm/SearchAndReplace.java
r4360 r4364 60 60 */ 61 61 public class SearchAndReplace 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 62 extends JDialog { 63 /** <i>true</i> if this dialog should allow for the replace and replace all buttons to be validated, <i>false</i> otherwise. */ 64 private boolean enable_replace = false; 65 /** <i>true</i> if the last call to find() found something, <i>false</i> otherwise. */ 66 private boolean found = false; 67 /** The size of the current screen real-estate. */ 68 private Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); 69 /** The metadata element that we are currently inspecting. If we are anywhere but within the find() function, then this is the last element found that matched the search string. */ 70 private ElementWrapper current_element = null; 71 /** A vector containing the result of a depth first enumeration of the records in the collection record set tree. */ 72 private EnumeratedVector records = null; 73 /** The file record that we are currently inspecting. If we are anywhere but within the find() function, and the target type is a FileNode, then this is the last record that matched our search string. Otherwise this is the record that the last metadata value that matched resides in. */ 74 private FileNode current_record = null; 75 /** The button used to close the dialog. Has the same action as the destroy button. */ 76 private JButton close_button = null; 77 /** The button used to begin a find() function call. Something must be typed in the search field before this button becomes active. */ 78 private JButton find_button = null; 79 /** The button used to repeat the find() call in order to find the next matching object. Note this only becomes active after a successful find button press. */ 80 private JButton find_next_button = null; 81 /** Redoes the action the was last undoes. Doesn't become active until there are job that are undone to be redone. :P */ 82 private JButton redo_button = null; 83 /** Replaces the currently selected object with the given replacement text. The selection was determined by find() either as part of a previous action, or immediately before the replace() is called. This button only become active if there is something typed in the search and replace fields and if enable_replace is set. */ 84 private JButton replace_button = null; 85 /** Replace all occurances of the search string with the replace string. This returns to the start of the collection and replaced every single matching reference. It only becomes enabled if something is typed in the search and replace fields and enable_replace is set. */ 86 private JButton replace_all_button = null; 87 /** When pressed this button returns the 'searching pointer' back to the start of the collection. This is used when there are no further matches for your query, or at anytime you wish to start a new query. This button only becomes active after a success find(). */ 88 private JButton reset_button = null; 89 /** This button undoes any changes made by the previous action, such as restoring all objects affected by a replace all. It only makes sense for this button to be active after a successful action. */ 90 private JButton undo_button = null; 91 /** When checked this control indicates the search comparisons should be made taking into account case. Otherwise the case becomes irrelevant to matching. */ 92 private JCheckBox case_sensitive = null; 93 /** When checked the SARM treats both search and replace string as regular expression when matching and replacing. */ 94 private JCheckBox regular_expression = null; 95 /** When checked the SARM will search the metadata element identifiers for values that match your search string. Note that at least one of the search domains is selected at all times. */ 96 private JCheckBox search_elements = null; 97 /** When checked the SARM will search the file records in the collection for values that match your search string. Note that at least one of the search domains is selected at all times. */ 98 private JCheckBox search_records = null; 99 /** When checked the SARM will search the metadata values associated with each file record in the collection for values that match your search string. Note that at least one of the search domains is selected at all times. */ 100 private JCheckBox search_values = null; 101 /** The status label shows the result of the last action. */ 102 private JLabel status = null; 103 /** This field is used to enter a string which is in turn used to replace any text value that matches the search string. If enable_replace is not set, this field remains grayed out. If enabled, and you type within this field the replace buttons will become active. */ 104 private JTextField replace = null; 105 /** The value entered in the search field is used to determine what object should be selected and brought to your attention. Note that this field can contain plain text or a regular expression, and that any typing in this field automatically resets the find() functions state to the start of the collection. */ 106 private JTextField search = null; 107 /** Whenever a string is tested against the compiled regular Pattern, a Matcher is created. Not only does this matcher allow us to determine if the test string matches the pattern, but is can be subsequently used during a replace action to evaluate special 'capture group flags' within a regular expression based replace string.<BR><BR>For instance:Seach - '(.*)fug(.*)' will match 'Refugee'<BR>and<BR>Replace - '\1\1\1. \2' will replace the match with 'ReReRe. ee'. */ 108 private Matcher matcher = null; 109 /** This is the current metadata value that we are inspecting. If non-null anywhere other then in the find() method, then this is the last metadata value that matched on search string. However it makes no sense by itself, and current_record is needed to actually reference this piece of metadata. */ 110 private Metadata current_value = null; 111 /** This is used to determine the target class of any object whose text component we are trying to replace. It is actually set to the last object that matched the search string. */ 112 private Object target = null; 113 /** This object is created by compiling a regular expression string retrieved from the search field, and in only non-null when regualr_expressions is checked. By compiling it once at the very beginning of the find() method, we lower processing time. Note that pattern is set to null whenever the find() state is reset. */ 114 private Pattern pattern = null; 115 /** A reference to ourselves so that our inner classes can dispose of us for the insurance money. */ 116 private SearchAndReplace self = null; 117 /** A stack of actions that have been undone, wait to be done again. */ 118 private Stack redo = new Stack(); 119 /** A stack of actions that have been done, waiting to undo. */ 120 private Stack undo = new Stack(); 121 /** A vector of the metadata elements attached to this collection, in the form of ElementWrappers for ease of data access. */ 122 private Vector elements = null; 123 /** The default size for a label, used to produce purty column alignment. */ 124 static final private Dimension LABEL_SIZE = new Dimension(120, 30); 125 /** The default size for the dialog. */ 126 static final private Dimension SIZE = new Dimension(640,210); 127 /** Constructor. 128 * @param enable_replace <i>true</i> if this dialog can be used for replace actions, <i>false</i> otherwise. 129 */ 130 public SearchAndReplace(boolean enable_replace) { 131 super(); 132 ///ystem.err.println("Creating a new search and replace dialog."); 133 this.enable_replace = enable_replace; 134 this.self = this; 135 // Creation 136 setModal(false); 137 setSize(SIZE); 138 setTitle(get("Title")); 139 JPanel content_pane = (JPanel) getContentPane(); 140 141 JPanel central_pane = new JPanel(); 142 143 JPanel search_pane = new JPanel(); 144 JLabel search_label = new JLabel(get("Search")); 145 search_label.setPreferredSize(LABEL_SIZE); 146 search = new JTextField(""); 147 148 JPanel replace_pane = new JPanel(); 149 JLabel replace_label = new JLabel(get("Replace")); 150 replace_label.setPreferredSize(LABEL_SIZE); 151 replace = new JTextField(""); 152 replace.setEnabled(enable_replace); 153 if(!enable_replace) { 154 replace.setBackground(Color.lightGray); 155 } 156 157 JPanel options_pane = new JPanel(); 158 JLabel options_label = new JLabel(get("Options")); 159 options_label.setPreferredSize(LABEL_SIZE); 160 JPanel inner_options_pane = new JPanel(); 161 case_sensitive = new JCheckBox(get("Case_Sensitive")); 162 regular_expression = new JCheckBox(get("Regular_Expression")); 163 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 164 JPanel options2_pane = new JPanel(); 165 JLabel options2_label = new JLabel(get("Search_In")); 166 options2_label.setPreferredSize(LABEL_SIZE); 167 JPanel inner_options2_pane = new JPanel(); 168 XORToggleButtonGroup check_box_group = new XORToggleButtonGroup(); 169 search_elements = new JCheckBox(get("Search_Elements")); 170 search_elements.setSelected(true); 171 check_box_group.add(search_elements); 172 search_records = new JCheckBox(get("Search_Records")); 173 search_records.setSelected(true); 174 check_box_group.add(search_records); 175 search_values = new JCheckBox(get("Search_Values")); 176 search_values.setSelected(true); 177 check_box_group.add(search_values); 178 179 JPanel results_pane = new JPanel(); 180 status = new JLabel(get("Status")); 181 182 JPanel buttons_pane = new JPanel(); 183 find_button = new JButton(get("Find_Button")); 184 find_button.setMnemonic(KeyEvent.VK_F); 185 find_next_button = new JButton(get("Find_Next_Button")); 186 find_next_button.setEnabled(false); 187 find_next_button.setMnemonic(KeyEvent.VK_N); 188 replace_button = new JButton(get("Replace_Button")); 189 replace_button.setEnabled(false); 190 replace_button.setMnemonic(KeyEvent.VK_R); 191 replace_all_button = new JButton(get("Replace_All_Button")); 192 replace_all_button.setEnabled(false); 193 replace_all_button.setMnemonic(KeyEvent.VK_N); 194 reset_button = new JButton(get("Reset")); 195 reset_button.setEnabled(false); 196 reset_button.setMnemonic(KeyEvent.VK_ESCAPE); 197 undo_button = new JButton(get("Undo")); 198 undo_button.setEnabled(false); 199 undo_button.setMnemonic(KeyEvent.VK_U); 200 redo_button = new JButton(get("Redo")); 201 redo_button.setEnabled(false); 202 redo_button.setMnemonic(KeyEvent.VK_D); 203 close_button = new JButton(get("General.Close")); 204 close_button.setMnemonic(KeyEvent.VK_C); 205 // Connection 206 close_button.addActionListener(new CloseActionListener()); 207 find_button.addActionListener(new FindActionListener()); 208 find_next_button.addActionListener(new FindNextActionListener()); 209 redo_button.addActionListener(new RedoActionListener()); 210 replace.addActionListener(new FindActionListener()); 211 replace_button.addActionListener(new ReplaceActionListener()); 212 replace_all_button.addActionListener(new ReplaceAllActionListener()); 213 reset_button.addActionListener(new ResetActionListener()); 214 undo_button.addActionListener(new UndoActionListener()); 215 replace.addKeyListener(new ReplaceKeyListener()); 216 search.addKeyListener(new SearchKeyListener()); 217 // Layout 218 search_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 219 220 search_pane.setLayout(new BorderLayout()); 221 search_pane.add(search_label, BorderLayout.WEST); 222 search_pane.add(search, BorderLayout.CENTER); 223 224 replace_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 225 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 226 replace_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); 227 replace_pane.setLayout(new BorderLayout()); 228 replace_pane.add(replace_label, BorderLayout.WEST); 229 replace_pane.add(replace, BorderLayout.CENTER); 230 231 inner_options_pane.setLayout(new GridLayout(1,2)); 232 inner_options_pane.add(case_sensitive); 233 inner_options_pane.add(regular_expression); 234 235 options_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); 236 options_pane.setLayout(new BorderLayout()); 237 options_pane.add(options_label, BorderLayout.WEST); 238 options_pane.add(inner_options_pane, BorderLayout.CENTER); 239 240 inner_options2_pane.setLayout(new GridLayout(1,3)); 241 inner_options2_pane.add(search_elements); 242 inner_options2_pane.add(search_records); 243 inner_options2_pane.add(search_values); 244 245 options2_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); 246 options2_pane.setLayout(new BorderLayout()); 247 options2_pane.add(options2_label, BorderLayout.WEST); 248 options2_pane.add(inner_options2_pane, BorderLayout.CENTER); 249 250 results_pane.setBorder(BorderFactory.createRaisedBevelBorder()); 251 results_pane.setLayout(new BorderLayout()); 252 results_pane.add(status, BorderLayout.CENTER); 253 254 central_pane.setLayout(new GridLayout(5,1)); 255 central_pane.add(search_pane); 256 central_pane.add(replace_pane); 257 central_pane.add(options_pane); 258 central_pane.add(options2_pane); 259 central_pane.add(results_pane); 260 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 261 buttons_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); 262 buttons_pane.setLayout(new GridLayout(2,4)); 263 buttons_pane.add(find_button); 264 buttons_pane.add(find_next_button); 265 buttons_pane.add(replace_button); 266 buttons_pane.add(replace_all_button); 267 buttons_pane.add(reset_button); 268 buttons_pane.add(undo_button); 269 buttons_pane.add(redo_button); 270 buttons_pane.add(close_button); 271 272 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 273 content_pane.setLayout(new BorderLayout()); 274 content_pane.add(central_pane, BorderLayout.CENTER); 275 content_pane.add(buttons_pane, BorderLayout.SOUTH); 276 // Reset data. 277 reset(); 278 // Restore visual position. 279 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 280 show(); 281 } 282 /** Updates the status bar to reflect the result of the latest action. Includes the ability to reset the status label whenever the find() state resets. 283 283 * @param key A <strong>String</strong> of text to use as the explaination of the actions result. 284 284 * @param optional_count An <i>int</i> which if any value, other than -1, shows the number of objects the action was performed on. 285 285 */ 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 286 private void changeStatus(String key, int optional_count) { 287 String args[] = null; 288 if(optional_count >= 0) { 289 args = new String[2]; 290 args[1] = "" + optional_count + ""; 291 } 292 else { 293 args = new String[1]; 294 } 295 if(key != null) { 296 args[0] = search.getText(); 297 status.setText(get("Status") + ": " + get(key, args)); 298 } 299 else { 300 status.setText(get("Status")); 301 } 302 } 303 /** The guts of this module, the find method, in conjunction with the matches method, searches through the several targetted domains looking for values which match the search string, returning true while leaving itself in a state that points to the matching object if one is found. What exact part of each object is matched against is decided by the objects type, and in one case this is made even more complex as one type of object exists inside the other so we need to carefully iterate through the inner before increamenting the outer. 304 304 * @param <i>true</i> if a match was found thus the current_<object> parameters are pointing to it, otherwise <i>false</i>. 305 305 */ 306 307 308 309 306 private boolean find() { 307 boolean local_found = false; 308 String find_str = search.getText(); 309 if(find_str.length() > 0) { 310 310 // If a pattern currently hasn't been compiled, compile one. 311 if(pattern == null) { 312 // If no actual regular expression script is present add .* to either end of the search string so we actually search for content of the target string not exact equality. 313 if(!regular_expression.isSelected()) { 314 find_str = "(.*)("+find_str+")(.*)"; 315 } 316 // Compile pattern 317 try { 318 if(case_sensitive.isSelected()) { 319 pattern = Pattern.compile(find_str); 320 } 321 else { 322 pattern = Pattern.compile(find_str.toLowerCase()); 323 } 324 } 325 catch(Exception error) { 326 } 311 if(pattern == null) { 312 // If no actual regular expression script is present add .* to either end of the search string so we actually search for content of the target string not exact equality. 313 if(!regular_expression.isSelected()) { 314 find_str = "(.*)("+find_str+")(.*)"; 315 } 316 // Compile pattern 317 try { 318 if(case_sensitive.isSelected()) { 319 pattern = Pattern.compile(find_str); 320 } 321 else { 322 pattern = Pattern.compile(find_str.toLowerCase()); 323 } 324 } 325 catch(Exception error) { 326 } 327 } 328 // Search through the metadata elements if required for a matching entry. 329 if(search_elements.isSelected()) { 330 for(int i = (current_element != null ? elements.indexOf(current_element) + 1 : 0); !local_found && i < elements.size(); i++) { 331 current_element = (ElementWrapper) elements.get(i); 332 if(match(current_element.getIdentity())) { 333 local_found = true; 334 target = current_element; 335 fireSelect(current_element); 336 } 337 } 338 } 339 // Search through file names. 340 if(!local_found && (search_records.isSelected() || search_values.isSelected()) && (records.hasMoreElements() || current_record != null)) { 341 if(current_record == null) { 342 current_record = (FileNode)records.nextElement(); 343 } 344 while(!local_found && (current_record != null || records.hasMoreElements())) { 345 // Try to match current record (if we haven't already). 346 if(match(current_record.toString()) && search_records.isSelected() && current_record != target) { 347 local_found = true; 348 target = current_record; 349 fireSelect(current_record); 350 } 351 else { 352 // Search through metadata values is required. 353 if(search_values.isSelected()) { 354 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(current_record.getFile()); 355 for(int i = (current_value != null ? metadatum.indexOf(current_value) + 1 : 0); !local_found && i < metadatum.size(); i++) { 356 current_value = (Metadata)metadatum.get(i); 357 if(match(current_value.getValue())) { 358 local_found = true; 359 target = current_value; 360 fireSelect(current_record, current_value); 327 361 } 328 // Search through the metadata elements if required for a matching entry. 329 if(search_elements.isSelected()) { 330 for(int i = (current_element != null ? elements.indexOf(current_element) + 1 : 0); !local_found && i < elements.size(); i++) { 331 current_element = (ElementWrapper) elements.get(i); 332 if(match(current_element.getIdentity())) { 333 local_found = true; 334 target = current_element; 335 fireSelect(current_element); 336 } 337 } 338 } 339 // Search through file names. 340 if(!local_found && (search_records.isSelected() || search_values.isSelected()) && (records.hasMoreElements() || current_record != null)) { 341 if(current_record == null) { 342 current_record = (FileNode)records.nextElement(); 343 } 344 while(!local_found && (current_record != null || records.hasMoreElements())) { 345 // Try to match current record (if we haven't already). 346 if(match(current_record.toString()) && search_records.isSelected() && current_record != target) { 347 local_found = true; 348 target = current_record; 349 fireSelect(current_record); 350 } 351 else { 352 // Search through metadata values is required. 353 if(search_values.isSelected()) { 354 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(current_record.getFile()); 355 for(int i = (current_value != null ? metadatum.indexOf(current_value) + 1 : 0); !local_found && i < metadatum.size(); i++) { 356 current_value = (Metadata)metadatum.get(i); 357 if(match(current_value.getValue())) { 358 local_found = true; 359 target = current_value; 360 fireSelect(current_record, current_value); 361 } 362 } 363 if(!local_found) { 364 current_value = null; 365 } 366 } 367 } 368 if(!local_found) { 369 current_record = (FileNode)records.nextElement(); 370 } 371 } 372 } 373 } 374 return local_found; 375 } 376 /** When a metadata element that matches our search string is found, this method is called to provide a visual cue as to what element matched in which collection. This is done by changing the tab view of the main gui and selecting the correct line from the list of sets/elements on one screen of the CDM. This method subsequently calls unhideBound() passing in the bounds of the selected component (cell or row). 362 } 363 if(!local_found) { 364 current_value = null; 365 } 366 } 367 } 368 if(!local_found) { 369 current_record = (FileNode)records.nextElement(); 370 } 371 } 372 } 373 } 374 return local_found; 375 } 376 /** When a metadata element that matches our search string is found, this method is called to provide a visual cue as to what element matched in which collection. This is done by changing the tab view of the main gui and selecting the correct line from the list of sets/elements on one screen of the CDM. This method subsequently calls unhideBound() passing in the bounds of the selected component (cell or row). 377 377 * @param element The <strong>ElementWrapper</strong> that matches our search string. 378 378 */ 379 380 381 382 383 384 385 386 379 private void fireSelect(ElementWrapper element) { 380 // Ensure the CDM view is visible. 381 Gatherer.g_man.setSelectedView(Gatherer.g_man.config_pane); 382 // Select the correct metadata element 383 Rectangle bounds = Gatherer.g_man.config_pane.setSelectedElement(element); 384 unhideSelection(bounds); 385 } 386 /** When a file record that matches our search string is found, this method is called to provide a visual cue as to what file matched. This is done by changing the tab view of the main gui and expanding and selecting the correct line from the tree of file records. This method subsequently calls unhideBound() passing in the bounds of the selected component (cell or row). 387 387 * @param record The <strong>FileNode</strong> that matches our search string. 388 388 */ 389 390 391 392 393 394 395 396 397 389 private void fireSelect(FileNode record) { 390 // Ensure that either the CollectionPane or the MetaEditPane is visible. If not make the CollectionPane visible. 391 Gatherer.g_man.setSelectedView(Gatherer.g_man.collection_pane); 392 // Select the appropriate record. 393 TreePath path = new TreePath(record.getPath()); 394 Rectangle bounds = Gatherer.g_man.collection_pane.expandPath(path); 395 unhideSelection(bounds); 396 } 397 /** When a metadata value that matches our search string is found, this method is called to provide a visual cue as to what value from which file matched. This is done by changing the tab view of the main gui and expanding and selecting the correct line from the tree of file records, and then highlighting the appropriate line of the metadata table. This method subsequently calls unhideBound() passing in the bounds of the selected component (cell or row). 398 398 * @param record The <strong>FileNode</strong> that matches our search string. 399 399 */ 400 401 402 403 404 405 406 407 408 409 410 400 private void fireSelect(FileNode record, Metadata metadata) { 401 // Ensure that either the MetaEditPane is visible. 402 Gatherer.g_man.setSelectedView(Gatherer.g_man.metaedit_pane); 403 // Select the appropriate record in the metaedit pane tree, which will cause the Metadata table to update with the appropriate bunch of metadata... 404 TreePath path = new TreePath(record.getPath()); 405 Gatherer.g_man.metaedit_pane.expandPath(path); 406 // Now select the correct metadata. 407 Rectangle bounds = Gatherer.g_man.metaedit_pane.setSelectedMetadata(metadata); 408 unhideSelection(bounds); 409 } 410 /** Retrieve a pharse string from the Dictionary matching the given key. 411 411 * @param key The <strong>String</strong> which maps to a certain phrase. 412 412 * @return The required, locale specific, phrase as a <strong>String</strong>. 413 413 * @see org.greenstone.gatherer.Dictionary 414 414 */ 415 416 417 418 415 private String get(String key) { 416 return get(key, null); 417 } 418 /** Retrieve a pharse string from the Dictionary matching the given key and based apon the given arguments. 419 419 * @param key The <strong>String</strong> which maps to a certain phrase. 420 420 * @param args The argument Strings to the Dictionary, as a <strong>String[]</strong>. … … 422 422 * @see org.greenstone.gatherer.Dictionary 423 423 */ 424 425 426 427 428 429 430 424 private String get(String key, String args[]) { 425 if(key.indexOf(".") == -1) { 426 key = "SearchAndReplace." + key; 427 } 428 return Gatherer.dictionary.get(key, args); 429 } 430 /** Used to determine is one string matches another tkaing into account the options of case sensitivity and regular expressions. 431 431 * @param find_str The search <strong>String</strong> we are trying to find. This may be a regular expression. 432 432 * @param target_str The <strong>String</strong> we are matching it against, as provided by some object. 433 433 * @return <i>true</i> if the Strings match given the option settings, <i>false</i> otherwise. 434 434 */ 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 435 private boolean match(String target_str) { 436 ///ystem.err.println("Matching " + target_str + " against " + pattern.pattern()); 437 boolean result = false; 438 if(!case_sensitive.isSelected()) { 439 target_str = target_str.toLowerCase(); 440 } 441 try { 442 matcher = pattern.matcher(target_str); 443 result = matcher.matches(); 444 } 445 catch(Exception error) { 446 } 447 return result; 448 } 449 /** Called once by the replace button, or repeatedly by replace all, in order to replace the text of the target object, as referenced by the current_<object>s, with the replace text string. This takes into account if regular expressions are expected, and modifies the replacement string with appropriate 'capture groups'. 450 450 * @return <i>true</i> if the replace action was successful, <i>false</i> otherwise. 451 451 */ 452 private boolean replace() { 453 boolean result = false; 454 String replace_str = replace.getText(); 455 // If regular expressions are allowed, we have to search for regular expression commands and deal with them. 456 if(matcher != null) { 457 StringBuffer temp = new StringBuffer(""); 458 int index_ptr = 0; 459 while(index_ptr < replace_str.length()) { 460 char c = replace_str.charAt(index_ptr); 461 // First we check for escaped regualr expression commands which start '\\' 462 if(c == '\\') { 463 index_ptr++; 464 char s = replace_str.charAt(index_ptr); 465 if(s == '\\') { 466 index_ptr++; 467 char i = replace_str.charAt(index_ptr); 468 // I currently only support one regular expression command, 'capture groups'. 469 if(Character.isDigit(i)) { 470 try { 471 int group_num = Character.getNumericValue(i); 472 if(group_num <= matcher.groupCount()) { 473 temp.append(matcher.group(group_num)); 474 } 475 } 476 catch (Exception error) { 477 } 478 } 479 } 480 // The \ had nothing to do with groups, so I guess its a path marker. 481 else { 482 temp.append(c); 483 temp.append(s); 484 } 485 } 486 else { 487 temp.append(c); 488 } 489 index_ptr++; 452 private boolean replace() { 453 boolean result = false; 454 String replace_str = replace.getText(); 455 // If regular expressions are allowed, we have to search for regular expression commands and deal with them. 456 if(matcher != null) { 457 StringBuffer temp = new StringBuffer(""); 458 int index_ptr = 0; 459 while(index_ptr < replace_str.length()) { 460 char c = replace_str.charAt(index_ptr); 461 // First we check for escaped regualr expression commands which start '\\' 462 if(c == '\\') { 463 index_ptr++; 464 char s = replace_str.charAt(index_ptr); 465 if(s == '\\') { 466 index_ptr++; 467 char i = replace_str.charAt(index_ptr); 468 // I currently only support one regular expression command, 'capture groups'. 469 if(Character.isDigit(i)) { 470 try { 471 int group_num = Character.getNumericValue(i); 472 if(group_num <= matcher.groupCount()) { 473 temp.append(matcher.group(group_num)); 490 474 } 491 replace_str = temp.toString(); 492 } 493 if(found && replace_str.length() > 0) { 494 if(target instanceof ElementWrapper) { 495 undo.push(new UndoItem(current_element, current_element.getIdentity(), replace_str)); 496 updateElement(current_element, replace_str); 497 result = true; 498 } 499 if(target instanceof FileNode) { 500 undo.push(new UndoItem(current_record, current_record.toString(), replace_str)); 501 updateRecord(current_record, replace_str); 502 result = true; 503 } 504 if(target instanceof Metadata) { 505 // If we are replacing metadata value there is a bit more messaging we need to do to any replacement string to ensure that any hierarchy context is transferred to the new value, so... 506 String absolute_value = current_value.getAbsoluteValue(); 507 if(absolute_value.indexOf("\\") != -1) { 508 String absolute_path = absolute_value.substring(0, absolute_value.lastIndexOf("\\") + 1); 509 replace_str = absolute_path + replace_str; 510 ///ystem.err.println("I replaced a hierarchy value like so:\n"+absolute_value+"\nto\n"+replace_str); 511 } 512 undo.push(new UndoItem(current_value, current_record, current_value.getValue(), replace_str)); 513 updateMetadata(current_value, current_record, replace_str); 514 result = true; 515 } 516 } 517 ///ystem.err.println("Undo queue length = " + undo.size()); 518 return result; 519 } 520 /** Used to restore the search space within the collection to its initial state for find(). 521 */ 522 private void reset() { 523 current_element = null; 524 current_record = null; 525 current_value = null; 526 elements = Gatherer.c_man.getCollection().msm.getElements(); 527 found = false; 528 matcher = null; 529 pattern = null; 530 records = Utility.depthFirstEnumeration((TreeNode)Gatherer.c_man.getRecordSet().getRoot(), new EnumeratedVector()); 531 target = null; 532 } 533 /** Attempts to move the dialog so that it doesn't obscure the coordinates detailed. Can't always do so. 475 } 476 catch (Exception error) { 477 } 478 } 479 } 480 // The \ had nothing to do with groups, so I guess its a path marker. 481 else { 482 temp.append(c); 483 temp.append(s); 484 } 485 } 486 else { 487 temp.append(c); 488 } 489 index_ptr++; 490 } 491 replace_str = temp.toString(); 492 } 493 if(found && replace_str.length() > 0) { 494 if(target instanceof ElementWrapper) { 495 undo.push(new UndoItem(current_element, current_element.getIdentity(), replace_str)); 496 updateElement(current_element, replace_str); 497 result = true; 498 } 499 if(target instanceof FileNode) { 500 undo.push(new UndoItem(current_record, current_record.toString(), replace_str)); 501 updateRecord(current_record, replace_str); 502 result = true; 503 } 504 if(target instanceof Metadata) { 505 // If we are replacing metadata value there is a bit more messaging we need to do to any replacement string to ensure that any hierarchy context is transferred to the new value, so... 506 String absolute_value = current_value.getAbsoluteValue(); 507 if(absolute_value.indexOf("\\") != -1) { 508 String absolute_path = absolute_value.substring(0, absolute_value.lastIndexOf("\\") + 1); 509 replace_str = absolute_path + replace_str; 510 ///ystem.err.println("I replaced a hierarchy value like so:\n"+absolute_value+"\nto\n"+replace_str); 511 } 512 undo.push(new UndoItem(current_value, current_record, current_value.getValue(), replace_str)); 513 updateMetadata(current_value, current_record, replace_str); 514 result = true; 515 } 516 } 517 ///ystem.err.println("Undo queue length = " + undo.size()); 518 return result; 519 } 520 /** Used to restore the search space within the collection to its initial state for find(). 521 */ 522 private void reset() { 523 current_element = null; 524 current_record = null; 525 current_value = null; 526 elements = Gatherer.c_man.getCollection().msm.getElements(); 527 found = false; 528 matcher = null; 529 pattern = null; 530 records = Utility.depthFirstEnumeration((TreeNode)Gatherer.c_man.getRecordSet().getRoot(), new EnumeratedVector()); 531 target = null; 532 } 533 /** Attempts to move the dialog so that it doesn't obscure the coordinates detailed. Can't always do so. 534 534 * @param bounds A <strong>Rectangle</strong> around the selected component, with co-ordinates corrected to be absolute to the screen rather than relative to whatever parent component the selection was in. 535 535 */ 536 537 538 539 540 536 private void unhideSelection(Rectangle bounds) { 537 /** @TODO - Elsewhere generate absolute references. Here make use of them. */ 538 ///ystem.err.println("Unhide selection with bounds " + bounds); 539 } 540 /** Whenever replace(), the undo action listener or the redo action listener wants to actually change the value of an element it uses this method. Hows that for code reuse. 541 541 * @param element The <strong>ElementWrapper</strong> containing the metadata element we want to change. 542 542 * @param value The new text value for the elements identifier as a <strong>String</strong>. 543 543 */ 544 545 546 547 544 private void updateElement(ElementWrapper element, String value) { 545 Gatherer.c_man.getCollection().msm.renameElement(element, value); 546 } 547 /** Whenever replace(), the undo action listener or the redo action listener wants to actually change the value of a metadata value it uses this method. Hows that for code reuse. 548 548 * @param metadata The <strong>Metadata</strong> we want to change. 549 549 * @param record The <strong>FileNode</strong> the target metadata will be found in. 550 550 * @param value The new text value for the metadata as a <strong>String</strong>. 551 551 */ 552 553 554 555 556 557 552 private void updateMetadata(Metadata metadata, FileNode record, String value) { 553 FileNode array[] = new FileNode[1]; 554 array[0] = record; 555 Gatherer.c_man.getCollection().msm.updateMetadata(0L, metadata, array, value, MetaEditPrompt.UPDATE_ONCE, metadata.isFileLevel()); 556 } 557 /** Whenever replace(), the undo action listener or the redo action listener wants to actually change the value of a file record it uses this method. Hows that for code reuse. 558 558 * @param record The <strong>FileNode</strong> we want to change. 559 559 * @param value The new text value for the files name as a <strong>String</strong>. 560 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 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 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 561 private void updateRecord(FileNode record, String value) { 562 TreeModel model = Gatherer.c_man.getRecordSet(); 563 /* @todo 564 model.rename(record, value); 565 */ 566 } 567 /** Several actions methods call this to check if the current state of the dialog allows for the replace butons to become active. 568 */ 569 private void validateReplaceButtons() { 570 boolean value = (replace.getText().length() > 0) && enable_replace; 571 ///ystem.err.println("Validating replace buttons to " + value); 572 replace_button.setEnabled(value); 573 replace_all_button.setEnabled(value); 574 } 575 576 private class CloseActionListener 577 implements ActionListener { 578 public void actionPerformed(ActionEvent event) { 579 self.dispose(); 580 } 581 } 582 583 private class FindActionListener 584 implements ActionListener { 585 public void actionPerformed(ActionEvent event) { 586 reset(); 587 if(find()) { 588 find_button.setEnabled(false); 589 find_next_button.setEnabled(true); 590 if(enable_replace) { 591 replace_button.setEnabled(true); 592 replace_all_button.setEnabled(true); 593 } 594 reset_button.setEnabled(true); 595 changeStatus("Found", -1); 596 found = true; 597 } 598 else { 599 changeStatus("Not_Found", -1); 600 found = false; 601 } 602 } 603 } 604 605 private class FindNextActionListener 606 implements ActionListener { 607 public void actionPerformed(ActionEvent event) { 608 if(find()) { 609 changeStatus("Found", -1); 610 found = true; 611 } 612 else { 613 find_next_button.setEnabled(false); 614 changeStatus("Not_Found", -1); 615 } 616 } 617 } 618 619 private class RedoActionListener 620 implements ActionListener { 621 private boolean start = false; 622 public void actionPerformed(ActionEvent event) { 623 if(!redo.empty()) { 624 start = true; 625 UndoItem redo_item = (UndoItem) redo.pop(); 626 while(redo_item != null && !redo_item.isDummy()) { 627 // Determine the type of action we have to undo, then undo it. 628 ElementWrapper element = redo_item.getElement(); 629 if(element != null) { 630 updateElement(element, redo_item.getAfter()); 631 addToUndo(redo_item); 632 } 633 FileNode record = redo_item.getRecord(); 634 Metadata metadata = redo_item.getMetadata(); 635 if(metadata != null && record != null) { 636 updateMetadata(metadata, record, redo_item.getAfter()); 637 addToUndo(redo_item); 638 } 639 else if(record != null) { 640 updateRecord(record, redo_item.getAfter()); 641 addToUndo(redo_item); 642 } 643 if(!redo.empty()) { 644 redo_item = (UndoItem) redo.pop(); 645 } 646 else { 647 redo_item = null; 648 } 649 } 650 if(redo.empty()) { 651 redo_button.setEnabled(false); 652 } 653 } 654 } 655 private void addToUndo(UndoItem redo_item) { 656 if(start) { 657 undo.push(new UndoItem()); 658 start = false; 659 } 660 undo.push(redo_item); 661 undo_button.setEnabled(true); 662 } 663 } 664 665 private class ReplaceActionListener 666 implements ActionListener { 667 public void actionPerformed(ActionEvent event) { 668 if(!found) { 669 found = find(); 670 } 671 if(found) { 672 undo.push(new UndoItem()); 673 replace(); 674 replace_button.setEnabled(false); 675 reset_button.setEnabled(true); 676 undo_button.setEnabled(true); 677 changeStatus("Replaced", -1); 678 } 679 else { 680 find_next_button.setEnabled(false); 681 replace_button.setEnabled(false); 682 replace_all_button.setEnabled(false); 683 changeStatus("Not_Found", -1); 684 } 685 } 686 } 687 688 private class ReplaceAllActionListener 689 implements ActionListener { 690 public void actionPerformed(ActionEvent event) { 691 reset(); 692 int count = 0; 693 boolean start = true; 694 while((found = find()) == true) { 695 if(start) { 696 undo.push(new UndoItem()); 697 start = false; 698 } 699 count++; 700 replace(); 701 } 702 find_button.setEnabled(false); 703 find_next_button.setEnabled(false); 704 replace_button.setEnabled(false); 705 replace_all_button.setEnabled(false); 706 reset_button.setEnabled(true); 707 if(!undo.empty()) { 708 undo_button.setEnabled(true); 709 } 710 changeStatus("Replaced_All", count); 711 } 712 } 713 714 private class ResetActionListener 715 implements ActionListener { 716 public void actionPerformed(ActionEvent event) { 717 reset(); 718 find_button.setEnabled(true); 719 find_next_button.setEnabled(false); 720 replace_button.setEnabled(false); 721 replace_all_button.setEnabled(false); 722 reset_button.setEnabled(false); 723 changeStatus(null, -1); 724 } 725 } 726 727 private class UndoActionListener 728 implements ActionListener { 729 private boolean start = false; 730 public void actionPerformed(ActionEvent event) { 731 if(!undo.empty()) { 732 start = true; 733 UndoItem undo_item = (UndoItem) undo.pop(); 734 while(undo_item != null && !undo_item.isDummy()) { 735 // Determine the type of action we have to undo, then undo it. 736 ElementWrapper element = undo_item.getElement(); 737 if(element != null) { 738 updateElement(element, undo_item.getBefore()); 739 addToRedo(undo_item); 740 } 741 else { 742 FileNode record = undo_item.getRecord(); 743 Metadata metadata = undo_item.getMetadata(); 744 if(metadata != null && record != null) { 745 // The old metadata doesn't actually exist anymore. We have to generate a new metadata object based on the old metadata objects element and the 'after' string. 746 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(metadata.getElement()); 747 GValueNode value = model.getValue(undo_item.getAfter()); 748 Metadata new_metadata = new Metadata(metadata.getElement(), value); 749 updateMetadata(new_metadata, record, undo_item.getBefore()); 750 addToRedo(undo_item); 751 } 752 else if(record != null) { 753 updateRecord(record, undo_item.getBefore()); 754 addToRedo(undo_item); 755 } 756 else { 757 ///ystem.err.println("Unknown undo item."); 758 } 759 } 760 if(!undo.empty()) { 761 undo_item = (UndoItem) undo.pop(); 762 } 763 else { 764 undo_item = null; 765 } 766 } 767 if(undo.empty()) { 768 undo_button.setEnabled(false); 769 } 770 } 771 } 772 private void addToRedo(UndoItem undo_item) { 773 if(start) { 774 redo.push(new UndoItem()); 775 start = false; 776 } 777 redo.push(undo_item); 778 redo_button.setEnabled(true); 779 } 780 } 781 782 private class ReplaceKeyListener 783 extends KeyAdapter { 784 public void keyTyped(KeyEvent event) { 785 validateReplaceButtons(); 786 } 787 } 788 789 private class SearchKeyListener 790 extends KeyAdapter { 791 public void keyTyped(KeyEvent event) { 792 if(current_element != null || current_record != null || current_value != null) { 793 reset(); 794 find_button.setEnabled(true); 795 find_next_button.setEnabled(false); 796 replace_button.setEnabled(false); 797 replace_all_button.setEnabled(false); 798 reset_button.setEnabled(false); 799 changeStatus(null, -1); 800 } 801 } 802 } 803 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 804 private class UndoItem { 805 private ElementWrapper element = null; 806 private FileNode record = null; 807 private Metadata metadata = null; 808 private String after = null; 809 private String before = null; 810 /** Constructor for a dummy item, which separates different batches of changes. */ 811 public UndoItem() { 812 } 813 public UndoItem(ElementWrapper element, String before, String after) { 814 this.after = after; 815 this.before = before; 816 this.element = element; 817 } 818 public UndoItem(FileNode record, String before, String after) { 819 this.after = after; 820 this.before = before; 821 this.record = record; 822 } 823 public UndoItem(Metadata metadata, FileNode record, String before, String after) { 824 this.after = after; 825 this.before = before; 826 this.metadata = metadata; 827 this.record = record; 828 } 829 public String getAfter() { 830 return after; 831 } 832 public String getBefore() { 833 return before; 834 } 835 public ElementWrapper getElement() { 836 return element; 837 } 838 public Metadata getMetadata() { 839 return metadata; 840 } 841 public FileNode getRecord() { 842 return record; 843 } 844 public boolean isDummy() { 845 return (element == null && record == null && metadata == null); 846 } 847 } 848 848 } 849 -
trunk/gli/src/org/greenstone/gatherer/shell/GBasicProgressMonitor.java
r4293 r4364 60 60 */ 61 61 public class GBasicProgressMonitor 62 63 64 65 66 67 68 69 70 71 62 extends JProgressBar 63 implements GShellProgressMonitor { 64 /** Whether the process has been asked to stop. */ 65 private boolean stop = false; 66 /** Constructor **/ 67 public GBasicProgressMonitor() { 68 super(); 69 //this.setIcon(ready); 70 } 71 /** Don't do anything if we are given a progress bar, we have our own. 72 72 * @param progress_bar A JProgressBar 73 73 */ 74 75 76 74 public void addProgressBar(JProgressBar progress_bar) { 75 } 76 /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value. 77 77 * @return A <i>int</i> with a value of zero if and only if the script was successful. 78 78 */ 79 80 81 82 83 79 public int exitValue() { 80 // Always return true as we don't know any better. 81 return 0; 82 } 83 /** As we are probably accessing this via an interface we need to gain 84 84 * access to the component itself. 85 85 */ 86 87 88 89 86 public Component getProgress() { 87 return this; 88 } 89 /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor. 90 90 * @return A <strong>boolean</strong> indicating if the process has been asked to stop. 91 91 */ 92 93 94 95 96 97 98 99 92 public boolean hasSignalledStop() { 93 return stop; 94 } 95 /** Inform the progress bar that it should programatically increment progress by one step. */ 96 public void increment() { 97 // We can't do anything about this. 98 } 99 /** The parse method is how more complex progress bars figure out how far they have progressed. However we don't. 100 100 * @param line A String representing the latest line of output from 101 101 * the external process we are monitoring. 102 102 */ 103 104 105 103 public void parse(String line) { 104 } 105 /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two. 106 106 * @param state The desired state of the stop flag as a <strong>boolean</strong>. 107 107 */ 108 109 110 111 108 public void setStop(boolean state) { 109 stop = state; 110 } 111 /** This method tells this class that the job it is showing the progress 112 112 * for has begun. 113 113 */ 114 115 116 117 118 114 public void start() { 115 //this.setIcon(busy); 116 setIndeterminate(true); 117 } 118 /** This method tells this class that the job it is showing the progress 119 119 * for has come to an end. 120 120 */ 121 122 123 124 121 public void stop() { 122 //this.setIcon(ready); 123 setIndeterminate(false); 124 } 125 125 } -
trunk/gli/src/org/greenstone/gatherer/shell/GBuildProgressMonitor.java
r4293 r4364 46 46 */ 47 47 public class GBuildProgressMonitor 48 49 50 51 52 53 54 55 56 57 58 48 implements GShellProgressMonitor { 49 /** Indicates if the GUI has asked the process this object monitors to stop. */ 50 private boolean stop = false; 51 52 private GImportProgressMonitor import_progress = null; 53 54 private int num_docs = -1; 55 56 private int num_indexes = -1; 57 58 private int state = 0; 59 59 60 60 /** The progress bar this monitor updates. */ 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 61 private JProgressBar progress_bar; 62 63 static final private int MIN = 0; 64 static final private int MAX = 1000000; 65 66 static final int BASE = 0; 67 static final int CTCT = 1; // Creating the compressed text 68 static final int CTCT_CTS = 2; // Creating the compressed text - collecting text statistics 69 static final int CTCT_CTS_PS = 3; // Creating the compressed text - collecting text statistics - parsed successfully 70 static final int CTCT_CTCD = 4; // Creating the compressed text - creating the compression dictionary 71 static final int CTCT_CTT = 5; // Creating the compressed text - compressing the text 72 static final int CTCT_CTT_PS = 6; // Creating the compressed text - compressing the text - parsed successfully 73 static final int BI = 7; // Building index 74 static final int BI_CID = 8; // Building index - creating index dictionary 75 static final int BI_CID_PS = 9; // Building index - creating index dictionary - parsed successfully 76 static final int BI_ITT = 10; // Building index - inverting the text 77 static final int BI_ITT_PS = 11; // Building index - inverting the text - parsed successfully 78 static final int BI_CTWF = 12; // Building index - create the weights file 79 static final int BI_CODSD = 13; // Building index - creating 'on-disk' stemmed dictionary 80 static final int BI_CSI = 14; // Building index - creating stem indexes 81 static final int CTID = 15; // Creating the info database 82 static final int CTID_PS = 16; // Creating the info database - parsed successfully 83 static final int CAF = 17; // Creating auxiliary files 84 85 static final String BI_STR = "*** building index"; 86 static final String CTS_STR = "collecting text statistics"; 87 static final String CTT_STR = "compressing the text"; 88 static final String CTWF_STR = "create the weights file"; 89 static final String CAF_STR = "creating auxiliary files"; 90 static final String CID_STR = "creating index dictionary"; 91 static final String CSI_STR = "creating stem indexes"; 92 static final String CTCT_STR = "*** creating the compressed text"; 93 static final String CTCD_STR = "creating the compression dictionary"; 94 static final String CTID_STR = "*** creating the info database and processing associated files"; 95 static final String CODSD_STR = "creating 'on-disk' stemmed dictionary"; 96 static final String ITT_STR = "inverting the text"; 97 static final String PS_STR = "hash"; 98 99 public GBuildProgressMonitor(GImportProgressMonitor import_progress) { 100 this.import_progress = import_progress; 101 progress_bar = new JProgressBar(); 102 progress_bar.setMaximum(MAX); 103 progress_bar.setMinimum(MIN); 104 progress_bar.setValue(MIN); 105 } 106 107 /** Method to register a new progress bar with this monitor. 108 108 * @param progress_bar The new <strong>JProgressBar</strong>. 109 109 */ 110 111 112 113 114 115 116 110 public void addProgressBar(JProgressBar progress_bar) { 111 this.progress_bar = progress_bar; 112 progress_bar.setMaximum(MAX); 113 progress_bar.setMinimum(MIN); 114 progress_bar.setValue(MIN); 115 } 116 /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value. 117 117 * @return A <i>int</i> with a value of zero if and only if the script was successful. 118 118 */ 119 120 121 122 123 124 125 119 public int exitValue() { 120 if(state == BASE) { 121 return 0; 122 } 123 return 1; 124 } 125 /** Method to retrieve whatever control is being used as the progress indicator. Usually a <strong>JProgressBar</strong> but there may be others implemented later. 126 126 * @return A <strong>Component</strong> on which the progress of the process is being displayed. 127 127 */ 128 129 130 131 128 public Component getProgress() { 129 return progress_bar; 130 } 131 /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor. 132 132 * @return A <strong>boolean</strong> indicating if the process has been asked to stop. 133 133 */ 134 135 136 137 138 139 140 141 142 134 public boolean hasSignalledStop() { 135 return stop; 136 } 137 /** Inform the progress bar that it should programatically increment progress by one step. */ 138 public void increment() { 139 // There is no reason this should be called for a build. 140 } 141 142 /** This method is used to 'feed in' a line of text captured from the process. 143 143 * @param line A <strong>String</strong> of text captured from either standard out or standard error. 144 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 145 public void parse(String raw_line) { 146 String line = raw_line.toLowerCase(); 147 // We first check if we can parse, which depends on num_docs > 0 and num_indexes > 0. 148 if(num_docs > 0 && num_indexes > 0) { 149 switch(state) { 150 case BASE: 151 // We are watching for the beginning of a phase 152 if(line.indexOf(CTCT_STR) != -1) { 153 // No progress really. 154 state = CTCT; 155 ///ystem.err.println("CTCT [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 156 } 157 else if(line.indexOf(BI_STR) != -1) { 158 // Skip the progress up to this point. 159 progress_bar.setValue(progress_bar.getValue() + (MAX / (3 + num_indexes))); 160 state = BI; 161 ///ystem.err.println("BI [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 162 } 163 else if(line.indexOf(CTID_STR) != -1) { 164 // Skip the progress up to this point 165 progress_bar.setValue(progress_bar.getValue() + (((1 + num_indexes ) * MAX) / (3 + num_indexes))); 166 state = CTID; 167 ///ystem.err.println("CTID [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 168 } 169 else if(line.indexOf(CAF_STR) != -1) { 170 // Skip the progres up to this point 171 progress_bar.setValue(progress_bar.getValue() + (((2 + num_indexes ) * MAX) / (3 + num_indexes))); 172 state = CAF; 173 ///ystem.err.println("CAF [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 174 } 175 break; 176 case CTCT: 177 if(line.indexOf(CTS_STR) != -1) { 178 // No progress really. 179 state = CTCT_CTS; 180 ///ystem.err.println("CTS [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 181 } 182 break; 183 case CTCT_CTS: 184 if(line.indexOf(PS_STR) != -1) { 185 // We have finally found something we can increment because of. 186 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 3 * num_docs))); 187 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 3 * num_docs))); 188 } 189 else if(line.indexOf(CTCD_STR) != -1) { 190 state = CTCT_CTCD; 191 ///ystem.err.println("CTCD [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 192 } 193 break; 194 case CTCT_CTCD: 195 if(line.indexOf(CTT_STR) != -1) { 196 // Finished creating the compression dictionary. 197 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 3))); 198 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 3))); 199 state = CTCT_CTT; 200 ///ystem.err.println("CTT [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 201 } 202 break; 203 case CTCT_CTT: 204 if(line.indexOf(PS_STR) != -1) { 205 // We have finally found something we can increment because of. 206 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 3 * num_docs))); 207 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 3 * num_docs))); 208 } 209 else if(line.indexOf(BI_STR) != -1) { 210 state = BI; 211 ///ystem.err.println("BI [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 212 } 213 break; 214 case BI: 215 if(line.indexOf(CID_STR) != -1) { 216 state = BI_CID; 217 ///ystem.err.println("CID [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 218 } 219 break; 220 case BI_CID: 221 if(line.indexOf(PS_STR) != -1) { 222 // We have finally found something we can increment because of. 223 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 5 * num_docs))); 224 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 5 * num_docs))); 225 } 226 else if(line.indexOf(ITT_STR) != -1) { 227 state = BI_ITT; 228 ///ystem.err.println("ITT [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 229 } 230 break; 231 case BI_ITT: 232 if(line.indexOf(PS_STR) != -1) { 233 // We have finally found something we can increment because of. 234 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 5 * num_docs))); 235 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 5 * num_docs))); 236 } 237 else if(line.indexOf(CTWF_STR) != -1) { 238 state = BI_CTWF; 239 ///ystem.err.println("CTWF [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 240 } 241 break; 242 case BI_CTWF: 243 if(line.indexOf(CODSD_STR) != -1) { 244 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 5))); 245 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 5))); 246 state = BI_CODSD; 247 ///ystem.err.println("CODSD [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 248 } 249 break; 250 case BI_CODSD: 251 if(line.indexOf(CSI_STR) != -1) { 252 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 5))); 253 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 5))); 254 state = BI_CSI; 255 ///ystem.err.println("CSI [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 256 } 257 break; 258 case BI_CSI: 259 if(line.indexOf(BI_STR) != -1) { 260 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 5))); 261 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 5))); 262 state = BI; 263 ///ystem.err.println("BI [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 264 } 265 else if(line.indexOf(CTID_STR) != -1) { 266 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * 5))); 267 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * 5))); 268 state = CTID; 269 ///ystem.err.println("CTID [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 270 } 271 break; 272 case CTID: 273 if(line.indexOf(PS_STR) != -1) { 274 // We have finally found something we can increment because of. 275 progress_bar.setValue(progress_bar.getValue() + (MAX / ((3 + num_indexes) * num_docs))); 276 ///ystem.err.println("Increase " + (MAX / ((3 + num_indexes) * num_docs))); 277 } 278 else if(line.indexOf(CAF_STR) != -1) { 279 state = CAF; 280 ///ystem.err.println("CAF [" + state + "] - " + ((progress_bar.getValue() * 100) / MAX) + "%"); 281 } 282 break; 283 case CAF: 284 progress_bar.setValue(MAX); 285 state = BASE; 286 ///ystem.err.println("Done! 100%"); 287 break; 288 } 289 } 290 else { 291 291 ///ystem.err.println("Number of documents = " + num_docs); 292 292 ///ystem.err.println("Number of indexes = " + num_indexes); 293 294 295 296 297 298 299 300 301 302 303 304 305 306 293 progress_bar.setIndeterminate(true); 294 } 295 } 296 297 public void reset() { 298 state = BASE; 299 progress_bar.setValue(MIN); 300 num_docs = -1; 301 num_indexes = -1; 302 stop = false; 303 progress_bar.setIndeterminate(false); 304 } 305 306 /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two. 307 307 * @param state The desired state of the stop flag as a <strong>boolean</strong>. 308 308 */ 309 310 311 312 313 309 public void setStop(boolean state) { 310 stop = state; 311 progress_bar.setIndeterminate(false); 312 } 313 /** This method resets this monitor to the start, reseting the process parsing and progress bar. 314 314 * TODO Everthing. 315 315 */ 316 317 318 319 320 321 316 public void start() { 317 progress_bar.setValue(MIN); 318 num_indexes = Gatherer.g_man.config_pane.getIndexes().size(); 319 num_docs = import_progress.getNumberOfDocuments(); 320 } 321 /** This method indicates the process is complete. 322 322 * TODO Everthing. 323 323 */ 324 325 326 327 324 public void stop() { 325 progress_bar.setIndeterminate(false); 326 progress_bar.setValue(MAX); // Not max, or else it won't blank properly for the next build. 327 } 328 328 } 329 330 -
trunk/gli/src/org/greenstone/gatherer/shell/GImportProgressMonitor.java
r4293 r4364 46 46 */ 47 47 public class GImportProgressMonitor 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 48 implements GShellProgressMonitor { 49 /** Indicates if the GUI has asked the process this object monitors to stop. */ 50 private boolean stop = false; 51 /** The number of directories that are expected to be scanned by this import process. May not be correct for various reasons, such as a custom RecPlug setup, but its seems the most reliable for general cases. */ 52 private int num_dirs = 0; 53 /** This holds the number of documents actually processed by the import command, as garnered from the final block of text output. */ 54 private int num_docs = 0; 55 /** The progress bar this monitor updates. */ 56 private JProgressBar progress_bar; 57 /** The maximum value for this progress bar. */ 58 static final private int MAX = 1000000; 59 /** The minimum value for this progress bar. */ 60 static final private int MIN = 0; 61 /** A String fragment which is used to determine if the current output process line is referring to the number of documents actually processed by the import script. */ 62 static final private String NUM_DOCS_PROCESSED = "processed and included in the collection"; 63 /** The Sentinel value is the String fragment an output process line must start with to be considered a milestone. In this case we are counting directories, so the fragment refers to a directory scan. */ 64 static final private String SENTINEL = "recplug: getting directory "; 65 public GImportProgressMonitor() { 66 progress_bar = new JProgressBar(); 67 progress_bar.setMaximum(MAX); 68 progress_bar.setMinimum(MIN); 69 progress_bar.setValue(MIN); 70 } 71 /** Method to register a new progress bar with this monitor. 72 72 * @param progress_bar The new <strong>JProgressBar</strong>. 73 73 */ 74 75 76 77 78 79 80 74 public void addProgressBar(JProgressBar progress_bar) { 75 this.progress_bar = progress_bar; 76 progress_bar.setMaximum(MAX); 77 progress_bar.setMinimum(MIN); 78 progress_bar.setValue(MIN); 79 } 80 /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value. 81 81 * @return A <i>int</i> with a value of zero if and only if the script was successful. 82 82 */ 83 84 85 86 87 88 89 83 public int exitValue() { 84 if(num_docs > 0) { 85 return 0; 86 } 87 return 1; 88 } 89 /** Retrieve the number of documents recorded by the import monitor during the execution of the import scripts. 90 90 * @return An <i>int</i> giving the document number. 91 91 */ 92 93 94 92 public int getNumberOfDocuments() { 93 return num_docs; 94 } 95 95 96 96 /** Method to retrieve whatever control is being used as the progress indicator. Usually a <strong>JProgressBar</strong> but there may be others implemented later. 97 97 * @return A <strong>Component</strong> on which the progress of the process is being displayed. 98 98 */ 99 100 101 102 99 public Component getProgress() { 100 return progress_bar; 101 } 102 /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor. 103 103 * @return A <strong>boolean</strong> indicating if the process has been asked to stop. 104 104 */ 105 106 107 108 109 110 111 112 113 114 115 116 117 105 public boolean hasSignalledStop() { 106 return stop; 107 } 108 /** Inform the progress bar that it should programatically increment progress by one step. This is only called during the metadata archive extraction so each step should be (100 / 5) / num_docs. */ 109 public void increment() { 110 if(num_docs > 0) { 111 progress_bar.setValue(progress_bar.getValue() + ((MAX / 5) / num_docs)); 112 } 113 else { 114 progress_bar.setIndeterminate(true); 115 } 116 } 117 /** This method is used to 'feed in' a line of text captured from the process. 118 118 * @param line A <strong>String</strong> of text captured from either standard out or standard error. 119 119 * TODO Everthing. 120 120 */ 121 122 123 121 public void parse(String line_raw) { 122 String line = line_raw.toLowerCase(); 123 if(line.indexOf(SENTINEL) != -1) { 124 124 ///ystem.err.println("Sentinal value detected: "); 125 126 127 128 129 130 131 132 133 134 135 125 if(num_dirs > 0) { 126 ///ystem.err.println(count + " of " + num_dirs); 127 ///ystem.err.println("Value before: " + progress_bar.getValue()); 128 progress_bar.setValue(progress_bar.getValue() + ((4 * (MAX / 5)) / num_dirs)); 129 ///ystem.err.println("Value after: " + progress_bar.getValue()); 130 } 131 else { 132 progress_bar.setIndeterminate(true); 133 } 134 } 135 else if(line.indexOf(NUM_DOCS_PROCESSED) != -1) { 136 136 // Increment the import because we have finished a directory 137 137 138 139 140 141 142 143 144 145 146 147 138 num_docs = -1; 139 StringTokenizer tokenizer = new StringTokenizer(line); 140 while(num_docs == -1 && tokenizer.hasMoreTokens()) { 141 String pos_num_docs_str = tokenizer.nextToken(); 142 try { 143 num_docs = Integer.parseInt(pos_num_docs_str); 144 } 145 catch (Exception error) { 146 } 147 } 148 148 ///ystem.err.println("Parsed num_docs to be " + num_docs); 149 150 151 149 } 150 } 151 /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two. 152 152 * @param state The desired state of the stop flag as a <strong>boolean</strong>. 153 153 */ 154 155 156 157 158 154 public void setStop(boolean state) { 155 stop = state; 156 progress_bar.setIndeterminate(false); 157 } 158 /** This method resets this monitor to the start, reseting the process parsing and progress bar. 159 159 * TODO Everthing. 160 160 */ 161 162 163 164 165 166 161 public void start() { 162 num_dirs = Gatherer.c_man.getCollection().getFolderCount(); 163 progress_bar.setValue(MIN); 164 System.err.println("Number of directories is " + num_dirs); 165 } 166 /** This method indicates the process is complete. 167 167 * TODO Everthing. 168 168 */ 169 170 171 172 169 public void stop() { 170 progress_bar.setIndeterminate(false); 171 progress_bar.setValue(MAX); 172 } 173 173 } -
trunk/gli/src/org/greenstone/gatherer/shell/GShell.java
r4317 r4364 48 48 */ 49 49 public class GShell 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 96 97 98 99 100 101 102 103 104 105 106 50 extends Thread { 51 /** A flag used to determine if this process has been asked to cancel. */ 52 private boolean cancel = false; 53 /** The list of listeners associated with this class. */ 54 private EventListenerList listeners = null; 55 /** The current status of this shell process. */ 56 private int status = -1; 57 /** The type of message being sent. */ 58 private int msg_type = -1; 59 /** The type of shell process. */ 60 private int type = -1; 61 /** The caller of this process, and thus the class most interested in messages. */ 62 private GShellListener caller = null; 63 /** The progress monitor associated with this process. */ 64 private GShellProgressMonitor progress = null; 65 /** Arguments to be given to the process (including the executable you are calling. */ 66 private String args[] = null; 67 /** Element in process type enumeration. */ 68 static final public int BUILD = 0; 69 /** Element in process type enumeration. */ 70 static final public int IMPORT = 1; 71 /** Element in process type enumeration. */ 72 static final public int NEW = 2; 73 /** Element in process type enumeration. */ 74 static final public int OTHER = 3; 75 /** Element in status type enumeration. */ 76 static final public int ERROR = 0; 77 /** Element in status type enumeration. */ 78 static final public int OK = 1; 79 /** Element in process type name enumeration. */ 80 static public String GSHELL_BUILD = "gshell_build"; 81 /** Element in process type name enumeration. */ 82 static public String GSHELL_IMPORT = "gshell_import"; 83 /** Element in process type name enumeration. */ 84 static public String GSHELL_NEW = "gshell_new"; 85 /** Constructor gatherer all the data required to create a new process, and emit meaningfull messages. 86 * @param args A <strong>String[]</strong> containing the arguments to the process thread, including the name of the executable. 87 * @param type An <strong>int</strong> that indicates what group of processes this process belongs to, as some are treated slightly differently (i.e an IMPORT type process is always followed by a BUILD one). 88 * @param msg_type As process threads may be background (like a makecol.pl call) or important processes in their own right (such as an IMPORT-BUILD) we must be able to set what level messages posted by this class will have by usings this <strong>int</strong>. 89 * @param caller The default <i>GShellListener</i> that is interested in the progress of this process. 90 * @param progress The <i>GShellProgressMonitor</i> associated with this process. 91 * @param name A <strong>String</strong> identifier given to the process, for convience and debug reasons. 92 */ 93 public GShell(String args[], int type, int msg_type, GShellListener caller, GShellProgressMonitor progress, String name) { 94 super(name); 95 this.args = args; 96 this.msg_type = msg_type; 97 this.type = type; 98 this.caller = caller; 99 this.progress = progress; 100 this.status = 0; 101 // Lower this jobs priority 102 this.setPriority(Thread.MIN_PRIORITY); 103 listeners = new EventListenerList(); 104 listeners.add(GShellListener.class, caller); 105 } 106 /** This method adds another shell listener to this process. 107 107 * @param listener The new <i>GShellListener</i>. 108 108 */ 109 110 111 112 109 public void addGShellListener(GShellListener listener) { 110 listeners.add(GShellListener.class, listener); 111 } 112 /** This method removes a certain shell listener from this process. 113 113 * @param listener The <i>GShellListener</i> to be removed. 114 114 */ 115 116 117 118 115 public void removeGShellListener(GShellListener listener) { 116 listeners.remove(GShellListener.class, listener); 117 } 118 /** Any threaded class must include this method to allow the thread body to be run. 119 119 */ 120 120 public void run() { 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 121 // Setup 122 if(progress != null) { 123 progress.start(); 124 } 125 // Determine if the user has asked for an outfile. 126 String out_name = null; 127 FileOutputStream fos = null; 128 BufferedOutputStream bos = null; 129 if(type == IMPORT || type == BUILD) { 130 if(type == IMPORT) { 131 out_name = (String) Gatherer.c_man.getCollection().build_options.getImportValue("out"); 132 } 133 else { 134 out_name = (String) Gatherer.c_man.getCollection().build_options.getBuildValue("out"); 135 } 136 if(out_name != null && out_name.length() > 0) { 137 File out = new File(out_name); 138 try { 139 if(out.exists()) { 140 boolean append = true; 141 fos = new FileOutputStream(out, append); 142 bos = new BufferedOutputStream(fos); 143 } 144 } 145 catch (Exception error) { 146 error.printStackTrace(); 147 } 148 } 149 } 150 // Issue a processBegun event 151 fireProcessBegun(type, status); 152 try { 153 String command = ""; 154 for(int i = 0; i < args.length; i++) { 155 command = command + args[i] + " "; 156 } 157 157 ///ystem.err.println("Command: " + command); 158 159 158 fireMessage(type, get("Command") + ": " + command, status); 159 message(Message.EVENT, get("Command") + ": " + command); 160 160 161 162 163 164 165 166 161 Runtime rt = Runtime.getRuntime(); 162 Process prcs = rt.exec(args); 163 InputStreamReader eisr = new InputStreamReader( prcs.getErrorStream() ); 164 InputStreamReader stdinisr = new InputStreamReader( prcs.getInputStream() ); 165 BufferedReader ebr = new BufferedReader( eisr ); 166 BufferedReader stdinbr = new BufferedReader( stdinisr ); 167 167 // Captures the std err of a program and pipes it into 168 168 // std in of java 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 169 String eline = null; 170 String stdinline = null; 171 while (((eline = ebr.readLine()) != null || (stdinline = stdinbr.readLine()) != null) && !hasSignalledStop()) { 172 if(eline != null) { 173 if(progress != null) { 174 progress.parse(eline); 175 } 176 if(bos != null) { 177 bos.write(eline.getBytes(), 0, eline.length()); 178 } 179 ///ystem.err.println("stderr: " + eline); 180 fireMessage(type, typeAsString(type) + "> " + eline, 181 status); 182 message(Message.INFO, eline); 183 } 184 if(stdinline != null) { 185 if(bos != null) { 186 //bos.write(stdinline.getBytes(), 0, stdinline.length()); 187 } 188 ///ystem.err.println("stdin: " + stdinline); 189 fireMessage(type, typeAsString(type) + "> " + stdinline, 190 status); 191 message(Message.INFO, stdinline); 192 } 193 } 194 194 195 196 197 195 if(!hasSignalledStop()) { 196 // Now display final message based on exit value 197 prcs.waitFor(); 198 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 199 if(prcs.exitValue() == 0) { 200 status = OK; 201 fireMessage(type, typeAsString(type) + "> " + get("Success"), status); 202 message(Message.MAIN, get("Success")); 203 } else { 204 status = ERROR; 205 fireMessage(type, typeAsString(type) + "> " + get("Failure"), status); 206 message(Message.ERROR, get("Failure")); 207 } 208 } 209 else { 210 // I need to somehow kill the child process. Unfortunately Thread.stop() and Process.destroy() both fail to do this. But now, thankx to the magic of Michaels 'close the stream suggestion', it works fine (no it doesn't!) 211 prcs.getOutputStream().close(); 212 prcs.destroy(); 213 status = ERROR; 214 } 215 } 216 // Exception 217 catch (Exception error) { 218 message(Message.ERROR, get("Failure")); 219 message(Message.ERROR, error.toString()); 220 Gatherer.printStackTrace(error); 221 status = ERROR; 222 } 223 // If no error occured, and this was an import process we now extract any new metadata from the archive directory. 224 if(status == OK && type == IMPORT) { 225 fireMessage(type, typeAsString(type) + "> " + get("Parsing_Metadata_Start"), status); 226 new GreenstoneArchiveParser(progress, this); 227 fireMessage(type, typeAsString(type) + "> " + get("Parsing_Metadata_Complete"), status); 228 } 229 // Tidy up. 230 if(progress != null) { 231 progress.stop(); 232 } 233 if(bos != null) { 234 try { 235 bos.close(); 236 } 237 catch (Exception e) { 238 } 239 } 240 // We're done. 241 fireProcessComplete(type, status); 242 } 243 /** Method for firing a message to all interested listeners. 244 244 * @param type An <strong>int</strong> indicating the process type. 245 245 * @param message The message as a <strong>String</strong>. 246 246 * @param status An <strong>int</strong> specifying the current status of the process. 247 247 */ 248 249 250 251 252 253 254 255 256 257 248 public void fireMessage(int type, String message, int status) { 249 GShellEvent event = new GShellEvent(this, 0, type, message, status); 250 Object[] concerned = listeners.getListenerList(); 251 for(int i = 0; i < concerned.length ; i++) { 252 if(concerned[i] == GShellListener.class) { 253 ((GShellListener)concerned[i+1]).message(event); 254 } 255 } 256 } 257 /** Method for firing a process begun event which is called, strangly enough, when the process begins. 258 258 * @param type An <strong>int</strong> indicating the process type. 259 259 * @param status An <strong>int</strong> specifying the current status of the process. 260 260 */ 261 262 263 264 265 266 267 268 269 270 261 protected void fireProcessBegun(int type, int status) { 262 GShellEvent event = new GShellEvent(this, 0, type, "", status); 263 Object[] concerned = listeners.getListenerList(); 264 for(int i = 0; i < concerned.length ; i++) { 265 if(concerned[i] == GShellListener.class) { 266 ((GShellListener)concerned[i+1]).processBegun(event); 267 } 268 } 269 } 270 /** Method for firing a process complete event which is called, no surprise here, when the process ends. 271 271 * @param type An <strong>int</strong> indicating the process type. 272 272 * @param status An <strong>int</strong> specifying the current status of the process. 273 273 */ 274 275 276 277 278 279 280 281 282 283 274 protected void fireProcessComplete(int type, int status) { 275 GShellEvent event = new GShellEvent(this, 0, type, "", status); 276 Object[] concerned = listeners.getListenerList(); 277 for(int i = 0; i < concerned.length ; i++) { 278 if(concerned[i] == GShellListener.class) { 279 ((GShellListener)concerned[i+1]).processComplete(event); 280 } 281 } 282 } 283 /** Retrieve a phrase from the dictionary based on the given key. 284 284 * @param key A <strong>String</strong> used to match against a phrase from the <strong>Dictionary</strong>. 285 285 * @return The phrase as a <strong>String</strong>. 286 286 */ 287 288 289 290 291 292 293 287 private String get(String key) { 288 if(key.indexOf('.') == -1) { 289 key = "GShell." + key; 290 } 291 return Gatherer.dictionary.get(key); 292 } 293 /** Method to determine if the user, via the progress monitor, has signalled stop. 294 294 * @return A <strong>boolean</strong> indicating if the user wanted to stop. 295 295 */ 296 297 298 299 300 301 302 303 296 private boolean hasSignalledStop() { 297 boolean has_signalled_stop = false; 298 if(progress != null) { 299 return progress.hasSignalledStop(); 300 } 301 return has_signalled_stop; 302 } 303 /** Creates and dispatches a message given the initial details. 304 304 * @param level An <strong>int</strong> indicating the message level for this message. 305 305 * @param message A <strong>String</strong> which contains the payload of this message. 306 306 */ 307 308 309 310 311 312 313 307 private void message(int level, String message) { 308 Message msg = new Message(msg_type, level, message); 309 if(Gatherer.g_man != null) { 310 Gatherer.log.add(msg); 311 } 312 } 313 /** Converts a type into a text representation. 314 314 * @param type An <strong>int</strong> which maps to a shell process type. 315 315 * @return A <strong>String</strong> which is the thread process's text name. 316 316 */ 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 317 public String typeAsString(int type) { 318 String name = null; 319 switch(type) { 320 case BUILD: 321 name = get("Build"); 322 break; 323 case IMPORT: 324 name = get("Import"); 325 break; 326 case NEW: 327 name = get("New"); 328 break; 329 default: 330 name = get("Other"); 331 } 332 return name; 333 } 334 334 } 335 336 -
trunk/gli/src/org/greenstone/gatherer/shell/GShellEvent.java
r4293 r4364 54 54 /** This class encapsulates all the information created by an event within a <strong>GShell</strong> process. */ 55 55 public class GShellEvent 56 57 58 59 60 61 62 63 64 65 66 67 68 56 extends AWTEvent { 57 /** The status of the process at the completion of event. */ 58 private int status = -1; 59 /** The process type (such as COPY, BUILD or IMPORT). */ 60 private int type = -1; 61 /** Any message associated with this event. */ 62 private String message = null; 63 /* Constructor. 64 * @param source The <strong>GShell</strong> that fired this message. 65 * @param id The event identifier as an <strong>int</strong>. 66 * @param type The process type as an <strong>int</strong>. 67 * @param message A <strong>String</strong> representing any message attatched with this event. 68 * @param status The status of the process post event, as an <strong>int</strong>. 69 69 */ 70 71 72 73 74 75 76 77 70 public GShellEvent(Object source, int id, int type, String message, 71 int status) { 72 super(source, id); 73 this.message = message; 74 this.status = status; 75 this.type = type; 76 } 77 /** Gets the message associated with this event. 78 78 * @return The message as a <strong>String</strong> or <i>null</i>. 79 79 */ 80 81 82 83 80 public String getMessage() { 81 return message; 82 } 83 /** Gets the status associated with this event. This status can then be matched back to the constants in <strong>GShell</strong>. 84 84 * @return An <strong>int</strong> signifying the process status. 85 85 */ 86 87 88 89 86 public int getStatus() { 87 return status; 88 } 89 /** Gets the type associated with this event. This type can then be matched back to the constants in <strong>GShell</strong>. 90 90 * @return An <strong>int</strong> signifying the process type. 91 91 */ 92 93 94 92 public int getType() { 93 return type; 94 } 95 95 96 97 98 96 public String toString() { 97 return "org.greenstone.gatherer.shell.GShellEvent[" + message + "," + status + "," + type + "]"; 98 } 99 99 } -
trunk/gli/src/org/greenstone/gatherer/shell/GShellListener.java
r4293 r4364 57 57 */ 58 58 public interface GShellListener 59 60 61 59 extends EventListener { 60 /** All implementation of <i>GShellListener</i> must include this method so the listener can be informed of messages from the <strong>GShell</strong>. 61 * @param event A <strong>GShellEvent</strong> that contains, amoung other things, the message. 62 62 */ 63 64 63 public void message(GShellEvent event); 64 /** All implementation of <i>GShellListener</i> must include this method so the listener can be informed when a <strong>GShell</strong> begins its task. 65 65 * @param event A <strong>GShellEvent</strong> that contains details of the initial state of the <strong>GShell</strong> before task comencement. 66 66 */ 67 68 67 public void processBegun(GShellEvent event); 68 /** All implementation of <i>GShellListener</i> must include this method so the listener can be informed when a <strong>GShell</strong> completes its task. 69 69 * @param event A <strong>GShellEvent</strong> that contains details of the final state of the <strong>GShell</strong> after task completion. 70 70 */ 71 71 public void processComplete(GShellEvent event); 72 72 } -
trunk/gli/src/org/greenstone/gatherer/shell/GShellProgressMonitor.java
r4293 r4364 58 58 */ 59 59 public interface GShellProgressMonitor { 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 60 /** Method to register a new progress bar with this monitor. 61 * @param progress_bar The new <strong>JProgressBar</strong>. 62 */ 63 public void addProgressBar(JProgressBar progress_bar); 64 /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value. 65 * @return A <i>int</i> with a value of zero if and only if the script was successful. 66 */ 67 public int exitValue(); 68 /** Method to retrieve whatever control is being used as the progress indicator. Usually a <strong>JProgressBar</strong> but there may be others implemented later. 69 * @return A <strong>Component</strong> on which the progress of the process is being displayed. 70 */ 71 public Component getProgress(); 72 /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor. 73 * @return A <strong>boolean</strong> indicating if the process has been asked to stop. 74 */ 75 public boolean hasSignalledStop(); 76 /** Inform the progress bar that it should programatically increment progress by one step. */ 77 public void increment(); 78 /** This method is used to 'feed in' a line of text captured from the process. 79 * @param line A <strong>String</strong> of text captured from either standard out or standard error. 80 */ 81 public void parse(String line); 82 /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two. 83 * @param state The desired state of the stop flag as a <strong>boolean</strong>. 84 */ 85 public void setStop(boolean state); 86 /** This method resets this monitor to the start, reseting the process parsing and progress bar. 87 */ 88 public void start(); 89 /** This method indicates the process is complete. 90 */ 91 public void stop(); 92 92 } 93 -
trunk/gli/src/org/greenstone/gatherer/undo/UndoManager.java
r4293 r4364 63 63 */ 64 64 public class UndoManager 65 66 67 68 69 70 71 72 73 /** In order to make this button a drop target we have to create a DropTarget instance with the button as its target. */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 65 extends JButton 66 implements ActionListener, DragComponent, DropTargetListener { 67 private ArrayList redo_sources; 68 private ArrayList undo_sources; 69 private boolean ignore = false; 70 private boolean ignore_next = false; 71 /** The group encompasses all of the objects you plan to drag and drop within, and ensures that only one has focus (as clearly identified by the colour of the selection field or, in this particular case, the background) and that actions only occur between components in the same group. */ 72 private DragGroup group; 73 /** In order to make this button a drop target we have to create a DropTarget instance with the button as its target. */ 74 private DropTarget drop_target; 75 private FileQueue file_queue; 76 private FileSystemModel model; 77 private GDMDocument obsolete_metadata; 78 /** What sort of action should a drag resemble. Not really used as we override with custom drag icon. */ 79 private int drag_action = DnDConstants.ACTION_MOVE; 80 /** The last point the mouse was at. Used to repaint 'spoilt' area. */ 81 private Point pt_last = null; 82 /** The area covered by the drag ghost, our custom drag icon. */ 83 private Rectangle ra_ghost = new Rectangle(); 84 private UndoStack redo; 85 private UndoStack undo; 86 static final public int FILE_COPY = 1; 87 static final public int FILE_DELETE = 2; 88 static final public int FILE_MOVE = 3; 89 90 public UndoManager() { 91 super(Utility.getImage("bin.gif")); 92 this.file_queue = Gatherer.f_man.getQueue(); 93 this.drop_target = new DropTarget(this, drag_action, this, true); 94 95 setBackground(Gatherer.config.getColor("coloring.button_background", true)); 96 setForeground(Gatherer.config.getColor("coloring.button_foreground", true)); 97 setOpaque(true); 98 99 // Creation 100 File recycle_directory = new File(Utility.RECYCLE); 101 if(!recycle_directory.exists()) { 102 recycle_directory.mkdirs(); 103 recycle_directory.deleteOnExit(); 104 } 105 this.model = new FileSystemModel(new FileNode(recycle_directory, "Undo")); 106 obsolete_metadata = new GDMDocument(); // This GDM is never saved. 107 redo = new UndoStack(false); 108 redo_sources = new ArrayList(); 109 undo = new UndoStack(true); 110 undo_sources = new ArrayList(); 111 if(Gatherer.debug != null) { 112 showTree(); 113 } 114 } 115 116 public void actionPerformed(ActionEvent event) { 117 // Is this an undo event source... 118 if(undo_sources.contains(event.getSource())) { 119 UndoJob undo_job = undo.pop(); 120 undo_job.action(true, file_queue); 121 121 // Now retrieve all other undo jobs with the same job-number and action them 122 123 124 125 126 127 128 129 122 while((undo_job = undo.getJob(undo_job.ID())) != null) { 123 undo_job.action(true, file_queue); 124 } 125 } 126 // Or a redo one. 127 else if(redo_sources.contains(event.getSource())) { 128 UndoJob redo_job = redo.pop(); 129 redo_job.action(false, file_queue); 130 130 // Now retrieve all other redo jobs with the same job-number and action them 131 132 133 134 135 136 137 138 139 131 while((redo_job = redo.getJob(redo_job.ID())) != null) { 132 redo_job.action(false, file_queue); 133 } 134 } 135 } 136 137 public void addMetadata(File file, ArrayList metadatum) { 138 for(int i = 0; metadatum != null && i < metadatum.size(); i++) { 139 Metadata metadata = (Metadata) metadatum.get(i); 140 140 ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath() + " => " + metadata); 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 141 obsolete_metadata.addMetadata(file.getAbsolutePath(), metadata); 142 } 143 } 144 145 public void addUndo(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) { 146 if(target_model == null) { 147 target_model = this; 148 } 149 UndoJobAdder job_adder = new UndoJobAdder(id, type, source_model, source_parent, target_model, record, undo_event); 150 SwingUtilities.invokeLater(job_adder); 151 } 152 153 public void clear() { 154 undo.clear(); 155 redo.clear(); 156 } 157 158 /** 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 repair the 'spoilt' region left by its ghost. */ 159 public void clearGhost(){ 160 } 161 162 public void destroy() { 163 // Remove all references of this as a listener 164 for(int i = 0; i < redo_sources.size(); i++) { 165 AbstractButton source = (AbstractButton) redo_sources.get(i); 166 source.removeActionListener(this); 167 } 168 for(int j = 0; j < undo_sources.size(); j++) { 169 AbstractButton source = (AbstractButton) undo_sources.get(j); 170 source.removeActionListener(this); 171 } 172 redo_sources = null; 173 undo_sources = null; 174 redo = null; 175 undo = null; 176 obsolete_metadata = null; 177 file_queue = null; 178 } 179 180 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus enters this component. We want to provide some sort of indication whether the current component is an acceptable drop target as well as indicating focus. */ 181 public void dragEnter(DropTargetDragEvent event) { 182 //ystem.err.println("Drag entered"); 183 group.grabFocus(this); 184 setBackground(Gatherer.config.getColor("coloring.button_selected_background", true)); 185 setForeground(Gatherer.config.getColor("coloring.button_selected_foreground", true)); 186 } 187 188 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus leaves this component. We need to indicate that we have lost focus. */ 189 public void dragExit(DropTargetEvent event) { 190 //ystem.err.println("Drag exitted"); 191 setBackground(Gatherer.config.getColor("coloring.button_background", true)); 192 setForeground(Gatherer.config.getColor("coloring.button_foreground", true)); 193 } 194 195 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag moves in this component. This is where we repaint our ghost icon at the tip of the mouse pointer. */ 196 public void dragOver(DropTargetDragEvent event) { 197 Graphics2D g2 = (Graphics2D) getGraphics(); 198 Point pt = event.getLocation(); 199 if(pt_last != null && pt.equals(pt_last)) { 200 return; 201 } 202 pt_last = pt; 203 if(!DragSource.isDragImageSupported()) { 204 204 // Erase the last ghost image and or cue line 205 205 paintImmediately(ra_ghost.getBounds()); 206 206 // Remember where you are about to draw the new ghost image 207 207 ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight()); 208 208 // Draw the ghost image 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 209 g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null); 210 } 211 } 212 213 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag ends, ie the transferable is dropped. This in turn triggers a series of add events preceded by a pre() and followed by a post(). */ 214 public void drop(DropTargetDropEvent event) { 215 ignore = true; 216 group.grabFocus(this); 217 setBackground(Gatherer.config.getColor("coloring.button_background", true)); 218 setForeground(Gatherer.config.getColor("coloring.button_foreground", true)); 219 Transferable transferable = event.getTransferable(); 220 try { 221 DragComponent source = group.getSource(); 222 TreePath[] selection = group.getSelection(); 223 FileNode[] source_nodes = new FileNode[selection.length]; 224 for(int i = 0; i < source_nodes.length; i++) { 225 source_nodes[i] = (FileNode) selection[i].getLastPathComponent(); 226 } 227 227 ///ystem.err.println("Dropped files vector contains " + new_files.size() + " files."); 228 228 event.acceptDrop(drag_action); 229 229 // Action delete 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 230 Gatherer.f_man.action(source, source_nodes, this, null); 231 group.setSource(null); 232 group.setSelection(null); 233 } 234 catch(Exception error) { 235 error.printStackTrace(); 236 event.rejectDrop(); 237 } 238 ignore = false; 239 // Clear up the group.image_ghost 240 paintImmediately(ra_ghost.getBounds()); 241 event.getDropTargetContext().dropComplete(true); 242 } 243 244 /** Any implementation of DropTargetListener must include this method so we can be notified when the action to be taken upon drop changes. We never change so we don't do anything here. */ 245 public void dropActionChanged(DropTargetDragEvent event) { 246 } 247 248 public void fileCopied(long id, DragComponent target_model, FileNode target_parent, FileNode record, boolean undo_event) { 249 ///ystem.err.println("fileCopied(" + id + ", " + target_model + ", " + target_parent + ", " + record + ", " + undo_event + ")"); 250 UndoJob job = new UndoJob(id, null, null, target_model, record, FILE_COPY); 251 if(undo_event) { 252 252 ///ystem.err.println("Add undo job"); 253 254 255 253 undo.push(job); 254 } 255 else { 256 256 ///ystem.err.println("Add redo job"); 257 258 259 260 261 262 263 257 redo.push(job); 258 } 259 } 260 261 public void fileDeleted(long id, DragComponent source_model, FileNode source_parent, FileNode target_parent, FileNode record, boolean undo_event) { 262 UndoJob job = new UndoJob(id, source_model, source_parent, this, record, FILE_DELETE); 263 if(undo_event) { 264 264 ///ystem.err.println("Add undo job"); 265 266 267 265 undo.push(job); 266 } 267 else { 268 268 ///ystem.err.println("Add redo job"); 269 270 271 272 273 274 275 276 269 redo.push(job); 270 } 271 } 272 273 public void fileMoved(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode target_parent, FileNode record, boolean undo_event) { 274 ///ystem.err.println("fileMoved(" + id + ", " + source_model + ", " + source_parent + ", " + target_model + ", " + target_parent + ", " + record + ", " + undo_event + ")"); 275 UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, FILE_MOVE); 276 if(undo_event) { 277 277 ///ystem.err.println("Add undo job"); 278 279 280 278 undo.push(job); 279 } 280 else { 281 281 ///ystem.err.println("Add redo job"); 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 282 redo.push(job); 283 } 284 } 285 286 /** Used to notify this component that it has gained focus by some method other that mouse focus. */ 287 public void gainFocus(){ 288 } 289 290 public ArrayList getMetadata(File file) { 291 ///ystem.err.println("UndoMetadata: " + file.getAbsolutePath()); 292 return obsolete_metadata.getMetadata(file.getAbsolutePath(), true, new ArrayList(), file); 293 } 294 295 /** Any implementation of DragComponent must include this method so that a outsider can get at the underlying tree model behind the component. */ 296 public FileSystemModel getTreeModel(){ 297 return (FileSystemModel) model; 298 } 299 300 public boolean ignore() { 301 return ignore; 302 } 303 304 /** This method is used to inform this component when it loses focus by means other than a drag mouse event, and should indicate this somehow. */ 305 public void loseFocus(){ 306 } 307 308 public void registerRedoSource(AbstractButton source) { 309 if(!redo_sources.contains(source)) { 310 redo_sources.add(source); 311 source.addActionListener(this); 312 } 313 } 314 315 public void registerUndoSource(AbstractButton source) { 316 if(!undo_sources.contains(source)) { 317 undo_sources.add(source); 318 source.addActionListener(this); 319 } 320 } 321 322 public void setGroup(DragGroup group) { 323 this.group = group; 324 } 325 326 public void undoAll() { 327 FileQueue immediate_queue = new FileQueue(true); 328 UndoJob undo_job = null; 329 while((undo_job = undo.pop()) != null) { 330 undo_job.action(true, immediate_queue); 331 331 // Now retrieve all other undo jobs with the same job-number and action them 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 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 332 while((undo_job = undo.getJob(undo_job.ID())) != null) { 333 undo_job.action(true, immediate_queue); 334 } 335 } 336 immediate_queue.run(); 337 // Returns only when all undo actions complete. 338 } 339 340 static final public File generateUniqueFile(FileNode record) { 341 String filename_raw = ArrayTools.objectArrayToString(record.getPath()); 342 int hash_code = filename_raw.hashCode(); 343 File file = new File(Utility.RECYCLE, String.valueOf(hash_code)); 344 int offset = 65; 345 while(file.exists() && offset != 90) { 346 file = new File(Utility.RECYCLE, String.valueOf(hash_code) + (char)offset); 347 offset++; 348 } 349 return file; 350 } 351 352 private void showTree() { 353 JDialog dialog = new JDialog(Gatherer.g_man, "Recycle Bin Model"); 354 dialog.setSize(new Dimension(400,300)); 355 JPanel content_pane = (JPanel) dialog.getContentPane(); 356 JTree tree = new JTree(model); 357 content_pane.setLayout(new BorderLayout()); 358 content_pane.add(new JScrollPane(tree), BorderLayout.CENTER); 359 dialog.show(); 360 } 361 362 private class UndoJob { 363 private FileNode record = null; 364 private FileNode source_parent = null; 365 private DragComponent source_model = null; 366 private DragComponent target_model = null; 367 private long id = 0; 368 private int type = -1; 369 private TreePath record_path = null; 370 private TreePath source_parent_path = null; 371 /** Undo file action Constructor. 372 * @param id A unique <i>long</i> id number for all actions associated with a certain gesture. 373 * @param source_model The source <strong>DragComponent</strong> where the new record originally came from. If this is <i>null</i> then it is assumed you are interested in using the 'recycle' bin model. 374 * @param source_parent The previous parent <strong>FileNode</strong> of the new record. 375 * @param target_model The target <strong>DragComponent</strong> where the new record is now. 376 * @param record The new <strong>FileNode</strong> itself. 377 * @param type An <i>int</i> indicating the action that has occured, not what undo action is needed. 378 */ 379 public UndoJob(long id, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, int type) { 380 this.id = id; 381 this.record = record; 382 if(record != null) { 383 this.record_path = new TreePath(record.getPath()); 384 } 385 this.source_model = source_model; 386 this.source_parent = source_parent; 387 if(source_parent != null) { 388 this.source_parent_path = new TreePath(source_parent.getPath()); 389 } 390 this.target_model = target_model; 391 this.type = type; 392 } 393 393 394 394 public void action(boolean is_undo, FileQueue file_queue) { 395 395 // Retrieve the lastest version of each file record 396 397 398 399 400 401 402 403 404 405 396 FileNode latest_record = null; 397 FileNode latest_source_parent = null; 398 if(target_model != null && record_path != null) { 399 ///ystem.err.println("Retrieving latest version of record from " + target_model + "."); 400 latest_record = ((FileSystemModel)target_model.getTreeModel()).getNode(record_path); 401 } 402 if(source_model != null && source_parent_path != null) { 403 ///ystem.err.println("Retrieving latest version of source parent."); 404 latest_source_parent = ((FileSystemModel)source_model.getTreeModel()).getNode(source_parent_path); 405 } 406 406 // Of course if there are no newer versions, stick to the ones we've already got. 407 408 409 410 411 412 413 414 407 if(latest_record == null) { 408 ///ystem.err.println("Using original record."); 409 latest_record = record; 410 } 411 if(latest_source_parent == null) { 412 ///ystem.err.println("Using original source parent."); 413 latest_source_parent = source_parent; 414 } 415 415 // Heres the fraction, too much friction. 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 416 switch(type) { 417 case FILE_COPY: 418 // To undo a file copy we issue a delete file action on the destination file record. 419 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false); 420 break; 421 case FILE_DELETE: 422 // To undo a file delete we issue a copy file action from our recycle bin. 423 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false); 424 break; 425 case FILE_MOVE: 426 // This may be a legitimate move, or may be a side effect of an undelete. If the formed source model and parent will be non-null. 427 if(source_model != null && source_parent != null) { 428 // To undo a file move we issue a move file action to return it to where it was. 429 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.MOVE, !is_undo, true, false); 430 } 431 // Otherwise we perform another delete. 432 else { 433 file_queue.addJob(id, target_model, latest_record, source_model, latest_source_parent, FileJob.DELETE, !is_undo, true, false); 434 } 435 break; 436 default: 437 System.err.println("Unknown code."); 438 } 439 } 440 441 public FileNode getRecord() { 442 return record; 443 } 444 445 public long ID() { 446 return id; 447 } 448 } 449 450 private class UndoJobAdder 451 implements Runnable { 452 private boolean undo_event; 453 private FileNode record; 454 private FileNode source_parent; 455 private DragComponent source_model; 456 private DragComponent target_model; 457 private int type; 458 private long id; 459 public UndoJobAdder(long id, int type, DragComponent source_model, FileNode source_parent, DragComponent target_model, FileNode record, boolean undo_event) { 460 this.id = id; 461 this.record = record; 462 this.source_model = source_model; 463 this.source_parent = source_parent; 464 this.target_model = target_model; 465 this.type = type; 466 this.undo_event = undo_event; 467 } 468 public void run() { 469 UndoJob job = new UndoJob(id, source_model, source_parent, target_model, record, type); 470 if(undo_event) { 471 undo.push(job); 472 } 473 else { 474 redo.push(job); 475 } 476 } 477 } 478 479 private class UndoStack 480 extends LinkedList { 481 private boolean enabled = false; 482 private boolean undo; 483 private int pos = 0; 484 public UndoStack(boolean undo) { 485 this.undo = undo; 486 } 487 public void clear() { 488 super.clear(); 489 pos = 0; 490 if(enabled) { 491 setEnabled(false); 492 } 493 } 494 public UndoJob getJob(long id) { 495 UndoJob job = null; 496 while(job == null && pos < size()) { 497 UndoJob temp = (UndoJob) get(pos); 498 if(temp.ID() == id) { 499 job = temp; 500 remove(temp); 501 } 502 else { 503 pos++; 504 } 505 } 506 if(size() == 0) { 507 setEnabled(false); 508 pos = 0; 509 } 510 return job; 511 } 512 public void push(UndoJob job) { 513 addFirst(job); 514 pos = 0; 515 if(!enabled) { 516 setEnabled(true); 517 } 518 } 519 public UndoJob pop() { 520 UndoJob job = null; 521 if(size() > 0) { 522 job = (UndoJob) removeFirst(); 523 if(size() == 0 && enabled) { 524 setEnabled(false); 525 pos = 0; 526 } 527 } 528 return job; 529 } 530 public void reset() { 531 pos = 0; 532 } 533 private void setEnabled(boolean state) { 534 ArrayList sources; 535 if(undo) { 536 sources = undo_sources; 537 } 538 else { 539 sources = redo_sources; 540 } 541 for(int i = 0; i < sources.size(); i++) { 542 AbstractButton source = (AbstractButton) sources.get(i); 543 source.setEnabled(state); 544 } 545 enabled = state; 546 } 547 } 548 548 } -
trunk/gli/src/org/greenstone/gatherer/util/ArrayTools.java
r4293 r4364 21 21 public class ArrayTools { 22 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 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 23 /** Append a Component onto an array of Components. */ 24 static public Component[] add(Component[] a, Component b) { 25 Component[] c = null; 26 if(a != null && b != null) { 27 c = new Component[a.length + 1]; 28 System.arraycopy(a, 0, c, 0, a.length); 29 c[c.length - 1] = b; 30 } 31 else if(a == null && b != null) { 32 c = new Component[1]; 33 c[0] = b; 34 } 35 else if(a != null && b == null) { 36 c = a; 37 } 38 return c; 39 } 40 /** This method efficiently appends a new element onto the end of a element array. 41 * @param a The initial <strong>Element[]</strong>. 42 * @param b The new <strong>Element</strong>. 43 * @return An <strong>Element[]</strong> containing a followed by b. 44 */ 45 static public Element[] add(Element a[], Element b) { 46 Element[] c = null; 47 if(a != null && b != null) { 48 c = new Element[a.length + 1]; 49 System.arraycopy(a, 0, c, 0, a.length); 50 c[c.length - 1] = b; 51 } 52 else if(a == null && b != null) { 53 c = new Element[1]; 54 c[0] = b; 55 } 56 else if(a != null && b == null) { 57 c = a; 58 } 59 return c; 60 } 61 /** This method efficently appends one element array onto the end of another. 62 * @param a The initial <strong>Element[]</strong>. 63 * @param b The <strong>Element[]</strong> to append. 64 * @return An <strong>Element[]</strong> containing a followed by b. 65 */ 66 static public Element[] add(Element a[], Element b[]) { 67 Element[] c = null; 68 if(a != null && b != null) { 69 c = new Element[a.length + b.length]; 70 System.arraycopy(a, 0, c, 0, a.length); 71 System.arraycopy(b, 0, c, a.length, b.length); 72 } 73 else if(a == null && b != null) { 74 c = b; 75 } 76 else if(a != null && b == null) { 77 c = a; 78 } 79 return c; 80 } 81 /** This method efficiently appends a new file onto the end of a file array. 82 * @param a The initial <strong>File[]</strong>. 83 * @param b The new <strong>File</strong>. 84 * @return A <strong>File[]</strong> containing a followed by b. 85 */ 86 static public File[] add(File a[], File b) { 87 File[] c = null; 88 if(a != null && b != null) { 89 c = new File[a.length + 1]; 90 System.arraycopy(a, 0, c, 0, a.length); 91 c[c.length - 1] = b; 92 } 93 else if(a == null && b != null) { 94 c = new File[1]; 95 c[0] = b; 96 } 97 else if(a != null && b == null) { 98 c = a; 99 } 100 return c; 101 } 102 /** This method efficently appends one file array onto the end of another. 103 * @param a The initial <strong>File[]</strong>. 104 * @param b The <strong>File[]</strong> to append. 105 * @return A <strong>File[]</strong> containing a followed by b. 106 */ 107 static public File[] add(File a[], File b[]) { 108 File[] c = null; 109 if(a != null && b != null) { 110 c = new File[a.length + b.length]; 111 System.arraycopy(a, 0, c, 0, a.length); 112 System.arraycopy(b, 0, c, a.length, b.length); 113 } 114 else if(a == null && b != null) { 115 c = b; 116 } 117 else if(a != null && b == null) { 118 c = a; 119 } 120 return c; 121 } 122 123 /** This method efficiently appends a new FileNode onto the end of a FileNode array. 124 * @param a The initial <strong>FileNode[]</strong>. 125 * @param b The new <strong>FileNode</strong>. 126 * @return An <strong>FileNode[]</strong> containing a followed by b. 127 */ 128 static public FileNode[] add(FileNode a[], FileNode b) { 129 FileNode[] c = null; 130 if(a != null && b != null) { 131 c = new FileNode[a.length + 1]; 132 System.arraycopy(a, 0, c, 0, a.length); 133 c[c.length - 1] = b; 134 } 135 else if(a == null && b != null) { 136 c = new FileNode[1]; 137 c[0] = b; 138 } 139 else if(a != null && b == null) { 140 c = a; 141 } 142 return c; 143 } 144 /** This method efficently appends one FileNode array onto the end of another FileNode array. 145 * @param a The initial <strong>FileNode[]</strong>. 146 * @param b The <strong>FileNode[]</strong> to append. 147 * @return An <strong>FileNode[]</strong> containing a followed by b. 148 */ 149 static final public FileNode[] add(FileNode a[], FileNode b[]) { 150 FileNode[] c = null; 151 if(a != null && b != null) { 152 c = new FileNode[a.length + b.length]; 153 System.arraycopy(a, 0, c, 0, a.length); 154 System.arraycopy(b, 0, c, a.length, b.length); 155 } 156 else if(a == null && b != null) { 157 c = b; 158 } 159 else if(a != null && b == null) { 160 c = a; 161 } 162 return c; 163 } 164 /** Determine if a certain FileNode is present in an array of FileNodes. 165 * @param array The <strong>FileNode[]</strong>. 166 * @param a The <strong>FileNode</strong> we are searching for. 167 * @return <i>true</i> if the FileNode is in the array, <i>false</i> otherwise. 168 */ 169 static final public boolean contains(FileNode array[], FileNode a) { 170 for(int i = 0; i < array.length; i++) { 171 if(array[i].equals(a)) { 172 return true; 173 } 174 } 175 return false; 176 } 177 /** Remove the first element from an array, and return the remaining 'tail'. 178 * @param a A <strong>FileNode[]</strong>. 179 * @return The same <strong>FileNode[]</strong> sans its head element. 180 */ 181 static final public FileNode[] tail(FileNode a[]) { 182 if(a.length == 1) { 183 return null; 184 } 185 FileNode b[] = new FileNode[a.length - 1]; 186 System.arraycopy(a, 1, b, 0, b.length); 187 return b; 188 } 189 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 190 /** Append a Metadata object onto an array of Metadata objects. */ 191 static public Metadata[] add(Metadata[] a, Metadata b) { 192 Metadata[] c = null; 193 if(a != null && b != null) { 194 c = new Metadata[a.length + 1]; 195 System.arraycopy(a, 0, c, 0, a.length); 196 c[c.length - 1] = b; 197 } 198 else if(a == null && b != null) { 199 c = new Metadata[1]; 200 c[0] = b; 201 } 202 else if(a != null && b == null) { 203 c = a; 204 } 205 return c; 206 } 207 /** Append a Metadata object array onto an array of Metadata objects. */ 208 static public Metadata[] add(Metadata[] a, Metadata[] b) { 209 Metadata[] c = null; 210 if(a != null && b != null) { 211 c = new Metadata[a.length + b.length]; 212 System.arraycopy(a, 0, c, 0, a.length); 213 System.arraycopy(b, 0, c, a.length, b.length); 214 } 215 else if(a == null && b != null) { 216 c = b; 217 } 218 else if(a != null && b == null) { 219 c = a; 220 } 221 return c; 222 } 223 /** Remove the Metadata object at the given index form the specified Metadata object array. */ 224 static public Metadata[] remove(Metadata[] a, int index) { 225 Metadata[] c = null; 226 if(a != null) { 227 c = new Metadata[a.length - 1]; 228 System.arraycopy(a, 0, c, 0, index); 229 System.arraycopy(a, index + 1, c, index, c.length - index); 230 } 231 return c; 232 } 233 234 /** This method efficiently appends a new node onto the end of a node array. 235 * @param a The initial <strong>Node[]</strong>. 236 * @param b The new <strong>Node</strong>. 237 * @return A <strong>Node[]</strong> containing a followed by b. 238 */ 239 static public Node[] add(Node a[], Node b) { 240 Node[] c = null; 241 if(a != null && b != null) { 242 c = new Node[a.length + 1]; 243 System.arraycopy(a, 0, c, 0, a.length); 244 c[c.length - 1] = b; 245 } 246 else if(a == null && b != null) { 247 c = new Node[1]; 248 c[0] = b; 249 } 250 else if(a != null && b == null) { 251 c = a; 252 } 253 return c; 254 } 255 /** This method efficently appends one node array onto the end of another. 256 * @param a The initial <strong>Node[]</strong>. 257 * @param b The <strong>Node[]</strong> to append. 258 * @return A <strong>Node[]</strong> containing a followed by b. 259 */ 260 static public Node[] add(Node a[], Node b[]) { 261 Node[] c = null; 262 if(a != null && b != null) { 263 c = new Node[a.length + b.length]; 264 System.arraycopy(a, 0, c, 0, a.length); 265 System.arraycopy(b, 0, c, a.length, b.length); 266 } 267 else if(a == null && b != null) { 268 c = b; 269 } 270 else if(a != null && b == null) { 271 c = a; 272 } 273 return c; 274 } 275 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 276 /** This method efficiently appends a new string onto the end of a string array. 277 * @param a The initial <strong>String[]</strong>. 278 * @param b The new <strong>String</strong>. 279 * @return A <strong>String[]</strong> containing a followed by b. 280 */ 281 static public String[] add(String a[], String b) { 282 String[] c = null; 283 if(a != null && b != null) { 284 c = new String[a.length + 1]; 285 System.arraycopy(a, 0, c, 0, a.length); 286 c[c.length - 1] = b; 287 } 288 else if(a == null && b != null) { 289 c = new String[1]; 290 c[0] = b; 291 } 292 else if(a != null && b == null) { 293 c = a; 294 } 295 return c; 296 } 297 /** This method efficently appends one string array onto the end of another. 298 * @param a The initial <strong>String[]</strong>. 299 * @param b The <strong>String[]</strong> to append. 300 * @return A <strong>String[]</strong> containing a followed by b. 301 */ 302 static public String[] add(String a[], String b[]) { 303 String[] c = null; 304 if(a != null && b != null) { 305 c = new String[a.length + b.length]; 306 System.arraycopy(a, 0, c, 0, a.length); 307 System.arraycopy(b, 0, c, a.length, b.length); 308 } 309 else if(a == null && b != null) { 310 c = b; 311 } 312 else if(a != null && b == null) { 313 c = a; 314 } 315 return c; 316 } 317 /** This method takes an array list and creates a string array. 318 * @param a An <strong>ArrayList</strong> containing hopefully <strong>String</strong> or else this will fail. 319 * @return A <strong>String[]</strong> or <i>null</i> if the array could not be created. 320 */ 321 static public String[] arrayListToStringArray(ArrayList a) { 322 String array[] = new String[a.size()]; 323 for(int i = 0; i < array.length; i++) { 324 array[i] = (String)a.get(i); 325 } 326 return array; 327 } 328 329 static public File[] filter(File[] files, String pattern, boolean exclude) { 330 int write_ptr = 0; 331 ///ystem.err.println("Filtering by '" + pattern + "', Exclude? " + exclude + " :"); 332 for(int read_ptr = 0; read_ptr < files.length; read_ptr++) { 333 File current = files[read_ptr]; 334 files[write_ptr] = current; 335 335 ///ystem.err.print("Testing " + current.getName() + " -> "); 336 336 // Determine whether we move the write pointer or not. 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 420 421 422 423 424 425 426 427 337 if(current.getName().toLowerCase().matches(pattern)) { 338 if(!exclude) { 339 ///ystem.err.println("Match, Exclude."); 340 write_ptr++; 341 } 342 else { 343 ///ystem.err.println("Match, Include."); 344 } 345 } 346 else { 347 // You can't exclude folders with an inclusion filter! 348 if(exclude || current.isDirectory()) { 349 ///ystem.err.println("Nonmatch, Exclude."); 350 write_ptr++; 351 } 352 else { 353 ///ystem.err.println("Nonmatch, Include."); 354 } 355 } 356 } 357 File[] result = new File[write_ptr]; 358 System.arraycopy(files, 0, result, 0, result.length); 359 pattern = null; 360 files = null; 361 return result; 362 } 363 364 /** Method to convert a list of nodes from the DOM model into a node array. This is quite useful as using a <strong>NodeList</strong> is problematic if you wish to traverse the tree while editing. Because they are 'live' representations of the model you are editing, it is easy to drop out of for loops early or run off the ends of arrays. 365 * @param node_list A <strong>NodeList</strong> representing the <strong>Node</strong>s we want to iterate through. 366 * @return A brand new <strong>Node[]</strong> with the first <strong>Node</strong> in the <strong>NodeList</strong> at the head. 367 * @see org.w3c.dom.Node 368 * @see org.w3c.dom.NodeList 369 */ 370 static public Node[] nodeListToNodeArray(NodeList node_list) { 371 Node nodes[] = null; 372 for(int i = 0; i < node_list.getLength(); i++) { 373 nodes = add(nodes, node_list.item(i)); 374 } 375 return nodes; 376 } 377 /** Transforms an Object array into a single string of the form '[o1,o2,...,on]'. 378 * @param objects An <strong>Object[]</strong>. Note that the objects in this array must support toString() reasonably. 379 * @return A <strong>String</strong> representing the given array. 380 */ 381 static public String objectArrayToString(Object objects[]) { 382 StringBuffer result = new StringBuffer("["); 383 for(int i = 0; i < objects.length; i++) { 384 result.append(objects[i].toString()); 385 if(i < objects.length - 1) { 386 result.append(","); 387 } 388 } 389 result.append("]"); 390 return result.toString(); 391 } 392 393 /** Sorts an array of files. */ 394 static public void sort(File[] files) { 395 sort(files, false); 396 } 397 398 /** Sorts an array of files. Can also to instructed to list directories first. Case insensitive. 399 * @param files The File[] to be sorted. 400 * @param directories_first true if you want to directories to be listed first. 401 */ 402 static public void sort(File[] files, boolean directories_first) { 403 if(files != null && files.length > 1) { 404 FileComparator comparator = new FileComparator(directories_first); 405 Arrays.sort(files, comparator); 406 } 407 } 408 409 /** Comparator used to order files. */ 410 static private class FileComparator 411 implements Comparator { 412 private boolean directories_first = false; 413 414 public FileComparator(boolean directories_first) { 415 this.directories_first = directories_first; 416 } 417 /** Compare two files in terms of ordering of their paths. 418 * @param o1 The <strong>Object</strong> that represents the first file. 419 * @param o2 The other <strong>Object</strong>, also a file. 420 * @return An <i>int</i> which is <1, 0 or >1 if o1 is < o2, = o2 or > o2 respectively. 421 */ 422 public int compare(Object o1, Object o2) { 423 int result = 0; 424 File f1 = (File) o1; 425 String n1 = f1.getName().toLowerCase(); 426 File f2 = (File) o2; 427 String n2 = f2.getName().toLowerCase(); 428 428 // Special checks for system roots as these include removable media drives and thus we should never attempt to call isDirectory until the user explicitly attempts to map drive. 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 429 boolean f1_system_root = FileSystemView.getFileSystemView().isFileSystemRoot(f1); 430 boolean f2_system_root = FileSystemView.getFileSystemView().isFileSystemRoot(f2); 431 if((f1_system_root && f2_system_root) || (!f2_system_root && f1_system_root && f2.isDirectory()) || (!f1_system_root && f1.isDirectory() && f2_system_root) || (f1.isDirectory() && f2.isDirectory()) || (f1.isFile() && f2.isFile())) { 432 result = n1.compareTo(n2); 433 } 434 else if(f1_system_root || f1.isDirectory()) { 435 result = -1; 436 } 437 else { 438 result = 1; 439 } 440 return result; 441 } 442 /** Compare two files for equality, in terms of their file paths. 443 * @param object The <strong>Object</strong> representing the file we are about to compare ourselves to. 444 * @return <i>true</i> if we equal the given file, <i>false</i> otherwise. 445 */ 446 public boolean equals(Object obj) { 447 return (compare(this, obj) == 0); 448 } 449 } 450 450 } 451 452 453 454 -
trunk/gli/src/org/greenstone/gatherer/util/DecodeHTMLReader.java
r4293 r4364 5 5 6 6 public class DecodeHTMLReader 7 7 extends PushbackReader { 8 8 9 10 11 9 public DecodeHTMLReader(Reader source) { 10 super(source, 4); 11 } 12 12 13 14 15 16 13 /** Read a single character. */ 14 public int read() { 15 return decode(); 16 } 17 17 18 19 20 21 22 23 24 25 26 18 /** Read characters into a portion of an array. */ 19 public int read(char[] cbuf, int off, int len) { 20 int count = 0; 21 for(int i = off; i < len && ready(); i++) { 22 cbuf[i] = (char)decode(); 23 count++; 24 } 25 return count; 26 } 27 27 28 29 30 31 32 33 34 35 28 public boolean ready() { 29 try { 30 return super.ready(); 31 } 32 catch (Exception error) { 33 } 34 return false; 35 } 36 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 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 37 /** Retrieve the next character off the stream. Unfortunately I have to do this a character at a time (which is slow). I also have to keep in mind that if a suspect encoded character turns out not to be I have to replace the extra characters. */ 38 private int decode() { 39 int character; 40 try { 41 character = super.read(); 42 } 43 catch (Exception error) { 44 character = ' '; 45 } 46 try { 47 if(character == '&') { 48 int amp = super.read(); 49 switch(amp) { 50 case 'a': 51 case 'A': 52 int ampap = super.read(); 53 int ampapo = super.read(); 54 int ampapos = super.read(); 55 int ampapossemi = super.read(); 56 if((ampap == 'p' || ampap == 'P') && (ampapo == 'o' || ampapo == 'O') && (ampapos == 's' || ampapos == 'S') && ampapossemi == ';') { 57 // Read an ' so return an apostrophy 58 return '\''; 59 } 60 // Not a apos. Return the characters removed in the correct order. 61 super.unread(ampapossemi); 62 super.unread(ampapos); 63 super.unread(ampapo); 64 super.unread(ampap); 65 break; 66 case 'g': 67 case 'G': 68 int ampgt = super.read(); 69 int ampgtsemi = super.read(); 70 if((ampgt == 't' || ampgt == 'T') && ampgtsemi == ';') { 71 return '>'; 72 } 73 super.unread(ampgtsemi); 74 super.unread(ampgt); 75 break; 76 case 'l': 77 case 'L': 78 int amplt = super.read(); 79 int ampltsemi = super.read(); 80 if((amplt == 't' || amplt == 'T') && ampltsemi == ';') { 81 return '<'; 82 } 83 super.unread(ampltsemi); 84 super.unread(amplt); 85 break; 86 case 'q': 87 case 'Q': 88 int ampqu = super.read(); 89 int ampquo = super.read(); 90 int ampquot = super.read(); 91 int ampquotsemi = super.read(); 92 if((ampqu == 'u' || ampqu == 'U') && (ampquo == 'o' || ampquo == 'O') && (ampquot == 't' || ampquot == 'T') && ampquotsemi == ';') { 93 return '\"'; 94 } 95 super.unread(ampquotsemi); 96 super.unread(ampquot); 97 super.unread(ampquo); 98 super.unread(ampqu); 99 break; 100 case '#': 101 int amphash = super.read(); 102 int amphash3 = super.read(); 103 int amphash39 = super.read(); 104 int amphash39semi = super.read(); 105 if(amphash == '#' && amphash3 == '3' && amphash39 == '9' && amphash39semi == ';') { 106 return '\''; 107 } 108 super.unread(amphash39semi); 109 super.unread(amphash39); 110 super.unread(amphash3); 111 super.unread(amphash); 112 break; 113 } 114 // Not a suspect. Return the character removed. 115 super.unread(amp); 116 } 117 } 118 catch (Exception error) { 119 Gatherer.printStackTrace(error); 120 } 121 // Nothing special. Simply return the character extracted. 122 return character; 123 } 124 124 } -
trunk/gli/src/org/greenstone/gatherer/util/DictionaryTreeNode.java
r4293 r4364 4 4 5 5 public interface DictionaryTreeNode { 6 7 8 9 10 11 12 13 6 /** Returns the child at the specified index in this node's child array. */ 7 public TreeNode getChildAt(int index); 8 /** Returns the number of children of this node. */ 9 public int getChildCount(); 10 /** This method should always return the same key, regardless of the value of toString(). */ 11 public String getKey(); 12 /** Set the text shown by toString() to the given value. */ 13 public void setText(String value); 14 14 } -
trunk/gli/src/org/greenstone/gatherer/util/DragComponent.java
r4293 r4364 52 52 /** Any component that wants to act a GTree like drag'n'drop component must implement this. */ 53 53 public interface DragComponent { 54 55 56 57 58 59 60 61 62 63 64 54 public void addFocusListener(FocusListener listener); 55 /** 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 Group to tell this component to repair the 'spoilt' region left by its ghost. */ 56 public void clearGhost(); 57 /** Used to notify this component that it has gained focus by some method other that mouse focus. */ 58 public void gainFocus(); 59 /** Retrieve the model associated with this component. */ 60 public FileSystemModel getTreeModel(); 61 /** This method is used to inform this component when it loses focus by means other than a drag mouse event, and should indicate this somehow. */ 62 public void loseFocus(); 63 /** Set the components group. */ 64 public void setGroup(DragGroup group); 65 65 } -
trunk/gli/src/org/greenstone/gatherer/util/DragGroup.java
r4293 r4364 61 61 /** This class acts as a linker between all the various drag and drop enabled DragComponent. It provides methods for ensuring only one component is the drop target, showing extra user feedback, and maintains a single point of focus. Moreover it provides a storage space for necessary shared variables such as mouse_offset of initial drag etc. */ 62 62 public class DragGroup 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 63 implements FocusListener { 64 /** The image used for the ghost icon when dragging. */ 65 public BufferedImage image_ghost = null; 66 /** The initial offset of the mouse from the selection within a DragComponent. Then as you drag the mouse, the ghost appears in a visually consistant place, and remains at the same offset throughout the dragging process. */ 67 public Point mouse_offset = null; 68 /** The component currently in charge of drawing the ghost icon. Note that this isn't necessarily the only component that needs to repaint 'spoilt' screen real-estate as a side effect of the ghost. */ 69 private DragComponent ghost_owner = null; 70 /** The component where the drag began, so that extra information necessary for the drag can be garnered by the drop target, or so that focus can return to the source component if the drop is rejected. 71 * @see org.greenstone.gatherer.tree.DragComponent#gainFocus 72 * @see org.greenstone.gatherer.tree.DragComponent#loseFocus 73 */ 74 private DragTree drag_source = null; 75 /** The selected nodes, as determined when the drag begun. */ 76 private TreePath[] selection = null; 77 /** A list of DragComponents registered as being part of this group. */ 78 private Vector registered = new Vector(); 79 79 80 80 /** Register a DragComponent as begin part of this group. 81 81 * @param component The DragComponent to add. 82 82 */ 83 84 85 86 87 88 89 83 public void add(DragComponent component) { 84 if(!registered.contains(component)) { 85 registered.add(component); 86 component.addFocusListener(this); 87 component.setGroup(this); 88 } 89 } 90 90 91 92 93 94 91 /** Invoked when a component gains the keyboard focus. */ 92 public void focusGained(FocusEvent event) { 93 ((DragComponent)event.getComponent()).gainFocus(); 94 } 95 95 96 97 98 99 100 96 /** Invoked when a component loses the keyboard focus. */ 97 public void focusLost(FocusEvent event) { 98 ((DragComponent)event.getComponent()).loseFocus(); 99 } 100 /** Determines the current 'active' component, ie that component with focus. 101 101 * @return The DragComponent which is currently responsible for drawing the ghost, and thus is active. 102 102 */ 103 104 105 106 103 public DragComponent getActive() { 104 return ghost_owner; 105 } 106 /** Retrieve the nodes selected at the beginning of this drag operation. 107 107 * @return A TreePath[]. 108 108 */ 109 110 111 112 109 public TreePath[] getSelection() { 110 return selection; 111 } 112 /** Retrieve the component which served as the source of this drag action. 113 113 * @return The DragComponent in question. 114 114 */ 115 116 117 118 115 public DragTree getSource() { 116 return drag_source; 117 } 118 /** When called this method asserts that one of the registered GComponents just became the proud owner of focus, and is henceforth responsible for drawing the ghost, and acting like a component thats in focus. 119 119 * @param new_owner The DragComponent that has gained focus during a drag action. 120 120 */ 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 121 public void grabFocus(DragComponent new_owner) { 122 // Tell the previous owner of ghost to clear itself. 123 if(ghost_owner != null) { 124 ghost_owner.clearGhost(); 125 } 126 // Assign new owner 127 ghost_owner = new_owner; 128 // Then tell all other GComponents to indicate they aren't in focus in whatever way they do that (ie selected items of a GTree go gray). Of course the new owners told it is in focus. 129 for(int i = 0; i < registered.size(); i++) { 130 DragComponent comp = (DragComponent)registered.get(i); 131 if(comp == new_owner) { 132 comp.gainFocus(); 133 } 134 else { 135 comp.loseFocus(); 136 } 137 } 138 } 139 /** Sets the value of selected_nodes. 140 140 * @param selection The new value for selected_nodes as a TreePath[]. 141 141 */ 142 143 144 145 142 public void setSelection(TreePath[] selection) { 143 this.selection = selection; 144 } 145 /** Sets the value of drag_source. 146 146 * @param drag_source The new value for drag_source as a DragTree. 147 147 */ 148 149 150 151 148 public void setSource(DragTree drag_source) { 149 this.drag_source = drag_source; 150 System.err.println("The drag source is now " + drag_source); 151 } 152 152 } 153 154 155 -
trunk/gli/src/org/greenstone/gatherer/util/DragTreeSelectionModel.java
r4293 r4364 45 45 46 46 public class DragTreeSelectionModel 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 96 97 98 99 100 101 102 103 104 105 47 extends DefaultTreeSelectionModel 48 implements MouseListener { 49 50 boolean immediate = false; 51 52 private int type = -1; 53 54 private String suffix = null; 55 56 private TreePath temp_path = null; 57 private TreePath temp_paths[] = null; 58 59 static private final int NONE = -1; 60 static private final int ADD = 0; 61 static private final int SET = 1; 62 63 public DragTreeSelectionModel(DragTree tree) { 64 super(); 65 setSelectionMode(DISCONTIGUOUS_TREE_SELECTION); 66 tree.addMouseListener(this); 67 } 68 69 public void addSelectionPath(TreePath path) { 70 if(!immediate) { 71 this.temp_path = path; 72 this.type = this.ADD; 73 } 74 else if(isAppropriate(path)) { 75 temp_path = null; 76 super.addSelectionPath(path); 77 suffix = null; 78 } 79 } 80 81 public void addSelectionPaths(TreePath paths[]) { 82 if(!immediate) { 83 this.temp_paths = paths; 84 this.type = this.ADD; 85 } 86 else if(isAppropriate(paths, true)) { 87 temp_paths = null; 88 super.setSelectionPaths(paths); 89 suffix = null; 90 } 91 } 92 93 public String getDetails() { 94 if(suffix == null) { 95 int file_count = 0; 96 int folder_count = 0; 97 for(int i = 0; selection != null && i < selection.length; i++) { 98 TreeNode node = (TreeNode) selection[i].getLastPathComponent(); 99 if(node.isLeaf()) { 100 file_count++; 101 } 102 else { 103 folder_count++; 104 } 105 } 106 106 // Update the metaaudit_suffix 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 107 String args[] = null; 108 if(file_count > 0 && folder_count > 0) { 109 if(file_count > 1 && folder_count > 1) { 110 args = new String[2]; 111 args[0] = String.valueOf(file_count); 112 args[1] = String.valueOf(folder_count); 113 suffix = Gatherer.dictionary.get("FileActions.Selected", args); 114 } 115 else if(file_count > 1) { 116 args = new String[1]; 117 args[0] = String.valueOf(file_count); 118 suffix = Gatherer.dictionary.get("FileActions.Files_And_Directory_Selected", args); 119 } 120 else if(folder_count > 1) { 121 args = new String[1]; 122 args[0] = String.valueOf(folder_count); 123 suffix = Gatherer.dictionary.get("FileActions.File_And_Directories_Selected", args); 124 } 125 else { 126 suffix = Gatherer.dictionary.get("FileActions.File_And_Directory_Selected"); 127 } 128 } 129 else if(file_count > 0) { 130 if(file_count > 1) { 131 args = new String[1]; 132 args[0] = String.valueOf(file_count); 133 suffix = Gatherer.dictionary.get("FileActions.Files_Selected", args); 134 } 135 else if(file_count == 1) { 136 suffix = Gatherer.dictionary.get("FileActions.File_Selected"); 137 } 138 } 139 else if(folder_count > 0) { 140 if(folder_count > 1) { 141 args = new String[1]; 142 args[0] = String.valueOf(folder_count); 143 suffix = Gatherer.dictionary.get("FileActions.Directories_Selected", args); 144 } 145 else { 146 suffix = Gatherer.dictionary.get("FileActions.Directory_Selected"); 147 } 148 } 149 else { 150 Gatherer.dictionary.get("FileActions.No_Selection"); 151 } 152 args = null; 153 } 154 return suffix; 155 } 156 157 /** Any implementation of MouseListener must include this method so 158 * we can be informed when the mouse is clicked. 159 * @param event A MouseEvent containing all the information about 160 * this mouse click. 161 */ 162 public void mouseClicked(MouseEvent event) { 163 } 164 165 /** Any implementation of MouseListener must include this method so 166 * we can be informed when the mouse enters a component. 167 * @param event A MouseEvent containing all the information about 168 * this mouse action. 169 */ 170 public void mouseEntered(MouseEvent event) { 171 } 172 173 /** Any implementation of MouseListener must include this method so 174 * we can be informed when the mouse exits a component. 175 * @param event A MouseEvent containing all the information about 176 * this mouse action. 177 */ 178 public void mouseExited(MouseEvent event) { 179 } 180 181 /** Any implementation of MouseListener must include this method so 182 * we can be informed when the mouse is pressed (start of click). 183 * @param event A MouseEvent containing all the information about 184 * this mouse action. 185 */ 186 public void mousePressed(MouseEvent event) { 187 } 188 189 /** Any implementation of MouseListener must include this method so 190 * we can be informed when the mouse is released (end of click). 191 * @param event A MouseEvent containing all the information about 192 * this mouse action. 193 */ 194 public void mouseReleased(MouseEvent event) { 195 switch(this.type) { 196 case 0: // this.ADD 197 if(this.temp_path != null && isAppropriate(temp_path)) { 198 super.addSelectionPath(this.temp_path); 199 this.temp_path = null; 200 } 201 if(this.temp_paths != null && isAppropriate(temp_paths, true)) { 202 super.addSelectionPaths(this.temp_paths); 203 this.temp_paths = null; 204 } 205 this.type = this.NONE; 206 suffix = null; 207 break; 208 case 1: // this.SET 209 if(this.temp_path != null) { 210 super.setSelectionPath(this.temp_path); 211 this.temp_path = null; 212 } 213 if(this.temp_paths != null && isAppropriate(temp_paths, false)) { 214 super.setSelectionPaths(this.temp_paths); 215 this.temp_paths = null; 216 } 217 this.type = this.NONE; 218 suffix = null; 219 break; 220 } 221 } 222 223 public void setImmediate(boolean value) { 224 immediate = value; 225 } 226 227 public void setSelectionPath(TreePath path) { 228 // Since this is only a single path we don't need to check whether its an appropriate selection. 229 if(!immediate) { 230 this.temp_path = path; 231 this.type = this.SET; 232 } 233 else { 234 temp_path = null; 235 super.setSelectionPath(path); 236 suffix = null; 237 } 238 } 239 240 public void setSelectionPaths(TreePath paths[]) { 241 if(!immediate) { 242 this.temp_paths = paths; 243 this.type = this.SET; 244 } 245 else if(isAppropriate(paths, false)) { 246 temp_paths = null; 247 super.setSelectionPaths(paths); 248 suffix = null; 249 } 250 } 251 252 /** Ensure that the given path is appropriate to add to the current selection, preventing mixed selections of files and folder. We also must check that no path is a ancestor/descendant of another. There is also a slight optimization in that if the current selection contains only files (which if the rules are followed, and the first 'test' node is a file, then it must) there is no point in checking the remaining files in the selection. Its a different story for folders however as we have to ensure no folder is in the lineage of another, even if they are all folders! */ 253 private boolean isAppropriate(TreePath path) { 254 boolean appropriate = true; 255 TreeNode new_node = (TreeNode) path.getLastPathComponent(); 256 if(selection != null && selection.length > 0) { 257 TreeNode test_node = (TreeNode) selection[0].getLastPathComponent(); 258 appropriate = appropriate && (new_node.isLeaf() == test_node.isLeaf()); 259 if(!test_node.isLeaf()) { 260 appropriate = appropriate && !path.isDescendant(selection[0]) && !selection[0].isDescendant(path); 261 for(int i = 1; appropriate && selection != null && i < selection.length; i++) { 262 TreeNode current_node = (TreeNode) selection[i].getLastPathComponent(); 263 appropriate = appropriate && (new_node.isLeaf() == current_node.isLeaf()); 264 appropriate = appropriate && !path.isDescendant(selection[i]) && !selection[i].isDescendant(path); 265 } 266 } 267 } 268 return appropriate; 269 } 270 /** Ensure that the given paths are appropriate to add to the current selection, preventing mixed selections of files and folder. We also must check that no path is a ancestor/descendant of another. One last detail to keep in mind is that adding selections depends upon the current selection, whereas set the selection paths doesn't (replaces them instead) and thus no check of the current paths is needed. */ 271 private boolean isAppropriate(TreePath[] paths, boolean check_current) { 272 boolean appropriate = true; 273 if(paths != null) { 274 if(paths.length >= 2) { 275 // Prevent folders being added to a previous selection of files and vice-versa 276 // First check that the new selection are all of the same type 277 for(int i = 0; appropriate && paths != null && i < paths.length - 1; i++) { 278 TreeNode one_node = (TreeNode) paths[i].getLastPathComponent(); 279 TreeNode other_node = (TreeNode) paths[i+1].getLastPathComponent(); 280 appropriate = appropriate && (one_node.isLeaf() == other_node.isLeaf()); 281 appropriate = appropriate && !paths[i].isDescendant(paths[i+1]) && !paths[i+1].isDescendant(paths[i]); 282 } 283 } 284 284 // Now we check the current selection against the first node in our new selection 285 286 287 288 289 290 285 if(appropriate && check_current) { 286 appropriate = isAppropriate(paths[0]); 287 } 288 } 289 return appropriate; 290 } 291 291 } 292 -
trunk/gli/src/org/greenstone/gatherer/util/EmailAddress.java
r4293 r4364 66 66 */ 67 67 public class EmailAddress { 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 68 /** The controls for editing this email. */ 69 private Control controls = null; 70 /** A reference to the Gatherer. */ 71 private Gatherer gatherer = null; 72 /** The type of this email, in collection configuration terms. */ 73 private int type = -1; 74 /** The host part of the email (after @). */ 75 public String host = null; 76 /** The user part of the email (before @). */ 77 public String user = null; 78 /** The name given to or associated with this particular email (maybe the persons name, or it this email belongs to a certain variable i.e Collection Author). */ 79 public String name = null; 80 /** Enumeration of important email address types - in this case the creator of a collection. */ 81 static final public int CREATOR = 0; 82 /** Enumeration of important email address types - in this case the maintainer of a collection. */ 83 static final public int MAINTAINER = 1; 84 /** Enumeration of important email address types - in this case any other email address other than the two noted above. */ 85 static final public int OTHER = 2; 86 /** Constructor. 87 * @param gatherer A reference to the <strong>Gatherer</strong> for messaging purposes. 88 */ 89 public EmailAddress(Gatherer gatherer) { 90 this.gatherer = gatherer; 91 } 92 /** Constructor. 93 * @param gatherer A reference to the <strong>Gatherer</strong> for messaging purposes. 94 * @param raw A <strong>String</strong> containing both the name and email separated by whitespace. 95 */ 96 public EmailAddress(Gatherer gatherer, String raw) { 97 this(gatherer); 98 StringTokenizer tokenizer = new StringTokenizer(raw); 99 if(tokenizer.countTokens() >= 2) { 100 this.name = tokenizer.nextToken(); 101 if(name.equals("creator")) { 102 type = CREATOR; 103 } 104 else if(name.equals("maintainer")) { 105 type = MAINTAINER; 106 } 107 else { 108 type = OTHER; 109 } 110 String email = tokenizer.nextToken(); 111 if(email.indexOf("@") != -1) { 112 this.user = email.substring(0, email.indexOf("@")); 113 this.host = email.substring(email.indexOf("@") + 1); 114 } 115 } 116 else { 117 this.name = "Invalid Email"; 118 this.user = ""; 119 this.host = ""; 120 } 121 } 122 /** Retrieve the controls associated with editing this email address. 123 * @return A <strong>JPanel</strong> containing those controls. 124 */ 125 public JPanel getControls() { 126 if(controls == null) { 127 controls = new Control(name); 128 } 129 return controls; 130 } 131 /** Retrieve the controls associated with editing this email address, but ensure they have a certain title label. 132 * @param label The <strong>String</strong> to use as the label. 133 * @return A <strong>JPanel</strong> containing those controls. 134 */ 135 public JPanel getControls(String label) { 136 if(controls == null) { 137 controls = new Control(label); 138 } 139 return controls; 140 } 141 /** Method to produce this email as a string. 142 * @return A <strong>String</string> representing this email. 143 */ 144 public String toString() { 145 return name + " " + user + "@" + host; 146 } 147 /** Determines if this is a valid email. 148 * @return <i>true</i> if it is current valid, <i>false</i> otherwise. 149 */ 150 public boolean isValid() { 151 return (user != null && user.length() > 0) && (host != null && host.length() > 0); 152 } 153 /** Overloaded to call get with both a key and an empty argument array. 154 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 155 * @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. 156 */ 157 private String get(String key) { 158 return get(key, null); 159 } 160 /** 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> 161 * 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>. 162 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 163 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String. 164 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatically populated with formatting Strings of with argument String provided in the get call. 165 * @see org.greenstone.gatherer.Dictionary 166 * @see org.greenstone.gatherer.Gatherer 167 */ 168 private String get(String key, String args[]) { 169 if(key.indexOf('.') == -1) { 170 key = "CDM.General.Email." + key; 171 } 172 return gatherer.dictionary.get(key, args); 173 } 174 /** The controls used for editing an email address. Basically two text fields with an '@' label in the middle. You also have a title label for this control, which is known for the major email address types, or can be supplied for any other purpose. */ 175 private class Control 176 extends JPanel { 177 /** The at symbol label. */ 178 private JLabel at = null; 179 /** The title label. */ 180 private JLabel label = null; 181 /** The first text field for the user part of an address. */ 182 private JTextField field1 = null; 183 /** The second text field for the host part of an address. */ 184 private JTextField field2 = null; 185 /** The panel on to which the text fields and the at label are placed. */ 186 private JPanel middle = null; 187 /** Constructor. 188 * @param name The <strong>String</strong> to use for the title label. 189 * @see org.greenstone.gatherer.util.EmailAddress.Control.HostListener 190 * @see org.greenstone.gatherer.util.EmailAddress.Control.UserListener 191 */ 192 public Control(String name) { 193 super(); 194 switch(type) { 195 case CREATOR: 196 label = new JLabel(get("Creator")); 197 break; 198 case MAINTAINER: 199 label = new JLabel(get("Maintainer")); 200 break; 201 default: 202 label = new JLabel(name); 203 } 204 middle = new JPanel(); 205 field1 = new JTextField(user); 206 at = new JLabel("@"); 207 field2 = new JTextField(host); 208 208 // Add listeners 209 210 209 field1.addKeyListener(new UserListener()); 210 field2.addKeyListener(new HostListener()); 211 211 // Layout 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 212 label.setBorder(BorderFactory.createEmptyBorder(2,5,2,10)); 213 214 at.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); 215 216 middle.setLayout(new BorderLayout()); 217 middle.add(field1, BorderLayout.CENTER); 218 middle.add(at, BorderLayout.EAST); 219 220 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 221 setLayout(new GridLayout(1,3)); 222 add(label); 223 add(middle); 224 add(field2); 225 } 226 /** Listens for changes of the host part of the email address and updates the email object accordingly. */ 227 private class HostListener 228 extends KeyAdapter { 229 229 /** Whenever a key is released (ie after a key typed event has been fired) when typing in the host field, this method updates the host field of the email object. 230 230 * @param event A <strong>KeyEvent</strong> containing extra information about the key release performed. 231 231 */ 232 233 234 235 236 237 238 232 public void keyReleased(KeyEvent event) { 233 host = field2.getText(); 234 } 235 } 236 /** Listens for changes of the user part of the email address and updates the email object accordingly. */ 237 private class UserListener 238 extends KeyAdapter { 239 239 /** Whenever a key is released (ie after a key typed event has been fired) when typing in the user field, this method updates the user field of the email object. 240 240 * @param event A <strong>KeyEvent</strong> containing extra information about the key release performed. 241 241 */ 242 243 244 245 242 public void keyReleased(KeyEvent event) { 243 user = field1.getText(); 244 } 245 } 246 246 247 247 } 248 248 } 249 250 251 252 253 -
trunk/gli/src/org/greenstone/gatherer/util/EnumeratedVector.java
r4293 r4364 50 50 */ 51 51 public class EnumeratedVector 52 53 54 55 56 57 52 extends Vector 53 implements Enumeration { 54 /** The current index into our enumerated vector. */ 55 private int index = 0; 56 /** Can we call nextElement() without running out of entries in the vector. 57 * @return <i>true</i> if there are elements remaining, <i>false</i> otherwise. 58 58 */ 59 60 61 62 59 public boolean hasMoreElements() { 60 return (index < size()); 61 } 62 /** Retrieve the next element indicated by the current index. 63 63 * @return The next element as an <strong>Object</strong>. 64 64 */ 65 66 67 68 69 70 71 72 65 public Object nextElement() { 66 Object object = null; 67 if(index < size()) { 68 object = get(index); 69 index++; 70 } 71 return object; 72 } 73 73 } -
trunk/gli/src/org/greenstone/gatherer/util/ExclusiveListSelectionListener.java
r4293 r4364 58 58 */ 59 59 public class ExclusiveListSelectionListener 60 61 62 63 64 65 66 60 implements ListSelectionListener { 61 /** A flag set to show that we are already dealing with a list selection change, and to ignore further ones until the flag becomes unset again. */ 62 private boolean ignore = false; 63 /** An array of JList components, containing each JList thats registered with this listener. */ 64 private JList lists[] = null; 65 /** Method to register a new JList with this listener. 66 * @param list The new <strong>JList</strong> to register. 67 67 */ 68 69 70 71 72 73 74 75 76 77 78 79 80 81 68 public void add(JList list) { 69 list.addListSelectionListener(this); 70 if(lists == null) { 71 lists = new JList[1]; 72 lists[0] = list; 73 } 74 else { 75 JList temp[] = new JList[lists.length + 1]; 76 System.arraycopy(lists, 0, temp, 0, lists.length); 77 temp[lists.length] = list; 78 lists = temp; 79 } 80 } 81 /** Any implementation of ListSelectionListener must include this method so we can be informed when the selection changes. In this case we want to clear the selections of all other registered JLists other than the caller. 82 82 * @param event A <strong>ListSelectionEvent</strong> containing informaiton about the event. 83 83 */ 84 85 86 87 88 89 90 91 92 93 94 95 84 public void valueChanged(ListSelectionEvent event) { 85 if(!ignore) { 86 ignore = true; 87 JList source = (JList)event.getSource(); 88 for(int i = 0; i < lists.length; i++) { 89 if(lists[i] != source) { 90 lists[i].clearSelection(); 91 } 92 } 93 ignore = false; 94 } 95 } 96 96 } -
trunk/gli/src/org/greenstone/gatherer/util/GSDLSiteConfig.java
r4357 r4364 5 5 6 6 public class GSDLSiteConfig 7 8 9 10 7 extends LinkedHashMap { 8 private File gsdlsite_cfg; 9 private String autoenter_initial; 10 private String start_browser_initial; 11 11 12 13 14 12 static final public String ADD_COMMAND = "a=config&cmd=add-collection&c="; 13 static final public String RELEASE_COMMAND = "a=config&cmd=release-collection&c="; 14 static final public String QUIT_COMMAND = "a="; 15 15 16 17 18 19 20 21 22 23 24 25 26 16 static final private String AUTOENTER = "autoenter"; 17 static final private String COLON = ":"; 18 static final private String ENTERLIB = "enterlib"; 19 static final private String FALSE = "0"; 20 static final private String GSDL = "gsdl"; 21 static final private String GSDLSITE_CFG = "gsdlsite.cfg"; 22 static final private String PORTNUMBER = "portnumber"; 23 static final private String SEPARATOR = "/"; 24 static final private String STARTBROWSER = "start_browser"; 25 static final private String TRUE = "1"; 26 static final private String URL = "url"; 27 27 28 29 30 28 public GSDLSiteConfig(File server_exe) { 29 debug("New GSDLSiteConfig for: " + server_exe.getAbsolutePath()); 30 gsdlsite_cfg = new File(server_exe.getParentFile(), GSDLSITE_CFG); 31 31 32 33 34 35 32 autoenter_initial = null; 33 start_browser_initial = null; 34 load(); 35 } 36 36 37 38 39 37 public boolean exists() { 38 return gsdlsite_cfg.exists(); 39 } 40 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 41 public String getURL() { 42 // URL is made from url and portnumber 43 String url = (String) get(URL); 44 if(url != null) { 45 StringBuffer temp = new StringBuffer(url); 46 temp.append(COLON); 47 temp.append((String)get(PORTNUMBER)); 48 String enterlib = (String)get(ENTERLIB); 49 if(enterlib == null || enterlib.length() == 0) { 50 // Use the default /gsdl and hope for the best. 51 temp.append(SEPARATOR); 52 temp.append(GSDL); 53 } 54 else { 55 if(!enterlib.startsWith(SEPARATOR)) { 56 temp.append(SEPARATOR); 57 } 58 temp.append(enterlib); 59 } 60 enterlib = null; 61 url = temp.toString(); 62 } 63 debug("Found Local Library Address: " + url); 64 return url; 65 } 66 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 67 public void load() { 68 if(gsdlsite_cfg.exists()) { 69 debug("Load: " + gsdlsite_cfg.getAbsolutePath()); 70 clear(); 71 try { 72 BufferedReader in = new BufferedReader(new FileReader(gsdlsite_cfg)); 73 String line = null; 74 while((line = in.readLine()) != null) { 75 String key = null; 76 String value = null; 77 int index = -1; 78 if((index = line.indexOf("=")) != -1 && line.length() >= index + 1) { 79 key = line.substring(0, index); 80 value = line.substring(index + 1); 81 } 82 else { 83 key = line; 84 } 85 put(key, value); 86 } 87 } 88 catch (Exception error) { 89 error.printStackTrace(); 90 } 91 } 92 else { 93 debug("No GSDLsite.cfg file can be found!"); 94 } 95 } 96 96 97 98 99 100 101 102 103 104 97 /** Restore the autoenter value to its initial value, and remove url if present. */ 98 public void restore() { 99 debug("Restore Initial Settings"); 100 put(AUTOENTER, autoenter_initial); 101 put(STARTBROWSER, start_browser_initial); 102 remove(URL); 103 save(); 104 } 105 105 106 107 108 109 110 111 112 113 114 115 116 117 118 106 public void set() { 107 debug("Set Session Settings"); 108 if(autoenter_initial == null) { 109 autoenter_initial = (String) get(AUTOENTER); 110 debug("Remember autoenter was: " + autoenter_initial); 111 } 112 put(AUTOENTER, TRUE); 113 if(start_browser_initial == null) { 114 start_browser_initial = (String) get(STARTBROWSER); 115 debug("Remember start_browser was: " + start_browser_initial); 116 } 117 save(); 118 } 119 119 120 121 122 120 private void debug(String message) { 121 System.err.println(message); 122 } 123 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 124 private void save() { 125 debug("Save: " + gsdlsite_cfg.getAbsolutePath()); 126 try { 127 BufferedWriter out = new BufferedWriter(new FileWriter(gsdlsite_cfg, false)); 128 for(Iterator keys = keySet().iterator(); keys.hasNext(); ) { 129 String key = (String) keys.next(); 130 String value = (String) get(key); 131 out.write(key, 0, key.length()); 132 if(value != null) { 133 out.write('='); 134 out.write(value, 0, value.length()); 135 } 136 out.newLine(); 137 } 138 out.flush(); 139 out.close(); 140 } 141 catch (Exception error) { 142 error.printStackTrace(); 143 } 144 } 145 145 } 146 147 148 149 150 -
trunk/gli/src/org/greenstone/gatherer/util/GURL.java
r4293 r4364 60 60 */ 61 61 public class GURL 62 62 implements Cloneable, Serializable { 63 63 64 64 private Integer type; 65 65 66 66 private URL url; 67 67 68 69 70 71 72 68 private String file; 69 private String host; 70 private String path; 71 private String protocol; 72 private String title = "empty"; 73 73 74 75 76 77 78 79 80 81 82 83 74 /** This Vector maintains a record of the url links from within 75 * this resource, and their type (EXTERNAL vs INTERNAL). Internal 76 * links can be 'hidden' from the user if desired, or a user can 77 * opt to have all internal links automatically moved into a 78 * collection if the page that depends upon them is moved. <BR> 79 * Each entry in the vector is of the form. 80 * links[i] = String url 81 * links[i+1] = int type 82 */ 83 private Vector links; 84 84 85 86 87 85 /* Static Integer identifiers for ease of use. */ 86 static public Integer INTERNAL = new Integer(0); 87 static public Integer EXTERNAL = new Integer(1); 88 88 89 90 91 89 public GURL(String url_str) { 90 this(url_str, GURL.EXTERNAL); 91 } 92 92 93 94 95 93 public GURL(URL url) { 94 this(url, GURL.EXTERNAL); 95 } 96 96 97 98 99 100 97 public GURL(URL url, Integer type) { 98 this.type = type; 99 this.url = url; 100 } 101 101 102 103 102 public GURL(String url_str, Integer type) { 103 this.type = type; 104 104 105 106 107 108 109 110 111 105 try { 106 if(!url_str.startsWith("file:") && url_str.indexOf("://") == -1) { 107 url_str = "http://" + url_str; 108 } 109 url = new URL(url_str); 110 } 111 catch (MalformedURLException e) { 112 112 // Fix it 113 114 113 } 114 } 115 115 116 117 118 116 public void addLink(String url) { 117 addLink(url, GURL.EXTERNAL); 118 } 119 119 120 121 122 123 124 125 126 120 public void addLink(String url, Integer type) { 121 if(links == null) { 122 links = new Vector(); 123 } 124 links.add(url); 125 links.add(type); 126 } 127 127 128 129 130 131 132 133 128 /** Used by tree renderer to display name. So we'll only return the 129 * filename. 130 */ 131 public String getFile() { 132 return parseFile(url.getFile()); 133 } 134 134 135 136 137 135 public String getHost() { 136 return url.getHost(); 137 } 138 138 139 140 141 142 143 144 145 146 139 public String getLocalFile() { 140 String local_file = getPath() + File.separator + getFile(); 141 // Strip msdos roots if they're still there. 142 while(local_file.indexOf(":") != -1) { 143 local_file = local_file.substring(0, local_file.indexOf(":")) + local_file.substring(local_file.indexOf(":") + 1); 144 } 145 return local_file; 146 } 147 147 148 149 150 148 public String getPath() { 149 return getHost() + parsePath(url.getFile()); 150 } 151 151 152 153 154 155 156 157 158 152 public String getProtocol() { 153 String protocol = url.getProtocol(); 154 if(protocol.equals("file")) { 155 return protocol + ":"; 156 } 157 return protocol + "://"; 158 } 159 159 160 161 162 160 public URL getURL() { 161 return url; 162 } 163 163 164 165 166 164 public String toString() { 165 return url.toString(); 166 } 167 167 168 169 170 168 public boolean valid() { 169 return (url != null); 170 } 171 171 172 173 172 private String parseFile(String raw) { 173 if(raw != null && !raw.equals("")) { 174 174 // Remove everything upto and including the last '/' 175 176 177 178 179 180 175 if(raw.indexOf("/") != -1) { 176 return raw.substring(raw.lastIndexOf("/") + 1); 177 } 178 } 179 return raw; 180 } 181 181 182 183 182 private String parsePath(String raw) { 183 if(raw != null && !raw.equals("")) { 184 184 // Remove everything after the last "/" 185 186 187 188 189 190 185 if(raw.indexOf("/") != -1) { 186 return raw.substring(0, raw.lastIndexOf("/")); 187 } 188 } 189 return raw; 190 } 191 191 192 192 } 193 -
trunk/gli/src/org/greenstone/gatherer/util/Generator.java
r4293 r4364 57 57 public class Generator { 58 58 59 private FileOutputStream file_output_stream = null; 60 61 private TemplateManager template_manager = null; 62 63 public Generator(File test_file, File template_file) { 64 template_manager = new TemplateManager(template_file); 65 File files[] = null; 66 // If test_file is a file, goody. 67 if(test_file.isFile()) { 68 files = new File[1]; 69 files[0] = test_file; 70 } 71 // Otherwise we use all of test_files children. 72 else { 73 files = test_file.listFiles(); 74 } 75 // Its unfortunate we test this twice for the basic case, but we need to ensure that source managers are only contructed for non-directory files. 76 for(int i = 0; i < files.length; i++) { 77 if(files[i].isFile()) { 78 SourceManager source_manager = new SourceManager(files[i]); 79 source_manager = null; 80 } 81 } 82 // All done. 83 } 84 85 public void exit() { 86 if(file_output_stream != null) { 87 try { 88 file_output_stream.close(); 89 file_output_stream = null; 90 } 91 catch(Exception error) { 92 error.printStackTrace(); 93 } 94 } 95 System.exit(0); 96 } 97 98 public void out(String msg) { 99 System.err.println(msg); 100 try { 101 if(file_output_stream == null) { 102 file_output_stream = new FileOutputStream("output.txt"); 103 } 104 file_output_stream.write((msg + "\n").getBytes()); 105 } 106 catch(Exception error) { 107 error.printStackTrace(); 108 System.err.println("An error has occured. View output.txt for more information."); 109 exit(); 110 } 111 } 112 113 static public void main(String args[]) { 114 System.err.println("Rapid repeated HTML generation tool."); 115 System.err.println(" written by John Thompson, 2002\n"); 116 boolean usage = false; 117 // Attempt to generate html from text 118 if(args.length == 2) { 119 File text_file = new File(args[0]); 120 File template_file = new File(args[1]); 121 if(!text_file.exists() || !template_file.exists() || !template_file.isFile()) { 122 usage = true; 123 } 59 private FileOutputStream file_output_stream = null; 60 61 private TemplateManager template_manager = null; 62 63 public Generator(File test_file, File template_file) { 64 template_manager = new TemplateManager(template_file); 65 File files[] = null; 66 // If test_file is a file, goody. 67 if(test_file.isFile()) { 68 files = new File[1]; 69 files[0] = test_file; 70 } 71 // Otherwise we use all of test_files children. 72 else { 73 files = test_file.listFiles(); 74 } 75 // Its unfortunate we test this twice for the basic case, but we need to ensure that source managers are only contructed for non-directory files. 76 for(int i = 0; i < files.length; i++) { 77 if(files[i].isFile()) { 78 SourceManager source_manager = new SourceManager(files[i]); 79 source_manager = null; 80 } 81 } 82 // All done. 83 } 84 85 public void exit() { 86 if(file_output_stream != null) { 87 try { 88 file_output_stream.close(); 89 file_output_stream = null; 90 } 91 catch(Exception error) { 92 error.printStackTrace(); 93 } 94 } 95 System.exit(0); 96 } 97 98 public void out(String msg) { 99 System.err.println(msg); 100 try { 101 if(file_output_stream == null) { 102 file_output_stream = new FileOutputStream("output.txt"); 103 } 104 file_output_stream.write((msg + "\n").getBytes()); 105 } 106 catch(Exception error) { 107 error.printStackTrace(); 108 System.err.println("An error has occured. View output.txt for more information."); 109 exit(); 110 } 111 } 112 113 static public void main(String args[]) { 114 System.err.println("Rapid repeated HTML generation tool."); 115 System.err.println(" written by John Thompson, 2002\n"); 116 boolean usage = false; 117 // Attempt to generate html from text 118 if(args.length == 2) { 119 File text_file = new File(args[0]); 120 File template_file = new File(args[1]); 121 if(!text_file.exists() || !template_file.exists() || !template_file.isFile()) { 122 usage = true; 123 } 124 else { 125 Generator generator = new Generator(text_file, template_file); 126 generator.exit(); 127 } 128 } 129 else { 130 usage = true; 131 } 132 // Print usage message 133 if(usage) { 134 System.err.println("Usage: GenHelp <text_file> <template_file>"); 135 System.err.println("text_file - A file containing text marked up using special tags."); 136 System.err.println("template_file - An HTML file using tags to specify where to insert text."); 137 } 138 } 139 140 /** An HTML block object is essentially a String buffer containing several places where it demands further text be appended. To this end you can ask if an HTML block is ready, and if not provide it with a further text fragment and try again. */ 141 private class HTMLBlock { 142 /** Prevent the original HTML block being changed. */ 143 private boolean original; 144 145 private int insertion_position = -1; 146 /** The name of this HTML block as taken from the template file. */ 147 private String name = null; 148 private String paragraph_tag = null; 149 /** The HTML block itself. */ 150 private StringBuffer data = null; 151 /** Constructor takes the HTML blocks name as extracted from the template file. 152 * @param name The name of this HTML block as a <strong>String</strong>. 153 */ 154 public HTMLBlock(String name) { 155 this.data = new StringBuffer(""); 156 this.name = name; 157 this.original = true; 158 } 159 /** Copy constructor takes the HTML block and its name as extracted from the template file. 160 * @param name The name of this HTML block as a <strong>String</strong>. 161 * @param raw The raw HTML code as a <strong>String</strong>. 162 */ 163 public HTMLBlock(String name, String raw) { 164 this.data = new StringBuffer(raw); 165 this.name = name; 166 this.original = false; 167 } 168 /** Append this text to the end of the current block. 169 * @param text The <strong>String</strong> to add. 170 */ 171 public void addLine(String text) { 172 if(original) { 173 data.append(text); 174 data.append("\n"); 175 } 176 } 177 /** Retrieve the name of this HTML block. 178 * @return A <strong>String</strong> representing the name. 179 */ 180 public String getName() { 181 return name; 182 } 183 /** Insert a text fragment into this HTML block. 184 * @param fragment The <strong>String</strong> to insert. 185 * @return <i>true</i> if an insertion occured, <i>false</i> otherwise. 186 */ 187 public boolean insert(String fragment) { 188 if(!original) { 189 // Find the start of an insertion tag. 190 int start = data.indexOf("<$"); 191 if(start != -1) { 192 // Find the end of an insertion tag. 193 int end = data.indexOf(">", start) + 1; 194 // Determine the tag, and search for any paragraphing information. 195 String tag = data.substring(start, end); 196 if(tag.length() > 7) { 197 paragraph_tag = "<p " + tag.substring(7) + "\n"; 198 fragment = paragraph_tag + fragment + "\n</p>\n"; 199 } 200 // Substitue the fragment for the tag. 201 data.replace(start, end, fragment); 202 // Now set insertion position 203 insertion_position = start + fragment.length(); 204 return true; 205 } 206 // Its possible that we've already got enough text to output this block. Essentially this new fragment is meant to be another paragraph. Such fragments are always added immediately after the last fragment inserted. 207 else if(insertion_position != -1) { 208 if(paragraph_tag != null) { 209 fragment = paragraph_tag + fragment + "\n</p>\n"; 210 } 211 data.insert(insertion_position, fragment); 212 insertion_position = insertion_position + fragment.length(); 213 } 214 } 215 return false; 216 } 217 /** Is this block ready to written out. 218 * @return <i>true</i> if we can write this block out, <i>false</i> if text insertion points still exist. 219 */ 220 public boolean ready() { 221 return data.indexOf("<$") == -1; 222 } 223 /** Produce a copy of this block with all text insertions intact. 224 * @return A new <strong>HTMLBlock</strong>. 225 */ 226 public HTMLBlock spawn() { 227 return new HTMLBlock(name, data.toString()); 228 } 229 /** Return the HTML block within this object. 230 * @return A <strong>String</strong> containing the HTML code. 231 */ 232 public String toString() { 233 return data.toString(); 234 } 235 } 236 /** Reads in the given text file, outputing HTML by utilizing HTML blocks stored in the TemplateManager. The manager understands one extra tag <$FILE name=<filename>> which is used to indicate the presence of several output files within one input file. If this tag is not found the default name for the output file is "<text_file_name>.htm". */ 237 private class SourceManager { 238 private File text_file = null; 239 240 private FileOutputStream output_file = null; 241 242 public SourceManager(File text_file) { 243 out("Building Source Manager:"); 244 this.text_file = text_file; 245 try { 246 out(" reading from: " + text_file.getName()); 247 // Create input stream from the text file which we read a line at a time. 248 FileReader file_reader = new FileReader(text_file); 249 BufferedReader buffered_reader = new BufferedReader(file_reader); 250 HTMLBlock block = null; 251 String line = null; 252 StringBuffer paragraph = null; 253 while((line = buffered_reader.readLine()) != null) { 254 boolean done = false; 255 // What we do here depends on what state we are in and what tag or text we are reading. 256 // The first case to consider is a file open command tag. This tells us the next block of html should be written to a certain file. 257 if(line.startsWith("<$FILE")) { 258 // Close any existing output file. 259 if(output_file != null) { 260 // Write any existing block 261 if(block != null) { 262 if(paragraph != null) { 263 block.insert(paragraph.toString()); 264 paragraph = null; 265 } 266 out(" write: " + block.getName()); 267 output_file.write((block.toString()).getBytes()); 268 block = null; 269 } 270 output_file.close(); 271 output_file = null; 272 } 273 // Determine the new file name. 274 String name = line.substring(12, line.lastIndexOf(">")); 275 // Create new file. 276 output_file = new FileOutputStream(name); 277 // Dealt with 278 out(" writing to: " + name); 279 name = null; 280 done = true; 281 } 282 else if(line.startsWith("<$")) { 283 // If we have a block open its time to write it out. 284 if(block != null) { 285 if(paragraph != null) { 286 block.insert(paragraph.toString()); 287 paragraph = null; 288 } 289 // Check we have somewhere to write to. 290 if(output_file == null) { 291 output_file = new FileOutputStream(text_file.getName() + ".htm"); 292 } 293 out(" write: " + block.getName()); 294 output_file.write((block.toString()).getBytes()); 295 block = null; 296 } 297 // Now determine the new blocks name. 298 String name = line.substring(2, line.lastIndexOf(">")); 299 // Retrieve that block 300 block = template_manager.getHTMLBlock(name); 301 if(block == null) { 302 // Somethings gone wrong, most likely an unidentified block name. 303 out(" no such block: " + name); 304 } 305 name = null; 306 done = true; 307 } 308 // Its just text. However we want text in paragraph blocks. A paragraph is delimited by a "\n\n" or the start of a tag. 309 if(!done) { 310 // If the line read is "" it means we've encountered a "\n\n". 311 if(line.equals("")) { 312 // Add paragraph to open block. 313 if(block != null && paragraph != null) { 314 block.insert(paragraph.toString()); 315 paragraph = null; 316 } 317 } 318 // Any other case just add to paragraph. Ensure that there is at least one space between the two fragments 124 319 else { 125 Generator generator = new Generator(text_file, template_file); 126 generator.exit(); 127 } 128 } 129 else { 130 usage = true; 131 } 132 // Print usage message 133 if(usage) { 134 System.err.println("Usage: GenHelp <text_file> <template_file>"); 135 System.err.println("text_file - A file containing text marked up using special tags."); 136 System.err.println("template_file - An HTML file using tags to specify where to insert text."); 137 } 138 } 139 140 /** An HTML block object is essentially a String buffer containing several places where it demands further text be appended. To this end you can ask if an HTML block is ready, and if not provide it with a further text fragment and try again. */ 141 private class HTMLBlock { 142 /** Prevent the original HTML block being changed. */ 143 private boolean original; 144 145 private int insertion_position = -1; 146 /** The name of this HTML block as taken from the template file. */ 147 private String name = null; 148 private String paragraph_tag = null; 149 /** The HTML block itself. */ 150 private StringBuffer data = null; 151 /** Constructor takes the HTML blocks name as extracted from the template file. 152 * @param name The name of this HTML block as a <strong>String</strong>. 153 */ 154 public HTMLBlock(String name) { 155 this.data = new StringBuffer(""); 156 this.name = name; 157 this.original = true; 158 } 159 /** Copy constructor takes the HTML block and its name as extracted from the template file. 160 * @param name The name of this HTML block as a <strong>String</strong>. 161 * @param raw The raw HTML code as a <strong>String</strong>. 162 */ 163 public HTMLBlock(String name, String raw) { 164 this.data = new StringBuffer(raw); 165 this.name = name; 166 this.original = false; 167 } 168 /** Append this text to the end of the current block. 169 * @param text The <strong>String</strong> to add. 170 */ 171 public void addLine(String text) { 172 if(original) { 173 data.append(text); 174 data.append("\n"); 175 } 176 } 177 /** Retrieve the name of this HTML block. 178 * @return A <strong>String</strong> representing the name. 179 */ 180 public String getName() { 181 return name; 182 } 183 /** Insert a text fragment into this HTML block. 184 * @param fragment The <strong>String</strong> to insert. 185 * @return <i>true</i> if an insertion occured, <i>false</i> otherwise. 186 */ 187 public boolean insert(String fragment) { 188 if(!original) { 189 // Find the start of an insertion tag. 190 int start = data.indexOf("<$"); 191 if(start != -1) { 192 // Find the end of an insertion tag. 193 int end = data.indexOf(">", start) + 1; 194 // Determine the tag, and search for any paragraphing information. 195 String tag = data.substring(start, end); 196 if(tag.length() > 7) { 197 paragraph_tag = "<p " + tag.substring(7) + "\n"; 198 fragment = paragraph_tag + fragment + "\n</p>\n"; 199 } 200 // Substitue the fragment for the tag. 201 data.replace(start, end, fragment); 202 // Now set insertion position 203 insertion_position = start + fragment.length(); 204 return true; 320 if(paragraph == null) { 321 paragraph = new StringBuffer(line); 322 } 323 else { 324 if(paragraph.charAt(paragraph.length() - 1) != ' ' && line.charAt(0) != ' ') { 325 paragraph.append(" "); 205 326 } 206 // Its possible that we've already got enough text to output this block. Essentially this new fragment is meant to be another paragraph. Such fragments are always added immediately after the last fragment inserted. 207 else if(insertion_position != -1) { 208 if(paragraph_tag != null) { 209 fragment = paragraph_tag + fragment + "\n</p>\n"; 210 } 211 data.insert(insertion_position, fragment); 212 insertion_position = insertion_position + fragment.length(); 213 } 214 } 215 return false; 216 } 217 /** Is this block ready to written out. 218 * @return <i>true</i> if we can write this block out, <i>false</i> if text insertion points still exist. 219 */ 220 public boolean ready() { 221 return data.indexOf("<$") == -1; 222 } 223 /** Produce a copy of this block with all text insertions intact. 224 * @return A new <strong>HTMLBlock</strong>. 225 */ 226 public HTMLBlock spawn() { 227 return new HTMLBlock(name, data.toString()); 228 } 229 /** Return the HTML block within this object. 230 * @return A <strong>String</strong> containing the HTML code. 231 */ 232 public String toString() { 233 return data.toString(); 234 } 235 } 236 /** Reads in the given text file, outputing HTML by utilizing HTML blocks stored in the TemplateManager. The manager understands one extra tag <$FILE name=<filename>> which is used to indicate the presence of several output files within one input file. If this tag is not found the default name for the output file is "<text_file_name>.htm". */ 237 private class SourceManager { 238 private File text_file = null; 239 240 private FileOutputStream output_file = null; 241 242 public SourceManager(File text_file) { 243 out("Building Source Manager:"); 244 this.text_file = text_file; 245 try { 246 out(" reading from: " + text_file.getName()); 247 // Create input stream from the text file which we read a line at a time. 248 FileReader file_reader = new FileReader(text_file); 249 BufferedReader buffered_reader = new BufferedReader(file_reader); 250 HTMLBlock block = null; 251 String line = null; 252 StringBuffer paragraph = null; 253 while((line = buffered_reader.readLine()) != null) { 254 boolean done = false; 255 // What we do here depends on what state we are in and what tag or text we are reading. 256 // The first case to consider is a file open command tag. This tells us the next block of html should be written to a certain file. 257 if(line.startsWith("<$FILE")) { 258 // Close any existing output file. 259 if(output_file != null) { 260 // Write any existing block 261 if(block != null) { 262 if(paragraph != null) { 263 block.insert(paragraph.toString()); 264 paragraph = null; 265 } 266 out(" write: " + block.getName()); 267 output_file.write((block.toString()).getBytes()); 268 block = null; 269 } 270 output_file.close(); 271 output_file = null; 272 } 273 // Determine the new file name. 274 String name = line.substring(12, line.lastIndexOf(">")); 275 // Create new file. 276 output_file = new FileOutputStream(name); 277 // Dealt with 278 out(" writing to: " + name); 279 name = null; 280 done = true; 281 } 282 else if(line.startsWith("<$")) { 283 // If we have a block open its time to write it out. 284 if(block != null) { 285 if(paragraph != null) { 286 block.insert(paragraph.toString()); 287 paragraph = null; 288 } 289 // Check we have somewhere to write to. 290 if(output_file == null) { 291 output_file = new FileOutputStream(text_file.getName() + ".htm"); 292 } 293 out(" write: " + block.getName()); 294 output_file.write((block.toString()).getBytes()); 295 block = null; 296 } 297 // Now determine the new blocks name. 298 String name = line.substring(2, line.lastIndexOf(">")); 299 // Retrieve that block 300 block = template_manager.getHTMLBlock(name); 301 if(block == null) { 302 // Somethings gone wrong, most likely an unidentified block name. 303 out(" no such block: " + name); 304 } 305 name = null; 306 done = true; 307 } 308 // Its just text. However we want text in paragraph blocks. A paragraph is delimited by a "\n\n" or the start of a tag. 309 if(!done) { 310 // If the line read is "" it means we've encountered a "\n\n". 311 if(line.equals("")) { 312 // Add paragraph to open block. 313 if(block != null && paragraph != null) { 314 block.insert(paragraph.toString()); 315 paragraph = null; 316 } 317 } 318 // Any other case just add to paragraph. Ensure that there is at least one space between the two fragments 319 else { 320 if(paragraph == null) { 321 paragraph = new StringBuffer(line); 322 } 323 else { 324 if(paragraph.charAt(paragraph.length() - 1) != ' ' && line.charAt(0) != ' ') { 325 paragraph.append(" "); 326 } 327 paragraph.append(line); 328 } 329 } 330 } 331 } 327 paragraph.append(line); 328 } 329 } 330 } 331 } 332 332 // Write out any outstanding blocks. 333 if(block != null) { 334 if(paragraph != null) { 335 block.insert(paragraph.toString()); 336 paragraph = null; 337 } 338 // Check we have somewhere to write to. 339 if(output_file == null) { 340 output_file = new FileOutputStream(text_file.getName() + ".htm"); 341 } 342 out(" write: " + block.getName()); 343 output_file.write((block.toString()).getBytes()); 344 } 333 if(block != null) { 334 if(paragraph != null) { 335 block.insert(paragraph.toString()); 336 paragraph = null; 337 } 338 // Check we have somewhere to write to. 339 if(output_file == null) { 340 output_file = new FileOutputStream(text_file.getName() + ".htm"); 341 } 342 out(" write: " + block.getName()); 343 output_file.write((block.toString()).getBytes()); 344 } 345 block = null; 346 paragraph = null; 347 line = null; 348 output_file.close(); 349 output_file = null; 350 } 351 catch (Exception error) { 352 error.printStackTrace(); 353 System.err.println("An error has occured. View output.txt for more information."); 354 exit(); 355 } 356 } 357 } 358 /** Reads in a template file, extracting HTMLBlocks and storing them. It then allows the program to retrieve a block by its name and process it accordingly. */ 359 private class TemplateManager 360 extends HashMap { 361 /** Constructor. 362 * @param template_file The <strong>File</strong> template information will be extracted from. 363 */ 364 public TemplateManager(File template_file) { 365 out("Building Template Manager:"); 366 try { 367 FileReader file_reader = new FileReader(template_file); 368 BufferedReader buffered_reader = new BufferedReader(file_reader); 369 HTMLBlock block = null; 370 String line = null; 371 while((line = buffered_reader.readLine()) != null) { 372 boolean done = false; 373 // Detect a template command. 374 if(line.startsWith("<$")) { 375 // See if this is an open block tag, text insertion tag, or a block closing tag and react appropriately. 376 if(line.startsWith("<$TEXT")) { 377 // Text tag. deal with it below. 378 } 379 else if(line.startsWith("<$/")) { 380 // Block closing tag. If we have an open block close it. 381 if(block != null) { 382 out(" parsed: " + block.getName()); 383 // Add to our hashmapping of known blocks. 384 put(block.getName(), block); 345 385 block = null; 346 paragraph = null; 347 line = null; 348 output_file.close(); 349 output_file = null; 350 } 351 catch (Exception error) { 352 error.printStackTrace(); 353 System.err.println("An error has occured. View output.txt for more information."); 354 exit(); 355 } 356 } 357 } 358 /** Reads in a template file, extracting HTMLBlocks and storing them. It then allows the program to retrieve a block by its name and process it accordingly. */ 359 private class TemplateManager 360 extends HashMap { 361 /** Constructor. 362 * @param template_file The <strong>File</strong> template information will be extracted from. 363 */ 364 public TemplateManager(File template_file) { 365 out("Building Template Manager:"); 366 try { 367 FileReader file_reader = new FileReader(template_file); 368 BufferedReader buffered_reader = new BufferedReader(file_reader); 369 HTMLBlock block = null; 370 String line = null; 371 while((line = buffered_reader.readLine()) != null) { 372 boolean done = false; 373 // Detect a template command. 374 if(line.startsWith("<$")) { 375 // See if this is an open block tag, text insertion tag, or a block closing tag and react appropriately. 376 if(line.startsWith("<$TEXT")) { 377 // Text tag. deal with it below. 378 } 379 else if(line.startsWith("<$/")) { 380 // Block closing tag. If we have an open block close it. 381 if(block != null) { 382 out(" parsed: " + block.getName()); 383 // Add to our hashmapping of known blocks. 384 put(block.getName(), block); 385 block = null; 386 done = true; 387 } 388 // Otherwise deal with it below. 389 } 390 // The last option is an open block tag. 391 else { 392 // If have a block open, close it. 393 if(block != null) { 394 out(" parsed: " + block.getName()); 395 put(block.getName(), block); 396 block = null; 397 } 398 // Now create a new block with the appropriate name. 399 String name = line.substring(2, line.indexOf(">")); 400 block = new HTMLBlock(name); 401 done = true; 402 } 403 } 404 // Otherwise its just HTML. 405 if(!done) { 406 // If we are building a block append this line. 407 if(block != null) { 408 block.addLine(line); 409 } 410 // Otherwise we just ignore it. 411 else { 412 if(line.length() > 0) { 413 out(" can't parse: " + line); 414 } 415 // Otherwise we're just chewing up blank space. 416 } 417 } 418 } 386 done = true; 387 } 388 // Otherwise deal with it below. 389 } 390 // The last option is an open block tag. 391 else { 392 // If have a block open, close it. 393 if(block != null) { 394 out(" parsed: " + block.getName()); 395 put(block.getName(), block); 396 block = null; 397 } 398 // Now create a new block with the appropriate name. 399 String name = line.substring(2, line.indexOf(">")); 400 block = new HTMLBlock(name); 401 done = true; 402 } 403 } 404 // Otherwise its just HTML. 405 if(!done) { 406 // If we are building a block append this line. 407 if(block != null) { 408 block.addLine(line); 409 } 410 // Otherwise we just ignore it. 411 else { 412 if(line.length() > 0) { 413 out(" can't parse: " + line); 414 } 415 // Otherwise we're just chewing up blank space. 416 } 417 } 418 } 419 419 // If we have a block open close it. 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 420 if(block != null) { 421 out(" parsed: " + block.getName()); 422 put(block.getName(), block); 423 } 424 line = null; 425 block = null; 426 buffered_reader.close(); 427 buffered_reader = null; 428 file_reader.close(); 429 file_reader = null; 430 } 431 catch (Exception error) { 432 error.printStackTrace(); 433 System.err.println("An error has occured. View output.txt for more information."); 434 exit(); 435 } 436 } 437 /** Retrieve the block indicated by the given name. 438 * @param name The name of the block as a <strong>String</strong>. 439 * @return The <strong>HTMLBlock</strong> asked for or <i>null</i> if no such block. 440 */ 441 public HTMLBlock getHTMLBlock(String name) { 442 HTMLBlock block = (HTMLBlock) get(name); 443 // Don't return the original, but a copy. 444 if(block != null) { 445 block = block.spawn(); 446 } 447 return block; 448 } 449 } 450 450 } -
trunk/gli/src/org/greenstone/gatherer/util/HTMLStringTokenizer.java
r4293 r4364 54 54 /** This class functions much like a <strong>StringTokenizer</strong> in that it tokenizes a long string into tokens, however this tokenizer cleverly notices HTML formatting tags. */ 55 55 public class HTMLStringTokenizer { 56 57 58 59 60 61 62 63 64 65 56 /** The current position in the source string. */ 57 private int pos = 0; 58 /** The current token, usually created by the last nextToken call. */ 59 private String current = null; 60 /** The previous token. */ 61 private String previous = null; 62 /** The string to be tokenized, including any HTML markup. */ 63 private String source = null; 64 /** Constructor. 65 * @param source The source <strong>String</strong> to be tokenized. 66 66 */ 67 68 69 70 71 72 67 public HTMLStringTokenizer(String source) { 68 this.source = source; 69 // Parse the first token. 70 parseToken(); 71 } 72 /** Determines if there are still tokens remaining unparsed in the source. 73 73 * @return A <strong>boolean</strong> which is <i>true</i> if there are more tokens. 74 74 */ 75 76 77 78 79 80 81 75 public boolean hasMoreTokens() { 76 if(current != null && current.length() > 0) { 77 return true; 78 } 79 return false; 80 } 81 /** Determines if the tag currently being returned by sameToken is a tag. 82 82 * @return A <strong>boolean</strong> indicating if the token is a tag. 83 83 */ 84 85 86 87 88 89 90 84 public boolean isTag() { 85 if(previous.startsWith("<") && previous.endsWith(">")) { 86 return true; 87 } 88 return false; 89 } 90 /** Retrieves the next token. 91 91 * @return A <strong>String</strong> representing the token. 92 92 */ 93 94 95 96 97 98 99 100 93 public String nextToken() { 94 previous = current; 95 // Get the next token. 96 parseToken(); 97 // Return previous. 98 return previous; 99 } 100 /** Repeats the result of the last <i>nextToken()</i>. 101 101 * @return A <strong>String</strong> representing the token. 102 102 */ 103 104 105 106 103 public String sameToken() { 104 return previous; 105 } 106 /** Parses the next token and stores it in current. 107 107 */ 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 108 private void parseToken() { 109 boolean found = false; 110 boolean tag = false; 111 boolean text = false; 112 // Reset current 113 current = ""; 114 // Parse away 115 dumpWhiteSpace(); 116 while(pos < source.length() && !found) { 117 char c = (char)source.charAt(pos); 118 if(!tag && !text) { 119 if(c == '<') { 120 tag = true; 121 } 122 else { 123 text = true; 124 } 125 current = current + c; 126 } 127 127 // Reading a tag. Watch only for '>'. 128 129 130 131 132 133 128 else if(tag) { 129 if(c == '>') { 130 found = true; 131 } 132 current = current + c; 133 } 134 134 // Reading text. Watch for ' ' and '<'. Rollback '<'. 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 135 else if(text) { 136 if(c == ' ') { 137 found = true; 138 } 139 else if(c == '<') { 140 found = true; 141 pos--; 142 } 143 else { 144 current = current + c; 145 } 146 } 147 pos++; 148 } 149 } 150 /** Method to ignore whitespace in the source. 151 151 */ 152 153 154 155 156 152 private void dumpWhiteSpace() { 153 while(pos < source.length() && source.charAt(pos) == ' ') { 154 pos++; 155 } 156 } 157 157 158 159 160 161 162 163 158 static public void main(String args[]) { 159 String init = "<HTML>Where material to be imported is found. Defaults to <i>GSDLHOME/collection/col_name/gimport</i></HTML>"; 160 ///ystem.err.println("Before: " + init); 161 String result = Utility.formatHTMLWidth(init, 40); 162 ///ystem.err.println("After: " + result); 163 } 164 164 } -
trunk/gli/src/org/greenstone/gatherer/util/HashMap3D.java
r4293 r4364 57 57 /** Provides a HashMap implementation that indexes by two keys. Perfect for the storage of metadata references based on their metadata element and assigned value. */ 58 58 public class HashMap3D 59 59 extends HashMap { 60 60 61 61 private int capacity = 4; 62 62 63 64 65 63 private Object last_key_one = null; 64 private Object last_key_two = null; 65 private Object last_value = null; 66 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 67 /** Default constructor. 68 */ 69 public HashMap3D() { 70 super(); 71 } 72 /** Constructor with a specific initial capacity, as we should already have a good idea based on the number of Metadata Elements. 73 * @param capacity The initial capacity as an <i>int</i>. 74 */ 75 public HashMap3D(int capacity) { 76 super(capacity); 77 this.capacity = capacity; 78 } 79 /** Completely remove the contents of this HashMap and its child HashMaps. */ 80 public void clear() { 81 Iterator iterator = values().iterator(); 82 while(iterator.hasNext()) { 83 HashMap inner_mapping = (HashMap) iterator.next(); 84 inner_mapping.clear(); 85 inner_mapping = null; 86 } 87 iterator = null; 88 super.clear(); 89 } 90 /** Determine if this hash map contains an entry for the given keys. Also cache this entry because theres a good chance the next get call will ask for this entry. 91 * @param key_one The first key as an <strong>Object</strong>. 92 * @param key_two The second key as an <strong>Object</strong>. 93 * @return <i>true</i> if such an entry exists, <i>false</i> otherwise. 94 */ 95 public boolean contains(Object key_one, Object key_two) { 96 boolean result = false; 97 // Retrieve the hash mapping at key_one. 98 HashMap map = (HashMap) get(key_one); 99 // If there is such a map then retrieve the value at key_two. 100 if(map != null) { 101 last_value = map.get(key_two); 102 if(last_value != null) { 103 last_key_one = key_one; 104 last_key_two = key_two; 105 result = true; 106 } 107 } 108 return result; 109 } 110 110 111 112 113 114 115 116 117 118 119 120 121 111 /** Retrieve an entry from this three dimensional hash mapping. 112 * @param key_one The first key as an <strong>Object</strong>. 113 * @param key_two The second key as an <strong>Object</strong>. 114 * @return The value <strong>Object</strong> located at (key_one, key_two), or <i>null</i> if no such value. 115 */ 116 public Object get(Object key_one, Object key_two) { 117 Object result = null; 118 if(key_one.equals(last_key_one) && key_two.equals(last_key_two)) { 119 result = last_value; 120 } 121 else { 122 122 // Retrieve the hash mapping at key_one. 123 123 HashMap map = (HashMap) get(key_one); 124 124 // If there is such a map then retrieve the value at key_two. 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 125 if(map != null) { 126 result = map.get(key_two); 127 if(result != null) { 128 last_key_one = key_one; 129 last_key_two = key_two; 130 last_value = result; 131 } 132 } 133 } 134 return result; 135 } 136 /** -SPECIAL - Attempts to retrieve a previous instance of metadata with the same parameters as the given one. If such a metadata is found, it is returned, otherwise the given metadata is added then returned as being the first unique instance. 137 * @param metadata The <strong>Metadata</strong> for whom we are trying to find the first unique instance. 138 * @return The first unique instance of the target <strong>Metadata</strong> which may in fact be the same metadata given as a paramater. 139 */ 140 public Metadata locate(Metadata metadata) { 141 Metadata result = null; 142 if(metadata != null) { 143 143 // Locate the appropriate value->metadata hashmap. 144 145 146 147 148 149 150 144 String element_name = metadata.getElement().toString(); 145 HashMap inner_mapping = (HashMap) get(element_name); 146 if(inner_mapping == null) { 147 inner_mapping = new HashMap(4); // Small initial capacity. 148 put(element_name, inner_mapping); 149 } 150 element_name = null; 151 151 // Locate the appropriate metadata 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 152 String value_name = metadata.getValueNode().getFullPath(); 153 result = (Metadata) inner_mapping.get(value_name); 154 if(result == null) { 155 result = metadata; 156 inner_mapping.put(value_name, metadata); 157 } 158 value_name = null; 159 inner_mapping = null; 160 } 161 return result; 162 } 163 /** Put an entry into this three dimensional hash mapping. 164 * @param key_one The first key, to store this value under, as an <strong>Object</strong>. 165 * @param key_two The second key, to store this value under, as an <strong>Object</strong>. 166 * @param value The value <strong>Object</strong> itself. 167 */ 168 public void put(Object key_one, Object key_two, Object value) { 169 // Retrieve the hash mapping at key_one, or if none exists create one. 170 HashMap map = (HashMap) get(key_one); 171 if(map == null) { 172 map = new HashMap(capacity); 173 put(key_one, map); 174 } 175 // Now add the value to this mapping. 176 map.put(key_two, value); 177 } 178 178 } -
trunk/gli/src/org/greenstone/gatherer/util/MED.java
r4293 r4364 57 57 */ 58 58 public class MED { 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 59 //**************************** 60 // Get minimum of three values 61 //**************************** 62 static private int Minimum (int a, int b, int c) { 63 int mi = a; 64 if (b < mi) { 65 mi = b; 66 } 67 if (c < mi) { 68 mi = c; 69 } 70 return mi; 71 } 72 //***************************** 73 // Compute Levenshtein distance 74 //***************************** 75 static public int LD (String s, String t) { 76 int d[][]; // matrix 77 int n; // length of s 78 int m; // length of t 79 int i; // iterates through s 80 int j; // iterates through t 81 char s_i; // ith character of s 82 char t_j; // jth character of t 83 int cost; // cost 84 // Step 1 85 // Set n to be the length of s. 86 // Set m to be the length of t. 87 // If n = 0, return m and exit. 88 // If m = 0, return n and exit. 89 // Construct a matrix containing 0..m rows and 0..n columns. 90 n = s.length (); 91 m = t.length (); 92 if (n == 0) { 93 return m; 94 } 95 if (m == 0) { 96 return n; 97 } 98 d = new int[n+1][m+1]; 99 // Step 2 100 // Initialize the first row to 0..n. 101 // Initialize the first column to 0..m. 102 for (i = 0; i <= n; i++) { 103 d[i][0] = i; 104 } 105 for (j = 0; j <= m; j++) { 106 d[0][j] = j; 107 } 108 // Step 3 109 // Examine each character of s (i from 1 to n). 110 for (i = 1; i <= n; i++) { 111 s_i = s.charAt (i - 1); 112 112 // Step 4 113 113 // Examine each character of t (j from 1 to m). 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 114 for (j = 1; j <= m; j++) { 115 t_j = t.charAt (j - 1); 116 // Step 5 117 // If s[i] equals t[j], the cost is 0. 118 // If s[i] doesn't equal t[j], the cost is 1. 119 if (s_i == t_j) { 120 cost = 0; 121 } 122 else { 123 cost = 1; 124 } 125 // Step 6 126 // Set cell d[i,j] of the matrix equal to the minimum of: 127 // a. The cell immediately above plus 1: d[i-1,j] + 1. 128 // b. The cell immediately to the left plus 1: d[i,j-1] + 1. 129 // c. The cell diagonally above and to the left plus the cost: d[i-1,j-1] + cost. 130 d[i][j] = Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost); 131 } 132 } 133 // Step 7 134 // After the iteration steps (3, 4, 5, 6) are complete, the distance is found in cell d[n,m]. 135 int result = d[n][m]; 136 d = null; 137 return result; 138 } 139 139 } -
trunk/gli/src/org/greenstone/gatherer/util/MetadataXML.java
r4296 r4364 57 57 58 58 public class MetadataXML { 59 60 61 62 63 64 65 66 59 //private boolean can_wait = true; 60 private Gatherer gatherer = null; 61 private Hashtable known_indexes = null; 62 //private int spare_processes = 25; 63 //private MetadataXML mummy = null; 64 private SaveProgressDialog spd = null; 65 //private Vector complete = null; 66 //private Vector processes = null; 67 67 68 68 static public String METADATA_FILE = "metadata.xml"; 69 69 70 70 // not actually used anywhere 71 71 /* static public IXMLElement read(String filename) { 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 72 IXMLElement xml = null; 73 IXMLParser parser = null; 74 IXMLReader reader = null; 75 try { 76 parser = XMLParserFactory.createDefaultXMLParser(); 77 File in_file = new File(filename); 78 FileReader in_reader = new FileReader(in_file); 79 BufferedReader in = new BufferedReader(in_reader, Utility.BUFFER_SIZE); 80 String content = ""; 81 String next_line = null; 82 while((next_line = in.readLine()) != null) { 83 // Throw away the document type, as I don't want to deal 84 // with dtd's. If someone has tampered with these files to 85 // the point that this parser can't read them then too bad. 86 if(!next_line.startsWith("<!DOCTYPE")) { 87 content = content + next_line; 88 } 89 } 90 in.close(); 91 reader = StdXMLReader.stringReader(content); 92 parser.setReader(reader); 93 xml = (IXMLElement) parser.parse(); 94 } 95 catch (Exception e) { 96 } 97 return xml; 98 98 } 99 99 */ 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 100 public static void write(Gatherer gatherer, String etc_dir) { 101 if(gatherer != null && gatherer.c_man != null && gatherer.c_man.getCollection() != null && gatherer.c_man.getCollection().msm != null) { 102 Vector elements = gatherer.c_man.getCollection().msm.getElements(true); 103 for(int i = 0; i < elements.size(); i++) { 104 ElementWrapper element = (ElementWrapper)elements.get(i); 105 ///ystem.err.print("Checking " + element + " for HFile: "); 106 GValueModel model = gatherer.c_man.getCollection().msm.getValueTree(element); 107 if(model != null && (element.getNamespace().equals(MetadataSetManager.HIDDEN) || model.isHierarchy())) { 108 ///ystem.err.println("Found. Writing file."); 109 write(model, gatherer.c_man.getCollection().msm, etc_dir); 110 } 111 else { 112 ///ystem.err.println("No file found."); 113 } 114 } 115 } 116 } 117 117 118 119 120 121 122 118 private String getHIndex(Gatherer gatherer, GValueModel model, String value) { 119 String index = null; 120 index = (String) known_indexes.get(value); 121 if(index == null) { 122 index = model.getHIndex(value); 123 123 ///ystem.err.println("Adding to known indexes: " + value + " -> " + index); 124 125 126 127 124 known_indexes.put(value, index); 125 } 126 return index; 127 } 128 128 129 130 131 132 133 134 135 136 137 138 129 static private String safe(String unsafe) { 130 String safe_str = ""; 131 for(int i = 0; i < unsafe.length(); i++) { 132 char c = unsafe.charAt(i); 133 if(c != ' ') { 134 safe_str = safe_str + c; 135 } 136 } 137 return safe_str; 138 } 139 139 140 141 142 143 144 145 140 static private void write(Writer w, String text) 141 throws Exception { 142 text = text + "\r\n"; 143 char buffer[] = text.toCharArray(); 144 w.write(buffer, 0, buffer.length); 145 } 146 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 147 static public void write(GValueModel model, MetadataSetManager msm, String etc_dir) { 148 try { 149 File out_file = new File(etc_dir + model.toString() + ".txt"); 150 FileOutputStream fos = new FileOutputStream(out_file); 151 OutputStreamWriter osw = new OutputStreamWriter(fos); 152 BufferedWriter bw = new BufferedWriter(osw, Utility.BUFFER_SIZE); 153 Vector all_values = model.traverseTree(); 154 for(int i = 0; i < all_values.size(); i++) { 155 GValueNode node = (GValueNode)all_values.get(i); 156 TreePath path = new TreePath(node.getPath()); 157 String value = GValueTree.formatPath(null, path, true); 158 String index = model.getHIndex(value); 159 String alias = node.getAlias(index); 160 if(value.indexOf("\\") != -1) { 161 value = value.substring(value.lastIndexOf("\\") + 1); 162 } 163 write(bw, index + "\t\"" + alias + "\"\t\"" + Utility.stripNL(value) + "\""); 164 } 165 165 // Very important we do this, or else buffer may not have 166 166 // flushed. 167 168 169 170 171 172 173 167 bw.flush(); 168 bw.close(); 169 } 170 catch(Exception error) { 171 error.printStackTrace(); 172 } 173 } 174 174 175 176 175 static final private long showTime(String message, long time) { 176 if(time == -1) { 177 177 ///ystem.err.println(message + System.currentTimeMillis()); 178 179 178 } 179 else { 180 180 ///ystem.err.println(message + (System.currentTimeMillis() - time)); 181 182 183 181 } 182 return System.currentTimeMillis(); 183 } 184 184 } 185 186 -
trunk/gli/src/org/greenstone/gatherer/util/MutableComboBoxEntry.java
r4293 r4364 2 2 3 3 public interface MutableComboBoxEntry { 4 4 public void setText(String text); 5 5 } -
trunk/gli/src/org/greenstone/gatherer/util/SwingWorker.java
r4293 r4364 101 101 public SwingWorker() { 102 102 final Runnable doFinished = new Runnable() { 103 104 103 public void run() { finished(); } 104 }; 105 105 106 106 Runnable doConstruct = new Runnable() { 107 108 109 110 111 112 113 107 public void run() { 108 try { 109 setValue(construct()); 110 } 111 finally { 112 threadVar.clear(); 113 } 114 114 115 116 117 115 SwingUtilities.invokeLater(doFinished); 116 } 117 }; 118 118 119 119 Thread t = new Thread(doConstruct); -
trunk/gli/src/org/greenstone/gatherer/util/SynchronizedTreeModelTools.java
r4293 r4364 35 35 */ 36 36 public class SynchronizedTreeModelTools { 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 37 /** Adds an insertNodeInto model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above. */ 38 static final public Runnable insertNodeInto(final DefaultTreeModel model, final MutableTreeNode parent, final MutableTreeNode target_node) { 39 final Runnable doInsertNodeInto = new Runnable() { 40 public void run() { 41 int index = -1; 42 int pos = 0; 43 while(index == -1 && pos < parent.getChildCount()) { 44 TreeNode node = parent.getChildAt(pos); 45 int result = 0; 46 ///ystem.err.println("Compare " + target_node + " to " + node); 47 if((target_node.isLeaf() && node.isLeaf()) || (!target_node.isLeaf() && !node.isLeaf())) { 48 result = target_node.toString().toLowerCase().compareTo(node.toString().toLowerCase()); 49 } 50 else if(target_node.isLeaf()) { 51 result = -1; 52 } 53 else { 54 result = 1; 55 } 56 if(result > 0) { 57 ///ystem.err.println("Keep searching..."); 58 pos++; 59 } 60 else { 61 ///ystem.err.println("Found!"); 62 index = pos; 63 } 64 } 65 if(index == -1) { 66 index = parent.getChildCount(); 67 } 68 model.insertNodeInto(target_node, parent, index); 69 } 70 }; 71 try { 72 72 //SwingUtilities.invokeLater(doInsertNodeInto); 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 73 SwingUtilities.invokeAndWait(doInsertNodeInto); 74 } 75 catch (Exception e) { 76 e.printStackTrace(); 77 } 78 // If (and only if) we've thrown an error because we tried to invoke the runnable task and wait, when we are in the AWTEvent thread already, then try agin but with an invoke later. 79 catch (java.lang.Error error) { 80 if(error.toString().equals("java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread")) { 81 SwingUtilities.invokeLater(doInsertNodeInto); 82 } 83 } 84 return doInsertNodeInto; 85 } 86 /** Adds a removeNodeFromParent model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above. 87 * @param model The <strong>GTreeModel</strong> we want to remove the node from. 88 * @param target_node The <strong>GTreeNode</strong> to remove. 89 */ 90 static final public void removeNodeFromParent(final DefaultTreeModel model, final MutableTreeNode target_node) { 91 ///ystem.err.println("Remove " + target_node + " from parent in model " + model); 92 final Runnable doRemoveNodeFromParent = new Runnable() { 93 public void run() { 94 model.removeNodeFromParent(target_node); 95 } 96 }; 97 try { 98 98 //SwingUtilities.invokeLater(doRemoveNodeFromParent); 99 100 101 102 99 SwingUtilities.invokeAndWait(doRemoveNodeFromParent); 100 } 101 catch (Exception e) { 102 e.printStackTrace(); 103 103 ///ystem.err.println(e); 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 104 } 105 // If we've thrown an error because we tried to invoke the runnable task and wait, when we are in the AWTEvent thread already, then try agin but with an invoke later. 106 catch (java.lang.Error error) { 107 if(error.toString().equals("java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread")) { 108 SwingUtilities.invokeLater(doRemoveNodeFromParent); 109 } 110 } 111 } 112 /** Adds a replaceNode model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above. 113 * @param model The <strong>GTreeModel</strong> we want to change. 114 * @param old_node The <strong>GTreeNode</strong> to replace. 115 * @param new_node The <strong>GTreeNode</strong> to replace it with. 116 */ 117 static final public void replaceNode(final DefaultTreeModel model, final MutableTreeNode old_node, final MutableTreeNode new_node) { 118 final Runnable doReplaceNode = new Runnable() { 119 public void run() { 120 MutableTreeNode parent = (MutableTreeNode) old_node.getParent(); 121 if(parent != null) { 122 int index = parent.getIndex(old_node); 123 model.removeNodeFromParent(old_node); 124 model.insertNodeInto(new_node, parent, index); 125 } 126 parent = null; 127 } 128 }; 129 try { 130 130 //SwingUtilities.invokeLater(doReplaceNode); 131 132 133 134 135 136 131 SwingUtilities.invokeAndWait(doReplaceNode); 132 } 133 catch (Exception e) { 134 e.printStackTrace(); 135 } 136 } 137 137 } -
trunk/gli/src/org/greenstone/gatherer/util/TreeSynchronizer.java
r4293 r4364 40 40 */ 41 41 final public class TreeSynchronizer 42 43 44 45 46 47 48 49 42 extends Vector 43 implements TreeExpansionListener, TreeSelectionListener { 44 /** <i>true</i> if we should temporarily ignore further events, most likely because we know our actions are causing them. */ 45 private boolean ignore; 46 /** A list of tree selection listeners. */ 47 private Vector selection_listeners = new Vector(); 48 /** Add a new tree to the synchronization list of trees to be synchronized. 49 * @param tree The lastest victim, a <strong>JTree</strong>. 50 50 */ 51 52 53 54 55 51 public void add(JTree tree) { 52 super.add(tree); 53 tree.addTreeExpansionListener(this); 54 //tree.addTreeSelectionListener(this); 55 } 56 56 57 58 59 60 61 62 57 /** We allow the Gatherer to add tree listeners to this class, as it persists between collection changes transparently. Thus there is no need to reattach listeners everytime the collection changes. */ 58 public void addTreeSelectionListener(TreeSelectionListener listener) { 59 if(!selection_listeners.contains(listener)) { 60 selection_listeners.add(listener); 61 } 62 } 63 63 64 64 /** Called whenever an item in the tree has been collapsed. 65 65 * @param event A <strong>TreeExpansionEvent</strong> containing information about the event. 66 66 */ 67 68 69 67 public void treeCollapsed(TreeExpansionEvent event) { 68 if(!ignore) { 69 ignore = true; 70 70 // Collapse that path in all registered trees. 71 72 73 74 75 76 77 78 79 80 81 82 71 JTree tree = (JTree)event.getSource(); 72 TreePath path = event.getPath(); 73 for(int i = size(); i != 0; i--) { 74 JTree sibling = (JTree) get(i - 1); 75 if(!sibling.equals(tree)) { 76 sibling.collapsePath(path); 77 } 78 } 79 ignore = false; 80 } 81 } 82 /** Called whenever an item in the tree has been expanded. 83 83 * @param event A <strong>TreeExpansionEvent</strong> containing information about the event. 84 84 */ 85 86 87 85 public void treeExpanded(TreeExpansionEvent event) { 86 if(!ignore) { 87 ignore = true; 88 88 // Expand that path in all registered trees. 89 90 91 92 93 94 95 96 97 98 99 100 89 JTree tree = (JTree)event.getSource(); 90 TreePath path = event.getPath(); 91 for(int i = size(); i != 0; i--) { 92 JTree sibling = (JTree) get(i - 1); 93 if(!sibling.equals(tree)) { 94 sibling.expandPath(path); 95 } 96 } 97 ignore = false; 98 } 99 } 100 /** Called whenever the one of the trees selection changes. 101 101 * @param event A <strong>TreeSelectionEvent</strong> containing information about the event. 102 102 */ 103 104 105 103 public void valueChanged(TreeSelectionEvent event) { 104 if(!ignore) { 105 ignore = true; 106 106 // Recover the tree that is the source. 107 107 JTree tree = (JTree) event.getSource(); 108 108 // Brute Force approach. 109 109 // Extract the currently selected paths. 110 110 TreePath paths[] = tree.getSelectionPaths(); 111 111 // Then for every registered tree, that isn't this one, ensure those paths are selected. 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 112 for(int i = size(); paths != null && i != 0; i--) { 113 JTree sibling = (JTree) get(i - 1); 114 if(!sibling.equals(tree)) { 115 // One last thing to do. If this is actually a DragTree we are dealing with we have to tell it to set the selection values immediately, not wait for the clear until it is sure it is no the pre-cursor to a drag. 116 if(sibling instanceof DragTree) { 117 DragTree gtree = (DragTree)sibling; 118 gtree.setImmediate(true); 119 gtree.setSelectionPaths(paths); 120 // I'm going to ensure that the last selected path is visible. 121 gtree.scrollPathToVisible(paths[paths.length - 1]); 122 gtree.setImmediate(false); 123 } 124 else { 125 sibling.setSelectionPaths(paths); 126 sibling.scrollPathToVisible(paths[paths.length - 1]); 127 } 128 } 129 } 130 130 // Pass on message to all listeners. 131 132 133 134 135 136 131 for(int i = 0; i < selection_listeners.size(); i++) { 132 ((TreeSelectionListener) selection_listeners.get(i)).valueChanged(event); 133 } 134 ignore = false; 135 } 136 } 137 137 } -
trunk/gli/src/org/greenstone/gatherer/util/Utility.java
r4344 r4364 53 53 */ 54 54 public class Utility { 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 /** Definition of an important directory name, in this case the location of help documentation. */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 55 /** The default size of a gatherer progress bar, in either the download view or the build view. */ 56 static final public Dimension PROGRESS_BAR_SIZE = new Dimension(580,65); 57 /** The number of kilobytes to use as a io buffer. */ 58 static final public int FACTOR = 1; 59 /** The size of the io buffer, calculated as FACTOR * 1024. */ 60 static final public int BUFFER_SIZE = FACTOR * 1024; 61 /** Definition of an important directory name, in this case the archive directory for the collection. */ 62 static final public String ARCHIVE_DIR = "archives" + File.separator; 63 /** Definition of an important directory name, in this case the base dir, or the working directory of the Gatherer. */ 64 static final public String BASE_DIR = System.getProperty("user.dir") + File.separator; 65 /** Definition of an important directory name, in this case the building directory for the collection. */ 66 static final public String BUILD_DIR = "building" + File.separator; 67 /** Definition of an important directory name, in this case the public web cache for the Gatherer. */ 68 static final public String CACHE_DIR = BASE_DIR + "cache" + File.separator; 69 static final public String CFG_COLLECTIONMETA_COLLECTIONNAME = "collectionmeta collectionname"; 70 static final public String CFG_COLLECTIONMETA_COLLECTIONEXTRA = "collectionmeta collectionextra"; 71 static final public String CFG_COLLECTIONMETA_ICONCOLLECTION = "collectionmeta iconcollection"; 72 static final public String CFG_CLASSIFY = "classify"; 73 static final public String CFG_CLASSIFY_BUTTONNAME = "-buttonname"; 74 static final public String CFG_CLASSIFY_HFILE = "-hfile"; 75 static final public String CFG_CLASSIFY_METADATA = "-metadata"; 76 static final public String CFG_CLASSIFY_SORT = "-sort"; 77 static final public String CFG_CREATOR = "creator"; 78 static final public String CFG_FORMAT = "format"; 79 static final public String CFG_MAINTAINER = "maintainer"; 80 /** Definition of an important directory name, in this case the parent directory of all the collections in the gsdl. */ 81 static final public String COL_DIR = "collect" + File.separator; 82 static final public String COLLECTION_DEMO = "greenstone demo"; 83 static final public String COLLECTION_DEMO_DIRECTORY = "demo" + File.separator; 84 static final public String COLLECTION_DLS = "Development Library Subset"; 85 static final public String COLLECTION_DLS_DIRECTORY = "dls" + File.separator; 86 static final public String COLLECTION_TREE = "Collection"; 87 /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */ 88 static final public String CONFIG_DIR = "etc" + File.separator + "collect.cfg"; 89 /** The default file name for the urls missing any file. */ 90 static final public String DEFAULT_FILE = "index.html"; 91 static final public String DEFAULT_NAMESPACE = "gsp"; 92 /** The default protocol header for those urls missing any protocol. */ 93 static final public String DEFAULT_PROTOCOL = "http://"; 94 /** The default dictionary to load. */ 95 static final public String DICTIONARY = "dictionary"; 96 static final public String DLS_MDS = "dls.mds"; 97 static final public String ENGLISH_VALUE = "en"; 98 /** Definition of an important directory name, in this case the etc (or extra information) directory for the collection. */ 99 static final public String ETC_DIR = "etc" + File.separator; 100 static final public String EXTRACTED_METADATA_NAMESPACE = "ex"; 101 /** The location of the default greenstone metadata file. */ 102 static final public String GREENSTONEDIRECTORYMETADATA_TEMPLATE = "xml/metadata.xml"; 103 /** Definition of an important directory name, in this case the private web cache directory for the collection. */ 104 static final public String GCACHE_DIR = "gcache" + File.separator; 105 /** Definition of an important directory name, in this case the location of help documentation. */ 106 static final public String HELP_DIR = BASE_DIR + "help" + File.separator; 107 /** Definition of an important directory name, in this case the import directory for the collection. */ 108 static final public String IMPORT_DIR = "gimport" + File.separator; 109 /** Definition of an important directory name, in this case the index directory for the collection. */ 110 static final public String INDEX_DIR = "index" + File.separator; 111 static final public String LANGUAGE_ATTRIBUTE = "language"; 112 /** Definition of an important directory name, in this case the log directory for the collection. */ 113 static final public String LOG_DIR = "log" + File.separator; 114 /** Definition of an important directory name, in this case the location of the expected collection metadata sets.. */ 115 static final public String META_DIR = "metadata" + File.separator; // Col. Copy 116 /** Definition of an important directory name, in this case the location of the default metadata sets. */ 117 static final public String METADATA_DIR = BASE_DIR + "metadata" + File.separator; 118 static final public String METADATA_EXTRACTED = "extracted.mds"; 119 /** The location the gatherer expects to find metadata set information. */ 120 static final public String METADATA_SET_TEMPLATE = "xml" + File.separator + "template.mds"; 121 static final public String METADATA_VALUE_TEMPLATE = "xml" + File.separator + "template.mdv"; 122 static final public String METADATA_XML = "metadata.xml"; 123 static final public String NAME_ELEMENT = "Name"; 124 /** Definition of an important directory name, in this case the import directory for the collection. */ 125 static final public String OLD_IMPORT_DIR = "import" + File.separator; 126 /** The default name of the perl executable under unix. */ 127 static final public String PERL_EXECUTABLE_UNIX = "perl"; 128 /** The default name of the perl executable under windows. */ 129 static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe"; 130 /** The name of the Gatherer. */ 131 static final public String PROGRAM_NAME = "The Librarian Interface"; 132 /** The current version of the Gatherer. */ 133 static final public String PROGRAM_VERSION = "ver 2.0"; 134 /** Definition of an important directory name, in this case the location of the recycled files location. */ 135 static final public String RECYCLE = BASE_DIR + "recycle" + File.separator; 136 /** Definition of an important directory name, in this case the location of image and other resources. */ 137 static final public String RES_DIR = BASE_DIR + "resource" + File.separator; 138 static final public String SERVER_EXE = "server.exe"; 139 /** Definition of an important directory name, in this case the location of opening (or welcome) screen html. */ 140 static final public String WELCOME_DIR = BASE_DIR + "welcome" + File.separator; 141 static final public String WORKSPACE_TREE = "Workspace"; 142 // These are out of alphabetic order to avoid forward reference error. 143 /** The default icon to produce a 'help-icon' sized blank space before a menu entry. */ 144 static final public ImageIcon BLANK_ICON = new ImageIcon(ClassLoader.getSystemResource("images/blank.gif")); 145 /** The default error icon image. */ 146 static final public ImageIcon ERROR_ICON = new ImageIcon(ClassLoader.getSystemResource("images/error.gif")); 147 static final public ImageIcon HELP_ICON = new ImageIcon(ClassLoader.getSystemResource("images/help.gif")); 148 /** The image for a toggle button whose state is 'on'. */ 149 static final public ImageIcon ON_ICON = new ImageIcon(ClassLoader.getSystemResource("images/check.gif")); 150 /** The image for a toggle button whose state is 'off'. */ 151 static final public ImageIcon OFF_ICON = new ImageIcon(ClassLoader.getSystemResource("images/cross.gif")); 152 /** Method to turn a file from with the system file tree into a tree path for insertion into a tree. 153 * @param file The <strong>File</strong> whose tree path you are attempting to discover. 154 * @param in_col A <i>boolean</i> indicating whether we are looking for a file within a collection of not. If <i>true</i> then the tree paths head in the collection name, and no element in the path refers to the import directory. Otherwise the paths head will be one of the system roots and all traversed file locations will exist in the path. 155 * @return A <strong>TreePath</strong> which traverses the file system tree to the specified file. 156 */ 157 public static TreePath createTreePath(File file, boolean in_col) { 158 TreePath path = null; 159 // Get the absolute path of the file. 160 String abs_path = file.getAbsolutePath(); 161 while(file != null) { 162 162 // If we are looking for a node within our collection, we expect 163 163 // its path from root to be <col_name>/... without any higher 164 164 // details and without gimport. So if we encounter a gimport we 165 165 // skip to its parent, add that, then return. 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 166 if(in_col && file.getName().equals("gimport")) { 167 file = file.getParentFile(); 168 if(path == null) { 169 path = new TreePath(file.getName()); 170 } 171 else { 172 path = path.pathByAddingChild(file.getName()); 173 } 174 file = null; 175 } 176 else { 177 if(path == null) { 178 path = new TreePath(file.getName()); 179 } 180 else { 181 path = path.pathByAddingChild(file.getName()); 182 } 183 file = file.getParentFile(); 184 } 185 } 186 // Unfortunately we've created the path in reverse order so we have to 187 // reverse it. 188 Object temp[] = new Object[path.getPathCount()]; 189 for(int i = 0; i < temp.length; i++) { 190 temp[(temp.length - 1) - i] = path.getPathComponent(i); 191 } 192 return new TreePath(temp); 193 } 194 /** Decodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces "\n" with a newline. 195 * @param raw The <strong>String</strong> before decoding, read from the configuration file.. 196 * @return A <strong>String</strong> ready to be placed in a component. 197 */ 198 static public String decodeGreenstone(String raw) { 199 raw = raw.replaceAll("'", "\'"); 200 raw = raw.replaceAll(">", ">"); 201 raw = raw.replaceAll("<", "<"); 202 raw = raw.replaceAll(""", "\""); 203 raw = raw.replaceAll("'", "\'"); 204 raw = raw.replaceAll("\\\\n", "\n"); 205 return raw; 206 } 207 /** Takes a rfc2616 'safe' String and translates it back into its 'unsafe' form. Basically the native c wget decode_string() function, but without pointer stuff. If searches through the String looking for the pattern %xy where x and y are hexidecimal digits and where xy maps to a character.<BR> If x or y are not hexidecimal or % is followed by a \0 then the pattern is left as is. 208 * @param encoded The url-safe <strong>String</strong> to be decoded. 209 * @return The decoded <strong>String</strong>. 210 */ 211 public static String decodeString(String encoded) { 212 String decoded = ""; 213 for(int i = 0; i < encoded.length(); i++) { 214 if(encoded.charAt(i) == '%') { 215 if(hexidecimal(encoded.charAt(i+1)) != -1 216 && hexidecimal(encoded.charAt(i+2)) != -1) { 217 char unsafe_chr = (char) 218 ((hexidecimal(encoded.charAt(i+1)) * 16) + 219 hexidecimal(encoded.charAt(i+2))); 220 decoded = decoded + unsafe_chr; 221 i = i + 2; 222 } 223 } 224 else { 225 decoded = decoded + encoded.charAt(i); 226 } 227 } 228 return decoded; 229 } 230 /** It turns out that in Java you have to make sure a directory is empty before you delete it (much like unix I suppose), and so just like unix I'll have to set up a recursive delete function. 231 * @param file The <strong>File</strong> you want to delete. 232 * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise. 233 */ 234 static public boolean delete(File file) { 235 boolean result = true; 236 // If files a directory, delete files children. 237 if(file.isDirectory()) { 238 File files[] = file.listFiles(); 239 for(int i = 0; files != null && result && i < files.length; i++) { 240 result = delete(files[i]); 241 } 242 } 243 if(result) { 244 244 // Delete file. 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 245 return file.delete(); 246 } 247 return result; 248 } 249 /** Generate a depth first enumeration of a tree. */ 250 static public EnumeratedVector depthFirstEnumeration(TreeNode node, EnumeratedVector result) { 251 result.add(node); 252 for(int i = 0; i < node.getChildCount(); i++) { 253 depthFirstEnumeration(node.getChildAt(i), result); 254 } 255 return result; 256 } 257 /** Encodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces newlines with their escaped form. 258 * @param raw The <strong>String</strong> before encoding. 259 * @return A <strong>String</strong> which is safe to write to the configuration file. 260 */ 261 static public String encodeGreenstone(String raw) { 262 raw = raw.replaceAll("<", "<"); 263 raw = raw.replaceAll(">", ">"); 264 return raw.replaceAll("\n", "\\\\n"); 265 } 266 267 /** Using this method we can request that a certain document be written, as valid XML, to a certain output stream. This makes use of the Xerces Serialization suite, which should in no way be confused with the usual method of Serialization used by Java. */ 268 static public boolean export(Document document, String filename) { 269 return export(document, new File(filename)); 270 } 271 272 static public boolean export(Document document, File file) { 273 try { 274 OutputStream os = new FileOutputStream(file); 275 275 // Create an output format for our document. 276 277 278 279 276 OutputFormat f = new OutputFormat(document); 277 f.setIndenting(true); 278 f.setLineWidth(256); 279 f.setPreserveSpace(false); 280 280 // Create the necessary writer stream for serialization. 281 282 281 OutputStreamWriter osw = new OutputStreamWriter(os); 282 Writer w = new BufferedWriter(osw); 283 283 // Generate a new serializer from the above. 284 285 284 XMLSerializer s = new XMLSerializer(w, f); 285 s.asDOMSerializer(); 286 286 // Finally serialize the document to file. 287 287 s.serialize(document); 288 288 // And close. 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 289 os.close(); 290 return true; 291 } 292 // A file not found exception is most likely thrown because the directory the metadata.xml file is attempting to be written to no longer has any files in it. I'll add a test in GDMDocument to test for this, but if it still happens ignore it (a non-existant directory can't really have metadata added to it any way. 293 catch (FileNotFoundException fnf_exception) { 294 if(!file.getName().endsWith(METADATA_XML)) { 295 fnf_exception.printStackTrace(); 296 return false; 297 } 298 return true; 299 } 300 catch (IOException ioe) { 301 ioe.printStackTrace(); 302 return false; 303 } 304 } 305 306 /** Given a starting directory, searches for the collect.cfg file and returns it if found. 307 * @return The collect.cfg File or null if not found. 308 */ 309 static final public File findConfigFile(File start) { 310 if(start == null) { 311 return null; 312 } 313 // See if the collect.cfg files here. 314 File collect_cfg = new File(start, "collect.cfg"); 315 if(collect_cfg.exists()) { 316 return collect_cfg; 317 } 318 // Search for the existance of collect.cfg in a etc directory. 319 File etc_dir = new File(start, "etc" + File.separator + "collect.cfg"); 320 if(etc_dir.exists()) { 321 return etc_dir; 322 } 323 // Otherwise search this directories parent if its not null. 324 return findConfigFile(start.getParentFile()); 325 } 326 327 /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */ 328 static final public String BYTE_SUFFIX = " b"; 329 static final public long GIGABYTE = 1024000000l; 330 static final public String GIGABYTE_SUFFIX = " Gb"; 331 static final public long KILOBYTE = 1024l; 332 static final public String KILOBYTE_SUFFIX = " kb"; 333 static final public long MEGABYTE = 1024000l; 334 static final public String MEGABYTE_SUFFIX = " mb"; 335 static final public String formatFileLength(long length) { 336 StringBuffer result = new StringBuffer(""); 337 float number = 0f; 338 String suffix = null; 339 // Determine the floating point number and the suffix (radix) used. 340 if(length >= GIGABYTE) { 341 number = (float) length / (float) GIGABYTE; 342 suffix = GIGABYTE_SUFFIX; 343 } 344 else if(length >= MEGABYTE) { 345 number = (float) length / (float) MEGABYTE; 346 suffix = MEGABYTE_SUFFIX; 347 } 348 else if(length >= KILOBYTE) { 349 number = (float) length / (float) KILOBYTE; 350 suffix = KILOBYTE_SUFFIX; 351 } 352 else { 353 number = (float) length; 354 suffix = BYTE_SUFFIX; 355 } 356 // Create the formatted string remembering to round the number to 2.d.p. To do this copy everything in the number string from the start to the first occurance of '.' then copy two more digits. Finally search for and print anything that appears after (and including) the optional 'E' delimter. 357 String number_str = Float.toString(number); 358 char number_char[] = number_str.toCharArray(); 359 int pos = 0; 360 // Print the characters up to the '.' 361 while(number_char != null && pos < number_char.length && number_char[pos] != '.') { 362 result.append(number_char[pos]); 363 pos++; 364 } 365 if(pos < number_char.length) { 366 366 // Print the '.' and at most two characters after it 367 368 369 370 371 367 result.append(number_char[pos]); 368 pos++; 369 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) { 370 result.append(number_char[pos]); 371 } 372 372 // Search through the remaining string for 'E' 373 374 375 373 while(pos < number_char.length && number_char[pos] != 'E') { 374 pos++; 375 } 376 376 // If we still have string then we found an E. Copy the remaining string. 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 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 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 377 while(pos < number_char.length) { 378 result.append(number_char[pos]); 379 pos++; 380 } 381 } 382 // Add suffix 383 result.append(suffix); 384 // Done 385 return result.toString(); 386 } 387 388 /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified. 389 * @param text The <strong>String</strong> requiring formatting. 390 * @param width The maximum width per line as an <i>int</i>. 391 * @return A <strong>String</strong> formatted so as to have no line longer than the specified width. 392 * TODO Currently HTML formatting tags are simply removed from the text, as the effects of spreading HTML tags over a break are undetermined. To solve this we need to associate tags with a certain text token so if it gets broken on to the next line the tags go with it, or if the tags cover a sequence of words that are broken we need to close then reopen the tags. However all this is a major task and well beyond anything I have time to 'muck-round' on. 393 */ 394 static public String formatHTMLWidth(String text, int width) { 395 HTMLStringTokenizer html = new HTMLStringTokenizer(text); 396 int current_width = 0; 397 int threshold = width / 2; 398 Stack lines = new Stack(); 399 String line = ""; 400 while(html.hasMoreTokens()) { 401 String token = html.nextToken(); 402 while(token != null) { 403 if(html.isTag()) { 404 // Insert smart HTML tag code here. 405 token = null; 406 } 407 else { 408 // If the token is bigger than two thirds width, before we've even started break it down. 409 if(current_width + 1 + token.length() > width && token.length() > threshold) { 410 String prefix = token.substring(0, width - 1 - current_width); 411 token = token.substring(prefix.length()); 412 if(current_width == 0) { 413 line = line + prefix; 414 } 415 else { 416 line = line + " " + prefix; 417 } 418 lines.push(line); 419 line = ""; 420 current_width = 0; 421 } 422 // If adding the next token would push us over the maximum line width. 423 else if(current_width + 1 + token.length() > width) { 424 line = space(line, width, current_width); 425 lines.push(line); 426 line = token; 427 current_width = token.length(); 428 token = null; 429 } 430 // Otherwise we should be able to just add the token, give or take. 431 else { 432 if(current_width == 0) { 433 line = line + token; 434 current_width = token.length(); 435 } 436 else { 437 // Special case for standard punctuation which may exist after a tag like so: 438 // My name is <scratchy>Slim Shady</scratchy>. <-- Annoying punctuation. 439 if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) { 440 line = line + token; 441 current_width = current_width + 1; 442 } 443 else { 444 line = line + " " + token; 445 current_width = current_width + 1 + token.length(); 446 } 447 } 448 token = null; 449 } 450 } 451 } 452 } 453 String result = line; 454 while(!lines.empty()) { 455 result = (String)lines.pop() + "<BR>" + result; 456 } 457 // Replace ' ' with " " 458 boolean tag = false; 459 int pos = 0; 460 while(pos < result.length()) { 461 if(result.charAt(pos) == '<') { 462 tag = true; 463 } 464 else if(result.charAt(pos) == '>') { 465 tag = false; 466 } 467 else if(result.charAt(pos) == ' ' && !tag) { 468 String prefix = result.substring(0, pos); 469 String suffix = result.substring(pos + 1); 470 result = prefix + " " + suffix; 471 } 472 pos++; 473 } 474 result = "<HTML>" + result + "</HTML>"; 475 return result; 476 } 477 /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ... 478 * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item. 479 * @param raw The raw filename path <strong>String</strong>. 480 * @param width The maximum width as an <i>int</i>. 481 * @return A path <strong>String</strong> no longer than width. 482 */ 483 static public String formatPath(String key, String raw, int width) { 484 JLabel label = new JLabel(Gatherer.dictionary.get(key, raw)); 485 int position = -1; 486 while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) { 487 raw = "..." + raw.substring(position + 1); 488 label.setText(Gatherer.dictionary.get(key, raw)); 489 } 490 if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) { 491 raw = raw.substring(3); 492 } 493 return raw; 494 } 495 496 /** Method which constructs the archive directory given a certain collection. 497 * @param col_dir The location of the collection directory as a <strong>String</strong>. 498 * @return The location of the given collections archive directory, also as a <strong>String</strong>. 499 */ 500 static public String getArchiveDir(String gsdl_path, String col_name) { 501 return gsdl_path + File.separator + COL_DIR + col_name + File.separator + ARCHIVE_DIR; 502 } 503 /** Method which constructs the build directory given a certain collection. 504 * @param col_dir The location of the collection directory as a <strong>String</strong>. 505 * @return The location of the given collections build directory, also as a <strong>String</strong>. 506 */ 507 static public String getBuildDir(String col_dir) { 508 if(col_dir == null) { 509 return BASE_DIR + BUILD_DIR; 510 } 511 return col_dir + BUILD_DIR; 512 } 513 /** Builds the private cache dir by appending col_dir and 'cache'. 514 * @param col_dir A String representing the directory path of the current collection. 515 * @return A String representing the path to the private file cache within the current collection. 516 */ 517 public static String getCacheDir(String col_dir) { 518 return col_dir + GCACHE_DIR; 519 } 520 /** Method which constructs the collection directory for Greenstone. 521 * @param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>. 522 * @return The location of the collection directory, also as a <strong>String</strong>. 523 */ 524 public static String getCollectionDir(String gsdl_path) { 525 return gsdl_path + COL_DIR; 526 } 527 /** Method which constructs the configuration file given a certain collection. 528 * @param col_dir The location of the collection directory as a <strong>String</strong>. 529 * @return The location of the given collections configuration file, also as a <strong>String</strong>. 530 */ 531 static public String getConfigDir(String col_dir) { 532 return col_dir + CONFIG_DIR; 533 } 534 535 static public String getDateString() { 536 Calendar current = Calendar.getInstance(); 537 String day_name = null; 538 switch(current.get(Calendar.DAY_OF_WEEK)) { 539 case Calendar.MONDAY: day_name = "Mon"; break; 540 case Calendar.TUESDAY: day_name = "Tue"; break; 541 case Calendar.WEDNESDAY: day_name = "Wed"; break; 542 case Calendar.THURSDAY: day_name = "Thu"; break; 543 case Calendar.FRIDAY: day_name = "Fri"; break; 544 case Calendar.SATURDAY: day_name = "Sat"; break; 545 case Calendar.SUNDAY: day_name = "Sun"; break; 546 default: day_name = ""; 547 } 548 String month_name = null; 549 switch(current.get(Calendar.MONTH)) { 550 case Calendar.JANUARY: month_name = "Jan"; break; 551 case Calendar.FEBRUARY: month_name = "Feb"; break; 552 case Calendar.MARCH: month_name = "Mar"; break; 553 case Calendar.APRIL: month_name = "Apr"; break; 554 case Calendar.MAY: month_name = "May"; break; 555 case Calendar.JUNE: month_name = "Jun"; break; 556 case Calendar.JULY: month_name = "Jul"; break; 557 case Calendar.AUGUST: month_name = "Aug"; break; 558 case Calendar.SEPTEMBER: month_name = "Sep"; break; 559 case Calendar.OCTOBER: month_name = "Oct"; break; 560 case Calendar.NOVEMBER: month_name = "Nov"; break; 561 case Calendar.DECEMBER: month_name = "Dec"; break; 562 default: month_name = ""; 563 } 564 int day = current.get(Calendar.DAY_OF_MONTH); 565 int hour = current.get(Calendar.HOUR_OF_DAY); 566 int minute = current.get(Calendar.MINUTE); 567 int second = current.get(Calendar.SECOND); 568 int year = current.get(Calendar.YEAR); 569 570 return day_name + " " + month_name + " " + day + " " + year + " " + Utility.pad(String.valueOf(hour), 2, '0', true) + ":" + Utility.pad(String.valueOf(minute), 2, '0', true) + ":" + Utility.pad(String.valueOf(second), 2, '0', true); 571 } 572 573 /** Retrieves and formats the depth field of the config file to four characters. 574 * @param length The length of the desired string as an <i>int</i>. 575 * @return A <strong>String</strong> representation of the mirroring depth padded to length characters. 576 */ 577 public static String getDepthString(int length) { 578 return pad("" + Gatherer.self.config.getInt("mirroring.depth", false), length); 579 } 580 /** Method which constructs the etc directory given a certain collection. 581 * @param col_dir The location of the collection directory as a <strong>String</strong>. 582 * @return The location of the given collections etc directory, also as a <strong>String</strong>. 583 */ 584 public static String getEtcDir(String col_dir) { 585 return col_dir + ETC_DIR; 586 } 587 /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory. 588 * @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists. 589 */ 590 static public ImageIcon getImage(String filename) { 591 ImageIcon image = null; 592 try { 593 image = new ImageIcon(ClassLoader.getSystemResource("images/" + Gatherer.dictionary.get("Version") + File.separator + filename)); 594 } 595 catch(Exception error_one) { 596 try { 597 image = new ImageIcon(ClassLoader.getSystemResource("images/" + filename)); 598 } 599 catch (Exception error_two) { 600 if(Gatherer.dictionary != null) { 601 File locale_image_file = new File(RES_DIR + Gatherer.dictionary.get("Version") + File.separator + filename); 602 if(locale_image_file.exists()) { 603 image = new ImageIcon(locale_image_file.toString()); 604 } 605 } 606 if(image == null) { 607 File image_file = new File(RES_DIR + filename); 608 if(image_file.exists()) { 609 image = new ImageIcon(image_file.toString()); 610 } 611 } 612 } 613 } 614 if(image == null) { 615 image = ERROR_ICON; 616 } 617 return image; 618 } 619 620 /** Method which constructs the import directory given a certain collection. 621 * @param col_dir The location of the collection directory as a <strong>String</strong>. 622 * @return The location of the given collections import directory, also as a <strong>String</strong>. 623 */ 624 public static String getImportDir(String col_dir) { 625 return col_dir + IMPORT_DIR; 626 } 627 /** Method which constructs the index directory given a certain collection. 628 * @param col_dir The location of the collection directory as a <strong>String</strong>. 629 * @return The location of the given collections index directory, also as a <strong>String</strong>. 630 */ 631 static public String getIndexDir(String col_dir) { 632 return col_dir + INDEX_DIR; 633 } 634 /** Method which constructs the log directory given a certain collection. 635 * @param col_dir The location of the collection directory as a <strong>String</strong>. 636 * @return The location of the given collections log directory, also as a <strong>String</strong>. 637 */ 638 public static String getLogDir(String col_dir) { 639 return col_dir + LOG_DIR; 640 } 641 /** Determine this machines name. 642 * @return The name as a <strong>String</strong>. 643 */ 644 644 static public String getMachineName() { 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 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 645 try { 646 return InetAddress.getLocalHost().getHostName(); 647 } 648 catch(UnknownHostException ex) { 649 } 650 return "Unknown Machine"; 651 } 652 /** Method which constructs the metadata directory given a certain collection. 653 * @param col_dir The location of the collection directory as a <strong>String</strong>. 654 * @return The location of the given collections metadata directory, also as a <strong>String</strong>. 655 */ 656 static public String getMetadataDir(String col_dir) { 657 return col_dir + META_DIR; 658 } 659 660 661 static public File getRecycleDirectory() { 662 return new File(RECYCLE); 663 } 664 665 /** Determine whether a character is a hexidecimal one. 666 * @param chr The <i>char</i> in question. 667 * @return An <i>int</i> representing the value of the hexidecimal character or -1 if not a hexidecimal. 668 */ 669 public static int hexidecimal(char chr) { 670 switch(chr) { 671 case '0': 672 return 0; 673 case '1': 674 return 1; 675 case '2': 676 return 2; 677 case '3': 678 return 3; 679 case '4': 680 return 4; 681 case '5': 682 return 5; 683 case '6': 684 return 6; 685 case '7': 686 return 7; 687 case '8': 688 return 8; 689 case '9': 690 return 9; 691 case 'A': 692 return 10; 693 case 'B': 694 return 11; 695 case 'C': 696 return 12; 697 case 'D': 698 return 13; 699 case 'E': 700 return 14; 701 case 'F': 702 return 15; 703 default: 704 return -1; 705 } 706 } 707 708 /** A string is a valid hierarchy index if it matches '[0-9](\.[0-9])*' */ 709 static public boolean isIndex(String raw) { 710 boolean result = true; 711 for(int i = 0; result && i < raw.length(); i++) { 712 char c = raw.charAt(i); 713 if(Character.isDigit(c) || (c == '.' && (i != 0 || i != raw.length() - 1))) { 714 // Valid index 715 } 716 else { 717 result = false; 718 } 719 } 720 return result; 721 } 722 723 /** Method to determine if the host system is Microsoft Windows based. 724 * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise. 725 */ 726 public static boolean isWindows() { 727 Properties props = System.getProperties(); 728 String os_name = props.getProperty("os.name",""); 729 if(os_name.startsWith("Windows")) { 730 return true; 731 } 732 return false; 733 } 734 /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left. 735 * @param str The target <strong>String</strong> that needs to be padded. 736 * @param length The desired length of the string as an <i>int</i>. 737 * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length. 738 */ 739 public static String pad(String str, int length) { 740 return pad(str, length, ' ', true); 741 } 742 public static String pad(String str_raw, int length, char fill, boolean end) { 743 StringBuffer str = new StringBuffer(str_raw); 744 while(str.length() < length) { 745 if(end) { 746 str.insert(0, fill); 747 } 748 else { 749 str.append(fill); 750 } 751 } 752 return str.toString(); 753 } 754 755 /** Parse in a xml document from a given filename. Note that this filename may need to be resolved by the class loader, especially for template files within a jar. */ 756 static public Document parse(String filename, boolean use_classloader) { 757 File file = null; 758 if(use_classloader) { 759 try { 760 URL url = ClassLoader.getSystemResource(filename); 761 file = new File(URLDecoder.decode(url.getFile())); 762 url = null; 763 } 764 catch (Exception error) { 765 // Most likely file name. 766 file = new File("classes" + File.separator + filename); 767 //Gatherer.printStackTrace(error); 768 } 769 } 770 if(file == null) { 771 file = new File(filename); 772 } 773 return parse(file, true); 774 } 775 /** Parse in a xml document from a given file. */ 776 static public Document parse(File file) { 777 return parse(file, true); 778 } 779 /** Parse in a xml document from a given file. */ 780 static public Document parse(File file, boolean noisey) { 781 Document document = null; 782 try { 783 FileInputStream fis = new FileInputStream(file); 784 InputStreamReader isr = new InputStreamReader(fis); 785 Reader r = new BufferedReader(isr); 786 InputSource isc = new InputSource(r); 787 DOMParser parser = new DOMParser(); 788 parser.setFeature("http://xml.org/sax/features/validation", false); 789 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 790 790 // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster. 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 791 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true); 792 parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false); 793 parser.parse(isc); 794 document = parser.getDocument(); 795 isr.close(); 796 fis.close(); 797 parser = null; 798 isc = null; 799 r = null; 800 isr = null; 801 fis = null; 802 file = null; 803 } 804 catch (Exception error) { 805 if(noisey) { 806 Gatherer.printStackTrace(error); 807 } 808 } 809 return document; 810 } 811 812 /** Method to spread out a line of text so that is is justified to the given width, by attempting to widen white-spacing in a balanced way. 813 * @param original The <strong>String</strong> to justify. 814 * @param width The desired width as an <i>int</i>. 815 * @param current_width An <i>int</i> representing the current width of the string, which takes into account special characters. 816 * @return The newly justified <strong>String</strong>. 817 */ 818 static public String space(String original, int width, int current_width) { 819 // Strip trailing whitespace. 820 while(original.charAt(original.length() - 1) == ' ') { 821 original = original.substring(0, original.length() - 2); 822 } 823 int diff = width - current_width; 824 // Now add diff spaces, one at each existing space. 825 int pos = 0; 826 while(diff > 0) { 827 if(pos == original.length()) { 828 pos = 0; 829 } 830 if(original.charAt(pos) == ' ') { 831 // Insert a space. 832 String prefix = original.substring(0, pos); 833 String suffix = original.substring(pos); 834 original = prefix + " " + suffix; 835 pos = pos + 2; 836 diff--; 837 } 838 pos++; 839 } 840 return original; 841 } 842 /** Method to strip new lines and extra spaces from a string. Used to restore text that has been mangled into width formatted blocks by the DOM parser. 843 * @param raw The <strong>Strong</strong> containing the mangled text. 844 * @return A <strong>String</strong> with new lines and extra spaces removed. 845 */ 846 static public String stripNL(String raw_str) { 847 byte raw[] = raw_str.getBytes(); 848 byte formatted[] = new byte[raw.length]; 849 byte previous = '\0'; 850 int j = 0; 851 for(int i = 0; i < raw.length; i++) { 852 if(raw[i] == '\n') { 853 // Skip new lines. 854 } 855 else if(raw[i] == '\t') { 856 // Skip tabs. 857 } 858 else if(raw[i] == ' ' && raw[i] == previous) { 859 // Skip erroneous whitespace. 860 } 861 else { 862 formatted[j] = raw[i]; 863 j++; 864 } 865 previous = raw[i]; 866 } 867 byte finish[] = new byte[j]; 868 System.arraycopy(formatted, 0, finish, 0, j); 869 return new String(finish); 870 } 871 /** Trims the string text to the length specified removing end characters and adding if necessary. 872 * @param text A <strong>String</strong> which you wish to ensure is shorter than length. 873 * @param length An <i>int</i> specifying the strings maximum length after which its trimmed. 874 * @return The trimmed <strong>String</strong>. 875 */ 876 public static String trim(String text, int length) { 877 if(text.length() > length) { 878 text = text.substring(0, length); 879 text = text + "..."; 880 } 881 return text; 882 } 883 884 static public String trimCenter(String text, int length) { 885 if(text.length() > length) { 886 int half = (length - 3) / 2; 887 StringBuffer temp = new StringBuffer(text.substring(0, half)); 888 temp.append("..."); 889 temp.append(text.substring(text.length() - half)); 890 text = temp.toString(); 891 } 892 return text; 893 } 894 /** This method checks to see what registered file system root directorys are mounted, and returns only accessible ones. The exception is removable media drives (in particular floppy-disk drives) which will throw all sorts of error if we test them here. Instead they are assumed to be always accessible, but a test is conducted at the time you attempt to map them to test for actual accessibility (then at least the errors are thrown after the user tries to initiate the mapping of the drive which has no disk in it). 895 * @param roots A <strong>File[]</strong> containing all of the file system roots registered on this system. 896 * @return A filtered <strong>File[]</strong> containing only those drives that are accessible and/or are floppy-disk media drives. 897 */ 898 public static File[] validateDrives(File roots[]) { 899 Vector valid = new Vector(); 900 for(int i = 0; i < roots.length; i++) { 901 String name = roots[i].getAbsolutePath(); 902 name = name.toLowerCase(); 903 if(!name.startsWith("a:") && !name.startsWith("b:")) { 904 valid.add(roots[i]); 905 } 906 } 907 roots = new File[valid.size()]; 908 for(int i = 0; i < roots.length; i++) { 909 roots[i] = (File)valid.get(i); 910 } 911 return roots; 912 } 913 913 } 914 915 916 -
trunk/gli/src/org/greenstone/gatherer/util/WinRegistry.java
r4293 r4364 59 59 /** A constant used to interpret the return of a native function as successful. */ 60 60 private final static int NATIVE_HANDLE = 0; 61 61 /** A constant used to interpret the return of a native function as an error. */ 62 62 private final static int ERROR_CODE = 1; 63 63 /** Windows error codes. */ … … 65 65 /** HKEY_LOCAL_MACHINE address. */ 66 66 private final static int HKEY_LOCAL_MACHINE = 0x80000002; 67 68 69 70 67 /** Called to build a command string that will launch the given file in its associated program according to the windows registry. 68 * @param filename The name of a file, including the required extension, as a <strong>String</strong>. 69 * @return Another <strong>String</strong> containing a command line which, when run in a command shell, will launch an external application to view this file. 70 */ 71 71 public static String openCommand(String filename) { 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 72 if(filename.indexOf(".") != -1) { 73 String extension = filename.substring(filename.indexOf(".")); 74 try { 75 // First find the name of the file type that is associated with this extension. 76 String file_type = getValue("Software\\CLASSES\\" + extension); 77 // Then if we got a type, get the open command from its shell reference. 78 ///ystem.err.println("Searching for file-type = " + file_type); 79 if(file_type != null) { 80 String raw = getValue("Software\\CLASSES\\" + file_type + "\\shell\\open\\command"); 81 //Replace "%1" with "filename" 82 if(raw.indexOf("%1") != -1) { 83 String prefix = raw.substring(0, raw.indexOf("%1")); 84 if(!prefix.endsWith("\"")) { 85 prefix = prefix + "\""; 86 } 87 String suffix = raw.substring(raw.indexOf("%1") + 2); 88 if(!suffix.endsWith("\"")) { 89 suffix = suffix + "\""; 90 } 91 return prefix + filename + suffix; 92 } 93 // Arg. There is a windows flag called /dde, which if it exists in a command tells it to use the filename (hence the meaningful name dde eh?) 94 else if(raw.indexOf("/dde") != -1) { 95 String prefix = raw.substring(0, raw.indexOf("/dde")); 96 if(!prefix.endsWith("\"")) { 97 prefix = prefix + "\""; 98 } 99 String suffix = raw.substring(raw.indexOf("/dde") + 4); 100 if(!suffix.endsWith("\"")) { 101 suffix = suffix + "\""; 102 } 103 return prefix + filename + suffix; 104 } 105 // No file parameter. Append the file to the command and hope for the best. 106 return raw + " \"" + filename + "\""; 107 } 108 } 109 catch (Exception error) { 110 ///ystem.err.println("No such value."); 111 //error.printStackTrace(); 112 } 113 } 114 return null; 115 } 116 /** Attempts to retrieve the value at the location given from the registry using Reflection. 117 * @param path The path to the desired key as a <strong>String</strong>. 118 * @return The value found at the indicated key as a <strong>String</strong>. 119 */ 120 private static String getValue(String path) 121 throws ClassNotFoundException, Exception, NoSuchMethodException { 122 ///ystem.err.println("Get value " + path); 123 byte[] WINDOWS_ROOT_PATH = stringToByteArray(path); 124 Class theClass = Class.forName("java.util.prefs.WindowsPreferences"); 125 int[] result = openKey(HKEY_LOCAL_MACHINE, WINDOWS_ROOT_PATH, KEY_QUERY_VALUE); 126 if (result[ERROR_CODE] != ERROR_SUCCESS) { 127 throw new Exception("Path not found!"); 128 } 129 int native_handle = result[NATIVE_HANDLE]; 130 Method m = theClass.getDeclaredMethod("WindowsRegQueryValueEx", new Class[]{int.class, byte[].class}); 131 m.setAccessible(true); 132 byte[] windows_name = toWindowsName(""); 133 Object value = m.invoke(null, new Object[]{new Integer(native_handle), windows_name}); 134 WindowsRegCloseKey(native_handle); 135 if (value == null) { 136 throw new Exception("Path found. Key not found."); 137 } 138 byte[] origBuffer = (byte[]) value; 139 byte[] destBuffer = new byte[origBuffer.length - 1]; 140 System.arraycopy(origBuffer, 0, destBuffer, 0, origBuffer.length - 1); 141 return new String(destBuffer); 142 } 143 /** Closes the reference (and hence the instantiated objects) to a certain entry within the registry. Do this or suffer massive memory leaks. 144 * @param nativeHandle A reference to the piece of memory containing an inspected windows key-value pair. 145 * @return The status code returned from closing the key, as an <i>int</i>. 146 */ 147 public static int WindowsRegCloseKey(int nativeHandle) 148 throws Exception { 149 Class theClass = Class.forName("java.util.prefs.WindowsPreferences"); 150 Method m = theClass.getDeclaredMethod("WindowsRegCloseKey", new Class[]{int.class}); 151 m.setAccessible(true); 152 Object ret = m.invoke(null, new Object[]{new Integer(nativeHandle)}); 153 return ((Integer) ret).intValue(); 154 } 155 /** Attempts to open a certain key-value pair from the windows registry, from within a certain region (or HKEY). 156 * @param hkey An <i>int</i> which is the offset for a certain group within the registry. 157 * @param windowsAbsolutePath A <i>byte[]</i> containing the path to the desired key, relative to the given HKEY. 158 * @param securityMask A mask needed to read values from the registry, as an <i>int</i>. 159 * @return An <i>int[]</i> pair, the first of which is the reference to the indicated key if no error occured, and the second of which reports the end error state of the registry open attempt. 160 */ 161 public static int[] openKey(int hkey, byte[] windowsAbsolutePath, int securityMask) 162 throws Exception { 163 Class theClass = Class.forName("java.util.prefs.WindowsPreferences"); 164 Method m = theClass.getDeclaredMethod("WindowsRegOpenKey", new Class[]{int.class, byte[].class, int.class}); 165 m.setAccessible(true); 166 Object ret = m.invoke(null, new Object[]{new Integer(hkey), windowsAbsolutePath, new Integer(securityMask)}); 167 return (int[]) ret; 168 } 169 /** Converts a string to a byte array. Duh! 170 * @param str The <strong>String</strong> to be converted. 171 * @return A <i>byte[]</i> containing orderer characters from the String. 172 */ 173 private static byte[] stringToByteArray(String str) { 174 byte[] result = new byte[str.length() + 1]; 175 for (int i = 0; i < str.length(); i++) { 176 result[i] = (byte) str.charAt(i); 177 } 178 result[str.length()] = 0; 179 return result; 180 } 181 181 182 183 184 185 186 187 188 189 182 private static String toJavaValueString(byte[] windowsNameArray) { 183 // Use modified native2ascii algorithm 184 String windowsName = byteArrayToString(windowsNameArray); 185 StringBuffer javaName = new StringBuffer(); 186 char ch; 187 for (int i = 0; i < windowsName.length(); i++) { 188 if ((ch = windowsName.charAt(i)) == '/') { 189 char next = ' '; 190 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 191 if (windowsName.length() > i + 1 && 192 (next = windowsName.charAt(i + 1)) == 'u') { 193 if (windowsName.length() < i + 6) { 194 break; 195 } else { 196 ch = (char) Integer.parseInt 197 (windowsName.substring(i + 2, i + 6), 16); 198 i += 5; 199 } 200 } else 201 if ((windowsName.length() > i + 1) && 202 ((windowsName.charAt(i + 1)) >= 'A') && (next <= 'Z')) { 203 ch = next; 204 i++; 205 } else if ((windowsName.length() > i + 1) && 206 (next == '/')) { 207 ch = '\\'; 208 i++; 209 } 210 } else if (ch == '\\') { 211 ch = '/'; 212 } 213 javaName.append(ch); 214 } 215 return javaName.toString(); 216 } 217 /** */ 218 private static String byteArrayToString(byte[] array) { 219 StringBuffer result = new StringBuffer(); 220 for (int i = 0; i < array.length - 1; i++) { 221 result.append((char) array[i]); 222 } 223 return result.toString(); 224 } 225 /** Magics the name of a certain path component from a Java String to the unique string windows requires. 226 * @param javaName The name of a path component as a <strong>String</strong>. 227 * @return A <i>byte[]</i> containing the translated string. 228 */ 229 private static byte[] toWindowsName(String javaName) { 230 StringBuffer windowsName = new StringBuffer(); 231 for (int i = 0; i < javaName.length(); i++) { 232 char ch = javaName.charAt(i); 233 if ((ch < 0x0020) || (ch > 0x007f)) { 234 throw new RuntimeException("Unable to convert to Windows name"); 235 } 236 if (ch == '\\') { 237 windowsName.append("//"); 238 } else if (ch == '/') { 239 windowsName.append('\\'); 240 } else if ((ch >= 'A') && (ch <= 'Z')) { 241 windowsName.append("/" + ch); 242 } else { 243 windowsName.append(ch); 244 } 245 } 246 return stringToByteArray(windowsName.toString()); 247 } 248 248 } -
trunk/gli/src/org/greenstone/gatherer/util/XORToggleButtonGroup.java
r4293 r4364 4 4 /** Just like any other button group, this object encapsulates several checkbox listeners, but instead of recording which are selected, or indeed what buttons it has assigned to it, this group has but one purpose: to ensure that at least one check button is always selected. */ 5 5 public class XORToggleButtonGroup 6 7 8 9 10 6 implements ActionListener { 7 /** true if we are to ignore any further change events we recieve. */ 8 private boolean ignore = false; 9 /** The current number of checkboxes that are selected. Never < 1. */ 10 private int selected_count = 0; 11 11 12 12 private JToggleButton last_button; 13 13 14 14 /** Any implementation of ActionListener includes this method so that we can be informed when an action has occured on any registered checkboxes, allowing us to roll back the action if this is the only selected checkbox remaining. 15 15 * @param event An <strong>ActionEvent</strong> containing information about the event. 16 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 17 public void actionPerformed(ActionEvent event) { 18 if(!ignore) { 19 ignore = true; 20 JToggleButton toggle_button = (JToggleButton) event.getSource(); 21 if(toggle_button.isSelected()) { 22 selected_count++; 23 } 24 else { 25 if(selected_count > 1) { 26 selected_count--; 27 } 28 else { 29 toggle_button.setSelected(true); 30 } 31 } 32 ignore = false; 33 } 34 } 35 public void add(JToggleButton toggle_button) { 36 last_button = toggle_button; 37 // If the checkbox is selected, increase our count. 38 if(toggle_button.isSelected()) { 39 selected_count++; 40 } 41 toggle_button.addActionListener(this); 42 } 43 43 44 45 46 47 48 49 44 /** Ensures that at least one option is selected. */ 45 public void establish() { 46 if(selected_count == 0 && last_button != null) { 47 last_button.setSelected(true); 48 } 49 } 50 50 } -
trunk/gli/src/org/greenstone/gatherer/valuetree/GNode.java
r4293 r4364 48 48 49 49 public class GNode 50 50 extends DefaultMutableTreeNode { 51 51 52 52 private String name = null; 53 53 54 55 56 54 public GNode(String name) { 55 this.name = name; 56 } 57 57 58 59 60 58 public String toString() { 59 return name; 60 } 61 61 } -
trunk/gli/src/org/greenstone/gatherer/valuetree/GValueModel.java
r4319 r4364 50 50 */ 51 51 public class GValueModel 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 52 extends DefaultTreeModel { 53 54 private ElementWrapper element; 55 56 public GValueModel() { 57 super(new DefaultMutableTreeNode("Temp")); 58 } 59 60 public GValueModel(String root) { 61 super(new DefaultMutableTreeNode(root)); 62 } 63 64 public GValueModel(ElementWrapper e) { 65 super(new DefaultMutableTreeNode("Temp")); 66 this.element = e; 67 // Load the template value tree document. 68 Document document = MSMUtils.getValueTreeTemplate(); 69 Element new_root = document.getDocumentElement(); 70 new_root.setAttribute("element", e.getName()); 71 root = new GValueNode(new_root); 72 } 73 74 public GValueModel(ElementWrapper e, Document document) { 75 super(new DefaultMutableTreeNode("Temp")); 76 this.element = e; 77 Element new_root = document.getDocumentElement(); 78 new_root.setAttribute("element", e.getName()); 79 root = new GValueNode(new_root); 80 } 81 82 /** Value may include path ie news/newssw */ 83 public GValueNode addValue(String value) { 84 ///ystem.err.println("Adding value to valuetree: " + value); 85 StringTokenizer tokenizer = new StringTokenizer(value, "\\"); 86 GValueNode subject = (GValueNode) root; 87 while(tokenizer.hasMoreTokens() && subject != null) { 88 String token = tokenizer.nextToken(); 89 subject = addValue(token, subject, null); 90 90 ///ystem.err.println("Found or created a node " + token); 91 92 93 94 95 96 97 98 91 } 92 return subject; 93 } 94 95 public GValueNode addValue(String value, GValueNode subject, String alias) { 96 // To add a value we must first ensure it isn't already present in -this- nodes children. The bummer is that the nice getElements functions search the whole tree so... 97 GValueNode value_node = subject.getValue(Utility.stripNL(value)); 98 if(value_node == null) { 99 99 // Now add the new value. 100 100 Document document = subject.getElement().getOwnerDocument(); 101 101 // Now we create a new subject and add it subject 102 103 104 105 106 107 108 109 110 111 112 113 102 Element new_subject = document.createElementNS("","Subject"); 103 Element new_value = document.createElementNS("","Value"); 104 new_subject.appendChild(new_value); 105 Text new_text = document.createTextNode(value); 106 new_value.appendChild(new_text); 107 if(alias != null && alias.length() > 0) { 108 Element new_alias = document.createElementNS("","Alias"); 109 new_subject.appendChild(new_alias); 110 Text new_alias_text = document.createTextNode(alias); 111 new_alias.appendChild(new_alias_text); 112 } 113 value_node = new GValueNode(new_subject); 114 114 // Figure out where this node will be inserted in subjects 115 115 // children. 116 117 118 119 120 121 122 123 124 125 126 127 116 int position = -1; 117 for(int i = 0; position == -1 && i < subject.getChildCount(); i++) { 118 Object sibling = subject.getChildAt(i); 119 int rel_pos = value.compareTo(sibling.toString()); 120 ///ystem.err.println("'"+value+"'.compareTo('"+sibling+"') = " + rel_pos); 121 if(rel_pos <= 0) { 122 position = i; 123 } 124 } 125 if(position == -1) { 126 position = subject.getChildCount(); 127 } 128 128 // Insert it. If position is still -1, append it to the end of subjects children. 129 129 ///ystem.err.println("Inserting '" + value + "' at position " + position); 130 130 insertNodeInto(value_node, subject, position); 131 131 // Inform listeners that we've changed. 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 132 Gatherer.c_man.getCollection().msm.fireValueChanged(element, null, this); 133 } 134 return value_node; 135 } 136 137 public Document getDocument() { 138 return ((GValueNode)root).getElement().getOwnerDocument(); 139 } 140 141 public ElementWrapper getElement() { 142 return element; 143 } 144 145 /** Retrieve the hindex for a certain value within the value tree. 146 * @param value The value whose index you wish to determine as a <strong>String</strong>. 147 * @return A <strong>String</strong> containing an index such as "1", "2.1" or "18.2.5". 148 */ 149 public String getHIndex(String value) { 150 ///ystem.err.println("getHIndex(" + value + ")"); 151 return getHIndex((GValueNode) root, value, null); 152 } 153 154 /** Retrieve a value node given its hierarchical reference or value. 155 * @param index The hierarchy index or value as a <strong>String</strong>. 156 */ 157 public GValueNode getValue(String index_str) { 158 ///ystem.err.println("Retrieve the value for: " + index_str); 159 GValueNode result = null; 160 if(isHierarchy() && Utility.isIndex(index_str)) { 161 161 // StringTokenize the index 162 163 162 StringTokenizer tokenizer = new StringTokenizer(index_str, "."); 163 result = (GValueNode) root; 164 164 // Using the index numbers retrieve the appropriate node. 165 166 167 168 169 170 171 172 173 174 175 176 177 178 165 try { 166 while(result != null && tokenizer.hasMoreTokens()) { 167 int index = Integer.parseInt(tokenizer.nextToken()) - 1; 168 // Retrieve the index'th child of the current result node 169 if(0 <= index && index < result.getChildCount()) { 170 result = (GValueNode) result.getChildAt(index); 171 } 172 // Otherwise we're broken. 173 else { 174 ///ystem.err.println("There is no " + index + "th childnode of " + result); 175 result = null; 176 } 177 } 178 } 179 179 // Most likely caused by parseInt throwing a wobbly. 180 181 182 183 184 185 186 187 188 180 catch (Exception error) { 181 result = null; 182 } 183 if(result != null) { 184 // Ensure result is enabled 185 result.setEnabled(true); 186 } 187 } 188 if(result == null) { 189 189 ///ystem.err.println("No existing value. Adding " + index_str); 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 312 313 314 315 316 317 318 319 320 321 322 323 190 result = addValue(index_str); 191 } 192 return result; 193 } 194 195 public boolean isHierarchy() { 196 boolean result = false; 197 // We are a hierarchy if our element says so.... 198 if(element.isHierarchy()) { 199 return true; 200 } 201 // Or if our children are actually a hierarchy. 202 for(int i = 0; i < root.getChildCount() && !result; i++) { 203 GValueNode node = (GValueNode) root.getChildAt(i); 204 if(node != null && node.getChildCount() > 0) { 205 result = true; 206 } 207 } 208 return result; 209 } 210 211 public void removeValue(GValueNode child) { 212 child.setEnabled(false); // Doesn't appear anymore. 213 SynchronizedTreeModelTools.removeNodeFromParent(this, child); 214 Gatherer.c_man.getCollection().msm.fireValueChanged(new ElementWrapper(child.getElement()), null, this); 215 } 216 217 public void removeValue(String value) { 218 // Retrieve the node to be removed. 219 GValueNode node = getValue(value); 220 // Now remove it 221 removeNodeFromParent(node); 222 } 223 224 /** Called to remove a certain value of metadata within a specific 225 * subject within a sbject hierarchy. 226 * Note that this method currently doesn't do anything. 227 * @param value A String representing the value to be removed. 228 * @param subject An Element representing the subject you wish to remove 229 * this value from. 230 */ 231 public void removeValue(String value, GValueNode subject) { 232 } 233 234 public int size() { 235 return size(root); 236 } 237 238 private int size(TreeNode current) { 239 int size = 1; 240 for(int i = 0; i < current.getChildCount(); i++) { 241 size = size + size(current.getChildAt(i)); 242 } 243 return size; 244 } 245 246 public String toString() { 247 return element.toString(); 248 } 249 250 public Vector traverseTree() { 251 Vector contents = new Vector(); 252 contents.addAll(traverseTree((GValueNode) root)); 253 contents.remove((GValueNode) root); 254 return contents; 255 } 256 257 public void updateValue(String new_value, String old_value) { 258 ///ystem.err.println("Updating '" + old_value + "' to '" + new_value + "'"); 259 StringTokenizer tokenizer = new StringTokenizer(new_value, "\\"); 260 GValueNode subject = (GValueNode) root; 261 while(tokenizer.hasMoreTokens()) { 262 String token = tokenizer.nextToken(); 263 subject = updateValue(token, old_value, subject); 264 } 265 } 266 267 /** Called to update a certain value of metadata within a specific 268 * subject within a sbject hierarchy. 269 * Note that this simply in turn calls removeValue() to deal with the 270 * old value, then addValue() to account for the new. 271 * @param new_value A String representing the updated value. 272 * @param old_value A String representing the old value. 273 * @param subject An Element representing the subject you wish to update 274 * this value in. 275 */ 276 public GValueNode updateValue(String new_value, String old_value, GValueNode subject) { 277 return addValue(new_value, subject, null); 278 } 279 280 private GValueModel copy() { 281 Element document_element = ((GValueNode) root).getElement(); 282 Document document_copy = MSMUtils.getValueTreeTemplate(); 283 Element document_copy_element = document_copy.getDocumentElement(); 284 document_copy_element.setAttribute("element", element.getName()); 285 for(Node node = document_element.getFirstChild(); node != null; node = node.getNextSibling()) { 286 if(node.getNodeName().equals("Subject")) { 287 Node node_copy = document_copy.importNode(node, true); 288 document_copy_element.appendChild(node_copy); 289 } 290 } 291 return new GValueModel(element, document_copy); 292 } 293 294 private String getHIndex(GValueNode node, String value, String index) { 295 for(int i = node.size(); i != 0; i--) { 296 GValueNode next = (GValueNode)node.get(i - 1); 297 String next_str = next.toString(); 298 if(value.startsWith(next_str)) { 299 if(index == null) { 300 index = String.valueOf(i); 301 } 302 else { 303 index = index + "." + i; 304 } 305 value = value.substring(next.toString().length()); 306 if(value.startsWith("\\")) { 307 value = value.substring(1); 308 index = getHIndex(next, value, index); 309 } 310 return index; 311 } 312 } 313 return index; 314 } 315 316 private Vector traverseTree(GValueNode node) { 317 Vector contents = new Vector(); 318 contents.add(node); 319 for(int i = 0; i < node.getChildCount(); i++) { 320 contents.addAll(traverseTree((GValueNode)node.getChildAt(i))); 321 } 322 return contents; 323 } 324 324 } 325 326 -
trunk/gli/src/org/greenstone/gatherer/valuetree/GValueNode.java
r4293 r4364 72 72 */ 73 73 public class GValueNode 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 74 extends DefaultMutableTreeNode { 75 private String element_name = null; 76 private String default_value = null; 77 public GValueNode(Element element) { 78 this.children = null; 79 this.userObject = element; 80 this.default_value = null; 81 } 82 83 public GValueNode(String element_name, String default_value) { 84 this.element_name = element_name; 85 this.default_value = default_value; 86 } 87 /** Compares two GValueNodes for ordering by using the String.compareTo method. 88 * @param sibling The <strong>Object</strong> representing the GValueNode we are comparing ourselves to. 89 * @return An <i>int</i> indicating the ordering. The value is less than zero if we are before the parameter object, zero if we are equal and greater than zero if we preceed sibling. 90 */ 91 public int compareTo(Object sibling) { 92 return toString().compareTo(sibling.toString()); 93 } 94 /** Determine if this tree node contains a child with a matching value. 95 * @param value The value we are attempting to match, as a <strong>String</strong>. 96 * @return <i>true</i> if there is a matching child node, <i>false</i> otherwise. 97 */ 98 public boolean containsValue(String value) { 99 if(default_value != null) { 100 return false; 101 } 102 return getValue(value) != null; 103 } 104 /** Returns an enumeration of the child nodes. 105 * @return An <strong>Enumeration</strong> containing the child nodes. 106 */ 107 public Enumeration children() { 108 if(default_value != null) { 109 return null; 110 } 111 if(children == null) { 112 map(); 113 } 114 return children.elements(); 115 } 116 public boolean equals(Object other) { 117 return compareTo(other) == 0; 118 } 119 120 public GValueNode get(int index) { 121 GValueNode result = null; 122 if(children != null) { 123 result = (GValueNode) children.get(index); 124 } 125 return result; 126 } 127 128 public String getAlias() { 129 // Attempt to retrieve a child node named Alias 130 Element element = (Element) userObject; 131 for(Node pos_node = element.getFirstChild(); pos_node != null; pos_node = pos_node.getNextSibling()) { 132 132 // And if we find such a node 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 133 if(pos_node.getNodeName().equals("Alias")) { 134 // Retrieve its text node 135 for(Node pos_text = pos_node.getFirstChild(); pos_text != null; pos_text = pos_text.getNextSibling()) { 136 // When we find the node return its value 137 if(pos_text.getNodeName().equals("#text")) { 138 return pos_text.getNodeValue(); 139 } 140 } 141 } 142 } 143 return ""; 144 } 145 146 public String getAlias(String index) { 147 return index; 148 } 149 150 /** Returns <I>true</I> if the reciever allows children. */ 151 public boolean getAllowsChildren() { 152 return true; 153 } 154 155 /** Returns the child <I>TreeNode</I> at index <I>childIndex</I>.*/ 156 public TreeNode getChildAt(int index) { 157 GValueNode result = null; 158 if(default_value != null) { 159 return this; 160 } 161 else { 162 if(children == null) { 163 map(); 164 } 165 for(int i = 0; index >= 0 && i < children.size(); i++) { 166 GValueNode child = (GValueNode) children.get(i); 167 if(child.getEnabled()) { 168 result = child; 169 index--; 170 } 171 } 172 } 173 return result; 174 } 175 /** Returns the number of children <I>TreeNode</I>s the reciever contains. */ 176 public int getChildCount() { 177 int size = 0; 178 if(default_value != null) { 179 size = 0; 180 } 181 else { 182 if(children == null) { 183 map(); 184 } 185 for(int i = 0; i < children.size(); i++) { 186 GValueNode child = (GValueNode) children.get(i); 187 if(child.getEnabled()) { 188 size++; 189 } 190 } 191 } 192 return size; 193 } 194 public Element getElement() { 195 return (Element) userObject; 196 } 197 public boolean getEnabled() { 198 return !(((Element)userObject).getAttribute("enabled").equals("false")); 199 } 200 201 /** Return the path to this node within the value tree model. 202 * @return A <strong>String</strong> representing the path, ie "Titles\Modern\The Green Mile" 203 */ 204 public String getFullPath() { 205 if(default_value != null) { 206 return default_value; 207 } 208 StringBuffer path = new StringBuffer(toString()); 209 GValueNode node = (GValueNode) getParent(); 210 while(node != null && !node.getElement().getNodeName().equalsIgnoreCase("AssignedValues")) { 211 path.insert(0, "\\"); 212 path.insert(0, node.toString()); 213 node = (GValueNode) node.getParent(); 214 } 215 return path.toString(); 216 } 217 218 /** Returns the index of <I>node</I> in the recievers children. If the 219 * reciever does not contain <I>node</I>, <I>-1</I> will be returned. */ 220 public int getIndex(TreeNode node) { 221 if(default_value != null) { 222 return 0; 223 } 224 if(children == null) { 225 map(); 226 } 227 return children.indexOf(node); 228 } 229 230 public String getMetadataElementName() { 231 if(default_value != null) { 232 return element_name; 233 } 234 GValueNode node = this; 235 while(node != null && !node.getElement().getNodeName().equalsIgnoreCase("AssignedValues")) { 236 node = (GValueNode)node.getParent(); 237 } 238 if(node != null) { 239 return node.getElement().getAttribute("element"); 240 } 241 return null; 242 } 243 244 public GValueNode getValue(String value) { 245 if(default_value != null) { 246 return this; 247 } 248 if(children == null) { 249 map(); 250 } 251 GValueNode result = null; 252 for(int i = 0; result == null && i < children.size(); i++) { 253 Object child = children.get(i); 254 if(child.toString().equals(value)) { 255 result = (GValueNode) child; 256 } 257 } 258 return result; 259 } 260 261 /** Adds <I>child</I> to the receiever at <I>index</I>. <I>child</I> will 262 * be messaged with setParent(). 263 */ 264 public void insert(MutableTreeNode child, int index) { 265 if(default_value != null) { 266 return; 267 } 268 if(index >= children.size()) { 269 269 // Append to children. 270 270 children.add(child); 271 271 // And to document 272 273 274 275 272 getElement().appendChild(((GValueNode)child).getElement()); 273 } 274 else { 275 GValueNode sibling = (GValueNode) children.get(index); 276 276 // Insert in children 277 277 children.add(index, child); 278 278 // And in document 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 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 279 getElement().insertBefore(((GValueNode)child).getElement(), sibling.getElement()); 280 } 281 child.setParent(this); 282 } 283 /** Returns <I>true</I> if the reciever is a leaf. */ 284 public boolean isLeaf() { 285 if(default_value != null || getChildCount() > 0) { 286 return false; 287 } 288 return true; 289 } 290 291 /** Ensure that the children nodes of this tree are instantiated, and record the initial mappings for legacy sake. 292 * @param mapping A <strong>HashMap</strong> into which to write the initial mappings. 293 * @param prefix The prefix to use for indexes, as a <strong>String</strong>. 294 */ 295 public void map(HashMap mapping, String prefix) { 296 if(default_value != null) { 297 return; 298 } 299 children = new Vector(); 300 if(prefix.length() > 0) { 301 prefix = prefix + "."; 302 } 303 int i = 1; 304 for(Node node = getElement().getFirstChild(); node != null; node = node.getNextSibling()) { 305 if(node.getNodeName().equals("Subject")) { 306 GValueNode child = new GValueNode((Element)node); 307 child.setParent(this); 308 children.add(child); 309 String index = prefix + i; 310 mapping.put(index, child); 311 child.map(mapping, index); 312 i++; 313 } 314 } 315 } 316 317 /** Removes the child at <I>index</I> from the reciever. */ 318 public void remove(int index) { 319 // Can't ever remove Elements from here. Have to use editor. */ 320 } 321 322 /** Removes <I>node</I> from the reciever. <I>setParent</I> will be 323 * messaged on node. 324 */ 325 public void remove(MutableTreeNode node) { 326 // Can't ever remove Elements from here. Have to use editor. */ 327 } 328 329 /** Removes the reciever from its parent. */ 330 public void removeFromParent() { 331 // Can't ever remove Elements from here. Have to use editor. */ 332 } 333 334 public void setAlias(String alias) { 335 setNodeValue("Alias", alias); 336 } 337 338 public void setEnabled(boolean state) { 339 Element element = (Element) userObject; 340 if(state) { 341 element.setAttribute("enabled", "true"); 342 } 343 else { 344 element.setAttribute("enabled", "false"); 345 } 346 } 347 348 /** Resets the user object of the reciever to <I>object</I>. */ 349 public void setUserObject(Object object) { 350 // Can't ever change Elements from here. Have to use editor. */ 351 } 352 353 public void setValue(String value) { 354 setNodeValue("Value", value); 355 } 356 357 public int size() { 358 int size = 0; 359 if(children != null) { 360 size = children.size(); 361 } 362 return size; 363 } 364 365 /** Returns a <I>String</I> representation of the reciever. If this 366 * happens to be the AssignedValues 'root' then we do something slightly 367 * different, otherwise we return the value of the <I>#Text</I> child of 368 * the <I>Value</I> of this <I>Subject</I> node! 369 */ 370 public String toString() { 371 if(default_value != null) { 372 return default_value; 373 } 374 Element element = getElement(); 375 String name = element.getNodeName(); 376 String result = null; 377 if(name.equals("Subject")) { 378 result = MSMUtils.getValue(element); 379 } 380 else if(name.equals("AssignedValues")) { 381 result = getMetadataElementName(); 382 } 383 return Utility.stripNL(result); 384 } 385 386 public void unmap() { 387 children = null; 388 } 389 390 private void map() { 391 children = new Vector(); 392 for(Node node = getElement().getFirstChild(); node != null; node = node.getNextSibling()) { 393 if(node.getNodeName().equals("Subject")) { 394 GValueNode child = new GValueNode((Element)node); 395 child.setParent(this); 396 children.add(child); 397 } 398 } 399 } 400 401 private void setNodeValue(String name, String value) { 402 boolean found = false; 403 // Attempt to retrieve a child node named name 404 Element element = (Element) userObject; 405 for(Node pos_node = element.getFirstChild(); pos_node != null; pos_node = pos_node.getNextSibling()) { 406 406 // And if we find such a node 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 407 if(pos_node.getNodeName().equals(name)) { 408 // And the new value is non-null, retrieve its text node and change it. 409 if(value != null) { 410 for(Node pos_text = pos_node.getFirstChild(); pos_text != null; pos_text = pos_text.getNextSibling()) { 411 // When we find the node... 412 if(pos_text.getNodeName().equals("#text")) { 413 pos_text.setNodeValue(value); 414 } 415 } 416 } 417 // Otherwise remove the node altogether 418 else { 419 element.removeChild(pos_node); 420 } 421 found = true; 422 } 423 } 424 // Otherwise if no such node exists add it. 425 if(!found && value != null) { 426 Document document = element.getOwnerDocument(); 427 Node new_node = document.createElementNS("", name); 428 element.appendChild(new_node); 429 Node new_text = document.createTextNode(value); 430 new_node.appendChild(new_text); 431 new_text = null; 432 new_node = null; 433 } 434 // Done. 435 element = null; 436 return; 437 } 438 438 } 439 440 441 442 -
trunk/gli/src/org/greenstone/gatherer/valuetree/GValueTree.java
r4354 r4364 61 61 */ 62 62 public class GValueTree 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 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 63 extends JPanel 64 implements ActionListener, FocusListener, MSMListener, TreeSelectionListener { 65 /** Is the metadata currently shown in the edit controls common to all records selected. */ 66 private boolean common = false; 67 /** Certain actions are disabled if the metadata selected isn't at file level. */ 68 private boolean file_level = false; 69 private boolean ignore = false; 70 private boolean ignore_item_change = false; 71 private CardLayout card_layout; 72 private DefaultListModel list_model; 73 /** The metadata element that is currently selected. */ 74 private ElementWrapper selected_metadata_element = null; 75 //private GComboBox elements; 76 private MetadataComboBoxModel em; 77 private GValueModel vm; 78 private Hashtable previous_values = new Hashtable(); 79 private JButton add; 80 /** This button creates an editor dialog to allow for an expanded area to type in text. */ 81 private JButton expand = null; 82 private JButton remove; 83 private JButton restore; 84 private JButton search; 85 private JButton update; 86 //private JLabel element_label; 87 private JLabel value_label; 88 private JList list; 89 private JPanel auto_pane; 90 private JPanel button_pane; 91 private JPanel center_pane; 92 //private JPanel list_pane; 93 private JPanel north_pane; 94 //private JPanel south_pane; 95 private JPanel tree_pane; 96 private JPanel value_pane; 97 private JTextArea auto_message; 98 private JTextField value; 99 private JTree tree; 100 private MetaEditPane metaedit; 101 private String args[] = new String[1]; 102 private TreePath previous; 103 private ValueListener value_listener; 104 /** Stock standard size for labels. */ 105 static final private Dimension LABEL_SIZE = new Dimension(132, 26); 106 107 //static final private String LIST = "List"; 108 static final private String NONE = "None"; 109 static final private String TREE = "Tree"; 110 111 112 public GValueTree(MetaEditPane metaedit, int width, int height, JButton add, JButton update, JButton remove) { 113 this(metaedit, new Dimension(width, height), add, update, remove); 114 } 115 116 public GValueTree(MetaEditPane metaedit, Dimension size, JButton add, JButton update, JButton remove) { 117 super(); 118 119 this.add = add; 120 this.metaedit = metaedit; 121 this.remove = remove; 122 this.update = update; 123 124 ///ystem.err.println("Created new ValueListener!"); 125 value_listener = new ValueListener(); 126 127 int height = size.height; 128 int width = size.width; 129 130 setFont(Gatherer.config.getFont("general.font", false)); 131 //setSize(size); 132 setPreferredSize(size); 133 134 //north_pane = new JPanel(); 135 136 //element_label = new JLabel(get("Element")); 137 //element_label.setPreferredSize(LABEL_SIZE); 138 139 //elements = new GComboBox(); 140 //elements.addItemListener(this); 141 //elements.setEditable(false); 142 //elements.setMaximumRowCount(5); 143 //elements.setPreferredSize(new Dimension(413, 24)); 144 145 center_pane = new JPanel(); 146 147 card_layout = new CardLayout(); 148 149 auto_pane = new JPanel(); 150 args[0] = "Title"; 151 auto_message = new SmarterTextArea(get("AutoMessage", args), false); 152 auto_message.setLineWrap(true); 153 auto_message.setOpaque(false); 154 auto_message.setWrapStyleWord(true); 155 156 tree_pane = new JPanel(); 157 158 JLabel tree_label = new JLabel(get("Tree")); 159 160 tree = new JTree(new GValueModel()); 161 tree.addTreeSelectionListener(this); 162 tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 163 tree.setRootVisible(true); 164 tree.putClientProperty("JTree.lineStyle", "Angled"); 165 //setTreeEnabled(false); 166 167 //list_pane = new JPanel(); 168 //JLabel list_label = new JLabel(get("List")); 169 //list_model = new DefaultListModel(); 170 //list = new JList(list_model); 171 //list.addListSelectionListener(new ListSelectionListenerImpl()); 172 173 JPanel controls_pane = new JPanel(); 174 175 value_pane = new JPanel(); 176 value_label = new JLabel(get("Value")); 177 value_label.setPreferredSize(LABEL_SIZE); 178 JPanel edit_pane = new JPanel(); 179 expand = new JButton(get("General.Expand")); 180 expand.setMnemonic(KeyEvent.VK_E); 181 expand.setPreferredSize(MetaEditPane.BUTTON_SIZE); 182 183 value = new JTextField(); 184 value.addActionListener(this); 185 value.setBackground(Gatherer.config.getColor("coloring.editable", false)); 186 value.addFocusListener(this); 187 //value.addKeyListener(this); 188 value.setPreferredSize(new Dimension(413, 24)); 189 190 button_pane = new JPanel(); 191 192 JPanel inner_button_pane = new JPanel(); 193 194 //restore = new JButton(get("Restore")); 195 //restore.setEnabled(false); 196 //restore.setMnemonic(KeyEvent.VK_T); 197 198 //search = new JButton(get("Search")); 199 //search.setMnemonic(KeyEvent.VK_S); 200 201 // Connection 202 expand.addActionListener(this); 203 //restore.addActionListener(this); 204 //search.addActionListener(this); 205 value.getDocument().addDocumentListener(new DocumentListenerImpl()); 206 207 // Layout 208 //element_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 209 //north_pane.setLayout(new BorderLayout()); 210 //north_pane.add(element_label, BorderLayout.CENTER); 211 //north_pane.add(elements, BorderLayout.EAST); 212 213 tree_pane.setLayout(new BorderLayout()); 214 tree_pane.add(tree_label, BorderLayout.NORTH); 215 tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER); 216 217 //list_pane.setLayout(new BorderLayout()); 218 //list_pane.add(list_label, BorderLayout.NORTH); 219 //list_pane.add(new JScrollPane(list), BorderLayout.CENTER); 220 221 auto_pane.setBorder(BorderFactory.createEmptyBorder(25,10,25,10)); 222 auto_pane.setLayout(new BorderLayout()); 223 auto_pane.add(auto_message, BorderLayout.CENTER); 224 225 center_pane.setBorder(BorderFactory.createEmptyBorder(2,0,2,0)); 226 center_pane.setLayout(card_layout); 227 center_pane.add(tree_pane, TREE); 228 //center_pane.add(list_pane, LIST); 229 center_pane.add(auto_pane, NONE); 230 231 value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 232 233 inner_button_pane.setLayout(new GridLayout(3,1,0,0)); 234 inner_button_pane.add(add); 235 inner_button_pane.add(update); 236 inner_button_pane.add(remove); 237 238 edit_pane.setLayout(new BorderLayout()); 239 edit_pane.add(expand, BorderLayout.NORTH); 240 241 button_pane.setLayout(new BorderLayout()); 242 button_pane.add(inner_button_pane, BorderLayout.WEST); 243 button_pane.add(edit_pane, BorderLayout.EAST); 244 245 value_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 246 value_pane.setLayout(new BorderLayout()); 247 value_pane.add(value_label, BorderLayout.CENTER); 248 value_pane.add(value, BorderLayout.EAST); 249 value_pane.add(button_pane, BorderLayout.SOUTH); 250 251 controls_pane.setLayout(new BorderLayout()); 252 controls_pane.add(value_pane, BorderLayout.CENTER); 253 //controls_pane.add(button_pane, BorderLayout.SOUTH); 254 255 this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 256 this.setLayout(new BorderLayout()); 257 this.add(controls_pane, BorderLayout.NORTH); 258 this.add(center_pane, BorderLayout.CENTER); 259 //this.add(south_pane, BorderLayout.SOUTH); 260 } 261 262 public void actionPerformed(ActionEvent event) { 263 ignore = true; 264 if(event.getSource() == value) { 265 StringTokenizer tokenizer = new StringTokenizer(value.getText(), "\\"); 266 TreePath closest = getClosestPath(tokenizer); 267 if(closest != null) { 268 TreePath path = tree.getSelectionPath(); 269 GValueNode node = (GValueNode)path.getLastPathComponent(); 270 ///ystem.err.println("1. Set value: " + node.toString()); 271 value.setText(Utility.stripNL(node.toString()));//formatPath(path, false))); 272 value.setCaretPosition(0); 273 // Now systematically add to selected record. 274 add.doClick(); 275 } 276 else { 277 int response = JOptionPane.showConfirmDialog(this, "Do you wish to create a new entry?", "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); 278 if(response == JOptionPane.YES_OPTION) { 279 TreePath new_path = addNode(value.getText()); 280 tree.setSelectionPath(new_path); 281 // tree.scrollPathToVisible(new_path); 282 scrollTreePathToVisible(new_path); 283 GValueNode node = (GValueNode)new_path.getLastPathComponent(); 284 ///ystem.err.println("2. Set value: " + node.toString()); 285 value.setText(Utility.stripNL(node.toString())); 286 value.setCaretPosition(0); 287 // Now systematically add to selected record. 288 add.doClick(); 289 } 290 } 291 } 292 //else if(event.getSource() == search) { 293 // searchValues(); 294 // restore.setEnabled(true); 295 // card_layout.show(center_pane, LIST); 296 //} 297 //else if(event.getSource() == restore) { 298 // restore.setEnabled(false); 299 // card_layout.show(center_pane, TREE); 300 //} 301 else if(event.getSource() == expand) { 302 EditorDialog ed = new EditorDialog(); 303 String temp = ed.display(value.getText()); 304 if(temp != null) { 305 ///ystem.err.println("3. Set value: " + temp); 306 value.setText(temp); 307 value.setCaretPosition(0); 308 } 309 //validateControls(); 310 } 311 ignore = false; 312 } 313 /** Called whenever an element within the metadata set module changes in some way. 314 314 * @param event A <strong>MSMEvent</strong> which contains information about the change. 315 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 316 public void elementChanged(MSMEvent event) { 317 /* @TODO - Not sure. Figure out. */ 318 } 319 public void focusGained(FocusEvent event) { 320 321 } 322 323 public void focusLost(FocusEvent event) { 324 325 } 326 327 public boolean getCommon() { 328 return common; 329 } 330 331 public ElementWrapper getSelectedMetadataElement() { 332 return selected_metadata_element; 333 } 334 335 public String getSelectedValue() { 336 String value_text = value.getText(); 337 previous_values.put(selected_metadata_element, value_text); 338 return value_text; 339 } 340 341 public GValueModel getValueModel() { 342 return (GValueModel) tree.getModel(); 343 } 344 345 /** This method is required for any ItemListener so that it can be informed when the state of an item set changes (such as when a new selection is made in a combobox). Note that this update is subtly different from the validateControls() method as it also gathers new models for each combobox as required. The reason this isn't done as part of validateControls() is that this would continously reset whatever the user is attempting to type (ie value would be found to be enabled and a new model would be loaded erasing whatever the user just typed in value to fire the validateControls() in the first place!) 346 346 * @param event An ItemEvent encapsulating all the information about this event. 347 347 */ … … 384 384 * @param event A <strong>MSMEvent</strong> which encapsulates relevant data about the change. 385 385 */ 386 387 388 386 public void metadataChanged(MSMEvent event) { 387 // Don't care. 388 } 389 389 390 390 public void newMetadataSetManager() { 391 392 393 394 395 } 396 397 398 399 400 401 391 if(Gatherer.c_man != null && Gatherer.c_man.getCollection() != null && Gatherer.c_man.getCollection().msm != null) { 392 Gatherer.c_man.getCollection().msm.addMSMListener(this); 393 } 394 setChanged(null); 395 } 396 397 public void refreshValueTree() { 398 if(em != null && em.getSize() > 0) { 399 setSelectedMetadataElement((ElementWrapper)em.getSelectedItem()); 400 } 401 } 402 402 403 403 public void registerModel(GValueModel model) { 404 405 406 407 408 } 409 404 vm = model; 405 vm.addTreeModelListener(value_listener); 406 tree.setModel(model); 407 tree.addTreeSelectionListener(this); 408 } 409 /** Called whenever a set within the metadata set module changes in some way. 410 410 * @param event A <strong>MSMEvent</strong> which contains information about the change. 411 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 412 public void setChanged(MSMEvent event) { 413 // Update the element model. 414 MetadataComboBoxModel model = Gatherer.c_man.getCollection().msm.getElementModel(); 415 if(model.getSize() > 0) { 416 setElementModel(model); 417 model.setSelectedItem(model.getElementAt(0)); 418 vm = Gatherer.c_man.getCollection().msm.getValueTree((ElementWrapper)model.getSelectedItem()); 419 setTreeEnabled(true); 420 if(vm == null) { 421 vm = new GValueModel(get("Not_Applicable")); 422 setTreeEnabled(false); 423 } 424 } 425 else { 426 vm = new GValueModel(); 427 setTreeEnabled(true); 428 } 429 vm.addTreeModelListener(value_listener); 430 tree.setModel(vm); 431 tree.addTreeSelectionListener(this); 432 } 433 434 public void setCommon(boolean common) { 435 this.common = common; 436 } 437 437 438 438 public void setElementModel(MetadataComboBoxModel model) { 439 440 441 442 443 444 445 446 447 448 449 439 ///ystem.err.println("Reset ElementModel."); 440 setTreeEnabled(true); 441 Gatherer.c_man.getCollection().msm.addMSMListener(this); 442 if(model == null) { 443 Gatherer.println("model = null"); 444 System.exit(0); 445 } 446 ignore_item_change = true; 447 em = model; 448 //elements.setModel(em); 449 if(em.getSize() > 0) { 450 450 ///ystem.err.println("em.size() > 0"); 451 451 //elements.setEnabled(true); 452 452 //elements.setSelectedIndex(0); 453 453 selected_metadata_element = (ElementWrapper)em.getSelectedItem(); 454 454 //elements.setToolTipText(selected_metadata_element.getToolTip()); 455 455 // First we create and install a blank model, or else no updates ever work. 456 457 456 vm = new GValueModel(get("Not_Applicable")); 457 tree.setModel(vm); 458 458 // Now we create a new model if applicable, disabling otherwise. 459 460 461 462 463 464 465 466 467 468 469 459 if(selected_metadata_element.getNamespace().length() > 0) { 460 vm = Gatherer.c_man.getCollection().msm.getValueTree(selected_metadata_element); 461 vm.addTreeModelListener(value_listener); 462 ///ystem.err.println("Found existing model containing " + vm.size() + " entries"); 463 setTreeEnabled(true); 464 tree.setModel(vm); // This doesn't seem to work 465 } 466 else { 467 ///ystem.err.println("Creating new model."); 468 setTreeEnabled(false); 469 } 470 470 ///ystem.err.println("The value tree should have been updated by now."); 471 471 ///ystem.err.println("6. Set value: " + getLastValue()); 472 473 474 475 472 value.setText(Utility.stripNL(getLastValue())); 473 value.setCaretPosition(0); 474 } 475 else { 476 476 ///ystem.err.println("No elements loaded."); 477 477 //elements.setEnabled(false); 478 478 //elements.setToolTipText(get("No_Description")); 479 479 //setTreeEnabled(false); 480 481 482 483 484 485 } 486 487 488 489 490 491 492 493 480 value.setText(""); 481 } 482 previous = null; 483 ignore_item_change = false; 484 validateControls(); 485 } 486 487 public void setFileLevel(boolean file_level) { 488 this.file_level = file_level; 489 } 490 491 public void setRootVisible(boolean val) { 492 tree.setRootVisible(val); 493 } 494 494 495 495 public void setSelectedMetadataElement(ElementWrapper element) { 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 496 if(!ignore_item_change) { 497 ignore_item_change = true; 498 if(element != null) { 499 selected_metadata_element = element; 500 //elements.setSelectedItem(element); 501 //elements.setToolTipText(element.getToolTip()); 502 vm = Gatherer.c_man.getCollection().msm.getValueTree(element); 503 if(vm != null) { 504 vm.addTreeModelListener(value_listener); 505 setTreeEnabled(true); 506 } 507 else { 508 vm = new GValueModel(get("Not_Applicable")); 509 setTreeEnabled(false); 510 } 511 tree.setModel(vm); 512 ///ystem.err.println("7. Set value: " + getLastValue()); 513 //value.setText(Utility.stripNL(getLastValue())); 514 //value.setCaretPosition(0); 515 } 516 else { 517 //elements.setToolTipText(get("No_Description")); 518 setTreeEnabled(false); 519 //value.setText(""); 520 } 521 metaedit.validateControls(false); 522 } 523 previous = null; 524 ignore_item_change = false; 525 //validateControls(); 526 } 527 528 public void setSelectedValue(String val) { 529 ignore = true; 530 value.setText(Utility.stripNL(val)); 531 value.setCaretPosition(0); 532 StringTokenizer tokenizer = new StringTokenizer(val, "\\"); 533 TreePath closest = getClosestPath(tokenizer); 534 if(previous != null) { 535 tree.collapsePath(getParent(previous)); 536 previous = null; 537 } 538 if(closest != null) { 539 tree.setSelectionPath(closest); 540 540 // tree.scrollPathToVisible(closest); 541 542 543 544 545 546 547 541 scrollTreePathToVisible(closest); 542 } 543 ignore = false; 544 //validateControls(); 545 } 546 547 /** This method enables or diables the various controls based upon the 548 548 * state of the system. This method should be called after every change 549 549 * which could affect the GUI. <br> 550 550 * Hack #1: For some reason this gets called 14 times for one selection. One day I'll figure out why, but for now I'll just ignore all but every fourteenth call. 551 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 604 605 606 607 608 609 610 611 612 613 614 552 public void validateControls() { 553 Metadata selected_metadata = metaedit.getSelectedMetadata(); 554 if(metaedit.getSelectedNode() != null && selected_metadata != null) { 555 ElementWrapper element = selected_metadata.getElement(); 556 if(element.getNamespace() != "") { 557 GValueNode value_node = selected_metadata.getValueNode(); 558 ///ystem.err.println("Value node = " + value_node); 559 // We may be dealing with an element that has no current value 560 if(value_node == null) { 561 String new_value = value.getText(); 562 if(new_value.length() > 0) { 563 add.setEnabled(true); 564 } 565 else { 566 add.setEnabled(false); 567 } 568 // Nothing to update 569 update.setEnabled(false); 570 // Nothing to remove 571 remove.setEnabled(false); 572 } 573 // Or else we have some current value that may or may not match the one in our value field. 574 else { 575 String current_value = selected_metadata.getValueNode().getFullPath(); 576 String new_value = value.getText(); 577 ///ystem.err.println("Current value: '" + current_value + "'"); 578 ///ystem.err.println("New value: '" + new_value + "'"); 579 if(new_value.length() > 0 && !current_value.equals(new_value)) { 580 add.setEnabled(true); 581 if(common) { 582 update.setEnabled(true); 583 } 584 else { 585 update.setEnabled(false); 586 } 587 } 588 else { 589 add.setEnabled(false); 590 update.setEnabled(false); 591 } 592 // If you have something selected, and we're at the file level, you can remove it. 593 if(file_level) { 594 remove.setEnabled(true); 595 } 596 else { 597 remove.setEnabled(false); 598 } 599 } 600 } 601 else { 602 add.setEnabled(false); 603 update.setEnabled(false); 604 remove.setEnabled(false); 605 } 606 } 607 else { 608 add.setEnabled(false); 609 update.setEnabled(false); 610 remove.setEnabled(false); 611 } 612 } 613 614 /** Called when the value tree of a certain element has changed significantly. If we are displaying the tree for the element noted in the event, then reload the value model. 615 615 * @param event A <strong>MSMEvent</strong> containing information about the event. 616 616 */ 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 617 public void valueChanged(MSMEvent event) { 618 ElementWrapper element = event.getElement(); 619 if(selected_metadata_element.equals(element)) { 620 GValueModel temp = Gatherer.c_man.getCollection().msm.getValueTree(element); 621 if(temp != null) { 622 vm = temp; 623 tree.setModel(vm); 624 vm.removeTreeModelListener(value_listener); 625 vm.addTreeModelListener(value_listener); 626 setTreeEnabled(true); 627 setSelectedValue(value.getText()); 628 } 629 else { 630 vm = new GValueModel(get("Not_Applicable")); 631 tree.setModel(vm); 632 setTreeEnabled(false); 633 } 634 } 635 } 636 636 637 637 public void valueChanged(TreeSelectionEvent event) { 638 639 640 641 642 643 644 638 if(tree.getSelectionCount() != 0 && !ignore) { 639 TreePath path = tree.getSelectionPath(); 640 GValueNode node = (GValueNode) path.getLastPathComponent(); 641 value.setText(node.getFullPath());//formatPath(tree.getSelectionPath(), false))); 642 value.setCaretPosition(0); 643 validateControls(); 644 } 645 645 } 646 646 647 647 private TreePath addNode(String reference) { 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 648 StringTokenizer tokenizer = new StringTokenizer(reference, "\\"); 649 GValueModel model = (GValueModel)tree.getModel(); 650 GValueNode current = (GValueNode)model.getRoot(); 651 while(tokenizer.hasMoreElements()) { 652 String token = tokenizer.nextToken(); 653 GValueNode node = (GValueNode)getClosestNode(token, current); 654 if(node == null) { 655 Gatherer.println("Before: '" + current.toString() + "'"); 656 for(int i = 0; i < current.getChildCount(); i++) { 657 Gatherer.println(current.getChildAt(i).toString()); 658 } 659 node = model.addValue(token, current, null); 660 Gatherer.println("After: '" + current.toString() + "'"); 661 for(int i = 0; i < current.getChildCount(); i++) { 662 Gatherer.println(current.getChildAt(i).toString()); 663 } 664 } 665 current = node; 666 } 667 tree.updateUI(); 668 if(current != null) { 669 return new TreePath(current.getPath()); 670 } 671 else { 672 return new TreePath(((GValueNode)model.getRoot()).getPath()); 673 } 674 } 675 /** Returns the given tree path as path formatted string (ie subject\subject\subject). 676 676 * @param tree The <strong>JTree</strong> the TreePath came from. Used to determine if the root node should be encoded as well. 677 677 * @param path A <strong>TreePath</strong> that you wish to encode to String. … … 681 681 * @see javax.swing.tree.TreePath 682 682 */ 683 static public String formatPath(JTree tree, TreePath path, boolean full) { 684 String text = ""; 685 int i = 0; 686 if(tree == null || (!tree.isRootVisible() && !full)) { 687 i = 1; 688 } 689 for( ; i < path.getPathCount(); i++) { 690 GValueNode node = (GValueNode)path.getPathComponent(i); 691 text = text + node.toString(); 692 if(node.getChildCount() > 0) { 693 text = text + "\\"; 694 } 695 } 696 if(full && text.endsWith("\\")) { 697 return text.substring(0, text.length() - 1); 698 } 699 return text; 700 } 701 702 private String get(String key) { 703 return get(key, null); 704 } 705 706 private String get(String key, String args[]) { 707 if(key.indexOf('.') == -1) { 708 key = "MetaEdit." + key; 709 } 710 return Gatherer.dictionary.get(key,args); 711 } 712 713 private GValueNode getClosestNode(String target, GValueNode current) { 714 // We have struck it lucky. 715 if(startsWith(current.toString(), target)) { 716 return current; 717 } 718 GValueNode closest = null; 719 for(int i = 0; i < current.getChildCount() && closest == null; i++) { 720 closest = getClosestNode(target, (GValueNode)current.getChildAt(i)); 721 } 722 return closest; 723 } 724 725 private TreePath getClosestPath(StringTokenizer tokenizer) { 726 if(tokenizer.countTokens() > 0) { 727 TreeNode tree_node = (TreeNode)tree.getModel().getRoot(); 728 if(tree_node instanceof GValueNode) { 729 GValueNode current = (GValueNode)tree_node; 730 TreePath closest = null; 731 while(tokenizer.hasMoreElements() && current != null) { 732 current = getClosestNode(tokenizer.nextToken(), current); 733 } 734 if(current != null) { 735 return new TreePath(current.getPath()); 736 } 737 return null; 738 } 739 } 740 return null; 741 } 742 743 private String getLastValue() { 744 if(previous_values.containsKey(selected_metadata_element)) { 745 return (String)previous_values.get(selected_metadata_element); 746 } 747 return ""; 748 } 749 750 private TreePath getParent(TreePath path) { 751 if(path.getPathCount() > 1) { 752 TreeNode nodes[] = new TreeNode[path.getPathCount() - 1]; 753 for(int i = 0; i < path.getPathCount() - 1; i++) { 754 nodes[i] = (TreeNode)path.getPathComponent(i); 755 } 756 return new TreePath(nodes); 683 static public String formatPath(JTree tree, TreePath path, boolean full) { 684 String text = ""; 685 int i = 0; 686 if(tree == null || (!tree.isRootVisible() && !full)) { 687 i = 1; 688 } 689 for( ; i < path.getPathCount(); i++) { 690 GValueNode node = (GValueNode)path.getPathComponent(i); 691 text = text + node.toString(); 692 if(node.getChildCount() > 0) { 693 text = text + "\\"; 694 } 695 } 696 if(full && text.endsWith("\\")) { 697 return text.substring(0, text.length() - 1); 698 } 699 return text; 700 } 701 702 private String get(String key) { 703 return get(key, null); 704 } 705 706 private String get(String key, String args[]) { 707 if(key.indexOf('.') == -1) { 708 key = "MetaEdit." + key; 709 } 710 return Gatherer.dictionary.get(key,args); 711 } 712 713 private GValueNode getClosestNode(String target, GValueNode current) { 714 // We have struck it lucky. 715 if(startsWith(current.toString(), target)) { 716 return current; 717 } 718 GValueNode closest = null; 719 for(int i = 0; i < current.getChildCount() && closest == null; i++) { 720 closest = getClosestNode(target, (GValueNode)current.getChildAt(i)); 721 } 722 return closest; 723 } 724 725 private TreePath getClosestPath(StringTokenizer tokenizer) { 726 if(tokenizer.countTokens() > 0) { 727 TreeNode tree_node = (TreeNode)tree.getModel().getRoot(); 728 if(tree_node instanceof GValueNode) { 729 GValueNode current = (GValueNode)tree_node; 730 TreePath closest = null; 731 while(tokenizer.hasMoreElements() && current != null) { 732 current = getClosestNode(tokenizer.nextToken(), current); 733 } 734 if(current != null) { 735 return new TreePath(current.getPath()); 757 736 } 758 737 return null; 759 } 760 761 private void searchValues() { 762 // Clear any old search results. 763 list_model.clear(); 764 String target = value.getText(); 765 GValueNode current = (GValueNode)tree.getModel().getRoot(); 766 Vector search_space = new Vector(); 767 search_space.add(current); 768 for(int i = 0; i < search_space.size(); i++) { 769 GValueNode node = (GValueNode)search_space.get(i); 770 // Add this nodes children. 771 for(int j = 0; j < node.getChildCount(); j++) { 772 search_space.add(node.getChildAt(j)); 773 } 774 // And if this node matches add it to list, except for the very 775 // first node as that is AssignedValues the root node! 776 if(node.toString().indexOf(target) != -1 && 777 node != current) { 778 list_model.add(list_model.size(), node); 779 } 780 } 781 } 782 783 private void setTreeEnabled(boolean value) { 784 tree.setRootVisible(!value); 785 tree.setEnabled(value); 786 if(value) { 787 card_layout.show(center_pane, TREE); 788 } 789 else { 790 Metadata selected_metadata = metaedit.getSelectedMetadata(); 791 if(selected_metadata != null && selected_metadata.getElement() != null) { 792 args[0] = selected_metadata.getElement().toString(); 793 } 794 else { 795 args[0] = ""; 796 } 797 auto_message.setText(get("AutoMessage", args)); 798 card_layout.show(center_pane, NONE); 799 } 800 } 801 802 private boolean startsWith(String longer, String shorter) { 803 if(longer.length() > shorter.length()) { 804 longer = longer.substring(0, shorter.length()); 805 } 806 return longer.equalsIgnoreCase(shorter); 807 } 738 } 739 } 740 return null; 741 } 742 743 private String getLastValue() { 744 if(previous_values.containsKey(selected_metadata_element)) { 745 return (String)previous_values.get(selected_metadata_element); 746 } 747 return ""; 748 } 749 750 private TreePath getParent(TreePath path) { 751 if(path.getPathCount() > 1) { 752 TreeNode nodes[] = new TreeNode[path.getPathCount() - 1]; 753 for(int i = 0; i < path.getPathCount() - 1; i++) { 754 nodes[i] = (TreeNode)path.getPathComponent(i); 755 } 756 return new TreePath(nodes); 757 } 758 return null; 759 } 760 761 private void searchValues() { 762 // Clear any old search results. 763 list_model.clear(); 764 String target = value.getText(); 765 GValueNode current = (GValueNode)tree.getModel().getRoot(); 766 Vector search_space = new Vector(); 767 search_space.add(current); 768 for(int i = 0; i < search_space.size(); i++) { 769 GValueNode node = (GValueNode)search_space.get(i); 770 // Add this nodes children. 771 for(int j = 0; j < node.getChildCount(); j++) { 772 search_space.add(node.getChildAt(j)); 773 } 774 // And if this node matches add it to list, except for the very 775 // first node as that is AssignedValues the root node! 776 if(node.toString().indexOf(target) != -1 && 777 node != current) { 778 list_model.add(list_model.size(), node); 779 } 780 } 781 } 782 783 private void setTreeEnabled(boolean value) { 784 tree.setRootVisible(!value); 785 tree.setEnabled(value); 786 if(value) { 787 card_layout.show(center_pane, TREE); 788 } 789 else { 790 Metadata selected_metadata = metaedit.getSelectedMetadata(); 791 if(selected_metadata != null && selected_metadata.getElement() != null) { 792 args[0] = selected_metadata.getElement().toString(); 793 } 794 else { 795 args[0] = ""; 796 } 797 auto_message.setText(get("AutoMessage", args)); 798 card_layout.show(center_pane, NONE); 799 } 800 } 801 802 private boolean startsWith(String longer, String shorter) { 803 if(longer.length() > shorter.length()) { 804 longer = longer.substring(0, shorter.length()); 805 } 806 return longer.equalsIgnoreCase(shorter); 807 } 808 808 809 809 private void printTreePaths() { … … 811 811 Vector nodes = new Vector(); 812 812 nodes.add(tree.getModel().getRoot()); 813 814 815 816 817 818 813 while(nodes.size() > 0) { 814 GValueNode node = (GValueNode)nodes.get(0); 815 nodes.remove(node); 816 for(int i = 0; i < node.getChildCount(); i++) { 817 nodes.add(node.getChildAt(i)); 818 } 819 819 ///ystem.out.println("TreePath = " + node.getPath()); 820 820 } 821 821 } 822 822 … … 840 840 } 841 841 842 843 844 845 846 847 842 private class DocumentListenerImpl 843 implements DocumentListener { 844 /** Gives notification that an attribute or set of attributes changed. */ 845 public void changedUpdate(DocumentEvent e) { 846 validate(); 847 } 848 848 849 850 851 852 849 /** Gives notification that there was an insert into the document. */ 850 public void insertUpdate(DocumentEvent e) { 851 validate(); 852 } 853 853 854 855 856 857 858 859 860 854 /** Gives notification that a portion of the document has been removed. */ 855 public void removeUpdate(DocumentEvent e) { 856 validate(); 857 } 858 859 public void validate() { 860 ignore = true; 861 861 ///ystem.err.println("Key released."); 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 862 StringTokenizer tokenizer = new StringTokenizer(value.getText(), "\\"); 863 TreePath closest = getClosestPath(tokenizer); 864 if(closest == previous) { 865 validateControls(); 866 ignore = false; 867 return; 868 } 869 else { 870 // Clear previous 871 if(previous != null) { 872 // We have a brand new match close up the old one. 873 if(closest != null) { 874 if(getParent(previous) != null && 875 !getParent(previous).toString().equals("[AssignedValues]")) { 876 tree.collapsePath(getParent(previous)); 877 } 878 previous = null; 879 } 880 // We haven't got a match, but we did have a matching path to 881 // this point. Move the selection to previous paths parent. 882 else { 883 tree.setSelectionPath(getParent(previous)); 884 } 885 } 886 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 887 if(closest != null) { 888 tree.setSelectionPath(closest); 889 // tree.scrollPathToVisible(closest); 890 scrollTreePathToVisible(closest); 891 previous = closest; 892 } 893 } 894 ignore = false; 895 validateControls(); 896 } 897 } 898 899 private class ListSelectionListenerImpl 900 implements ListSelectionListener { 901 public void valueChanged(ListSelectionEvent event) { 902 GValueNode node = (GValueNode)list.getSelectedValue(); 903 value.setText(Utility.stripNL(node.toString()));//formatPath(node.getPath(), false))); 904 value.setCaretPosition(0); 905 } 906 } 907 907 908 908 private class ValueListener 909 implements TreeModelListener { 910 /* Called when nodes have been modified, but the model hasn't changed. 909 implements TreeModelListener { 910 /* Called when nodes have been modified, but the model hasn't changed. 911 * @param event Everything you ever wanted to know about model changes 912 * in one tidy TreeModelEvent package. 913 */ 914 public void treeNodesChanged(TreeModelEvent event) { 915 update(); 916 } 917 918 /* Called when nodes have been added to the model. 911 919 * @param event Everything you ever wanted to know about model changes 912 920 * in one tidy TreeModelEvent package. 913 921 */ 914 public void treeNodesChanged(TreeModelEvent event) {915 916 922 public void treeNodesInserted(TreeModelEvent event) { 923 update(); 924 } 917 925 918 /* Called when nodes have been added tothe model.926 /* Called when nodes have been removed from the model. 919 927 * @param event Everything you ever wanted to know about model changes 920 928 * in one tidy TreeModelEvent package. 921 929 */ 922 public void treeNodesInserted(TreeModelEvent event) {923 924 930 public void treeNodesRemoved(TreeModelEvent event) { 931 update(); 932 } 925 933 926 /* Called when nodes have been removed fromthe model.934 /** Called when nodes have been rearranged within the model. 927 935 * @param event Everything you ever wanted to know about model changes 928 936 * in one tidy TreeModelEvent package. 929 937 */ 930 public void treeNodesRemoved(TreeModelEvent event) {931 932 938 public void treeStructureChanged(TreeModelEvent event) { 939 update(); 940 } 933 941 934 /** Called when nodes have been rearranged within the model. 935 * @param event Everything you ever wanted to know about model changes 936 * in one tidy TreeModelEvent package. 937 */ 938 public void treeStructureChanged(TreeModelEvent event) { 939 update(); 940 } 941 942 private void update() { 942 private void update() { 943 943 // Don't bother updating if the metaedit tab isn't selected. 944 945 946 947 948 949 950 951 952 953 954 955 956 944 if(Gatherer.g_man.getSelectedView() == Gatherer.g_man.metaedit_pane) { 945 vm = null; 946 vm = Gatherer.c_man.getCollection().msm.getValueTree((ElementWrapper)em.getSelectedItem()); 947 if(vm != null) { 948 vm.addTreeModelListener(value_listener); 949 } 950 else { 951 vm = new GValueModel(get("Not_Applicable")); 952 } 953 tree.setModel(vm); 954 tree.repaint(); 955 } 956 } 957 957 } 958 958 } 959 960 961 962 963
Note:
See TracChangeset
for help on using the changeset viewer.