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

prerna.reactor.Assimilator Maven / Gradle / Ivy

The newest version!
package prerna.reactor;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.plexus.util.StringUtils;

import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.sablecc2.om.task.TaskUtility;
import prerna.util.Constants;

public class Assimilator extends AbstractReactor implements JavaExecutable {
	
	private static final Logger classLogger = LogManager.getLogger(Assimilator.class);

	// 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;

	protected List formulas = new Vector();
	
	@Override
	public NounMetadata execute() {
		modifyForEmbeddedScripts();
		modifySignatureFromLambdas();
		for(String formula : formulas) {
			this.signature = StringUtils.replaceOnce( this.signature, formula, "( 1.0 * " + formula.substring(1, formula.length()));
		}
		// 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();

		// 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;
	}

	/**
	 * 
	 * method to get the run time class for the assimilator
	 */
	private AssimilatorEvaluator getAssimilatorEvaluator() {
		// store the variables
		// this will get added to by reference
		Map vars = new HashMap();
		//stringified method for the evaluator
		String stringMethod = buildMethodString(vars);
		return buildAssimilatorEvaluator(stringMethod, vars);
	}

	/**
	 * 
	 * @param stringMethod
	 * @return
	 * 
	 * method responsible for building a new assimilator class from a stringified method
	 */
	private AssimilatorEvaluator buildAssimilatorEvaluator(String stringMethod, Map vars) {
		// evaluate the assimilator as an object
		ClassMaker maker = new ClassMaker();
		// add a super so we have a base method to execute
		maker.addSuper(AssimilatorEvaluator.class.getName());
		maker.addMethod(stringMethod);
		Class newClass = maker.toClass();

		try {
			AssimilatorEvaluator newInstance = (AssimilatorEvaluator) newClass.newInstance();
			newInstance.setVars(vars);
			newInstance.containsStringValue = this.containsStringValue;
			return newInstance;
		} catch (InstantiationException | IllegalAccessException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}

		return null;
	}

	/**
	 * method responsible for building the method
	 */
	private String buildMethodString(Map vars) {
		// 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, vars);
		// 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 pixel data type
			expressionBuilder.append("return new java.math.BigDecimal(1.0 * ").append(this.signature).append(");}");
		}
		return expressionBuilder.toString();
	}

	/**
	 * Append the variables used within the expression
	 * @param expressionBuilder
	 */
	private void appendVariables(StringBuilder expressionBuilder, Map vars) {
		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);
			}
			Object value = data.getValue();
			PixelDataType dataType = data.getNounType();
			if(dataType == PixelDataType.CONST_DECIMAL) {
				expressionBuilder.append("double ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).doubleValue()").append(";");
				vars.put(input, value);
			} else if(dataType == PixelDataType.CONST_INT) {
				expressionBuilder.append("int ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).intValue()").append(";");
				vars.put(input, value);
			} else if(dataType == PixelDataType.CONST_STRING) {
				this.containsStringValue = true;
				expressionBuilder.append("String ").append(input).append(" = ").append("(String)super.vars.get("+"\""+input+"\")").append(";");
				vars.put(input, value);
			} 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) {
					Object newValue = data.getValue();
					PixelDataType newDataType = data.getNounType();
					if(newDataType == PixelDataType.CONST_DECIMAL) {
						expressionBuilder.append("double ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).doubleValue()").append(";");
						vars.put(input, newValue);
					} else if(newDataType == PixelDataType.CONST_INT) {
						expressionBuilder.append("int ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).intValue()").append(";");
						vars.put(input, newValue);
					} else {
						this.containsStringValue = true;
						expressionBuilder.append("String ").append(input).append(" = ").append("(String)super.vars.get("+"\""+input+"\")").append(";");
						vars.put(input, newValue);
					}
				} else {
					// this should never ever happen....
					throw new IllegalArgumentException("Assimilator cannot handle this type if input");
				}
			} else if(dataType == PixelDataType.FORMATTED_DATA_SET || dataType == PixelDataType.TASK) {
				NounMetadata formatData = TaskUtility.getTaskDataScalarElement(value);
				if(formatData == null) {
					throw new IllegalArgumentException("Can only handle query data that is a scalar input");
				} else {
					Object newValue = formatData.getValue();
					PixelDataType newDataType = formatData.getNounType();
					if(newDataType == PixelDataType.CONST_DECIMAL) {
						expressionBuilder.append("double ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).doubleValue()").append(";");
						vars.put(input, newValue);
					} else if(newDataType == PixelDataType.CONST_INT) {
						expressionBuilder.append("int ").append(input).append(" = ").append("((Number)super.vars.get("+"\""+input+"\")).intValue()").append(";");
						vars.put(input, newValue);
					} else {
						this.containsStringValue = true;
						expressionBuilder.append("String ").append(input).append(" = ").append("(String)super.vars.get("+"\""+input+"\")").append(";");
						vars.put(input, newValue);
					}
				}
			} else {
				throw new IllegalArgumentException("Unable to handle this type of input");
			}
		}
		
		// need to how check if there are strings
		int numValues = this.curRow.size();
		for(int i = 0; i < numValues; i++) {
			if(this.curRow.getNoun(i).getNounType() == PixelDataType.CONST_STRING) {
				this.containsStringValue = true;
				break;
			}
		}
	}
	
	/**
	 * Modify the expression for embedded scripts
	 */
	protected void modifyForEmbeddedScripts() {
		// need to account for embedded scripts that we want to evaluate
		List lambdas = curRow.getNounsOfType(PixelDataType.LAMBDA);
		for(int i = 0; i < lambdas.size(); i++) {
			NounMetadata n = lambdas.get(i);
			if(n.getValue() instanceof EmbeddedScriptReactor) {
				// replace the value with the output
				NounMetadata output = ((EmbeddedScriptReactor) n.getValue()).execute();
				PixelDataType dataType = output.getNounType();
				if(dataType == PixelDataType.CONST_DECIMAL || dataType == PixelDataType.CONST_INT) {
					this.signature = StringUtils.replaceOnce( this.signature, ((EmbeddedScriptReactor) n.getValue()).getOriginalSignature(), output.getValue().toString());
				} else if(dataType == PixelDataType.CONST_STRING) {
					this.signature = StringUtils.replaceOnce( this.signature, ((EmbeddedScriptReactor) n.getValue()).getOriginalSignature(), output.getValue().toString());
					this.containsStringValue = true;
				} else if(dataType == PixelDataType.FORMATTED_DATA_SET || dataType == PixelDataType.TASK) {
					NounMetadata formatData = TaskUtility.getTaskDataScalarElement(output);
					if(formatData == null) {
						throw new IllegalArgumentException("Can only handle query data that is a scalar input");
					} else {
						Object newValue = formatData.getValue();
						PixelDataType newDataType = formatData.getNounType();
						if(newDataType == PixelDataType.CONST_DECIMAL || newDataType == PixelDataType.CONST_INT) {
							this.signature = StringUtils.replaceOnce( this.signature, ((EmbeddedScriptReactor) n.getValue()).getOriginalSignature(), newValue.toString());
						} else {
							this.signature = StringUtils.replaceOnce( this.signature, ((EmbeddedScriptReactor) n.getValue()).getOriginalSignature(), newValue.toString());
							this.containsStringValue = true;
						}
					}
				} else {
					throw new IllegalArgumentException("Unable to handle this type of input");
				}
			}
		}
	}

	public void addFormula(String formula) {
		// always append at the beginning
		// so that we address the most inner one
		this.formulas.add(0, formula);
	}

	@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;
	}
	
	protected String modifyJavaSignature(String javaSignature, String stringToFind, String stringReplacement) {
		return StringUtils.replaceOnce(javaSignature, stringToFind, stringReplacement);
	}

	@Override
	public List getJavaInputs() {
		return null;
	}

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy