/**
* Copyright (c) 2011 itemis AG and others.
* 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:
* itemis AG - initial API and implementation
*
*/
package org.yakindu.sct.model.stext.validation;
import java.util.Iterator;
import java.util.LinkedList;
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.TreeIterator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.validation.Check;
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;
import org.yakindu.base.expressions.expressions.ExpressionsPackage;
import org.yakindu.base.expressions.expressions.FeatureCall;
import org.yakindu.base.expressions.validation.ExpressionsJavaValidator;
import org.yakindu.base.types.AnnotatableElement;
import org.yakindu.base.types.Annotation;
import org.yakindu.base.types.Declaration;
import org.yakindu.base.types.Direction;
import org.yakindu.base.types.Event;
import org.yakindu.base.types.Operation;
import org.yakindu.base.types.Parameter;
import org.yakindu.base.types.Property;
import org.yakindu.base.types.TypesPackage;
import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
import org.yakindu.sct.domain.extension.DomainRegistry;
import org.yakindu.sct.model.sgraph.Choice;
import org.yakindu.sct.model.sgraph.Entry;
import org.yakindu.sct.model.sgraph.Exit;
import org.yakindu.sct.model.sgraph.ReactionProperty;
import org.yakindu.sct.model.sgraph.Region;
import org.yakindu.sct.model.sgraph.SGraphPackage;
import org.yakindu.sct.model.sgraph.Scope;
import org.yakindu.sct.model.sgraph.ScopedElement;
import org.yakindu.sct.model.sgraph.Statechart;
import org.yakindu.sct.model.sgraph.Synchronization;
import org.yakindu.sct.model.sgraph.Transition;
import org.yakindu.sct.model.sgraph.Trigger;
import org.yakindu.sct.model.sgraph.Vertex;
import org.yakindu.sct.model.sgraph.resource.AbstractSCTResource;
import org.yakindu.sct.model.sgraph.util.ContextElementAdapter;
import org.yakindu.sct.model.sgraph.validation.SCTResourceValidator;
import org.yakindu.sct.model.sgraph.validation.SGraphJavaValidator;
import org.yakindu.sct.model.stext.services.STextGrammarAccess;
import org.yakindu.sct.model.stext.stext.ArgumentedAnnotation;
import org.yakindu.sct.model.stext.stext.DefaultTrigger;
import org.yakindu.sct.model.stext.stext.EntryEvent;
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;
import org.yakindu.sct.model.stext.stext.InterfaceScope;
import org.yakindu.sct.model.stext.stext.InternalScope;
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;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/**
* Several validations for nonsensical expressions.
*
* @author muehlbrandt
* @author muelder
*
*/
@ComposedChecks(validators = { SGraphJavaValidator.class, SCTResourceValidator.class, ExpressionsJavaValidator.class })
public class STextJavaValidator extends AbstractSTextJavaValidator implements STextValidationMessages {
@Inject
private ITypeSystemInferrer typeInferrer;
@Inject
private STextGrammarAccess grammarAccess;
@Inject
private IQualifiedNameProvider nameProvider;
@Inject
@Named(Constants.LANGUAGE_NAME)
private String languageName;
@Inject(optional = true)
@Named(DomainRegistry.DOMAIN_ID)
private String domainID = BasePackage.Literals.DOMAIN_ELEMENT__DOMAIN_ID.getDefaultValueLiteral();
@Check(CheckType.FAST)
public void checkExpression(VariableDefinition expression) {
if (expression.getType() == null || expression.getType().eIsProxy())
return;
typeInferrer.infer(expression, this);
}
@Check(CheckType.FAST)
public void checkExpression(TimeEventSpec expression) {
typeInferrer.infer(expression, this);
}
@Check(CheckType.FAST)
public void checkExpression(Guard expression) {
typeInferrer.infer(expression, this);
}
@Check(CheckType.FAST)
public void checkNoAssignmentInGuard(Guard guard) {
TreeIterator<EObject> eAllContents = guard.eAllContents();
while (eAllContents.hasNext()) {
EObject e = eAllContents.next();
if (e instanceof AssignmentExpression) {
error(GUARD_CONTAINS_ASSIGNMENT, guard, null);
}
}
}
@Check(CheckType.FAST)
public void transitionsWithNoTrigger(Transition trans) {
if (trans.getSource() instanceof Entry || trans.getSource() instanceof Choice
|| trans.getSource() instanceof Synchronization || (trans.getTarget() instanceof Synchronization
&& (trans.getTarget().getIncomingTransitions().size() > 1))) {
return;
}
if (trans.getSource() instanceof org.yakindu.sct.model.sgraph.State) {
org.yakindu.sct.model.sgraph.State state = (org.yakindu.sct.model.sgraph.State) trans.getSource();
if (state.isComposite()) {
for (Region r : state.getRegions()) {
for (Vertex v : r.getVertices()) {
if (v instanceof Exit) {
return;
}
}
}
}
}
if (!STextValidationModelUtils.getExitPointSpecs(trans.getProperties()).isEmpty()) {
return;
}
if (trans.getTrigger() == null) {
warning(ISSUE_TRANSITION_WITHOUT_TRIGGER, trans, null, -1);
}
}
@Check(CheckType.FAST)
public void checkUnusedEntry(final Entry entry) {
if (entry.getParentRegion().getComposite() instanceof org.yakindu.sct.model.sgraph.State
&& entry.getIncomingTransitions().isEmpty()) {
org.yakindu.sct.model.sgraph.State state = (org.yakindu.sct.model.sgraph.State) entry.getParentRegion()
.getComposite();
if (!STextValidationModelUtils.isDefault(entry)) {
boolean hasIncomingTransition = false;
Iterator<Transition> transitionIt = state.getIncomingTransitions().iterator();
while (transitionIt.hasNext() && !hasIncomingTransition) {
Iterator<ReactionProperty> propertyIt = transitionIt.next().getProperties().iterator();
while (propertyIt.hasNext() && !hasIncomingTransition) {
ReactionProperty property = propertyIt.next();
if (property instanceof EntryPointSpec) {
hasIncomingTransition = entry.getName().equals(((EntryPointSpec) property).getEntrypoint());
}
}
}
if (!hasIncomingTransition) {
warning(ENTRY_UNUSED, entry, null, -1);
}
}
}
}
@Check(CheckType.FAST)
public void checkLeftHandAssignment(final AssignmentExpression expression) {
Expression varRef = expression.getVarRef();
if (varRef instanceof FeatureCall) {
EObject referencedObject = ((FeatureCall) varRef).getFeature();
if (!(referencedObject instanceof Property)) {
error(LEFT_HAND_ASSIGNMENT, ExpressionsPackage.Literals.ASSIGNMENT_EXPRESSION__VAR_REF);
}
} else if (varRef instanceof ElementReferenceExpression) {
EObject referencedObject = ((ElementReferenceExpression) varRef).getReference();
if (!(referencedObject instanceof Property)) {
error(LEFT_HAND_ASSIGNMENT, ExpressionsPackage.Literals.ASSIGNMENT_EXPRESSION__VAR_REF);
}
} else {
error(LEFT_HAND_ASSIGNMENT, ExpressionsPackage.Literals.ASSIGNMENT_EXPRESSION__VAR_REF);
}
}
@Check(CheckType.FAST)
public void checkAssignmentToFinalVariable(AssignmentExpression exp) {
Expression varRef = exp.getVarRef();
EObject referencedObject = null;
if (varRef instanceof FeatureCall)
referencedObject = ((FeatureCall) varRef).getFeature();
else if (varRef instanceof ElementReferenceExpression)
referencedObject = ((ElementReferenceExpression) varRef).getReference();
if (referencedObject instanceof VariableDefinition) {
if (((VariableDefinition) referencedObject).isConst()) {
error(ASSIGNMENT_TO_VALUE, ExpressionsPackage.Literals.ASSIGNMENT_EXPRESSION__VAR_REF);
}
}
}
@Check(CheckType.FAST)
public void checkValueDefinitionExpression(VariableDefinition definition) {
// applies only to constants
if (!definition.isConst())
return;
Expression initialValue = definition.getInitialValue();
if (initialValue == null) {
error(CONST_MUST_HAVE_VALUE_MSG, definition, null, CONST_MUST_HAVE_VALUE_CODE);
return;
}
List<Expression> toCheck = Lists.newArrayList(initialValue);
TreeIterator<EObject> eAllContents = initialValue.eAllContents();
while (eAllContents.hasNext()) {
EObject next = eAllContents.next();
if (next instanceof Expression)
toCheck.add((Expression) next);
}
for (Expression expression : toCheck) {
EObject referencedObject = null;
if (expression instanceof FeatureCall)
referencedObject = ((FeatureCall) expression).getFeature();
else if (expression instanceof ElementReferenceExpression)
referencedObject = ((ElementReferenceExpression) expression).getReference();
if (referencedObject instanceof VariableDefinition) {
if (!((VariableDefinition) referencedObject).isConst()) {
error(REFERENCE_TO_VARIABLE, StextPackage.Literals.VARIABLE_DEFINITION__INITIAL_VALUE);
}
}
}
}
@Check(CheckType.NORMAL)
public void checkUnusedVariablesInInternalScope(InternalScope internalScope) {
EList<Declaration> internalScopeDeclarations = internalScope.getDeclarations();
EObject rootContainer = EcoreUtil.getRootContainer(internalScope);
Resource rootRes = getResource(rootContainer);
Statechart statechart = (Statechart) EcoreUtil.getObjectByType(rootRes.getContents(),
SGraphPackage.Literals.STATECHART);
if (statechart == null)
return;
List<ElementReferenceExpression> allUsedElementReferences = EcoreUtil2.getAllContentsOfType(statechart,
ElementReferenceExpression.class);
if (statechart.getSpecification() != null) {
for (Declaration internalDeclaration : internalScopeDeclarations) {
boolean internalDeclarationUsed = false;
for (ElementReferenceExpression elementReference : allUsedElementReferences) {
if (elementReference.getReference().eContainer() instanceof InternalScope) {
if (elementReference.getReference() instanceof VariableDefinition) {
if (((VariableDefinition) elementReference.getReference()).getName()
.equals(internalDeclaration.getName())
&& internalDeclaration instanceof VariableDefinition) {
internalDeclarationUsed = true;
break;
}
} else if (elementReference.getReference() instanceof EventDefinition) {
if (((EventDefinition) elementReference.getReference()).getName().equals(
internalDeclaration.getName()) && internalDeclaration instanceof EventDefinition) {
internalDeclarationUsed = true;
break;
}
} else if (elementReference.getReference() instanceof OperationDefinition) {
if (((OperationDefinition) elementReference.getReference()).getName()
.equals(internalDeclaration.getName())
&& internalDeclaration instanceof OperationDefinition) {
internalDeclarationUsed = true;
break;
}
}
}
}
if (!internalDeclarationUsed) {
if (internalDeclaration instanceof VariableDefinition
|| internalDeclaration instanceof EventDefinition
|| internalDeclaration instanceof OperationDefinition)
warning(INTERNAL_DECLARATION_UNUSED, internalDeclaration, null, -1);
}
}
}
}
@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();
Set<QualifiedName> defined = Sets.newHashSet();
for (Declaration declaration : declarations) {
if (declaration instanceof VariableDefinition) {
VariableDefinition definition = (VariableDefinition) declaration;
if (!definition.isConst())
return;
Expression initialValue = definition.getInitialValue();
List<Expression> toCheck = Lists.newArrayList(initialValue);
TreeIterator<EObject> eAllContents = initialValue.eAllContents();
while (eAllContents.hasNext()) {
EObject next = eAllContents.next();
if (next instanceof Expression)
toCheck.add((Expression) next);
}
for (Expression expression : toCheck) {
EObject referencedObject = null;
if (expression instanceof FeatureCall)
referencedObject = ((FeatureCall) expression).getFeature();
else if (expression instanceof ElementReferenceExpression)
referencedObject = ((ElementReferenceExpression) expression).getReference();
if (referencedObject instanceof VariableDefinition) {
if (!defined.contains(nameProvider.getFullyQualifiedName(referencedObject)))
error(REFERENCE_CONSTANT_BEFORE_DEFINED, definition,
StextPackage.Literals.VARIABLE_DEFINITION__INITIAL_VALUE);
}
}
defined.add(nameProvider.getFullyQualifiedName(definition));
}
}
}
@Check(CheckType.NORMAL)
public void checkUnusedExit(final Exit exit) {
if (exit.getParentRegion().getComposite() instanceof org.yakindu.sct.model.sgraph.State
&& exit.getOutgoingTransitions().isEmpty()) {
org.yakindu.sct.model.sgraph.State state = (org.yakindu.sct.model.sgraph.State) exit.getParentRegion()
.getComposite();
if (!STextValidationModelUtils.isDefault(exit)) {
boolean hasOutgoingTransition = false;
Iterator<Transition> transitionIt = state.getOutgoingTransitions().iterator();
while (transitionIt.hasNext() && !hasOutgoingTransition) {
Transition transition = transitionIt.next();
hasOutgoingTransition = STextValidationModelUtils.isDefaultExitTransition(transition) ? true
: STextValidationModelUtils.isNamedExitTransition(transition, exit.getName());
}
if (!hasOutgoingTransition) {
error(EXIT_UNUSED, exit, null, -1);
}
} else {
boolean hasOutgoingTransition = false;
Iterator<Transition> transitionIt = state.getOutgoingTransitions().iterator();
while (transitionIt.hasNext() && !hasOutgoingTransition) {
hasOutgoingTransition = STextValidationModelUtils.isDefaultExitTransition(transitionIt.next());
}
if (!hasOutgoingTransition) {
error(EXIT_DEFAULT_UNUSED, exit, null, -1);
}
}
}
}
@Check(CheckType.FAST)
public void checkTransitionPropertySpec(final Transition transition) {
for (ReactionProperty property : transition.getProperties()) {
if (property instanceof EntryPointSpec) {
if (transition.getTarget() instanceof org.yakindu.sct.model.sgraph.State) {
org.yakindu.sct.model.sgraph.State state = (org.yakindu.sct.model.sgraph.State) transition
.getTarget();
if (!state.isComposite()) {
warning(TRANSITION_ENTRY_SPEC_NOT_COMPOSITE, transition, null, -1);
}
}
} else if (property instanceof ExitPointSpec) {
final ExitPointSpec exitPointSpec = (ExitPointSpec) property;
if (transition.getSource() instanceof org.yakindu.sct.model.sgraph.State) {
org.yakindu.sct.model.sgraph.State state = (org.yakindu.sct.model.sgraph.State) transition
.getSource();
if (!state.isComposite()) {
warning(TRANSITION_EXIT_SPEC_NOT_COMPOSITE, transition, null, -1);
} else {
// Validate an exit point is continued on one transition
// only.
for (Transition t : state.getOutgoingTransitions()) {
if (transition != t && STextValidationModelUtils.isNamedExitTransition(t,
exitPointSpec.getExitpoint())) {
warning(TRANSITION_EXIT_SPEC_ON_MULTIPLE_SIBLINGS, transition, null, -1);
}
}
// Validate the state has minimally one named exit
// region
boolean hasExit = false;
Iterator<Region> regionIter = state.getRegions().iterator();
while (regionIter.hasNext() && !hasExit) {
Iterator<Exit> exitIter = STextValidationModelUtils.getExits(regionIter.next().eContents())
.iterator();
while (exitIter.hasNext() && !hasExit) {
Exit exit = exitIter.next();
hasExit = exitPointSpec.getExitpoint().equals(exit.getName());
}
}
if (!hasExit) {
error(TRANSITION_NOT_EXISTING_NAMED_EXIT_POINT, transition, null, -1);
}
}
}
}
}
}
@Check(CheckType.NORMAL)
public void checkUnboundEntryPoints(final org.yakindu.sct.model.sgraph.State state) {
if (state.isComposite()) {
final List<Transition>[] transitions = STextValidationModelUtils
.getEntrySpecSortedTransitions(state.getIncomingTransitions());
Map<Region, List<Entry>> regions = null;
// first list contains Transitions without entry spec
if (!transitions[0].isEmpty()) {
regions = STextValidationModelUtils.getRegionsWithoutDefaultEntry(state.getRegions());
if (!regions.isEmpty()) {
for (Transition transition : transitions[0]) {
error(TRANSITION_UNBOUND_DEFAULT_ENTRY_POINT, transition, null, -1);
}
for (Region region : regions.keySet()) {
error(REGION_UNBOUND_DEFAULT_ENTRY_POINT, region, null, -1);
}
}
}
// second list contains Transitions with entry spec
if (!transitions[1].isEmpty()) {
if (regions == null) {
regions = STextValidationModelUtils.getRegionsWithoutDefaultEntry(state.getRegions());
}
for (Transition transition : transitions[1]) {
boolean hasTargetEntry = true;
for (ReactionProperty property : transition.getProperties()) {
if (property instanceof EntryPointSpec) {
EntryPointSpec spec = (EntryPointSpec) property;
String specName = "'" + spec.getEntrypoint() + "'";
for (Region region : regions.keySet()) {
boolean hasEntry = false;
for (Entry entry : regions.get(region)) {
if (entry.getName().equals(spec.getEntrypoint())) {
hasEntry = true;
break;
}
}
if (!hasEntry) {
error(REGION_UNBOUND_NAMED_ENTRY_POINT + specName, region, null, -1);
hasTargetEntry = false;
}
}
if (!hasTargetEntry) {
error(TRANSITION_UNBOUND_NAMED_ENTRY_POINT + specName, transition, null, -1);
}
}
}
}
}
}
}
@Check(CheckType.NORMAL)
public void checkTopLeveEntryIsDefaultEntry(final Entry entry) {
Region parentRegion = entry.getParentRegion();
EObject eContainer = parentRegion.eContainer();
boolean isTopLevelRegionEntry = eContainer instanceof Statechart;
// 1. check if is toplevel
if (isTopLevelRegionEntry) {
boolean isDefaultEntry = STextValidationModelUtils.isDefault(entry);
// 2. check if is default entry
if (!isDefaultEntry) {
Map<Region, List<Entry>> regionsWithoutDefaultEntry = STextValidationModelUtils
.getRegionsWithoutDefaultEntry(Lists.newArrayList(parentRegion));
List<Entry> list = regionsWithoutDefaultEntry.get(parentRegion);
if (list != null)
error(TOP_LEVEL_REGION_ENTRY_HAVE_TO_BE_A_DEFAULT_ENTRY, entry, SGraphPackage.Literals.ENTRY__KIND,
-1);
else
warning(TOP_LEVEL_REGION_ENTRY_HAVE_TO_BE_A_DEFAULT_ENTRY, entry,
SGraphPackage.Literals.ENTRY__KIND, -1);
}
}
}
@Check(CheckType.FAST)
public void checkOperationArguments_FeatureCall(final FeatureCall call) {
if (call.getFeature() instanceof Operation) {
Operation operation = (Operation) call.getFeature();
assertOperationArguments(operation, call.getArgs());
}
}
@Check(CheckType.FAST)
public void checkAnnotationArguments(ArgumentedAnnotation annotation) {
if (annotation.getArgs().size() != annotation.getType().getProperties().size()) {
error(String.format(WRONG_NUMBER_OF_ARGUMENTS_MSG, annotation.getType().getProperties()), null,
WRONG_NUMBER_OF_ARGUMENTS_CODE);
}
}
@Check(CheckType.FAST)
public void checkOperationArguments_TypedElementReferenceExpression(final ElementReferenceExpression call) {
if (call.getReference() instanceof Operation) {
Operation operation = (Operation) call.getReference();
assertOperationArguments(operation, call.getArgs());
}
}
public void assertOperationArguments(Operation operation, List<Expression> args) {
EList<Parameter> parameters = operation.getParameters();
if ((operation.isVariadic() && operation.getVarArgIndex() > args.size())
|| !operation.isVariadic() && parameters.size() != args.size()) {
error(String.format(WRONG_NUMBER_OF_ARGUMENTS_MSG, parameters), null, WRONG_NUMBER_OF_ARGUMENTS_CODE);
}
}
@Check(CheckType.FAST)
public void checkVarArgParameterIsLast(Operation operation) {
if (operation.isVariadic() && operation.getVarArgIndex() != operation.getParameters().size() - 1) {
error(VAR_ARGS_LAST_MSG, operation.getParameters().get(operation.getVarArgIndex()), null,
VAR_ARGS_LAST_CODE);
}
}
@Check(CheckType.FAST)
public void checkAssignmentExpression(final AssignmentExpression exp) {
final String name = getVariableName(exp);
List<AssignmentExpression> contents = EcoreUtil2.eAllOfType(exp, AssignmentExpression.class);
contents.remove(exp);
Iterable<AssignmentExpression> filter = Iterables.filter(contents, new Predicate<AssignmentExpression>() {
public boolean apply(final AssignmentExpression ex) {
String variableName = getVariableName(ex);
return variableName.equals(name);
}
});
if (Iterables.size(filter) > 0) {
error(ASSIGNMENT_EXPRESSION, null);
}
}
@Check(CheckType.FAST)
public void checkFeatureCall(FeatureCall call) {
if (call.eContainer() instanceof FeatureCall) {
return;
}
if (call.getFeature() instanceof Scope) {
error("A variable, event or operation is required.", ExpressionsPackage.Literals.FEATURE_CALL__FEATURE,
INSIGNIFICANT_INDEX, FEATURE_CALL_TO_SCOPE);
}
}
@Check(CheckType.FAST)
public void checkFeatureCall(ElementReferenceExpression call) {
if (call.eContainer() instanceof FeatureCall) {
return;
}
if (call.getReference() instanceof Scope) {
error("A variable, event or operation is required.",
ExpressionsPackage.Literals.ELEMENT_REFERENCE_EXPRESSION__REFERENCE, INSIGNIFICANT_INDEX,
FEATURE_CALL_TO_SCOPE);
}
}
@Check(CheckType.FAST)
public void checkReactionTrigger(ReactionTrigger reactionTrigger) {
for (EventSpec eventSpec : reactionTrigger.getTriggers()) {
if (!(reactionTrigger.eContainer() instanceof LocalReaction)
&& (eventSpec instanceof EntryEvent || eventSpec instanceof ExitEvent)) {
error("Entry and exit events are allowed as local reactions only.",
StextPackage.Literals.REACTION_TRIGGER__TRIGGERS, INSIGNIFICANT_INDEX,
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.
*
* @param effect
*/
@Check(CheckType.FAST)
public void checkReactionEffectActions(ReactionEffect effect) {
for (Expression exp : effect.getActions()) {
if (!(exp instanceof AssignmentExpression) && !(exp instanceof EventRaisingExpression)) {
if (exp instanceof FeatureCall) {
checkFeatureCallEffect((FeatureCall) exp);
} else if (exp instanceof ElementReferenceExpression) {
checkElementReferenceEffect((ElementReferenceExpression) exp);
} else {
error("Action has no effect.", StextPackage.Literals.REACTION_EFFECT__ACTIONS,
effect.getActions().indexOf(exp), FEATURE_CALL_HAS_NO_EFFECT);
}
}
}
}
protected void checkFeatureCallEffect(FeatureCall call) {
if (call.getFeature() != null && call.getFeature() instanceof Declaration
&& !(call.getFeature() instanceof Operation)) {
if (call.getFeature() instanceof Property) {
error("Access to property '" + nameProvider.getFullyQualifiedName(call.getFeature())
+ "' has no effect.", call, ExpressionsPackage.Literals.FEATURE_CALL__FEATURE,
INSIGNIFICANT_INDEX, FEATURE_CALL_HAS_NO_EFFECT);
} else if (call.getFeature() instanceof Event) {
error("Access to event '" + nameProvider.getFullyQualifiedName(call.getFeature()) + "' has no effect.",
call, ExpressionsPackage.Literals.FEATURE_CALL__FEATURE, INSIGNIFICANT_INDEX,
FEATURE_CALL_HAS_NO_EFFECT);
} else {
error("Access to feature '" + nameProvider.getFullyQualifiedName(call.getFeature())
+ "' has no effect.", call, ExpressionsPackage.Literals.FEATURE_CALL__FEATURE,
INSIGNIFICANT_INDEX, FEATURE_CALL_HAS_NO_EFFECT);
}
}
}
private String getVariableName(AssignmentExpression exp) {
Expression varRef = exp.getVarRef();
if (varRef instanceof ElementReferenceExpression
&& ((ElementReferenceExpression) varRef).getReference() instanceof Property) {
Property reference = (Property) ((ElementReferenceExpression) varRef).getReference();
return reference.getName();
} else if (varRef instanceof FeatureCall && ((FeatureCall) varRef).getFeature() instanceof Property) {
Property reference = (Property) ((FeatureCall) varRef).getFeature();
return reference.getName();
}
return null;
}
@Check(CheckType.FAST)
public void checkEventDefinition(EventDefinition event) {
if (event.eContainer() instanceof InterfaceScope && event.getDirection() == Direction.LOCAL) {
error(LOCAL_DECLARATIONS, TypesPackage.Literals.EVENT__DIRECTION);
}
if (event.eContainer() instanceof InternalScope && event.getDirection() != Direction.LOCAL) {
error(IN_OUT_DECLARATIONS, TypesPackage.Literals.EVENT__DIRECTION);
}
}
@Check(CheckType.FAST)
public void checkExitPointSpecWithTrigger(Transition t) {
if (!STextValidationModelUtils.getExitPointSpecs(t.getProperties()).isEmpty() && t.getTrigger() != null
&& t.getSource() instanceof org.yakindu.sct.model.sgraph.State) {
error(EXITPOINTSPEC_WITH_TRIGGER, t, null, -1);
}
}
@Check(CheckType.FAST)
public void checkInterfaceScope(ScopedElement statechart) {
List<InterfaceScope> defaultInterfaces = new LinkedList<InterfaceScope>();
for (Scope scope : statechart.getScopes()) {
if (scope instanceof InterfaceScope && ((InterfaceScope) scope).getName() == null) {
defaultInterfaces.add((InterfaceScope) scope);
}
}
if (defaultInterfaces.size() > 1) {
for (InterfaceScope scope : defaultInterfaces) {
error(ONLY_ONE_INTERFACE, scope, grammarAccess.getInterfaceScopeAccess().getInterfaceKeyword_1(),
ValidationMessageAcceptor.INSIGNIFICANT_INDEX, ONLY_ONE_INTERFACE);
}
}
}
@Check(CheckType.FAST)
public void checkChoiceWithoutDefaultTransition(final Choice choice) {
boolean found = false;
for (Transition transition : choice.getOutgoingTransitions()) {
Trigger trigger = transition.getTrigger();
if (isDefault(trigger)) {
found = true;
}
}
if (!found)
warning(CHOICE_ONE_OUTGOING_DEFAULT_TRANSITION, SGraphPackage.Literals.VERTEX__OUTGOING_TRANSITIONS);
}
@Check(CheckType.FAST)
public void checkAnnotationTarget(final AnnotatableElement element) {
EList<Annotation> annotations = element.getAnnotations();
for (Annotation annotation : annotations) {
EList<EObject> targets = annotation.getType().getTargets();
boolean found = Iterables.any(targets, new Predicate<EObject>() {
@Override
public boolean apply(EObject input) {
return ((EClass) input).isInstance(element);
}
});
if (!found) {
error(String.format(ERROR_WRONG_ANNOTATION_TARGET_MSG, annotation.getType().getName(),
element.eClass()), null,
element.getAnnotations().indexOf(annotation), ERROR_WRONG_ANNOTATION_TARGET_CODE);
}
}
}
protected void checkElementReferenceEffect(ElementReferenceExpression refExp) {
if (!(refExp.getReference() instanceof Operation)) {
if (refExp.getReference() instanceof Property) {
error("Access to property '" + nameProvider.getFullyQualifiedName(refExp.getReference())
+ "' has no effect.", refExp,
ExpressionsPackage.Literals.ELEMENT_REFERENCE_EXPRESSION__REFERENCE, INSIGNIFICANT_INDEX,
FEATURE_CALL_HAS_NO_EFFECT);
} else if (refExp.getReference() instanceof Event) {
error("Access to event '" + nameProvider.getFullyQualifiedName(refExp.getReference())
+ "' has no effect.", refExp,
ExpressionsPackage.Literals.ELEMENT_REFERENCE_EXPRESSION__REFERENCE, INSIGNIFICANT_INDEX,
FEATURE_CALL_HAS_NO_EFFECT);
} else {
error("Access to feature '" + nameProvider.getFullyQualifiedName(refExp.getReference())
+ "' has no effect.", refExp,
ExpressionsPackage.Literals.ELEMENT_REFERENCE_EXPRESSION__REFERENCE, INSIGNIFICANT_INDEX,
FEATURE_CALL_HAS_NO_EFFECT);
}
}
}
protected boolean isDefault(Trigger trigger) {
return trigger == null || trigger instanceof DefaultTrigger
|| ((trigger instanceof ReactionTrigger) && ((ReactionTrigger) trigger).getTriggers().size() == 0
&& ((ReactionTrigger) trigger).getGuard() == null);
}
@Override
protected String getCurrentLanguage(Map<Object, Object> context, EObject eObject) {
Resource eResource = eObject.eResource();
if (eResource instanceof XtextResource) {
return super.getCurrentLanguage(context, eObject);
} else if (eResource instanceof AbstractSCTResource) {
return ((AbstractSCTResource) eResource).getLanguageName();
}
return "";
}
protected void error(String message, EObject source, Keyword keyword, int index, String code) {
final String[] issueData = null;
ICompositeNode rootNode = NodeModelUtils.findActualNodeFor(source);
if (rootNode != null) {
INode child = findNode(source, false, rootNode, keyword, new int[] { index });
if (child != null) {
int offset = child.getTotalOffset();
int length = child.getTotalLength();
getMessageAcceptor().acceptError(message, source, offset, length, code, issueData);
return;
}
}
error(message, source, (EStructuralFeature) null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, code);
}
private INode findNode(EObject source, boolean sourceFound, INode root, Keyword keyword, int[] index) {
if (sourceFound && root.getSemanticElement() != source) {
return null;
}
if (root.getSemanticElement() == source) {
sourceFound = true;
}
EObject grammarElement = root.getGrammarElement();
// .equals or == does not work because sub grammars use their own
// Modules with custom
// grammarAccess instance and .equals is not overwritten.
if (grammarElement instanceof Keyword && keyword.getValue().equals(((Keyword) grammarElement).getValue())) {
if (index[0] != INSIGNIFICANT_INDEX) {
index[0]--;
}
if (index[0] == 0 || index[0] == INSIGNIFICANT_INDEX) {
return root;
}
}
if (root instanceof ICompositeNode) {
ICompositeNode node = (ICompositeNode) root;
for (INode child : node.getChildren()) {
INode result = findNode(source, sourceFound, child, keyword, index);
if (result != null) {
return result;
}
}
}
return null;
}
@Override
protected List<EPackage> getEPackages() {
List<EPackage> result = super.getEPackages();
result.add(ExpressionsPackage.eINSTANCE);
return result;
}
protected Resource getResource(EObject context) {
final ContextElementAdapter provider = (ContextElementAdapter) EcoreUtil.getExistingAdapter(context.eResource(),
ContextElementAdapter.class);
if (provider == null) {
return context.eResource();
} else {
return provider.getElement().eResource();
}
}
@Override
protected boolean isResponsible(Map<Object, Object> context, EObject eObject) {
boolean isResponsible = super.isResponsible(context, eObject);
if (!isResponsible)
return false;
Statechart statechart = getStatechart(eObject);
if ((statechart != null) && !domainID.equals(statechart.getDomainID())) {
return false;
}
return true;
}
protected Statechart getStatechart(EObject context) {
final ContextElementAdapter provider = (ContextElementAdapter) EcoreUtil.getExistingAdapter(context.eResource(),
ContextElementAdapter.class);
if (provider == null) {
return EcoreUtil2.getContainerOfType(context, Statechart.class);
} else {
return (Statechart) EcoreUtil.getObjectByType(provider.getElement().eResource().getContents(),
SGraphPackage.Literals.STATECHART);
}
}
}