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() == 0) return;
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 = (File) i.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() >= 0 && 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 ((RhsAction) theActionObject).setActionContext(actionContext);
350 ((RhsAction) theActionObject).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 != null) sourceInfo.enhanceTheThrowable(e);
370 if(e instanceof Error) {
371 throw (Error)e;
372 }
373 if(e instanceof JapeException) {
374 throw (JapeException)e;
375 }
376 if(e 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
|