/******************************************************************************* * Copyright (c) 2010 protos software gmbh (http://www.protos.de). * 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: * Thomas Schuetz and Henrik Rentz-Reichert (initial contribution) * *******************************************************************************/ package org.eclipse.etrice.core.validation; import java.util.ArrayList; import org.eclipse.etrice.core.room.ActorClass; import org.eclipse.etrice.core.room.ActorContainerClass; import org.eclipse.etrice.core.room.ActorContainerRef; import org.eclipse.etrice.core.room.ActorRef; import org.eclipse.etrice.core.room.Binding; import org.eclipse.etrice.core.room.BindingEndPoint; import org.eclipse.etrice.core.room.DataClass; import org.eclipse.etrice.core.room.EntryPoint; import org.eclipse.etrice.core.room.ExitPoint; import org.eclipse.etrice.core.room.ExternalPort; import org.eclipse.etrice.core.room.InitialTransition; import org.eclipse.etrice.core.room.InterfaceItem; import org.eclipse.etrice.core.room.LayerConnection; import org.eclipse.etrice.core.room.NonInitialTransition; import org.eclipse.etrice.core.room.Port; import org.eclipse.etrice.core.room.ProtocolClass; import org.eclipse.etrice.core.room.RefSAPoint; import org.eclipse.etrice.core.room.RelaySAPoint; import org.eclipse.etrice.core.room.SPPRef; import org.eclipse.etrice.core.room.ServiceImplementation; import org.eclipse.etrice.core.room.State; import org.eclipse.etrice.core.room.StateGraph; import org.eclipse.etrice.core.room.StructureClass; import org.eclipse.etrice.core.room.SubStateTrPointTerminal; import org.eclipse.etrice.core.room.TrPoint; import org.eclipse.etrice.core.room.TrPointTerminal; import org.eclipse.etrice.core.room.Transition; import org.eclipse.etrice.core.room.TransitionPoint; import org.eclipse.etrice.core.room.TransitionTerminal; public class ValidationUtil { public static class Result { private boolean ok; private String msg; static Result ok() { return new Result(true, ""); } static Result error(String msg) { return new Result(false, msg); } private Result(boolean ok, String msg) { this.ok = ok; this.msg = msg; } public boolean isOk() { return ok; } public String getMsg() { return msg; } } /** * check whether dc1 is super type of dc2 * @param dc1 * @param dc2 * @return <code>true</code> if dc1 or one of its base types is identical to dc2 */ public static boolean isKindOf(DataClass dc1, DataClass dc2) { if (dc2==null) return false; while (dc1!=null) { if (dc2==dc1) return true; dc1 = dc1.getBase(); } return false; } /** * check whether dc1 is base class of dc2 * @param dc1 * @param dc2 * @return <code>true</code> if dc1 is base class of dc2 */ public static boolean isBaseOf(DataClass dc1, DataClass dc2) { return isKindOf(dc2.getBase(), dc1); } /** * check whether pc1 is super type of pc2 * @param pc1 * @param pc2 * @return <code>true</code> if pc1 or one of its base types is identical to pc2 */ public static boolean isKindOf(ProtocolClass pc1, ProtocolClass pc2) { if (pc2==null) return false; while (pc1!=null) { if (pc2==pc1) return true; pc1 = pc1.getBase(); } return false; } /** * check whether pc1 is base class of pc2 * @param pc1 * @param pc2 * @return <code>true</code> if pc1 is base class of pc2 */ public static boolean isBaseOf(ProtocolClass pc1, ProtocolClass pc2) { return isKindOf(pc2.getBase(), pc1); } /** * check whether ac1 is super type of ac2 * @param ac1 * @param ac2 * @return <code>true</code> if ac1 or one of its base types is identical to ac2 */ public static boolean isKindOf(ActorClass ac1, ActorClass ac2) { if (ac2==null) return false; while (ac1!=null) { if (ac2==ac1) return true; ac1 = ac1.getBase(); } return false; } /** * check whether ac1 is base class of ac2 * @param ac1 * @param ac2 * @return <code>true</code> if ac1 is base class of ac2 */ public static boolean isBaseOf(ActorClass ac1, ActorClass ac2) { return isKindOf(ac2.getBase(), ac1); } /** * check if ref recursively is referencing ac * @param ref * @param ac * @return <code>true</code> if ref recursively is referencing ac */ public static boolean isReferencing(ActorClass ref, ActorClass ac) { if (isKindOf(ref,ac)) return true; for (ActorRef ar : ref.getActorRefs()) { if (isKindOf(ar.getType(), ac)) return true; else if (isReferencing(ar.getType(), ac)) return true; } return false; } /** * returns true if this is a relay port * * @param port * @return true if relay port */ public static boolean isRelay(Port port) { ActorContainerClass acc = (ActorContainerClass) port.eContainer(); if (acc instanceof ActorClass) { if (((ActorClass)acc).getIfPorts().contains(port)) { for (ExternalPort xp : ((ActorClass)acc).getExtPorts()) { if (xp.getIfport()==port) return false; } return true; } return false; } else return true; } /** * returns true if this port is connectable inside its parent, i.e. an internal end port or a relay port * * @param port * @return ok if connectable */ public static Result isConnectable(Port port, ActorContainerRef ref, StructureClass acc) { return isConnectable(port, ref, acc, null); } public static Result isConnectable(Port port, ActorContainerRef ref, StructureClass acc, Binding exclude) { if (port.getMultiplicity()==1 && isConnected(port, ref, acc, exclude)) return Result.error("port with multiplicity 1 is already connected"); if (acc instanceof ActorClass) { for (ExternalPort xp : ((ActorClass)acc).getExtPorts()) { if (xp.getIfport()==port) return Result.error("external end ports must not be connected"); } return Result.ok(); } else return Result.ok(); } public static Result isValid(Binding bind) { return isConnectable(bind.getEndpoint1().getPort(), bind.getEndpoint1().getActorRef(), bind.getEndpoint2().getPort(), bind.getEndpoint2().getActorRef(), (StructureClass)bind.eContainer(), bind); } public static Result isConnectable(BindingEndPoint ep1, BindingEndPoint ep2, StructureClass sc) { return isConnectable(ep1.getPort(), ep1.getActorRef(), ep2.getPort(), ep2.getActorRef(), sc); } public static Result isConnectable(Port p1, ActorContainerRef ref1, Port p2, ActorContainerRef ref2, StructureClass sc) { return isConnectable(p1, ref1, p2, ref2, sc, null); } public static Result isConnectable(Port p1, ActorContainerRef ref1, Port p2, ActorContainerRef ref2, StructureClass sc, Binding exclude) { if (p1==p2) return Result.error("no self connection allowed, ports are indentical"); if (p1.getProtocol()!=p2.getProtocol()) return Result.error("protocols don't match"); if (ref1==null && ref2==null) return Result.error("cannot connect two local ports"); if (ref1!=null && ref2!=null) { if (ref1==ref2) return Result.error("ports of one ref must not be connected"); // both ports are on references if (p1.isConjugated()==p2.isConjugated()) return Result.error("connected sub component ports must be conjugated to each other"); Result result = isConnectable(p1, ref1, sc, exclude); if (!result.isOk()) return result; result = isConnectable(p2, ref2, sc, exclude); if (!result.isOk()) return result; } else { // one port is an internal end port or a relay port Port local = ref1==null? p1:p2; Port sub = ref1!=null? p1:p2; ActorContainerRef ref = ref1!=null? ref1:ref2; ActorContainerClass acc = (ActorContainerClass) ref.eContainer(); if (isRelay(local)) { if (local.isConjugated()!=sub.isConjugated()) return Result.error("connected relay port must have same direction"); // both must be replicated or both must be not if (local.getMultiplicity()>1 && sub.getMultiplicity()==1) return Result.error("connected relay port must match replication type of peer"); if (local.getMultiplicity()==1 && sub.getMultiplicity()>1) return Result.error("connected relay port must match replication type of peer"); Result result = isConnectable(local, null, acc, exclude); if (!result.isOk()) return result; result = isConnectable(sub, ref, acc, exclude); if (!result.isOk()) return result; } else { // local port must be an internal end port if (local.isConjugated()==sub.isConjugated()) return Result.error("internal end port must have opposite direction"); if (local.getMultiplicity()>1 && sub.getMultiplicity()>1) return Result.error("not both ports can be replicated"); } } return Result.ok(); } public static boolean isConnected(Port port, ActorContainerRef ref, StructureClass sc) { return isConnected(port, ref, sc, null); } public static boolean isConnected(Port port, ActorContainerRef ref, StructureClass sc, Binding exclude) { for (Binding bind : sc.getBindings()) { if (bind!=exclude) { if (isEndpoint(bind.getEndpoint1(), port, ref)) return true; if (isEndpoint(bind.getEndpoint2(), port, ref)) return true; } } if (sc instanceof ActorClass) { if (((ActorClass)sc).getBase()!=null) return isConnected(port, ref, ((ActorClass)sc).getBase(), exclude); } return false; } private static boolean isEndpoint(BindingEndPoint ep, Port port, ActorContainerRef ref) { return ep.getActorRef()==ref && ep.getPort()==port; } public static boolean isRelay(SPPRef spp) { ActorContainerClass acc = (ActorContainerClass) spp.eContainer(); if (acc instanceof ActorClass) { ActorClass ac = (ActorClass) acc; for (ServiceImplementation svc : ac.getServiceImplementations()) { if (svc.getSpp()==spp) return false; } } return true; } public static Result isValid(LayerConnection lc) { if (lc.getFrom() instanceof RelaySAPoint) return isConnectable(((RelaySAPoint)lc.getFrom()).getRelay(), null, lc.getTo().getService(), lc.getTo().getRef(), (StructureClass)lc.eContainer(), lc); else if (lc.getFrom() instanceof RefSAPoint) return isConnectable(null, ((RefSAPoint)lc.getFrom()).getRef(), lc.getTo().getService(), lc.getTo().getRef(), (StructureClass)lc.eContainer(), lc); else { assert(false): "unexpected sub type"; return Result.error("internal error"); } } public static Result isConnectable(SPPRef src, ActorContainerRef srcRef, SPPRef tgt, ActorContainerRef tgtRef, StructureClass ac) { return isConnectable(src, srcRef, tgt, tgtRef, ac, null); } public static Result isConnectable(SPPRef src, ActorContainerRef srcRef, SPPRef dst, ActorContainerRef dstRef, StructureClass sc, LayerConnection exclude) { if (sc==null) { return Result.error("internal error"); } if ((src==null && srcRef==null) || (src!=null && srcRef!=null)) return Result.error("source can be an own SPP _or_ a ref"); if (dst==null || dstRef==null) return Result.error("destination must be an SPP on a ref"); if (src!=null && isConnectedSrc(src, sc, exclude)) return Result.error("source SPP is already connected"); // the destination may be connected several times, so don't check this // if (isConnectedDst(dst, dstRef, sc, exclude)) // return Result.error("destination SPP is already connected"); return Result.ok(); } public static boolean isConnectableSrc(SPPRef src, ActorContainerRef ref, StructureClass sc) { return isConnectableSrc(src, ref, sc, null); } public static boolean isConnectableSrc(SPPRef src, ActorContainerRef ref, StructureClass sc, LayerConnection exclude) { if (sc==null) { return false; } if ((src==null && ref==null) || (src!=null && ref!=null)) return false; // in case of ref!=null no further checks possible // the connection is attached to an ActorContainerRef // which can be multiply connected if (src!=null) { if (isConnectedSrc(src, sc, exclude)) return false; } return true; } public static boolean isConnectedSrc(SPPRef src, StructureClass sc) { return isConnectedSrc(src, sc, null); } public static boolean isConnectedSrc(SPPRef src, StructureClass sc, LayerConnection exclude) { for (LayerConnection lc : sc.getConnections()) { if (lc!=exclude) if (lc.getFrom() instanceof RelaySAPoint) if (((RelaySAPoint)lc.getFrom()).getRelay()==src) return true; } if (sc instanceof ActorClass) { ActorClass ac = (ActorClass)sc; // check for attached services for (ServiceImplementation svc : ac.getServiceImplementations()) { if (svc.getSpp()==src) return true; } // recurse into base classes if (ac.getBase()!=null) return isConnectedSrc(src, ac.getBase(), exclude); } return false; } public static boolean isConnectableDst(SPPRef src, ActorContainerRef ref, StructureClass sc) { return isConnectableDst(src, ref, sc, null); } public static boolean isConnectableDst(SPPRef dst, ActorContainerRef ref, StructureClass sc, LayerConnection exclude) { if (sc==null) { return false; } if (dst==null || ref==null) return false; if (dst!=null) { if (isConnectedDst(dst, ref, sc, exclude)) return false; } return true; } public static boolean isConnectedDst(SPPRef src, ActorContainerRef acr, StructureClass sc) { return isConnectedDst(src, acr, sc, null); } public static boolean isConnectedDst(SPPRef src, ActorContainerRef acr, StructureClass sc, LayerConnection exclude) { for (LayerConnection lc : sc.getConnections()) { if (lc!=exclude) if (lc.getTo().getService()==src && lc.getTo().getRef()==acr) return true; } if (sc instanceof ActorClass) { if (((ActorClass)sc).getBase()!=null) return isConnectedDst(src, acr, ((ActorClass)sc).getBase(), exclude); } return false; } public static Result isConnectable(TransitionTerminal src, TransitionTerminal tgt, StateGraph sg) { return isConnectable(src, tgt, null, sg); } public static Result isConnectable(TransitionTerminal src, TransitionTerminal tgt, Transition trans, StateGraph sg) { Result result = isConnectableSrc(src, trans, sg); if (!result.isOk()) return result; if (tgt instanceof TrPointTerminal) { if (((TrPointTerminal) tgt).getTrPoint() instanceof EntryPoint) return Result.error("entry point can not be transition target"); // TransitionPoint and ExitPoint are valid destinations // ExitPoint can be multiply connected inside a state } else if (tgt instanceof SubStateTrPointTerminal) { if (((SubStateTrPointTerminal) tgt).getTrPoint() instanceof ExitPoint) return Result.error("sub state exit point can not be transition target"); // sub state EntryPoint is valid as destination for (Transition t : sg.getTransitions()) { if (t==trans) continue; if (t.getTo() instanceof SubStateTrPointTerminal) { SubStateTrPointTerminal tpt = (SubStateTrPointTerminal)t.getTo(); if (tpt.getTrPoint()==((SubStateTrPointTerminal) tgt).getTrPoint()) return Result.error("target transition point already is connected"); } } } return Result.ok(); } public static Result isConnectable(TransitionTerminal src, StateGraph sg) { return isConnectableSrc(src, null, sg); } public static Result isConnectableSrc(TransitionTerminal src, Transition trans, StateGraph sg) { if (src==null) { for (Transition t : sg.getTransitions()) { if (t==trans) continue; if (t instanceof InitialTransition) return Result.error("there already is an InitialTransition"); } } else if (src instanceof TrPointTerminal) { TrPoint srcTP = ((TrPointTerminal) src).getTrPoint(); if (srcTP instanceof ExitPoint) return Result.error("exit point can not be transition source"); // TransitionPoint and EntryPoint are valid if (srcTP instanceof EntryPoint) { for (Transition t : sg.getTransitions()) { if (t==trans) continue; if (t instanceof NonInitialTransition) { if (((NonInitialTransition) t).getFrom() instanceof TrPointTerminal) { TrPointTerminal tpt = (TrPointTerminal)((NonInitialTransition) t).getFrom(); if (tpt.getTrPoint()==srcTP) return Result.error("source transition point already is connected"); } } } } } else if (src instanceof SubStateTrPointTerminal) { if (((SubStateTrPointTerminal) src).getTrPoint() instanceof EntryPoint) return Result.error("sub state entry point can not be transition source"); // ExitPoint is valid as source for (Transition t : sg.getTransitions()) { if (t==trans) continue; if (t instanceof NonInitialTransition) { if (((NonInitialTransition) t).getFrom() instanceof SubStateTrPointTerminal) { SubStateTrPointTerminal tpt = (SubStateTrPointTerminal)((NonInitialTransition) t).getFrom(); if (tpt.getTrPoint()==((SubStateTrPointTerminal) src).getTrPoint()) return Result.error("target transition point already is connected"); } } } } return Result.ok(); } public static Result isValid(TrPoint tp) { if (tp instanceof TransitionPoint) return Result.ok(); if (tp.eContainer().eContainer() instanceof State) return Result.ok(); return Result.error("entry and exit points forbidden on top level state graph"); } public static Result isUniqueName(InterfaceItem item) { if (item.eContainer() instanceof ActorClass) { ArrayList<InterfaceItem> all = new ArrayList<InterfaceItem>(); ActorClass ac = (ActorClass) item.eContainer(); while (ac.getBase()!=null) { ac = ac.getBase(); all.addAll(ac.getIfPorts()); all.addAll(ac.getIntPorts()); all.addAll(ac.getIfSPPs()); all.addAll(ac.getStrSAPs()); } for (InterfaceItem ii : all) { if (ii!=item && ii.getName().equals(item.getName())) { return Result.error("name already used in base class "+((ActorClass)ii.eContainer()).getName()); } } } // else // we don't have to check SubSystemClasses since this is done by xtext (standard namespace) return Result.ok(); } }