diff options
author | Juergen Haug | 2015-07-15 15:50:32 +0000 |
---|---|---|
committer | Juergen Haug | 2015-07-15 15:50:32 +0000 |
commit | 8d942627cd52f23e776ff9fcb3127ce7cb8c5f85 (patch) | |
tree | 018bba4bb12c960693a0ed8bbf3fcea8a7f4a1c9 | |
parent | ae0407d0703e6c4cd69e0f674cc5b8b15e85acf6 (diff) | |
download | org.eclipse.etrice-8d942627cd52f23e776ff9fcb3127ce7cb8c5f85.tar.gz org.eclipse.etrice-8d942627cd52f23e776ff9fcb3127ce7cb8c5f85.tar.xz org.eclipse.etrice-8d942627cd52f23e776ff9fcb3127ce7cb8c5f85.zip |
[core] improved (inherited) names validation
Change-Id: Idb8c1ba3e09fcb3b8943cb424994c237a1d327dd
12 files changed, 768 insertions, 146 deletions
diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/ValidationHelpers.xtend b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/ValidationHelpers.xtend index 5563245a2..b9ec50910 100644 --- a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/ValidationHelpers.xtend +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/ValidationHelpers.xtend @@ -17,6 +17,7 @@ import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.EStructuralFeature import java.util.ArrayList import org.eclipse.emf.ecore.resource.Resource +import com.google.common.base.Function /** * @author hrentz @@ -65,4 +66,17 @@ class ValidationHelpers { def static Iterable<NamedObject> inSameResource(Iterable<NamedObject> items, Resource resource) { items.filter(i|i.obj.eResource==resource) } + + /** + * Visitor for inheritance, safeguards from null, eProxy and circular issues. + * + * @param start EObject, may be null + * @param function return null to exit + */ + def static <E extends EObject> saveRecursiveVisitor(E start, Function<E , E> function){ + val Set<E> visited = newHashSet + var E next = start + while(next != null && !next.eIsProxy && (visited += next)) + next = function.apply(next) + } } diff --git a/plugins/org.eclipse.etrice.core.common/xtend-gen/org/eclipse/etrice/core/common/validation/ValidationHelpers.java b/plugins/org.eclipse.etrice.core.common/xtend-gen/org/eclipse/etrice/core/common/validation/ValidationHelpers.java index 5314950b4..999f37cd8 100644 --- a/plugins/org.eclipse.etrice.core.common/xtend-gen/org/eclipse/etrice/core/common/validation/ValidationHelpers.java +++ b/plugins/org.eclipse.etrice.core.common/xtend-gen/org/eclipse/etrice/core/common/validation/ValidationHelpers.java @@ -10,6 +10,7 @@ */ package org.eclipse.etrice.core.common.validation; +import com.google.common.base.Function; import com.google.common.base.Objects; import java.util.ArrayList; import java.util.List; @@ -123,4 +124,19 @@ public class ValidationHelpers { }; return IterableExtensions.<ValidationHelpers.NamedObject>filter(items, _function); } + + /** + * Visitor for inheritance, safeguards from null, eProxy and circular issues. + * + * @param start EObject, may be null + * @param function return null to exit + */ + public static <E extends EObject> void saveRecursiveVisitor(final E start, final Function<E, E> function) { + final Set<E> visited = CollectionLiterals.<E>newHashSet(); + E next = start; + while ((((!Objects.equal(next, null)) && (!next.eIsProxy())) && visited.add(next))) { + E _apply = function.apply(next); + next = _apply; + } + } } diff --git a/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java b/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java index 107a7d482..50f8321eb 100644 --- a/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java +++ b/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java @@ -9,7 +9,7 @@ import java.util.List; import org.eclipse.emf.ecore.EPackage; import org.eclipse.xtext.validation.ComposedChecks; -@ComposedChecks(validators= {org.eclipse.xtext.validation.ImportUriValidator.class, org.eclipse.xtext.validation.NamesAreUniqueValidator.class, org.eclipse.etrice.core.validation.ValidatorExtensionManager.class}) +@ComposedChecks(validators= {org.eclipse.xtext.validation.ImportUriValidator.class, org.eclipse.etrice.core.validation.ValidatorExtensionManager.class, org.eclipse.etrice.core.validation.RoomNamesAreUniqueValidator.class}) public class AbstractRoomJavaValidator extends org.eclipse.etrice.core.fsm.validation.FSMJavaValidator { @Override diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 index ccfc775b5..46d8568f6 100644 --- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 @@ -95,10 +95,10 @@ Workflow { fragment = validation.JavaValidatorFragment { // HOWTO: use URI imports - configure validator composedCheck = "org.eclipse.xtext.validation.ImportUriValidator" - composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator" // HOWTO: extension manager is added here (CAUTION: don't register with RoomPackage in the class since this makes it is called twice) composedCheck = "org.eclipse.etrice.core.validation.ValidatorExtensionManager" + composedCheck = "org.eclipse.etrice.core.validation.RoomNamesAreUniqueValidator" } // scoping and exporting API diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/FQNAreUniqueValidationHelper.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/FQNAreUniqueValidationHelper.java index a6c844e1c..8abe02da5 100644 --- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/FQNAreUniqueValidationHelper.java +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/FQNAreUniqueValidationHelper.java @@ -17,19 +17,31 @@ import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.xtext.validation.NamesAreUniqueValidationHelper; /** - * Clustering is deactivated.<br> - * Names are unique: two objects sharing the same fqn prefix have distinct names.<br> - * Called by {@link org.eclipse.xtext.validation.NamesAreUniqueValidator NamesAreUniqueValidator}. + * Xtext's Clustering is deactivated.<br> + * Names are unique: two objects sharing the same fqn prefix have distinct + * names.<br> + * Called by {@link org.eclipse.xtext.validation.NamesAreUniqueValidator + * NamesAreUniqueValidator}. */ public class FQNAreUniqueValidationHelper extends NamesAreUniqueValidationHelper { - + @Override protected EClass getAssociatedClusterType(EClass eClass) { - return EcorePackage.eINSTANCE.eClass(); + return EcorePackage.Literals.ECLASS; } - + @Override protected String getTypeLabel(EClass eClass) { - return ""; + // validated object's cluster type is EClass + if (eClass == EcorePackage.Literals.ECLASS) + return "name"; + + return super.getTypeLabel(eClass); } + +// @Override +// public String getDuplicateNameErrorMessage(IEObjectDescription description, EClass clusterType, +// EStructuralFeature feature) { +// } + } diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java index bfd088e97..7766d2441 100644 --- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java @@ -15,19 +15,19 @@ package org.eclipse.etrice.core.validation; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; 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.AnnotationType; import org.eclipse.etrice.core.common.base.BasePackage; import org.eclipse.etrice.core.common.base.Import; import org.eclipse.etrice.core.common.base.LiteralType; @@ -38,10 +38,8 @@ 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.naming.RoomNameProvider; 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.ActorInstanceMapping; import org.eclipse.etrice.core.room.ActorRef; import org.eclipse.etrice.core.room.Attribute; @@ -71,24 +69,25 @@ 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.IQualifiedNameProvider; +import org.eclipse.xtext.naming.QualifiedName; 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 - private RoomHelpers roomHelpers; + @Inject protected RoomHelpers roomHelpers; - @Inject - private RoomNameProvider roomNameProvider; + @Inject protected ValidationUtil validationUtil; - @Inject - private 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 [*]"; @@ -273,56 +272,6 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { } @Check - public void checkUniqueNames(ActorClass ac) { - if (roomHelpers.isCircularClassHierarchy(ac)) - return; - - HashMap<String, EObject> name2obj = new HashMap<String, EObject>(); - - // first add all base class objects (we'll add no errors for them) - if (ac.getActorBase()!=null) { - ActorClass base = ac.getActorBase(); - List<InterfaceItem> items = roomHelpers.getAllInterfaceItems(base); - for (InterfaceItem item : items) { - name2obj.put(item.getName(), item); - } - List<ActorContainerRef> refs = roomHelpers.getAllActorContainerRefs(base); - for (ActorContainerRef ref : refs) { - name2obj.put(ref.getName(), ref); - } - } - - // now we check our own items and refs - List<InterfaceItem> items = roomHelpers.getInterfaceItems(ac); - for (InterfaceItem item : items) { - if (name2obj.containsKey(item.getName())) { - EObject duplicate = name2obj.get(item.getName()); - String location = roomNameProvider.getLocation(duplicate); - EObject parent = item.eContainer(); - @SuppressWarnings("unchecked") - int idx = ((List<EObject>)parent.eGet(item.eContainingFeature())).indexOf(item); - error("names must be unique (duplicate of "+location+")", parent, item.eContainingFeature(), idx); - } - else - name2obj.put(item.getName(), item); - } - - List<ActorContainerRef> refs = roomHelpers.getRefs(ac, false); - for (ActorContainerRef ref : refs) { - if (name2obj.containsKey(ref.getName())) { - EObject duplicate = name2obj.get(ref.getName()); - String location = roomNameProvider.getLocation(duplicate); - EObject parent = ref.eContainer(); - @SuppressWarnings("unchecked") - int idx = ((List<EObject>)parent.eGet(ref.eContainingFeature())).indexOf(ref); - error("names must be unique (duplicate of "+location+")", parent, ref.eContainingFeature(), idx); - } - else - name2obj.put(ref.getName(), ref); - } - } - - @Check public void checkExecModelConsistent(ActorClass ac) { if (roomHelpers.isCircularClassHierarchy(ac)) return; @@ -350,7 +299,7 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { @Check public void checkTopLevelRefinedStates(ActorClass ac) { - List<Result> errors = ValidationUtil.checkTopLevelRefinedStates(ac); + List<Result> errors = validationUtil.checkTopLevelRefinedStates(ac); for (Result err : errors) { error(err); } @@ -362,7 +311,7 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { 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 dflt_thread"); + warning("at least one thread has to be defined", RoomPackage.Literals.SUB_SYSTEM_CLASS__THREADS, THREAD_MISSING, "LogicalThread defaultThread"); checkMappings(ssc.getActorInstanceMappings()); } @@ -408,7 +357,7 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { @Check public void checkPortCompatibility(Binding bind) { - Result result = ValidationUtil.isValid(bind); + Result result = validationUtil.isValid(bind); if (!result.isOk()) { EObject sc = bind.eContainer(); @SuppressWarnings("unchecked") @@ -419,19 +368,12 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { @Check public void checkServiceCompatibility(LayerConnection conn) { - Result result = ValidationUtil.isValid(conn); + Result result = validationUtil.isValid(conn); if (!result.isOk()) error(result.getMsg(), RoomPackage.eINSTANCE.getLayerConnection_From()); } @Check - public void checkInterfaceItemUniqueName(InterfaceItem item) { - Result result = ValidationUtil.isUniqueName(item); - if (!result.isOk()) - error(result.getMsg(), FSMPackage.eINSTANCE.getAbstractInterfaceItem_Name()); - } - - @Check public void checkPortCommunicationCompatibility(ActorClass ac){ if(ac.getCommType() == ComponentCommunicationType.SYNCHRONOUS){ // not supported yet @@ -546,6 +488,94 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { } } + /** + * {@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(); @@ -569,47 +599,6 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { } @Check - public void checkAttribute(Attribute att) { - ArrayList<Attribute> all = new ArrayList<Attribute>(); - - if (att.eContainer() instanceof ActorClass) { - ActorClass ac = (ActorClass) att.eContainer(); - if (roomHelpers.isCircularClassHierarchy(ac)) - // is checked elsewhere - return; - - do { - all.addAll(ac.getAttributes()); - ac = ac.getActorBase(); - } - while (ac!=null); - } - else if (att.eContainer() instanceof DataClass) { - DataClass dc = (DataClass) att.eContainer(); - if (roomHelpers.isCircularClassHierarchy(dc)) - // is checked elsewhere - return; - - do { - all.addAll(dc.getAttributes()); - dc = dc.getBase(); - } - while (dc!=null); - } - // skip PortClass case since they don't inherit (yet) - - String name = att.getName(); - for (Attribute a : all) { - if (a!=att && a.getName().equals(name)) - if (a.eContainer()!=att.eContainer()) - error("name already used in base class '"+((RoomClass)a.eContainer()).getName()+"'", - RoomPackage.Literals.ATTRIBUTE__NAME); - else - error("name already used", RoomPackage.Literals.ATTRIBUTE__NAME); - } - } - - @Check public void checkReplicatedPortBindingPatterns(StructureClass sc) { HashSet<Port> haveReplPeer = new HashSet<Port>(); for (Binding bind : sc.getBindings()) { @@ -681,31 +670,6 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { a.getType().getName()+" {target = "+invalidTargetType.getLiteral()+" ...", invalidTargetType.getLiteral()); } - } - - @Check - public void checkRoomClassAnnotationTypeUniqueness(RoomClass rc) { - if(rc.eContainer() instanceof RoomModel) { - RoomModel model = (RoomModel)rc.eContainer(); - for(AnnotationType at : model.getAnnotationTypes()) { - if(rc.getName().equals(at.getName())) { - error("The name \""+at.getName()+"\" already exists as an AnnotationType name", rc, RoomPackage.Literals.ROOM_CLASS__NAME); - } - } - } - } - - @Check - public void checkRoomClassAnnotationTypeUniqueness(AnnotationType at) { - if(at.eContainer() instanceof RoomModel) { - RoomModel model = (RoomModel)at.eContainer(); - for(Object obj : org.eclipse.emf.ecore.util.EcoreUtil.getObjectsByType(model.eContents(), RoomPackage.Literals.ROOM_CLASS)) { - RoomClass rc = (RoomClass)obj; - if(at.getName().equals(rc.getName())) { - error("The name \""+at.getName()+"\" already exists as a RoomClass name", at, BasePackage.Literals.ANNOTATION_TYPE__NAME); - } - } - } } @Check diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomNamesAreUniqueValidator.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomNamesAreUniqueValidator.java new file mode 100644 index 000000000..8ad02dbb6 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomNamesAreUniqueValidator.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2015 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: + * Juergen Haug (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.core.validation; + +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.etrice.core.room.RoomModel; +import org.eclipse.xtext.naming.IQualifiedNameProvider; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.validation.EValidatorRegistrar; +import org.eclipse.xtext.validation.NamesAreUniqueValidator; + +import com.google.inject.Inject; + +public class RoomNamesAreUniqueValidator extends NamesAreUniqueValidator { + + @Inject + protected IQualifiedNameProvider fqnProvider; + + @Override + public void checkUniqueNamesInResourceOf(EObject eObject) { + if (!(eObject instanceof RoomModel) && getContext() != null) { + // restrict validation on current object's fqn + QualifiedName restrictedFQN = fqnProvider.apply(eObject); + if (restrictedFQN != null && restrictedFQN != QualifiedName.EMPTY) + getContext().put(this, restrictedFQN); + } + + // Every object is on global scope, thus delegate to NamesAreUniqueValidator + // Validates only once per resource and #getContext() + super.checkUniqueNamesInResourceOf(eObject); + } + + @Override + public void acceptError(String message, EObject object, EStructuralFeature feature, int index, String code, + String... issueData) { + if (acceptNamespace(object)) + super.acceptError(message, object, feature, index, code, issueData); + } + + @Override + public void acceptError(String message, EObject object, int offset, int length, String code, String... issueData) { + if (acceptNamespace(object)) + super.acceptError(message, object, offset, length, code, issueData); + } + + protected boolean acceptNamespace(EObject eObject) { + Map<Object, Object> context = getContext(); + if (context != null && context.containsKey(this)) { + QualifiedName restrictedFQN = (QualifiedName) context.get(this); + QualifiedName eObjectFQN = fqnProvider.apply(eObject); + if (eObjectFQN != null){ + if(eObjectFQN.startsWith(restrictedFQN)){ + if(eObjectFQN.equals(restrictedFQN)) + return eObject == getCurrentObject(); + return true; + } + } + + return false; + } + + return true; + } + + @Override + public void register(EValidatorRegistrar registrar) { + // library validator is not registered for a specific language + } +} diff --git a/tests/org.eclipse.etrice.core.room.tests/.classpath b/tests/org.eclipse.etrice.core.room.tests/.classpath index 4e5c64811..fde31cf56 100644 --- a/tests/org.eclipse.etrice.core.room.tests/.classpath +++ b/tests/org.eclipse.etrice.core.room.tests/.classpath @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> + <classpathentry kind="src" path="xtend-gen"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> diff --git a/tests/org.eclipse.etrice.core.room.tests/.gitignore b/tests/org.eclipse.etrice.core.room.tests/.gitignore index ba077a403..65847a73f 100644 --- a/tests/org.eclipse.etrice.core.room.tests/.gitignore +++ b/tests/org.eclipse.etrice.core.room.tests/.gitignore @@ -1 +1,2 @@ bin +*._trace diff --git a/tests/org.eclipse.etrice.core.room.tests/models/NamesAreUnique.room b/tests/org.eclipse.etrice.core.room.tests/models/NamesAreUnique.room new file mode 100644 index 000000000..78c890c7d --- /dev/null +++ b/tests/org.eclipse.etrice.core.room.tests/models/NamesAreUnique.room @@ -0,0 +1,175 @@ +RoomModel NamesAreUnique { + + import room.basic.types.* from "../../org.eclipse.etrice.modellib.java/model/Types.room" + + /* + * Room model duplicate names + */ + LogicalSystem ClassDuplicate1 { + SubSystemRef dummy: SFlat + } + + ActorClass ClassDuplicate1 { } + + Enumeration ClassDuplicate1 { + DUMMY + } + + PrimitiveType ClassDuplicate1: ptBoolean -> boolean default "false" + + AnnotationType ClassDuplicate1 { + target = { + ActorClass, SubSystemClass + } + } + + /* + * Children unique names + */ + LogicalSystem LFlat { + SubSystemRef duplicate1: SFlat + SubSystemRef duplicate1: SFlat + } + + SubSystemClass SFlat { + Port duplicate1: PDummy + SPP duplicate2: PDummy + ActorRef duplicate2: Dummy + LogicalThread duplicate1 + } + + ActorClass Flat { + Interface { + Port duplicate1: PDummy + SPP dupliCAte2: PDummy + } + Structure { + external Port duplicate1 + SAP dupliCAte2: PEventBase + ActorRef duplicate1: Dummy + Attribute duplicate5: boolean + } + Behavior { + Operation duplicate5() { + "" + } + } + } + + DataClass DFlat { + Attribute duplicate1: boolean + Operation duplicate1() { + "" + } + } + + Enumeration EnumFlat { + Duplicate1, + Duplicate1 + } + + /* + * Inherited children unique names + */ + ActorClass Base { + Interface { + Port base_duplicate1: PDummy + SPP base_duplicate2: PDummy + } + Structure { + SAP base_duplicate3: PDummy + ActorRef base_duplicate4: Dummy + Attribute base_duplicate5: boolean + } + Behavior { + ctor { + "" + } + dtor { + "" + } + Operation base_duplicate6() { + "" + } + StateMachine { } + } + } + + ActorClass Sub extends Base { + Interface { + Port base_duplicate2: PDummy + SPP base_duplicate6: PDummy + } + Structure { + SAP base_duplicate4: PDummy + ActorRef base_duplicate1: Dummy + Attribute base_duplicate3: boolean + Attribute sm : int32 // special: StateMachine + Attribute ^ctor : int32 // special: ctor + Attribute ^dtor : int32 // special: dtor + } + Behavior { + Operation base_duplicate5() { + "" + } + } + } + + ActorClass Override extends Base { + Behavior { + ctor { + "" + } + dtor { + "" + } + override Operation base_duplicate6() { + "" + } + StateMachine { } + } + } + + DataClass DBase { + Attribute base_duplicate1: boolean + ctor { + "" + } + dtor { + "" + } + Operation base_duplicate2() { + "" + } + } + + DataClass DSub extends DBase { + Attribute ^ctor : int32 // special: ctor + Attribute ^dtor : int32 // special: dtor + Attribute base_duplicate2: boolean + Operation base_duplicate1() { + "" + } + } + + DataClass DOverride extends DBase { + ctor { + "" + } + dtor { + "" + } + override Operation base_duplicate2() { + "" + } + } + + ActorClass Dummy { } + + ProtocolClass PDummy { + incoming { + Message in1() + } + } + +}
\ No newline at end of file diff --git a/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestNames.xtend b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestNames.xtend new file mode 100644 index 000000000..e58fd7400 --- /dev/null +++ b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestNames.xtend @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2015 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: + * Juergen Haug (initial contribution) + * + *******************************************************************************/ +package org.eclipse.etrice.core + +import com.google.common.collect.Maps +import java.util.Map +import org.eclipse.emf.common.util.Diagnostic +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.etrice.core.room.ExternalPort +import org.eclipse.xtext.validation.AbstractValidationDiagnostic +import org.junit.Assert +import org.junit.Before +import org.junit.Test + +class TestNames extends TestBase { + + val Map<EObject, Boolean> nameErrorMap = Maps.newHashMap + Resource res; + + @Before + def void SetUp() { + prepare(); + res = getResource("NamesAreUnique.room"); + + val model = res.contents.head + val diag = getDiag(model) + diag.children.forEach[ + if(it instanceof AbstractValidationDiagnostic){ + val obj = it.sourceEObject + var nameError = nameErrorMap.get(obj) + if(nameError == null) nameError = false + nameErrorMap.put(obj, (nameError || it.isNameErrorMessage)) + } + ] + } + + @Test + def void RoomClassNames() { + Assert.assertTrue(hasNameErrorMessage(res.getEObject("ActorClass:ClassDuplicate1"))) + Assert.assertTrue(hasNameErrorMessage(res.getEObject("LogicalSystem:ClassDuplicate1"))) + Assert.assertTrue(hasNameErrorMessage(res.getEObject("AnnotationType:ClassDuplicate1"))) + Assert.assertTrue(hasNameErrorMessage(res.getEObject("EnumerationType:ClassDuplicate1"))) + Assert.assertTrue(hasNameErrorMessage(res.getEObject("PrimitiveType:ClassDuplicate1"))) + } + + @Test + def void BaseClassNames() { + val ac = res.getEObject("ActorClass:Base") + val dc = res.getEObject("DataClass:DBase") + + val items = newArrayList(ac, dc) + items += ac.eContents + items += dc.eContents + Assert.assertEquals("Unexpected item count", 15, items.size); + items.forEach[ + Assert.assertFalse("expected no name error: " + it, it.hasNameErrorMessage) + ] + } + + @Test + def void OverrideNames(){ + val ac = res.getEObject("ActorClass:Override") + val dc = res.getEObject("DataClass:DOverride") + + val items = newArrayList(ac, dc) + items += ac.eContents + items += dc.eContents + Assert.assertEquals("Unexpected item count", 9, items.size); + items.forEach[ + Assert.assertFalse("expected no name error: " + it, it.hasNameErrorMessage) + ] + } + + @Test + def void InheritedNames() { + val ac = res.getEObject("ActorClass:Sub") + val dc = res.getEObject("DataClass:DSub") + + val items = newArrayList() + items += ac.eContents.filter[!(it instanceof ExternalPort)] + items += dc.eContents + Assert.assertEquals("Unexpected item count", 13, items.size); + items.forEach[ + Assert.assertTrue("expected name error: " + it, it.hasNameErrorMessage) + ] + } + + @Test + def void FlatNames() { + val ac = res.getEObject("ActorClass:Flat") + val dc = res.getEObject("DataClass:DFlat") + val lc = res.getEObject("DataClass:LFlat") + val sc = res.getEObject("DataClass:SFlat") + val enum = res.getEObject("DataClass:EnumFlat") + + val items = newArrayList + items += ac.eContents.filter[!(it instanceof ExternalPort)] + items += dc.eContents + items += lc.eContents + items += sc.eContents + items += enum.eContents + Assert.assertEquals("Unexpected item count", 16, items.size); + items.forEach[ + Assert.assertTrue("expected name error: " + it, it.hasNameErrorMessage) + ] + } + + private def boolean hasNameErrorMessage(EObject obj){ + nameErrorMap.containsKey(obj) && nameErrorMap.get(obj) + } + + protected def boolean isNameErrorMessage(Diagnostic diag) { + val message = diag.message + return diag.severity >= Diagnostic.ERROR && (message.contains("Duplicate name") || (message.contains("name") && message.contains("is already assigned to"))); + } +} diff --git a/tests/org.eclipse.etrice.core.room.tests/xtend-gen/org/eclipse/etrice/core/TestNames.java b/tests/org.eclipse.etrice.core.room.tests/xtend-gen/org/eclipse/etrice/core/TestNames.java new file mode 100644 index 000000000..e61bea5a3 --- /dev/null +++ b/tests/org.eclipse.etrice.core.room.tests/xtend-gen/org/eclipse/etrice/core/TestNames.java @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2015 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: + * Juergen Haug (initial contribution) + */ +package org.eclipse.etrice.core; + +import com.google.common.base.Objects; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.eclipse.emf.common.util.Diagnostic; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.etrice.core.TestBase; +import org.eclipse.etrice.core.room.ExternalPort; +import org.eclipse.xtext.validation.AbstractValidationDiagnostic; +import org.eclipse.xtext.xbase.lib.CollectionLiterals; +import org.eclipse.xtext.xbase.lib.Functions.Function1; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +@SuppressWarnings("all") +public class TestNames extends TestBase { + private final Map<EObject, Boolean> nameErrorMap = Maps.<EObject, Boolean>newHashMap(); + + private Resource res; + + @Before + public void SetUp() { + this.prepare(); + Resource _resource = this.getResource("NamesAreUnique.room"); + this.res = _resource; + EList<EObject> _contents = this.res.getContents(); + final EObject model = IterableExtensions.<EObject>head(_contents); + final Diagnostic diag = this.getDiag(model); + List<Diagnostic> _children = diag.getChildren(); + final Procedure1<Diagnostic> _function = new Procedure1<Diagnostic>() { + public void apply(final Diagnostic it) { + if ((it instanceof AbstractValidationDiagnostic)) { + final EObject obj = ((AbstractValidationDiagnostic)it).getSourceEObject(); + Boolean nameError = TestNames.this.nameErrorMap.get(obj); + boolean _equals = Objects.equal(nameError, null); + if (_equals) { + nameError = Boolean.valueOf(false); + } + boolean _or = false; + if ((nameError).booleanValue()) { + _or = true; + } else { + boolean _isNameErrorMessage = TestNames.this.isNameErrorMessage(it); + _or = _isNameErrorMessage; + } + TestNames.this.nameErrorMap.put(obj, Boolean.valueOf(_or)); + } + } + }; + IterableExtensions.<Diagnostic>forEach(_children, _function); + } + + @Test + public void RoomClassNames() { + EObject _eObject = this.res.getEObject("ActorClass:ClassDuplicate1"); + boolean _hasNameErrorMessage = this.hasNameErrorMessage(_eObject); + Assert.assertTrue(_hasNameErrorMessage); + EObject _eObject_1 = this.res.getEObject("LogicalSystem:ClassDuplicate1"); + boolean _hasNameErrorMessage_1 = this.hasNameErrorMessage(_eObject_1); + Assert.assertTrue(_hasNameErrorMessage_1); + EObject _eObject_2 = this.res.getEObject("AnnotationType:ClassDuplicate1"); + boolean _hasNameErrorMessage_2 = this.hasNameErrorMessage(_eObject_2); + Assert.assertTrue(_hasNameErrorMessage_2); + EObject _eObject_3 = this.res.getEObject("EnumerationType:ClassDuplicate1"); + boolean _hasNameErrorMessage_3 = this.hasNameErrorMessage(_eObject_3); + Assert.assertTrue(_hasNameErrorMessage_3); + EObject _eObject_4 = this.res.getEObject("PrimitiveType:ClassDuplicate1"); + boolean _hasNameErrorMessage_4 = this.hasNameErrorMessage(_eObject_4); + Assert.assertTrue(_hasNameErrorMessage_4); + } + + @Test + public void BaseClassNames() { + final EObject ac = this.res.getEObject("ActorClass:Base"); + final EObject dc = this.res.getEObject("DataClass:DBase"); + final ArrayList<EObject> items = CollectionLiterals.<EObject>newArrayList(ac, dc); + EList<EObject> _eContents = ac.eContents(); + Iterables.<EObject>addAll(items, _eContents); + EList<EObject> _eContents_1 = dc.eContents(); + Iterables.<EObject>addAll(items, _eContents_1); + int _size = items.size(); + Assert.assertEquals("Unexpected item count", 15, _size); + final Procedure1<EObject> _function = new Procedure1<EObject>() { + public void apply(final EObject it) { + boolean _hasNameErrorMessage = TestNames.this.hasNameErrorMessage(it); + Assert.assertFalse(("expected no name error: " + it), _hasNameErrorMessage); + } + }; + IterableExtensions.<EObject>forEach(items, _function); + } + + @Test + public void OverrideNames() { + final EObject ac = this.res.getEObject("ActorClass:Override"); + final EObject dc = this.res.getEObject("DataClass:DOverride"); + final ArrayList<EObject> items = CollectionLiterals.<EObject>newArrayList(ac, dc); + EList<EObject> _eContents = ac.eContents(); + Iterables.<EObject>addAll(items, _eContents); + EList<EObject> _eContents_1 = dc.eContents(); + Iterables.<EObject>addAll(items, _eContents_1); + int _size = items.size(); + Assert.assertEquals("Unexpected item count", 9, _size); + final Procedure1<EObject> _function = new Procedure1<EObject>() { + public void apply(final EObject it) { + boolean _hasNameErrorMessage = TestNames.this.hasNameErrorMessage(it); + Assert.assertFalse(("expected no name error: " + it), _hasNameErrorMessage); + } + }; + IterableExtensions.<EObject>forEach(items, _function); + } + + @Test + public void InheritedNames() { + final EObject ac = this.res.getEObject("ActorClass:Sub"); + final EObject dc = this.res.getEObject("DataClass:DSub"); + final ArrayList<EObject> items = CollectionLiterals.<EObject>newArrayList(); + EList<EObject> _eContents = ac.eContents(); + final Function1<EObject, Boolean> _function = new Function1<EObject, Boolean>() { + public Boolean apply(final EObject it) { + return Boolean.valueOf((!(it instanceof ExternalPort))); + } + }; + Iterable<EObject> _filter = IterableExtensions.<EObject>filter(_eContents, _function); + Iterables.<EObject>addAll(items, _filter); + EList<EObject> _eContents_1 = dc.eContents(); + Iterables.<EObject>addAll(items, _eContents_1); + int _size = items.size(); + Assert.assertEquals("Unexpected item count", 13, _size); + final Procedure1<EObject> _function_1 = new Procedure1<EObject>() { + public void apply(final EObject it) { + boolean _hasNameErrorMessage = TestNames.this.hasNameErrorMessage(it); + Assert.assertTrue(("expected name error: " + it), _hasNameErrorMessage); + } + }; + IterableExtensions.<EObject>forEach(items, _function_1); + } + + @Test + public void FlatNames() { + final EObject ac = this.res.getEObject("ActorClass:Flat"); + final EObject dc = this.res.getEObject("DataClass:DFlat"); + final EObject lc = this.res.getEObject("DataClass:LFlat"); + final EObject sc = this.res.getEObject("DataClass:SFlat"); + final EObject enum_ = this.res.getEObject("DataClass:EnumFlat"); + final ArrayList<EObject> items = CollectionLiterals.<EObject>newArrayList(); + EList<EObject> _eContents = ac.eContents(); + final Function1<EObject, Boolean> _function = new Function1<EObject, Boolean>() { + public Boolean apply(final EObject it) { + return Boolean.valueOf((!(it instanceof ExternalPort))); + } + }; + Iterable<EObject> _filter = IterableExtensions.<EObject>filter(_eContents, _function); + Iterables.<EObject>addAll(items, _filter); + EList<EObject> _eContents_1 = dc.eContents(); + Iterables.<EObject>addAll(items, _eContents_1); + EList<EObject> _eContents_2 = lc.eContents(); + Iterables.<EObject>addAll(items, _eContents_2); + EList<EObject> _eContents_3 = sc.eContents(); + Iterables.<EObject>addAll(items, _eContents_3); + EList<EObject> _eContents_4 = enum_.eContents(); + Iterables.<EObject>addAll(items, _eContents_4); + int _size = items.size(); + Assert.assertEquals("Unexpected item count", 16, _size); + final Procedure1<EObject> _function_1 = new Procedure1<EObject>() { + public void apply(final EObject it) { + boolean _hasNameErrorMessage = TestNames.this.hasNameErrorMessage(it); + Assert.assertTrue(("expected name error: " + it), _hasNameErrorMessage); + } + }; + IterableExtensions.<EObject>forEach(items, _function_1); + } + + private boolean hasNameErrorMessage(final EObject obj) { + boolean _and = false; + boolean _containsKey = this.nameErrorMap.containsKey(obj); + if (!_containsKey) { + _and = false; + } else { + Boolean _get = this.nameErrorMap.get(obj); + _and = (_get).booleanValue(); + } + return _and; + } + + protected boolean isNameErrorMessage(final Diagnostic diag) { + final String message = diag.getMessage(); + boolean _and = false; + int _severity = diag.getSeverity(); + boolean _greaterEqualsThan = (_severity >= Diagnostic.ERROR); + if (!_greaterEqualsThan) { + _and = false; + } else { + boolean _or = false; + boolean _contains = message.contains("Duplicate name"); + if (_contains) { + _or = true; + } else { + boolean _and_1 = false; + boolean _contains_1 = message.contains("name"); + if (!_contains_1) { + _and_1 = false; + } else { + boolean _contains_2 = message.contains("is already assigned to"); + _and_1 = _contains_2; + } + _or = _and_1; + } + _and = _or; + } + return _and; + } +} |