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 != null) message += "(" + 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(0, 100));
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 == null) throw 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 a {@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 a {@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 a {@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!= null) batch.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 }
|