| Main.java |
1 /*
2 * Main.java
3 *
4 * Copyright (c) 1998-2004, The University of Sheffield.
5 *
6 * This file is part of GATE (see http://gate.ac.uk/), and is free
7 * software, licenced under the GNU Library General Public License,
8 * Version 2, June 1991 (in the distribution as file licence.html,
9 * and also available at http://gate.ac.uk/gate/licence.html).
10 *
11 * Hamish Cunningham, 1/Nov/00
12 *
13 * $Id: Main.java,v 1.52 2004/09/30 11:37:36 valyt Exp $
14 */
15
16 package gate;
17
18 import java.awt.*;
19 import java.io.*;
20 import java.net.MalformedURLException;
21 import java.net.URL;
22 import java.util.*;
23 import java.util.List;
24
25 import javax.swing.*;
26
27 import gate.gui.*;
28 import gate.util.*;
29
30 import gnu.getopt.Getopt;
31
32
33 /** Top-level entry point for the GATE command-line and GUI interfaces.
34 * <P>
35 */
36 public class Main {
37
38 /** Debug flag */
39 private static final boolean DEBUG = false;
40
41 /** Status flag for normal exit. */
42 private static final int STATUS_NORMAL = 0;
43
44 /** Status flag for error exit. */
45 private static final int STATUS_ERROR = 1;
46
47 /** Main routine for GATE.
48 * Command-line arguments:
49 * <UL>
50 * <LI>
51 * <B>-h</B> display a short help message
52 * <LI>
53 * <B>-d URL</B> define URL to be a location for CREOLE resoures
54 * <LI>
55 * <B>-i file</B> additional initialisation file (probably called
56 * <TT>gate.xml</TT>). Used for site-wide initialisation by the
57 * start-up scripts
58 * <LI>
59 * <B>-a</B> run the DB administration tool
60 * </UL>
61 */
62 public static void main(String[] args) throws GateException {
63 Main.annotatorArgsMap = null;
64 // check we have a useable JDK
65 if(
66 System.getProperty("java.version").compareTo(Gate.getMinJdkVersion())
67 < 0
68 ) {
69 throw new GateException(
70 "GATE requires JDK " + Gate.getMinJdkVersion() + " or newer"
71 );
72 }
73
74 // process command-line options
75 processArgs(args);
76
77 // GATE builtins should be loaded from the jar (or classes dir), not
78 // from a web server (we load them over the web during testing to
79 // make sure that users can load their own that way)
80 Gate.setNetConnected(false);
81 Gate.setLocalWebServer(false);
82
83
84 // run the interface or do batch processing
85 if(batchMode) {
86 if(DEBUG) Out.prln("running batch process");
87 batchProcess();
88 } else if(dbAdminMode) {
89 if(DEBUG) Out.prln("running dbAdmin");
90 dbAdmin();
91 } else {
92 runGui();
93 }
94 } // main
95
96 /** Register any CREOLE URLs that we got on the command line */
97 private static void registerCreoleUrls() {
98 CreoleRegister reg = Gate.getCreoleRegister();
99 Iterator iter = pendingCreoleUrls.iterator();
100 while(iter.hasNext()) {
101 URL u = (URL) iter.next();
102 try {
103 reg.registerDirectories(u);
104 } catch(GateException e) {
105 Err.prln("Couldn't register CREOLE directory: " + u);
106 Err.prln(e);
107 System.exit(STATUS_ERROR);
108 }
109 }
110 } // registerCreoleUrls()
111
112 /** Main Frame of the GUI; null when no GUI running */
113 private static MainFrame frame;
114
115 /** The splash shown when Gate starts*/
116 private static Splash splash;
117
118 /**
119 * Get the main frame of the GUI. If the GUI isn't running, it
120 * is started.
121 */
122 public static MainFrame getMainFrame() throws GateException {
123 if(frame == null)
124 runGui();
125 return frame;
126 } // getMainFrame()
127
128 /** Run the user interface. */
129 private static void runGui() throws GateException {
130
131 Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
132 //show the splash
133 SwingUtilities.invokeLater(new Runnable(){
134 public void run(){
135 //build the Spash
136 JPanel splashBox = new JPanel();
137 splashBox.setLayout(new BoxLayout(splashBox, BoxLayout.Y_AXIS));
138 splashBox.setBackground(Color.white);
139
140 String splashName =
141 System.getProperty(GateConstants.APP_SPLASH_JAVA_PROPERTY_NAME);
142 if(splashName == null) {
143 splashName = "gateSplash.gif";
144 } // if
145
146 JLabel gifLbl = new JLabel(new ImageIcon(Main.class.getResource(
147 "/gate/resources/img/"+splashName)));
148 Box box = new Box(BoxLayout.X_AXIS);
149 box.add(Box.createHorizontalGlue());
150 box.add(gifLbl);
151 box.add(Box.createHorizontalGlue());
152 splashBox.add(box);
153 gifLbl = new JLabel(new ImageIcon(Main.class.getResource(
154 "/gate/resources/img/gateHeader.gif")));
155 box = new Box(BoxLayout.X_AXIS);
156 box.add(Box.createHorizontalGlue());
157 box.add(gifLbl);
158 box.add(Box.createHorizontalGlue());
159 splashBox.add(box);
160 splashBox.add(Box.createVerticalStrut(15));
161 splash = new Splash(splashBox);
162 splash.showSplash();
163 }
164 });
165
166 // initialise the library and load user CREOLE directories
167 try{
168 Gate.init();
169 }catch(Throwable t){
170 int selection = JOptionPane.showOptionDialog(
171 null,
172 "Error during initialisation:\n" + t.toString() +
173 "\nDo you still want to start GATE?",
174 "GATE", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE,
175 null, new String[]{"Cancel", "Start anyway"},
176 "Cancel");
177 if(selection != 1){
178 t.printStackTrace();
179 System.exit(1);
180 }
181 }
182
183
184 //create the main frame, show it and hide the splash
185 SwingUtilities.invokeLater(new Runnable(){
186 public void run(){
187 //this needs to run before any GUI component is constructed.
188 //the initial gate splash is exempted from this rule.
189 applyUserPreferences();
190
191 //all the defaults tables have been updated; build the GUI
192 if(Gate.isSlugGui()) {
193 frame = new ShellSlacFrame();
194 if(DEBUG) Out.prln("constructing SLUG GUI");
195 }
196 else {
197 frame = new MainFrame();
198 if(DEBUG) Out.prln("constructing GUI");
199 } // if - SLUG
200
201 // run the GUI
202 frame.setTitleChangable(true);
203 if(Gate.isSlugGui()) {
204 frame.setTitle("SLUG application");
205 }
206 else {
207 frame.setTitle(name + " " + version + " build " + build);
208 } // if - SLUG
209
210 // Set title from Java properties
211 String title =
212 System.getProperty(GateConstants.TITLE_JAVA_PROPERTY_NAME);
213 if(title != null) {
214 frame.setTitle(title);
215 } // if
216 frame.setTitleChangable(false);
217
218 // Set icon from Java properties
219 // iconName could be absolute or "gate:/img/....gif"
220 String iconName =
221 System.getProperty(GateConstants.APP_ICON_JAVA_PROPERTY_NAME);
222 if(iconName != null) {
223 try {
224 frame.setIconImage(Toolkit.getDefaultToolkit().getImage(
225 new URL(iconName)));
226 } catch(MalformedURLException mue){
227 mue.printStackTrace(Err.getPrintWriter());
228 }
229 } // if
230
231 // Validate frames that have preset sizes
232 frame.validate();
233
234 // Center the window
235 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
236 Dimension frameSize = frame.getSize();
237 if (frameSize.height > screenSize.height) {
238 frameSize.height = screenSize.height;
239 }
240 if (frameSize.width > screenSize.width) {
241 frameSize.width = screenSize.width;
242 }
243 frame.setLocation((screenSize.width - frameSize.width) / 2,
244 (screenSize.height - frameSize.height) / 2);
245
246 frame.setVisible(true);
247 if(splash != null) splash.setVisible(false);
248
249 if(!Gate.isSlugGui()) {
250 //load session if required and available;
251 //do everything from a new thread.
252 Runnable runnable = new Runnable(){
253 public void run(){
254 try{
255 File sessionFile = new File(Gate.getUserSessionFileName());
256 if(sessionFile.exists()){
257 MainFrame.lockGUI("Loading saved session...");
258 gate.util.persistence.PersistenceManager.loadObjectFromFile(sessionFile);
259 }
260 }catch(Exception e){
261 Err.prln("Failed to load session data:");
262 e.printStackTrace(Err.getPrintWriter());
263 }finally{
264 MainFrame.unlockGUI();
265 }
266 }
267 };
268 Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
269 runnable, "Session loader");
270 thread.setPriority(Thread.MIN_PRIORITY);
271 thread.start();
272 } // if - when no SLUG GUI load session
273 }
274 });
275 registerCreoleUrls();
276 } // runGui()
277
278 /** Run the db admin interface. */
279 private static void dbAdmin() throws GateException {
280 try { UserGroupEditor.main(null); } catch(Exception e) {
281 throw new GateException(e);
282 }
283 } // dbAdmin()
284
285 /**
286 * Reads the user config data and applies the required settings.
287 */
288 protected static void applyUserPreferences(){
289 //look and feel
290 String lnfClassName = Gate.getUserConfig().
291 getString(GateConstants.LOOK_AND_FEEL);
292 if(lnfClassName == null){
293 lnfClassName = UIManager.getSystemLookAndFeelClassName();
294 Gate.getUserConfig().put(GateConstants.LOOK_AND_FEEL, lnfClassName);
295 }
296 try {
297 UIManager.setLookAndFeel(lnfClassName);
298 } catch(Exception e) {
299 throw new gate.util.GateRuntimeException(e.toString());
300 }
301
302 //read the user config data
303 OptionsMap userConfig = Gate.getUserConfig();
304
305 //text font
306 Font font = userConfig.getFont(GateConstants.TEXT_COMPONENTS_FONT);
307 if(font == null){
308 String fontName = Gate.guessUnicodeFont();
309 if(fontName != null){
310 font = new Font(fontName, Font.PLAIN, 12);
311 }else{
312 font = UIManager.getFont("TextPane.font");
313 }
314 }
315
316 if(font != null){
317 OptionsDialog.setTextComponentsFont(font);
318 }
319
320 //menus font
321 font = userConfig.getFont(GateConstants.MENUS_FONT);
322 if(font == null){
323 String fontName = Gate.guessUnicodeFont();
324 if(fontName != null){
325 font = new Font(fontName, Font.PLAIN, 12);
326 }else{
327 font = UIManager.getFont("Menu.font");
328 }
329 }
330
331 if(font != null){
332 OptionsDialog.setMenuComponentsFont(font);
333 }
334
335 //other gui font
336 font = userConfig.getFont(GateConstants.OTHER_COMPONENTS_FONT);
337 if(font == null){
338 String fontName = Gate.guessUnicodeFont();
339 if(fontName != null){
340 font = new Font(fontName, Font.PLAIN, 12);
341 }else{
342 font = UIManager.getFont("Button.font");
343 }
344 }
345
346 if(font != null){
347 OptionsDialog.setComponentsFont(font);
348 }
349
350
351 }
352
353
354
355 // find out the version and build numbers
356 static {
357 // find out the version number
358 try {
359 InputStream ver = Files.getGateResourceAsStream("version.txt");
360 if (ver==null) {
361 throw new IOException();
362 }
363 BufferedReader reader = new BufferedReader(new InputStreamReader(ver,
364 "UTF-8"));
365 Main.version = reader.readLine();
366 } catch(IOException ioe) {
367 Main.version = "3.0";
368 }
369
370 // find out the build number
371 try{
372 InputStream build = Files.getGateResourceAsStream("build.txt");
373 if (build==null) {
374 throw new IOException();
375 }
376 BufferedReader reader = new BufferedReader(new InputStreamReader(build,
377 "UTF-8"));
378 Main.build = reader.readLine();
379 } catch(IOException ioe) {
380 Main.build = "0000";
381 }
382 } // static initialiser finding build and version
383
384
385 /**
386
387 <BR>
388 <B>Options processing: </B>
389
390 <BR>
391 <TABLE>
392 <TR>
393 <TH ALIGN=left COLSPAN=15>
394 -a annotator arg(s)
395 </TH>
396 <TH ALIGN=left>
397 A CREOLE annotator to run on the collection, with zero or more
398 arguments. The set of such annotators will be run in the sequence
399 they appear in the arguments list. The arguments list must end with the
400 start of another option; otherwise add a "-" after the arguments to
401 terminate the list.
402 </TH>
403 </TR>
404 <TR>
405 <TH ALIGN=left COLSPAN=15>
406 -b
407 </TH>
408 <TH ALIGN=left>
409 Batch mode. Don't start the GUI, just process options and exit after
410 any actions (e.g. running annotators).
411 </TH>
412 </TR>
413 <TR>
414 <TH ALIGN=left COLSPAN=15>
415 -c collname
416 </TH>
417 <TH ALIGN=left>
418 Name of the collection to use. If the collection already exists then
419 it will be used as it stands, otherwise it will be created. See also
420 -f.
421 </TH>
422 </TR>
423 <TR>
424 <TH ALIGN=left COLSPAN=15>
425 -d
426 </TH>
427 <TH ALIGN=left>
428 Destroy the collection after use. (The default is to save it to
429 disk.)
430 </TH>
431 </TR>
432 <TR>
433 <TH ALIGN=left COLSPAN=15>
434 -f file(s)
435 </TH>
436 <TH ALIGN=left>
437 One or more files to create a collection with. If the collection
438 being used (see -c) already exists, these files are ignored.
439 Otherwise they are used to create the collection.
440 </TH>
441 </TR>
442 <TR>
443 <TH ALIGN=left COLSPAN=15>
444 -h
445 </TH>
446 <TH ALIGN=left>
447 Print a usage message and exit.
448 </TH>
449 </TR>
450 <TR>
451 <TH ALIGN=left COLSPAN=15>
452 -p creolepath
453 </TH>
454 <TH ALIGN=left>
455 Sets the search path for CREOLE modules.
456 </TH>
457 </TR>
458 <TR>
459 <TH ALIGN=left COLSPAN=15>
460 -v classname(s)
461 </TH>
462 <TH ALIGN=left>
463 Verbose: turns on debugging output. Takes zero or more class names
464 to debug.
465 </TH>
466 </TR>
467 </TABLE>
468
469 */
470 /** Name of the collection we were asked to process. */
471 private static String collName;
472
473 /** Search path for CREOLE modules. */
474 private static String creolePath;
475
476 /** List of files we were asked to build a collection from. */
477 private static List fileNames = new ArrayList();
478
479 /** List of annotators we were asked to run on the collection. */
480 private static List annotatorNames = new ArrayList();
481
482 /** Map of annotator arguments. */
483 private static Map annotatorArgsMap = new HashMap();
484
485 /** List of classes we were asked to debug. */
486 private static List debugNames = new ArrayList();
487
488 /** Are we in batch mode? */
489 public static boolean batchMode = false;
490
491 /** Are we in db admin mode? */
492 public static boolean dbAdminMode = false;
493
494 /** Don't save collection after batch? */
495 private static boolean destroyColl = false;
496
497 /** Verbose? */
498 private static boolean verbose = false;
499
500 private static boolean runCorpusBenchmarkTool = false;
501
502 public static String name = "GATE";
503 public static String version;
504 public static String build;
505
506 /** Process arguments and set up member fields appropriately.
507 * Will shut down the process (via System.exit) if there are
508 * incorrect arguments, or if the arguments ask for something
509 * simple like printing the help message.
510 */
511 public static void processArgs(String[] args) {
512
513 Getopt g = new Getopt("GATE main", args, "hd:ei:asj");
514 int c;
515 while( (c = g.getopt()) != -1 )
516 switch(c) {
517 // -a
518 case 'a':
519 dbAdminMode = true;
520 break;
521 // -h
522 case 'h':
523 help();
524 usage();
525 System.exit(STATUS_NORMAL);
526 break;
527 // -d creole-dir
528 case 'd':
529 String urlString = g.getOptarg();
530 URL u = null;
531 try {
532 u = new URL(urlString);
533 } catch(MalformedURLException e) {
534 Err.prln("Bad URL: " + urlString);
535 Err.prln(e);
536 System.exit(STATUS_ERROR);
537 }
538 pendingCreoleUrls.add(u);
539 Out.prln(
540 "CREOLE Directory " + urlString + " queued for registration"
541 );
542 break;
543 // -i gate.xml site-wide init file
544 case 'i':
545 String optionString = g.getOptarg();
546 URL u2 = null;
547 File f = new File(optionString);
548 try {
549 u2 = f.toURL();
550 } catch(MalformedURLException e) {
551 Err.prln("Bad initialisation file: " + optionString);
552 Err.prln(e);
553 System.exit(STATUS_ERROR);
554 }
555 Gate.setSiteConfigFile(f);
556 if(DEBUG)
557 Out.prln(
558 "Initialisation file " + optionString +
559 " recorded for initialisation"
560 );
561 break;
562 // -e runs the CorpusBenchmarkTool (e for evaluate)
563 case 'e':
564 try {
565 CorpusBenchmarkTool.main(args);
566 } catch (GateException ex) {
567 Out.prln("Error running the evaluation tool: " + ex.getMessage());
568 System.exit(-1);
569 }
570 break;
571 // -s runs the SLUG GUI
572 case 's':
573 Gate.setSlugGui(true);
574 break;
575 // -j enable Jape Debugger
576 case 'j':
577 Gate.setEnableJapeDebug(true);
578 break;
579
580
581
582 /*
583 // -c collname
584 case '-c':
585 collName = g.getOptarg();
586 break;
587
588 // -b
589 case '-b':
590 batchMode = true;
591 break;
592
593 // -a annotator(s)
594 case '-a':
595 if(++i == args.length) { usage(); return; }
596 String annotatorName = g.getOptarg();
597 annotatorNames.add(annotatorName);
598 // collect any args for the annotator
599 break;
600
601 // -d
602 case '-d':
603 destroyColl = true;
604 break;
605
606 // -f file(s)
607 case '-f':
608 while(++i < args.length)
609 if(args[i].toCharArray()[0] == '-') { // start of another option
610 i--;
611 break;
612 }
613 else
614 fileNames.add(args[i]);
615 break;
616
617 // -p creolepath
618 case '-p':
619 if(++i < args.length)
620 creolePath = args[i];
621 else
622 { usage(); return; }
623 break;
624
625 // -v classname(s)
626 case '-v':
627 verbose = true;
628 Debug.setDebug(true);
629 while(++i < args.length) {
630 if(args[i].toCharArray()[0] == '-') { // start of another option
631 i--;
632 break;
633 }
634 else
635 debugNames.add(args[i]);
636 } // while
637 break;
638 */
639
640 case '?':
641 // leave the warning to getopt
642 System.exit(STATUS_ERROR);
643 break;
644
645 default:
646 // shouldn't happen!
647 Err.prln("getopt() returned " + c + "\n");
648 System.exit(STATUS_ERROR);
649 break;
650 } // getopt switch
651
652 } // processArgs()
653
654 /** Run commands as a batch process. */
655 private static void batchProcess() throws GateException{
656 // initialise the library and load user CREOLE directories
657 Gate.init();
658 registerCreoleUrls();
659
660 /*
661 // turn debugging on where requested
662 if(verbose) {
663 for(ArrayIterator i = debugNames.begin(); ! i.atEnd(); i.advance()) {
664 try { Debug.setDebug(Class.forName(((String) i.get())), true); }
665 catch(ClassNotFoundException e) {
666 System.err.println(
667 "can't debug class " + (String) i.get() + ": " + e.toString()
668 );
669 }
670 } // for
671 } // debugging on
672
673 // collection: does it exist and can we open it?
674 if(collName == null) {
675 System.err.println("no collection name given");
676 usage();
677 return;
678 }
679 File collDir = new File(collName);
680 JdmCollection coll = null;
681 if(collDir.exists()) { // open collection
682 Debug.prnl("opening collection " + collName);
683 try {
684 coll = new JdmCollection(collName);
685 } catch (JdmException e) {
686 System.err.println(
687 "Couldn't open collection " + collName + " " + e.toString()
688 );
689 return;
690 }
691 } else { // create collection and add documents
692 Debug.prnl("creating collection " + collName);
693 JdmAttributeSequence attrs = new JdmAttributeSequence();
694 try {
695 coll = new JdmCollection(collName, attrs);
696 } catch (JdmException e) {
697 System.err.println(
698 "Couldn't create collection " + collName + " " + e.toString()
699 );
700 return;
701 }
702
703 // add the documents to the collection
704 for(ArrayIterator i = fileNames.begin(); ! i.atEnd(); i.advance()) {
705 Debug.prnl("adding document " + (String) i.get());
706 try {
707 JdmDocument doc = coll.createDocument(
708 (String) i.get(),
709 null,
710 new JdmAnnotationSet(),
711 new JdmAttributeSequence()
712 );
713 } catch (JdmException e) {
714 System.err.println(
715 "Can't add document " + (String) i.get() + ": " + e.toString()
716 );
717 } // catch
718 } // for each filename
719 } // collection create
720
721 // run the annotators on each document in the collection
722 // for each document
723 JdmDocument doc = null;
724 if(coll.length() > 0)
725 try{ doc = coll.firstDocument(); } catch(JdmException e) { }
726 for(int i = 0; i<coll.length(); i++) {
727 if(doc == null) continue; // first and next doc shouldn't throw excptns!
728
729 // for each annotator
730 for(ArrayIterator j = annotatorNames.begin(); !j.atEnd(); j.advance()) {
731 String annotatorName = (String) j.get();
732 Debug.prnl(
733 "calling annotator " + annotatorName + " on doc " + doc.getId()
734 );
735
736 // load the annotator class
737 Annotator annotator = null;
738 Class annotatorClass = null;
739 try {
740 // cheat and assume that all annotators are on CLASSPATH
741 annotatorClass = Class.forName(annotatorName);
742 } catch (Exception ex) {
743 System.err.println(
744 "Could load class for CREOLE object " + annotatorName + ": " +
745 ex.toString()
746 );
747 continue;
748 }
749
750 // construct the annotator
751 try {
752 annotator = (Annotator) annotatorClass.newInstance();
753 } catch (Throwable ex) { // naughty chap
754 System.err.println(
755 "Could create instance of CREOLE object " + annotatorName + ": " +
756 ex.toString()
757 );
758 continue;
759 }
760
761 // annotate this document
762 String[] args = (String[]) annotatorArgsMap.get(annotatorName);
763 if(args == null) args = new String[0];
764 annotator.annotate(doc, args);
765 } // for each annotator
766
767 doc = null;
768 try { doc = coll.nextDocument(); } catch(JdmException e) { }
769 } // for each doc, annotate
770
771 // save collection?
772 if(! destroyColl) {
773 Debug.prnl("saving the collection");
774 try {
775 coll.sync();
776 } catch (JdmException e) {
777 System.err.println(
778 "Can't save collection " + collName + ": " + e.toString()
779 );
780 }
781 } else {
782 Debug.prnl("destroying collection");
783 try { coll.destroy(); } catch(JdmException e) {
784 // if we didn't sync we can't destroy, but that's not an error
785 }
786 }
787
788 Debug.prnl("done batch process");
789 */
790 } // batchProcess()
791
792 /** Display a usage message */
793 public static void usage() {
794 Out.prln(
795 "Usage: java gate.Main " +
796 "[ -h [-d CREOLE-URL]" +
797 ""
798 );
799 } // usage()
800
801 /** Display a help message */
802 public static void help() {
803 String nl = Strings.getNl();
804 Out.prln(
805 "For help on command-line options and other information " + nl +
806 "see the user manual in your GATE distribution or at " + nl +
807 "http://gate.ac.uk/sale/tao/"
808 );
809 } // help()
810
811 /** The list of pending URLs to add to the CREOLE register */
812 private static List pendingCreoleUrls = new ArrayList();
813
814 } // class Main
815