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