| RightHandSide |

    1   /*
    2    *  RightHandSide.java - transducer class
    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, 24/07/98
    12   *
    13   *  $Id: RightHandSide.java,v 1.19 2001/11/16 13:03:35 hamish Exp $
    14   */
    15
    16
    17  package gate.jape;
    18
    19  import java.util.*;
    20  import java.io.*;
    21  import java.lang.reflect.Constructor;
    22  import java.lang.reflect.Method;
    23
    24
    25  import gate.annotation.*;
    26  import gate.util.*;
    27  import gate.*;
    28
    29
    30  /**
    31    * The RHS of a CPSL rule. The action part. Contains an inner class
    32    * created from the code in the grammar RHS.
    33    */
    34  public class RightHandSide implements JapeConstants, java.io.Serializable
    35  {
    36    /** Debug flag */
    37    private static final boolean DEBUG = false;
    38
    39    /** The "action class" we create to implement the action. Has a static
    40      * method that performs the action of the RHS.
    41      */
    42    transient private Class theActionClass;
    43
    44    /** An instance of theActionClass. */
    45    transient private Object theActionObject;
    46
    47    /** The string we use to create the action class. */
    48    private StringBuffer actionClassString;
    49
    50    /** The bytes of the compiled action class. */
    51    private byte[] actionClassBytes;
    52
    53    /** The name of the action class. */
    54    private String actionClassName;
    55
    56    /** Package name for action classes. It's called a "dir name" because
    57      * we used to dump the action classes to disk and compile them there.
    58      */
    59    static private String actionsDirName = "japeactionclasses";
    60
    61    /** The qualified name of the action class. */
    62    private String actionClassQualifiedName;
    63
    64    /** Name of the .java file for the action class. */
    65    private String actionClassJavaFileName;
    66
    67    /** Name of the .class file for the action class. */
    68    private String actionClassClassFileName;
    69
    70    /** Cardinality of the action class set. Used for ensuring class name
    71      * uniqueness.
    72      */
    73    private static int actionClassNumber = 0;
    74
    75    /** Allow setting of the initial action class number. Used for ensuring
    76      * class name uniqueness when running more than one transducer. The
    77      * long-term solution is to have separate class loaders for each
    78      * transducer.
    79      */
    80    public static void setActionClassNumber(int n) { actionClassNumber = n; }
    81
    82    /** The set of block names.
    83      * Used to ensure we only get their annotations once in the action class.
    84      */
    85    private HashSet blockNames;
    86
    87    /** For debugging. */
    88    public String getActionClassString() { return actionClassString.toString(); }
    89
    90    /** The LHS of our rule, where we get bindings from. */
    91    private LeftHandSide lhs;
    92
    93    /** A list of the files and directories we create. */
    94    static private ArrayList tempFiles = new ArrayList();
    95
    96    /** Local fashion for newlines. */
    97    private final String nl = Strings.getNl();
    98
    99    /** Debug flag. */
    100   static final boolean debug = false;
    101   private String phaseName;
    102   private String ruleName;
    103
    104   /** Construction from the transducer name, rule name and the LHS. */
    105   public RightHandSide(
    106     String transducerName, String ruleName, LeftHandSide lhs
    107   ) {
    108     // debug = true;
    109     this.lhs = lhs;
    110     this.phaseName = transducerName;
    111     this.ruleName = ruleName;
    112     actionClassName = new String(
    113       transducerName + ruleName + "ActionClass" + actionClassNumber++
    114     );
    115     blockNames = new HashSet();
    116
    117     // initialise the class action string
    118     actionClassString = new StringBuffer(
    119       "// " + actionClassName + nl +
    120       "package " + actionsDirName + "; " + nl +
    121       "import java.io.*;" + nl +
    122       "import java.util.*;" + nl +
    123       "import gate.*;" + nl +
    124       "import gate.jape.*;" + nl +
    125       "import gate.annotation.*;" + nl +
    126       "import gate.util.*;" + nl + nl +
    127       "public class " + actionClassName + nl +
    128       "implements java.io.Serializable, RhsAction { " + nl +
    129       "  public void doit(Document doc, AnnotationSet annotations, " +
    130       " java.util.Map bindings) { " + nl
    131     );
    132
    133     // initialise various names
    134     actionClassJavaFileName =
    135       actionsDirName +  File.separator +
    136       actionClassName.replace('.', File.separatorChar) + ".java";
    137     actionClassQualifiedName =
    138       actionsDirName.
    139       replace(File.separatorChar, '.').replace('/', '.').replace('\\', '.') +
    140       "." + actionClassName;
    141     actionClassClassFileName =
    142       actionClassQualifiedName.replace('.', File.separatorChar) + ".class";
    143   } // Construction from lhs
    144
    145   /** Add an anonymous block to the action class */
    146   public void addBlock(String anonymousBlock) {
    147     actionClassString.append(anonymousBlock);
    148     actionClassString.append(nl);
    149   } // addBlock(anon)
    150
    151   /** Add a named block to the action class */
    152   public void addBlock(String name, String namedBlock) {
    153     // is it really a named block?
    154     // (dealing with null name cuts code in the parser...)
    155     if(name == null) {
    156       addBlock(namedBlock);
    157       return;
    158     }
    159
    160     if(blockNames.add(name)) // it wasn't already a member
    161       actionClassString.append(
    162         "    AnnotationSet " + name + "Annots = (AnnotationSet)bindings.get(\""
    163         + name + "\"); " + nl
    164       );
    165
    166     actionClassString.append(
    167       "    if(" + name + "Annots != null && " + name +
    168       "Annots.size() != 0) { " + nl + "      " + namedBlock +
    169       nl + "    }" + nl
    170     );
    171   } // addBlock(name, block)
    172
    173   /** Create the action class and an instance of it. */
    174   public void createActionClass() throws JapeException {
    175     // terminate the class string
    176     actionClassString.append("  }" + nl + "}" + nl);
    177
    178     // create class:
    179     // contruct a file name from the class name
    180     // write the action class string out into the file
    181     // call javac on the class
    182     // call the gate class loader to load the resultant class
    183     // read in the bytes if the compiled file for later serialisation
    184     // create an instance of the class
    185     /* writeActionClass();
    186     compileActionClass();
    187     loadActionClass();
    188     readActionClass();
    189     instantiateActionClass();
    190     */
    191     //Out.println(actionClassString);
    192     try {
    193       actionClassBytes = new Jdk().compile(
    194         actionClassString.toString(),
    195         actionClassJavaFileName
    196       );
    197     } catch(GateException e) {
    198       String nl = Strings.getNl();
    199       String actionWithNumbers =
    200         Strings.addLineNumbers(actionClassString.toString());
    201       throw new JapeException(
    202         "Couldn't create action class: " + nl + e + nl +
    203         "offending code was: " + nl + actionWithNumbers + nl
    204       );
    205     }
    206     defineActionClass();
    207     instantiateActionClass();
    208
    209     //Debug.pr(this, "RightHandSide: action class loaded ok");
    210   } // createActionClass
    211
    212   /** Write out the action class file. */
    213   public void writeActionClass() throws JapeException {
    214
    215     File actionClassJavaFile = new File(actionClassJavaFileName);
    216     try {
    217       FileWriter writer = new FileWriter(actionClassJavaFile);
    218       writer.write(actionClassString.toString());
    219       writer.close();
    220     } catch(IOException e) {
    221       throw new JapeException(
    222         "problem writing to " + actionClassJavaFileName + ": " + e.getMessage()
    223       );
    224     }
    225   } // writeActionClass
    226
    227   /** Compile the action class. First tries to use the sun.tools.javac
    228     * class directly via reflection. If that fails, tries to exec javac
    229     * as an external process.
    230     */
    231   public void compileActionClass() throws JapeException {
    232     if(debug) Out.println("RightHandSide: trying to compile action");
    233
    234     // see if we can find the sun compiler class
    235     Class sunCompilerClass = null;
    236     try {
    237       sunCompilerClass = Class.forName("sun.tools.javac.Main");
    238     } catch(ClassNotFoundException e) {
    239       sunCompilerClass = null;
    240     }
    241
    242     // if it's 1.2, we can't support the compiler class at present
    243     String jversion = System.getProperty("java.version");
    244     if(jversion == null || jversion.startsWith("1.2"))
    245       sunCompilerClass = null;
    246
    247     // if we have the sun compiler class, try to use it directly
    248     if(sunCompilerClass != null) {
    249       // none-reflection version:
    250       // sun.tools.javac.Main compiler =
    251       //   new sun.tools.javac.Main(System.err, "RhsCompiler");
    252       // String toBeCompiled[] = new String[1];
    253       // toBeCompiled[0] = actionClassJavaFileName;
    254       // boolean compiledOk = compiler.compile(toBeCompiled);
    255
    256       Boolean compiledOk = new Boolean(false);
    257       try {
    258         // get the compiler constructor
    259         Class[] consTypes = new Class[2];
    260         consTypes[0] = OutputStream.class;
    261         consTypes[1] = String.class;
    262         Constructor compilerCons = sunCompilerClass.getConstructor(consTypes);
    263
    264         // get an instance of the compiler
    265         Object[] consArgs = new Object[2];
    266         consArgs[0] = System.err;
    267         consArgs[1] = "RhsCompiler";
    268         Object sunCompiler = compilerCons.newInstance(consArgs);
    269
    270         // get the compile method
    271         Class[] compilerTypes = new Class[1];
    272         compilerTypes[0] = String[].class;
    273         Method compileMethod = sunCompilerClass.getDeclaredMethod(
    274           "compile", compilerTypes
    275         );
    276
    277         // call the compiler
    278         String toBeCompiled[] = new String[1];
    279         toBeCompiled[0] = actionClassJavaFileName;
    280         Object[] compilerArgs = new Object[1];
    281         compilerArgs[0] = toBeCompiled;
    282         compiledOk = (Boolean) compileMethod.invoke(sunCompiler, compilerArgs);
    283
    284       // any exceptions mean the reflection stuff failed, as the compile
    285       // method doesn't throw any. so (apart from RuntimeExceptions) we just
    286       // print a warning and go on to try execing javac
    287       } catch(RuntimeException e) { // rethrow runtime exceptions as they are
    288         throw e;
    289       } catch(Exception e) { // print out other sorts, and try javac exec
    290         compiledOk = new Boolean(false);
    291         Err.println(
    292           "Warning: RHS compiler error: " + e.toString()
    293         );
    294       }
    295       if(debug) Out.println("RightHandSide: action compiled ok");
    296       if(compiledOk.booleanValue())
    297         return;
    298     }
    299
    300     // no sun compiler: try execing javac as an external process
    301     Runtime runtime = Runtime.getRuntime();
    302     try {
    303       String actionCompileCommand = new String(
    304         "javac -classpath " +
    305         System.getProperty("java.class.path") +
    306         " " + actionClassJavaFileName
    307       );
    308       if(debug) Out.println("doing " + actionCompileCommand);
    309
    310       Process actionCompile = runtime.exec(actionCompileCommand);
    311       //InputStream stdout = actionCompile.getInputStream();
    312       //InputStream stderr = actionCompile.getErrorStream();
    313       actionCompile.waitFor();
    314
    315       //Out.flush();
    316       //while(stdout.available() > 0)
    317       //  Out.print((char) stdout.read());
    318       //while(stderr.available() > 0)
    319       //  Out.print((char) stderr.read());
    320       //Out.flush();
    321       if(debug) Out.println("process complete");
    322
    323     } catch(Exception e) {
    324       throw new JapeException(
    325         "couldn't compile " + actionClassJavaFileName + ": " + e.toString()
    326       );
    327     }
    328   } // compileActionClass
    329
    330   /** Read action class bytes, for storing during serialisation. */
    331   public void readActionClass() throws JapeException {
    332     try {
    333       File f = new File(actionClassClassFileName);
    334       FileInputStream fis = new FileInputStream(actionClassClassFileName);
    335       actionClassBytes = new byte[(int) f.length()];
    336       fis.read(actionClassBytes, 0, (int) f.length());
    337       fis.close();
    338     } catch(IOException e) {
    339       throw(new JapeException("couldn't read action class bytes: " + e));
    340     }
    341   } // readActionClass
    342
    343   /** Load the action class. */
    344   public void loadActionClass() throws JapeException {
    345     //Debug.pr(this, "RightHandSide: trying to load the action class");
    346     try {
    347       theActionClass =
    348         Gate.getClassLoader().loadClass(actionClassClassFileName, true);
    349     } catch(Exception e) {
    350       e.printStackTrace();
    351       throw new JapeException(
    352         "couldn't load " + actionClassClassFileName + ": " + e.getMessage()
    353       );
    354     }
    355   } // loadActionClass
    356
    357   /** Define the action class (after deserialisation). */
    358   public void defineActionClass() throws JapeException {
    359     //Debug.pr(this, "RightHandSide: trying to define the action class");
    360     try {
    361       theActionClass = Gate.getClassLoader().defineGateClass(
    362         actionClassQualifiedName, actionClassBytes, 0, actionClassBytes.length
    363       );
    364     } catch(ClassFormatError e) {
    365       e.printStackTrace();
    366       throw new JapeException(
    367         "couldn't define " + actionClassName + ": " + e
    368       );
    369
    370     }
    371     Gate.getClassLoader().resolveGateClass(theActionClass);
    372   } // defineActionClass
    373
    374   /** Create an instance of the action class. */
    375   public void instantiateActionClass() throws JapeException {
    376
    377     try {
    378       theActionObject = theActionClass.newInstance();
    379     } catch(Exception e) {
    380       throw new JapeException(
    381         "couldn't create instance of action class " + actionClassName + ": "
    382         + e.getMessage()
    383       );
    384     }
    385   } // instantiateActionClass
    386
    387   /** Remove class files created for actions. */
    388   public static void cleanUp() {
    389     if(tempFiles.size() == 0) return;
    390
    391     // traverse the list in reverse order, coz any directories we
    392     // created were done first
    393     for(ListIterator i = tempFiles.listIterator(tempFiles.size()-1);
    394         i.hasPrevious();
    395        ) {
    396       File tempFile = (File) i.previous();
    397       tempFile.delete();
    398     } // for each tempFile
    399
    400     tempFiles.clear();
    401   } // cleanUp
    402
    403
    404   /** Makes changes to the document, using LHS bindings. */
    405   public void transduce(Document doc, AnnotationSet annotations,
    406                         java.util.Map bindings) throws JapeException {
    407     if(theActionObject == null) {
    408       defineActionClass();
    409       instantiateActionClass();
    410     }
    411
    412     // run the action class
    413     try {
    414       ((RhsAction) theActionObject).doit(doc, annotations, bindings);
    415
    416     // if the action class throws an exception, re-throw it with a
    417     // full description of the problem, inc. stack trace and the RHS
    418     // action class code
    419     } catch (Exception e) {
    420       StringWriter stackTraceWriter = new StringWriter();
    421       e.printStackTrace(new PrintWriter(stackTraceWriter));
    422       throw new JapeException(
    423         "Couldn't run RHS action: " + Strings.getNl() +
    424         stackTraceWriter.getBuffer().toString() +
    425         Strings.addLineNumbers(actionClassString.toString())
    426       );
    427     }
    428   } // transduce
    429
    430   /** Create a string representation of the object. */
    431   public String toString() { return toString(""); }
    432
    433   /** Create a string representation of the object. */
    434   public String toString(String pad) {
    435     String nl = Strings.getNl();
    436     StringBuffer buf = new StringBuffer(
    437       pad + "RHS: actionClassName(" + actionClassName + "); "
    438     );
    439     //buf.append("actionClassString(" + nl + actionClassString + nl);
    440     buf.append(
    441       "actionClassClassFileName(" + nl + actionClassClassFileName + nl
    442     );
    443     buf.append("actionClassJavaFileName(" + nl + actionClassJavaFileName + nl);
    444     buf.append(
    445       "actionClassQualifiedName(" + nl + actionClassQualifiedName + nl
    446     );
    447
    448     buf.append("blockNames(" + blockNames.toString() + "); ");
    449
    450     buf.append(nl + pad + ") RHS." + nl);
    451
    452     return buf.toString();
    453   } // toString
    454
    455   /** Create a string representation of the object. */
    456   public String shortDesc() {
    457     String res = "" + actionClassName;
    458     return res;
    459   }
    460   public void setPhaseName(String phaseName) {
    461     this.phaseName = phaseName;
    462   }
    463   public String getPhaseName() {
    464     return phaseName;
    465   }
    466   public void setRuleName(String ruleName) {
    467     this.ruleName = ruleName;
    468   }
    469   public String getRuleName() {
    470     return ruleName;
    471   } // toString
    472
    473 } // class RightHandSide
    474
    475
    476 // $Log: RightHandSide.java,v $
    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
    608

| RightHandSide |