|
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 |
|