RightHandSide.java
001 /*
002  *  RightHandSide.java - transducer class
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 24/07/98
013  *
014  *  $Id: RightHandSide.java 17895 2014-04-24 06:21:59Z markagreenwood $
015  */
016 
017 
018 package gate.jape;
019 
020 import gate.AnnotationSet;
021 import gate.Document;
022 import gate.Factory;
023 import gate.FeatureMap;
024 import gate.Gate;
025 import gate.creole.ontology.Ontology;
026 import gate.util.Err;
027 import gate.util.GateClassLoader;
028 import gate.util.GateRuntimeException;
029 import gate.util.Strings;
030 
031 import java.io.File;
032 import java.io.IOException;
033 import java.lang.reflect.InvocationHandler;
034 import java.lang.reflect.Method;
035 import java.lang.reflect.Proxy;
036 import java.util.ArrayList;
037 import java.util.HashMap;
038 import java.util.HashSet;
039 import java.util.List;
040 import java.util.ListIterator;
041 import java.util.Map;
042 import java.util.Set;
043 import java.util.concurrent.atomic.AtomicInteger;
044 
045 
046 /**
047   * The RHS of a CPSL rule. The action part. Contains an inner class
048   * created from the code in the grammar RHS.
049   */
050 public class RightHandSide implements JapeConstants, java.io.Serializable
051 {
052   private static final long serialVersionUID = -4359589687308736378L;
053 
054   /** An instance of theActionClass. */
055   transient private Object theActionObject;
056 
057   /** The string we use to create the action class. */
058   private StringBuffer actionClassString;
059 
060   /** The name of the action class. */
061   private String actionClassName;
062 
063   /** Package name for action classes. It's called a "dir name" because
064     * we used to dump the action classes to disk and compile them there.
065     */
066   static private String actionsDirName = "japeactionclasses";
067 
068   /** The qualified name of the action class. */
069   private String actionClassQualifiedName;
070 
071   /** Name of the .java file for the action class. */
072   private String actionClassJavaFileName;
073 
074   /** Name of the .class file for the action class. */
075   private String actionClassClassFileName;
076   
077   /** A list of source info object for mapping between Java and Jape. */
078   //private transient List<SourceInfo> sourceInfo;
079   private transient SourceInfo sourceInfo;
080   
081   private transient GateClassLoader classloader;
082 
083   /** Cardinality of the action class set. Used for ensuring class name
084     * uniqueness.
085     */
086   private static AtomicInteger actionClassNumber = new AtomicInteger();
087 
088   /** The set of block names.
089     * Used to ensure we only get their annotations once in the action class.
090     */
091   private Set<String> blockNames;
092 
093   /** Returns the string for the java code */
094   public String getActionClassString() { return actionClassString.toString()}
095 
096   public String getActionClassName() { return actionClassQualifiedName; }
097 
098   /** The LHS of our rule, where we get bindings from. */
099   private LeftHandSide lhs;
100 
101   /** A list of the files and directories we create. */
102   static private List<File> tempFiles = new ArrayList<File>();
103 
104   /** Local fashion for newlines. */
105   private final String nl = Strings.getNl();
106 
107   /** Debug flag. */
108   static final boolean debug = false;
109   private String phaseName;
110   private String ruleName;
111 
112   /** Construction from the transducer name, rule name and the LHS. */
113   public RightHandSide(
114     String transducerName,
115     String ruleName,
116     LeftHandSide lhs,
117     String importblock
118   ) {
119     // debug = true;
120     this.lhs = lhs;
121     this.phaseName = transducerName;
122     this.ruleName = ruleName;
123     actionClassName = new String(
124       transducerName + ruleName + "ActionClass" + actionClassNumber.getAndIncrement()
125     );
126     blockNames = new HashSet<String>();
127     actionClassString = new StringBuffer(
128       "// " + actionClassName + nl +
129       "package " + actionsDirName + "; " + nl +
130       importblock + nl +
131       "public class " + actionClassName + nl +
132       "implements java.io.Serializable, gate.jape.RhsAction { " + nl +
133       "  private gate.jape.ActionContext ctx;"+nl+
134       "  public java.lang.String ruleName() { return \""+ruleName+"\"; }"+nl+
135       "  public java.lang.String phaseName() { return \""+phaseName+"\"; }"+nl+
136       "  public void setActionContext(gate.jape.ActionContext ac) { ctx = ac; }"+nl+
137       "  public gate.jape.ActionContext getActionContext() { return ctx; }"+nl+
138       "  public void doit(gate.Document doc, " + nl +
139       "                   java.util.Map<java.lang.String, gate.AnnotationSet> bindings, " + nl +
140       //"                   gate.AnnotationSet annotations, " + nl +
141       "                   gate.AnnotationSet inputAS, gate.AnnotationSet outputAS, " + nl +
142       "                   gate.creole.ontology.Ontology ontology) throws gate.jape.JapeException {" + nl
143     );
144 
145     // initialise various names
146     actionClassJavaFileName =
147       actionsDirName +  File.separator +
148       actionClassName.replace('.', File.separatorChar".java";
149     actionClassQualifiedName =
150       actionsDirName.
151       replace(File.separatorChar, '.').replace('/''.').replace('\\''.'+
152       "." + actionClassName;
153     actionClassClassFileName =
154       actionClassQualifiedName.replace('.', File.separatorChar".class";
155     
156     sourceInfo = new SourceInfo(actionClassQualifiedName, phaseName, ruleName);
157   // Construction from lhs
158   
159   /** Construction from an existing RHS */
160   public RightHandSide(RightHandSide existingRhs) {
161     this.lhs = existingRhs.lhs;
162     this.phaseName = existingRhs.phaseName;
163     this.ruleName = existingRhs.ruleName;
164     this.actionClassName = existingRhs.actionClassName;
165     this.blockNames = existingRhs.blockNames;
166     this.actionClassString = existingRhs.actionClassString;
167     this.actionClassJavaFileName = existingRhs.actionClassJavaFileName;
168     this.actionClassQualifiedName = existingRhs.actionClassQualifiedName;
169     this.actionClassClassFileName = existingRhs.actionClassClassFileName;
170     this.sourceInfo = existingRhs.sourceInfo;
171     
172     // this is the important bit - the cloned RHS needs to create its own
173     // instance of the action class the first time its transduce() is called.
174     this.theActionObject = null;
175   // Construction from existing RHS
176 
177   /** Add an anonymous block to the action class */
178   public void addBlock(String anonymousBlock) {
179     actionClassString.append(nl);
180     actionClassString.append("if (true) {");
181     actionClassString.append(nl);
182     actionClassString.append(sourceInfo.addBlock(actionClassString.toString(), anonymousBlock));
183     actionClassString.append(nl);
184     actionClassString.append("}");
185     actionClassString.append(nl);
186   // addBlock(anon)
187 
188   /** Add a named block to the action class */
189   public void addBlock(String name, String namedBlock) {
190     // is it really a named block?
191     // (dealing with null name cuts code in the parser...)
192     if(name == null) {
193       addBlock(namedBlock);
194       return;
195     }
196 
197     if(blockNames.add(name)) // it wasn't already a member
198       actionClassString.append(
199         "    gate.AnnotationSet " + name + "Annots = bindings.get(\""
200         + name + "\"); " + nl
201       );
202 
203     actionClassString.append(
204       "    if(" + name + "Annots != null && " + name +
205       "Annots.size() != 0) { " + nl);
206       
207     actionClassString.append(sourceInfo.addBlock(actionClassString.toString(), namedBlock));
208       
209    actionClassString.append(
210       nl + "    }" + nl
211     );
212   // addBlock(name, block)
213   
214   public void finish(GateClassLoader classloader) {
215     this.classloader = classloader;
216   }
217   
218 
219   /** Create the action class and an instance of it. */
220   public void createActionClass() throws JapeException {
221     // terminate the class string
222     actionClassString.append("  }" + nl + "}" + nl);
223 //    try {
224 //      Javac.loadClass(actionClassString.toString(),
225 //                           actionClassJavaFileName);
226 //    } catch(GateException e) {
227 //      String nl = Strings.getNl();
228 //      String actionWithNumbers =
229 //        Strings.addLineNumbers(actionClassString.toString());
230 //      throw new JapeException(
231 //        "Couldn't create action class: " + nl + e + nl +
232 //        "offending code was: " + nl + actionWithNumbers + nl
233 //      );
234 //    }
235 //    instantiateActionClass();
236   // createActionClass
237 
238   /** Create an instance of the action class. */
239   public void instantiateActionClass() throws JapeException {
240 
241     try {
242       theActionObject = classloader.
243                         loadClass(actionClassQualifiedName).
244                         newInstance();
245     catch(Exception e) {
246       throw new JapeException(
247         "couldn't create instance of action class " + actionClassName + ": "
248         + e.getMessage()
249       );
250     }
251   // instantiateActionClass
252 
253   /** Remove class files created for actions. */
254   public static void cleanUp() {
255     if(tempFiles.size() == 0return;
256 
257     // traverse the list in reverse order, coz any directories we
258     // created were done first
259     for(ListIterator<File> i = tempFiles.listIterator(tempFiles.size()-1);
260         i.hasPrevious();
261        ) {
262       File tempFile = i.previous();
263       tempFile.delete();
264     // for each tempFile
265 
266     tempFiles.clear();
267   // cleanUp
268 
269   private void writeObject(java.io.ObjectOutputStream out)
270   throws IOException{
271     out.defaultWriteObject();
272     //now we need to save the class for the action
273     try{
274     Class<?> class1 = classloader.loadClass(actionClassQualifiedName);
275     //System.out.println(class1.getName());
276     out.writeObject(class1);
277     }catch(ClassNotFoundException cnfe){
278       throw new GateRuntimeException(cnfe);
279     }
280   }
281   
282   private void readObject(java.io.ObjectInputStream in)
283   throws IOException, ClassNotFoundException{
284     in.defaultReadObject();
285     //now read the class
286     String className = getActionClassName();
287     if (classloader == null)
288       classloader = Gate.getClassLoader().getDisposableClassLoader(in.toString(),true);
289   
290     try{
291       Map<String, String> actionClasses = new HashMap<String, String>();
292       actionClasses.put(className, getActionClassString());
293       
294       gate.util.Javac.loadClasses(actionClasses, classloader);
295     }catch(Exception e1){
296       throw new GateRuntimeException (e1);
297     }
298   
299   }
300   
301   /** Makes changes to the document, using LHS bindings. */
302   public void transduce(Document doc, java.util.Map<String, AnnotationSet> bindings,
303                         AnnotationSet inputAS, final AnnotationSet outputAS,
304                         Ontology ontology,
305                         final ActionContext actionContext)
306                         throws JapeException {
307     if(theActionObject == null) {
308       instantiateActionClass();
309     }
310       
311     // run the action class
312     try {
313       ((RhsActiontheActionObject).setActionContext(actionContext);
314       
315       if (actionContext.isDebuggingEnabled()) {
316         AnnotationSet outputASproxy =
317                 (AnnotationSet)Proxy.newProxyInstance(classloader, new Class<?>[] {AnnotationSet.class},
318                         new InvocationHandler() {
319                           public Object invoke(Object proxy, Method method,
320                                   Object[] argsthrows Throwable {
321                             
322                             if (method.getName().equals("add")) {
323                               int index = args.length - 1;
324                               Class<?> lastArgType = method.getParameterTypes()[index];
325                               if (lastArgType.equals(FeatureMap.class)) {
326                                 FeatureMap features = (FeatureMap)args[index];
327                                 if (features == null) {
328                                   features = Factory.newFeatureMap();
329                                   args[index= features;
330                                 }
331                                 
332                                 features.put("addedByPR", actionContext.getPRName());
333                                 features.put("addedByPhase", getPhaseName());
334                                 features.put("addedByRule",getRuleName());
335                               }
336                             }
337                             
338                             return method.invoke(outputAS, args);
339                           }
340                         });
341         ((RhsAction)theActionObject).doit(doc, bindings, inputAS, outputASproxy,
342                 ontology);
343       else {
344         ((RhsAction)theActionObject).doit(doc, bindings, inputAS, outputAS, ontology);
345       }
346     catch (NonFatalJapeException e) {
347       // if the action class throws a non-fatal exception then respond by
348       // dumping a whole bunch of useful debug information but then allow
349       // processing to continue on as if nothing happened.
350       Throwable t = e.getCause();
351       Err.println("A non-fatal JAPE exception occurred while processing document '"+doc.getName()+"'.");
352       Err.println("The issue occurred during execution of rule '"+getRuleName()+"' in phase '"+getPhaseName()+"':");
353       if (t != null) {
354         sourceInfo.enhanceTheThrowable(t);
355         t.printStackTrace(Err.getPrintWriter());
356       else {
357         Err.println("Line number and exception details are not available!");
358       }
359     catch (Throwable e) {
360       // if the action class throws an exception, re-throw it with a
361       // full description of the problem, inc. stack trace and the RHS
362       // action class code
363       if (sourceInfo != nullsourceInfo.enhanceTheThrowable(e);
364       if(instanceof Error) {
365         throw (Error)e;
366       }
367       if(instanceof JapeException) {
368         throw (JapeException)e;
369       }      
370       if(instanceof RuntimeException) {
371         throw (RuntimeException)e;
372       }
373       
374       // shouldn't happen...
375         throw new JapeException(
376           "Couldn't run RHS action", e);
377     }
378   // transduce
379 
380   /** Create a string representation of the object. */
381   @Override
382   public String toString() { return toString("")}
383 
384   /** Create a string representation of the object. */
385   public String toString(String pad) {
386     String nl = Strings.getNl();
387     StringBuffer buf = new StringBuffer(
388       pad + "RHS: actionClassName(" + actionClassName + "); "
389     );
390     //buf.append("actionClassString(" + nl + actionClassString + nl);
391     buf.append(
392       "actionClassClassFileName(" + nl + actionClassClassFileName + nl
393     );
394     buf.append("actionClassJavaFileName(" + nl + actionClassJavaFileName + nl);
395     buf.append(
396       "actionClassQualifiedName(" + nl + actionClassQualifiedName + nl
397     );
398 
399     buf.append("blockNames(" + blockNames.toString() "); ");
400 
401     buf.append(nl + pad + ") RHS." + nl);
402 
403     return buf.toString();
404   // toString
405 
406   /** Create a string representation of the object. */
407   public String shortDesc() {
408     String res = "" + actionClassName;
409     return res;
410   }
411   public void setPhaseName(String phaseName) {
412     this.phaseName = phaseName;
413   }
414   public String getPhaseName() {
415     return phaseName;
416   }
417   public void setRuleName(String ruleName) {
418     this.ruleName = ruleName;
419   }
420   public String getRuleName() {
421     return ruleName;
422   // toString
423   
424 // class RightHandSide
425 
426 
427 // $Log$
428 // Revision 1.31  2005/10/10 14:59:15  nirajaswani
429 // bug fixed - reenabled JAPE serialization
430 //
431 // Revision 1.30  2005/10/10 10:29:38  valyt
432 // Serialisatoin to savwe the RHS action class object as well.
433 //
434 // Revision 1.29  2005/09/30 16:01:04  valyt
435 // BUGFIX:
436 // RHS Java blocks now have braces around them (to reduce visibility of local variables)
437 //
438 // Revision 1.28  2005/01/11 13:51:36  ian
439 // Updating copyrights to 1998-2005 in preparation for v3.0
440 //
441 // Revision 1.27  2004/07/21 17:10:08  akshay
442 // Changed copyright from 1998-2001 to 1998-2004
443 //
444 // Revision 1.26  2004/03/25 13:01:14  valyt
445 // Imports optimisation throughout the Java sources
446 // (to get rid of annoying warnings in Eclipse)
447 //
448 // Revision 1.25  2002/05/14 09:43:17  valyt
449 //
450 // Ontology Aware JAPE transducers
451 //
452 // Revision 1.24  2002/02/27 15:11:16  valyt
453 //
454 // bug 00011:
455 // Jape access to InputAS
456 //
457 // Revision 1.23  2002/02/26 13:27:12  valyt
458 //
459 // Error messages from the compiler
460 //
461 // Revision 1.22  2002/02/26 10:30:07  valyt
462 //
463 // new compile solution
464 //
465 // Revision 1.21  2002/02/12 11:39:03  valyt
466 //
467 // removed sate and status members for Jape generated classes
468 //
469 // Revision 1.20  2002/02/04 13:59:04  hamish
470 // added status and state members to RhsAction
471 //
472 // Revision 1.19  2001/11/16 13:03:35  hamish
473 // moved line numbers method to Strings
474 //
475 // Revision 1.18  2001/11/16 10:29:45  hamish
476 // JAPE RHS compiler errors now include the RHS code; test added
477 //
478 // Revision 1.17  2001/11/15 14:05:09  hamish
479 // better error messages from JAPE RHS problems
480 //
481 // Revision 1.16  2001/11/01 15:49:09  valyt
482 //
483 // DEBUG mode for Japes
484 //
485 // Revision 1.15  2001/09/13 12:09:50  kalina
486 // Removed completely the use of jgl.objectspace.Array and such.
487 // Instead all sources now use the new Collections, typically ArrayList.
488 // I ran the tests and I ran some documents and compared with keys.
489 // JAPE seems to work well (that's where it all was). If there are problems
490 // maybe look at those new structures first.
491 //
492 // Revision 1.14  2000/11/08 16:35:03  hamish
493 // formatting
494 //
495 // Revision 1.13  2000/10/26 10:45:30  oana
496 // Modified in the code style
497 //
498 // Revision 1.12  2000/10/16 16:44:34  oana
499 // Changed the comment of DEBUG variable
500 //
501 // Revision 1.11  2000/10/10 15:36:36  oana
502 // Changed System.out in Out and System.err in Err;
503 // Added the DEBUG variable seted on false;
504 // Added in the header the licence;
505 //
506 // Revision 1.10  2000/07/04 14:37:39  valyt
507 // Added some support for Jape-ing in a different annotations et than the default one;
508 // Changed the L&F for the JapeGUI to the System default
509 //
510 // Revision 1.9  2000/06/12 13:33:27  hamish
511 // removed japeactionclasse create code (static init block
512 //
513 // Revision 1.8  2000/05/16 10:38:25  hamish
514 // removed printout
515 //
516 // Revision 1.7  2000/05/16 10:30:33  hamish
517 // uses new gate.util.Jdk compiler
518 //
519 // Revision 1.6  2000/05/05 12:51:12  valyt
520 // Got rid of deprecation warnings
521 //
522 // Revision 1.5  2000/05/05 10:14:09  hamish
523 // added more to toString
524 //
525 // Revision 1.4  2000/05/02 16:54:47  hamish
526 // porting to new annotation API
527 //
528 // Revision 1.3  2000/04/20 13:26:42  valyt
529 // Added the graph_drawing library.
530 // Creating of the NFSM and DFSM now works.
531 //
532 // Revision 1.2  2000/02/24 17:28:48  hamish
533 // more porting to new API
534 //
535 // Revision 1.1  2000/02/23 13:46:11  hamish
536 // added
537 //
538 // Revision 1.1.1.1  1999/02/03 16:23:02  hamish
539 // added gate2
540 //
541 // Revision 1.21  1998/11/13 17:25:10  hamish
542 // stop it using sun.tools... when in 1.2
543 //
544 // Revision 1.20  1998/10/30 15:31:07  kalina
545 // Made small changes to make compile under 1.2 and 1.1.x
546 //
547 // Revision 1.19  1998/10/29 12:17:12  hamish
548 // use reflection when using sun compiler classes, so can compile without them
549 //
550 // Revision 1.18  1998/10/01 16:06:36  hamish
551 // new appelt transduction style, replacing buggy version
552 //
553 // Revision 1.17  1998/09/18 16:54:17  hamish
554 // save/restore works except for attribute seq
555 //
556 // Revision 1.16  1998/09/18 13:35:44  hamish
557 // refactored to split up createActionClass
558 //
559 // Revision 1.15  1998/09/18 12:15:40  hamish
560 // bugs fixed: anon block null ptr; no error for some non-existant labelled blocks
561 //
562 // Revision 1.14  1998/08/19 20:21:41  hamish
563 // new RHS assignment expression stuff added
564 //
565 // Revision 1.13  1998/08/17 10:43:29  hamish
566 // action classes have unique names so can be reloaded
567 //
568 // Revision 1.12  1998/08/12 15:39:42  hamish
569 // added padding toString methods
570 //
571 // Revision 1.11  1998/08/10 14:16:38  hamish
572 // fixed consumeblock bug and added batch.java
573 //
574 // Revision 1.10  1998/08/07 12:01:46  hamish
575 // parser works; adding link to backend
576 //
577 // Revision 1.9  1998/08/05 21:58:07  hamish
578 // backend works on simple test
579 //
580 // Revision 1.8  1998/08/04 12:42:56  hamish
581 // fixed annots null check bug
582 //
583 // Revision 1.7  1998/08/03 21:44:57  hamish
584 // moved parser classes to gate.jape.parser
585 //
586 // Revision 1.6  1998/08/03 19:51:26  hamish
587 // rollback added
588 //
589 // Revision 1.5  1998/07/31 16:50:18  hamish
590 // RHS compilation works; it runs - and falls over...
591 //
592 // Revision 1.4  1998/07/31 13:12:25  hamish
593 // done RHS stuff, not tested
594 //
595 // Revision 1.3  1998/07/30 11:05:24  hamish
596 // more jape
597 //
598 // Revision 1.2  1998/07/29 11:07:10  hamish
599 // first compiling version
600 //
601 // Revision 1.1.1.1  1998/07/28 16:37:46  hamish
602 // gate2 lives