All Downloads are FREE. Search and download functionalities are using the official Maven repository.

astra.ast.reflection.ReflectionHelper Maven / Gradle / Ivy

There is a newer version: 1.4.3
Show newest version
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();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy