Transducer.java
001 /*
002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *
010  *  Valentin Tablan, 01 Feb 2000
011  *
012  *  $Id: Transducer.java 13495 2011-03-01 17:30:35Z johann_p $
013  */
014 package gate.creole;
015 
016 import gate.*;
017 import gate.creole.metadata.CreoleParameter;
018 import gate.creole.metadata.CreoleResource;
019 import gate.creole.metadata.Optional;
020 import gate.creole.metadata.RunTime;
021 import gate.gui.MainFrame;
022 import gate.jape.Batch;
023 import gate.jape.DefaultActionContext;
024 import gate.jape.JapeException;
025 import gate.jape.constraint.AnnotationAccessor;
026 import gate.jape.constraint.ConstraintPredicate;
027 import gate.util.Benchmarkable;
028 import gate.util.Err;
029 import java.io.File;
030 import java.io.FileOutputStream;
031 import java.io.IOException;
032 import java.io.ObjectInputStream;
033 import java.io.ObjectOutputStream;
034 import java.util.*;
035 
036 import javax.swing.*;
037 
038 /**
039  * A cascaded multi-phase transducer using the Jape language which is a variant
040  * of the CPSL language.
041  */
042 @CreoleResource(name = "JAPE Transducer",
043   comment = "A module for executing Jape grammars.",
044   helpURL = "http://gate.ac.uk/userguide/chap:jape",
045   icon = "jape"
046   )
047 public class Transducer
048   extends AbstractLanguageAnalyser
049   implements gate.gui.ActionsPublisher, Benchmarkable, ControllerAwarePR
050 {
051   public static final String TRANSD_DOCUMENT_PARAMETER_NAME = "document";
052 
053   public static final String TRANSD_INPUT_AS_PARAMETER_NAME = "inputASName";
054 
055   public static final String TRANSD_OUTPUT_AS_PARAMETER_NAME = "outputASName";
056 
057   public static final String TRANSD_ENCODING_PARAMETER_NAME = "encoding";
058 
059   public static final String TRANSD_GRAMMAR_URL_PARAMETER_NAME = "grammarURL";
060 
061   public static final String TRANSD_BINARY_GRAMMAR_URL_PARAMETER_NAME = "binaryGrammarURL";
062 
063   public static final String TRANSD_OPERATORS_PARAMETER_NAME = "operators";
064 
065   public static final String TRANSD_ANNOTATION_ACCESSORS_PARAMETER_NAME = "annotationAccessors";
066 
067 
068   protected List<Action> actionList;
069   protected DefaultActionContext actionContext;
070 
071   /**
072    * Default constructor. Does nothing apart from calling the default
073    * constructor from the super class. The actual object initialisation is done
074    * via the {@link #init} method.
075    */
076   public Transducer() {
077     actionList = new ArrayList<Action>();
078     actionList.add(null);
079     actionList.add(new SerializeTransducerAction());
080   }
081 
082   /*
083    * private void writeObject(ObjectOutputStream oos) throws IOException {
084    * Out.prln("writing transducer"); oos.defaultWriteObject();
085    * Out.prln("finished writing transducer"); } // writeObject
086    */
087   /**
088    * This method is the one responsible for initialising the transducer. It
089    * assumes that all the needed parameters have been already set using the
090    * appropiate setXXX() methods.
091    *
092    @return a reference to <b>this</b>
093    */
094   public Resource init() throws ResourceInstantiationException {
095     try {
096       fireProgressChanged(0);
097 
098       initCustomConstraints();
099 
100       if(binaryGrammarURL != null) {
101         ObjectInputStream s = new ObjectInputStream(binaryGrammarURL
102                 .openStream());
103         batch = (gate.jape.Batch)s.readObject();
104       else if(grammarURL != null) {
105         if(encoding != null) {
106           batch = new Batch(grammarURL, encoding, new InternalStatusListener());
107           if(enableDebugging != null) {
108             batch.setEnableDebugging(enableDebugging.booleanValue());
109           else {
110             batch.setEnableDebugging(false);
111           }
112           batch.setOntology(ontology);
113         else {
114           throw new ResourceInstantiationException("encoding is not set!");
115         }
116       else {
117         throw new ResourceInstantiationException(
118                 "Neither grammarURL or binaryGrammarURL parameters are set!");
119       }
120     catch(Exception e) {
121       String message = "Error while parsing the grammar ";
122       if(grammarURL != nullmessage += "(" + grammarURL.toExternalForm() ")";
123       message += ":";
124       throw new ResourceInstantiationException(message, e);
125     finally {
126       fireProcessFinished();
127     }
128     actionContext = new DefaultActionContext();
129     batch.setActionContext(actionContext);
130     batch.addProgressListener(new IntervalProgressListener(0100));
131     return this;
132   }
133 
134   /**
135    * Implementation of the run() method from {@link java.lang.Runnable}. This
136    * method is responsible for doing all the processing of the input document.
137    */
138   public void execute() throws ExecutionException {
139     interrupted = false;
140     if(document == nullthrow new ExecutionException("No document provided!");
141     if(inputASName != null && inputASName.equals("")) inputASName = null;
142     if(outputASName != null && outputASName.equals("")) outputASName = null;
143     // the action context always reflects, for each document executed,
144     // the current PR features and the corpus, if present
145     actionContext.setCorpus(corpus);
146     actionContext.setPRFeatures(features);
147     try {
148       batch.transduce(document, inputASName == null
149               ? document.getAnnotations()
150               : document.getAnnotations(inputASName), outputASName == null
151               ? document.getAnnotations()
152               : document.getAnnotations(outputASName));
153     catch(JapeException je) {
154       throw new ExecutionException(je);
155     }
156   }
157 
158   /**
159    * Gets the list of actions that can be performed on this resource.
160    *
161    @return a List of Action objects (or null values)
162    */
163   public List<Action> getActions() {
164     List<Action> result = new ArrayList<Action>();
165     result.addAll(actionList);
166     return result;
167   }
168 
169   /**
170    * Loads any custom operators and annotation accessors into the ConstraintFactory.
171    @throws ResourceInstantiationException
172    */
173   protected void initCustomConstraints() throws ResourceInstantiationException {
174     //Load operators
175     if (operators != null) {
176       for(String opName : operators) {
177         Class<? extends ConstraintPredicate> clazz = null;
178         try {
179           clazz = Class.forName(opName, true, Gate.getClassLoader())
180                         .asSubclass(ConstraintPredicate.class);
181         }
182         catch(ClassNotFoundException e) {
183           //if couldn't find it that way, try with current thread class loader
184           try {
185             clazz = Class.forName(opName, true,
186                 Thread.currentThread().getContextClassLoader())
187                   .asSubclass(ConstraintPredicate.class);
188           }
189           catch(ClassNotFoundException e1) {
190             throw new ResourceInstantiationException("Cannot load class for operator: " + opName, e1);
191           }
192         }
193         catch(ClassCastException cce) {
194           throw new ResourceInstantiationException("Operator class '" + opName + "' must implement ConstraintPredicate");
195         }
196 
197         //instantiate an instance of the class so can get the operator string
198         try {
199           ConstraintPredicate predicate = clazz.newInstance();
200           String opSymbol = predicate.getOperator();
201           //now store it in ConstraintFactory
202           Factory.getConstraintFactory().addOperator(opSymbol, clazz);
203         }
204         catch(Exception e) {
205           throw new ResourceInstantiationException("Cannot instantiate class for operator: " + opName, e);
206         }
207       }
208     }
209 
210     //Load annotationAccessors
211     if (annotationAccessors != null) {
212       for(String accessorName : annotationAccessors) {
213         Class<? extends AnnotationAccessor> clazz = null;
214         try {
215           clazz = Class.forName(accessorName, true, Gate.getClassLoader())
216                      .asSubclass(AnnotationAccessor.class);
217         }
218         catch(ClassNotFoundException e) {
219           //if couldn't find it that way, try with current thread class loader
220           try {
221             clazz = Class.forName(accessorName, true,
222                 Thread.currentThread().getContextClassLoader())
223                    .asSubclass(AnnotationAccessor.class);
224           }
225           catch(ClassNotFoundException e1) {
226             throw new ResourceInstantiationException("Cannot load class for accessor: " + accessorName, e1);
227           }
228         }
229         catch(ClassCastException cce) {
230           throw new ResourceInstantiationException("Operator class '" + accessorName + "' must implement AnnotationAccessor");
231         }
232 
233         //instantiate an instance of the class so can get the meta-property name string
234         try {
235           AnnotationAccessor aa = clazz.newInstance();
236           String accSymbol = (String)aa.getKey();
237           //now store it in ConstraintFactory
238           Factory.getConstraintFactory().addMetaProperty(accSymbol, clazz);
239         }
240         catch(Exception e) {
241           throw new ResourceInstantiationException("Cannot instantiate class for accessor: " + accessorName, e);
242         }
243 
244       }
245     }
246   }
247 
248 
249   /**
250    * Saves the Jape Transuder to the binary file.
251    *
252    @author niraj
253    */
254   protected class SerializeTransducerAction extends javax.swing.AbstractAction {
255     public SerializeTransducerAction() {
256       super("Serialize Transducer");
257       putValue(SHORT_DESCRIPTION, "Serializes the Transducer as binary file");
258     }
259 
260     public void actionPerformed(java.awt.event.ActionEvent evt) {
261       Runnable runnable = new Runnable() {
262         public void run() {
263           JFileChooser fileChooser = MainFrame.getFileChooser();
264           fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());
265           fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
266           fileChooser.setMultiSelectionEnabled(false);
267           if(fileChooser.showSaveDialog(null== JFileChooser.APPROVE_OPTION) {
268             File file = fileChooser.getSelectedFile();
269             try {
270               MainFrame.lockGUI("Serializing JAPE Transducer...");
271               FileOutputStream out = new FileOutputStream(file);
272               ObjectOutputStream s = new ObjectOutputStream(out);
273               s.writeObject(batch);
274               s.flush();
275               s.close();
276               out.close();
277             catch(IOException ioe) {
278               JOptionPane.showMessageDialog(MainFrame.getInstance()"Error!\n" + ioe.toString(),
279                       "GATE", JOptionPane.ERROR_MESSAGE);
280               ioe.printStackTrace(Err.getPrintWriter());
281             finally {
282               MainFrame.unlockGUI();
283             }
284           }
285         }
286       };
287       Thread thread = new Thread(runnable, "Transduer Serialization");
288       thread.setPriority(Thread.MIN_PRIORITY);
289       thread.start();
290     }
291   }
292 
293   /**
294    * Notifies all the PRs in this controller that they should stop their
295    * execution as soon as possible.
296    */
297   public synchronized void interrupt() {
298     interrupted = true;
299     batch.interrupt();
300   }
301 
302   /**
303    * Sets the grammar to be used for building this transducer.
304    *
305    @param newGrammarURL
306    *          an URL to a file containing a Jape grammar.
307    */
308   @CreoleParameter(
309     comment = "The URL to the grammar file.",
310     suffixes = "jape",
311     disjunction = "grammar",
312     priority = 1
313   )
314   public void setGrammarURL(java.net.URL newGrammarURL) {
315     grammarURL = newGrammarURL;
316   }
317 
318   /**
319    * Gets the URL to the grammar used to build this transducer.
320    *
321    @return {@link java.net.URL} pointing to the grammar file.
322    */
323   public java.net.URL getGrammarURL() {
324     return grammarURL;
325   }
326 
327   /**
328    *
329    * Sets the encoding to be used for reding the input file(s) forming the Jape
330    * grammar. Note that if the input grammar is a multi-file one than the same
331    * encoding will be used for reding all the files. Multi file grammars with
332    * different encoding across the composing files are not supported!
333    *
334    @param newEncoding
335    *          a {link String} representing the encoding.
336    */
337   @CreoleParameter(
338     comment = "The encoding used for reading the grammar",
339     defaultValue = "UTF-8"
340   )
341   public void setEncoding(String newEncoding) {
342     encoding = newEncoding;
343   }
344 
345   /**
346    * Gets the encoding used for reding the grammar file(s).
347    */
348   public String getEncoding() {
349     return encoding;
350   }
351 
352   /**
353    * Sets the {@link gate.AnnotationSet} to be used as input for the transducer.
354    *
355    @param newInputASName
356    *          a {@link gate.AnnotationSet}
357    */
358   @RunTime
359   @Optional
360   @CreoleParameter(
361     comment = "The annotation set to be used as input for the transducer"
362   )
363   public void setInputASName(String newInputASName) {
364     inputASName = newInputASName;
365   }
366 
367   /**
368    * Gets the {@link gate.AnnotationSet} used as input by this transducer.
369    *
370    @return {@link gate.AnnotationSet}
371    */
372   public String getInputASName() {
373     return inputASName;
374   }
375 
376   /**
377    * Sets the {@link gate.AnnotationSet} to be used as output by the transducer.
378    *
379    @param newOutputASName
380    *          a {@link gate.AnnotationSet}
381    */
382   @RunTime
383   @Optional
384   @CreoleParameter(
385     comment = "The annotation set to be used as output for the transducer"
386   )
387   public void setOutputASName(String newOutputASName) {
388     outputASName = newOutputASName;
389   }
390 
391   /**
392    * Gets the {@link gate.AnnotationSet} used as output by this transducer.
393    *
394    @return {@link gate.AnnotationSet}
395    */
396   public String getOutputASName() {
397     return outputASName;
398   }
399 
400   public Boolean getEnableDebugging() {
401     return enableDebugging;
402   }
403 
404   public void setEnableDebugging(Boolean enableDebugging) {
405     this.enableDebugging = enableDebugging;
406   }
407 
408   /**
409    * Gets the list of class names for any custom boolean operators.
410    * Classes must implement {@link gate.jape.constraint.ConstraintPredicate}.
411    */
412   public List<String> getOperators() {
413     return operators;
414   }
415 
416   /**
417    * Sets the list of class names for any custom boolean operators.
418    * Classes must implement {@link gate.jape.constraint.ConstraintPredicate}.
419    */
420   @Optional
421   @CreoleParameter(
422     comment = "Class names that implement gate.jape.constraint.ConstraintPredicate."
423   )
424   public void setOperators(List<String> operators) {
425     this.operators = operators;
426   }
427 
428   /**
429    * Gets the list of class names for any custom
430    {@link gate.jape.constraint.AnnotationAccessor}s.
431    */
432   public List<String> getAnnotationAccessors() {
433     return annotationAccessors;
434   }
435 
436   /**
437    * Sets the list of class names for any custom
438    {@link gate.jape.constraint.AnnotationAccessor}s.
439    */
440   @Optional
441   @CreoleParameter(
442     comment = "Class names that implement gate.jape.constraint.AnnotationAccessor."
443   )
444   public void setAnnotationAccessors(List<String> annotationAccessors) {
445     this.annotationAccessors = annotationAccessors;
446   }
447   
448   /**
449    * Get the benchmark ID of this Transducers batch.
450    */
451   public String getBenchmarkId() {
452     return batch.getBenchmarkId();
453   }
454 
455   /**
456    * Set the benchmark ID of this PR.
457    */
458   public void setBenchmarkId(String benchmarkId) {
459     batch.setBenchmarkId(benchmarkId);
460   }
461 
462   /**
463    * The URL to the jape file used as grammar by this transducer.
464    */
465   protected java.net.URL grammarURL;
466 
467   /**
468    * The URL to the serialized jape file used as grammar by this transducer.
469    */
470   protected java.net.URL binaryGrammarURL;
471 
472   /**
473    * The actual JapeTransducer used for processing the document(s).
474    */
475   protected Batch batch;
476 
477   /**
478    * The encoding used for reding the grammar file(s).
479    */
480   protected String encoding;
481 
482   /**
483    * The {@link gate.AnnotationSet} used as input for the transducer.
484    */
485   protected String inputASName;
486 
487   /**
488    * The {@link gate.AnnotationSet} used as output by the transducer.
489    */
490   protected String outputASName;
491 
492   /**
493    * The ontology that will be available on the RHS of JAPE rules.
494    */
495   protected gate.creole.ontology.Ontology ontology;
496 
497   /**
498    * List of class names for any custom
499    {@link gate.jape.constraint.ConstraintPredicate}.
500    */
501   protected List<String> operators = null;
502 
503   /**
504    * List of class names for any custom
505    {@link gate.jape.constraint.AnnotationAccessor}s.
506    */
507   protected List<String> annotationAccessors = null;
508 
509   /**
510    * Gets the ontology used by this transducer.
511    *
512    @return an {@link gate.creole.ontology.Ontology} value.
513    */
514   public gate.creole.ontology.Ontology getOntology() {
515     return ontology;
516   }
517 
518   /**
519    * Sets the ontology used by this transducer.
520    *
521    @param ontology
522    *          an {@link gate.creole.ontology.Ontology} value.
523    */
524   @RunTime
525   @Optional
526   @CreoleParameter(
527     comment = "The ontology to be used by this transducer"
528   )
529   public void setOntology(gate.creole.ontology.Ontology ontology) {
530     this.ontology = ontology;
531     //ontology is now a run-time param so we need to propagate it down to the
532     //actual SPTs included in this transducer.
533     if(batch!= nullbatch.setOntology(ontology);
534   }
535 
536   /**
537    * A switch used to activate the JAPE debugger.
538    */
539   protected Boolean enableDebugging;
540 
541 
542   public java.net.URL getBinaryGrammarURL() {
543     return binaryGrammarURL;
544   }
545 
546   @CreoleParameter(
547     comment = "The URL to the binary grammar file.",
548     suffixes = "jape",
549     disjunction = "grammar",
550     priority = 100
551   )
552   public void setBinaryGrammarURL(java.net.URL binaryGrammarURL) {
553     this.binaryGrammarURL = binaryGrammarURL;
554   }
555 
556   // methods implemeting ControllerAwarePR
557   public void controllerExecutionStarted(Controller c)
558     throws ExecutionException {
559     actionContext.setController(c);
560     actionContext.setCorpus(corpus);
561     actionContext.setPRFeatures(features);
562     batch.runControllerExecutionStartedBlock(actionContext,c,ontology);
563   }
564 
565   public void controllerExecutionFinished(Controller c)
566     throws ExecutionException {
567     batch.runControllerExecutionFinishedBlock(actionContext,c,ontology);
568     actionContext.setCorpus(null);
569     actionContext.setController(null);
570   }
571 
572   public void controllerExecutionAborted(Controller c, Throwable t)
573     throws ExecutionException {
574     batch.runControllerExecutionAbortedBlock(actionContext,c,t,ontology);
575     actionContext.setCorpus(null);
576     actionContext.setController(null);
577   }
578 
579 
580 }