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

prerna.reactor.planner.graph.RuntimeJavaClassBuilder Maven / Gradle / Ivy

The newest version!
package prerna.reactor.planner.graph;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.common.collect.Lists;

import prerna.reactor.BaseJavaRuntime;
import prerna.reactor.ClassMaker;
import prerna.util.Constants;
import prerna.util.Utility;

public class RuntimeJavaClassBuilder {
	
	private static final Logger classLogger = LogManager.getLogger(RuntimeJavaClassBuilder.class);

	private String packageName;
	private String className;
	
	// this will hold the equations we need to push into the methods
	protected List equations = new Vector();
	// this will hold the fields required for this class
	protected List fields = new Vector();
	// this will hold the variables used by this class
	protected Set variables;
	// this will hold the count for the methods used
	// we need this because each method has a max size based on javasist
	protected int methodCount;
	// this will hold the name of the super class
	protected String superClassName;
	// this is so we keep within javasist limits
	protected int maxEquationsPerMethod = 700;
	protected int maxFieldsPerClass = 7000;
	protected int maxMethodsPerClass = 30;

	// used if we want to write the file for testing
	private boolean testing = false;

	/**
	 * Default constructor
	 * Will set the super as BaseJavaRuntime
	 */
	public RuntimeJavaClassBuilder() {
		this(Utility.getRandomString(12), Utility.getRandomString(12));
	}
	
	/**
	 * Default constructor
	 * Will set the super as BaseJavaRuntime
	 */
	public RuntimeJavaClassBuilder(String packageName, String className) {
		this.packageName = packageName;
		this.className = className;
		this.superClassName = BaseJavaRuntime.class.getName();
	}

	/**
	 * Set the super class for this class
	 * Useful when the class is to large
	 * and we need to make additional super classes
	 * @param superClass
	 */
	public void setSuperClass(Class superClass) {
		this.superClassName = superClass.getName();
	}

	/**
	 * used when you only want fields and no methods
	 * @return
	 */
	public Class generateClassWithOnlyFields() {
		// just use the fields
		// pass in empty list for the methods
		return buildRuntimeClass(this.fields, new Vector());
	}

	/**
	 * Used for class which only contains methods
	 * @return
	 */
	public Class generateClassWithOnlyMethods() {
		List methods = buildInitMethods();
		methods.addAll(buildMethods());
		return buildRuntimeClassSeperately(this.fields, methods);
	}

	/**
	 * Build the class
	 * Uses both fields and methods
	 * @return
	 */
	public Class generateClass() {
		List methods = buildInitMethods();
		methods.addAll(buildMethods());
		methods.add(buildMainExecutionMethod());
		return buildRuntimeClass(this.fields, methods);
	}

	/**
	 * Builds a class to update values in an existing class
	 * @return
	 */
	public Class generateUpdateClass() {
		List methods = new ArrayList();
		methods.add(buildUpdateMethods());
		methods.add(buildMainExecutionMethod());
		return buildRuntimeClass(this.fields, methods);
	}

	/**
	 * Add to the list of equations currently being kept
	 * These equations are pushed into methods
	 * @param equations
	 */
	public void addEquations(List equations) {
		for (String equation : equations) {
			if (!equations.isEmpty()) {
				this.equations.add(equation);
			}
		}
	}

	/**
	 * Add to the list of fields
	 * These fields are defined at the beginning of the class
	 * @param fields
	 */
	public void addFields(List fields) {
		for (String field : fields) {
			if (!field.isEmpty()) {
				this.fields.add(field);
			}
		}
	}

	/**
	 * This is the main class for generating the model
	 * @param stringFields			This is the list of fields
	 * @param stringMethods			This is the list of methods
	 * @return
	 */
	private Class buildRuntimeClass(List stringFields, List stringMethods) {
		ClassMaker maker = new ClassMaker(this.packageName, this.className);
		// add a super so we have a base method to execute
		maker.addSuper(superClassName);
		// loop through and add all the fields
		if (stringFields != null) {
			for (String stringField : stringFields) {
				maker.addField(stringField);
			}
		}

		// loop through and add the methods
		if (stringMethods != null) {
			for (String stringMethod : stringMethods) {
				maker.addMethod(stringMethod);
			}
		}

		// generate the class
		Class generatedClass = maker.toClass();

		// for testing, we can write out the class if we would like
		if(testing) {
			writeClassForTesting(stringFields, stringMethods);
		}

		return generatedClass;
	}

	/**
	 * Build the methods that we need to add using the list of equations
	 * @return
	 */
	private List buildMethods() {
		List equationExecutionMethods = new ArrayList<>();
		// if we have no equations... this is simple
		if (this.equations == null || this.equations.isEmpty()) {
			return equationExecutionMethods;
		}

		// we have equations
		// we need to keep track of the size such that this will compile
		this.methodCount++;
		int equationCount = 0;
		// each method gets its own string builder
		StringBuilder curMethod = new StringBuilder();

		// each method gets execute + i, where i is an integer
		curMethod.append("private void execute" + this.methodCount + "() {");
		for (String equation : equations) {
			curMethod.append(equation);
			String varName = getVarNameFromEquation(equation);
			if (varName == "") {
				continue;
			}

			// method a is to store the variable
			// so we call that after each formula
			// we put the actual name to the variable reference as a result of the name
			curMethod.append("a(\"" + varName + "\"," + varName + ");");
			// if we have the max equation amount for this method, move on
			if (equationCount == this.maxEquationsPerMethod) {
				// end the method
				curMethod.append("}");
				// add it ot the list
				equationExecutionMethods.add(curMethod.toString());

				// create a new method
				equationCount = 0;
				this.methodCount++;
				curMethod = new StringBuilder();
				curMethod.append("private void execute" + this.methodCount + "() {");
			} else {
				equationCount++;
			}
		}
		// finish the last method
		curMethod.append("}");
		// add it to the list
		equationExecutionMethods.add(curMethod.toString());
		return equationExecutionMethods;
	}

	/**
	 * Create a execute method that will call all the 
	 * execute + i, where i is an integer methods
	 * since there is a max size for each method
	 * @return
	 */
	private String buildMainExecutionMethod() {
		StringBuilder method = new StringBuilder();
		method.append("public void execute() {super.update(); update(); super.execute();");
		for (int i = 1; i <= methodCount; i++) {
			method.append("execute" + i + "();");
		}
		method.append("}");
		return method.toString();
	}

	/**
	 * All the fields that we are using
	 * They need to be pushed into the map
	 * For retrieval later on
	 * @return
	 */
	private List buildInitMethods() {
		List initMethods = new Vector<>();
		StringBuilder curMethod = new StringBuilder();

		this.methodCount = 1;
		int equationCount = 0;

		// this will iterate through and add all the variables into the map
		// also keeping in track the max size
		curMethod.append("private void execute" + this.methodCount + "(){");
		for (String varName : variables) {
			curMethod.append("a(\"" + varName + "\"," + varName + ");");
			if (equationCount >= maxEquationsPerMethod) {
				curMethod.append("}");
				initMethods.add(curMethod.toString());
				curMethod = new StringBuilder();
				this.methodCount++;
				equationCount = 0;
				curMethod.append("private void execute" + this.methodCount + "(){");
			} else {
				equationCount++;
			}
		}
		curMethod.append("}");
		initMethods.add(curMethod.toString());

		return initMethods;
	}

	/**
	 * Generate the update method
	 * @return
	 */
	private String buildUpdateMethods() {
		// will implement the update method
		// assumption, since this is done via the user clicking on the UI
		// no way this should be large enough to cause any issues with size
		StringBuilder updateMethod = new StringBuilder();
		if (equations != null && !equations.isEmpty()) {
			updateMethod.append("public void update(){");
			for(String equation : equations) {
				updateMethod.append(equation);
				String varName = getVarNameFromEquation(equation);
				if (varName == "") {
					continue;
				}
				updateMethod.append("a(\"" + varName + "\"," + varName + ");");
			}
			updateMethod.append("}");
		}
		return updateMethod.toString();
	}

	/**
	 * Split the string to get the variable name being used
	 * @param equation
	 * @return
	 */
	private String getVarNameFromEquation(String equation) {
		return equation.split("=")[0].trim();
	}

	/**
	 * 
	 * @param stringFields
	 * @param stringMethods
	 * @return
	 */
	private Class buildRuntimeClassSeperately(List stringFields, List stringMethods) {
		buildSuperClassWithOnlyFields(stringFields);
		Class generatedClass = buildSuperClassWithOnlyMethods(stringMethods);
		return generatedClass;
	}

	/**
	 * Build MainExcetution from start to end
	 * 
	 * @return
	 */
	private String buildMainExecutionMethod(int start, int end) {
		StringBuilder method = new StringBuilder();
		method.append("public void execute() {super.update();update();super.execute();");
		for (int i = start; i <= end; i++) {
			method.append("execute" + i + "();");
		}
		method.append("}");
		return method.toString();
	}

	/**
	 * Serparate fields into several classes
	 * 
	 * @param fieldsList
	 * @param superClass
	 */
	private Class buildSuperClassWithOnlyFields(List fieldsList) {
		Class superClass = null;
		for (List partList : Lists.partition(fieldsList, maxFieldsPerClass)) {
			superClass = this.buildRuntimeClass(partList, null).getClass();
			superClassName = superClass.getName();
		}
		return superClass;
	}

	/**
	 * Sepeate methods into several classes
	 * 
	 * @param methods
	 * @param superClass
	 */
	private Class buildSuperClassWithOnlyMethods(List stringMethods) {
		Class superClass = null;
		int start = 1;
		int end = stringMethods.size();
		for (List partList : Lists.partition(stringMethods, maxMethodsPerClass)) {
			end = start + partList.size() - 1;
			String mainMethod = buildMainExecutionMethod(start, end);
			partList.add(mainMethod);
			superClass = this.buildRuntimeClass(null, partList).getClass();
			superClassName = superClass.getName();
			start = end + 1;
		}
		return superClass;
	}

	/**
	 * Used only for testing!!!
	 * 
	 * @param stringFields
	 * @param stringMethods
	 */
	public void writeClassForTesting(List stringFields, List stringMethods) {
		BufferedWriter writer = null;
		FileWriter fw = null;
		try {
			fw = new FileWriter("C:\\workspace\\Semoss_Dev\\src\\prerna\\sablecc2\\reactor\\test\\ClassTest.txt");
			writer = new BufferedWriter(fw);
			//			writer.write("#################################    Class" + superClassesCount++
			//					+ "###########################################\n");

			if (stringFields != null) {
				for (String stringField : stringFields) {
					writer.write(stringField);
					writer.write("\n");
				}
			}

			if (stringMethods != null) {
				for (String stringMethod : stringMethods) {
					writer.write(stringMethod);
					writer.write("\n");
				}
			}

			
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if(writer != null) {
			try {
				writer.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				classLogger.error(Constants.STACKTRACE, e);
			}
			}
			if(fw != null) {
			try {
				fw.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				classLogger.error(Constants.STACKTRACE, e);
			}
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy