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.25 2002/05/14 09:43:17 valyt 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.creole.ontology.Ontology;
28  import gate.*;
29  
30  
31  /**
32    * The RHS of a CPSL rule. The action part. Contains an inner class
33    * created from the code in the grammar RHS.
34    */
35  public class RightHandSide implements JapeConstants, java.io.Serializable
36  {
37    /** Debug flag */
38    private static final boolean DEBUG = false;
39  
40    /** An instance of theActionClass. */
41    transient private Object theActionObject;
42  
43    /** The string we use to create the action class. */
44    private StringBuffer actionClassString;
45  
46    /** The bytes of the compiled action class. */
47    private byte[] actionClassBytes;
48  
49    /** The name of the action class. */
50    private String actionClassName;
51  
52    /** Package name for action classes. It's called a "dir name" because
53      * we used to dump the action classes to disk and compile them there.
54      */
55    static private String actionsDirName = "japeactionclasses";
56  
57    /** The qualified name of the action class. */
58    private String actionClassQualifiedName;
59  
60    /** Name of the .java file for the action class. */
61    private String actionClassJavaFileName;
62  
63    /** Name of the .class file for the action class. */
64    private String actionClassClassFileName;
65  
66    /** Cardinality of the action class set. Used for ensuring class name
67      * uniqueness.
68      */
69    private static int actionClassNumber = 0;
70  
71    /** Allow setting of the initial action class number. Used for ensuring
72      * class name uniqueness when running more than one transducer. The
73      * long-term solution is to have separate class loaders for each
74      * transducer.
75      */
76    public static void setActionClassNumber(int n) { actionClassNumber = n; }
77  
78    /** The set of block names.
79      * Used to ensure we only get their annotations once in the action class.
80      */
81    private HashSet blockNames;
82  
83    /** Returns the string for the java code */
84    public String getActionClassString() { return actionClassString.toString(); }
85  
86    public String getActionClassName() { return actionClassQualifiedName; }
87  
88    /** The LHS of our rule, where we get bindings from. */
89    private LeftHandSide lhs;
90  
91    /** A list of the files and directories we create. */
92    static private ArrayList tempFiles = new ArrayList();
93  
94    /** Local fashion for newlines. */
95    private final String nl = Strings.getNl();
96  
97    /** Debug flag. */
98    static final boolean debug = false;
99    private String phaseName;
100   private String ruleName;
101 
102   /** Construction from the transducer name, rule name and the LHS. */
103   public RightHandSide(
104     String transducerName, String ruleName, LeftHandSide lhs
105   ) {
106     // debug = true;
107     this.lhs = lhs;
108     this.phaseName = transducerName;
109     this.ruleName = ruleName;
110     actionClassName = new String(
111       transducerName + ruleName + "ActionClass" + actionClassNumber++
112     );
113     blockNames = new HashSet();
114 
115     // initialise the class action string
116     actionClassString = new StringBuffer(
117       "// " + actionClassName + nl +
118       "package " + actionsDirName + "; " + nl +
119       "import java.io.*;" + nl +
120       "import java.util.*;" + nl +
121       "import gate.*;" + nl +
122       "import gate.jape.*;" + nl +
123       "import gate.creole.ontology.Ontology;" + nl +
124       "import gate.annotation.*;" + nl +
125       "import gate.util.*;" + nl + nl +
126       "public class " + actionClassName + nl +
127       "implements java.io.Serializable, RhsAction { " + nl +
128       "  public void doit(Document doc, java.util.Map bindings, " + nl +
129       "                   AnnotationSet annotations, " + nl +
130       "                   AnnotationSet inputAS, AnnotationSet outputAS, " + nl +
131       "                   Ontology ontology) {" + nl
132     );
133 
134     // initialise various names
135     actionClassJavaFileName =
136       actionsDirName +  File.separator +
137       actionClassName.replace('.', File.separatorChar) + ".java";
138     actionClassQualifiedName =
139       actionsDirName.
140       replace(File.separatorChar, '.').replace('/', '.').replace('\\', '.') +
141       "." + actionClassName;
142     actionClassClassFileName =
143       actionClassQualifiedName.replace('.', File.separatorChar) + ".class";
144   } // Construction from lhs
145 
146   /** Add an anonymous block to the action class */
147   public void addBlock(String anonymousBlock) {
148     actionClassString.append(anonymousBlock);
149     actionClassString.append(nl);
150   } // addBlock(anon)
151 
152   /** Add a named block to the action class */
153   public void addBlock(String name, String namedBlock) {
154     // is it really a named block?
155     // (dealing with null name cuts code in the parser...)
156     if(name == null) {
157       addBlock(namedBlock);
158       return;
159     }
160 
161     if(blockNames.add(name)) // it wasn't already a member
162       actionClassString.append(
163         "    AnnotationSet " + name + "Annots = (AnnotationSet)bindings.get(\""
164         + name + "\"); " + nl
165       );
166 
167     actionClassString.append(
168       "    if(" + name + "Annots != null && " + name +
169       "Annots.size() != 0) { " + nl + "      " + namedBlock +
170       nl + "    }" + nl
171     );
172   } // addBlock(name, block)
173 
174   /** Create the action class and an instance of it. */
175   public void createActionClass() throws JapeException {
176     // terminate the class string
177     actionClassString.append("  }" + nl + "}" + nl);
178 //    try {
179 //      Javac.loadClass(actionClassString.toString(),
180 //                           actionClassJavaFileName);
181 //    } catch(GateException e) {
182 //      String nl = Strings.getNl();
183 //      String actionWithNumbers =
184 //        Strings.addLineNumbers(actionClassString.toString());
185 //      throw new JapeException(
186 //        "Couldn't create action class: " + nl + e + nl +
187 //        "offending code was: " + nl + actionWithNumbers + nl
188 //      );
189 //    }
190 //    instantiateActionClass();
191   } // createActionClass
192 
193   /** Create an instance of the action class. */
194   public void instantiateActionClass() throws JapeException {
195 
196     try {
197       theActionObject = Gate.getClassLoader().
198                         loadClass(actionClassQualifiedName).
199                         newInstance();
200     } catch(Exception e) {
201       throw new JapeException(
202         "couldn't create instance of action class " + actionClassName + ": "
203         + e.getMessage()
204       );
205     }
206   } // instantiateActionClass
207 
208   /** Remove class files created for actions. */
209   public static void cleanUp() {
210     if(tempFiles.size() == 0) return;
211 
212     // traverse the list in reverse order, coz any directories we
213     // created were done first
214     for(ListIterator i = tempFiles.listIterator(tempFiles.size()-1);
215         i.hasPrevious();
216        ) {
217       File tempFile = (File) i.previous();
218       tempFile.delete();
219     } // for each tempFile
220 
221     tempFiles.clear();
222   } // cleanUp
223 
224 
225   /** Makes changes to the document, using LHS bindings. */
226   public void transduce(Document doc, java.util.Map bindings,
227                         AnnotationSet inputAS, AnnotationSet outputAS,
228                         Ontology ontology)
229                         throws JapeException {
230     if(theActionObject == null) {
231       instantiateActionClass();
232     }
233 
234     // run the action class
235     try {
236       ((RhsAction) theActionObject).doit(doc, bindings, outputAS,
237                                          inputAS, outputAS, ontology);
238 
239     // if the action class throws an exception, re-throw it with a
240     // full description of the problem, inc. stack trace and the RHS
241     // action class code
242     } catch (Exception e) {
243       StringWriter stackTraceWriter = new StringWriter();
244       e.printStackTrace(new PrintWriter(stackTraceWriter));
245       throw new JapeException(
246         "Couldn't run RHS action: " + Strings.getNl() +
247         stackTraceWriter.getBuffer().toString() +
248         Strings.addLineNumbers(actionClassString.toString())
249       );
250     }
251   } // transduce
252 
253   /** Create a string representation of the object. */
254   public String toString() { return toString(""); }
255 
256   /** Create a string representation of the object. */
257   public String toString(String pad) {
258     String nl = Strings.getNl();
259     StringBuffer buf = new StringBuffer(
260       pad + "RHS: actionClassName(" + actionClassName + "); "
261     );
262     //buf.append("actionClassString(" + nl + actionClassString + nl);
263     buf.append(
264       "actionClassClassFileName(" + nl + actionClassClassFileName + nl
265     );
266     buf.append("actionClassJavaFileName(" + nl + actionClassJavaFileName + nl);
267     buf.append(
268       "actionClassQualifiedName(" + nl + actionClassQualifiedName + nl
269     );
270 
271     buf.append("blockNames(" + blockNames.toString() + "); ");
272 
273     buf.append(nl + pad + ") RHS." + nl);
274 
275     return buf.toString();
276   } // toString
277 
278   /** Create a string representation of the object. */
279   public String shortDesc() {
280     String res = "" + actionClassName;
281     return res;
282   }
283   public void setPhaseName(String phaseName) {
284     this.phaseName = phaseName;
285   }
286   public String getPhaseName() {
287     return phaseName;
288   }
289   public void setRuleName(String ruleName) {
290     this.ruleName = ruleName;
291   }
292   public String getRuleName() {
293     return ruleName;
294   } // toString
295 
296 } // class RightHandSide
297 
298 
299 // $Log: RightHandSide.java,v $
300 // Revision 1.25  2002/05/14 09:43:17  valyt
301 //
302 // Ontology Aware JAPE transducers
303 //
304 // Revision 1.24  2002/02/27 15:11:16  valyt
305 //
306 // bug 00011:
307 // Jape access to InputAS
308 //
309 // Revision 1.23  2002/02/26 13:27:12  valyt
310 //
311 // Error messages from the compiler
312 //
313 // Revision 1.22  2002/02/26 10:30:07  valyt
314 //
315 // new compile solution
316 //
317 // Revision 1.21  2002/02/12 11:39:03  valyt
318 //
319 // removed sate and status members for Jape generated classes
320 //
321 // Revision 1.20  2002/02/04 13:59:04  hamish
322 // added status and state members to RhsAction
323 //
324 // Revision 1.19  2001/11/16 13:03:35  hamish
325 // moved line numbers method to Strings
326 //
327 // Revision 1.18  2001/11/16 10:29:45  hamish
328 // JAPE RHS compiler errors now include the RHS code; test added
329 //
330 // Revision 1.17  2001/11/15 14:05:09  hamish
331 // better error messages from JAPE RHS problems
332 //
333 // Revision 1.16  2001/11/01 15:49:09  valyt
334 //
335 // DEBUG mode for Japes
336 //
337 // Revision 1.15  2001/09/13 12:09:50  kalina
338 // Removed completely the use of jgl.objectspace.Array and such.
339 // Instead all sources now use the new Collections, typically ArrayList.
340 // I ran the tests and I ran some documents and compared with keys.
341 // JAPE seems to work well (that's where it all was). If there are problems
342 // maybe look at those new structures first.
343 //
344 // Revision 1.14  2000/11/08 16:35:03  hamish
345 // formatting
346 //
347 // Revision 1.13  2000/10/26 10:45:30  oana
348 // Modified in the code style
349 //
350 // Revision 1.12  2000/10/16 16:44:34  oana
351 // Changed the comment of DEBUG variable
352 //
353 // Revision 1.11  2000/10/10 15:36:36  oana
354 // Changed System.out in Out and System.err in Err;
355 // Added the DEBUG variable seted on false;
356 // Added in the header the licence;
357 //
358 // Revision 1.10  2000/07/04 14:37:39  valyt
359 // Added some support for Jape-ing in a different annotations et than the default one;
360 // Changed the L&F for the JapeGUI to the System default
361 //
362 // Revision 1.9  2000/06/12 13:33:27  hamish
363 // removed japeactionclasse create code (static init block
364 //
365 // Revision 1.8  2000/05/16 10:38:25  hamish
366 // removed printout
367 //
368 // Revision 1.7  2000/05/16 10:30:33  hamish
369 // uses new gate.util.Jdk compiler
370 //
371 // Revision 1.6  2000/05/05 12:51:12  valyt
372 // Got rid of deprecation warnings
373 //
374 // Revision 1.5  2000/05/05 10:14:09  hamish
375 // added more to toString
376 //
377 // Revision 1.4  2000/05/02 16:54:47  hamish
378 // porting to new annotation API
379 //
380 // Revision 1.3  2000/04/20 13:26:42  valyt
381 // Added the graph_drawing library.
382 // Creating of the NFSM and DFSM now works.
383 //
384 // Revision 1.2  2000/02/24 17:28:48  hamish
385 // more porting to new API
386 //
387 // Revision 1.1  2000/02/23 13:46:11  hamish
388 // added
389 //
390 // Revision 1.1.1.1  1999/02/03 16:23:02  hamish
391 // added gate2
392 //
393 // Revision 1.21  1998/11/13 17:25:10  hamish
394 // stop it using sun.tools... when in 1.2
395 //
396 // Revision 1.20  1998/10/30 15:31:07  kalina
397 // Made small changes to make compile under 1.2 and 1.1.x
398 //
399 // Revision 1.19  1998/10/29 12:17:12  hamish
400 // use reflection when using sun compiler classes, so can compile without them
401 //
402 // Revision 1.18  1998/10/01 16:06:36  hamish
403 // new appelt transduction style, replacing buggy version
404 //
405 // Revision 1.17  1998/09/18 16:54:17  hamish
406 // save/restore works except for attribute seq
407 //
408 // Revision 1.16  1998/09/18 13:35:44  hamish
409 // refactored to split up createActionClass
410 //
411 // Revision 1.15  1998/09/18 12:15:40  hamish
412 // bugs fixed: anon block null ptr; no error for some non-existant labelled blocks
413 //
414 // Revision 1.14  1998/08/19 20:21:41  hamish
415 // new RHS assignment expression stuff added
416 //
417 // Revision 1.13  1998/08/17 10:43:29  hamish
418 // action classes have unique names so can be reloaded
419 //
420 // Revision 1.12  1998/08/12 15:39:42  hamish
421 // added padding toString methods
422 //
423 // Revision 1.11  1998/08/10 14:16:38  hamish
424 // fixed consumeblock bug and added batch.java
425 //
426 // Revision 1.10  1998/08/07 12:01:46  hamish
427 // parser works; adding link to backend
428 //
429 // Revision 1.9  1998/08/05 21:58:07  hamish
430 // backend works on simple test
431 //
432 // Revision 1.8  1998/08/04 12:42:56  hamish
433 // fixed annots null check bug
434 //
435 // Revision 1.7  1998/08/03 21:44:57  hamish
436 // moved parser classes to gate.jape.parser
437 //
438 // Revision 1.6  1998/08/03 19:51:26  hamish
439 // rollback added
440 //
441 // Revision 1.5  1998/07/31 16:50:18  hamish
442 // RHS compilation works; it runs - and falls over...
443 //
444 // Revision 1.4  1998/07/31 13:12:25  hamish
445 // done RHS stuff, not tested
446 //
447 // Revision 1.3  1998/07/30 11:05:24  hamish
448 // more jape
449 //
450 // Revision 1.2  1998/07/29 11:07:10  hamish
451 // first compiling version
452 //
453 // Revision 1.1.1.1  1998/07/28 16:37:46  hamish
454 // gate2 lives
455