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

prerna.sablecc2.reactor.Assimilator Maven / Gradle / Ivy

There is a newer version: 4.2.2
Show newest version
package prerna.sablecc2.reactor;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.codehaus.plexus.util.StringUtils;

import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.nounmeta.NounMetadata;

public class Assimilator extends AbstractReactor implements JavaExecutable {

	// roles of the assimilator is simple, just assimilate an expression and then
	// plug it into the parent
	// filter is a good example of assimilator for example
	private boolean containsStringValue = false;

	@Override
	public NounMetadata execute() {
		modifySignatureFromLambdas();
		
		// get the assimilator evaluator
		// this is the class we are going to be using to execute
		// if we are running this again, we will not create the class and add
		// it to the ClassPool, but if it is new, we will
		AssimilatorEvaluator newInstance = getAssimilatorEvaluator();
		// set the values into the new instance's var map
		// this is implemented this way so we can re-use the class
		// even if a few variables are changed
		setInstanceVariables(newInstance);

		// noun object to return
		// need to cast to get the type of the NounMetadata object
		NounMetadata noun = new NounMetadata(newInstance, PixelDataType.LAMBDA);
		return noun;
		
//		Object retVal = newInstance.execute();
//		if(newInstance.containsStringValue) {
//			noun = new NounMetadata(retVal.toString(), PkslDataTypes.CONST_STRING);
//		} else if(allIntValue) {
//			Number result = (Number) retVal;
//			if(result.doubleValue() == Math.rint(result.doubleValue())) {
//				noun = new NounMetadata( ((Number) retVal).intValue(), PkslDataTypes.CONST_INT);
//			} else {
//				// not a valid integer
//				// return as a double
//				noun = new NounMetadata( ((Number) retVal).doubleValue(), PkslDataTypes.CONST_DECIMAL);
//			}
//		} else {
//			noun = new NounMetadata( ((Number) retVal).doubleValue(), PkslDataTypes.CONST_DECIMAL);
//		}
//
//		return noun;
	}

	/**
	 * 
	 * @param evaluator
	 * 
	 * This method sets the values to the evaluator through the abstract
	 */
	private void setInstanceVariables(AssimilatorEvaluator evaluator) {
		List inputColumns = curRow.getAllColumns();
		// these input columns should be defined at the beginning of the expression
		// technically, someone can use the same variable multiple times
		// so need to account for this
		// ... just add them to a set and call it a day
		Set uniqueInputs = new HashSet();
		uniqueInputs.addAll(inputColumns);
		for(String input : uniqueInputs) {
			NounMetadata data = planner.getVariableValue(input);			
			PixelDataType dataType = data.getNounType();
			if(dataType == PixelDataType.CONST_DECIMAL) {
				evaluator.allIntValue = false;
				evaluator.setVar(input, data.getValue());
			} else if(dataType == PixelDataType.CONST_INT) {
				evaluator.setVar(input, data.getValue());
			} else if(dataType == PixelDataType.CONST_STRING) {
				evaluator.containsStringValue = true;
				evaluator.setVar(input, data.getValue());
			} else if(dataType == PixelDataType.LAMBDA){
				// in case the variable points to another reactor
				// that we need to get the value from
				// evaluate the lambda
				// object better be a reactor to run
				Object rVal = data.getValue();
				if(rVal instanceof IReactor) {
					NounMetadata newNoun = ((IReactor) rVal).execute(); 
					PixelDataType newDataType = data.getNounType();
					if(newDataType == PixelDataType.CONST_DECIMAL) {
						evaluator.setVar(input, newNoun.getValue());
					} else if(newDataType == PixelDataType.CONST_STRING) {
						evaluator.containsStringValue = true;
						evaluator.setVar(input, newNoun.getValue());
					}
				} else {
					// this should never ever happen....
					throw new IllegalArgumentException("Assimilator cannot handle this type if input");
				}
			} else {
				throw new IllegalArgumentException("Assimilator can currently only handle outputs of scalar variables");
			}
		}
	}

	/**
	 * 
	 * method to get the run time class for the assimilator
	 */
	private AssimilatorEvaluator getAssimilatorEvaluator() {
		AssimilatorEvaluator evaluator;

		//stringified method for the evaluator
		String stringMethod = buildMethodString();
		//id for this particular assimilator
		String classId = "$Assimilator"+stringMethod.hashCode();
		//if we have an existing one, grab that
		if(this.planner.hasVariable(classId)) {
			evaluator = (AssimilatorEvaluator)this.planner.getVariable(classId).getValue();
		} 

		//otherwise build a new one
		else {
			evaluator = buildAssimilatorEvaluator(stringMethod);
//			NounMetadata newEvaluator = new NounMetadata(evaluator, PkslDataTypes.CACHED_CLASS);
//			this.planner.addVariable(classId, newEvaluator);
		}

		return evaluator;
	}

	/**
	 * 
	 * @param stringMethod
	 * @return
	 * 
	 * method responsible for building a new assimilator class from a stringified method
	 */
	private AssimilatorEvaluator buildAssimilatorEvaluator(String stringMethod) {
		// evaluate the assimilator as an object
		ClassMaker maker = new ClassMaker();

		// add a super so we have a base method to execute
		maker.addSuper("prerna.sablecc2.reactor.AssimilatorEvaluator");
		maker.addMethod(stringMethod);
		Class newClass = maker.toClass();

		try {
			AssimilatorEvaluator newInstance = (AssimilatorEvaluator) newClass.newInstance();
			return newInstance;
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * method responsible for building the method
	 */
	private String buildMethodString() {
		// keep a string to generate the method to execute that will
		// return an object that runs the expression
		StringBuilder expressionBuilder = new StringBuilder();
		expressionBuilder.append("public Object getExpressionValue(){");
		// we need to grab any variables and define them at the top of the method
		appendVariables(expressionBuilder);
		// now that the variables are defined
		// we just want to add the expression as a return
		if(this.containsStringValue) {
			expressionBuilder.append("return new String(").append(this.signature).append("));}");
		} else {
			// multiply by 1.0 to make sure everything is a double...
			// as a pksl data type
			expressionBuilder.append("return new Double(1.0 * ( ").append(this.signature).append("));}");
		}
		return expressionBuilder.toString();
	}

	/**
	 * Append the variables used within the expression
	 * @param expressionBuilder
	 */
	private void appendVariables(StringBuilder expressionBuilder) {
		List inputColumns = curRow.getAllColumns();
		// these input columns should be defined at the beginning of the expression
		// technically, someone can use the same variable multiple times
		// so need to account for this
		// ... just add them to a set and call it a day
		Set uniqueInputs = new HashSet();
		uniqueInputs.addAll(inputColumns);
		for(String input : uniqueInputs) {
			NounMetadata data = planner.getVariableValue(input);
			if(data == null) {
				// this only happens when a variable is being used but isn't defined
				throw new IllegalArgumentException("Undefined variable : " + input);
			}
			PixelDataType dataType = data.getNounType();
			if(dataType == PixelDataType.CONST_DECIMAL) {
				expressionBuilder.append("double ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).doubleValue()").append(";");
			} else if(dataType == PixelDataType.CONST_INT) {
				expressionBuilder.append("int ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).intValue()").append(";");
			} else if(dataType == PixelDataType.CONST_STRING) {
				this.containsStringValue = true;
				expressionBuilder.append("String ").append(input).append(" = ").append("(String)super.vars.get("+"\""+input+"\")").append(";");
			} else if(dataType == PixelDataType.LAMBDA){
				// in case the variable points to another reactor
				// that we need to get the value from
				// evaluate the lambda
				// object better be a reactor to run
				Object rVal = data.getValue();
				if(rVal instanceof IReactor) {
					PixelDataType newDataType = data.getNounType();
					if(newDataType == PixelDataType.CONST_DECIMAL) {
						expressionBuilder.append("double ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).doubleValue()").append(";");
					} else if(newDataType == PixelDataType.CONST_INT) {
						expressionBuilder.append("int ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).intValue()").append(";");
					} else if(newDataType == PixelDataType.CONST_STRING) {
						this.containsStringValue = true;
						expressionBuilder.append("String ").append(input).append(" = ").append("(String)super.vars.get("+"\""+input+"\")").append(";");
					}
				} else {
					// this should never ever happen....
					throw new IllegalArgumentException("Assimilator cannot handle this type if input");
				}
			} else {
				throw new IllegalArgumentException("Assimilator can currently only handle outputs of scalar variables");
			}
		}
	}

	@Override
	public List getOutputs() {
		List outputs = super.getOutputs();
		if(outputs != null) {
			return outputs;
		}

		outputs = new Vector();
		NounMetadata output = new NounMetadata(this.signature, PixelDataType.LAMBDA);
		outputs.add(output);
		return outputs;
	}

	@Override
	public String getJavaSignature() {
		String javaSig = this.signature;
		// replace all the values that is inside this. this could be a recursive call
		for(int i = 0; i < curRow.size(); i++) {
			NounMetadata thisLambdaMeta = curRow.getNoun(i);
			
			Object nextValue = (Object)thisLambdaMeta.getValue();
			
			String replaceValue;
			if(nextValue instanceof JavaExecutable) {
				 replaceValue = ((JavaExecutable)nextValue).getJavaSignature();
			} else {
				continue;
			}
			
			String rSignature;
			if(nextValue instanceof IReactor) {
				rSignature = ((IReactor)nextValue).getOriginalSignature();
			} else {
				continue;
			}
//			NounMetadata result = thisReactor.execute();// this might further trigger other things

//			// for compilation reasons
//			// if we have a double
//			// we dont want it to print with the exponential
//			Object replaceValue = result.getValue();
//			PkslDataTypes replaceType = result.getNounName();
//			if(replaceType == PkslDataTypes.CONST_DECIMAL || 
//					replaceType == PkslDataTypes.CONST_INT) {
//				// big decimal is easiest way i have seen to do this formatting
//				replaceValue = new BigDecimal( ((Number) replaceValue).doubleValue()).toPlainString();
//			} else {
//				replaceValue = replaceValue + "";
//			}
			javaSig = modifyJavaSignature(javaSig, rSignature, replaceValue);
		}
		
		return javaSig;
//		return null;
	}
	
	public String modifyJavaSignature(String javaSignature, String stringToFind, String stringReplacement) {
		return StringUtils.replaceOnce(javaSignature, stringToFind, stringReplacement);
	}

	@Override
	public List getJavaInputs() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getReturnType() {
		return "double";
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy