astra.ast.reflection.ReflectionHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of astra-compiler Show documentation
Show all versions of astra-compiler Show documentation
Core compiler artifact for the ASTRA Language
package astra.ast.reflection;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import astra.ast.core.ASTRAClassElement;
import astra.ast.core.AbstractHelper;
import astra.ast.core.BuildContext;
import astra.ast.core.IJavaHelper;
import astra.ast.core.ITerm;
import astra.ast.core.ImportElement;
import astra.ast.core.ParseException;
import astra.ast.core.Token;
import astra.ast.element.PackageElement;
import astra.ast.formula.MethodSignature;
import astra.ast.formula.MethodType;
import astra.ast.formula.PredicateFormula;
import astra.ast.term.Variable;
import astra.ast.type.ObjectType;
import astra.core.ActionParam;
import astra.core.Module.ACTION;
import astra.core.Module.EVENT;
import astra.core.Module.FORMULA;
public class ReflectionHelper extends AbstractHelper {
// private static Logger log = LoggerFactory.getLogger(ReflectionHelper.class);
private static Map annotations = new HashMap();
static {
annotations.put(ACTION, "astra.core.Module.ACTION");
annotations.put(TERM, "astra.core.Module.TERM");
annotations.put(FORMULA, "astra.core.Module.FORMULA");
annotations.put(SENSOR, "astra.core.Module.SENSOR");
annotations.put(EVENT, "astra.core.Module.EVENT");
}
PackageElement packageElement;
ImportElement[] imports;
BuildContext context = new BuildContext();
private File source;
private File target;
public ReflectionHelper(File source, File target) {
this.source = source;
this.target = target;
}
public BuildContext getBuildContext() {
return context;
}
public String resolveModule(String className) {
Class> clazz = resolveClass(className);
return clazz == null ? null : clazz.getCanonicalName();
}
public Class> resolveClass(String clazz) {
try {
return Class.forName(clazz);
} catch (ClassNotFoundException e) {
try {
// System.out.println("trying: " +
// packageElement.packageName()+"."+clazz);
if (packageElement != null)
return Class.forName(packageElement.packageName() + "." + clazz);
} catch (ClassNotFoundException e0) {
}
// Try astra.lang.*
try {
return Class.forName("astra.lang." + clazz);
} catch (ClassNotFoundException e0) {
}
// Try java.lang.*
try {
return Class.forName("java.lang." + clazz);
} catch (ClassNotFoundException e0) {
}
for (ImportElement imp : imports) {
String im = imp.name();
if (im.endsWith(clazz)) {
try {
return Class.forName(im);
} catch (ClassNotFoundException e1) {
}
}
if (im.endsWith("*")) {
try {
return Class.forName(im.substring(0, im.length() - 1) + clazz);
} catch (ClassNotFoundException e1) {
}
}
}
try {
return Class.forName("astra.lang." + clazz);
} catch (ClassNotFoundException e2) {
}
}
throw new RuntimeException("Could not locate class with name: " + clazz);
}
public void setup(PackageElement packageElement, ImportElement[] imports) {
this.packageElement = packageElement;
this.imports = imports;
}
public String getFullClassName(String className) {
return resolveClass(className).getCanonicalName();
}
public String getQualifiedName(String parent, String packageName, ImportElement[] imports) {
// System.out.println("[ReflectionHelper] parent: " + parent);
// System.out.println("[ReflectionHelper] packageName: " + packageName);
if (parent.contains(".")) return parent;
if (!packageName.isEmpty()) {
String qname = "/" + packageName.replace(".", "/") + "/" + parent + ".astra";
// System.out.println("Possible package: " + qname);
if (classExists(qname)) return packageName + "." + parent;
// System.out.println("FAILED");
} else {
String qname = "/" + parent + ".astra";
// System.out.println("In same package: " + qname);
if (classExists(qname)) return parent;
}
String qname = "/astra/lang/" + parent.replace(".", "/") + ".astra";
if (classExists(qname)) return "astra.lang." + parent;
for (ImportElement imp : imports) {
String im = imp.name();
// System.out.println("Testing: " + im);;
if (im.endsWith("*")) {
im = im.substring(0, im.length() - 1) + parent;
// System.out.println("Refined: " + im);;
}
if (im.endsWith(parent)) {
if (classExists("/" + im.replace(".", "/") + ".astra")) return im;
// System.out.println("Failed");
}
}
return parent;
}
private boolean classExists(String qname) {
// File file = getSourceFileReference(qname);
// System.out.println("file: " +file);
return getSourceFileReference(qname).exists() || getClass().getResourceAsStream(qname) != null;
}
public ASTRAClassElement loadAST(String clazz) throws ParseException {
File file = getSourceFileReference(clazz.replace(".", "/") + ".astra");
return file.exists() ? loadASTFromFile(clazz, file):loadASTFromJar(clazz);
}
private ASTRAClassElement loadASTFromFile(String clazz, File file) throws ParseException {
try {
InputStream in = new FileInputStream(file);
ASTRAClassElement element = new ASTRAClassElement(clazz, in, true);
in.close();
return element;
} catch (FileNotFoundException e) {
return loadASTFromJar(clazz);
} catch (IOException e) {
return loadASTFromJar(clazz);
}
}
private ASTRAClassElement loadASTFromJar(String clazz) throws ParseException {
InputStream in = getClass().getResourceAsStream("/" + clazz.replace(".", "/") + ".astra");
if (in == null) return null;
return new ASTRAClassElement(clazz, in, true);
}
private boolean matchMethodSignature(Method mthd, MethodSignature signature) {
return mthd.getName().equals(signature.name()) &&
signature.termCount() == (mthd.getParameterTypes().length-(signature.symbol() ? 1:0));
}
private Method getMatchingMethod(String moduleClass, MethodSignature signature) {
Class> cls = resolveClass(moduleClass);
while (cls.getSuperclass() != null) {
for (Method mthd : cls.getMethods()) {
if (matchMethodSignature(mthd, signature)) {
if (signature.type() == -1) {
if (validateSignature(signature, mthd, null)) return mthd;
} else {
for (Annotation ann : mthd.getAnnotations()) {
if (ann.annotationType().getCanonicalName().equals(annotations.get(signature.type())) &&
validateSignature(signature, mthd, ann)) {
return mthd;
}
}
}
}
}
cls = cls.getSuperclass();
}
return null;
}
public boolean validate(String moduleClass, MethodSignature signature) {
return getMatchingMethod(moduleClass, signature) != null;
}
public boolean isInline(String moduleClass, MethodSignature signature) {
Method method = getMatchingMethod(moduleClass, signature);
if (method == null) return true;
for (Annotation ann : method.getAnnotations()) {
if (ann.annotationType().getCanonicalName().equals(annotations.get(signature.type()))) {
if (signature.type() == IJavaHelper.ACTION) {
return ((ACTION) ann).inline();
}
}
}
return false;
}
private boolean validateSignature(MethodSignature signature, Method mthd, Annotation ann) {
switch (signature.type()) {
case IJavaHelper.EVENT:
return validateEventSignature(signature, mthd, ann);
case IJavaHelper.FORMULA:
// Check for new Formula model (queryable==true)
if (isNewFormulaModel(signature, mthd, ann)) {
return validateFormulaSignature(signature, mthd, ann);
}
// If not, revert to the old Formula model...
default:
Type[] params = mthd.getGenericParameterTypes();
int i = 0;
boolean match = true;
while (match && i < params.length) {
match = matchType(params[i], signature.type(i));
i++;
}
if (match) {
String retType = mthd.getReturnType().getCanonicalName();
String t = MethodType.resolveType(retType);
if (t == null)
signature.returnType(retType);
else
signature.returnType(t);
return true;
}
return false;
}
}
private boolean validateEventSignature(MethodSignature signature, Method mthd, Annotation ann) {
String[] params = ((EVENT) ann).types();
int i = 0;
boolean match = true;
while (match && i < params.length) {
match = params[i].equals(signature.type(i).type());
i++;
}
if (match) {
signature.signature(((EVENT) ann).signature());
String retType = mthd.getReturnType().getCanonicalName();
String t = MethodType.resolveType(retType);
if (t == null)
signature.returnType(retType);
else
signature.returnType(t);
}
return match;
}
private boolean isNewFormulaModel(MethodSignature signature, Method mthd, Annotation ann) {
return ((FORMULA) ann).types().length == mthd.getParameterTypes().length;
}
private boolean validateFormulaSignature(MethodSignature signature, Method mthd, Annotation ann) {
String[] params = ((FORMULA) ann).types();
int i = 0;
boolean match = true;
while (match && i < params.length) {
match = params[i].equals(signature.type(i).type());
signature.type(i).primitiveType("astra.term.Term");
i++;
}
if (match) {
// signature.signature(((FORMULA) ann).signature());
String retType = mthd.getReturnType().getCanonicalName();
String t = MethodType.resolveType(retType);
if (t == null)
signature.returnType(retType);
else
signature.returnType(t);
}
return match;
}
private boolean matchType(Type cls, MethodType methodType) {
// System.out.println("Matching: " + cls + " and: " + methodType);
if (cls instanceof ParameterizedType) {
if (methodType.variable()) {
ParameterizedType t = (ParameterizedType) cls;
if (t.getRawType().equals(ActionParam.class)) {
boolean result = matchType(t.getActualTypeArguments()[0], methodType);
if (result) {
methodType.actionParam(true);
}
return result;
}
} else if (methodType.isFunct()) {
ParameterizedType t = (ParameterizedType) cls;
if (t.getRawType().equals(ActionParam.class)) {
boolean result = matchType(t.getActualTypeArguments()[0], methodType);
if (result) {
methodType.actionParam(true);
}
return result;
}
}
}
// handle primitives
if (methodType.isPrimitive()) {
// System.out.println(">>>>>>> PRIMITIVE");
return methodType.validatePrimitive(((Class>) cls).getCanonicalName());
}
// validate raw types...
// System.out.println("method.type="+methodType.type());
Class> cl = resolveClass(methodType.type());
// System.out.println("cl: " + cl);
if ((cl != null) && ((Class>) cls).isAssignableFrom(cl)) {
methodType.primitiveType(cl.getCanonicalName());
return true;
}
return false;
}
public List getSensors(String name) {
List list = new LinkedList();
Class> cls = resolveClass(name);
// System.out.println("class: " + name + " / cls=" + cls);
for (Method mthd : cls.getMethods()) {
// System.out.println("\tmethod: " + mthd);
for (Annotation ann : mthd.getAnnotations()) {
// System.out.println("\t\tannotation: " + ann);
if (mthd.getParameterTypes().length == 0 && ann.toString().startsWith("@astra.core.Module$SENSOR")) {
list.add(mthd.getName());
}
}
}
return list;
}
public IJavaHelper spawn() {
return new ReflectionHelper(source, target);
}
public long lastModified(String clazz, String type) {
String filename = clazz.replace(".", "/") + type;
// Try to check if the file is local (source) first
File file = getSourceFileReference(filename);
if (file.exists()) {
// System.out.println("LOCAL FILE: " + filename + " / Last Modified: " + file.lastModified());
return file.lastModified();
}
// Try to check if the file is local (target) second
file = getTargetFileReference(filename);
if (file.exists()) {
// System.out.println("LOCAL FILE: " + filename + " / Last Modified: " + file.lastModified());
return file.lastModified();
}
// Okay, so it isn't local, so search the classpath...
URL url = getClass().getResource("/" + filename);
// url will be null if there is not resource with the given name...
if (url == null) {
// log.warn("NOT IN LOCAL CLASSPATH: " + filename + " / Last Modified: 0");
return 0;
}
// okay, get the resources last modified date...
try {
long lastModified = url.openConnection().getLastModified();
// log.warn("IN LOCAL CLASSPATH: " + filename + " / Last Modified: " + lastModified);
return lastModified;
} catch (IOException e) {
return 0;
}
}
private File getSourceFileReference(String filename) {
return new File(source, filename);
}
private File getTargetFileReference(String filename) {
return new File(target, filename);
}
public boolean hasAutoAction(String className) {
return validate(className, getAutoMethodSignature());
}
public boolean hasTRAutoAction(String className) {
return validate(className, getAutoTRMethodSignature());
}
private MethodSignature getAutoMethodSignature() {
Variable V = new Variable("X", null, null, null);
V.setType(new ObjectType(Token.OBJECT_TYPE, "astra.formula.Predicate"));
Variable V2 = new Variable("Y", null, null, null);
V2.setType(new ObjectType(Token.OBJECT_TYPE, "astra.core.Intention"));
return new MethodSignature(
new PredicateFormula("auto_action", Arrays.asList((ITerm) V2, (ITerm) V), null, null, null), -1);
}
private MethodSignature getAutoTRMethodSignature() {
Variable V = new Variable("X",null,null,null);
V.setType(new ObjectType(Token.OBJECT_TYPE, "astra.formula.Predicate"));
Variable V2 = new Variable("Y",null,null,null);
V2.setType(new ObjectType(Token.OBJECT_TYPE, "astra.tr.TRContext"));
return new MethodSignature(
new PredicateFormula("auto_action",
Arrays.asList((ITerm) V2, (ITerm) V),
null, null, null
),
-1
);
}
public boolean hasAutoFormula(String className) {
Variable V = new Variable("X", null, null, null);
V.setType(new ObjectType(Token.OBJECT_TYPE, "astra.formula.Predicate"));
return validate(className, new MethodSignature(
new PredicateFormula("auto_formula", Arrays.asList((ITerm) V), null, null, null), -1));
}
public boolean getEventSymbols(String className, MethodSignature signature, String symbol) {
Class> cls = resolveClass(className);
while (cls.getSuperclass() != null) {
for (Method mthd : cls.getMethods()) {
if (mthd.getName().equals(signature.name())) {
if (signature.termCount() != (mthd.getParameterTypes().length - (signature.symbol() ? 1 : 0)))
continue;
for (Annotation ann : mthd.getAnnotations()) {
if (ann.annotationType().getCanonicalName().equals(annotations.get(signature.type()))) {
String[] params = ((EVENT) ann).symbols();
for (int i = 0; i < params.length; i++) {
if (params[i].equals(symbol))
return true;
}
}
}
}
}
cls = cls.getSuperclass();
}
return false;
}
public boolean suppressAutoActionNotifications(String className) {
Method mthd = getMatchingMethod(className, getAutoMethodSignature());
for (Annotation ann : mthd.getAnnotations()) {
if (ann.annotationType().getCanonicalName().equals("astra.core.Module.SUPPRESS_NOTIFICATIONS"))
return true;
}
return false;
}
public void createTarget(ASTRAClassElement element, String code) {
try {
File file = getTargetFileReference(element.getFilename());
// Create the folder structure (if it does not already exist).
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
// Create the file
file.createNewFile();
// Write the generated code...
FileWriter out = new FileWriter(file);
out.write(code);
out.close();
// System.out.println("[ASTRACompiler] Generated Target File: " + element.getFilename() + " / " + file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}