/*
* JapePlusExtended.java
*
* $Id: JapePlusExtendedTempl.java 48 2012-06-12 23:24:15Z johann.petrak@gmail.com $
*/
package at.ofai.gate.japeutils;
import gate.creole.ResourceInstantiationException;
import gate.*;
import gate.creole.ExecutionException;
import gate.creole.metadata.*;
import gate.jape.DefaultActionContext;
import gate.jape.constraint.ConstraintPredicate;
import gate.util.Files;
import gate.util.GateRuntimeException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.stringtemplate.v4.ST;
/**
* A version of the GATE JAPE Plus Transducer that already has the necessary
* constraint predicate predefined to allow backwards value references using
* the language construct
* <pre>{Token valueref {Token.string != "ref1"}} {Token} {Token valueref {Token.string == "ref1"}}</pre>
*
* @author Johann Petrak
*/
@CreoleResource(
name = "Jape Plus Extended Template",
helpURL="http://code.google.com/p/gateplugin-japeutils/wiki/JapePlusExtendedTempl",
icon = "jape",
comment = "Extended JAPE Plus Transducer for JAPE templates with more operators preconfigured and JapeUtils library available for import")
public class JapePlusExtendedTempl
extends gate.jape.plus.Transducer
implements ProcessingResource {
private static final long serialVersionUID = 7156914566650678961L;
@RunTime
@Optional
@CreoleParameter(comment = "Parameter for use in the JAPE Java code")
public void setParameters(FeatureMap parms) {
parameters = parms;
}
public FeatureMap getParameters() {
return parameters;
}
protected FeatureMap parameters = null;
@Optional
@CreoleParameter(
comment = "Template variables if grammar is a template"
)
public void setVariables(FeatureMap vars) {
variables = vars;
}
public FeatureMap getVariables() {
return variables;
}
protected FeatureMap variables = null;
@CreoleParameter(
comment = "The URL of the grammar template")
public void setGrammarTemplateURL(URL url) {
grammarTemplateURL = url;
}
public URL getGrammarTemplateURL() {
return grammarTemplateURL;
}
private URL grammarTemplateURL;
@Optional
@CreoleParameter(
comment = "The URL of the directory where to generate the grammar file (default: system temp dir)")
public void setGeneratedJapeDirUrl(URL url) {
if(url != null && !url.getProtocol().startsWith("file:")) {
throw new GateRuntimeException("generatedJapeDirUrl parameter must be a file URL not "+url.getProtocol());
}
generatedJapeDirUrl = url;
}
public URL getGeneratedJapeDirUrl() {
return generatedJapeDirUrl;
}
private URL generatedJapeDirUrl = null;
@HiddenCreoleParameter
@Override
public void setGrammarURL(URL url) {
// hide the parameter!
}
protected File generatedGrammarFile;
protected java.net.URL generatedFileURL = null;
@Override
public Resource init() throws ResourceInstantiationException {
//super.init();
initOP("at.ofai.gate.japeutils.ops.ValueRef");
initOP("at.ofai.gate.japeutils.ops.StartsAt");
initOP("at.ofai.gate.japeutils.ops.NotStartsAt");
initOP("at.ofai.gate.japeutils.ops.EndsAt");
initOP("at.ofai.gate.japeutils.ops.NotEndsAt");
initOP("at.ofai.gate.japeutils.ops.Coextensive");
initOP("at.ofai.gate.japeutils.ops.NotCoextensive");
initOP("at.ofai.gate.japeutils.ops.Overlaps");
initOP("at.ofai.gate.japeutils.ops.NotOverlaps");
// for now we just re-generate the grammar file on every init or re-init
// We "generate" the template even if no variables are set: this will
// allow to start with a template that does not have any vars.
FeatureMap vs = null;
if(variables != null && variables.keySet().size() > 0) {
vs = variables;
} else {
vs = Factory.newFeatureMap();
}
// If we do not have the runtime parameter "parameters" set, initialize
// it from the variables
// TODO: is this really a good idea to do?
if(parameters == null) {
parameters = Factory.newFeatureMap();
}
parameters.putAll(vs);
grammarURL = regenerateInputFile(grammarTemplateURL, parameters);
super.setGrammarURL(grammarURL);
this.setParameterValue("grammarURL", grammarURL);
super.init();
return this;
}
protected URL regenerateInputFile(
java.net.URL originalURL,
FeatureMap vs)
throws ResourceInstantiationException {
if(generatedFileURL == null) {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
if(getGeneratedJapeDirUrl() != null) {
tmpDir = gate.util.Files.fileFromURL(getGeneratedJapeDirUrl());
}
File grammarfile = gate.util.Files.fileFromURL(getGrammarTemplateURL());
String fileName = grammarfile.getName().replaceAll("\\.jape$","") + Gate.genSym() + ".jape";
//System.out.println("Generated file name: "+fileName);
try {
generatedFileURL = new File(tmpDir,fileName).toURI().toURL();
} catch (MalformedURLException ex) {
throw new ResourceInstantiationException("Cannot create generated file URL for "+tmpDir+" / "+fileName);
}
}
String jape = "";
try {
jape = readURL2String(originalURL,encoding);
} catch (IOException ex) {
throw new ResourceInstantiationException("Could not read grammar file "+originalURL,ex);
}
//System.out.println("Got jape file: \n"+jape);
ST template = new ST(jape,'$','$');
//System.out.println("Created template");
//System.out.println("regenerating: variables="+variables);
//System.out.println("regenerating: parameters="+parameters);
//System.out.println("regenerating: vs="+vs);
for(Object key : vs.keySet()) {
if(key instanceof String) {
//System.out.println("Setting key/val: "+key+"/"+vs.get(key));
template.add((String)key, vs.get(key));
} else {
System.err.println("Ignored parameter for template (not a String): "+
key+"="+vs.get(key).toString());
}
}
String result = template.render();
//System.out.println("Rendered template: \n"+result);
File generatedFile = Files.fileFromURL(generatedFileURL);
System.out.println("Generated grammar file: "+generatedFile);
try {
FileUtils.writeStringToFile(generatedFile, result, encoding);
} catch (IOException ex) {
throw new ResourceInstantiationException("Could not write to file "+generatedFile,ex);
}
return generatedFileURL;
}
protected String readURL2String(java.net.URL url, String encoding)
throws IOException {
InputStream is = url.openStream();
String result = IOUtils.toString(is, encoding);
is.close();
return result;
}
protected void initOP(String opName) throws ResourceInstantiationException {
Class<? extends ConstraintPredicate> clazz = null;
try {
clazz = Class.forName(opName, true, Gate.getClassLoader()).asSubclass(ConstraintPredicate.class);
} catch (ClassNotFoundException e) {
//if couldn't find it that way, try with current thread class loader
try {
clazz = Class.forName(opName, true,
Thread.currentThread().getContextClassLoader()).asSubclass(ConstraintPredicate.class);
} catch (ClassNotFoundException e1) {
throw new ResourceInstantiationException("Cannot load class for operator: " + opName, e1);
}
} catch (ClassCastException cce) {
throw new ResourceInstantiationException("Operator class '" + opName + "' must implement ConstraintPredicate");
}
//instantiate an instance of the class so can get the operator string
try {
ConstraintPredicate predicate = clazz.newInstance();
String opSymbol = predicate.getOperator();
//now store it in ConstraintFactory
Factory.getConstraintFactory().addOperator(opSymbol, clazz);
} catch (Exception e) {
throw new ResourceInstantiationException("Cannot instantiate class for operator: " + opName, e);
}
}
@Override
protected DefaultActionContext initActionContext() {
ExtendedActionContext ctx = new ExtendedActionContext();
return ctx;
}
@Override
public void execute() throws ExecutionException {
((ExtendedActionContext)actionContext).setParameters(parameters);
super.execute();
}
}