浏览代码

Issue p454 (#1097)

* Introduced reusable resource description strategy...

* extended event filter predicate of ContectPredicateProvider

* made ContextPredicteProvider more relaxed for reaction triggers.

* extended validation to check that a regular trigger specifies an event.

* Fixed context predicates for events and added validations to check that valueof() expression get an event as argument.
Axel Terfloth 8 年之前
父节点
当前提交
d6763b1694

+ 3 - 1
plugins/org.yakindu.base.types/META-INF/MANIFEST.MF

@@ -15,6 +15,7 @@ Export-Package: org.yakindu.base.base,
  org.yakindu.base.types.impl,
  org.yakindu.base.types.inferrer,
  org.yakindu.base.types.interpreter,
+ org.yakindu.base.types.resource,
  org.yakindu.base.types.typesystem,
  org.yakindu.base.types.util,
  org.yakindu.base.types.validation
@@ -25,7 +26,8 @@ Require-Bundle: org.eclipse.core.runtime,
  com.google.inject,
  org.eclipse.xtend.lib,
  com.google.guava,
- org.eclipse.xtext.util
+ org.eclipse.xtext.util,
+ org.eclipse.xtext
 Bundle-ActivationPolicy: lazy
 Import-Package: org.apache.log4j
 

+ 95 - 0
plugins/org.yakindu.base.types/src/org/yakindu/base/types/resource/TypedResourceDescriptionStrategy.java

@@ -0,0 +1,95 @@
+package org.yakindu.base.types.resource;
+
+import java.util.Map;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.EObjectDescription;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy;
+import org.eclipse.xtext.util.IAcceptor;
+import org.yakindu.base.types.ComplexType;
+import org.yakindu.base.types.EnumerationType;
+import org.yakindu.base.types.Type;
+import org.yakindu.base.types.TypeAlias;
+import org.yakindu.base.types.TypedElement;
+
+import com.google.common.collect.Maps;
+
+public  class TypedResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
+
+	/**
+	 * This flag is true if the element has or contains elements with an unknown
+	 * resp. unsupported type
+	 */
+	public static final String HAS_UNKNOWN_TYPE = "has_unknown_type";
+	
+	/**
+	 * For types that are visible (<code>Type.isVisible()==true</code>) this
+	 * flag is true, otherwise false
+	 */
+	public static final String IS_VISIBLE_TYPE = "is_visible_type";
+
+	/**
+	 * Type aliases whose original type is an enumeration type will have this
+	 * flag set to true, otherwise this flag is missing
+	 */
+	public static final String IS_ALIAS_ON_ENUM = "is_alias_on_enum";
+	
+	/** 
+	 * This flag indicates the if the type of a TypedElelemnt is complex or not
+	 */
+	public static final String HAS_COMPLEX_TYPE = "has_complex_type";
+
+	
+	public TypedResourceDescriptionStrategy() {
+		super();
+	}
+
+
+	public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
+		if (getQualifiedNameProvider() == null)
+			return false;
+		
+		try {
+			QualifiedName qualifiedName = getQualifiedNameProvider().getFullyQualifiedName(eObject);
+			if (qualifiedName != null) {
+				Map<String, String> userData = Maps.newHashMap();
+				defineUserData(eObject, userData);
+				acceptor.accept(EObjectDescription.create(qualifiedName, eObject, userData));
+			}
+		} catch (Exception exc) {
+			exc.printStackTrace();
+		}
+		return true;
+	}
+
+
+	protected void defineUserData(EObject eObject, Map<String, String> userData) {
+		if (eObject instanceof TypedElement) {
+			userData.put(HAS_UNKNOWN_TYPE, String.valueOf(isUnknownType((TypedElement) eObject)));
+			userData.put(HAS_COMPLEX_TYPE, String.valueOf(hasComplexType((TypedElement) eObject)));
+		}
+		if (eObject instanceof Type) {
+			userData.put(IS_VISIBLE_TYPE, String.valueOf(((Type) eObject).isVisible()));
+		}
+		if (eObject instanceof TypeAlias) {
+			if (((TypeAlias) eObject).getOriginType() instanceof EnumerationType) {
+				userData.put(IS_ALIAS_ON_ENUM, String.valueOf(true));
+			}
+		}
+	}
+
+
+	
+	protected boolean isUnknownType(TypedElement element) {
+		return false;
+	}
+
+	
+	protected boolean hasComplexType(TypedElement element) {
+		return false ; //element.getType() instanceof ComplexType; 
+	}
+	
+
+}

+ 4 - 3
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/resource/SCTResourceDescriptionStrategy.java

@@ -16,6 +16,8 @@ import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
 import org.eclipse.xtext.resource.IEObjectDescription;
 import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy;
 import org.eclipse.xtext.util.IAcceptor;
+import org.yakindu.base.types.Operation;
+import org.yakindu.base.types.resource.TypedResourceDescriptionStrategy;
 import org.yakindu.sct.model.sgraph.Statechart;
 
 /**
@@ -25,9 +27,7 @@ import org.yakindu.sct.model.sgraph.Statechart;
  * @author andreas muelder - Initial contribution and API
  * 
  */
-public class SCTResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
-
-	public static final String IS_COMPLEX_TYPE = "IS_COMPLEX_TYPE";
+public class SCTResourceDescriptionStrategy extends TypedResourceDescriptionStrategy {
 
 	@Override
 	public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
@@ -35,5 +35,6 @@ public class SCTResourceDescriptionStrategy extends DefaultResourceDescriptionSt
 			return super.createEObjectDescriptions(eObject, acceptor);
 		return false;
 	}
+	
 
 }

+ 32 - 4
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/validation/ContextPredicateProvider.java

@@ -37,6 +37,7 @@ import static org.yakindu.sct.model.stext.stext.StextPackage.Literals.TRANSITION
 import static org.yakindu.sct.model.stext.stext.StextPackage.Literals.TRANSITION_SPECIFICATION;
 import static org.yakindu.sct.model.stext.stext.StextPackage.Literals.VARIABLE_DEFINITION;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -46,8 +47,13 @@ import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.resource.IEObjectDescription;
 import org.eclipse.xtext.util.Pair;
 import org.eclipse.xtext.util.Tuples;
+import org.yakindu.base.types.Type;
 import org.yakindu.base.types.TypesPackage;
+import org.yakindu.base.types.resource.TypedResourceDescriptionStrategy;
+import org.yakindu.base.types.typesystem.GenericTypeValueProvider;
 import org.yakindu.sct.model.sgraph.SGraphPackage;
+import org.yakindu.sct.model.stext.stext.StextPackage;
+import org.yakindu.sct.model.stext.stext.VariableDefinition;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
@@ -70,18 +76,33 @@ public class ContextPredicateProvider {
 	public static class FeaturedTypePredicate implements Predicate<IEObjectDescription> {
 		public boolean apply(IEObjectDescription input) {
 			EClass eClass = input.getEClass();
-			return SGraphPackage.Literals.SCOPE.isSuperTypeOf(eClass)
-					|| (TypesPackage.Literals.TYPE.isSuperTypeOf(eClass)
-							&& TypesPackage.Literals.DECLARATION.isSuperTypeOf(eClass));
+			
+			return (SGraphPackage.Literals.SCOPE.isSuperTypeOf(eClass)); 
+//					|| (TypesPackage.Literals.DECLARATION.isSuperTypeOf(eClass));
 		}
 	}
 
+	protected static EClass getVariableType(IEObjectDescription ieod) {
+		 EObject eObj = ieod.getEObjectOrProxy();
+		 if (eObj != null && (! eObj.eIsProxy()) ) {
+			 EObject eTS = (EObject) eObj.eGet(TypesPackage.Literals.TYPED_ELEMENT__TYPE_SPECIFIER, false);
+			 if (eTS != null && (! eTS.eIsProxy())) {
+				 EObject eT = (EObject) eObj.eGet(TypesPackage.Literals.TYPE_SPECIFIER__TYPE, false);
+				 if (eT != null) {
+					 return eT.eClass();
+				 }
+			 }
+		 }
+		 return TypesPackage.Literals.TYPE;
+	}
+	
+	
 	public static class EventPredicate extends FeaturedTypePredicate {
 		@Override
 		public boolean apply(IEObjectDescription input) {
 			if (super.apply(input))
 				return true;
-			return TypesPackage.Literals.EVENT.isSuperTypeOf(input.getEClass());
+			return TypesPackage.Literals.EVENT.isSuperTypeOf(input.getEClass())	 || (TypesPackage.Literals.DECLARATION.isSuperTypeOf(input.getEClass()));
 		}
 	}
 
@@ -206,4 +227,11 @@ public class ContextPredicateProvider {
 		}
 		return predicate;
 	}
+	
+	
+	protected static boolean hasComplexType(IEObjectDescription input) {
+		String hasComplexType = input.getUserData(TypedResourceDescriptionStrategy.HAS_COMPLEX_TYPE);
+		return hasComplexType != null && Boolean.valueOf(hasComplexType);
+	}
+
 }

+ 57 - 1
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/validation/STextJavaValidator.java

@@ -43,6 +43,7 @@ import org.eclipse.xtext.validation.CheckType;
 import org.eclipse.xtext.validation.ComposedChecks;
 import org.eclipse.xtext.validation.ValidationMessageAcceptor;
 import org.yakindu.base.base.BasePackage;
+import org.yakindu.base.base.NamedElement;
 import org.yakindu.base.expressions.expressions.AssignmentExpression;
 import org.yakindu.base.expressions.expressions.ElementReferenceExpression;
 import org.yakindu.base.expressions.expressions.Expression;
@@ -84,6 +85,7 @@ import org.yakindu.sct.model.stext.stext.EntryPointSpec;
 import org.yakindu.sct.model.stext.stext.EventDefinition;
 import org.yakindu.sct.model.stext.stext.EventRaisingExpression;
 import org.yakindu.sct.model.stext.stext.EventSpec;
+import org.yakindu.sct.model.stext.stext.EventValueReferenceExpression;
 import org.yakindu.sct.model.stext.stext.ExitEvent;
 import org.yakindu.sct.model.stext.stext.ExitPointSpec;
 import org.yakindu.sct.model.stext.stext.Guard;
@@ -93,6 +95,7 @@ import org.yakindu.sct.model.stext.stext.LocalReaction;
 import org.yakindu.sct.model.stext.stext.OperationDefinition;
 import org.yakindu.sct.model.stext.stext.ReactionEffect;
 import org.yakindu.sct.model.stext.stext.ReactionTrigger;
+import org.yakindu.sct.model.stext.stext.RegularEventSpec;
 import org.yakindu.sct.model.stext.stext.StextPackage;
 import org.yakindu.sct.model.stext.stext.TimeEventSpec;
 import org.yakindu.sct.model.stext.stext.VariableDefinition;
@@ -349,6 +352,31 @@ public class STextJavaValidator extends AbstractSTextJavaValidator implements ST
 
 	}
 
+	
+	@Check(CheckType.FAST)
+	public void checkValueOfNoEvent(EventValueReferenceExpression exp){
+		
+		Expression eventExpr = exp.getValue();
+		
+		EObject element = null;
+		if (eventExpr instanceof ElementReferenceExpression) {
+			element =  ((ElementReferenceExpression) eventExpr).getReference();
+		} else if (eventExpr instanceof FeatureCall) {
+			element = ((FeatureCall) eventExpr).getFeature();
+		}
+		
+		if (element != null && (! (element instanceof Event))) {
+			String elementName = "";
+			if ( element instanceof NamedElement ) {
+				elementName = "'" + ((NamedElement) element).getName() +"' ";
+			}
+			error( elementName + "is no event.",
+					StextPackage.Literals.EVENT_VALUE_REFERENCE_EXPRESSION__VALUE, 0,
+					VALUE_OF_REQUIRES_EVENT);
+		}
+	}
+
+	
 	@Check(CheckType.NORMAL)
 	public void checkValueReferenedBeforeDefined(Scope scope) {
 		EList<Declaration> declarations = scope.getDeclarations();
@@ -619,11 +647,39 @@ public class STextJavaValidator extends AbstractSTextJavaValidator implements ST
 					&& (eventSpec instanceof EntryEvent || eventSpec instanceof ExitEvent)) {
 				error("Entry and exit events are allowed as local reactions only.",
 						StextPackage.Literals.REACTION_TRIGGER__TRIGGERS, INSIGNIFICANT_INDEX,
-						LOCAL_REACTIONS_NOT_ALLOWED);
+						ENTRY_EXIT_TRIGGER_NOT_ALLOWED);
+			}
+		}
+	}
+
+	@Check(CheckType.FAST)
+	public void checkReactionTriggerRegularEvent(ReactionTrigger reactionTrigger) {
+		for (int i=0; i<reactionTrigger.getTriggers().size(); i++) {
+			EventSpec eventSpec = reactionTrigger.getTriggers().get(i);
+			if (eventSpec instanceof RegularEventSpec) {
+
+				Expression eventExpression = ((RegularEventSpec) eventSpec).getEvent();
+				EObject element = null;
+				if (eventExpression instanceof ElementReferenceExpression) {
+					element =  ((ElementReferenceExpression) eventExpression).getReference();
+				} else if (eventExpression instanceof FeatureCall) {
+					element = ((FeatureCall) eventExpression).getFeature();
+				}
+				
+				if (element != null && (! (element instanceof Event))) {
+					String elementName = "";
+					if ( element instanceof NamedElement ) {
+						elementName = "'" + ((NamedElement) element).getName() +"' ";
+					}
+					error("Trigger " + elementName + "is no event.",
+							StextPackage.Literals.REACTION_TRIGGER__TRIGGERS, i,
+							TRIGGER_IS_NO_EVENT);
+				}
 			}
 		}
 	}
 
+	
 	/**
 	 * Only Expressions that produce an effect should be used as actions.
 	 * 

+ 4 - 0
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/validation/STextValidationMessages.java

@@ -36,6 +36,7 @@ public interface STextValidationMessages {
 	public static final String TRANSITION_UNBOUND_DEFAULT_ENTRY_POINT = "Target state has regions without 'default' entries.";
 	public static final String TRANSITION_UNBOUND_NAMED_ENTRY_POINT = "Target state has regions without named entries: ";
 	public static final String TRANSITION_NOT_EXISTING_NAMED_EXIT_POINT = "Source State needs at least one region with the named exit point.";
+	public static final String TRIGGER_IS_NO_EVENT = "Trigger is no event.";
 	public static final String TOP_LEVEL_REGION_ENTRY_HAVE_TO_BE_A_DEFAULT_ENTRY = "Entry of top level region have to be a default entry.";
 	public static final String REGION_UNBOUND_DEFAULT_ENTRY_POINT = "Region must have a 'default' entry.";
 	public static final String REGION_UNBOUND_NAMED_ENTRY_POINT = "Region should have a named entry to support transitions entry specification: ";
@@ -55,5 +56,8 @@ public interface STextValidationMessages {
 	public static final String ERROR_WRONG_ANNOTATION_TARGET_MSG = "Annotation '%s' can not be applied on %s ";
 	public static final String ERROR_WRONG_CONTEXT_ELEMENT_MSG = "Element of type '%s' is not allowed in this context.";
 	public static final String ERROR_WRONG_CONTEXT_ELEMENT_CODE = "ElementNotAllowedInContext";
+	
+	public static final String VALUE_OF_REQUIRES_EVENT = "valueof() expression requires event as argument.";
+
 
 }

+ 4 - 4
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/ContextPredicateProviderTest.java

@@ -229,8 +229,8 @@ public class ContextPredicateProviderTest extends AbstractSTextValidationTest {
 				 * filter.put(key(EVENT_VALUE_REFERENCE_EXPRESSION), EVENTS)
 				 */
 				{ "valueof(e2)", Expression.class.getSimpleName(), INTERNAL_SCOPE, true }, //
-				{ "valueof(myInt)", Expression.class.getSimpleName(), INTERNAL_SCOPE, false }, //
-				{ "valueof(myOpp3())", Expression.class.getSimpleName(), INTERNAL_SCOPE, false }, //
+				// { "valueof(myInt)", Expression.class.getSimpleName(), INTERNAL_SCOPE, true }, // context predicates can't validate this expression. This will be done in the SText validation instead.
+				// { "valueof(myOpp3())", Expression.class.getSimpleName(), INTERNAL_SCOPE, true }, // context predicates can't validate this expression. This will be done in the SText validation instead.
 				/*
 				 * filter.put(key(VARIABLE_DEFINITION, TYPED_ELEMENT__TYPE),
 				 * TYPES)
@@ -238,8 +238,8 @@ public class ContextPredicateProviderTest extends AbstractSTextValidationTest {
 				{ "var x : integer", VariableDefinition.class.getSimpleName(), INTERNAL_SCOPE, true }, //
 				/* filter.put(key(REGULAR_EVENT_SPEC), EVENTS) */
 				{ "e1 / myInt = 0", LocalReaction.class.getSimpleName(), INTERNAL_SCOPE, true }, //
-				{ "myOpp(4) / myInt = 10", LocalReaction.class.getSimpleName(), INTERNAL_SCOPE, false }, //
-				{ "myInt / myInt = 10", LocalReaction.class.getSimpleName(), INTERNAL_SCOPE, false }, //
+				//{ "myOpp(4) / myInt = 10", LocalReaction.class.getSimpleName(), INTERNAL_SCOPE, true }, // context predicates can't validate this expression. This will be done in the SText validation instead.
+				//{ "myInt / myInt = 10", LocalReaction.class.getSimpleName(), INTERNAL_SCOPE, true }, // context predicates can't validate this expression. This will be done in the SText validation instead.
 		});
 	}
 }

+ 59 - 2
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/validation/STextJavaValidatorTest.java

@@ -12,6 +12,7 @@
 package org.yakindu.sct.model.stext.test.validation;
 
 import static org.eclipse.xtext.junit4.validation.AssertableDiagnostics.errorCode;
+import static org.eclipse.xtext.junit4.validation.AssertableDiagnostics.errorMsg;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -231,11 +232,11 @@ public class STextJavaValidatorTest extends AbstractSTextValidationTest implemen
 		String scope = "internal : event a : integer var myVar : integer";
 		EObject model = super.parseExpression("entry / myVar = 5", TransitionSpecification.class.getSimpleName(), scope);
 		AssertableDiagnostics validationResult = tester.validate(model);
-		validationResult.assertError(LOCAL_REACTIONS_NOT_ALLOWED);
+		validationResult.assertError(ENTRY_EXIT_TRIGGER_NOT_ALLOWED);
 
 		model = super.parseExpression("exit / myVar = 5", TransitionSpecification.class.getSimpleName(), scope);
 		validationResult = tester.validate(model);
-		validationResult.assertError(LOCAL_REACTIONS_NOT_ALLOWED);
+		validationResult.assertError(ENTRY_EXIT_TRIGGER_NOT_ALLOWED);
 
 		model = super.parseExpression("oncycle / myVar = 5", TransitionSpecification.class.getSimpleName(), scope);
 		validationResult = tester.validate(model);
@@ -245,6 +246,33 @@ public class STextJavaValidatorTest extends AbstractSTextValidationTest implemen
 		validationResult = tester.validate(model);
 		validationResult.assertOK();
 	}
+	
+	@Test
+	public void checkReactionTriggerRegularEvent(){
+		
+		String scope = "interface : in event e  var x : integer  var y : integer  operation op():integer";
+
+		EObject model = super.parseExpression("e", TransitionSpecification.class.getSimpleName(), scope);
+		AssertableDiagnostics validationResult = tester.validate(model);
+		validationResult.assertOK();
+		
+		model = super.parseExpression("x", TransitionSpecification.class.getSimpleName(), scope);
+		validationResult = tester.validate(model);
+		validationResult.assertError(TRIGGER_IS_NO_EVENT);
+
+		model = super.parseExpression("e, x", TransitionSpecification.class.getSimpleName(), scope);
+		validationResult = tester.validate(model);
+		validationResult.assertError(TRIGGER_IS_NO_EVENT);
+
+		model = super.parseExpression("op()", TransitionSpecification.class.getSimpleName(), scope);
+		validationResult = tester.validate(model);
+		validationResult.assertError(TRIGGER_IS_NO_EVENT);
+		
+		model = super.parseExpression("x, y", TransitionSpecification.class.getSimpleName(), scope);
+		validationResult = tester.validate(model);
+		validationResult.assertAll(errorMsg("Trigger 'x' is no event."), errorMsg("Trigger 'y' is no event."));
+		
+	}
 
 	/**
 	 * @see STextJavaValidator#checkReactionEffectActions(org.yakindu.sct.model.stext.stext.ReactionEffect)
@@ -284,6 +312,8 @@ public class STextJavaValidatorTest extends AbstractSTextValidationTest implemen
 
 	}
 
+	
+	
 	/**
 	 * @see STextJavaValidator#checkEventDefinition(org.yakindu.sct.model.stext.stext.EventDefinition)
 	 */
@@ -352,6 +382,33 @@ public class STextJavaValidatorTest extends AbstractSTextValidationTest implemen
 		//Nothing to do -> this is covered by ContextPredicateProviderTest
 	}
 
+	@Test
+	public void checkValueOfNoEvent(){
+		String decl = "interface: in event e1:integer var x:integer operation op():integer interface i: in event e2:integer var y:integer";
+
+		EObject model = super.parseExpression("valueof(e1)", Expression.class.getSimpleName(), decl);
+		AssertableDiagnostics result = tester.validate(model);
+		result.assertOK();
+	
+		model = super.parseExpression("valueof(i.e2)", Expression.class.getSimpleName(), decl);
+		result = tester.validate(model);
+		result.assertOK();
+	
+		model = super.parseExpression("valueof(x)", Expression.class.getSimpleName(), decl);
+		result = tester.validate(model);
+		result.assertError(VALUE_OF_REQUIRES_EVENT);
+		
+		model = super.parseExpression("valueof(i.y)", Expression.class.getSimpleName(), decl);
+		result = tester.validate(model);
+		result.assertError(VALUE_OF_REQUIRES_EVENT);
+		
+		model = super.parseExpression("valueof(op())", Expression.class.getSimpleName(), decl);
+		result = tester.validate(model);
+		result.assertError(VALUE_OF_REQUIRES_EVENT);
+		
+	}
+	
+	
 	/**
 	 * checks tht each @Check method of {@link STextJavaValidator} has a @Test
 	 * method in this class with the same name