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