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