blob: 9c539a011bcc0eb120942e32205fbd62af469908 [file] [log] [blame]
/**
* Copyright (c) 2019 CEA.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* CEA - initial API and implementation and/or initial documentation
*/
package org.eclipse.sensinact.studio.view.projectexplorer.handler.sna2json;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.sensinact.studio.model.resource.utils.JsonUtil;
import org.eclipse.sensinact.studio.view.projectexplorer.handler.SnaParsingResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_AFTER;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_AVG;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_BEFORE;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_COINCIDE;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_COUNT;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_DURATION;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_DURATION_MIN;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_DURATION_SEC;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_MAX;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_MIN;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_STATEMENT;
import org.eclipse.sensinact.studio.language.sensinact.DSL_CEP_SUM;
import org.eclipse.sensinact.studio.language.sensinact.DSL_ECA_STATEMENT;
import org.eclipse.sensinact.studio.language.sensinact.DSL_ElseDo;
import org.eclipse.sensinact.studio.language.sensinact.DSL_ElseIfDo;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_And;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Diff;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Division;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Equal;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Larger;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Larger_Equal;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Minus;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Modulo;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Multiplication;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Negate;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Or;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Plus;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Smaller;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Expression_Smaller_Equal;
import org.eclipse.sensinact.studio.language.sensinact.DSL_IfDo;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Object_Boolean;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Object_Number;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Object_Ref;
import org.eclipse.sensinact.studio.language.sensinact.DSL_Object_String;
import org.eclipse.sensinact.studio.language.sensinact.DSL_REF;
import org.eclipse.sensinact.studio.language.sensinact.DSL_REF_CONDITION;
import org.eclipse.sensinact.studio.language.sensinact.DSL_ResourceAction;
import org.eclipse.sensinact.studio.language.sensinact.DSL_SENSINACT;
/**
* Converts a SNA file into a JSON one packed into a SnaParsingResult object.
*
* This class offers a single entry point : the static public method
* parseSnaFile.
* @author Etienne Gandrille
*/
public abstract class SnaToJsonUtil {
/** The logger */
private static final Logger logger = Logger.getLogger(SnaToJsonUtil.class);
/**
* Converts a SNA file into a JSON one packed into a SnaParsingResult
* object.
*
* @param fileModel
* the file model
* @param fileName
* the file name
* @return A JSON packed into a SnaParsingResult object
* @throws JSONException
*/
public static SnaParsingResult parseSnaFile(DSL_SENSINACT fileModel, String fileName) throws JSONException {
JSONArray components = new JSONArray();
JSONObject initialize = new JSONObject();
JSONObject finalize = new JSONObject();
ResourcesDeclarationManager resMgr = new ResourcesDeclarationManager(fileModel.getResources());
// initialize : autostart
setupInitializeOptions(fileModel, initialize);
// set up file structure
JSONObject root = initFileStructure(fileName, components, initialize, finalize);
// CEP
for (DSL_CEP_STATEMENT cep : fileModel.getCep()) {
components.put(parseCepStatement(resMgr, cep));
}
// On
List<DSL_REF> triggers = new ArrayList<>();
for (DSL_REF_CONDITION triger : fileModel.getOn().getTriggers()) {
triggers.add(triger.getRef());
}
// If ... do ... else if ... do ... else do ... endif
DSL_ECA_STATEMENT eca = fileModel.getEca();
parseEcaStatement(fileName, resMgr, eca, components, triggers);
return new SnaParsingResult(root);
}
private static void setupInitializeOptions(DSL_SENSINACT fileModel, JSONObject initialize) throws JSONException {
boolean autostart = true; // default value
if (fileModel.getAutostart() != null)
autostart = fileModel.getAutostart().isActivated();
JSONObject object = new JSONObject();
object.put("autostart", autostart);
initialize.put("options", object);
}
/**
* Parse an ECA statement and updates the JSON component array.
*
* @param eca
* @param components
* @param triggers
* @throws JSONException
*/
private static void parseEcaStatement(String fileName, ResourcesDeclarationManager resMgr, DSL_ECA_STATEMENT eca, JSONArray components, List<DSL_REF> triggers) throws JSONException {
VariableGenerator var = VariableGenerator.get(fileName, "condition");
// if ...
try {
DSL_Expression expre = eca.getIfdo().getCondition();
EventManager eventManager = new IfEventManager(resMgr, triggers);
parseExpression(fileName, components, expre, triggers, eventManager, resMgr, var.newVariable());
} catch (JSONException e) {
logger.error("condition parsing error", e);
}
// do ...
DSL_IfDo ifDo = eca.getIfdo();
if (ifDo != null && ifDo.getActions() != null && ifDo.getActions().getActionList() != null) {
EList<DSL_ResourceAction> actionsTrue = ifDo.getActions().getActionList();
parseConditionalActions(fileName, resMgr, components, var.curVariable(), true, actionsTrue);
}
// else if ... do ...
EList<DSL_ElseIfDo> elseIfDoList = eca.getElseIfdo();
if (elseIfDoList != null) {
for (DSL_ElseIfDo elseIfDo : elseIfDoList) {
// else if ...
try {
DSL_Expression expre = elseIfDo.getCondition();
EventManager eventManager = new BooleanVariableEventManager(var.curVariable(), false);
parseExpression(fileName, components, expre, null, eventManager, resMgr, var.newVariable());
} catch (JSONException e) {
logger.error("condition parsing error", e);
}
// do ...
if (elseIfDo.getActions() != null && elseIfDo.getActions().getActionList() != null) {
EList<DSL_ResourceAction> actionsTrue = elseIfDo.getActions().getActionList();
parseConditionalActions(fileName, resMgr, components, var.curVariable(), true, actionsTrue);
}
}
}
// else do ...
DSL_ElseDo elseDo = eca.getElsedo();
if (elseDo != null && elseDo.getActions() != null && elseDo.getActions().getActionList() != null) {
EList<DSL_ResourceAction> actionsFalse = elseDo.getActions().getActionList();
parseConditionalActions(fileName, resMgr, components, var.curVariable(), false, actionsFalse);
}
}
/**
* Creates a JSON skeleton.
*
* @param fileName
* the file name
* @param components
* a components array
* @param finalize
* a finalize statement array
* @return the JSON skeleton
*/
private static JSONObject initFileStructure(String fileName, JSONArray components, JSONObject initialize, JSONObject finalize) {
try {
JSONObject root = new JSONObject();
JSONArray parameters = new JSONArray();
root.put("parameters", parameters);
JSONObject info = JsonUtil.createNameTypeValue("name", String.class.getName(), fileName);
parameters.put(info);
JSONObject contentValue = new JSONObject();
JSONObject content = JsonUtil.createNameTypeValue("content", JSONObject.class.getName(), contentValue);
parameters.put(content);
// initialize
contentValue.put("initialize", initialize);
// application
contentValue.put("application", components);
// finalize
contentValue.put("finalize", finalize);
return root;
} catch (JSONException e) {
logger.error("JSON init failed", e);
throw new RuntimeException("JSON init failed", e);
}
}
/**
* Creates a JSON component representing a CEP statement. This component is
* triggered by the function parameters and the output is the variable name.
*
* @param statement
* a cep statement
* @return a JSON component
*/
private static JSONObject parseCepStatement(ResourcesDeclarationManager resMgr, DSL_CEP_STATEMENT statement) {
try {
String variableName = statement.getName();
EObject operation = statement.getOperation();
if (operation instanceof DSL_CEP_AFTER) {
DSL_CEP_AFTER after = (DSL_CEP_AFTER) operation;
DSL_REF ref1 = after.getRef1().getRef();
DSL_REF ref2 = after.getRef2().getRef();
String start = buildDurationString(after.getStart());
String end = buildDurationString(after.getEnd());
ComponentBuilder cb = new ComponentBuilder("after", variableName, true, null, resMgr);
cb.event(ref1).event(ref2);
DSL_REF[] events = { ref1, ref2 };
cb.parameterEvents(events).parameter(start).parameter(end);
return cb.build();
} else if (operation instanceof DSL_CEP_BEFORE) {
DSL_CEP_BEFORE before = (DSL_CEP_BEFORE) operation;
DSL_REF ref1 = before.getRef1().getRef();
DSL_REF ref2 = before.getRef2().getRef();
String start = buildDurationString(before.getStart());
String end = buildDurationString(before.getEnd());
ComponentBuilder cb = new ComponentBuilder("before", variableName, true, null, resMgr);
cb.event(ref1).event(ref2);
DSL_REF[] events = { ref1, ref2 };
cb.parameterEvents(events).parameter(start).parameter(end);
return cb.build();
} else if (operation instanceof DSL_CEP_COINCIDE) {
DSL_CEP_COINCIDE coincide = (DSL_CEP_COINCIDE) operation;
DSL_REF ref1 = coincide.getRef1().getRef();
DSL_REF ref2 = coincide.getRef2().getRef();
String window = buildDurationString(coincide.getWindow());
ComponentBuilder cb = new ComponentBuilder("coincides", variableName, true, null, resMgr);
cb.event(ref1).event(ref2);
DSL_REF[] events = { ref1, ref2 };
cb.parameterEvents(events).parameter(window);
return cb.build();
} else if (operation instanceof DSL_CEP_MIN) {
DSL_CEP_MIN min = (DSL_CEP_MIN) operation;
DSL_REF ref = min.getRef().getRef();
String window = buildDurationString(min.getWindow());
ComponentBuilder cb = new ComponentBuilder("min", variableName, true, null, resMgr);
cb.event(ref);
cb.parameterEvent(ref).parameter(window);
return cb.build();
} else if (operation instanceof DSL_CEP_MAX) {
DSL_CEP_MAX max = (DSL_CEP_MAX) operation;
DSL_REF ref = max.getRef().getRef();
String window = buildDurationString(max.getWindow());
ComponentBuilder cb = new ComponentBuilder("max", variableName, true, null, resMgr);
cb.event(ref);
cb.parameterEvent(ref).parameter(window);
return cb.build();
} else if (operation instanceof DSL_CEP_AVG) {
DSL_CEP_AVG avg = (DSL_CEP_AVG) operation;
DSL_REF ref = avg.getRef().getRef();
String window = buildDurationString(avg.getWindow());
ComponentBuilder cb = new ComponentBuilder("avg", variableName, true, null, resMgr);
cb.event(ref);
cb.parameterEvent(ref).parameter(window);
return cb.build();
} else if (operation instanceof DSL_CEP_SUM) {
DSL_CEP_SUM sum = (DSL_CEP_SUM) operation;
DSL_REF ref = sum.getRef().getRef();
String window = buildDurationString(sum.getWindow());
ComponentBuilder cb = new ComponentBuilder("sum", variableName, true, null, resMgr);
cb.event(ref);
cb.parameterEvent(ref).parameter(window);
return cb.build();
} else if (operation instanceof DSL_CEP_COUNT) {
DSL_CEP_COUNT count = (DSL_CEP_COUNT) operation;
DSL_REF ref = count.getRef().getRef();
String window = buildDurationString(count.getWindow());
ComponentBuilder cb = new ComponentBuilder("count", variableName, true, null, resMgr);
cb.event(ref);
cb.parameterEvent(ref).parameter(window);
return cb.build();
} else {
throw new RuntimeException("Unknown statement operation : should never happen");
}
} catch (JSONException e) {
logger.error("DSL_CEP_STATEMENT parsing error", e);
throw new RuntimeException("DSL_CEP_STATEMENT parsing error", e);
}
}
/**
* Parse actions which are embedded into a conditional statement. Creates
* components accordingly.
*
* @param actions
* an action list
* @param components
* the components array, to be populated
* @param triggerTrueFalse
* the conditional statement part
* @throws JSONException
*/
private static void parseConditionalActions(String fileName, ResourcesDeclarationManager resMgr, JSONArray components, Variable triggerVariable, boolean triggerTrueFalse, EList<DSL_ResourceAction> actions) throws JSONException {
for (DSL_ResourceAction action : actions) {
parseConditionalAction(fileName, resMgr, components, triggerVariable, triggerTrueFalse, action);
}
}
/**
* Parse an action which is embedded into a conditional statement. Creates
* components accordingly.
*
* @param actions
* an action
* @param components
* the components array, to be populated
* @param triggerTrueFalse
* the conditional statement part
* @throws JSONException
*/
private static void parseConditionalAction(String fileName, ResourcesDeclarationManager resMgr, JSONArray components, Variable triggerVariable, boolean triggerTrueFalse, DSL_ResourceAction action) throws JSONException {
BooleanVariableEventManager eventManager = new BooleanVariableEventManager(triggerVariable, triggerTrueFalse);
String identifier = action.getVariable();
boolean register = identifier != null;
ComponentBuilder cb = new ComponentBuilder(action.getActiontype(), identifier, register, eventManager, resMgr);
// First parameter is the resource name on which the act is applied
cb.parameter(action.getRef());
if (action.getListParam() != null && action.getListParam().getParam() != null)
{
for (DSL_Expression param : action.getListParam().getParam()) {
addParameter(fileName, param, cb, components, null, eventManager, resMgr);
}
}
components.put(cb.build());
}
/**
* Add component to handle an expression.
*
* @param expr
* The expression to be translated into components
* @param output
* The expression output variable name
* @param components
* The list of components, updated during the process
* @param triggers
* @param eventManager
* @throws JSONException
*/
private static void parseExpression(String fileName, JSONArray components, DSL_Expression expr, List<DSL_REF> triggers, EventManager eventManager, ResourcesDeclarationManager resMgr, Variable output)
throws JSONException {
// Or: left associative, priority 1
if (expr instanceof DSL_Expression_Or) {
DSL_Expression_Or or = (DSL_Expression_Or) expr;
DSL_Expression left = or.getLeft();
DSL_Expression right = or.getRight();
parseExpressionInternal(fileName, resMgr, components, "or", triggers, eventManager, output, left, right);
}
// And: left associative, priority 2
else if (expr instanceof DSL_Expression_And) {
DSL_Expression_And and = (DSL_Expression_And) expr;
DSL_Expression left = and.getLeft();
DSL_Expression right = and.getRight();
parseExpressionInternal(fileName, resMgr, components, "and", triggers, eventManager, output, left, right);
}
// different/equal: left associative, priority 3
else if (expr instanceof DSL_Expression_Diff) {
DSL_Expression_Diff diff = (DSL_Expression_Diff) expr;
DSL_Expression left = diff.getLeft();
DSL_Expression right = diff.getRight();
parseExpressionInternal(fileName, resMgr, components, "diff", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Equal) {
DSL_Expression_Equal equal = (DSL_Expression_Equal) expr;
DSL_Expression left = equal.getLeft();
DSL_Expression right = equal.getRight();
parseExpressionInternal(fileName, resMgr, components, "equal", triggers, eventManager, output, left, right);
}
// Comparisons: left associative, priority 4
else if (expr instanceof DSL_Expression_Larger) {
DSL_Expression_Larger largerThan = (DSL_Expression_Larger) expr;
DSL_Expression left = largerThan.getLeft();
DSL_Expression right = largerThan.getRight();
parseExpressionInternal(fileName, resMgr, components, "greaterThan", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Larger_Equal) {
DSL_Expression_Larger_Equal largerEqual = (DSL_Expression_Larger_Equal) expr;
DSL_Expression left = largerEqual.getLeft();
DSL_Expression right = largerEqual.getRight();
parseExpressionInternal(fileName, resMgr, components, "greaterEqual", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Smaller) {
DSL_Expression_Smaller smallerThan = (DSL_Expression_Smaller) expr;
DSL_Expression left = smallerThan.getLeft();
DSL_Expression right = smallerThan.getRight();
parseExpressionInternal(fileName, resMgr, components, "lesserThan", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Smaller_Equal) {
DSL_Expression_Smaller_Equal smallerEqual = (DSL_Expression_Smaller_Equal) expr;
DSL_Expression left = smallerEqual.getLeft();
DSL_Expression right = smallerEqual.getRight();
parseExpressionInternal(fileName, resMgr, components, "lesserEqual", triggers, eventManager, output, left, right);
}
// addition/subtraction: left associative, priority 5
else if (expr instanceof DSL_Expression_Plus) {
DSL_Expression_Plus plus = (DSL_Expression_Plus) expr;
DSL_Expression left = plus.getLeft();
DSL_Expression right = plus.getRight();
parseExpressionInternal(fileName, resMgr, components, "add", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Minus) {
DSL_Expression_Minus minus = (DSL_Expression_Minus) expr;
DSL_Expression left = minus.getLeft();
DSL_Expression right = minus.getRight();
parseExpressionInternal(fileName, resMgr, components, "sub", triggers, eventManager, output, left, right);
}
// multiplication/division/modulo, left associative, priority 6
else if (expr instanceof DSL_Expression_Multiplication) {
DSL_Expression_Multiplication multiply = (DSL_Expression_Multiplication) expr;
DSL_Expression left = multiply.getLeft();
DSL_Expression right = multiply.getRight();
parseExpressionInternal(fileName, resMgr, components, "times", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Division) {
DSL_Expression_Division divide = (DSL_Expression_Division) expr;
DSL_Expression left = divide.getLeft();
DSL_Expression right = divide.getRight();
parseExpressionInternal(fileName, resMgr, components, "div", triggers, eventManager, output, left, right);
} else if (expr instanceof DSL_Expression_Modulo) {
DSL_Expression_Modulo modulo = (DSL_Expression_Modulo) expr;
DSL_Expression left = modulo.getLeft();
DSL_Expression right = modulo.getRight();
parseExpressionInternal(fileName, resMgr, components, "mod", triggers, eventManager, output, left, right);
}
// Unary operators: right associative, priority 7
else if (expr instanceof DSL_Expression_Negate) {
DSL_Expression_Negate negate = (DSL_Expression_Negate) expr;
DSL_Expression exp = negate.getExp();
parseExpressionInternal(fileName, resMgr, components, "not", triggers, eventManager, output, exp);
}
else
throw new RuntimeException("Should never happend");
}
private static void parseExpressionInternal(String fileName, ResourcesDeclarationManager resMgr, JSONArray components, String function, List<DSL_REF> triggers, EventManager eventManager,
Variable identifier, DSL_Expression... params) throws JSONException {
ComponentBuilder cb = new ComponentBuilder(function, identifier.getShortName(), false, eventManager, resMgr);
for (DSL_Expression param : params)
addParameter(fileName, param, cb, components, triggers, eventManager, resMgr);
components.put(cb.build());
}
private static void addParameter(String fileName, DSL_Expression expr, ComponentBuilder cb, JSONArray components, List<DSL_REF> triggers, EventManager eventManager, ResourcesDeclarationManager resMgr)
throws JSONException {
if (expr instanceof DSL_Object_Number) {
DSL_Object_Number number = (DSL_Object_Number) expr;
try {
long value = number.getValue().longValueExact();
cb.parameter(value);
} catch (ArithmeticException e) {
double value = number.getValue().doubleValue();
cb.parameter(value);
}
} else if (expr instanceof DSL_Object_String) {
DSL_Object_String str = (DSL_Object_String) expr;
cb.parameter(str.getValue());
} else if (expr instanceof DSL_Object_Boolean) {
DSL_Object_Boolean bool = (DSL_Object_Boolean) expr;
cb.parameter(bool.isValue());
} else if (expr instanceof DSL_Object_Ref) {
DSL_Object_Ref objRef = (DSL_Object_Ref) expr;
DSL_REF ref = objRef.getValue();
if (triggers != null && refInList(ref, triggers)) {
cb.parameterEvent(ref);
cb.event(ref);
} else {
cb.parameter(ref);
}
} else {
Variable outputVariable = VariableGenerator.get(fileName, "var").newVariable();
cb.parameterEvent(outputVariable.getFullName());
cb.eventVariable(outputVariable.getFullName());
parseExpression(fileName, components, expr, triggers, eventManager, resMgr, outputVariable);
}
}
/**
* Checks if a reference is inside a list.
*
* @param ref
* a reference
* @param a
* list of references
* @return true if the reference is inside the list, false otherwise.
*/
private static boolean refInList(DSL_REF ref, List<DSL_REF> list) {
for (DSL_REF trigger : list)
if (trigger.getName().equals(ref.getName()))
return true;
return false;
}
/**
* Converts a cep duration into a string.
*
* @param duration
* @return a string
*/
private static String buildDurationString(DSL_CEP_DURATION duration) {
StringBuilder sb = new StringBuilder();
if (duration != null && duration.getUnits() != null) {
for (EObject unit : duration.getUnits()) {
if (unit instanceof DSL_CEP_DURATION_MIN) {
DSL_CEP_DURATION_MIN min = (DSL_CEP_DURATION_MIN) unit;
sb.append(min.getMin());
sb.append(" min ");
} else if (unit instanceof DSL_CEP_DURATION_SEC) {
DSL_CEP_DURATION_SEC sec = (DSL_CEP_DURATION_SEC) unit;
sb.append(sec.getSec());
sb.append(" sec ");
} else {
throw new RuntimeException("Should never happen");
}
}
} else {
sb.append("0 sec");
}
return sb.toString().trim();
}
}