blob: db3ba44b003ec6c20ef1206bd970305575e14c1f [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2020 The University of York.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*********************************************************************/
package org.eclipse.epsilon.pinset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.epsilon.common.module.IModule;
import org.eclipse.epsilon.common.parse.AST;
import org.eclipse.epsilon.common.util.AstUtil;
import org.eclipse.epsilon.eol.dom.AnnotatableModuleElement;
import org.eclipse.epsilon.eol.dom.ExecutableBlock;
import org.eclipse.epsilon.eol.dom.IExecutableModuleElement;
import org.eclipse.epsilon.eol.dom.Parameter;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.execute.context.FrameType;
import org.eclipse.epsilon.eol.execute.context.IEolContext;
import org.eclipse.epsilon.eol.execute.context.Variable;
import org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter;
import org.eclipse.epsilon.eol.models.IModel;
import org.eclipse.epsilon.eol.types.EolModelElementType;
import org.eclipse.epsilon.eol.types.EolType;
import org.eclipse.epsilon.pinset.columnGenerators.Column;
import org.eclipse.epsilon.pinset.columnGenerators.ColumnGenerator;
import org.eclipse.epsilon.pinset.columnGenerators.Grid;
import org.eclipse.epsilon.pinset.columnGenerators.NestedFrom;
import org.eclipse.epsilon.pinset.columnGenerators.Properties;
import org.eclipse.epsilon.pinset.columnGenerators.Reference;
import org.eclipse.epsilon.pinset.parse.PinsetParser;
/**
* DatasetRule.
*
* @author Alfonso de la Vega
* @since 2.1
*/
public class DatasetRule extends AnnotatableModuleElement {
protected String name;
protected Parameter parameter;
protected ExecutableBlock<Boolean> guardBlock;
protected IExecutableModuleElement fromBlock = null;
protected List<ColumnGenerator> generators = new ArrayList<>();
protected Dataset dataset;
@SuppressWarnings("unchecked")
@Override
public void build(AST cst, IModule module) {
super.build(cst, module);
name = cst.getFirstChild().getText();
parameter = (Parameter) module.createAst(cst.getSecondChild(), this);
guardBlock = (ExecutableBlock<Boolean>) module.createAst(
AstUtil.getChild(cst, PinsetParser.GUARD), this);
AST fromAST = AstUtil.getChild(cst, PinsetParser.FROM);
if (fromAST != null) {
fromBlock = (IExecutableModuleElement) module.createAst(fromAST.getFirstChild(), this);
}
boolean isSilent = ((PinsetModule) module).isSilent() ||
this.hasAnnotation(PinsetModule.SILENT_ANNOTATION);
// loop over children looking for column generators
for (AST child : cst.getChildren()) {
if (isColumnGenerator(child)) {
ColumnGenerator gen = (ColumnGenerator) module.createAst(child, this);
setSilent(gen, isSilent);
generators.add(gen);
}
}
}
public static void setSilent(ColumnGenerator colGen, boolean isSilent) {
if (colGen instanceof Column) {
((Column) colGen).setSilent(isSilent);
}
else if (colGen instanceof Grid) {
((Grid) colGen).setSilent(isSilent);
}
else if (colGen instanceof NestedFrom) {
((NestedFrom) colGen).setSilent(isSilent);
}
}
public static boolean isColumnGenerator(AST child) {
switch (child.getType()) {
case PinsetParser.PROPERTIES:
case PinsetParser.REFERENCE:
case PinsetParser.COLUMN:
case PinsetParser.GRID:
case PinsetParser.NESTEDFROM:
return true;
default:
return false;
}
}
public boolean isIncluded(Object object, IEolContext context,
String varName) throws EolRuntimeException {
if (guardBlock != null) {
return guardBlock.execute(context,
Variable.createReadOnlyVariable(varName, object));
}
else {
return true;
}
}
public void execute(IEolContext context) throws EolRuntimeException {
Collection<?> oElements = null;
IModel model = null;
IPropertyGetter getter = null;
EolType parameterType = parameter.getType(context);
if (!(parameterType instanceof EolModelElementType)) {
if (fromBlock == null) {
throw new EolRuntimeException(
"Datasets generated over non-model types must specify a 'from' expression");
}
}
else {
model = ((EolModelElementType) parameterType).getModel();
getter = model.getPropertyGetter();
}
if (fromBlock == null) {
oElements = ((EolModelElementType) parameterType).getAllOfKind();
}
else {
context.getFrameStack().enterLocal(FrameType.UNPROTECTED, fromBlock);
Object result = ReturnValueParser.obtainValue(
context.getExecutorFactory().execute(fromBlock, context));
context.getFrameStack().leaveLocal(fromBlock);
if (!(result instanceof Collection<?>)) {
throw new EolRuntimeException(
"The 'from' expression must return a collection of elements");
}
oElements = (Collection<?>) result;
}
initialiseGenerators(context, getter);
dataset = new Dataset();
dataset.setColumnNames(getColumnNames());
for (Object oElem : oElements) {
if (!isIncluded(oElem, context, parameter.getName())) {
continue;
}
dataset.addColumnValues(getRowValues(context, oElem));
}
postProcess(context, dataset);
}
private void postProcess(IEolContext context, Dataset dataset)
throws EolRuntimeException {
for (ColumnGenerator colGen : generators) {
if (colGen instanceof Column || colGen instanceof Grid) {
AnnotatableModuleElement colGenElement =
((AnnotatableModuleElement) colGen);
if (colGenElement.hasAnnotation(PinsetModule.NORMALIZE_ANNOTATION)) {
Object value = colGenElement
.getAnnotationsValues(PinsetModule.NORMALIZE_ANNOTATION,
context)
.get(0);
if (value instanceof String) {
try {
value = Double.parseDouble((String) value);
}
catch (NumberFormatException nfe) {
/* treated just below */}
}
if (value != null && !(value instanceof Number)) {
throw new EolRuntimeException("Normalization value must be a number");
}
for (String colName : colGen.getNames()) {
PostProcessing.normalize(dataset.getValuesByColumn(colName),
(Number) value);
}
}
if (colGenElement.hasAnnotation(PinsetModule.FILL_NULLS_ANNOTATION)) {
String value = (String) colGenElement
.getAnnotationsValues(PinsetModule.FILL_NULLS_ANNOTATION,
context)
.get(0);
PostProcessing.FillType fType = PostProcessing.FillType.VALUE;
if (value != null && value.equals(PinsetModule.FILL_NULLS_MODE)) {
fType = PostProcessing.FillType.MODE;
}
else if (value != null && value.equals(PinsetModule.FILL_NULLS_MEAN)) {
fType = PostProcessing.FillType.MEAN;
}
for (String colName : colGen.getNames()) {
PostProcessing.fillNullValues(dataset.getValuesByColumn(colName),
fType, value);
}
}
}
}
}
private List<Object> getRowValues(IEolContext context, Object oElem)
throws EolRuntimeException {
List<Object> rowValues = new ArrayList<>();
context.getFrameStack().enterLocal(FrameType.UNPROTECTED, this);
context.getFrameStack().put(
Variable.createReadOnlyVariable(parameter.getName(), oElem));
for (ColumnGenerator generator : generators) {
List<Object> values = generator.getValues(oElem);
rowValues.addAll(values);
// if we calculate a column, we add it to the stack so it can be used
// in later column calculations. We know that values only has 1 elem
if (generator instanceof Column) {
context.getFrameStack().put(
Variable.createReadOnlyVariable(((Column) generator).getName(),
values.get(0)));
}
}
context.getFrameStack().leaveLocal(this);
return rowValues;
}
private List<String> getColumnNames() throws EolRuntimeException {
List<String> columnNames = new ArrayList<>();
for (ColumnGenerator generator : generators) {
columnNames.addAll(generator.getNames());
}
return columnNames;
}
private void initialiseGenerators(IEolContext context,
IPropertyGetter getter) {
for (ColumnGenerator generator : generators) {
initialise(generator, context, getter);
}
}
public static void initialise(ColumnGenerator generator, IEolContext context,
IPropertyGetter getter) {
if (generator instanceof Properties) {
((Properties) generator).setContext(context);
((Properties) generator).setGetter(getter);
}
else if (generator instanceof Reference) {
((Reference) generator).setContext(context);
((Reference) generator).setGetter(getter);
}
else if (generator instanceof Column) {
((Column) generator).setContext(context);
}
else if (generator instanceof Grid) {
((Grid) generator).setContext(context);
}
else if (generator instanceof NestedFrom) {
((NestedFrom) generator).initialise(context, getter);
}
}
public Parameter getParameter() {
return parameter;
}
public void setParameter(Parameter parameter) {
this.parameter = parameter;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dataset getDataset() {
return dataset;
}
public void dispose() {
dataset = null;
}
}