/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. */ package org.sintef.thingml.helpers; import org.eclipse.emf.ecore.util.EcoreUtil; import org.sintef.thingml.*; import org.sintef.thingml.constraints.ThingMLHelpers; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.HashSet; import java.util.Set; /** * Created by ffl on 10.05.2016. */ public class ThingHelper { public static boolean hasSession(Thing self) { for(StateMachine sm : ThingMLHelpers.allStateMachines(self)) { for(State s : StateHelper.allStatesIncludingSessions(sm)) { if (s instanceof Session) return true; } } return false; } public static boolean isSingleton(Thing self) { return AnnotatedElementHelper.isDefined(self, "singleton", "true"); } public static List<Transition> allTransitionsWithAction(Thing self) { //var result = new ArrayList[Handler]() final List<Transition> result = new ArrayList<Transition>(); for(StateMachine sm : self.getBehaviour()) { for(State s : StateHelper.allStates(sm)) { for(Transition o : s.getOutgoing()) { if (o.getAction() != null) { result.add(o); } } } } return result; } public static List<InternalTransition> allInternalTransitionsWithAction(Thing self) { //var result = new ArrayList[Handler]() final List<InternalTransition> result = new ArrayList<InternalTransition>(); for(StateMachine sm : self.getBehaviour()) { for(State s : StateHelper.allStates(sm)) { for(InternalTransition o : s.getInternal()) { if (o.getAction() != null) { result.add(o); } } } } return result; } public static List<Property> allPropertiesInDepth(Thing self) { List<Property> result = ThingMLHelpers.allProperties(self); for(StateMachine sm : ThingMLHelpers.allStateMachines(self)) { result.addAll(CompositeStateHelper.allContainedProperties(sm)); } return result; } public static List<Property> allUsedProperties(Thing self) { List<Property> result = new ArrayList<>(); for(Property p : allPropertiesInDepth(self)) { for (Action a : ActionHelper.getAllActions(self, VariableAssignment.class)) { if (EcoreUtil.equals(p, ((VariableAssignment)a).getProperty())) { boolean isPresent = false; for(Property pr : result) { if (EcoreUtil.equals(p, pr)) { isPresent = true; break; } } if (!isPresent) result.add(p); break; } } for (Expression e : ThingMLHelpers.getAllExpressions(self, PropertyReference.class)) { if (EcoreUtil.equals(p, ((PropertyReference)e).getProperty())) { boolean isPresent = false; for(Property pr : result) { if (EcoreUtil.equals(p, pr)) { isPresent = true; break; } } if (!isPresent) result.add(p); break; } } } return result; } public static Expression initExpression(Thing self, Property p) { if (ThingMLHelpers.allProperties(self).contains(p)) { // It is a property of the thing List<PropertyAssign> assigns = new ArrayList<PropertyAssign>(); for (PropertyAssign e : self.getAssign()) { if (e.getProperty().equals(p)) assigns.add(e); } // If the expression is defined locally return the init expression if (self.getProperties().contains(p)) { if (assigns.size() > 0) System.out.println("Error: Thing " + self.getName() + " cannot redefine initial value for property " + p.getName()); return p.getInit(); } if (assigns.size() > 1) System.out.println("Error: Thing " + self.getName() + " contains several assignments for property " + p.getName()); if (assigns.size() == 1) { return assigns.get(0).getInit(); } List<Thing> imports = new ArrayList<Thing>(); for (Thing t : self.getIncludes()) { if (ThingMLHelpers.allProperties(t).contains(p)) { imports.add(t); } } // imports cannot be empty since the property must be defined in a imported thing if (imports.size() > 1) System.out.println("Warning: Thing " + self.getName() + " gets property " + p.getName() + " from several paths, it should define its initial value"); return ThingHelper.initExpression(imports.get(0), p); } else { // It is a property of a state machine return p.getInit(); } } public static List<PropertyAssign> initExpressionsForArray(Thing self, Property p) { List<PropertyAssign> result = new ArrayList<PropertyAssign>(); if (ThingMLHelpers.allProperties(self).contains(p)) { // It is a property of the thing // collect assignment in the imported things first: for (Thing t : self.getIncludes()) { if (ThingMLHelpers.allProperties(t).contains(p)) result.addAll(ThingHelper.initExpressionsForArray(t,p)); } // collect assignments in this thing List<PropertyAssign> assigns = null; for(PropertyAssign pa : self.getAssign()) { if (pa.getProperty().equals(p)) result.add(pa); } } else { // It is a property of a state machine // No way to initialize arrays in state machines (so far) } return result; } /** * For each port, returns the list of the messages that are actually sent * @param self * @return */ public static Map<Port, List<Message>> allSentMessages(Thing self) { Map<Port, List<Message>> result = new HashMap<>(); for(Port p : ThingMLHelpers.allPorts(self)) { if (p instanceof InternalPort) { List<Message> msgs = new ArrayList<>(); msgs.addAll(p.getSends()); result.put(p, msgs); } else { for (Message m : p.getSends()) { for (Action b : ActionHelper.getAllActions(self, SendAction.class)) { SendAction sa = (SendAction) b; if (EcoreUtil.equals(sa.getPort(), p) && EcoreUtil.equals(sa.getMessage(), m)) { List<Message> msgs = result.get(p); if (msgs == null) { msgs = new ArrayList<>(); } msgs.add(m); result.put(p, msgs); break; } } } } } return result; } /** * Returns a list of all the types that is used in a thing * @param self * @return */ public static Set<Type> allUsedTypes(Thing self) { //TODO: Optimise for only Types that are actually used List<Type> list = new ArrayList<Type>(); // Types for all properties (things or state machines) for(Property p : ThingHelper.allPropertiesInDepth(self)) { list.add(p.getType()); } // Types for all messages for(Message m : ThingMLHelpers.allMessages(self)) { for(Parameter p : m.getParameters()) { list.add(p.getType()); } } // Types for all variables for (Variable v : ThingMLHelpers.allVariables(self)) { list.add(v.getType()); } // Types for all functions for (Function f : ThingMLHelpers.allFunctions(self)) { for (Parameter p : f.getParameters()) { list.add(p.getType()); } } // Make sure we only have one of each type in the resulting set Set<Type> result = new HashSet<Type>(); for (Type tl : list) { boolean found = false; for (Type ts : result) { if (EcoreUtil.equals(tl,ts)) { found = true; break; } } if (!found) { result.add(tl); } } return result; } }