blob: 66d74bc53d7baadf44d4a68221a52d8d95933012 [file] [log] [blame]
/*******************************************************************************
* 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)
* Eyrak Paen
*
*******************************************************************************/
package org.eclipse.etrice.core.validation;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.etrice.core.common.base.Annotation;
import org.eclipse.etrice.core.common.base.BasePackage;
import org.eclipse.etrice.core.common.base.Import;
import org.eclipse.etrice.core.common.base.LiteralType;
import org.eclipse.etrice.core.common.validation.ValidationHelpers;
import org.eclipse.etrice.core.common.validation.ValidationHelpers.NamedObject;
import org.eclipse.etrice.core.common.validation.ValidationHelpers.NamedObjectList;
import org.eclipse.etrice.core.fsm.fSM.ComponentCommunicationType;
import org.eclipse.etrice.core.fsm.fSM.FSMPackage;
import org.eclipse.etrice.core.fsm.fSM.MessageFromIf;
import org.eclipse.etrice.core.fsm.validation.FSMValidationUtilXtend.Result;
import org.eclipse.etrice.core.room.ActorClass;
import org.eclipse.etrice.core.room.ActorContainerClass;
import org.eclipse.etrice.core.room.ActorInstanceMapping;
import org.eclipse.etrice.core.room.ActorRef;
import org.eclipse.etrice.core.room.Attribute;
import org.eclipse.etrice.core.room.Binding;
import org.eclipse.etrice.core.room.CommunicationType;
import org.eclipse.etrice.core.room.CompoundProtocolClass;
import org.eclipse.etrice.core.room.DataClass;
import org.eclipse.etrice.core.room.EnumerationType;
import org.eclipse.etrice.core.room.ExternalPort;
import org.eclipse.etrice.core.room.InterfaceItem;
import org.eclipse.etrice.core.room.LayerConnection;
import org.eclipse.etrice.core.room.LogicalSystem;
import org.eclipse.etrice.core.room.Message;
import org.eclipse.etrice.core.room.Operation;
import org.eclipse.etrice.core.room.Port;
import org.eclipse.etrice.core.room.PortClass;
import org.eclipse.etrice.core.room.PrimitiveType;
import org.eclipse.etrice.core.room.ProtocolClass;
import org.eclipse.etrice.core.room.RefPath;
import org.eclipse.etrice.core.room.ReferenceType;
import org.eclipse.etrice.core.room.RoomAnnotationTargetEnum;
import org.eclipse.etrice.core.room.RoomClass;
import org.eclipse.etrice.core.room.RoomModel;
import org.eclipse.etrice.core.room.RoomPackage;
import org.eclipse.etrice.core.room.ServiceImplementation;
import org.eclipse.etrice.core.room.StandardOperation;
import org.eclipse.etrice.core.room.StructureClass;
import org.eclipse.etrice.core.room.SubSystemClass;
import org.eclipse.etrice.core.room.util.RoomHelpers;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.scoping.impl.ImportUriResolver;
import org.eclipse.xtext.validation.Check;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
public class RoomJavaValidator extends AbstractRoomJavaValidator {
@Inject protected RoomHelpers roomHelpers;
@Inject protected ValidationUtil validationUtil;
@Inject protected IQualifiedNameProvider fqnProvider;
/* message strings */
public static final String OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY = "optional refs have to have multiplicity any [*]";
public static final String MULTIPLICITY_ANY_REQUIRES_OPTIONAL = "multiplicity any [*] requires optional";
public static final String A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER = "a replicated port must have at most one replicated peer";
/* tags for quick fixes */
public static final String THREAD_MISSING = "RoomJavaValidator.ThreadMissing";
public static final String DUPLICATE_ACTOR_INSTANCE_MAPPING = "RoomJavaValidator.DuplicateActorInstanceMapping";
public static final String WRONG_NAMESPACE = "RoomJavaValidator.WrongNamespace";
public static final String CIRCULAR_CONTAINMENT = "RoomJavaValidator.CircularContainment";
public static final String ACTOR_REF_CHANGE_REF_TYPE_TO_FIXED_OR_MULT_TO_ANY = "RoomJavaValidator.ActorRefChangeRefTypeToFixed";
public static final String ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL = "RoomJavaValidator.ActorRefChangeRefTypeToOptional";
public static final String CHANGE_DESTRUCTOR_NAME = "RoomJavaValidator.ChangeDestructorName";
public static final String CHANGE_CONSTRUCTOR_NAME = "RoomJavaValidator.ChangeConstructorName";
public static final String INVALID_ANNOTATION_TARGET = "RoomJavaValidator.InvalidAnnotationTarget";
public static final String OPERATION_MISSING_OVERRIDE = "RoomJavaValidator.OperationMissingOverride";
public static final String OPERATION_EXTRANEOUS_OVERRIDE = "RoomJavaValidator.OperationExtraneousOverride";
@Inject ImportUriResolver importUriResolver;
@Check
public void checkImportedNamespace(Import imp) {
if (imp.getImportURI()==null)
return;
IQualifiedNameConverter nameConverter = new IQualifiedNameConverter.DefaultImpl();
QualifiedName importedFQN;
boolean isWildcard = false;
try {
QualifiedName orig = nameConverter.toQualifiedName(imp.getImportedNamespace());
isWildcard = orig.getLastSegment().equals("*");
importedFQN = (isWildcard) ? orig.skipLast(1) : orig;
} catch(IllegalArgumentException e){
return;
}
String uriString = importUriResolver.resolve(imp);
if(uriString == null) {
warning("could not load referenced model", BasePackage.Literals.IMPORT__IMPORT_URI);
return;
}
URI uri = URI.createURI(uriString);
ResourceSet rs = imp.eResource().getResourceSet();
RoomModel importedModel;
try {
Resource res = rs.getResource(uri, true);
if (res==null)
return;
if (res.getContents().isEmpty()) {
error("referenced model is empty", BasePackage.Literals.IMPORT__IMPORT_URI);
return;
}
if (!(res.getContents().get(0) instanceof RoomModel)) {
if (uri.lastSegment().endsWith(".room"))
error("referenced model is no ROOM model (but has .room extension)", BasePackage.Literals.IMPORT__IMPORT_URI);
else
warning("referenced model is no ROOM model", BasePackage.Literals.IMPORT__IMPORT_URI);
return;
}
importedModel = (RoomModel) res.getContents().get(0);
}
catch (RuntimeException re) {
warning("could not load referenced model", BasePackage.Literals.IMPORT__IMPORT_URI);
return;
}
final IResourceServiceProvider resourceServiceProvider = IResourceServiceProvider.Registry.INSTANCE.getResourceServiceProvider(uri);
if (resourceServiceProvider==null)
return;
IResourceDescription.Manager manager = resourceServiceProvider.getResourceDescriptionManager();
IResourceDescription description = manager.getResourceDescription(importedModel.eResource());
// TODO check for empty namespace
boolean exportedNameMatch = false;
if (description != null) {
Iterator<IEObjectDescription> iter = description.getExportedObjects().iterator();
while(iter.hasNext()){
QualifiedName exportedFQN = iter.next().getQualifiedName();
if(exportedNameMatch |= importedFQN.equals(exportedFQN))
break;
}
}
if(!exportedNameMatch)
error("no match for imported namespace", BasePackage.Literals.IMPORT__IMPORTED_NAMESPACE, WRONG_NAMESPACE, importedModel.getName()+".*");
}
@Check
public void checkActorRef(ActorRef ar) {
if (ar.eContainer() instanceof ActorClass) {
ActorClass ac = (ActorClass) ar.eContainer();
if (roomHelpers.isReferencing(ar.getType(), ac)) {
error("Actor reference is circular", RoomPackage.eINSTANCE.getActorRef_Type());
}
}
// fixed actor ref must NOT be of abstract type
if (ar.getRefType()==ReferenceType.FIXED && ar.getType().isAbstract()) {
error("A (fixed) actor reference must not be of an abstract type", null);
}
// check actor ref array upper bound
if (ar.getMultiplicity()<0) {
// multiplicity * requires optional
if (ar.getRefType()!=ReferenceType.OPTIONAL)
error(MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
RoomPackage.eINSTANCE.getActorRef_RefType(), ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL);
}
else if (ar.getMultiplicity()>1) {
// fixed multiplicity requires fixed type
if (ar.getRefType()==ReferenceType.OPTIONAL)
error(OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY,
RoomPackage.eINSTANCE.getActorRef_RefType(), ACTOR_REF_CHANGE_REF_TYPE_TO_FIXED_OR_MULT_TO_ANY, ar.getName());
}
// check actor ref array has ports with fixed multiplicity
if (ar!=null) {
ActorClass ac = ar.getType();
if (ar.getMultiplicity()>1) {
for (Port p : ac.getInterfacePorts()) {
if (p.getMultiplicity()<0) {
int idx = ((ActorContainerClass)ar.eContainer()).getActorRefs().indexOf(ar);
error("replicated actor must not have replicated port with arbitrary multiplicity", null);
}
}
}
}
}
@Check
public void checkLayerConnectiontarget(LayerConnection lc) {
if (lc.getTo().getRef() instanceof ActorRef)
if (((ActorRef)lc.getTo().getRef()).getMultiplicity()>1)
error("layer connection must not connect to replicated actor", null);
}
@Check
public void checkBaseClassesNotCircular(DataClass dc) {
if (dc==null)
return;
if (roomHelpers.isCircularClassHierarchy(dc))
error("Base classes are circular", RoomPackage.eINSTANCE.getDataClass_Base());
}
@Check
public void checkAttributeNotCircular(Attribute att) {
if (att.eContainer() instanceof ActorClass)
// no circle possible
return;
if (att.eContainer() instanceof PortClass)
// no circle possible
return;
if (!(att.eContainer() instanceof DataClass)) {
assert(false): "unexpected parent class";
return;
}
DataClass dc = (DataClass) att.eContainer();
if (roomHelpers.isCircularClassHierarchy(dc))
return;
while (dc!=null) {
if (att.getType().getType()==dc && !att.getType().isRef()) {
error(
"Attribute type must not refer to own class or a super class",
RoomPackage.Literals.ATTRIBUTE__TYPE,
CIRCULAR_CONTAINMENT,
""+att.getType().getType().getName().length()
);
break;
}
dc = dc.getBase();
}
}
@Check
public void checkAttribute(Attribute attr){
if(attr.getType() == null)
error("missing type", attr, RoomPackage.Literals.ATTRIBUTE__TYPE);
if(attr.getDefaultValueLiteral() != null)
warning("deprecated, initialize in user code or .config instead", attr, RoomPackage.Literals.ATTRIBUTE__DEFAULT_VALUE_LITERAL);
}
@Check
public void checkAttributeNoStringArray(Attribute att){
// TODO-Enum
if(!att.getType().isRef() && att.getType().getType() instanceof PrimitiveType){
PrimitiveType type = (PrimitiveType)att.getType().getType();
if(type.getName().equalsIgnoreCase("string") && att.getSize() > 0)
error("string type must have multiplicity 0", RoomPackage.Literals.ATTRIBUTE__SIZE);
}
}
@Check
public void checkBaseClassesNotCircular(ProtocolClass pc) {
if (pc==null)
return;
if (roomHelpers.isCircularClassHierarchy(pc))
error("Base classes are circular", RoomPackage.eINSTANCE.getProtocolClass_Base());
}
@Check
public void checkBaseClassesNotCircular(ActorClass ac) {
if (ac==null)
return;
if (roomHelpers.isCircularClassHierarchy(ac))
error("Base classes are circular", FSMPackage.eINSTANCE.getModelComponent_Base());
}
@Check
public void checkExecModelConsistent(ActorClass ac) {
if (roomHelpers.isCircularClassHierarchy(ac))
return;
ComponentCommunicationType commType = ac.getCommType();
switch (commType) {
case ASYNCHRONOUS:
break;
case DATA_DRIVEN:
break;
case EVENT_DRIVEN:
break;
case SYNCHRONOUS:
error("synchronous communication type not supported yet", FSMPackage.eINSTANCE.getModelComponent_CommType());
}
while (ac.getActorBase()!=null) {
ac = ac.getActorBase();
if (commType!=ac.getCommType())
error("data_driven attribute not consistent in inheritance hierarchy", FSMPackage.eINSTANCE.getModelComponent_CommType());
}
}
@Check
public void checkTopLevelRefinedStates(ActorClass ac) {
List<Result> errors = validationUtil.checkTopLevelRefinedStates(ac);
for (Result err : errors) {
error(err);
}
}
@Check
public void checkSubSystem(SubSystemClass ssc){
if (ssc.getActorRefs().isEmpty())
warning("SubSystemClass must contain at least one ActorRef", RoomPackage.eINSTANCE.getActorContainerClass_ActorRefs());
if (ssc.getThreads().isEmpty())
warning("at least one thread has to be defined", RoomPackage.Literals.SUB_SYSTEM_CLASS__THREADS, THREAD_MISSING, "LogicalThread defaultThread");
checkMappings(ssc.getActorInstanceMappings());
}
@Check
public void checkLogicalSystem(LogicalSystem ls){
if (ls.getSubSystems().isEmpty())
error("LogicalSystem must contain at least one SubSystemRef", RoomPackage.eINSTANCE.getLogicalSystem_SubSystems());
}
@Check
public void checkActorInstanceMapping(ActorInstanceMapping aim) {
ActorContainerClass root = roomHelpers.getParentContainer(aim);
if (root != null && !root.eIsProxy()) {
RefPath path = aim.getPath();
if (path != null) {
String invalidSegment = roomHelpers.checkPath(root, path);
if (invalidSegment != null)
error("no match for segment '" + invalidSegment + "'",
RoomPackage.Literals.ACTOR_INSTANCE_MAPPING__PATH);
else {
ActorRef aRef = roomHelpers.getLastActorRef(root, path);
if (aRef == null)
error("invalid actor reference",
RoomPackage.Literals.ACTOR_INSTANCE_MAPPING__PATH);
}
}
}
checkMappings(aim.getActorInstanceMappings());
}
private void checkMappings(EList<ActorInstanceMapping> actorInstanceMappings) {
HashSet<String> paths = new HashSet<String>();
for (ActorInstanceMapping aim : actorInstanceMappings) {
if (!paths.add(aim.getPath().toString())) {
EObject parent = aim.eContainer();
int idx = actorInstanceMappings.indexOf(aim);
error("duplicate mapping", parent, aim.eContainingFeature(), idx, DUPLICATE_ACTOR_INSTANCE_MAPPING);
}
}
}
@Check
public void checkPortCompatibility(Binding bind) {
Result result = validationUtil.isValid(bind);
if (!result.isOk()) {
EObject sc = bind.eContainer();
@SuppressWarnings("unchecked")
int idx = ((List<EObject>)sc.eGet(bind.eContainingFeature())).indexOf(bind);
error(result.getMsg(), sc, bind.eContainingFeature(), idx);
}
}
@Check
public void checkServiceCompatibility(LayerConnection conn) {
Result result = validationUtil.isValid(conn);
if (!result.isOk())
error(result.getMsg(), RoomPackage.eINSTANCE.getLayerConnection_From());
}
@Check
public void checkPortCommunicationCompatibility(ActorClass ac){
if(ac.getCommType() == ComponentCommunicationType.SYNCHRONOUS){
// not supported yet
return;
}
// check external ports
List<InterfaceItem> extPorts = new ArrayList<InterfaceItem>();
for(ExternalPort exPort : ac.getExternalPorts())
extPorts.add(exPort.getInterfacePort());
checkPortCommunicationCompatibility(ac, extPorts, RoomPackage.eINSTANCE.getActorClass_ExternalPorts());
// check internal ports
checkPortCommunicationCompatibility(ac, ac.getInternalPorts(), RoomPackage.eINSTANCE.getActorClass_InternalPorts());
// check SAPs
checkPortCommunicationCompatibility(ac, ac.getServiceAccessPoints(), RoomPackage.eINSTANCE.getActorClass_ServiceAccessPoints());
// check service implementations
List<InterfaceItem> serviceImpls = new ArrayList<InterfaceItem>();
for(ServiceImplementation si: ac.getServiceImplementations())
serviceImpls.add(si.getSpp());
checkPortCommunicationCompatibility(ac, serviceImpls, RoomPackage.eINSTANCE.getActorClass_ServiceImplementations());
}
private void checkPortCommunicationCompatibility(ActorClass ac, List<? extends InterfaceItem> items, EReference ref){
boolean datadriven = ac.getCommType() == ComponentCommunicationType.DATA_DRIVEN;
boolean eventdriven = ac.getCommType() == ComponentCommunicationType.EVENT_DRIVEN;
boolean async = ac.getCommType() == ComponentCommunicationType.ASYNCHRONOUS;
//boolean synchronous = ac.getCommType() == ComponentCommunicationType.SYNCHRONOUS;
for(InterfaceItem item : items){
ProtocolClass pc = roomHelpers.getProtocol(item);
if (pc!=null)
switch(pc.getCommType()){
case DATA_DRIVEN:
if(!datadriven && !async)
error("ports with datadriven protocols not allowed", ref, items.indexOf(item));
break;
case EVENT_DRIVEN:
if(!eventdriven && !async)
error("ports with eventdriven protocols not allowed", ref, items.indexOf(item));
break;
case SYNCHRONOUS:
// not supported yet
}
}
}
@Check
public void checkPort(Port port) {
if (port.getMultiplicity()==0)
error("multiplicity must not be 0", RoomPackage.eINSTANCE.getPort_Multiplicity());
if (port.getMultiplicity()<-1)
error("multiplicity must be -1 or positive", RoomPackage.eINSTANCE.getPort_Multiplicity());
if (port.getProtocol() instanceof ProtocolClass)
if (((ProtocolClass)port.getProtocol()).getCommType()==CommunicationType.DATA_DRIVEN && port.getMultiplicity()!=1)
error("multiplicity must be 1 for data driven ports", RoomPackage.eINSTANCE.getPort_Multiplicity());
}
@Check
public void checkProtocol(ProtocolClass pc) {
if (roomHelpers.isCircularClassHierarchy(pc))
return;
switch (pc.getCommType()) {
case DATA_DRIVEN:
if (pc.getBase()!=null && pc.getBase().getCommType()!=CommunicationType.DATA_DRIVEN)
error("super protocol has to have same communication type", RoomPackage.Literals.PROTOCOL_CLASS__COMM_TYPE);
if (roomHelpers.getAllMessages(pc, true).isEmpty())
error("at least one incoming message must be defined", RoomPackage.Literals.PROTOCOL_CLASS__INCOMING_MESSAGES);
if (!roomHelpers.getAllMessages(pc, false).isEmpty())
error("data driven protocols must have no outgoing messages", RoomPackage.Literals.PROTOCOL_CLASS__OUTGOING_MESSAGES);
break;
case EVENT_DRIVEN:
if (pc.getBase()!=null && pc.getBase().getCommType()!=CommunicationType.EVENT_DRIVEN)
error("super protocol has to have same communication type", RoomPackage.Literals.PROTOCOL_CLASS__COMM_TYPE);
if (roomHelpers.getAllMessages(pc, true).isEmpty()
&& roomHelpers.getAllMessages(pc, false).isEmpty()
&& roomHelpers.getAllOperations(pc, true).isEmpty()
&& roomHelpers.getAllOperations(pc, false).isEmpty())
error("at least one message/port operation (incoming or outgoing) must be defined", RoomPackage.Literals.PROTOCOL_CLASS__INCOMING_MESSAGES);
break;
case SYNCHRONOUS:
error("synchronous communication type not supported yet", RoomPackage.Literals.PROTOCOL_CLASS__COMM_TYPE);
break;
default:
}
checkDuplicates(pc, true);
checkDuplicates(pc, false);
if (pc.getBase()!=null) {
// derived protocol
if (pc.getIncomingMessages().size()>0 && pc.getOutgoingMessages().size()>0) {
warning("a derived protocol should add either incoming or outgoing messages, not both", RoomPackage.Literals.PROTOCOL_CLASS__OUTGOING_MESSAGES);
}
}
}
/**
* checks duplicates in the set of messages and operations including inherited items
* per direction
*
* @param pc the protocol class
* @param incoming a flag giving the direction to be checked
*/
private void checkDuplicates(ProtocolClass pc, boolean incoming) {
NamedObjectList all = new NamedObjectList();
all.addAll(roomHelpers.getAllMessages(pc, incoming), RoomPackage.Literals.MESSAGE__NAME);
all.addAll(roomHelpers.getAllOperations(pc, incoming), RoomPackage.Literals.OPERATION__NAME);
Iterable<NamedObject> duplicates = ValidationHelpers.inSameResource(ValidationHelpers.removeUniques(all), pc.eResource());
for (NamedObject dupl : duplicates) {
error("duplicate message name", dupl.getObj(), dupl.getFeature());
}
}
/**
* {@link #checkInheritedNames(DataClass)}
*/
@Check
public void checkInheritedNames(ActorClass ac){
final Set<? extends EStructuralFeature> overrideFeatures = Sets.newHashSet(
RoomPackage.Literals.ACTOR_CLASS__OPERATIONS, RoomPackage.Literals.ACTOR_CLASS__STRUCTORS, FSMPackage.Literals.MODEL_COMPONENT__STATE_MACHINE);
final Map<String, EObject> superNames = Maps.newHashMap();
// gather all named elements of super classes
ValidationHelpers.saveRecursiveVisitor(ac.getActorBase(), new Function<ActorClass, ActorClass>(){
@Override
public ActorClass apply(ActorClass input) {
for(EObject containee : input.eContents()){
QualifiedName qualifiedName = fqnProvider.apply(containee);
String name = (qualifiedName != null)?qualifiedName.getLastSegment():null;
if(name != null && !superNames.containsKey(name))
superNames.put(name, containee);
}
return input.getActorBase();
}});
for(EObject containee : ac.eContents()){
QualifiedName qualifiedName = fqnProvider.apply(containee);
String name = (qualifiedName != null)?qualifiedName.getLastSegment():null;
if(name == null)
continue;
EObject existing = superNames.get(name);
if(existing == null)
continue;
if(overrideFeatures.contains(containee.eContainingFeature()) && containee.eContainingFeature() == existing.eContainingFeature())
continue;
if(superNames.containsKey(fqnProvider.apply(existing).getLastSegment()))
issueInheritedNameError(containee, existing);
}
}
/**
* Copy&Paste of {@link #checkInheritedNames(ActorClass)}
*/
@Check
public void checkInheritedNames(DataClass dc){
final Set<? extends EStructuralFeature> overrideFeatures = Sets.newHashSet(
RoomPackage.Literals.DATA_CLASS__OPERATIONS, RoomPackage.Literals.DATA_CLASS__STRUCTORS);
final Map<String, EObject> superNames = Maps.newHashMap();
// gather all named elements of super classes
ValidationHelpers.saveRecursiveVisitor(dc.getBase(), new Function<DataClass, DataClass>(){
@Override
public DataClass apply(DataClass input) {
for(EObject containee : input.eContents()){
QualifiedName qualifiedName = fqnProvider.apply(containee);
String name = (qualifiedName != null)?qualifiedName.getLastSegment():null;
if(name != null && !superNames.containsKey(name))
superNames.put(name, containee);
}
return input.getBase();
}});
for(EObject containee : dc.eContents()){
QualifiedName qualifiedName = fqnProvider.apply(containee);
String name = (qualifiedName != null)?qualifiedName.getLastSegment():null;
if(name == null)
continue;
EObject existing = superNames.get(name);
if(existing == null)
continue;
if(overrideFeatures.contains(containee.eContainingFeature()) && containee.eContainingFeature() == existing.eContainingFeature())
continue;
if(superNames.containsKey(fqnProvider.apply(existing).getLastSegment()))
issueInheritedNameError(containee, existing);
}
}
/**
* Assuming target and source have the feature 'name', name not null, contained in a {@link RoomClass}
*/
private void issueInheritedNameError(EObject target, EObject source){
String targetName = fqnProvider.apply(target).getLastSegment();
String sourceName = fqnProvider.apply(source).getLastSegment();
String sourceType = source.eClass().getName();
String sourceOwner = roomHelpers.getRoomClass(source).getName();
error("name '" + targetName + "' is already assigned to " + sourceType + sourceOwner+"."+sourceName, target, target.eClass().getEStructuralFeature("name"));
}
@Check
public void checkMessage(Message msg) {
ProtocolClass pc = (ProtocolClass) msg.eContainer();
if (pc.getCommType()==CommunicationType.DATA_DRIVEN)
if (msg.getData()==null)
error("Messages of data driven protocols must carry data", RoomPackage.Literals.MESSAGE__DATA);
}
@Check
public void checkMessageFromIf(MessageFromIf mfi){
if(mfi.getFrom() != null){
if(roomHelpers.getProtocol((InterfaceItem)mfi.getFrom()).getCommType() != CommunicationType.EVENT_DRIVEN)
error("port must have event driven protocol", mfi, FSMPackage.eINSTANCE.getMessageFromIf_From());
}
}
@Check
public void checkDataClass(DataClass dc) {
if (dc.getAttributes().isEmpty() && dc.getBase()==null)
error("Non-derived data classes have to define at least one attribute", RoomPackage.Literals.DATA_CLASS__ATTRIBUTES);
}
@Check
public void checkReplicatedPortBindingPatterns(StructureClass sc) {
HashSet<Port> haveReplPeer = new HashSet<Port>();
for (Binding bind : sc.getBindings()) {
Port p1 = bind.getEndpoint1().getPort();
Port p2 = bind.getEndpoint2().getPort();
if (p1.getMultiplicity()!=1 && p2.getMultiplicity()!=1) {
if (!haveReplPeer.add(p1))
error(A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER,
bind,
RoomPackage.Literals.BINDING__ENDPOINT1);
if (!haveReplPeer.add(p2))
error(A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER,
bind,
RoomPackage.Literals.BINDING__ENDPOINT2);
}
}
}
@Check
public void checkPortClassContents(PortClass pc) {
if (pc.getAttributes().isEmpty() && pc.getMsgHandlers().isEmpty() && pc.getOperations().isEmpty())
error("port class must not be empty", pc, RoomPackage.Literals.PORT_CLASS__ATTRIBUTES);
}
@Check
public void checkCompoundProtocolClass(CompoundProtocolClass cpc) {
if (cpc.getSubProtocols().isEmpty())
error("no sub protocols defined", cpc, RoomPackage.Literals.COMPOUND_PROTOCOL_CLASS__SUB_PROTOCOLS);
}
@Check
public void checkAnnotationTarget(Annotation a) {
if(a.getType() == null || a.getType().eIsProxy())
return;
EObject parent = a.eContainer();
EList<String> targetList = a.getType().getTargets();
RoomAnnotationTargetEnum invalidTargetType = null;
if(parent instanceof ActorClass) {
ActorClass actorParent = (ActorClass)parent;
if(actorParent.getAnnotations().contains(a) && !targetList.contains(RoomAnnotationTargetEnum.ACTOR_CLASS.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.ACTOR_CLASS;
}
else if(actorParent.getBehaviorAnnotations().contains(a) && !targetList.contains(RoomAnnotationTargetEnum.ACTOR_BEHAVIOR.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.ACTOR_BEHAVIOR;
}
}
else if(parent instanceof DataClass && !targetList.contains(RoomAnnotationTargetEnum.DATA_CLASS.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.DATA_CLASS;
}
else if(parent instanceof ProtocolClass && !targetList.contains(RoomAnnotationTargetEnum.PROTOCOL_CLASS.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.PROTOCOL_CLASS;
}
else if(parent instanceof CompoundProtocolClass && !targetList.contains(RoomAnnotationTargetEnum.COMPOUND_PROTOCOL_CLASS.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.COMPOUND_PROTOCOL_CLASS;
}
else if(parent instanceof LogicalSystem && !targetList.contains(RoomAnnotationTargetEnum.LOGICAL_SYSTEM_CLASS.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.LOGICAL_SYSTEM_CLASS;
}
else if(parent instanceof SubSystemClass && !targetList.contains(RoomAnnotationTargetEnum.SUBSYSTEM_CLASS.getLiteral())) {
invalidTargetType = RoomAnnotationTargetEnum.SUBSYSTEM_CLASS;
}
if(invalidTargetType != null) {
error("AnnotationType " + a.getType().getName() + " is not allowed for target " + invalidTargetType.getLiteral(),
a,
BasePackage.Literals.ANNOTATION__TYPE,
INVALID_ANNOTATION_TARGET,
a.getType().getName(),
a.getType().getName()+" {target = "+invalidTargetType.getLiteral()+" ...",
invalidTargetType.getLiteral());
}
}
@Check
public void checkTestInstanceAnnotation(Annotation annotation){
if(annotation.getType() == null || !"TestInstance".equals(annotation.getType().getName()))
return;
RoomClass roomClass = roomHelpers.getRoomClass(annotation);
if(roomClass instanceof SubSystemClass){
if(((SubSystemClass)roomClass).getThreads().size() > 0)
error("Annotation 'TestInstance' does not allow (explicit) LogicalThreads", annotation, null);
}
}
@Check
public void checkEnumeration(EnumerationType et) {
if (et.getPrimitiveType()!=null) {
if (et.getPrimitiveType().getType()!=LiteralType.INT) {
error("enumerations must be of integer type", RoomPackage.Literals.ENUMERATION_TYPE__PRIMITIVE_TYPE);
}
}
if (et.getLiterals().isEmpty())
error("at least one literal has to be specified", RoomPackage.Literals.ENUMERATION_TYPE__LITERALS);
}
private void error(Result result) {
error(result.getMsg(), result.getSource(), result.getFeature(), result.getIndex());
}
@Check
public void checkOperations(ActorClass ac){
if(roomHelpers.isCircularClassHierarchy(ac))
return;
// issue warning for deprecated ctor/dtor operations
for(Operation op : ac.getOperations())
if(ac.getName().equals(op.getName()))
warning("Operation name is discouraged, may be mistaken for ctor/dtor", op, RoomPackage.Literals.OPERATION__NAME);
checkOperationsOverride(roomHelpers.getAllOperations(ac), ac.getOperations());
}
@Check
public void checkOperations(DataClass dc){
if(roomHelpers.isCircularClassHierarchy(dc))
return;
// issue warning for deprecated ctor/dtor operations
for(Operation op : dc.getOperations())
if(dc.getName().equals(op.getName()))
warning("Operation name is discouraged, may be mistaken for ctor/dtor", op, RoomPackage.Literals.OPERATION__NAME);
checkOperationsOverride(roomHelpers.getAllOperations(dc), dc.getOperations());
}
/**
* @param allOperations list of all operations ordered by base class first
* @param toCheck
*/
private void checkOperationsOverride(List<StandardOperation> allOperations, List<StandardOperation> toCheck){
Map<String, StandardOperation> map = Maps.newHashMap();
for(StandardOperation op : allOperations){
if(!map.containsKey(op.getName()))
map.put(op.getName(), op);
}
for(StandardOperation op : toCheck){
if(op.getName() == null)
continue;
StandardOperation baseOp = map.get(op.getName());
if(baseOp == op){
if(op.isOverride())
error("Operation '"+op.getName()+"' must override an operation in super class", op, RoomPackage.Literals.STANDARD_OPERATION__OVERRIDE, OPERATION_EXTRANEOUS_OVERRIDE);
continue;
}
String baseOpFQN = roomHelpers.getRoomClass(baseOp).getName()+"."+baseOp.getName()+"()";
if(!op.isOverride()){
error("Implicit override of operation "+baseOpFQN, op, RoomPackage.Literals.OPERATION__NAME, OPERATION_MISSING_OVERRIDE);
continue;
}
if(!roomHelpers.matchingArguments(baseOp, op))
error("Arguments must be identical to overriden operation in " +baseOpFQN, op, RoomPackage.Literals.OPERATION__ARGUMENTS);
if(!roomHelpers.matchingReturnType(baseOp, op))
error("Return type must be identical to overriden operation " +baseOpFQN, op, RoomPackage.Literals.OPERATION__RETURN_TYPE);
}
}
}