Changeset 4363
- Timestamp:
- 2003-05-27T15:34:50+12:00 (21 years ago)
- Location:
- trunk/gli/src/org/greenstone/gatherer
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/Configuration.java
r4348 r4363 54 54 */ 55 55 public class Configuration 56 57 58 59 60 61 56 extends Hashtable { 57 public File exec_file; 58 /** The path (or url) to the webserver which is serving the Greenstone collection. */ 59 public String exec_path = null; 60 /** The path to the Greenstone Suite installation directory. */ 61 public String gsdl_path = ""; 62 62 /** The path to the PERL executable, up to and including Perl.exe. */ 63 63 public String perl_path = ""; 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 64 /** The password for the proxy server indicated above. */ 65 public String proxy_pass = null; 66 /** The username for the proxy server indicated above. */ 67 public String proxy_user = null; 68 /** The screen size of the desktop the Gatherer will be displayed on. */ 69 public Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); 70 /** Collection level configuration (which in some cases overrides general configuration. */ 71 private Document collection_config; 72 /** The general configuration settings. */ 73 private Document general_config; 74 private int cache_hit = 0; 75 private int cache_miss = 0; 76 public URL exec_address = null; 77 /** The string identifying an argument's name attribute. */ 78 static final private String ARGUMENT_NAME = "name"; 79 /** The name of the general Gatherer configuration file. */ 80 static final private String CONFIG_XML = "config.xml"; 81 /** The name of the root element of the subtree containing gatherer configuration options. This is required as the document itself may contain several other subtrees of settings (such as in the case of a '.col' file). */ 82 static final private String GATHERER_CONFIG = "GathererConfig"; 83 /** The string identifying an argument element. */ 84 static final private String GATHERER_CONFIG_ARGUMENT = "Argument"; 85 /** The name of a Name Element. */ 86 static final private String NAME = "Name"; 87 /** The name of the other arguments element. */ 88 static final private String OTHER = "Other"; 89 /** The name of an information Element within the Other subtree. */ 90 static final private String OTHER_INFO = "Info"; 91 /** The name of the general Gatherer configuration template. */ 92 static final private String TEMPLATE_CONFIG_XML = "xml/config.xml"; 93 /** The first of two patterns used during tokenization, this pattern handles a comma separated list. */ 94 static final private String TOKENIZER_PATTERN1 = " ,\n\t"; 95 /** The second of two patterns used during tokenization, this pattern handles an underscore separated list. */ 96 static final private String TOKENIZER_PATTERN2 = "_\n\t"; 97 /** Constructor. 98 * @param gsdl_path The path to the Greenstone directory as a <strong>String</strong>. 99 * @param exec_path A <strong>String</strong> containing the path or url to the webserver serving the greenstone collections. 100 * @param perl_path The path to the PERL executable, as a <strong>String</strong>. 101 */ 102 public Configuration(String gsdl_path, String exec_path, String perl_path) { 103 super(); 104 this.gsdl_path = gsdl_path; 105 this.exec_path = exec_path; 106 // The exec_path may contain an url address, in which case we blindly use that and leave it up to the user to worry about settings and resetting. 107 System.err.println("EXEC_PATH = " + exec_path); 108 if(exec_path != null && exec_path.length() > 0) { 109 try { 110 exec_address = new URL(exec_path); 111 } 112 catch (MalformedURLException error) { 113 ///ystem.err.println("Not an address."); 114 } 115 } 116 // If the above failed, then its up to us to try and figure out what to do. 117 if(exec_address == null) { 118 118 // Try building a file from the given exec_path 119 120 121 122 123 124 125 126 127 128 119 try { 120 File local_file = new File(exec_path); 121 if(local_file.exists()) { 122 // All good. I hope. 123 exec_file = local_file; 124 } 125 else { 126 ///ystem.err.println("No local library at given file path."); 127 } 128 } 129 129 // All sorts of errors might be thrown by a bogus file path. 130 131 132 130 catch (Exception error) { 131 System.err.println("Not a valid file."); 132 } 133 133 // We can generate the path to where the local library should be and use that if it is there. 134 135 136 137 138 139 140 141 142 134 if(exec_file == null) { 135 File server_exe = new File(gsdl_path + Utility.SERVER_EXE); 136 if(server_exe.exists()) { 137 exec_file = server_exe; 138 } 139 else { 140 ///ystem.err.println("No local library."); 141 } 142 } 143 143 // If we get to here with no exec_address nor an exec_file its just plain not going to work. 144 145 144 } 145 else { 146 146 ///ystem.err.println("exec_address != null -> " + exec_address); 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 147 } 148 ///ystem.err.println("Perl path."); 149 this.perl_path = perl_path; 150 // Ensure the perl path includes exe under windoze 151 if(Utility.isWindows() && !perl_path.toLowerCase().endsWith(".exe")) { 152 if(!perl_path.endsWith(File.separator)) { 153 perl_path = perl_path + File.separator; 154 } 155 perl_path = perl_path + "perl.exe"; 156 } 157 // Try to reload the configuration. 158 File config_xml = new File(CONFIG_XML); 159 if(config_xml.exists()) { 160 general_config = Utility.parse(CONFIG_XML, false); 161 } 162 // If that fails retrieve the default configuration file from our xml library, which I'll personally guarantee to work. 163 if(general_config == null) { 164 general_config = Utility.parse(TEMPLATE_CONFIG_XML, true); 165 Gatherer.println("Loaded default Gatherer configuration template."); 166 } 167 else { 168 Gatherer.println("Loaded current Gatherer configuration."); 169 } 170 // Re-establish the color settings. 171 updateUI(); 172 173 System.err.println("EXEC_FILE = " + exec_file); 174 System.err.println("EXEC_ADDRESS = " + exec_address); 175 } 176 177 /** The default get action retrieves the named property from the desired configuration, and returns a true or false. */ 178 public boolean get(String property, boolean general) { 179 String raw = getString(property, general); 180 return (raw != null && raw.equalsIgnoreCase("true")); 181 } 182 183 /** Retrieve all of the configuration preferences which match a certain string. They are returned as a hash map of property names to String objects. */ 184 public HashMap getAll(String property_pattern, boolean general) { 185 HashMap properties = new HashMap(); 186 try { 187 187 // Locate the appropriate element 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 188 Element document_element = null; 189 if(general) { 190 document_element = general_config.getDocumentElement(); 191 } 192 else if(collection_config != null) { 193 document_element = collection_config.getDocumentElement(); 194 } 195 if(document_element != null) { 196 // Retrieve the Gatherer element 197 Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG); 198 NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT); 199 for(int i = 0; i < arguments.getLength(); i++) { 200 Element argument_element = (Element) arguments.item(i); 201 if(argument_element.getAttribute(ARGUMENT_NAME).matches(property_pattern)) { 202 String result = MSMUtils.getValue(argument_element); 203 // Store a mapping in the cache. Sometimes we will overwrite an existing value (say for collection and general level workflow options) but the processing overhead of detecting these clashes far exceeds any savings. 204 put(argument_element.getAttribute(ARGUMENT_NAME) + general, new SoftReference(argument_element)); 205 // Add mapping to the properties we're going to return 206 properties.put(argument_element.getAttribute(ARGUMENT_NAME), result); 207 } 208 } 209 } 210 } 211 catch(Exception error) { 212 } 213 return properties; 214 } 215 216 /** Retrieve the information subtree containing the arguments for the desired external program. If the program has marked superclasses append their arguments as well. */ 217 public Element getArguments(String filename) { 218 Element argument_element = null; 219 try { 220 220 // Retrieve the other information subtree. 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 221 Element document_element = general_config.getDocumentElement(); 222 Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER); 223 NodeList argument_elements = other_element.getElementsByTagName(OTHER_INFO); 224 for(int i = 0; argument_element == null && i < argument_elements.getLength(); i++) { 225 Element possible_element = (Element) argument_elements.item(i); 226 Element possible_name_element = (Element) MSMUtils.getNodeFromNamed(possible_element, NAME); 227 String possible_name = MSMUtils.getValue(possible_name_element); 228 ///ystem.err.println("Does " + possible_name + " equal " + filename); 229 if(possible_name.equalsIgnoreCase(filename)) { 230 argument_element = possible_element; 231 } 232 possible_name = null; 233 possible_name_element = null; 234 possible_element = null; 235 } 236 argument_elements = null; 237 other_element = null; 238 document_element = null; 239 } 240 catch(Exception error) { 241 } 242 return argument_element; 243 } 244 245 /** Retrieve the value of the named property as a Color. */ 246 public Color getColor(String property, boolean general) { 247 Color result = Color.white; // Default 248 try { 249 String raw = getString(property, general); 250 250 // Color is a RGB triplet list, comma separated (also remove whitespace) 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 251 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); 252 int red = Integer.parseInt(tokenizer.nextToken()); 253 int green = Integer.parseInt(tokenizer.nextToken()); 254 int blue = Integer.parseInt(tokenizer.nextToken()); 255 result = new Color(red, green, blue); 256 } 257 catch(Exception error) { 258 Gatherer.printStackTrace(error); 259 } 260 return result; 261 } 262 263 /** Retrieve the value of the named property as a Dimension. */ 264 public Dimension getDimension(String property, boolean general) { 265 Dimension result = new Dimension(100, 100); // Default 266 try { 267 String raw = getString(property, general); 268 268 // Dimension is a width by height pair, comma separated (also remove whitespace) 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 269 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); 270 int width = Integer.parseInt(tokenizer.nextToken()); 271 int height = Integer.parseInt(tokenizer.nextToken()); 272 result = new Dimension(width, height); 273 } 274 catch(Exception error) { 275 Gatherer.printStackTrace(error); 276 } 277 return result; 278 } 279 280 /** Retrieve the value of the named property as a FontUIResource. */ 281 public FontUIResource getFont(String property, boolean general) { 282 FontUIResource result = new FontUIResource("Times New Roman", Font.PLAIN, 10); 283 try { 284 String raw = getString(property, general); 285 285 // Font is a face, style, size triplet. 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 286 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN1); 287 String face = tokenizer.nextToken(); 288 int style = Font.PLAIN; 289 String temp = tokenizer.nextToken().toUpperCase(); 290 if(temp.equals("BOLD")) { 291 style = Font.BOLD; 292 } 293 else if(temp.equals("ITALIC")) { 294 style = Font.ITALIC; 295 } 296 int size = Integer.parseInt(tokenizer.nextToken()); 297 result = new FontUIResource(face, style, size); 298 } 299 catch(Exception error) { 300 Gatherer.printStackTrace(error); 301 } 302 return result; 303 } 304 305 /** Retrieve the value of the named property as an integer. */ 306 public int getInt(String property, boolean general) { 307 int result = -1; 308 try { 309 String raw = getString(property, general); 310 result = Integer.parseInt(raw); 311 } 312 catch(Exception error) { 313 Gatherer.printStackTrace(error); 314 } 315 return result; 316 } 317 318 /** Retrieve the value of the named property as a Locale. */ 319 public Locale getLocale(String property, boolean general) { 320 Locale result = Locale.getDefault(); 321 try { 322 String raw = getString(property, general); 323 323 // Locale is a underscore separated code. 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 324 StringTokenizer tokenizer = new StringTokenizer(raw, TOKENIZER_PATTERN2); 325 String language = tokenizer.nextToken(); 326 String country = tokenizer.nextToken(); 327 result = new Locale(language, country); 328 } 329 catch(Exception error) { 330 Gatherer.printStackTrace(error); 331 } 332 return result; 333 } 334 335 /** Retrieve the value of the named property, and noting whether we consult the general or collection specific configuration. */ 336 public String getString(String property, boolean general) { 337 // Its up to this method to find the appropriate node and retrieve the data itself. 338 String result = ""; 339 try { 340 340 // First of all we look in the cache to see if we have a match. 341 342 343 344 345 346 347 348 341 SoftReference reference = (SoftReference) get(property + general); 342 if(reference != null) { 343 Element argument_element = (Element) reference.get(); 344 if(argument_element != null) { 345 cache_hit++; 346 result = MSMUtils.getValue(argument_element); 347 } 348 } 349 349 // We may have missed in the cache, or the reference may have been consumed. 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 350 if(result.length() == 0) { 351 cache_miss++; 352 // Locate the appropriate element 353 Element document_element = null; 354 if(general) { 355 document_element = general_config.getDocumentElement(); 356 } 357 else if(collection_config != null) { 358 document_element = collection_config.getDocumentElement(); 359 } 360 if(document_element != null) { 361 // Retrieve the Gatherer element 362 Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG); 363 NodeList arguments = gatherer_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT); 364 for(int i = 0; result.length() == 0 && i < arguments.getLength(); i++) { 365 Element argument_element = (Element) arguments.item(i); 366 if(argument_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) { 367 result = MSMUtils.getValue(argument_element); 368 // Store a mapping in the cache. Sometimes we will overwrite an existing value (say for collection and general level workflow options) but the processing overhead of detecting these clashes far exceeds any savings. 369 put(property + general, new SoftReference(argument_element)); 370 } 371 } 372 } 373 } 374 } 375 catch (Exception error) { 376 Gatherer.printStackTrace(error); 377 } 378 // If we still have no result, and the search was made in the collection configuration, retrieve the general one instead. 379 if(result.length() == 0 && !general) { 380 result = getString(property, true); 381 } 382 return result; 383 } 384 385 /** Retrieve the path to the PERL scripts within the Greenstone directory. 386 * @return A <strong>String</strong> containing the path. 387 */ 388 public String getScriptPath() { 389 return gsdl_path + "bin" + File.separator + "script" + File.separator; 390 } 391 392 /** Export the general configuration to file. */ 393 public void save() { 394 ///ystem.err.println("Hits " + cache_hit + " vs Misses " + cache_miss); 395 Utility.export(general_config, Utility.BASE_DIR + CONFIG_XML); 396 } 397 398 /** Set the named property, from the specified configuration, using the given boolean value. */ 399 public void set(String property, boolean general, boolean value) { 400 setString(property, general, (value ? "true" : "false")); 401 } 402 403 /** Add a subtree of argument information to the other arguments part of the general configuration. This overwrites any such existing subtree. */ 404 public void setArguments(Element arguments_element) { 405 try { 406 Element document_element = general_config.getDocumentElement(); 407 Element other_element = (Element) MSMUtils.getNodeFromNamed(document_element, OTHER); 408 408 // Retrieve the name of the information 409 410 409 Element arguments_name_element = (Element)MSMUtils.getNodeFromNamed(arguments_element, NAME); 410 String filename = MSMUtils.getValue(arguments_element); 411 411 // Find any argument information subtree starting with the same name 412 412 Element obsolete_arguments_element = getArguments(filename); 413 413 // Create a copy of the arguments_element within our tree (import). 414 414 Element our_arguments_element = (Element) general_config.importNode(arguments_element, true); 415 415 // Now we insert this new node into the tree. If a previous node exists we replace it instead. 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 416 if(obsolete_arguments_element == null) { 417 other_element.appendChild(our_arguments_element); 418 } 419 else { 420 other_element.replaceChild(our_arguments_element, obsolete_arguments_element); 421 } 422 our_arguments_element = null; 423 obsolete_arguments_element = null; 424 filename = null; 425 arguments_name_element = null; 426 other_element = null; 427 document_element = null; 428 } 429 catch (Exception error) { 430 Gatherer.println("Error in Configuration.setArguments(): " + error); 431 Gatherer.printStackTrace(error); 432 } 433 } 434 435 /** Set the collection configuration. */ 436 public void setCollectionConfiguration(Document collection_config) { 437 this.collection_config = collection_config; 438 updateUI(); 439 ///atherer.println("Collection configuration set."); 440 } 441 442 /** Set the named property, from the specified configuration, using the given Color value. */ 443 public void setColor(String property, boolean general, Color value) { 444 StringBuffer text = new StringBuffer(""); 445 text.append(value.getRed()); 446 text.append(", "); 447 text.append(value.getGreen()); 448 text.append(", "); 449 text.append(value.getBlue()); 450 setString(property, general, text.toString()); 451 } 452 453 /** Set the named property, from the specified configuration, using the given Dimension value. */ 454 public void setDimension(String property, boolean general, Dimension value) { 455 StringBuffer text = new StringBuffer(""); 456 text.append(value.width); 457 text.append(", "); 458 text.append(value.height); 459 setString(property, general, text.toString()); 460 } 461 462 /** Set the named property, from the specified configuration, using the given Font value. */ 463 public void setFont(String property, boolean general, Font value) { 464 StringBuffer text = new StringBuffer(""); 465 text.append(value.getName()); 466 text.append(", "); 467 switch(value.getStyle()) { 468 case Font.BOLD: 469 text.append("BOLD"); 470 break; 471 case Font.ITALIC: 472 text.append("ITALIC"); 473 break; 474 default: 475 text.append("PLAIN"); 476 } 477 text.append(", "); 478 text.append(value.getSize()); 479 setString(property, general, text.toString()); 480 } 481 482 /** Set the named property, from the specified configuration, using the given integer value. */ 483 public void setInt(String property, boolean general, int value) { 484 setString(property, general, String.valueOf(value)); 485 } 486 487 /** Set the named property, from the specified configuration, using the given Locale value. */ 488 public void setLocale(String property, boolean general, Locale value) { 489 StringBuffer text = new StringBuffer(""); 490 text.append(value.getLanguage()); 491 text.append("_"); 492 text.append(value.getCountry()); 493 setString(property, general, text.toString()); 494 } 495 496 /** Sets the value of the named property argument using the given string. */ 497 public void setString(String property, boolean general, String value) { 498 ///atherer.println("Set configuration property: " + property + " = " + value + (general ? "" : " [Collection]")); 499 try { 500 Document document = null; 501 if(general) { 502 document = general_config; 503 } 504 else if(collection_config != null) { 505 document = collection_config; 506 } 507 if(document != null) { 508 Element argument_element = null; 509 // Try to retrieve from cache 510 SoftReference reference = (SoftReference) get(property + general); 511 if(reference != null) { 512 argument_element = (Element) reference.get(); 513 } 514 if(argument_element == null) { 515 Element document_element = document.getDocumentElement(); 516 Element gatherer_element = (Element) MSMUtils.getNodeFromNamed(document_element, GATHERER_CONFIG); 517 NodeList arguments = document_element.getElementsByTagName(GATHERER_CONFIG_ARGUMENT); 518 boolean found = false; 519 for(int i = 0; argument_element == null && i < arguments.getLength(); i++) { 520 Element possible_element = (Element) arguments.item(i); 521 if(possible_element.getAttribute(ARGUMENT_NAME).equalsIgnoreCase(property)) { 522 argument_element = possible_element; 523 } 524 } 525 // If argument element is still null, create it in the target document. 526 if(argument_element == null) { 527 argument_element = document.createElement(GATHERER_CONFIG_ARGUMENT); 528 argument_element.setAttribute(ARGUMENT_NAME, property); 529 gatherer_element.appendChild(argument_element); 530 } 531 // Update cache 532 put(property + general, new SoftReference(argument_element)); 533 534 } 535 if(value == null) { 536 value = ""; 537 } 538 // Now remove any current text node children. 539 NodeList children = argument_element.getChildNodes(); 540 for(int i = 0; i < children.getLength(); i++) { 541 argument_element.removeChild(children.item(i)); 542 } 543 // Add a new text node child with the new value 544 argument_element.appendChild(document.createTextNode(value)); 545 } 546 } 547 catch (Exception error) { 548 } 549 } 550 551 private void updateUI() { 552 // Buttons 553 UIManager.put("Button.select", new ColorUIResource(getColor("coloring.button_selected_background", false))); 554 UIManager.put("Button.background", new ColorUIResource(getColor("coloring.button_background", false))); 555 UIManager.put("Button.foreground", new ColorUIResource(getColor("coloring.button_foreground", false))); 556 UIManager.put("ToggleButton.background", new ColorUIResource(getColor("coloring.button_background", false))); 557 UIManager.put("ToggleButton.foreground", new ColorUIResource(getColor("coloring.button_foreground", false))); 558 UIManager.put("ToggleButton.select", new ColorUIResource(getColor("coloring.button_selected_background", false))); 559 560 // All the things with a lovely Collection green background 561 UIManager.put("OptionPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); 562 UIManager.put("Panel.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); 563 UIManager.put("Label.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); 564 UIManager.put("TabbedPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); 565 UIManager.put("SplitPane.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); 566 UIManager.put("CheckBox.background", new ColorUIResource(getColor("coloring.collection_heading_background", false))); 567 568 // Editable coloring 569 UIManager.put("ComboBox.background", new ColorUIResource(getColor("coloring.button_background", false))); // Indicate clickable 570 UIManager.put("Tree.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); 571 UIManager.put("Tree.textBackground", new ColorUIResource(getColor("coloring.collection_tree_background", false))); 572 UIManager.put("ProgressBar.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); 573 UIManager.put("TextArea.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); 574 UIManager.put("TextField.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); 575 UIManager.put("Table.background", new ColorUIResource(getColor("coloring.collection_tree_background", false))); 576 577 // Selection color 578 UIManager.put("TabbedPane.selected", new ColorUIResource(getColor("coloring.collection_selection_background", false))); 579 UIManager.put("Tree.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); 580 UIManager.put("ComboBox.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); 581 UIManager.put("ProgressBar.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); 582 UIManager.put("TextArea.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); 583 UIManager.put("TextField.selectionBackground", new ColorUIResource(getColor("coloring.collection_selection_background", false))); 584 585 // Scroll bar stuff 586 UIManager.put("ScrollBar.background", new ColorUIResource(getColor("coloring.scrollbar_background", false))); 587 UIManager.put("ScrollBar.thumb", new ColorUIResource(getColor("coloring.scrollbar_foreground", false))); 588 if(Gatherer.g_man != null) { 589 JPanel pane = (JPanel) Gatherer.g_man.getContentPane(); 590 pane.updateUI(); 591 591 // Also update all of the tabs according to workflow. 592 593 594 595 596 597 598 599 600 601 592 Gatherer.g_man.workflowUpdate("Browser", get("workflow.browse", false)); 593 Gatherer.g_man.workflowUpdate("Mirroring", get("workflow.mirror", false)); 594 Gatherer.g_man.workflowUpdate("Collection", get("workflow.gather", false)); 595 Gatherer.g_man.workflowUpdate("MetaEdit", get("workflow.enrich", false)); 596 Gatherer.g_man.workflowUpdate("Build", get("workflow.design", false)); 597 Gatherer.g_man.workflowUpdate("Export", get("workflow.export", false)); 598 Gatherer.g_man.workflowUpdate("Create", get("workflow.create", false)); 599 Gatherer.g_man.workflowUpdate("Preview", get("workflow.preview", false)); 600 } 601 } 602 602 } -
trunk/gli/src/org/greenstone/gatherer/Dictionary.java
r4293 r4363 68 68 */ 69 69 public class Dictionary 70 71 72 73 74 75 76 77 78 79 80 81 82 70 extends HashMap { 71 /** A String which more explicitly states the Locale of this dictionary. */ 72 public String language = null; 73 /** A static reference to ourself. */ 74 static public Dictionary self; 75 /** The font used when displaying various html text. */ 76 private FontUIResource font = null; 77 /** A reference to remind us of the current locale. */ 78 private Locale locale = null; 79 /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/ 80 private ResourceBundle dictionary = null; 81 /** Constructs the Dictionary class by first checking if a Locale has been set. If not the default locale is used, and a ResourceBundle is created. Finally a single important String, Language, is made available outside the class so a more read-able version of the Locale of this Dictionary is present. 82 * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle. 83 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 84 public Dictionary(Locale locale, FontUIResource font) { 85 super(); 86 this.self = this; 87 // Initialize. 88 this.font = font; 89 if(locale == null) { 90 this.locale = Locale.getDefault(); 91 } 92 else { 93 this.locale = locale; 94 Locale.setDefault(locale); 95 } 96 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale); 97 // Now quickly read in language name. 98 language = dictionary.getString("Language"); 99 } 100 /** Change the currently loaded dictionary and update registered (ie dynamic) components as possible. */ 101 public void changeDictionary(Locale locale) { 102 this.locale = locale; 103 // Load new dictionary 104 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale); 105 // Refresh all registered component 106 for(Iterator keys = keySet().iterator(); keys.hasNext(); ) { 107 Object component = keys.next(); 108 String[] args = (String[]) get(component); 109 if(component instanceof AbstractButton) { 110 register((AbstractButton)component, args, true); 111 } 112 else if(component instanceof JComboBox) { 113 register((JComboBox)component, args, true); 114 } 115 else if(component instanceof JDialog) { 116 register((JDialog)component, args, true); 117 } 118 else if(component instanceof JFrame) { 119 register((JFrame)component, args, true); 120 } 121 else if(component instanceof JLabel) { 122 register((JLabel)component, args, true); 123 } 124 else if(component instanceof JTabbedPane) { 125 register((JTabbedPane)component, args, true); 126 } 127 else if(component instanceof JTextComponent) { 128 register((JTextComponent)component, args, true); 129 } 130 else if(component instanceof JTree) { 131 register((JTree)component, args, true); 132 } 133 else if(component instanceof TitledBorder) { 134 register((TitledBorder)component, args, true); 135 } 136 args = null; 137 component = null; 138 } 139 } 140 /** Remove the component from our registered components list. */ 141 public void deregister(Object component) { 142 remove(component); 143 } 144 144 145 145 /** Overloaded to call get with both a key and an empty argument array. 146 146 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 147 147 * @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. 148 148 */ 149 150 151 152 153 154 155 156 157 158 149 public String get(String key) { 150 return get(key, (String[])null); 151 } 152 /** Convienence method with transforms the second string argument into a string array. */ 153 public String get(String key, String arg) { 154 String[] args = new String[1]; 155 args[0] = arg; 156 return get(key, args); 157 } 158 /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR> 159 159 * 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>. 160 160 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. … … 162 162 * @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. 163 163 */ 164 165 166 164 public String get(String key, String args[]) { 165 try { 166 String initial = dictionary.getString(key); 167 167 // If the string contains arguments we have to insert them. 168 168 String complete = ""; 169 169 // While we still have initial string left. 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 170 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) { 171 // Remove preamble 172 int opening = initial.indexOf('{'); 173 int closing = initial.indexOf('}'); 174 complete = complete + initial.substring(0, opening); 175 // Parse arg_num 176 String arg_str = initial.substring(opening + 1, closing); 177 int arg_num = Integer.parseInt(arg_str); 178 if(closing + 1 < initial.length()) { 179 initial = initial.substring(closing + 1); 180 } 181 else { 182 initial = ""; 183 } 184 // Insert argument 185 if(args != null && 0 <= arg_num && arg_num < args.length) { 186 complete = complete + args[arg_num]; 187 } 188 else if(arg_num >= 32) { 189 String f_subargs[] = new String[1]; 190 if(font != null) { 191 f_subargs[0] = font.getFontName(); 192 } 193 else { 194 f_subargs[0] = "Arial"; 195 } 196 complete = complete + get("Farg" + arg_num, f_subargs); 197 } 198 } 199 return complete + initial; 200 } 201 catch (Exception e) { 202 if(!key.endsWith("_Tooltip")) { 203 Gatherer.println("Missing value for key: " + key); 204 } 205 return key; 206 } 207 } 208 /** Retrieve the two letter code of the current language we are using, according to the stored locale. 209 209 * @return A <strong>String</strong> containing the two letter ISO639 language code. 210 210 */ 211 212 213 214 215 216 211 public String getLanguage() { 212 return locale.getLanguage(); 213 } 214 /** Register an abstract button component. */ 215 public void register(AbstractButton component, String[] args, boolean already_registered) { 216 if(component != null) { 217 217 // Determine the key 218 219 220 221 222 223 224 218 String key = ""; 219 if(!already_registered) { 220 key = component.getText(); 221 } 222 else { 223 key = args[args.length - 1]; 224 } 225 225 // Update the component using the AWTEvent queue 226 227 228 229 226 String value = get(key, args); 227 String tooltip = get(key + "_Tooltip", (String[])null); 228 ChangeTask task = new AbstractButtonChangeTask(component, key, value, tooltip); 229 SwingUtilities.invokeLater(task); 230 230 // Register as necessary 231 232 233 234 235 236 237 238 239 231 if(!already_registered) { 232 args = ArrayTools.add(args, key); 233 put(component, args); 234 } 235 } 236 } 237 /** Register a combobox component. */ 238 public void register(JComboBox component, String[] args, boolean already_registered) { 239 if(component != null) { 240 240 // If not already registered then args will be null. 241 242 243 241 if(!already_registered) { 242 args = new String[component.getItemCount()]; 243 } 244 244 // Retrieve the tooltip. The key is mostly derived from the comboboxes name. 245 246 247 248 245 String key = component.getName(); 246 String tooltip = get(key + "_Tooltip", (String[])null); 247 ChangeTask task = new JComboBoxChangeTask(component, key, -1, tooltip); 248 SwingUtilities.invokeLater(task); 249 249 // Iterate through the combobox, updating values and recording the original key of each item in args. 250 251 252 253 254 255 256 257 250 for(int i = 0; i < args.length; i++) { 251 if(args[i] == null) { 252 args[i] = component.getItemAt(i).toString(); 253 } 254 String value = get(args[i], (String[])null); 255 task = new JComboBoxChangeTask(component, key, i, value); 256 SwingUtilities.invokeLater(task); 257 } 258 258 // Register if necessary 259 260 261 262 263 264 265 266 259 if(!already_registered) { 260 put(component, args); 261 } 262 } 263 } 264 /** Register a dialog component. */ 265 public void register(JDialog component, String[] args, boolean already_registered) { 266 if(component != null) { 267 267 // Determine the key 268 269 270 271 272 273 274 268 String key = ""; 269 if(!already_registered) { 270 key = component.getTitle(); 271 } 272 else { 273 key = args[args.length - 1]; 274 } 275 275 // Update the component using the AWTEvent queue 276 277 278 276 String value = get(key, args); 277 ChangeTask task = new JDialogChangeTask(component, key, value); 278 SwingUtilities.invokeLater(task); 279 279 // Register as necessary 280 281 282 283 284 285 286 287 288 280 if(!already_registered) { 281 args = ArrayTools.add(args, key); 282 put(component, args); 283 } 284 } 285 } 286 /** Register a frame component. */ 287 public void register(JFrame component, String[] args, boolean already_registered) { 288 if(component != null) { 289 289 // Determine the key 290 291 292 293 294 295 296 290 String key = ""; 291 if(!already_registered) { 292 key = component.getTitle(); 293 } 294 else { 295 key = args[args.length - 1]; 296 } 297 297 // Update the component using the AWTEvent queue 298 299 300 298 String value = get(key, args); 299 ChangeTask task = new JFrameChangeTask(component, key, value); 300 SwingUtilities.invokeLater(task); 301 301 // Register as necessary 302 303 304 305 306 307 308 309 310 302 if(!already_registered) { 303 args = ArrayTools.add(args, key); 304 put(component, args); 305 } 306 } 307 } 308 /** Register a label component. */ 309 public void register(JLabel component, String[] args, boolean already_registered) { 310 if(component != null) { 311 311 // Determine the key 312 313 314 315 316 317 318 312 String key = ""; 313 if(!already_registered) { 314 key = component.getText(); 315 } 316 else { 317 key = args[args.length - 1]; 318 } 319 319 // Update the component using the AWTEvent queue 320 321 322 320 String value = get(key, args); 321 ChangeTask task = new JLabelChangeTask(component, key, value); 322 SwingUtilities.invokeLater(task); 323 323 // Register as necessary 324 325 326 327 328 329 330 331 332 324 if(!already_registered) { 325 args = ArrayTools.add(args, key); 326 put(component, args); 327 } 328 } 329 } 330 /** Register a tab pane component. */ 331 public void register(JTabbedPane component, String[] args, boolean already_registered) { 332 if(component != null) { 333 333 // If not already registered then args will be null. 334 335 336 334 if(!already_registered) { 335 args = new String[component.getTabCount()]; 336 } 337 337 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args. 338 339 340 341 342 343 344 345 346 338 for(int i = 0; i < args.length; i++) { 339 if(args[i] == null) { 340 args[i] = component.getTitleAt(i); 341 } 342 String value = get(args[i], (String[])null); 343 String tooltip = get(args[i] + "_Tooltip", (String[])null); 344 ChangeTask task = new JTabbedPaneChangeTask(component, args[i], i, value, tooltip); 345 SwingUtilities.invokeLater(task); 346 } 347 347 // Register if necessary 348 349 350 351 352 353 354 355 348 if(!already_registered) { 349 put(component, args); 350 } 351 } 352 } 353 /** Register a text component. */ 354 public void register(JTextComponent component, String[] args, boolean already_registered) { 355 if(component != null) { 356 356 // Determine the key 357 358 359 360 361 362 363 357 String key = ""; 358 if(!already_registered) { 359 key = component.getText(); 360 } 361 else { 362 key = args[args.length - 1]; 363 } 364 364 // Update the component using the AWTEvent queue 365 366 367 368 365 String value = get(key, args); 366 String tooltip = get(key + "_Tooltip", (String[])null); 367 ChangeTask task = new JTextComponentChangeTask(component, key, value, tooltip); 368 SwingUtilities.invokeLater(task); 369 369 // Register as necessary 370 371 372 373 374 375 376 377 378 370 if(!already_registered) { 371 args = ArrayTools.add(args, key); 372 put(component, args); 373 } 374 } 375 } 376 /** Register a tree component. */ 377 public void register(JTree component, String[] args, boolean already_registered) { 378 if(component != null) { 379 379 // Retrieve the tooltip using the components name 380 381 382 383 380 String key = component.getName(); 381 String tooltip = get(key + "_Tooltip", (String[])null); 382 ChangeTask task = new JTreeChangeTask(component, key, tooltip); 383 SwingUtilities.invokeLater(task); 384 384 // A tree can never be previously registered. In otherwords the keys are harvested each time. Thus for a tree to remain consistant its up to the implementer to implement DictionaryTreeNode for the tree nodes! 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 385 ArrayList nodes = new ArrayList(); 386 nodes.add(component.getModel().getRoot()); 387 while(nodes.size() > 0) { 388 DictionaryTreeNode node = (DictionaryTreeNode) nodes.remove(0); 389 // Update 390 String value = get(node.getKey(), (String[])null); 391 task = new JTreeChangeTask(component, node.getKey(), node, value); 392 SwingUtilities.invokeLater(task); 393 // Add children to nodes 394 for(int i = 0; i < node.getChildCount(); i++) { 395 nodes.add(node.getChildAt(i)); 396 } 397 } 398 } 399 } 400 /** Register a titled border component. */ 401 public void register(TitledBorder component, String[] args, boolean already_registered) { 402 if(component != null) { 403 403 // Determine the key 404 405 406 407 408 409 410 404 String key = ""; 405 if(!already_registered) { 406 key = component.getTitle(); 407 } 408 else { 409 key = args[args.length - 1]; 410 } 411 411 // Update the component using the AWTEvent queue 412 413 414 412 String value = get(key, args); 413 ChangeTask task = new TitledBorderChangeTask(component, key, value); 414 SwingUtilities.invokeLater(task); 415 415 // Register as necessary 416 417 418 419 420 421 422 423 424 425 416 if(!already_registered) { 417 args = ArrayTools.add(args, key); 418 put(component, args); 419 } 420 } 421 } 422 /** A get method called internally by components that have been previously been registered, which means that arg[index] is the original key value. Index is usually the last entry in the array, however this is not true for comboboxes, tabbed panes and trees. */ 423 private String get(String[] args, int index) { 424 return get(args[index], args); 425 } 426 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 427 private abstract class ChangeTask 428 implements Runnable { 429 protected String key; 430 protected String value; 431 public ChangeTask(String key, String value) { 432 this.key = key; 433 this.value = value; 434 } 435 public void run() { 436 } 437 } 438 /** Update the text and tooltip for this button. */ 439 private class AbstractButtonChangeTask 440 extends ChangeTask { 441 private AbstractButton component; 442 private String tooltip; 443 public AbstractButtonChangeTask(AbstractButton component, String key, String value, String tooltip) { 444 super(key, value); 445 this.component = component; 446 this.tooltip = tooltip; 447 } 448 public void run() { 449 component.setText(value); 450 if(!tooltip.equals(key+"_Tooltip")) { 451 component.setToolTipText(tooltip); 452 } 453 else { 454 component.setToolTipText(null); 455 } 456 } 457 } 458 /** Update the text associated with a combobox. If the index used is -1 then we are setting the tooltip for this combobox. */ 459 private class JComboBoxChangeTask 460 extends ChangeTask { 461 private int index; 462 private JComboBox component; 463 public JComboBoxChangeTask(JComboBox component, String key, int index, String value) { 464 super(key, value); 465 this.component = component; 466 this.index = index; 467 } 468 public void run() { 469 if(index != -1) { 470 try { 471 MutableComboBoxEntry entry = (MutableComboBoxEntry)component.getItemAt(index); 472 entry.setText(value); 473 } 474 catch (Exception error) { 475 } 476 } 477 else { 478 if(!value.equals(key+"_Tooltip")) { 479 component.setToolTipText(value); 480 } 481 else { 482 component.setToolTipText(null); 483 } 484 } 485 } 486 } 487 /** Update the title of this dialog. */ 488 private class JDialogChangeTask 489 extends ChangeTask { 490 private JDialog component; 491 public JDialogChangeTask(JDialog component, String key, String value) { 492 super(key, value); 493 this.component = component; 494 } 495 public void run() { 496 component.setTitle(value); 497 } 498 } 499 /** Update the title of this frame. */ 500 private class JFrameChangeTask 501 extends ChangeTask { 502 private JFrame component; 503 public JFrameChangeTask(JFrame component, String key, String value) { 504 super(key, value); 505 this.component = component; 506 } 507 public void run() { 508 component.setTitle(value); 509 } 510 } 511 /** Update the text of this label. */ 512 private class JLabelChangeTask 513 extends ChangeTask { 514 private JLabel component; 515 public JLabelChangeTask(JLabel component, String key, String value) { 516 super(key, value); 517 this.component = component; 518 } 519 public void run() { 520 component.setText(value); 521 } 522 } 523 /** Updates a tabbed panes tab title and tooltip. */ 524 private class JTabbedPaneChangeTask 525 extends ChangeTask { 526 private int index; 527 private JTabbedPane component; 528 private String tooltip; 529 public JTabbedPaneChangeTask(JTabbedPane component, String key, int index, String value, String tooltip) { 530 super(key, value); 531 this.component = component; 532 this.index = index; 533 this.tooltip = tooltip; 534 } 535 public void run() { 536 component.setTitleAt(index, value); 537 if(!tooltip.equals(key+"_Tooltip")) { 538 component.setToolTipTextAt(index, tooltip); 539 } 540 else { 541 component.setToolTipTextAt(index, null); 542 } 543 } 544 } 545 /** Update the text and tooltip of this text component. */ 546 private class JTextComponentChangeTask 547 extends ChangeTask { 548 private JTextComponent component; 549 private String tooltip; 550 public JTextComponentChangeTask(JTextComponent component, String key, String value, String tooltip) { 551 super(key, value); 552 this.component = component; 553 this.tooltip = tooltip; 554 } 555 public void run() { 556 component.setText(value); 557 if(!tooltip.equals(key+"_Tooltip")) { 558 component.setToolTipText(tooltip); 559 } 560 else { 561 component.setToolTipText(null); 562 } 563 } 564 } 565 /** Update the tooltip of a tree and its tree node's labels. Shouldn't really ever be used on a dynamic tree, but is quite useful for a 'contents' tree type control. */ 566 private class JTreeChangeTask 567 extends ChangeTask { 568 private DictionaryTreeNode node; 569 private JTree component; 570 public JTreeChangeTask(JTree component, String key, String value) { 571 super(key, value); 572 this.component = component; 573 } 574 public JTreeChangeTask(JTree component, String key, DictionaryTreeNode node, String value) { 575 super(key, value); 576 this.component = component; 577 this.node = node; 578 } 579 public void run() { 580 if(value != null) { 581 node.setText(value); 582 ((DefaultTreeModel)component.getModel()).nodeChanged((TreeNode)node); 583 } 584 584 // Set the tool tip 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 585 else { 586 if(!value.equals(key+"_Tooltip")) { 587 component.setToolTipText(value); 588 } 589 else { 590 component.setToolTipText(null); 591 } 592 } 593 } 594 } 595 /** Update the title of this titled border. */ 596 private class TitledBorderChangeTask 597 extends ChangeTask { 598 private TitledBorder component; 599 public TitledBorderChangeTask(TitledBorder component, String key, String value) { 600 super(key, value); 601 this.component = component; 602 } 603 public void run() { 604 component.setTitle(value); 605 component.getParent().repaint(); 606 } 607 } 608 608 } 609 609 -
trunk/gli/src/org/greenstone/gatherer/GAuthenticator.java
r4293 r4363 55 55 import javax.swing.JPasswordField; 56 56 /** Provides a graphic authenticator for network password requests. 57 * @author John Thompson, Greenstone Digital Library, University of Waikato58 * @version 2.359 */57 * @author John Thompson, Greenstone Digital Library, University of Waikato 58 * @version 2.3 59 */ 60 60 public class GAuthenticator 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 61 extends Authenticator { 62 /** Indicates if this authentication prompt been cancelled, and if so rolls-back authentication. */ 63 private boolean authentication_cancelled = false; 64 /** The button used to cancel a prompt. */ 65 private JButton cancel_button = null; 66 /** The button used to submit the login/password. */ 67 private JButton ok_button = null; 68 /** A reference to the dialog prompt created so inner classes can dispose of it. */ 69 private JDialog dialog = null; 70 /** The password is a special starred out password field. */ 71 private JPasswordField password = null; 72 /** The default size of this dialog. */ 73 static final private Dimension SIZE = new Dimension(410,130); 74 /** Constructor. */ 75 public GAuthenticator() { 76 } 77 /** Prompt the user for authentication using a pretty dialog box. 78 78 * @return A <strong>PasswordAuthentication</strong> object containing the login and password valuees the user has submitted. 79 79 * @see org.greenstone.gatherer.GAuthenticator.AuthenticationActionListener 80 80 * @see org.greenstone.gatherer.GAuthenticator.RequestFocusListener 81 81 */ 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 82 protected PasswordAuthentication getPasswordAuthentication() { 83 // Component definition. 84 dialog = new JDialog (Gatherer.g_man, get("Title"), true); 85 dialog.setModal(true); 86 dialog.setSize(SIZE); 87 JPanel content_pane = (JPanel)dialog.getContentPane(); 88 JLabel title_label = new JLabel(getRequestingPrompt()); 89 JPanel user_panel = new JPanel(); 90 JLabel username_label = new JLabel(get("Username")); 91 JTextField username = new JTextField(); 92 JPanel password_panel = new JPanel(); 93 JLabel password_label = new JLabel(get("Password")); 94 password = new JPasswordField(); 95 password.setEchoChar ('*'); 96 JPanel button_panel = new JPanel(); 97 ok_button = new JButton(get("General.OK")); 98 cancel_button = new JButton(get("General.Cancel")); 99 // Connect listeners. 100 cancel_button.addActionListener(new AuthenticationActionListener(true)); 101 ok_button.addActionListener(new AuthenticationActionListener(false)); 102 password.addActionListener(new AuthenticationActionListener(false)); 103 username.addActionListener(new RequestFocusListener(password)); 104 // Layout the components. 105 user_panel.setLayout(new GridLayout(1,2)); 106 user_panel.add(username_label); 107 user_panel.add(username); 108 108 109 110 111 109 password_panel.setLayout(new GridLayout(1,2)); 110 password_panel.add(password_label); 111 password_panel.add(password); 112 112 113 114 115 113 button_panel.setLayout(new GridLayout(1,2)); 114 button_panel.add(ok_button); 115 button_panel.add(cancel_button); 116 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 117 content_pane.setLayout(new GridLayout(4,1)); 118 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 119 content_pane.add(title_label); 120 content_pane.add(user_panel); 121 content_pane.add(password_panel); 122 content_pane.add(button_panel); 123 // Position the window. 124 Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); 125 dialog.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 126 dialog.show(); 127 if(!authentication_cancelled) { 128 return new PasswordAuthentication(username.getText(), password.getPassword()); 129 } else { 130 return null; 131 } 132 } 133 133 134 134 /** Retrieve a phrase from the dictionary. 135 135 * @param key A <strong>String</strong> used to determine what phrase to retrieve. 136 136 * @return The required phrase as a <strong>String</strong>, or at least some meaningful error message. … … 138 138 * @see org.greenstone.gatherer.Gatherer 139 139 */ 140 141 142 143 144 145 146 147 148 149 150 151 152 140 private String get(String key) { 141 if(key.indexOf(".") == -1) { 142 key = "GAuthenticator." + key; 143 } 144 return Gatherer.dictionary.get(key); 145 } 146 /** Detects actions upon any control that attempt to submit the current details for authentication. */ 147 private class AuthenticationActionListener 148 implements ActionListener { 149 /** <i>true</i> if this authentication action cancels the authentication, <i>false</i> otherwise. */ 150 private boolean cancel_action = false; 151 /** Constructor. 152 * @param cancel_action <i>true</i> if this authentication action cancels the authentication, <i>false</i> otherwise. 153 153 */ 154 155 156 157 154 public AuthenticationActionListener(boolean cancel_action) { 155 this.cancel_action = cancel_action; 156 } 157 /** Any implementation of an ActionListener must include this method so that we can be informed when an action has been performed on our registered controls, allowing us to dispose of the authentication dialog after determining if this is a submit action or a cancel one. 158 158 * @param event An <strong>ActionEvent</strong> with information about the event that fired this method. 159 159 */ 160 161 162 163 164 165 166 167 168 169 170 171 160 public void actionPerformed(ActionEvent event) { 161 authentication_cancelled = cancel_action; 162 dialog.dispose(); 163 } 164 } 165 /** This listener detects actions on registered controls, and when they occur ensures the focus is moved to some targetted component. */ 166 private class RequestFocusListener 167 implements ActionListener { 168 /*The <strong>Component</strong> you wish to gain focus when an action is performed on a registered control. */ 169 private Component target = null; 170 /** Constructor. 171 * @param target The <strong>Component</strong> you wish to gain focus when an action is performed on a registered control. 172 172 */ 173 174 175 176 173 public RequestFocusListener(Component target) { 174 this.target = target; 175 } 176 /** Any implementation of an ActionListener must include this method so that we can be informed when an action has been performed on our registered controls, allowing us to request focus in the target control. 177 177 * @param event An <strong>ActionEvent</strong> with information about the event that fired this method. 178 178 */ 179 180 181 182 179 public void actionPerformed(ActionEvent event) { 180 target.requestFocus(); 181 } 182 } 183 183 } 184 184 -
trunk/gli/src/org/greenstone/gatherer/Gatherer.java
r4349 r4363 62 62 import sun.misc.*; 63 63 /** Containing the main() method for the Gatherer, this class is the starting point for the rest of the application. It first parses the command line arguments, preparing to update the configuration as required. Next it loads several important support classes such as the Configuration and Dictionary. Finally it creates the other important managers and sends them on their way. 64 * @author John Thompson, Greenstone Digital Library, University of Waikato65 * @version 2.366 */64 * @author John Thompson, Greenstone Digital Library, University of Waikato 65 * @version 2.3 66 */ 67 67 public class Gatherer { 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 68 /** Has the exit flag been set? <i>true</i> if so, <i>false</i> otherwise. */ 69 public boolean exit = false; 70 /** The size of the Gatherer window. */ 71 public Dimension frame_size = null; 72 /** A temporary shared memory area to store HIndexes to speed up metadata.xml writing. */ 73 public Hashtable known_indexes = null; 74 /** Legacy copy of the debug_ps. */ 75 public PrintStream debug_ps; 76 /** All of the external applications that must exit before we close the Gatherer. */ 77 public Vector apps = new Vector(); 78 /** Messages that have been issued before we have anyway to show them, ie prior to Log initialization. */ 79 public Vector waiting_messages = new Vector(); 80 /** The manager in charge of remembering what file extension gets opened with what program. */ 81 static public FileAssociationManager assoc_man; 82 /** A public reference to the CollectionManager. */ 83 static public CollectionManager c_man; 84 /** A public reference to the Gatherer's configuration. */ 85 static public Configuration config; 86 /** A public reference to the Dictionary. */ 87 static public Dictionary dictionary; 88 /** A public reference to the FileManager. */ 89 static public FileManager f_man; 90 /** A public reference to the GUIManager. */ 91 static public GUIManager g_man; 92 /** A static reference to ourselves. */ 93 static public Gatherer self; 94 /** A public reference to the message log. */ 95 static public Log log; 96 /** The debug print stream. */ 97 static public PrintStream debug; 98 /** The name of the necessary environment variable to check for in the programs environment. */ 99 static public String KEY = "GSDLPATH"; 100 /** Extra environment information which must be set before shell processes will run properly. Should always be null if the startup script/program has done its job properly. */ 101 static public String extra_env[] = null; 102 private GSDLSiteConfig gsdlsite_cfg = null; 103 private ExternalApplication server = null; 104 /** The name of the Gatherers configuration file. */ 105 static private String CONFIG_FILE_NAME = "gatherer.cfg"; 106 /** Constructor. Make the three main modules, c_man, f_man and g_man, and any other necessary classes such as Dictionary. 107 * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>. 108 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>. 109 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>. 110 * @param debug <i>true</i> to print verbose debug messages to "debug.txt", <i>false</i> otherwise. 111 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions. 112 * @param splash A reference to the splash screen. 113 * @param no_load <i>true</i> to prevent the previously opened collection from reopening. 114 * @see java.io.FileOutputStream 115 * @see java.io.PrintStream 116 * @see java.lang.Exception 117 * @see java.lang.StringBuffer 118 * @see java.util.Calendar 119 * @see org.greenstone.gatherer.Configuration 120 * @see org.greenstone.gatherer.Dictionary 121 * @see org.greenstone.gatherer.Gatherer.CTRLCHandler 122 * @see org.greenstone.gatherer.GAuthenticator 123 * @see org.greenstone.gatherer.collection.CollectionManager 124 * @see org.greenstone.gatherer.file.FileManager 125 * @see org.greenstone.gatherer.gui.GUIManager 126 * @see org.greenstone.gatherer.gui.Splash 127 */ 128 public Gatherer() { 129 this.self = this; 130 } 131 132 public void run(Dimension size, String gsdl_path, String exec_path, boolean debug_enabled, String perl_path, boolean no_load, Splash splash, String open_collection) { 133 134 // This will hopefully catch ctrl-c and terminate, and exit gracefully. However it is platform specific, and may not be supported by some JVMs. 135 /** It does, but I get bloddy sick of it working when the Gatherer hangs. 136 CTRLCHandler handler = new CTRLCHandler(); 137 Signal.handle(new Signal("INT"), handler); 138 Signal.handle(new Signal("TERM"), handler); 139 handler = null; 140 */ 141 // Create the debug stream only if required. 142 if(debug_enabled) { 143 try { 144 Calendar now = Calendar.getInstance(); 145 StringBuffer name = new StringBuffer("debug"); 146 name.append(now.get(Calendar.DATE)); 147 name.append("-"); 148 name.append(now.get(Calendar.MONTH)); 149 name.append("-"); 150 name.append(now.get(Calendar.YEAR)); 151 name.append(".txt"); 152 this.debug = new PrintStream(new FileOutputStream(name.toString())); 153 Properties props = System.getProperties(); 154 props.list(debug); 155 // Legacy 156 debug_ps = debug; 157 } 158 catch(Exception error) { 159 ///ystem.err.println("Error in Gatherer.init(): " + error); 160 error.printStackTrace(); 161 System.exit(1); 162 } 163 } 164 try { 165 165 // Create log 166 166 log = new Log(); 167 167 // Load Config 168 168 loadConfig(gsdl_path, exec_path, perl_path); 169 169 170 170 // Read Dictionary 171 171 dictionary = new Dictionary(config.getLocale("general.locale", true), config.getFont("general.font", true)); 172 172 173 173 // If we were given a server run it if neccessary. 174 175 176 174 if(config.exec_file != null) { 175 startServerEXE(); 176 } 177 177 178 178 // Having loaded the configuration (necessary to determine if certain warnings have been disabled) and dictionary, we now check if the necessary path variables have been provided. 179 179 180 181 182 183 184 185 180 if(config.exec_file == null && config.exec_address == null) { 181 missingEXEC(dictionary); 182 } 183 if(gsdl_path == null) { 184 missingGSDL(dictionary); 185 } 186 186 // Perl path is a little different as it is perfectly ok to start the Gatherer without providing a perl path 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 187 boolean found_perl = false; 188 if(config.perl_path != null) { 189 // See if the file pointed to actually exists 190 File perl_file = new File(config.perl_path); 191 found_perl = perl_file.exists(); 192 perl_file = null; 193 } 194 if(config.perl_path == null || !found_perl) { 195 // Run test to see if we can run perl as is. 196 PerlTest perl_test = new PerlTest(); 197 if(perl_test.found()) { 198 // If so replace the perl path with the system default (or null for unix). 199 config.perl_path = perl_test.toString(); 200 found_perl = true; 201 } 202 } 203 if(!found_perl) { 204 // Time for an error message. 205 missingPERL(dictionary); 206 } 207 else { 208 ///ystem.err.println("Now perl_path = " + config.perl_path); 209 } 210 210 211 211 // Size the screen 212 213 214 215 216 217 218 219 220 221 212 Dimension temp = config.getDimension("general.size", true); 213 if(temp != null) { 214 size = temp; 215 } 216 if (size.height > config.screen_size.height) { 217 size.setSize(size.width, config.screen_size.height); 218 } 219 if (size.width > config.screen_size.width) { 220 size.setSize(config.screen_size.width, size.height); 221 } 222 222 // Set default font 223 223 setUIFont(config.getFont("general.font", true), config.getFont("general.tooltip_font", true)); 224 224 // Set up proxy 225 225 setProxy(); 226 226 // Now we set up an Authenticator 227 228 229 227 Authenticator.setDefault(new GAuthenticator()); 228 229 assoc_man = new FileAssociationManager(); 230 230 // Create File Manager 231 231 f_man = new FileManager(); 232 232 // Create Collection Manager 233 233 c_man = new CollectionManager(); 234 234 // If there was an open collection last session, reopen it. 235 236 237 238 239 240 235 if(open_collection == null) { 236 open_collection = config.getString("general.open_collection", true); 237 } 238 if(!no_load && open_collection.length() > 0) { 239 c_man.loadCollection(open_collection); 240 } 241 241 // Create GUI Manager (last) or else suffer the death of a thousand NPE's 242 243 244 242 splash.toFront(); 243 g_man = new GUIManager(size); 244 g_man.display(); 245 245 246 246 // Center the screen, if this is do-able (not under most linux window managers apparently. In fact you're lucky if they listen to any of your screen size requests). 247 248 247 g_man.setLocation((config.screen_size.width - size.width) / 2, (config.screen_size.height - size.height) / 2); 248 g_man.setVisible(true); 249 249 // The 'after-display' triggers several events which don't occur until after the visual components are actually available on screen. Examples of these would be the various html renderings, as they can't happen offscreen. 250 250 g_man.afterDisplay(); 251 251 // Hide the splash. 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 252 splash.hide(); 253 splash.destroy(); 254 splash = null; 255 } 256 catch (Exception error) { 257 error.printStackTrace(); 258 } 259 } 260 /** Writes a message to the debug filestream. 261 * @param message The message as a <strong>String</strong>. 262 */ 263 public void debug(String message) { 264 debug(null, message); 265 } 266 /** Writes a message to the debug filestream. 267 * @param error The <strong>Exception</strong> associated with this message, or <i>null</i> for no exception. 268 * @param message The message as a <strong>String</strong>. 269 * @see java.io.FileOutputStream 270 * @see java.io.PrintStream 271 * @see java.lang.Exception 272 */ 273 273 public void debug(Exception exception, String message) { 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 274 if(message != null) { 275 Gatherer.println(message); 276 } 277 if(exception != null) { 278 Gatherer.printStackTrace(exception); 279 } 280 } 281 /** Exits the Gatherer after ensuring that things needing saving are saved. 282 * @see java.io.FileOutputStream 283 * @see java.io.PrintStream 284 * @see java.lang.Exception 285 * @see javax.swing.JOptionPane 286 * @see org.greenstone.gatherer.Configuration 287 * @see org.greenstone.gatherer.collection.CollectionManager 288 * @see org.greenstone.gatherer.gui.GUIManager 289 */ 290 public void exit() { 291 exit = true; 292 // If we have an open collection make note of it. 293 config.setString("general.open_collection", true, null); 294 if(c_man.ready()) { 295 295 ///ystem.err.println("Collection open."); 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 296 if(c_man.saved()) { 297 ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time."); 298 config.setString("general.open_collection", true, c_man.getCollectionFilename()); 299 } 300 c_man.closeCollection(); 301 } 302 if(assoc_man != null) { 303 assoc_man.destroy(); 304 assoc_man = null; 305 } 306 // Check the current size of the Gatherer, and if its reasonable (ie greater than minsize) then store it for next time. 307 Dimension size = g_man.getSize(); 308 if(size.width > 100 && size.height > 100) { 309 config.setDimension("general.size", true, size); 310 } 311 312 // Save configuration. 313 saveConfig(); 314 // Flush debug 315 if(debug != null) { 316 try { 317 debug.flush(); 318 debug.close(); 319 } 320 catch (Exception error) { 321 error.printStackTrace(); 322 } 323 } 324 325 // If we started a server, we should try to stop it. 326 if(gsdlsite_cfg != null) { 327 stopServerEXE(); 328 } 329 330 if(apps.size() == 0) { 331 System.exit(0); 332 } 333 else { 334 JOptionPane.showMessageDialog(g_man, get("General.Outstanding_Processes"), get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE); 335 g_man.hide(); 336 } 337 } 338 /** Overloaded to call get with both a key and an empty argument array. 339 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 340 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call. 341 */ 342 public String get(String key) { 343 return dictionary.get(key, (String[])null); 344 } 345 /** Overloaded to call get with both a key and an argument array with one element. 346 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 347 * @param arg A single argument as a <strong>String</strong>. 348 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call. 349 */ 350 public String get(String key, String arg) { 351 String args[] = new String[1]; 352 args[0] = arg; 353 return dictionary.get(key, args); 354 } 355 /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR> 356 * Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {<I>n</I>}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Farg<I>n</I>. 357 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle. 358 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String. 359 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatically populated with formatting Strings of with argument String provided in the get call. 360 * @see org.greenstone.gatherer.Gatherer 361 * @see org.greenstone.gatherer.Dictionary 362 */ 363 public String get(String key, String args[]) { 364 return dictionary.get(key, args); 365 } 366 /** Retrieve the metadata directory, as required by any MSMCaller implementation. 367 * @return The currently active collection metadata directory as a <strong>String</strong>. 368 * @see org.greenstone.gatherer.collection.CollectionManager 369 */ 370 public String getCollectionMetadata() { 371 if(c_man != null && c_man.ready()) { 372 return c_man.getCollectionMetadata(); 373 } 374 return ""; 375 } 376 /** Retrieve a reference to the frame that any dialog boxes will appear relative to, as required by any MSMCaller or CDMCaller implementation. 377 * @return A <strong>JFrame</strong>. 378 * @see org.greenstone.gatherer.gui.GUIManager 379 */ 380 public JFrame getFrame() { 381 return g_man; 382 } 383 /** Method to retrieve a reference to the metadata set manager class. This is then used to create the 'metadataset' commands in the collection configuration file. 384 * @return A reference to the <Strong>MetadataSetManager</strong>. 385 * @see org.greenstone.gatherer.collection.CollectionManager 386 */ 387 public MetadataSetManager getMSM() { 388 if(c_man != null && c_man.getCollection() != null && c_man.getCollection().msm != null) { 389 return c_man.getCollection().msm; 390 } 391 return null; 392 } 393 /** Used to 'spawn' a new child application when a file is double clicked. 394 * @param command The command to run in the child process to start the application, garnered from the registry of a default associations file, and presented as a <strong>String</strong>. 395 * @see java.util.Vector 396 * @see org.greenstone.gatherer.Gatherer.ExternalApplication 397 */ 398 public void spawnApplication(File file) { 399 String command = assoc_man.getCommand(file); 400 if(command != null) { 401 ExternalApplication app = new ExternalApplication(command); 402 apps.add(app); 403 app.start(); 404 } 405 else { 406 406 ///ystem.err.println("No open command available."); 407 408 409 410 411 412 413 407 } 408 } 409 410 /** Some startup arguments to the Gatherer have been encoded, where ' ' is replaced with '%', in order to allow arguments containing spaces to be parsed correctly by the JVM, and this method restores these arguments to thier original state. 411 * @param encoded An encoded <strong>String</strong>. 412 * @return The decoded <strong>String</strong>. 413 */ 414 414 static public String decode(String encoded) { 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 415 return encoded.replace('%', ' '); 416 } 417 /** The entry point into the Gatherer. Parses arguments. 418 * @param args A collection of arguments that may include: initial screen size, dictionary, path to the GSDL etc. 419 * @see java.io.File 420 * @see java.io.FileInputStream 421 * @see java.lang.Exception 422 * @see java.util.Properties 423 * @see org.greenstone.gatherer.Dictionary 424 * @see org.greenstone.gatherer.Gatherer 425 * @see org.greenstone.gatherer.gui.Splash 426 */ 427 static public void main(String[] args) { 428 // A serious hack, but its good enough to stop crappy 'Could not lock user prefs' error messages. 429 // Thanks to Walter Schatz from the java forums. 430 System.setProperty("java.util.prefs.syncInterval","2000000"); // One message every 600 hours! 431 432 Gatherer gatherer = new Gatherer(); 433 434 boolean debug = false; 435 boolean no_load = false; 436 Dictionary dictionary = new Dictionary(null, null); // Default dictionary. Only used for starting error messages. 437 Dimension size = new Dimension(800, 540); 438 String exec_path = null; 439 String extra = null; 440 String filename = null; 441 String gsdl_path = null; 442 String perl_path = null; 443 // Parse arguments 444 for(int i = 0; i < args.length; i++) { 445 if(args[i].equals("-gsdl")) { 446 gsdl_path = decode(args[i+1]); 447 if(!gsdl_path.endsWith(File.separator)) { 448 gsdl_path = gsdl_path + File.separator; 449 } 450 } 451 if(args[i].equals("-load")) { 452 filename = decode(args[i+1]); 453 } 454 else if(args[i].equals("-library") && (i + 1) < args.length) { 455 exec_path = args[i+1]; 456 // If we are on a non-windows system, and thus the local server is unavailable, we can append http:// if no protocol found. 457 if(exec_path.lastIndexOf(":", 5) == -1) { 458 exec_path = "http://" + exec_path; 459 } 460 // If the user has given us an address, but it ends with a '/' we assume we're using the greenstone library.cgi 461 if(exec_path.startsWith("http://") && exec_path.endsWith("/")) { 462 exec_path = exec_path + "library"; 463 } 464 } 465 else if(args[i].equals("-perl")) { 466 perl_path = decode(args[i+1]); 467 // Test whether this points to the Perl bin directory or the Perl executable itself. 468 File perl_file = new File(perl_path); 469 if(perl_file.isDirectory()) { 470 // If this is windows we create a child file perl.exe, otherwise we create perl 471 if(Utility.isWindows()) { 472 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_WINDOWS); 473 } 474 else { 475 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_UNIX); 476 } 477 // And store this new path. 478 perl_path = perl_file.getAbsolutePath(); 479 perl_file = null; 480 } 481 // Otherwise its fine as it is 482 ///ystem.err.println("Perl executable is: " + perl_path); 483 } 484 else if(args[i].equals("--help") || args[i].equals("-help") || args[i].equals("?") || args[i].equals("/?") || args[i].equals("/help")) { 485 printUsage(dictionary); 486 System.exit(0); 487 } 488 else if(args[i].equals("--debug") || args[i].equals("-debug")) { 489 debug = true; 490 } 491 491 // Don't load any previous collection. Convenient for me 492 493 494 492 else if(args[i].equals("-no_load")) { 493 no_load = true; 494 } 495 495 // Check if they want it skinned. 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 496 else if(args[i].equals("-skinlf")) { 497 // SkinLF 498 try { 499 SkinLookAndFeel.setSkin(SkinLookAndFeel.loadThemePackDefinition(SkinUtils.toURL(new File("lib/greenaqua/greenaqua.xml")))); 500 SkinLookAndFeel.enable(); 501 } 502 catch (Exception error) { 503 ///ystem.err.println("Error: " + error); 504 error.printStackTrace(); 505 } 506 } 507 } 508 509 // Splash screen. 510 Splash splash = new Splash(); 511 512 // We take appropriate action when an empty gsdl_path is detected. 513 if(gsdl_path == null) { 514 514 // Check for the presence of the path.cfg file. 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 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 515 File path_file = new File("path.cfg"); 516 if(path_file.exists()) { 517 try { 518 // Read in then add each property to the Java Environment. 519 FileInputStream prop_file = new FileInputStream(path_file); 520 Properties p = new Properties(); 521 p.load(prop_file); 522 extra = p.getProperty(KEY); 523 } 524 catch (Exception error) { 525 System.out.println("Error in main():"); 526 error.printStackTrace(); 527 System.exit(1); 528 } 529 } 530 else { 531 missingGSDL(dictionary); 532 } 533 } 534 // We take appropriate action when an empty bin_path is detected. 535 gatherer.run(size, gsdl_path, exec_path, debug, perl_path, no_load, splash, filename); 536 } 537 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer. 538 */ 539 static public void missingEXEC(Dictionary dictionary) { 540 WarningDialog dialog = new WarningDialog("warning.MissingEXEC", false); 541 dialog.display(); 542 dialog.dispose(); 543 dialog = null; 544 ///ystem.out.println(dictionary.get("General.Missing_EXEC")); 545 } 546 /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer. 547 */ 548 static public void missingGSDL(Dictionary dictionary) { 549 WarningDialog dialog = new WarningDialog("warning.MissingGSDL", false); 550 dialog.display(); 551 dialog.dispose(); 552 dialog = null; 553 ///ystem.out.println(dictionary.get("General.Missing_GSDL")); 554 } 555 /** Prints a warning message about a missing PERL path, which although not fatal pretty much ensures no collection creation/building will work properly in the Gatherer. */ 556 static public void missingPERL(Dictionary dictionary) { 557 WarningDialog dialog = new WarningDialog("warning.MissingPERL", false); 558 dialog.display(); 559 dialog.dispose(); 560 dialog = null; 561 ///ystem.out.println(dictionary.get("General.Missing_PERL")); 562 } 563 /** Print a message to the debug stream. */ 564 static synchronized public void println(String message) { 565 if(debug != null) { 566 debug.println(message); 567 } 568 else { 569 System.err.println(message); 570 } 571 } 572 /** Print a stack trace to the debug stream. */ 573 static synchronized public void printStackTrace(Exception exception) { 574 if(debug != null) { 575 exception.printStackTrace(debug); 576 } 577 else { 578 exception.printStackTrace(); 579 } 580 } 581 /** Prints a usage message to screen. 582 */ 583 static public void printUsage(Dictionary dictionary) { 584 System.out.println(dictionary.get("General.Usage")); 585 } 586 587 /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator. 588 * @see java.lang.Exception 589 * @see java.lang.System 590 * @see java.net.Authenticator 591 * @see org.greenstone.gatherer.Configuration 592 * @see org.greenstone.gatherer.GAuthenticator 593 */ 594 static public void setProxy() { 595 try {// Can throw several exceptions 596 if(Gatherer.config.get("general.use_proxy", true)) { 597 System.setProperty("http.proxyType", "4"); 598 System.setProperty("http.proxyHost", Gatherer.config.getString("general.proxy_host", true)); 599 System.setProperty("http.proxyPort", Gatherer.config.getString("general.proxy_port", true)); 600 System.setProperty("http.proxySet", "true"); 601 } else { 602 System.setProperty("http.proxySet", "false"); 603 } 604 } catch (Exception error) { 605 Gatherer.println("Error in Gatherer.initProxy(): " + error); 606 Gatherer.printStackTrace(error); 607 } 608 } 609 610 /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font. 611 * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>. 612 * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>. 613 * @see java.util.Enumeration 614 * @see javax.swing.UIManager 615 */ 616 static public void setUIFont (FontUIResource f, FontUIResource ttf){ 617 // sets the default font for all Swing components. 618 // ex. 619 // setUIFont (new FontUIResource("Serif",Font.ITALIC,12)); 620 // 621 Enumeration keys = UIManager.getDefaults().keys(); 622 while (keys.hasMoreElements()) { 623 Object key = keys.nextElement(); 624 Object value = UIManager.get (key); 625 if (value instanceof FontUIResource) 626 UIManager.put (key, f); 627 } 628 // Now set the tooltip font to some fixed width font 629 UIManager.put("ToolTip.font", ttf); 630 } 631 632 /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization. 633 * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>. 634 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>. 635 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>. 636 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions. 637 * @see java.io.FileInputStream 638 * @see java.io.ObjectInputStream 639 * @see java.lang.Exception 640 * @see org.greenstone.gatherer.Configuration 641 */ 642 private void loadConfig(String gsdl_path, String exec_path, String perl_path) { 643 try { 644 config = new Configuration(gsdl_path, exec_path, perl_path); 645 } 646 catch (Exception error) { 647 Gatherer.println("config.xml is not a well formed XML document."); 648 Gatherer.printStackTrace(error); 649 } 650 } 651 652 /** Creates and dispatches a message given the initial details. 653 * @param level An <i>int</i> indicating the message level for this message. 654 * @param message A <strong>String</strong> which contains the payload of this message. 655 * @see org.greenstone.gatherer.Message 656 * @see org.greenstone.gatherer.Log 657 */ 658 private void message(int level, String message) { 659 Message msg = new Message(Message.GENERAL, level, message); 660 log.add(msg); 661 } 662 663 /** Causes the general configuration file to export itself to xml. Doesn't effect any remaining collection configuration, as its up to the collection manager to handle them (especially since we have no idea where they are going). */ 664 private void saveConfig() { 665 try { 666 config.save(); 667 } catch (Exception error) { 668 Gatherer.printStackTrace(error); 669 } 670 } 671 672 private void startServerEXE() { 673 if(config.exec_file != null && config.exec_address == null && Utility.isWindows()) { 674 674 // First of all we create a GSDLSiteCFG object and check if a URL is already present, in which case the server is already running. 675 676 675 gsdlsite_cfg = new GSDLSiteConfig(config.exec_file); 676 String url = gsdlsite_cfg.getURL(); 677 677 // If its already running then set exec address. 678 679 680 681 682 683 684 678 if(url != null) { 679 try { 680 config.exec_address = new URL(url); 681 } 682 catch(Exception error) { 683 } 684 } 685 685 // Otherwise its time to run the server in a spawned process. 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 686 if(config.exec_address == null && config.exec_file.exists()) { 687 // Configure for immediate entry. Note that this only works if the gsdlsite.cfg file exists. 688 gsdlsite_cfg.set(); 689 // Spawn server 690 server = new ExternalApplication(config.exec_file.getAbsolutePath()); 691 server.start(); 692 // Now we have to wait until program has started. We do this by reloading and checking 693 try { 694 gsdlsite_cfg.load(); 695 while((url = gsdlsite_cfg.getURL()) == null) { 696 synchronized(this) { 697 wait(1000); 698 } 699 gsdlsite_cfg.load(); 700 } 701 // Ta-da. Now the url should be available. 702 config.exec_address = new URL(url); 703 } 704 catch (Exception error) { 705 error.printStackTrace(); 706 } 707 } 708 708 // Can't do a damb thing. 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 709 } 710 System.err.println("Having started server.exe, exec_address is: " + config.exec_address); 711 } 712 713 private void stopServerEXE() { 714 // See if its already exited for some reason. 715 gsdlsite_cfg.load(); 716 if(gsdlsite_cfg.getURL() != null) { 717 // Send the command for it to exit. 718 //Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.QUIT_COMMAND); 719 // Wait until it exits. 720 try { 721 gsdlsite_cfg.load(); 722 while(gsdlsite_cfg.getURL() != null) { 723 synchronized(this) { 724 wait(1000); 725 } 726 gsdlsite_cfg.load(); 727 } 728 } 729 catch (Exception error) { 730 error.printStackTrace(); 731 } 732 } 733 // Restore the gsdlsite_cfg 734 if(gsdlsite_cfg != null) { 735 gsdlsite_cfg.restore(); 736 } 737 gsdlsite_cfg = null; 738 } 739 740 /** This private class contains an instance of an external application running within a JVM shell. It is important that this process sits in its own thread, but its more important that when we exit the Gatherer we don't actually System.exit(0) the Gatherer object until the user has volunteerily ended all of these child processes. Otherwise when we quit the Gatherer any changes the users may have made in external programs will be lost and the child processes are automatically deallocated. */ 741 private class ExternalApplication 742 extends Thread { 743 private Process process = null; 744 /** The initial command string given to this sub-process. */ 745 private String command = null; 746 /** Constructor. 747 * @param command The initial command <strong>String</strong>. 748 */ 749 public ExternalApplication(String command) { 750 this.command = command; 751 } 752 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer. 753 * @see java.lang.Exception 754 * @see java.lang.Process 755 * @see java.lang.Runtime 756 * @see java.lang.System 757 * @see java.util.Vector 758 */ 759 public void run() { 760 760 // Call an external process using the args. 761 762 763 764 765 766 767 768 769 761 try { 762 debug("Running " + command); 763 Runtime rt = Runtime.getRuntime(); 764 process = rt.exec(command); 765 process.waitFor(); 766 } 767 catch (Exception error) { 768 debug(error, "Error in ExternalApplication.run(): " + error); 769 } 770 770 // Remove ourself from Gatherer list of threads. 771 771 apps.remove(this); 772 772 // Call exit if we were the last outstanding child process thread. 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 773 if(apps.size() == 0 && exit == true) { 774 stopServerEXE(); 775 System.exit(0); 776 } 777 } 778 public void stopExternalApplication() { 779 if(process != null) { 780 process.destroy(); 781 } 782 } 783 } 784 /** This class is intented to detect a specific SIGNAL, in this case SIGINT, and exit properly, rather than letting the Gatherer be interrupted which has the potential to leave erroneous lock files. */ 785 private class CTRLCHandler 786 implements SignalHandler { 787 /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */ 788 private boolean ignore = false; 789 /** The method called by the system to inform us a signal has occured. 790 * @param sig The <strong>Signal</strong> itself. 791 * @see org.greenstone.gatherer.collection.CollectionManager 792 */ 793 public void handle(Signal sig) { 794 if(!ignore) { 795 ignore = true; 796 // handle SIGINT 797 System.out.println("Caught Ctrl-C..."); 798 if(c_man != null && c_man.ready()) { 799 c_man.closeCollection(); 800 } 801 exit(); 802 ignore = false; 803 } 804 } 805 } 806 807 private class PerlTest { 808 809 private String[] command = new String[2]; 810 811 public PerlTest() { 812 if(Utility.isWindows()) { 813 command[0] = Utility.PERL_EXECUTABLE_WINDOWS; 814 } 815 else { 816 command[0] = Utility.PERL_EXECUTABLE_UNIX; 817 } 818 command[1] = "-version"; 819 } 820 821 public boolean found() { 822 boolean found = false; 823 try { 824 Process prcs = Runtime.getRuntime().exec(command); 825 prcs.waitFor(); 826 found = (prcs.exitValue() == 0); 827 prcs = null; 828 } 829 catch(Exception error) { 830 } 831 return found; 832 } 833 834 public String toString() { 835 return command[0]; 836 } 837 } 838 838 } 839 839 -
trunk/gli/src/org/greenstone/gatherer/Log.java
r4293 r4363 47 47 */ 48 48 public class Log 49 50 51 52 53 54 55 49 implements ActionListener { 50 /** A reference to the dialog box the log will be displayed in so our inner classes have the ability to interact. */ 51 private JDialog dialog = null; 52 /** Constructor */ 53 public Log() { 54 } 55 /** Any implementation of ActionListener must include this method so 56 56 * we can be informed when an action has occured. 57 57 * @param event An <strong>ActionEvent</strong> which contains the details about the 58 58 * event which fired this call. 59 59 */ 60 61 62 63 64 65 60 public void actionPerformed(ActionEvent event) { 61 if(event.getSource() == dialog) { 62 dialog.dispose(); 63 } 64 } 65 /** Add a message to the log. Note that this may be called from within 66 66 * several different threads so I'd better synchronize. 67 67 * @param message A <strong>Message</strong> which contains, surprisingly enough, the … … 69 69 * @see org.greenstone.gatherer.Gatherer 70 70 */ 71 72 73 74 75 71 public synchronized void add(Message message) { 72 //Gatherer.println(message.toString()); 73 /* @TODO */ 74 } 75 /** Create a modal dialog box components to allow the user to view, and 76 76 * filter the log. 77 77 */ 78 79 80 78 public void display() { 79 /* @TODO */ 80 } 81 81 } 82 82 -
trunk/gli/src/org/greenstone/gatherer/Message.java
r4293 r4363 53 53 import java.util.Random; 54 54 /** Provides an event-type wrapper around a message for displaying to the user. 55 * @author John Thompson, Greenstone Digital Library, University of Waikato56 * @version 2.157 */55 * @author John Thompson, Greenstone Digital Library, University of Waikato 56 * @version 2.1 57 */ 58 58 public class Message { 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 59 /** The level of this message. See above. */ 60 public int level; 61 /** The source of this message. See above. */ 62 public int source; 63 /** The age of this message. Used for ordering. */ 64 public long time; 65 /** The actual message content of this message. */ 66 public String message; 67 /** Used to indicate the source of the message is anywhere in general. */ 68 static final public int GENERAL = 0; 69 /** Used to indicate the source of the message is the browsing methods. */ 70 static final public int BROWSER = 1; 71 /** Used to indicate the source of the message is the mirroring methods. */ 72 static final public int MIRRORING = 2; 73 /** Used to indicate the source of the message is the file collection methods. */ 74 static final public int COLLECT = 3; 75 /** Used to indicate the source of the message is the metadata and set methods. */ 76 static final public int METAEDIT = 4; 77 /** Used to indicate the source of the message is the building methods. */ 78 static final public int BUILDING = 5; 79 /** Used to indicate the source of the message is the preview methods. */ 80 static final public int FINAL = 6; 81 /** Used to indicate the source of the message is the log itself. */ 82 static final public int LOG = 7; 83 /** Used to indicate the level of the message is Main. */ 84 static final public int MAIN = 0; 85 /** Used to indicate the level of the message is Event. */ 86 static final public int EVENT = 1; 87 /** Used to indicate the level of the message is Information. */ 88 static final public int INFO = 2; 89 /** Used to indicate the level of the message is Error. */ 90 static final public int ERROR = 3; 91 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 92 /** 93 * Creates a new message. 94 * @param source the source of this message as an <i>int</i>. See above. 95 * @param level the level of this message as an <i>int</i>. See above. 96 * @param message the message content of this message as a <strong>String</strong>. 97 */ 98 public Message(int source, int level, String message) { 99 this.level = level; 100 this.source = source; 101 time = (new Date()).getTime(); 102 this.message = message; 103 } 104 /** 105 * Used to display a message, stating level and source. 106 * @param gatherer the main <strong>Gatherer</strong> class, used to get at the Dictionary. 107 * @see org.greenstone.gatherer.Dictionary 108 */ 109 public String toString(Gatherer gatherer) { 110 String source_str = ""; 111 switch(source) { 112 case GENERAL: 113 source_str = gatherer.dictionary.get("Source.General"); 114 break; 115 case BROWSER: 116 source_str = gatherer.dictionary.get("Source.Browser"); 117 break; 118 case MIRRORING: 119 source_str = gatherer.dictionary.get("Source.Mirroring"); 120 break; 121 case COLLECT: 122 source_str = gatherer.dictionary.get("Source.Collect"); 123 break; 124 case METAEDIT: 125 source_str = gatherer.dictionary.get("Source.MetaEdit"); 126 break; 127 case BUILDING: 128 source_str = gatherer.dictionary.get("Source.Building"); 129 break; 130 default: 131 source_str = gatherer.dictionary.get("Source.Unknown"); 132 } 133 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 134 String level_str = ""; 135 switch(level) { 136 case MAIN: 137 level_str = gatherer.dictionary.get("Level.Main"); 138 break; 139 case EVENT: 140 level_str = gatherer.dictionary.get("Level.Event"); 141 break; 142 case INFO: 143 level_str = gatherer.dictionary.get("Level.Information"); 144 break; 145 case ERROR: 146 level_str = gatherer.dictionary.get("Level.Error"); 147 break; 148 default: 149 level_str = gatherer.dictionary.get("Level.Unknown"); 150 } 151 return source_str + " " + level_str + " > " + message; 152 } 153 /** Used during testing testing to generate a random message. 154 * @return A new <strong>Message</strong> object which has a random source, level and a message body of the current system time. 155 */ 156 static public Message random() { 157 Random generator = new Random(); 158 int s = generator.nextInt(6); 159 int l = generator.nextInt(4); 160 String m = new Long(System.currentTimeMillis()).toString(); 161 return new Message(s,l,m); 162 } 163 163 } -
trunk/gli/src/org/greenstone/gatherer/WGet.java
r4293 r4363 45 45 import org.greenstone.gatherer.util.GURL; 46 46 /** This class provides access to the functionality of the WGet program, either by calling it via a shell script or by the JNI. It maintains a queue of pending jobs, and the component for showing these tasks to the user. 47 * @author John Thompson, Greenstone Digital Library, University of Waikato48 * @version 2.349 */47 * @author John Thompson, Greenstone Digital Library, University of Waikato 48 * @version 2.3 49 */ 50 50 public class WGet 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 51 extends Thread { 52 /** <i>true</i> if there is a task currently being carried out, <i>false</i> otherwise. */ 53 private boolean busy = false; 54 /** <i>true</i> if verbose debug messages should be displayed, <i>false</i> otherwise. */ 55 private boolean debug = false; 56 /** <i>true</i> if successfully completed tasks should be automatically removed from the job queue. */ 57 private boolean remove_complete_jobs = true; 58 /** The panel that the task list will be shown it. */ 59 private JPanel list_pane; 60 /** The job currently underway. */ 61 private Job job; 62 /** A scroll pane which will be used to display the list of pending tasks. */ 63 private JScrollPane list_scroll; 64 /** A queue of download tasks. */ 65 private Vector job_queue; 66 /** A static flag used to switch between simple and advanced modes. If <i>true</i> the Process object is used to externally call the Wget program. If <i>false</i> the native WGet libraries are statically loaded and the JNI used to download directly. */ 67 static final private boolean simple = true; 68 /** Load the WGet native library. */ 69 static { 70 if(!simple) { 71 System.load(System.getProperty("user.dir") + File.separator + "libgatherer.so"); 72 } 73 } 74 75 /** Constructor. Nothing special. */ 76 public WGet() { 77 job = null; 78 job_queue = new Vector(); 79 list_pane = new JPanel(); 80 list_pane.setLayout(new BoxLayout(list_pane, BoxLayout.Y_AXIS)); 81 //list_pane.setLayout(new GridLayout(1,1)); 82 list_scroll = new JScrollPane(list_pane); 83 //list_scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 84 } 85 86 /** Called by the WGet native code to inform us of a new download starting within the given job. 87 87 * @param url The url that is being downloaded, as a <strong>String</strong>. 88 88 * @see org.greenstone.gatherer.collection.Job 89 89 */ 90 91 92 93 94 90 public synchronized void addDownload(String url) { 91 job.addDownload(url); 92 } 93 94 /* Used to advise the Job of a newly parsed link. Its up to Job to decide if it already knows about this url, and if not to update its progress bar. 95 95 * @param url The url in question as a <strong>String</strong>. 96 96 * @param type <i>true</i> if this is an internal link, <i>false</i> for and external one. … … 98 98 * @see org.greenstone.gatherer.collection.Job 99 99 */ 100 101 102 103 104 100 public synchronized boolean addLink(String url, int type) { 101 return job.addLink(url, type); 102 } 103 104 /* Whenever files are moved into or out of the collection we need to 105 105 * run convertLinks on the files remaining. This ensures that we have 106 106 * the most efficient balance between local and absolute links. … … 110 110 */ 111 111 // public void convertLinks(Vector records) { 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 for(Enumeration e = records.elements(); e.hasMoreElements();) {128 129 130 }131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 for(Enumeration e = records.elements(); e.hasMoreElements(); ) {147 148 149 150 151 }152 153 154 155 156 157 }158 159 112 public void convertLinks() { 113 Vector args = new Vector(); 114 Vector files = new Vector(); 115 Vector urls = new Vector(); 116 // Args - there ain't many 117 args.add("-d"); 118 args.add("-o"); 119 args.add("debug.txt"); 120 args.add("-P"); 121 args.add("/tmp/"); 122 123 // Downloaded urls (two entries for each record). The pattern here is: 124 // local file 125 // original url of file 126 /* 127 for(Enumeration e = records.elements(); e.hasMoreElements();) { 128 urls.add(((GURL)e).getLocalName()); 129 urls.add(((GURL)e).getURL()); 130 } 131 */ 132 //urls.add("/tmp//www.cs.waikato.ac.nz/index.html"); 133 //urls.add("http://www.cs.waikato.ac.nz/index.html"); 134 //urls.add("/tmp//www.cs.waikato.ac.nz/events.html"); 135 //urls.add("http://www.cs.waikato.ac.nz/events.html"); 136 //urls.add("/tmp//www.cs.waikato.ac.nz/history.html"); 137 //urls.add("http://www.cs.waikato.ac.nz/history.html"); 138 //urls.add("/tmp//www.cs.waikato.ac.nz/icons/cs_title_logo.gif"); 139 //urls.add("http://www.cs.waikato.ac.nz/icons/cs_title_logo.gif"); 140 //urls.add("/tmp//www.cs.waikato.ac.nz/icons/scms_title_logo.gif"); 141 //urls.add("http://www.cs.waikato.ac.nz/icons/scms_title_logo.gif"); 142 143 // Downloaded files (html only). We race back through our records 144 // looking for html/text content ones. 145 /* 146 for(Enumeration e = records.elements(); e.hasMoreElements(); ) { 147 GURL record = (GURL)e; 148 if(record.isHTML()) { 149 files.add(record.getLocalName()); 150 } 151 } 152 */ 153 //files.add("/tmp//www.cs.waikato.ac.nz/index.html"); 154 155 //wren(args.size(), args.toArray(), urls.size(), urls.toArray(), 156 // files.size(), files.toArray()); 157 } 158 159 /** This method is called to delete a certain job from the queue. 160 160 * This job may be pending, complete or even in progress. However 161 161 * if it is currently downloading then the delete method must … … 164 164 * @param delete_me The <strong>Job</strong> that is to be deleted. 165 165 */ 166 167 166 public void deleteJob(Job delete_me) { 167 if(delete_me == job) { 168 168 // While this seems wasteful its only for the briefest moment. 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 /** Called by the WGet native code when the current download,169 while(busy) { 170 } 171 job = null; 172 } 173 if(delete_me.hasSignalledStop()) { 174 list_pane.remove(delete_me.getProgressBar()); 175 list_pane.updateUI(); 176 job_queue.remove(delete_me); 177 } 178 else { 179 Gatherer.println("Somehow we're trying to delete a job that is still running."); 180 } 181 } 182 183 /** Called by the WGet native code when the current download, 184 184 * for the indicated job, is completed. In turn all download 185 185 * listeners are informed. 186 186 * @see org.greenstone.gatherer.collection.Job 187 187 */ 188 public synchronized void downloadComplete() {189 190 }191 192 /** Called by the WGet native code when the requested download returns188 public synchronized void downloadComplete() { 189 job.downloadComplete(); 190 } 191 192 /** Called by the WGet native code when the requested download returns 193 193 * a status code other than 200-399 for the specified download job. 194 194 * for. 195 195 * @see org.greenstone.gatherer.collection.Job 196 196 */ 197 public synchronized void downloadFailed() {198 199 200 }201 202 /** Called by the WGet native code when some non-fatal error has caused197 public synchronized void downloadFailed() { 198 // Add the failed download as a new job if the user so requests. 199 job.downloadFailed(); 200 } 201 202 /** Called by the WGet native code when some non-fatal error has caused 203 203 * a download to fail. An example of a warning would be if a file can't 204 204 * be downloaded as doing so would clobber an existing file and the -nc … … 206 206 * @see org.greenstone.gatherer.collection.Job 207 207 */ 208 public synchronized void downloadWarning() {209 210 }211 212 208 public synchronized void downloadWarning() { 209 job.downloadWarning(); 210 } 211 212 /** Used by other graphic functions to get a reference to the 213 213 * scroll pane containing the current list of jobs. 214 214 */ 215 216 217 218 219 /** Returns the current state of the stop flag for the job indicated.215 public JScrollPane getJobList() { 216 return list_scroll; 217 } 218 219 /** Returns the current state of the stop flag for the job indicated. 220 220 * @return A boolean representing whether the user has requested to 221 221 * stop. 222 222 * @see org.greenstone.gatherer.collection.Job 223 223 */ 224 public synchronized boolean hasSignalledStop() {225 226 }227 228 224 public synchronized boolean hasSignalledStop() { 225 return job.hasSignalledStop(); 226 } 227 228 /** Creates a new mirroring job on the queue given the target url and the destination (private, public). All other details are harvested from the config file, but these two must be captured from the GUI's current state. 229 229 * @param url A <strong>GURL</strong> which points to the root url for the mirroring. 230 230 * @param model The <strong>GTreeModel</strong> that any new records should be added to. … … 236 236 * @see org.greenstone.gatherer.util.GURL 237 237 */ 238 239 238 public void newJob(GURL url, TreeModel model, String destination) { 239 if(url.valid()) { 240 240 // Create the job and fill in the details from gatherer.config. 241 242 241 Gatherer.println("About to create a new job"); 242 Job new_job = new Job(model, Gatherer.config.get("mirroring.overwrite", false), Gatherer.config.get("mirroring.debug", false), Gatherer.config.get("mirroring.no_parents", false), Gatherer.config.get("mirroring.other_hosts", false), Gatherer.config.get("mirroring.page_requisites", false), Gatherer.config.get("mirroring.quiet", false), url, Gatherer.config.getInt("mirroring.depth", false), destination, Gatherer.config.proxy_pass, Gatherer.config.proxy_user, this, simple); 243 243 // Add to job_queue job list. 244 244 job_queue.add(new_job); 245 245 // Now add it to the visual component, job list. 246 246 list_pane.add(new_job.getProgressBar()); 247 247 //list_pane.setAlignmentX(Component.LEFT_ALIGNMENT); 248 249 250 251 252 253 254 255 256 /** Called by the WGet native code to signal the current progress of248 list_pane.updateUI(); 249 new_job = null; 250 synchronized(this) { 251 notify(); // Just incase its sleeping. 252 } 253 } 254 } 255 256 /** Called by the WGet native code to signal the current progress of 257 257 * downloading for the specified job. 258 258 * @param current A long representing the number of bytes that have … … 262 262 * @see org.greenstone.gatherer.collection.Job 263 263 */ 264 public synchronized void updateProgress(long current, long expected) {265 266 }267 268 /* There may be times when the download thread is sleeping, but the264 public synchronized void updateProgress(long current, long expected) { 265 job.updateProgress(current, expected); 266 } 267 268 /* There may be times when the download thread is sleeping, but the 269 269 * user has indicated that a previously paused job should now begin 270 270 * again. The flag within the job will change, so we tell the thread 271 271 * to start again. 272 272 */ 273 public void resumeThread() {274 275 276 277 }278 279 /* This begins the WGet thread, which simply iterates through the waiting273 public void resumeThread() { 274 synchronized(this) { 275 notify(); // Just incase its sleeping. 276 } 277 } 278 279 /* This begins the WGet thread, which simply iterates through the waiting 280 280 * jobs attempting each one. Successful downloads are removed from the 281 281 * waiting list. … … 283 283 * @see org.greenstone.gatherer.collection.Job 284 284 */ 285 public void run() {286 287 288 289 290 291 292 293 285 public void run() { 286 while(true) { 287 // If there are jobs job_queue and we have more room. 288 if(job_queue.size() > 0) { 289 // Get the first job that isn't stopped. 290 for(Enumeration e = job_queue.elements(); e.hasMoreElements();) { 291 job = (Job) e.nextElement(); 292 if(job.getState() == Job.RUNNING) { 293 Gatherer.println("Job " + job.toString() + " Begun."); 294 294 // A lock to prevent us deleting this job while its being 295 295 // run, unless you want things to go really wrong. 296 297 298 299 300 301 302 303 304 296 busy = true; 297 if(simple) { 298 job.callWGet(); 299 } 300 else { 301 job.callWGetNative(); 302 } 303 busy = false; 304 Gatherer.println("Job " + job.toString() + " complete."); 305 305 // And if the user has requested that complete jobs 306 306 // be removed, then remove it from the list. 307 if(remove_complete_jobs) { 308 deleteJob(job); 309 } 310 job = null; 311 } 312 } 307 if(remove_complete_jobs) { 308 deleteJob(job); 309 } 310 job = null; 311 } 313 312 } 314 // In order to save processor time, I'll suspend the thread315 // if theres no advantage to it running. Actions such as316 // new or complete jobs will resume the thread.317 else {318 try{319 synchronized(this){320 Gatherer.println("WGet thread is waiting for Jobs.");321 wait();322 }323 } catch (InterruptedException e) {324 // Time to get going again.325 }313 } 314 // In order to save processor time, I'll suspend the thread 315 // if theres no advantage to it running. Actions such as 316 // new or complete jobs will resume the thread. 317 else { 318 try { 319 synchronized(this) { 320 Gatherer.println("WGet thread is waiting for Jobs."); 321 wait(); 322 } 323 } catch (InterruptedException e) { 324 // Time to get going again. 326 325 } 327 } // End While. 328 } 329 330 /* Link to the call to the WGet Native method for downloading. 326 } 327 } // End While. 328 } 329 330 /* Link to the call to the WGet Native method for downloading. 331 331 * @param argc An int representing the number of elements in argv. 332 332 * @param argv An array of objects passed as arguments to wget. … … 334 334 * messages. 335 335 */ 336 public native int wget(int argc, Object argv[], boolean debug);337 338 /* Link to the call to the WGet Native method for converting url links.336 public native int wget(int argc, Object argv[], boolean debug); 337 338 /* Link to the call to the WGet Native method for converting url links. 339 339 * @param argc An int representing the number of object elements in argv. 340 340 * @param argv An array of objects passed as arguments to wget. … … 346 346 * @param filev An array of strings representing files. 347 347 */ 348 public native void wren(int argc, Object argv[], int urlc, Object urlv[], int filec, Object filev[]);348 public native void wren(int argc, Object argv[], int urlc, Object urlv[], int filec, Object filev[]); 349 349 } 350 350 -
trunk/gli/src/org/greenstone/gatherer/cdm/custom/CustomAZList.java
r4293 r4363 61 61 */ 62 62 final public class CustomAZList 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 63 implements CustomClassifier { 64 /** When this the control dialog is disposed, should we go ahead a add this custom AZ list (by processing all file records and building a new hierarchy)? */ 65 private boolean process = false; 66 /** A reference to the Gatherer for access to the Collection and MetadataSetManager. */ 67 private Gatherer gatherer = null; 68 /** A mapping from a String pattern to a specific GValueNode within the hidden hierarchy. */ 69 private Hashtable mappings = null; 70 /** The button used to cancel the dialog. */ 71 private JButton cancel = null; 72 /** The button used to confirm and close the dialog. */ 73 private JButton ok = null; 74 /** The dialog which contains the controls for configuring this custom classifier. */ 75 private JDialog controls = null; 76 /** The checkbox used to indicate whether there is a name given for the classifier button (in the Greenstone collections menubar). */ 77 private JCheckBox buttonname_label = null; 78 /** The checkbox used to indicate that the final classifier should be sorted in some way. */ 79 private JCheckBox sort_label = null; 80 /** The control allowing you to choose the assigned metadata this classifier should be based on. */ 81 private JComboBox metadata = null; 82 /** The metadata the final classifier should be sorted by. */ 83 private JComboBox sort = null; 84 /** A field for entering the button name. */ 85 private JTextField buttonname = null; 86 /** A field which provides a clear indication of the current ranges selected for your custom classifier. */ 87 private JTextField separators_preview = null; 88 /** An array of togglebuttons showing what separations are available. */ 89 private JToggleButton separators[] = null; 90 /** A private dictionary for this custom classifier. */ 91 private ResourceBundle dictionary = null; 92 /** The name of the hidden metadata. We at least luck out in that we don't need a live reference to the metadata, because the user can never change it (or if they do its their own stinking fault). */ 93 private String hidden_mde_name = null; 94 /** The name of this pseudo-classifier. */ 95 final static public String NAME = "CustomAZList"; 96 /** The size of a label. */ 97 final static private Dimension LABEL_SIZE = new Dimension(200,25); 98 /** The size of the controls for this pseudo-classifier. */ 99 final static private Dimension SIZE = new Dimension(550,400); 100 /** An array of values for the separators. */ 101 final static private String VALUES[] = {"Numbers", "A", "B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}; 102 /** The same array as above, except as integer values (note that "Numbers" becomes "#"). */ 103 static public int INT_VALUES[] = {'#','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; 104 /** A third array which is almost the same as the others (can anyone else say poor code reuse) but subsitutes "#" for "Numbers". */ 105 static public String STRING_VALUES[] = {"#","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}; 106 /** Default Constructor. Needed to load this class dynamically. */ 107 public CustomAZList() {} 108 /** Constructor. 109 109 * @param gatherer A reference to the <strong>Gatherer</strong>. 110 110 */ 111 112 113 114 111 public CustomAZList(Gatherer gatherer) { 112 this.gatherer = gatherer; 113 } 114 /** Used to compare this classifier to another classifier for the purposes of ordering. 115 115 * @param object The other classifier as an <strong>Object</strong>. 116 116 * @see org.greenstone.gatherer.cdm.Classifier 117 117 * @see org.greenstone.gatherer.cdm.CustomClassifier 118 118 */ 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 119 public int compareTo(Object object) { 120 if(object instanceof Classifier) { 121 Classifier classifier = (Classifier) object; 122 return NAME.compareTo(classifier.getName()); 123 } 124 else if(object instanceof CustomClassifier) { 125 CustomClassifier classifier = (CustomClassifier) object; 126 return NAME.compareTo(classifier.getName()); 127 } 128 return NAME.compareTo(object.toString()); 129 } 130 /** Ensure that the dialog can be correctly garbage collected. */ 131 public void destroy() { 132 controls = null; 133 } 134 /** Produce a new copy of this custom classifier. Remember that what the classifier manager does is create instances of all possible classifiers, then as they are assigned creates and assigns copies of the original reserve. This way we only have to parse arguments once. 135 135 * @return A new <strong>CustomClassifier</strong> which is a copy of this one. 136 136 * @see org.greenstone.gatherer.Gatherer 137 137 */ 138 139 140 141 138 public CustomClassifier copy() { 139 return new CustomAZList(gatherer); 140 } 141 /** Show the controls for configuring this pseudo-classifier. 142 142 * @param show <i>true</i> to actually display the configuration dialog on screen, <i>false</i> to do everything except show the control and process the data (useful for setting the control up and/or reloading custom classifiers from collect.cfg). 143 143 * @see org.greenstone.gatherer.Configuration … … 152 152 * @see org.greenstone.gatherer.msm.MetadataSetManager 153 153 */ 154 155 156 154 public boolean display(boolean show) { 155 if(controls == null) { 156 Vector elements = gatherer.c_man.getCollection().msm.getAssignedElements(); 157 157 // Creation 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 158 controls = new JDialog(); 159 controls.setModal(true); 160 controls.setSize(SIZE); 161 controls.setTitle(get("Title_JDialog")); 162 JPanel content_pane = (JPanel) controls.getContentPane(); 163 JPanel upper_pane = new JPanel(); 164 JPanel metadata_pane = new JPanel(); 165 JLabel metadata_label = new JLabel(get("Metadata_JLabel")); 166 metadata_label.setPreferredSize(LABEL_SIZE); 167 metadata = new JComboBox(elements); 168 JPanel buttonname_pane = new JPanel(); 169 buttonname_label = new JCheckBox(get("Buttonname_JCheckBox")); 170 buttonname_label.setPreferredSize(LABEL_SIZE); 171 buttonname = new JTextField(); 172 buttonname.setBackground(Color.lightGray); 173 buttonname.setEnabled(false); 174 JPanel sort_pane = new JPanel(); 175 sort_label = new JCheckBox(get("Sort_JLabel")); 176 sort = new JComboBox(elements); 177 JPanel separations_pane = new JPanel(); 178 JLabel separations_label = new JLabel(get("Separations_JLabel")); 179 separations_label.setPreferredSize(LABEL_SIZE); 180 separators_preview = new JTextField(); 181 separators_preview.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 182 separators_preview.setEnabled(false); // View only. 183 separators_preview.setForeground(Color.black); 184 JPanel lower_pane = new JPanel(); 185 JPanel button_pane = new JPanel(); 186 ok = new JButton(get("OK_JButton")); 187 cancel = new JButton(get("Cancel_JButton")); 188 188 // Connection 189 190 191 192 189 buttonname_label.addActionListener(new ButtonNameListener()); 190 cancel.addActionListener(new CancelListener()); 191 ok.addActionListener(new OKListener()); 192 sort_label.addActionListener(new SortListener()); 193 193 // The Toggle Button block. Done all at once so we don't end up with three for loops. 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 194 JPanel center_pane = new JPanel(); 195 center_pane.setLayout(new GridLayout(3, 9)); 196 separators = new JToggleButton[VALUES.length]; 197 SeparatorListener sl = new SeparatorListener(); 198 for(int i = 0; i < VALUES.length; i++) { 199 separators[i] = new JToggleButton(get(VALUES[i])); 200 separators[i].addActionListener(sl); 201 if(i != 0) { 202 center_pane.add(separators[i]); 203 } 204 else { 205 JLabel temp = new JLabel(get(VALUES[0])); 206 temp.setHorizontalAlignment(JLabel.CENTER); 207 center_pane.add(temp); 208 } 209 } 210 separators_preview.setText(getPreview(true)); 211 211 // Layout 212 213 214 215 212 metadata_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0)); 213 metadata_pane.setLayout(new BorderLayout()); 214 metadata_pane.add(metadata_label, BorderLayout.WEST); 215 metadata_pane.add(metadata, BorderLayout.CENTER); 216 216 217 218 219 220 217 buttonname_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0)); 218 buttonname_pane.setLayout(new BorderLayout()); 219 buttonname_pane.add(buttonname_label, BorderLayout.WEST); 220 buttonname_pane.add(buttonname, BorderLayout.CENTER); 221 221 222 223 224 225 222 sort_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0)); 223 sort_pane.setLayout(new BorderLayout()); 224 sort_pane.add(sort_label, BorderLayout.WEST); 225 sort_pane.add(sort, BorderLayout.CENTER); 226 226 227 228 229 230 231 227 upper_pane.setBorder(BorderFactory.createEmptyBorder(0,0,4,0)); 228 upper_pane.setLayout(new GridLayout(3, 1)); 229 upper_pane.add(metadata_pane); 230 upper_pane.add(buttonname_pane); 231 upper_pane.add(separations_label); 232 232 233 234 235 233 separations_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createRaisedBevelBorder())); 234 separations_pane.setLayout(new BorderLayout()); 235 separations_pane.add(separators_preview, BorderLayout.CENTER); 236 236 237 238 239 237 lower_pane.setLayout(new GridLayout(2,1)); 238 lower_pane.add(separations_pane); 239 lower_pane.add(button_pane); 240 240 241 242 243 241 button_pane.setLayout(new GridLayout(1,2)); 242 button_pane.add(ok); 243 button_pane.add(cancel); 244 244 245 246 247 248 249 245 content_pane.setBorder(BorderFactory.createEmptyBorder(4,5,5,5)); 246 content_pane.setLayout(new BorderLayout()); 247 content_pane.add(upper_pane, BorderLayout.NORTH); 248 content_pane.add(center_pane, BorderLayout.CENTER); 249 content_pane.add(lower_pane, BorderLayout.SOUTH); 250 250 // Display 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 251 Dimension screen_size = Gatherer.config.screen_size; 252 controls.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 253 screen_size = null; 254 if(show) { 255 controls.show(); 256 // Create the new metadata, hierarchy etc. 257 if(process) { 258 process((ElementWrapper)metadata.getSelectedItem(), getPreview(false)); 259 return true; 260 } 261 return false; 262 } 263 else { 264 return true; 265 } 266 266 // We do everything -except- display the control. 267 268 269 270 271 272 273 274 275 276 277 278 267 } 268 else { 269 process = false; 270 controls.show(); 271 if(process) { 272 process((ElementWrapper)metadata.getSelectedItem(), getPreview(false)); 273 return true; 274 } 275 return false; 276 } 277 } 278 /** Determine if this classifier and another given classifier are equal. 279 279 * @param object The classifier to check against for equality, as an <strong>Object</strong>. 280 280 * @see org.greenstone.gatherer.cdm.CustomClassifier 281 281 */ 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 282 public boolean equals(Object object) { 283 if(object instanceof CustomClassifier) { 284 CustomClassifier classifier = (CustomClassifier) object; 285 if(getCommand().equalsIgnoreCase(classifier.getCommand())) { 286 return true; 287 } 288 return false; 289 } 290 else { 291 if(getCommand().equalsIgnoreCase(object.toString())) { 292 return true; 293 } 294 return false; 295 } 296 } 297 /** Method to return this pseudo-classifier represented as a String. 298 298 * @return A <strong>String</strong>. 299 299 */ 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 300 public String getCommand() { 301 StringBuffer text = new StringBuffer("classify "); 302 text.append("Hierarchy"); 303 text.append(" -metadata "); 304 text.append(hidden_mde_name); // @TODO 305 if(buttonname_label.isSelected()) { 306 text.append(" -buttonname \""); 307 text.append(buttonname.getText()); 308 text.append("\""); 309 } 310 text.append(" -hfile "); 311 text.append(hidden_mde_name + ".txt"); 312 text.append(" -hlist_at_top"); 313 text.append("\n"); 314 return text.toString(); 315 } 316 /** Retrieve the custom command, a command line that overrides and replaces some other 'actual' classifier. 317 317 * @param index The number of the classifer this one is replacing. 318 318 */ 319 320 321 322 323 324 325 326 327 328 319 public String getCustomCommand(int index) { 320 StringBuffer text = new StringBuffer("customclassifier "); 321 text.append(NAME); 322 text.append(" -replaces CL"); 323 text.append(index); 324 text.append(" -separations "); 325 text.append(getPreview(false)); 326 return text.toString(); 327 } 328 /** Get the name of this custom classifier. 329 329 * @return The name as a <strong>String</strong>. 330 330 */ 331 332 333 334 331 public String getName() { 332 return NAME; 333 } 334 /** Process a record by adding hidden metadata as necessary. 335 335 * @param record The <strong>FileNode</strong> to be edited. 336 336 * @see org.greenstone.gatherer.msm.ElementWrapper 337 337 */ 338 339 340 341 342 343 344 338 public void process(FileNode record) { 339 ElementWrapper element = (ElementWrapper) metadata.getSelectedItem(); 340 if(element != null) { 341 addMetadata(0L, record, element); 342 } 343 } 344 /** Recreate a CustomAZList given several parameters including the real classifier created during custom design. 345 345 * @param classifier The real <strong>Classifier</strong>. 346 346 * @param separations A <strong>String</strong> representing the choosen separations. … … 353 353 * @see org.greenstone.gatherer.valuetree.GValueNode 354 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 355 public void recreate(Classifier classifier, String separations) { 356 // Rebuild controls 357 display(false); 358 // Calculate original element. 359 hidden_mde_name = classifier.getArgument("metadata").getValue(); 360 ElementWrapper hidden_mde = gatherer.c_man.getCollection().msm.getElement(hidden_mde_name); 361 String mde_name = (hidden_mde_name.substring(MetadataSetManager.HIDDEN.length() + 1)).replace('_','.'); 362 ElementWrapper mde = gatherer.c_man.getCollection().msm.getElement(mde_name); 363 // Set metadata element combobox. 364 metadata.setSelectedItem(mde); 365 // Set button name. 366 String bn = classifier.getArgument("buttonname").getValue(); 367 if(bn != null) { 368 buttonname_label.setSelected(true); 369 buttonname.setBackground(Color.white); 370 buttonname.setEnabled(true); 371 buttonname.setText(bn); 372 } 373 // Set sort element. 374 String sort_mde_name = classifier.getArgument("sort").getValue(); 375 ElementWrapper sort_mde = gatherer.c_man.getCollection().msm.getElement(sort_mde_name); 376 if(sort_mde != null) { 377 sort_label.setSelected(true); 378 sort.setBackground(Color.white); 379 sort.setEnabled(true); 380 sort.setSelectedItem(sort_mde); 381 } 382 // Recover value tree. 383 GValueModel model = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde); 384 // For each token in the tokenizer, set toggle buttons and recover node. 385 StringTokenizer tokenizer = new StringTokenizer(separations, ","); 386 mappings = new Hashtable(); 387 while(tokenizer.hasMoreTokens()) { 388 String key = tokenizer.nextToken(); 389 for(int j = 1; j < STRING_VALUES.length; j++) { 390 if(key.startsWith(STRING_VALUES[0])) { 391 j = STRING_VALUES.length; 392 } 393 else if(key.startsWith(STRING_VALUES[j])) { 394 separators[j].setSelected(true); 395 j = STRING_VALUES.length; 396 } 397 } 398 398 // Rebuild mappings hashtable for this pattern. Add it if not present. 399 399 GValueNode node = model.addValue(key); 400 400 // If the key is simple, ie "C", then add the mapping "C"->node 401 402 403 404 405 406 401 if(node == null) { 402 // Do nothing. 403 } 404 else if(key.indexOf("-") == -1) { 405 mappings.put(key, node); 406 } 407 407 // If the key is more complex, say "D-L", then we add mappings for all string values so "D"->node, "E"->node, ... "L"->node. This is miles eaiser to do here, than when you are trying to match a records metadata ie matchin "D-L" to "Igloos are cool". Note that it is guarantee that the keys are all of the same length, ie "A-L", "MA-ML", "MMA-STF", "STFA-ZZZZ". 408 409 410 411 412 413 414 415 416 417 418 419 408 else { 409 String start = key.substring(0, key.indexOf("-")); 410 String end = key.substring(key.indexOf("-") + 1); 411 for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) { 412 ///ystem.err.println("Adding " + key + " -> " + node); 413 mappings.put(key, node); 414 } 415 } 416 } 417 separators_preview.setText(getPreview(true)); 418 } 419 /** Sets the value of Gatherer, for those classes loaded dynamically. 420 420 * @param gatherer A reference to the <strong>Gatherer</strong>. 421 421 */ 422 423 424 422 public void setGatherer(Gatherer gatherer) { 423 this.gatherer = gatherer; 424 } 425 425 426 427 426 public void setManager(ClassifierManager manager) { 427 } 428 428 429 429 /** Translate this object into a string such as you would find in the collection configuration file. 430 430 * @return A <strong>String</strong> representation. 431 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 432 public String toString() { 433 StringBuffer text = new StringBuffer("classify "); 434 text.append(NAME); 435 text.append(" -metadata "); 436 text.append(metadata.getSelectedItem().toString()); 437 if(buttonname_label.isSelected()) { 438 text.append(" -buttonname \""); 439 text.append(buttonname.getText()); 440 text.append("\""); 441 } 442 text.append(" -separations "); 443 text.append(getPreview(false)); 444 return text.toString(); 445 } 446 /** Add a pertinant piece of metadata to the given record. */ 447 private void addMetadata(long id, FileNode record, ElementWrapper element) { 448 // Add custom metadata based on target metadata. No recursion. 449 ArrayList temp_value = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile(), element); 450 boolean found = false; // Only want to add the custom classifier metadata once. 451 for(int i = 0; !found && i < temp_value.size(); i++) { 452 String metadata_value = (String)temp_value.get(i); 453 for(Enumeration keys = mappings.keys(); metadata_value != null && keys.hasMoreElements(); ) { 454 String key = (String)keys.nextElement(); 455 // Try to match the value and the pattern. Remember to check for any case, ie key is '#' and metadata_value starts with anything other than a character. 456 if(metadata_value.toUpperCase().startsWith(key) || (key.equals("#") && !Character.isLetter(metadata_value.charAt(0)))) { 457 GValueNode node = (GValueNode) mappings.get(key); 458 if(node != null) { // And it shouldn't. 459 Metadata metadata = new Metadata(node); 460 //record.addMetadata(id, metadata, MetaEditPromptBox.ACCUMULATE_ALL, false, 1); 461 Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, record, null, metadata); 462 found = true; 463 } 464 } 465 } 466 } 467 } 468 /** Create a separation preview String from the currently selected toggle buttons. 469 469 * @param spaces <i>true</i> if the returned String should contain spaces for readability, <i>false</i> if we mean to process it and spaces would just make that more difficult. 470 470 * @return A <strong>String</strong> representing the current separation state, such as "#, A-L, M-Z". 471 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 472 private String getPreview(boolean spaces) { 473 String sep_sep = ","; 474 if(spaces) { 475 sep_sep = ", "; 476 } 477 StringBuffer preview = new StringBuffer(get(VALUES[0])); 478 for(int i = 1; i < separators.length - 1; i++) { 479 if(separators[i].isSelected()) { 480 // Special case, where we specifically avoid #-#, A... 481 if(i == 1) { 482 preview.append(sep_sep); 483 preview.append(get(VALUES[i])); 484 } 485 // Check previous value. We don't want ...A-L, M-M, N... 486 else if(separators[i - 1].isSelected()) { 487 preview.append(sep_sep); 488 preview.append(get(VALUES[i])); 489 } 490 // Otherwise ..-VALUE[i-1], VALUE[i]... 491 else { 492 preview.append("-"); 493 preview.append(get(VALUES[i-1])); 494 preview.append(sep_sep); 495 preview.append(get(VALUES[i])); 496 } 497 } 498 } 499 // Special case of "...-Y,Z" 500 if(separators[separators.length - 1].isSelected()) { 501 preview.append("-"); 502 preview.append(get(VALUES[VALUES.length - 2])); 503 preview.append(sep_sep); 504 preview.append(get(VALUES[VALUES.length - 1])); 505 } 506 // Otherwise "...-Z" 507 else { 508 preview.append("-"); 509 preview.append(get(VALUES[VALUES.length - 1])); 510 } 511 return preview.toString(); 512 } 513 /** Load the custom argument dictionary if necessary, then retrieve the requested phrase. 514 514 * @param key A key <strong>String</strong> which maps to the phrase to retrieve. 515 515 * @return A phrase as a <strong>String</strong>. … … 517 517 * @see org.greenstone.gatherer.Gatherer 518 518 */ 519 520 521 522 523 524 525 526 527 528 529 519 private String get(String key) { 520 if(dictionary == null) { 521 dictionary = ResourceBundle.getBundle("org.greenstone.gatherer.cdm.custom." + NAME, Gatherer.config.getLocale("general.locale", false)); 522 } 523 return dictionary.getString(key); 524 } 525 /** Listens to the buttonname checkbox, and enable buttonname appropriately when action detected. */ 526 private class ButtonNameListener 527 implements ActionListener { 528 /** When an action is performed on a registered control, this method is called which either enables or disables the buttonname field depending on the buttonname checkbox. 529 * @param event An <strong>ActionEvent</strong> containing information about the action performed. 530 530 */ 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 531 public void actionPerformed(ActionEvent event) { 532 if(buttonname_label.isSelected()) { 533 buttonname.setBackground(Color.white); 534 buttonname.setEnabled(true); 535 } 536 else { 537 buttonname.setBackground(Color.lightGray); 538 buttonname.setEnabled(false); 539 } 540 } 541 } 542 /** Listens to the cancel button, and disposes appropriately when action detected. */ 543 private class CancelListener 544 implements ActionListener { 545 /** If the cancel button is pressed then we should dispose of the dialog without processing any records. 546 * @param event An <strong>ActionEvent</strong> containing information about the action performed. 547 547 */ 548 549 550 551 552 553 554 555 556 557 548 public void actionPerformed(ActionEvent event) { 549 process = false; 550 controls.dispose(); 551 } 552 } 553 /** Listens to the ok button, and sets process to true then disposes appropriately when action detected. */ 554 private class OKListener 555 implements ActionListener { 556 /** If the ok button is pressed then we should dispose of the dialog after processing all records. 557 * @param event An <strong>ActionEvent</strong> containing information about the action performed. 558 558 */ 559 560 561 562 563 564 565 566 567 568 559 public void actionPerformed(ActionEvent event) { 560 process = true; 561 controls.dispose(); 562 } 563 } 564 /** Listens for the toggling of a separator value, and updates the separator preview field. */ 565 private class SeparatorListener 566 implements ActionListener { 567 /** When one of the separator buttons is toggled we update the separator preview field. 568 * @param event An <strong>ActionEvent</strong> containing information about the action performed. 569 569 */ 570 571 572 573 574 575 576 577 578 570 public void actionPerformed(ActionEvent event) { 571 separators_preview.setText(getPreview(true)); 572 } 573 } 574 /** Listens to the sort checkbox, and enables/disables sort appropriately when action detected. */ 575 private class SortListener 576 implements ActionListener { 577 /** If this checkbox is actioned, enabled or disable the sort control based on whether we are checked (selected) or not. 578 * @param event An <strong>ActionEvent</strong> containing information about the action performed. 579 579 */ 580 581 582 583 584 585 586 587 588 589 590 591 580 public void actionPerformed(ActionEvent event) { 581 if(sort_label.isSelected()) { 582 sort.setBackground(Color.white); 583 sort.setEnabled(true); 584 } 585 else { 586 sort.setBackground(Color.lightGray); 587 sort.setEnabled(false); 588 } 589 } 590 } 591 /** Display a progress bar while creating the required hidden system hierarchy. 592 592 * @param element An <strong>ElementWrapper</strong> containing the metadata element we want to build a custom classifier on. 593 593 * @param state A <strong>String</strong> representing the currently selected separators, of the form "#,A-L,MA-Ml,MM-MZ,N-Z". */ 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 594 private void process(ElementWrapper element, String state) { 595 // Create a new progress bar dialog, using these divisions: 596 // 33% - Creation and removal of old metadata tree. 597 // 33% - Creation of new metadata tree, 33 / <number of separations>. 598 // 33% - Allocation of metadata to records, 33 / <number of files>. 599 // Hopefully the previous dialog should have already vanished (its dispose() caused by the process click). 600 ProgressDialog pd = new ProgressDialog("Custom Classifier Creation", "Preparing for operation."); 601 // Step 1: Create the new dummy element and add it if necessary. 602 pd.setText("Creating dummy metadata element."); 603 MetadataSet hidden_mds = gatherer.c_man.getCollection().msm.getSet(MetadataSetManager.HIDDEN); 604 Element hidden_e = hidden_mds.getElement(element.toString().replace('.','_')); 605 boolean found = true; 606 ElementWrapper hidden_mde = null; 607 if(hidden_mde != null) { 608 hidden_mde = new ElementWrapper(hidden_e); 609 } 610 else { 611 hidden_mde = hidden_mds.addElement(element.toString().replace('.','_')); 612 found = false; 613 } 614 hidden_mde_name = hidden_mde.toString(); 615 pd.increase(1,2,20); 616 // Step 2: Remove any existing value tree for this element. Wastefull I know but I don't want to lag for too long, and this is the quickest way. 617 pd.setText("Removing any existing hierarchy."); 618 if(found) { 619 hidden_mds.removeValueTree(hidden_mde); 620 } 621 pd.increase(1,2,20); 622 // Step 3: Create a value tree for this element. 623 pd.setText("Creating new hierary root."); 624 GValueModel value_tree = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde); 625 pd.increase(5, 5, 5); 626 // Step 4: Build the single level value hierarchy using state's value. Here a hashtable mapping string patterns to value nodes is built. 627 pd.setText("Creating separations."); 628 StringTokenizer tokenizer = new StringTokenizer(state, ","); 629 int separator_count = tokenizer.countTokens(); 630 mappings = new Hashtable(); 631 for(int i = 0; i < separator_count; i++) { 632 String key = tokenizer.nextToken(); 633 GValueNode node = value_tree.addValue(key); 634 634 // If the key is simple, ie "C", then add the mapping "C"->node 635 636 637 635 if(key.indexOf("-") == -1) { 636 mappings.put(key, node); 637 } 638 638 // If the key is more complex, say "D-L", then we add mappings for all string values so "D"->node, "E"->node, ... "L"->node. This is miles eaiser to do here, than when you are trying to match a records metadata ie matchin "D-L" to "Igloos are cool". Note that it is guarantee that the keys are all of the same length, ie "A-L", "MA-ML", "MMA-STF", "STFA-ZZZZ". 639 640 641 642 643 644 645 639 else { 640 String start = key.substring(0, key.indexOf("-")); 641 String end = key.substring(key.indexOf("-") + 1); 642 for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) { 643 mappings.put(key, node); 644 } 645 } 646 646 // Each iteration. 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 647 pd.increase(1, separator_count, 25); 648 } 649 // Step 5: Recurse the entire tree, adding the appropriate values where necessary (overwriting any existing value for this element). 650 pd.setText("Allocating metadata to records."); 651 int document_count = gatherer.c_man.getCollection().getDocumentCount(); 652 TreeModel record_tree = gatherer.c_man.getRecordSet(); 653 FileNode records[] = ArrayTools.add(null, (FileNode)record_tree.getRoot()); 654 while(records != null) { 655 if(records[0].isLeaf()) { 656 addMetadata(0L, records[0], element); 657 // Each iteration. 658 pd.increase(1, document_count, 50); 659 } 660 else { 661 // Add all children to records at once hopefully. 662 FileNode children[] = new FileNode[records[0].getChildCount()]; 663 for(int i = 0; i < children.length; i++) { 664 children[i] = (FileNode) records[0].getChildAt(i); 665 } 666 records = ArrayTools.add(records, children); 667 } 668 records = ArrayTools.tail(records); 669 } 670 // Step 6: Done. 671 pd.dispose(); 672 } 673 /** A dialog window used to show the progress of processing the file records in terms of adding hidden metadata. */ 674 private class ProgressDialog 675 extends JDialog { 676 /** The current value represented by the progress bar (where value is double and the progress bar shows an int). */ 677 private double value = 0.0; 678 /** The default size of this dialog. */ 679 private Dimension dialog_size = new Dimension(500,100); 680 /** The label explaining what action is currently happening (1 of 5 steps). */ 681 private JLabel label = new JLabel(); 682 /** The progress bar itself. */ 683 private JProgressBar bar = new JProgressBar(); 684 /** Constructor. 685 * @param t The title for this dialog as a <strong>String</strong>. 686 * @param l The initial label for the progress bar also a <strong>String</strong>. 687 */ 688 public ProgressDialog(String t, String l) { 689 super(); 690 setModal(false); 691 setSize(dialog_size); 692 setTitle(t); 693 693 // Create 694 695 696 697 698 694 JPanel content_pane = (JPanel) getContentPane(); 695 label.setText(l); 696 bar.setMaximum(100); 697 bar.setMinimum(0); 698 bar.setValue(0); 699 699 // Connect 700 700 // Layout 701 702 703 704 701 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 702 content_pane.setLayout(new BorderLayout()); 703 content_pane.add(label, BorderLayout.NORTH); 704 content_pane.add(bar, BorderLayout.CENTER); 705 705 // Display 706 707 708 709 710 711 706 Dimension screen_size = Gatherer.config.screen_size; 707 setLocation((screen_size.width - dialog_size.width) / 2, (screen_size.height - dialog_size.height) / 2); 708 screen_size = null; 709 show(); 710 } 711 /** Increase the progress bar by a certain ammount. 712 712 * @param item An <i>int</i> indicating what item number this is. 713 713 * @param out_of An <i>int</i> detailing the number of items expected in total for this phase. 714 714 * @param total The maximum value of the progress bar as an <i>int</i>. 715 715 */ 716 717 718 716 public void increase(int item, int out_of, int total) { 717 value = value + (((double) item / (double) out_of) * (double)total); 718 int int_value = (int) value; 719 719 ///ystem.err.println("Value is " + value + " which rounds to " + int_value); 720 721 722 723 724 720 if(int_value != bar.getValue()) { 721 bar.setValue(int_value); 722 } 723 } 724 /** Set the message shown in the progress bar. 725 725 * @param l The new progress bar label as a <strong>String</strong>. 726 726 */ 727 728 729 730 731 727 public void setText(String l) { 728 label.setText(l); 729 } 730 } 731 /** The String equivelent of int++, this method increments the value of a String by one letter at a time. 732 732 * @param str The <strong>String</strong> to be incremented. 733 733 * @return A <strong>String</strong> whose 'checksum' value (adding the INT_VALUE[] of the letters together) is one greater than the initial String, while still containing only those values in STRING_VALUE[]. 734 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 735 public String increment(String str) { 736 ///ystem.err.print("Incrementing " + str + " -> "); 737 char chars[] = str.toCharArray(); 738 boolean found = false; 739 int pos = str.length() - 1; 740 while(found == false && pos >= 0) { 741 if((int)chars[pos] + 1 <= INT_VALUES[26]) { 742 if((int)chars[pos] == INT_VALUES[0]) { // Anything else symbol 743 chars[pos] = (char) INT_VALUES[1]; 744 } 745 else { 746 chars[pos] = (char)((int)chars[pos] + 1); 747 } 748 found = true; 749 } 750 else { 751 chars[pos] = (char)INT_VALUES[1]; 752 pos--; 753 } 754 } 755 // If we haven't found it yet, return null. 756 if(!found) { 757 return null; 758 } 759 ///ystem.err.println(String.valueOf(chars)); 760 return String.valueOf(chars); 761 } 762 762 } 763 763
Note:
See TracChangeset
for help on using the changeset viewer.