/*
* RightHandSide.java - transducer class
*
* Copyright (c) 1998-2001, The University of Sheffield.
*
* This file is part of GATE (see http://gate.ac.uk/), and is free
* software, licenced under the GNU Library General Public License,
* Version 2, June 1991 (in the distribution as file licence.html,
* and also available at http://gate.ac.uk/gate/licence.html).
*
* Hamish Cunningham, 24/07/98
*
* $Id$
*/
package ca.umontreal.iro.rali.gate.jape;
import java.util.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import gate.annotation.*;
import gate.util.*;
import gate.creole.ontology.Ontology;
import gate.*;
/**
* The RHS of a CPSL rule. The action part. Contains an inner class
* created from the code in the grammar RHS.
*/
public class RightHandSide implements JapeConstants, java.io.Serializable
{
/** Debug flag */
private static final boolean DEBUG = false;
/** An instance of theActionClass. */
transient private Object theActionObject;
/** The string we use to create the action class. */
private StringBuffer actionClassString;
/** The bytes of the compiled action class. */
private byte[] actionClassBytes;
/** The name of the action class. */
private String actionClassName;
/** Package name for action classes. It's called a "dir name" because
* we used to dump the action classes to disk and compile them there.
*/
static private String actionsDirName = "japeactionclasses";
/** The qualified name of the action class. */
private String actionClassQualifiedName;
/** Name of the .java file for the action class. */
private String actionClassJavaFileName;
/** Name of the .class file for the action class. */
private String actionClassClassFileName;
/** Cardinality of the action class set. Used for ensuring class name
* uniqueness.
*/
private static int actionClassNumber = 0;
/** Allow setting of the initial action class number. Used for ensuring
* class name uniqueness when running more than one transducer. The
* long-term solution is to have separate class loaders for each
* transducer.
*/
public static void setActionClassNumber(int n) { actionClassNumber = n; }
/** The set of block names.
* Used to ensure we only get their annotations once in the action class.
*/
private HashSet blockNames;
/** Returns the string for the java code */
public String getActionClassString() { return actionClassString.toString(); }
public String getActionClassName() { return actionClassQualifiedName; }
/** The LHS of our rule, where we get bindings from. */
private LeftHandSide lhs;
/** A list of the files and directories we create. */
static private ArrayList tempFiles = new ArrayList();
/** Local fashion for newlines. */
private final String nl = Strings.getNl();
/** Debug flag. */
static final boolean debug = false;
private String phaseName;
private String ruleName;
/** Construction from the transducer name, rule name and the LHS. */
public RightHandSide(
String transducerName, String ruleName, LeftHandSide lhs
) {
// debug = true;
this.lhs = lhs;
this.phaseName = transducerName;
this.ruleName = ruleName;
actionClassName = new String(
transducerName + ruleName + "ActionClass" + actionClassNumber++
);
blockNames = new HashSet();
// initialise the class action string
actionClassString = new StringBuffer(
"// " + actionClassName + nl +
"package " + actionsDirName + "; " + nl +
"import java.io.*;" + nl +
"import java.util.*;" + nl +
"import gate.*;" + nl +
"import ca.umontreal.iro.rali.gate.jape.*;" + nl +
"import gate.creole.ontology.Ontology;" + nl +
"import gate.annotation.*;" + nl +
"import gate.util.*;" + nl + nl +
"public class " + actionClassName + nl +
"implements java.io.Serializable, RhsAction { " + nl +
" public void doit(Document doc, java.util.Map bindings, " + nl +
" AnnotationSet annotations, " + nl +
" AnnotationSet inputAS, AnnotationSet outputAS, " + nl +
" Ontology ontology) {" + nl
);
// initialise various names
actionClassJavaFileName =
actionsDirName + File.separator +
actionClassName.replace('.', File.separatorChar) + ".java";
actionClassQualifiedName =
actionsDirName.
replace(File.separatorChar, '.').replace('/', '.').replace('\\', '.') +
"." + actionClassName;
actionClassClassFileName =
actionClassQualifiedName.replace('.', File.separatorChar) + ".class";
} // Construction from lhs
/** Add an anonymous block to the action class */
public void addBlock(String anonymousBlock) {
actionClassString.append(anonymousBlock);
actionClassString.append(nl);
} // addBlock(anon)
/** Add a named block to the action class */
public void addBlock(String name, String namedBlock) {
// is it really a named block?
// (dealing with null name cuts code in the parser...)
if(name == null) {
addBlock(namedBlock);
return;
}
if(blockNames.add(name)) // it wasn't already a member
actionClassString.append(
" AnnotationSet " + name + "Annots = (AnnotationSet)bindings.get(\""
+ name + "\"); " + nl
);
actionClassString.append(
" if(" + name + "Annots != null && " + name +
"Annots.size() != 0) { " + nl + " " + namedBlock +
nl + " }" + nl
);
} // addBlock(name, block)
/** Create the action class and an instance of it. */
public void createActionClass() throws JapeException {
// terminate the class string
actionClassString.append(" }" + nl + "}" + nl);
// try {
// Javac.loadClass(actionClassString.toString(),
// actionClassJavaFileName);
// } catch(GateException e) {
// String nl = Strings.getNl();
// String actionWithNumbers =
// Strings.addLineNumbers(actionClassString.toString());
// throw new JapeException(
// "Couldn't create action class: " + nl + e + nl +
// "offending code was: " + nl + actionWithNumbers + nl
// );
// }
// instantiateActionClass();
} // createActionClass
/** Create an instance of the action class. */
public void instantiateActionClass() throws JapeException {
try {
theActionObject = Gate.getClassLoader().
loadClass(actionClassQualifiedName).
newInstance();
} catch(Exception e) {
throw new JapeException(
"couldn't create instance of action class " + actionClassName + ": "
+ e.getMessage()
);
}
} // instantiateActionClass
/** Remove class files created for actions. */
public static void cleanUp() {
if(tempFiles.size() == 0) return;
// traverse the list in reverse order, coz any directories we
// created were done first
for(ListIterator i = tempFiles.listIterator(tempFiles.size()-1);
i.hasPrevious();
) {
File tempFile = (File) i.previous();
tempFile.delete();
} // for each tempFile
tempFiles.clear();
} // cleanUp
/** Makes changes to the document, using LHS bindings. */
public void transduce(Document doc, java.util.Map bindings,
AnnotationSet inputAS, AnnotationSet outputAS,
Ontology ontology)
throws JapeException {
if(theActionObject == null) {
instantiateActionClass();
}
// run the action class
try {
((RhsAction) theActionObject).doit(doc, bindings, outputAS,
inputAS, outputAS, ontology);
// if the action class throws an exception, re-throw it with a
// full description of the problem, inc. stack trace and the RHS
// action class code
} catch (Exception e) {
StringWriter stackTraceWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stackTraceWriter));
throw new JapeException(
"Couldn't run RHS action: " + Strings.getNl() +
stackTraceWriter.getBuffer().toString() +
Strings.addLineNumbers(actionClassString.toString())
);
}
} // transduce
/** Create a string representation of the object. */
public String toString() { return toString(""); }
/** Create a string representation of the object. */
public String toString(String pad) {
String nl = Strings.getNl();
StringBuffer buf = new StringBuffer(
pad + "RHS: actionClassName(" + actionClassName + "); "
);
//buf.append("actionClassString(" + nl + actionClassString + nl);
buf.append(
"actionClassClassFileName(" + nl + actionClassClassFileName + nl
);
buf.append("actionClassJavaFileName(" + nl + actionClassJavaFileName + nl);
buf.append(
"actionClassQualifiedName(" + nl + actionClassQualifiedName + nl
);
buf.append("blockNames(" + blockNames.toString() + "); ");
buf.append(nl + pad + ") RHS." + nl);
return buf.toString();
} // toString
/** Create a string representation of the object. */
public String shortDesc() {
String res = "" + actionClassName;
return res;
}
public void setPhaseName(String phaseName) {
this.phaseName = phaseName;
}
public String getPhaseName() {
return phaseName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
public String getRuleName() {
return ruleName;
} // toString
} // class RightHandSide