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