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