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

astra.compiler.ASTRAClassHierarchy Maven / Gradle / Ivy

The newest version!
package astra.compiler;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

import astra.ast.core.IJavaHelper;
import astra.ast.core.ParseException;
import astra.ast.visitor.CodeGeneratorVisitor;
import astra.ast.visitor.ComponentStore;
import astra.ast.visitor.ComponentVisitor;
import astra.ast.visitor.GoalCheckVisitor;
import astra.ast.visitor.TypeCheckVisitor;
import graph.core.DirectedGraph;
import graph.core.Edge;
import graph.core.Vertex;
import graph.impl.DirectedAdjacencyListGraph;

public class ASTRAClassHierarchy {
	private IJavaHelper helper;
	
	Map> classes = new HashMap>();
	DirectedGraph graph = new DirectedAdjacencyListGraph();
	
	public ASTRAClassHierarchy(IJavaHelper helper) {
		this.helper = helper;
		
		// add the default Agent type
		// we assume that there are no problems here...
		Vertex vertex = addClass("astra.lang.Agent");
//        System.out.println("Loading 'Agent' class...");
		vertex.element().load(helper);
	}

	private Vertex addClass(String clazz) {
		ASTRAClass node = new ASTRAClass(clazz);
		Vertex vertex = graph.insertVertex(node);
		classes.put(clazz, vertex);
		return vertex;
	}

	/**
	 * This method returns true if the class is already loaded, false otherwise...
	 * 
	 * @param cls the name of the ASTRA class
	 * @return true if the class is loaded, false otherwise
	 */
	public synchronized boolean contains(String cls) {
		return classes.containsKey(cls);
	}
	
	public synchronized void compile(String cls, Map> errors) {
		List> dependencies = new LinkedList>(); 

		load(cls, errors, dependencies);
//		System.out.println("Dependencies: " + dependencies);
		// NOTE: this method can return with an empty dependencies list - this happens if
		//       all the code has already been compiled AND is up to date...
		if (dependencies.isEmpty()) return;
		
		// Add all dependencies to the error map - this is to ensure that all existing
		// problem markers are deleted...
		for (Vertex vertex : dependencies) {
			if (!errors.containsKey(vertex.element().name())) {
				errors.put(vertex.element().name(), new LinkedList());
			}
		}
		
		// Check if the class being recompiled has errors
		if (errors.get(cls) == null || errors.get(cls).isEmpty()) {
			// No errors, so start building it...
			// Compile the main class
			compile(dependencies.remove(0), errors);
			
			// Now refresh the dependencies (needs reload of AST + recompilation)
			refreshDependencies(dependencies, errors);
		} else {
			// Errors - add an error to each dependent indicating that it could
			// not be compiled do to the error in this class...
			dependencies.remove(0);
			for (Vertex vertex : dependencies) {
				if (vertex.element().element() != null) {
					addError(
							vertex, 
							errors, 
							new ParseException("Cannot compile: " + vertex.element().name() + " due to error in " + cls, vertex.element().element()));
				}
			}			
		}
	}

	private void compile(Vertex vertex, Map> errors) {
		try {
			compile(vertex);
		} catch (ParseException e) {
			addError(vertex, errors, e);
		}
	}

	/**
	 * This method actually loads the specified class and updates the
	 * hierarchy to reflect it. This can include loading of parents
	 * (where the class is new) or invalidating of classes (forcing
	 * them to be recompiled).
	 * 
	 * Upon completion, a list of invalid classes will have been
	 * created. ALL of these classes will need to be recompiled...
	 *  
	 * @param cls
	 */

	private void load(String cls, Map> errors, List> list) {
		// System.out.println("[ASTRAClassHierarchy.load("+cls+")] Starting...");
		Vertex vertex = classes.get(cls);
		if (vertex != null) {
//			System.out.println("[ASTRACompiler] Class: " +  cls + " has already been loaded.");
			
			// Check if we need to recompile...
			if (!vertex.element().sourceChanged(helper)) {
				// System.out.println("[ASTRACompiler] Class: " +  cls + " has already been compiled.");
				// class already loaded and source not changed, so
				// do nothing...
				return ;
			} else {
				System.out.println("[ASTRACompiler] Class: " +  cls + " is not compiled.");
			}
			
			// Source is newer than compiled code - need to remove upward dependencies and reload AST.
			// Here we also need to invalidate any children as they will need to be recompiled once the
			// parent is recompiled...
			removeParentDependencies(vertex);
		} else {
//			System.out.println("[ASTRACompiler] New Class: " +  cls + " is ADDED.");
			// Class has not been seen before, so add it...
			vertex = addClass(cls);
		}
		invalidate(vertex, list);
		
		// Okay, so the vertex representing the class
		// has been added / existing parent dependencies
		// have been removed.
		//
		// Lets reload the AST and re-add the parent dependencies...
		if (!vertex.element().load(helper)) {
			// Okay, we did not manage to load the class so there should be
			// some errors, lets return them...
			errors.put(cls, vertex.element().errorList());
		} else {
			ASTRAClass clazz = vertex.element();
			String[] parents = clazz.element().getClassDeclaration().parents();
			for (int i=0; i < parents.length; i++) {
				parents[i] = helper.getQualifiedName(parents[i], clazz.element().packageElement().packageName(), clazz.element.imports());
				// System.out.println("Loading Parent: " + parents[i]);

				// Recursive call to load(...) method fills error map
				load(parents[i], errors, list);
				graph.insertEdge(classes.get(parents[i]), vertex, "");
			}
		}
	}
	
	private void invalidate(Vertex vertex, List> list) {
		list.add(vertex);
		for(Edge edge : graph.outEdges(vertex)) {
			invalidate(graph.target(edge), list);
		}
	}
	
	public synchronized void refreshDependencies(List> list, Map> errors) {
		while (!list.isEmpty()) {
			// Only try to compile the class if there are no syntax
			// errors...
			Vertex vertex = list.remove(0);
			vertex.element().load(helper);
			if (vertex.element().errorList().isEmpty()) {
				vertex.element().load(helper);
				compile(vertex, errors);
			}
		}
	}

	private void addError(Vertex vertex, Map> errors, ParseException e) {
		List l = errors.get(vertex.element().name());
		if (l == null) {
			l = new LinkedList();
			errors.put(vertex.element().name(), l);
		}
		l.add(e);
		
	}

	private void compile(Vertex vertex) throws ParseException {
		if (vertex.element().element() == null) {
			throw new ParseException("Cannot compile: " + vertex.element().name() + " source is not local", vertex.element().element());
		}
		

		// Only try to generate code if the file is local...
		if  (vertex.element().element().local()) {
			// The class is loaded, but only syntactic checks have been carried out. Now do
			// the semantic checks...
			LinkedList linearisation = getLinearisation(vertex);
			
			// Step 1: Iterate through the linearised list of classes (general-specific order)
			//         building a representation of the ontologies & triggering events.
			ComponentStore store = new ComponentStore();
			ComponentVisitor visitor = new ComponentVisitor(helper, store);
			for (int i=linearisation.size()-1; i >= 0; i--) {
				ASTRAClass node = linearisation.get(i);
				if (!node.isLoaded()) {
					throw new ParseException("Could not compile: " + vertex.element().name() + " due to error in: " + node.name(), 0, 0, 0);
				}
				
				// Construct the component store for the class: contains a set of resolved modules,
				// ontologies and rule trigger events.
				node.element().accept(visitor, store);
			}
			// Now get a reference to the class you are actually compiling...
			ASTRAClass node = linearisation.get(0);

			// Step 2: Check that the formulae and goals have corresponding entries in
			//         the component store.
			node.element().accept(new TypeCheckVisitor(), store);
			node.element().accept(new GoalCheckVisitor(), store);

			// Step 3: Generate the source code and save it to disk
			CodeGeneratorVisitor cgv = new CodeGeneratorVisitor(helper,store);
			node.element().accept(cgv, null);
			helper.createTarget(node.element(), cgv.toString());
		}
	}

	private void removeParentDependencies(Vertex vertex) {
		for(Edge edge : graph.inEdges(vertex)) {
			graph.removeEdge(edge);
		}
	}

	public synchronized boolean checkClass(String cls) {
		Vertex vertex = classes.get(cls);
		if (vertex != null) {
			// check if the class has been modified...
			return vertex.element().sourceChanged(helper);
		}
		return false;		
	}

	/**
	 * Linearisation of Class Hierarchy using Breadth First Traversal of
	 * the Directed Graph.
	 * 
	 * @param vertex the vertex containing the class to be linearized
	 * @return a list of classes ordered from child to root
	 * @throws ParseException  thrown by methods called in this method
	 */
	public synchronized LinkedList getLinearisation(Vertex vertex) throws ParseException {
		LinkedList linearization = new LinkedList();
		
		Queue> queue = new LinkedList>();
		
		queue.add(vertex);
		while (!queue.isEmpty()) {
			vertex = queue.poll();
			if (!linearization.contains(vertex.element())) {
				for (Edge edge: graph.inEdges(vertex)) {
					Vertex source = graph.source(edge);
					if (!queue.contains(source)) queue.add(source);
				}
			}
			linearization.add(vertex.element());
		}
		return linearization;
	}

	public synchronized void deleteClass(String cls, Map> errors) {
		System.out.println("[ASTRACompiler] Deleting:  " + cls);
		Vertex vertex = classes.remove(cls);
		if (vertex == null) {
			System.out.println("VERTEX is null");
			return;
		} 
		List> list = new LinkedList>();
		if (vertex.element() != null && vertex.element().isLoaded()) invalidate(vertex, list);
		graph.removeVertex(vertex);
		
		//remove vertex from the list
		list.remove(0);
		refreshDependencies(list, errors);
	}

	public synchronized LinkedList getLinearisation(String cls) throws ParseException {
		return getLinearisation(classes.get(cls));
	}

	public synchronized ASTRAClass getClass(String cls) {
		return classes.get(cls).element();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy