/**
* 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.thingml.xtext.helpers;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.sintef.thingml.constraints.ThingMLHelpers;
import org.thingml.xtext.thingML.*;
import java.util.*;
/**
* Created by ffl on 10.05.2016.
*/
public class ConfigurationHelper {
public static class MergedConfigurationCache {
static Map<Configuration, Configuration> cache = new HashMap<Configuration, Configuration>();
static Configuration getMergedConfiguration(Configuration c) {
return cache.get(c);
}
static void cacheMergedConfiguration(Configuration c, Configuration mc) {
cache.put(c, mc);
}
public static void clearCache() {
cache.clear();
}
}
/*
private static Configuration merge(Configuration self) {
if (MergedConfigurationCache.getMergedConfiguration(self) != null)
return MergedConfigurationCache.getMergedConfiguration(self);
final Configuration copy = EcoreUtil.copy(self);
final Map<String, Instance> instances = new HashMap<String, Instance>();
final List<AbstractConnector> connectors = new ArrayList<AbstractConnector>();
final Map<String, ConfigPropertyAssign> assigns = new HashMap<String, ConfigPropertyAssign>();
_merge(self, instances, connectors, assigns);
copy.getInstances().clear();
copy.getConnectors().clear();
copy.getPropassigns().clear();
copy.getInstances().addAll(instances.values());
copy.getConnectors().addAll(connectors);
copy.getPropassigns().addAll(assigns.values());
MergedConfigurationCache.cacheMergedConfiguration(self, copy);
return copy;
}
*/
private static void _merge(Configuration self, Map<String, Instance> instances, List<AbstractConnector> connectors, Map<String, ConfigPropertyAssign> assigns) {
// Add the instances of this configuration (actually a copy)
for(Instance inst : self.getInstances()) {
//final String key = prefix + "_" + inst.getName();
Instance copy = null;
if (ThingHelper.isSingleton(inst.getType())) {
// TODO: This could become slow if we have a large number of instances
List<Instance> others = new ArrayList<Instance>();
for(Instance i : instances.values()) {
if (EcoreUtil.equals(i.getType(), inst.getType())) {
others.add(i);
}
}
if (others.isEmpty()) {
copy = EcoreUtil.copy(inst);
copy.setName(inst.getName()); // no prefix needed
}
else copy = others.get(0); // There will be only one in the list
}
else {
copy = EcoreUtil.copy(inst);
copy.setName(inst.getName()); // rename the instance with the prefix
}
instances.put(inst.getName(), copy);
}
// Add the connectors
for(Connector c : ConfigurationHelper.getInternalConnectors(self)) {
Connector copy = EcoreUtil.copy(c);
// look for the instances:
Instance cli = instances.get(c.getCli().getName());
Instance srv = instances.get(c.getSrv().getName());
copy.setCli(cli);
copy.setSrv(srv);
connectors.add(copy);
}
for(ExternalConnector c : ConfigurationHelper.getExternalConnectors(self)) {
ExternalConnector copy = EcoreUtil.copy(c);
// look for the instances:
Instance cli = instances.get(c.getInst().getName());
copy.setInst(cli);
connectors.add(copy);
}
for(ConfigPropertyAssign a : self.getPropassigns()) {
ConfigPropertyAssign copy = EcoreUtil.copy(a);
String inst_name = a.getInstance().getName();
Instance inst = instances.get(inst_name);
copy.setInstance(inst);
String id = inst_name + "_" + a.getProperty().getName();
if (a.getIndex().size() > 0) { // It is an array
id += a.getIndex().get(0);
//println(id)
}
assigns.put(id, copy); // This will replace any previous initialization of the variable
}
}
public static Map<Instance, String[]> allRemoteInstances(Configuration self) {
Map<Instance, String[]> result = new HashMap<Instance, String[]>();
for (PlatformAnnotation a : self.getAnnotations()) {
if (a.getName().equals("remote")) {
final String[] regex = a.getValue().split("::");
for(Instance i : allInstances(self)) {
if (i.getName().matches(self.getName()+"_"+regex[0]) && i.getType().getName().matches(regex[1]) ) {
result.put(i, regex);
}
}
}
}
return result;
}
public static Set<Instance> allInstances(Configuration self) {
MergedConfigurationCache.clearCache();
Set<Instance> result = new HashSet<Instance>();
//result.addAll(merge(self).getInstances());
result.addAll(self.getInstances());
return result;
}
public static Set<Connector> allConnectors(Configuration self) {
Set<Connector> result = new HashSet<Connector>();
MergedConfigurationCache.clearCache();
//result.addAll(ConfigurationHelper.getInternalConnectors(merge(self)));
result.addAll(ConfigurationHelper.getInternalConnectors(self));
return result;
}
public static Set<ConfigPropertyAssign> allPropAssigns(Configuration self) {
Set<ConfigPropertyAssign> result = new HashSet<ConfigPropertyAssign>();
MergedConfigurationCache.clearCache();
//result.addAll(merge(self).getPropassigns());
result.addAll(self.getPropassigns());
return result;
}
public static Map<Port, AbstractMap.SimpleImmutableEntry<List<Message>, List<Message>>> allRemoteMessages(Configuration self) {
Map<Port, AbstractMap.SimpleImmutableEntry<List<Message>, List<Message>>> result = new HashMap<Port, AbstractMap.SimpleImmutableEntry<List<Message>, List<Message>>>();
for(Map.Entry<Instance, String[]> entry : allRemoteInstances(self).entrySet()) {
for(Port p : entry.getKey().getType().getPorts()) {
if (p.getName().matches(entry.getValue()[2])) {
List<Message> send = new ArrayList<Message>();
for(Message m : p.getSends()) {
if (m.getName().matches(entry.getValue()[3])) {
send.add(m);
}
}
List<Message> rec = new ArrayList<Message>();
for(Message m : p.getReceives()) {
if (m.getName().matches(entry.getValue()[3])) {
rec.add(m);
}
}
result.put(p, new AbstractMap.SimpleImmutableEntry<List<Message>, List<Message>>(send, rec));
}
}
}
return result;
}
public static List<Thing> allThings(Configuration self) {
List<Thing> result = new ArrayList<Thing>();
for(Instance i : allInstances(self)) {
if (!result.contains(i.getType())) //TODO: maybe we should return a set instead?
result.add(i.getType());
}
return result;
}
public static Set<Message> allMessages(Configuration self) {
Set<Message> result = new HashSet<Message>();
for(Thing t : allThings(self)) {
result.addAll(ThingMLHelpers.allMessages(t));
}
return result;
}
public static List<Property> allArrays(Configuration self, Instance i) {
List<Property> result = new ArrayList<Property>();
for(Property p : ThingHelper.allPropertiesInDepth(i.getType())) {
if (p.getTypeRef().getCardinality() != null)
result.add(p);
}
return result;
}
public static List<Expression> initExpressions(Configuration self, Instance i, Property p) {
List<Expression> result = new ArrayList<Expression>();
for(AbstractMap.SimpleImmutableEntry<Property, Expression> entry : initExpressionsForInstance(self, i)) {
if (EcoreUtil.equals(entry.getKey(), p)) {
result.add(entry.getValue());
}
}
return result;
}
public static List<AbstractMap.SimpleImmutableEntry<Property, Expression>> initExpressionsForInstance(Configuration self, Instance i) {
List<AbstractMap.SimpleImmutableEntry<Property, Expression>> result = new ArrayList<AbstractMap.SimpleImmutableEntry<Property, Expression>>();
//println("init instance " + i.getName + " " + i.toString)
for(Property p : ThingHelper.allPropertiesInDepth(i.getType())) {
Set<ConfigPropertyAssign> confassigns = new HashSet<ConfigPropertyAssign>();
for(ConfigPropertyAssign a : allPropAssigns(self)) {
if (EcoreUtil.equals(a.getInstance(), i) && EcoreUtil.equals(a.getProperty(), p)) {
confassigns.add(a);
}
}
if (confassigns.size() > 0) { // There is an assignment for this property
result.add(new AbstractMap.SimpleImmutableEntry<Property, Expression>(p, ((ConfigPropertyAssign)confassigns.toArray()[0]).getInit()));
}
else {
result.add(new AbstractMap.SimpleImmutableEntry<Property, Expression>(p, ThingHelper.initExpression(i.getType(), p)));
}
}
return result;
}
public static Map<Property, List<AbstractMap.SimpleImmutableEntry<Expression, Expression>>> initExpressionsForInstanceArrays(Configuration self, Instance i) {
Map<Property, List<AbstractMap.SimpleImmutableEntry<Expression, Expression>>> result = new HashMap<Property, List<AbstractMap.SimpleImmutableEntry<Expression, Expression>>>();
for(Property p : allArrays(self, i)) {
// look for assignements in the things:
for(PropertyAssign a : ThingHelper.initExpressionsForArray(i.getType(), p)) {
initExpressionsForInstanceArraysHelper(self, result, "in thing " + ((Thing)a.eContainer()).getName(), p, a.getIndex().size(), (Expression)a.getIndex().toArray()[0], a.getInit());
}
for(ConfigPropertyAssign a : allPropAssigns(self)) {
if(EcoreUtil.equals(a.getProperty(), p)) {
initExpressionsForInstanceArraysHelper(self, result, "in instance " + i.getName(), p, a.getIndex().size(), (Expression) a.getIndex().toArray()[0], a.getInit());
}
}
}
return result;
}
private static void initExpressionsForInstanceArraysHelper(Configuration self, Map<Property, List<AbstractMap.SimpleImmutableEntry<Expression, Expression>>> result, String errMsg, Property p, int size, Expression index, Expression init) {
List<AbstractMap.SimpleImmutableEntry<Expression, Expression>> assigns = result.get(p);
if (assigns == null) {
assigns = new ArrayList<AbstractMap.SimpleImmutableEntry<Expression, Expression>>();
result.put(p, assigns);
}
if (size == 1) {
assigns.add(new AbstractMap.SimpleImmutableEntry<Expression, Expression>(index, init));
//result.put(p, new AbstractMap.SimpleImmutableEntry<Expression, Expression>(index, init));
}
else if (size == 0)
System.err.println("ERROR: Malformed array initialization for property " + p.getName() + " " + errMsg + ". No initialization is possible!");
else {
System.err.println("WARNING: Malformed array initialization for property " + p.getName() + " " + errMsg +". Multiple initializations are possible! We're going to take one among them...");
assigns.add(new AbstractMap.SimpleImmutableEntry<Expression, Expression>(index, init));
//result.put(p, new AbstractMap.SimpleImmutableEntry<Expression, Expression>(index, init));
}
}
/**
Returns the set of destination for messages sent through the port p
For each outgoing message the results gives the list of destinations
sorted by source instance as a list of target instances+port
message* -> source instance* -> (target instance, port)*
TODO: WTF?! We need to return a proper structure that one can exploit intuitively, not that mess of Map<Message, ...>!!!
*/
public static Map<Message, Map<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>>> allMessageDispatch(Configuration self, Thing t, Port p) {
Map<Message, Map<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>>> result = new HashMap<Message, Map<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>>>();
for(Instance i : allInstances(self)) {
if (EcoreUtil.equals(i.getType(), t)) {
for(Connector c : allConnectors(self)) {
if(EcoreUtil.equals(c.getCli(), i) && EcoreUtil.equals(c.getRequired(), p)) {
for(Message m : p.getSends()) {
MSGLOOP: for(Message m2 : c.getProvided().getReceives()) { //TODO: we should implement a derived property on Thing to compute input and output messages, to avoid duplicating code (see below)
if (EcoreUtil.equals(m, m2)) {
Map<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>> mtable = result.get(m);
if (mtable == null) {
mtable = new HashMap<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>>();
result.put(m, mtable);
}
List<AbstractMap.SimpleImmutableEntry<Instance, Port>> itable = mtable.get(i);
if (itable == null) {
itable = new ArrayList<AbstractMap.SimpleImmutableEntry<Instance, Port>>();
mtable.put(i, itable);
}
itable.add(new AbstractMap.SimpleImmutableEntry<Instance, Port>(c.getSrv(), c.getProvided()));
//break MSGLOOP;
}
}
}
}
}
for(Connector c : allConnectors(self)) {
if(EcoreUtil.equals(c.getSrv(), i) && EcoreUtil.equals(c.getProvided(), p)) {
for(Message m : p.getSends()) {
MSGLOOP: for(Message m2 : c.getRequired().getReceives()) { //TODO: remove duplicated code
if (EcoreUtil.equals(m, m2)) {
Map<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>> mtable = result.get(m);
if (mtable == null) {
mtable = new HashMap<Instance, List<AbstractMap.SimpleImmutableEntry<Instance, Port>>>();
result.put(m, mtable);
}
List<AbstractMap.SimpleImmutableEntry<Instance, Port>> itable = mtable.get(i);
if (itable == null) {
itable = new ArrayList<AbstractMap.SimpleImmutableEntry<Instance, Port>>();
mtable.put(i, itable);
}
itable.add(new AbstractMap.SimpleImmutableEntry<Instance, Port>(c.getCli(), c.getRequired()));
//break MSGLOOP;
}
}
}
}
}
}
}
return result;
}
public static List<Connector> getInternalConnectors(Configuration self) {
List<Connector> result = new ArrayList<Connector>();
for(AbstractConnector c : self.getConnectors()) {
if (c instanceof Connector) {
result.add((Connector)c);
}
}
return result;
}
public static List<ExternalConnector> getExternalConnectors(Configuration self) {
List<ExternalConnector> result = new ArrayList<ExternalConnector>();
for(AbstractConnector c : self.getConnectors()) {
if (c instanceof ExternalConnector) {
result.add((ExternalConnector)c);
}
}
return result;
}
public static List<Instance> getClients(Configuration self, Instance i) {
List<Instance> result = new ArrayList<Instance>();
for(Connector c : allConnectors(self)) {
if (EcoreUtil.equals(c.getSrv(), i)) {
result.add(c.getCli());
}
}
return result;
}
public static List<Instance> getServers(Configuration self, Instance i) {
List<Instance> result = new ArrayList<Instance>();
for(Connector c : allConnectors(self)) {
if (EcoreUtil.equals(c.getCli(), i)) {
result.add(c.getSrv());
}
}
return result;
}
public static Map<Instance, List<Port>> danglingPorts(Configuration self) {
Map<Instance, List<Port>> result = new HashMap<Instance, List<Port>>();
for(Instance i : allInstances(self)) {
List<Port> ports = new ArrayList<Port>();
for (Port p : ThingMLHelpers.allPorts(i.getType())) {
boolean connected = false;
for(Connector c : allConnectors(self)) {
if ((EcoreUtil.equals(c.getCli(), i) && EcoreUtil.equals(c.getRequired(), p)) || (EcoreUtil.equals(c.getProvided(), p) && EcoreUtil.equals(c.getSrv(), i))) {
connected = true;
break;
}
}
for(ExternalConnector c : getExternalConnectors(self)) {
//System.out.println("External connector " + c.getInst().getInstance().qname("_") + "." + c.getPort().getName() + "? " + i.getName() + "." + p.getName());
if (EcoreUtil.equals(c.getInst(), i) && EcoreUtil.equals(c.getPort(), p)) {
System.out.println("\tis connected to " + i.getName() + "." + p.getName());
connected = true;
break;
}
}
if (!connected && !(p instanceof InternalPort)) {
ports.add(p);
}
}
result.put(i, ports);
}
return result;
}
public static Map<Instance, List<InternalPort>> allInternalPorts(Configuration self) {
Map<Instance, List<InternalPort>> result = new HashMap<Instance, List<InternalPort>>();
for(Instance i : allInstances(self)) {
List<InternalPort> iports = new ArrayList<InternalPort>();
for(Port p : ThingMLHelpers.allPorts(i.getType())) {
if (p instanceof InternalPort) {
InternalPort iport = (InternalPort) p;
iports.add(iport);
}
}
if(!iports.isEmpty()) {
result.put(i, iports);
}
}
return result;
}
private static List<Instance> isRequiredBy(Configuration self, Instance cur, List<Connector> Cos, List<Instance> Instances) {
List<Instance> res = new LinkedList<Instance>();
//List<Connector> toBeRemoved = new LinkedList<Connector>();
Instance needed;
for (Connector co : Cos) {
if(co.getCli().getName().compareTo(cur.getName()) == 0) {
needed = co.getSrv();
for(Instance inst : Instances) {
if(inst.getName().compareTo(needed.getName()) == 0) {
Instances.remove(inst);
//Cos.remove(co);
res.addAll(0, isRequiredBy(self, inst,Cos,Instances));
res.add(0, inst);
break;
}
}
}
}
return res;
}
public static List<Instance> orderInstanceInit(Configuration self) {
List<Instance> Instances = new LinkedList<Instance>(allInstances(self));
List<Connector> Cos = new LinkedList<Connector>(allConnectors(self));
List<Instance> res = new LinkedList<Instance>();
Instance cur;
while(!Instances.isEmpty()) {
cur = Instances.get(0);
Instances.remove(cur);
res.addAll(0, isRequiredBy(self, cur, Cos, Instances));
res.add(0, cur);
}
return res;
}
}