/***************************************************************************** * Copyright (c) 2011 CEA LIST. * * * 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: * CEA LIST - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.alf.validation; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.OperationHistoryFactory; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; 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.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.papyrus.infra.core.utils.EditorUtils; import org.eclipse.papyrus.uml.alf.alf.AcceptBlock; import org.eclipse.papyrus.uml.alf.alf.AcceptStatement; import org.eclipse.papyrus.uml.alf.alf.AlfPackage; import org.eclipse.papyrus.uml.alf.alf.AssignmentOperator; import org.eclipse.papyrus.uml.alf.alf.ClassificationClause; import org.eclipse.papyrus.uml.alf.alf.ClassificationFromClause; import org.eclipse.papyrus.uml.alf.alf.ClassificationToClause; import org.eclipse.papyrus.uml.alf.alf.ClassifyStatement; import org.eclipse.papyrus.uml.alf.alf.ConcurrentClauses; import org.eclipse.papyrus.uml.alf.alf.DoStatement; import org.eclipse.papyrus.uml.alf.alf.DocumentedStatement; import org.eclipse.papyrus.uml.alf.alf.ForStatement; import org.eclipse.papyrus.uml.alf.alf.IfStatement; import org.eclipse.papyrus.uml.alf.alf.InstanceCreationInvocationStatement; import org.eclipse.papyrus.uml.alf.alf.InvocationOrAssignementOrDeclarationStatement; import org.eclipse.papyrus.uml.alf.alf.LocalNameDeclarationStatement; import org.eclipse.papyrus.uml.alf.alf.LoopVariableDefinition; import org.eclipse.papyrus.uml.alf.alf.NameExpression; import org.eclipse.papyrus.uml.alf.alf.NonEmptyStatementSequence; import org.eclipse.papyrus.uml.alf.alf.NonFinalClause; import org.eclipse.papyrus.uml.alf.alf.PropertyCallExpression; import org.eclipse.papyrus.uml.alf.alf.QualifiedNameList; import org.eclipse.papyrus.uml.alf.alf.QualifiedNameWithBinding; import org.eclipse.papyrus.uml.alf.alf.ReturnStatement; import org.eclipse.papyrus.uml.alf.alf.StatementSequence; import org.eclipse.papyrus.uml.alf.alf.SuffixExpression; import org.eclipse.papyrus.uml.alf.alf.SuperInvocationStatement; import org.eclipse.papyrus.uml.alf.alf.ThisInvocationStatement; import org.eclipse.papyrus.uml.alf.alf.TupleElement; import org.eclipse.papyrus.uml.alf.alf.UnqualifiedName; import org.eclipse.papyrus.uml.alf.alf.VariableDeclarationCompletion; import org.eclipse.papyrus.uml.alf.alf.WhileStatement; import org.eclipse.papyrus.uml.alf.scoping.AlfPartialScope; import org.eclipse.papyrus.uml.alf.scoping.AlfScopeProvider; import org.eclipse.papyrus.uml.alf.validation.AbstractAlfJavaValidator; import org.eclipse.papyrus.uml.alf.validation.typing.AssignmentPolicy; import org.eclipse.papyrus.uml.alf.validation.typing.ErrorTypeFacade; import org.eclipse.papyrus.uml.alf.validation.typing.MultiplicityFacade; import org.eclipse.papyrus.uml.alf.validation.typing.MultiplicityFacadeFactory; import org.eclipse.papyrus.uml.alf.validation.typing.SignatureFacade; import org.eclipse.papyrus.uml.alf.validation.typing.TypeExpression; import org.eclipse.papyrus.uml.alf.validation.typing.TypeExpressionFactory; import org.eclipse.papyrus.uml.alf.validation.typing.TypeFacade; import org.eclipse.papyrus.uml.alf.validation.typing.TypeFacadeFactory; import org.eclipse.papyrus.uml.alf.validation.typing.TypeUtils; import org.eclipse.papyrus.uml.alf.validation.typing.VoidFacade; import org.eclipse.papyrus.uml.extensionpoints.library.RegisteredLibrary; import org.eclipse.papyrus.uml.extensionpoints.utils.Util; import org.eclipse.swt.widgets.Shell; import org.eclipse.uml2.uml.Class; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.Comment; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Model; import org.eclipse.uml2.uml.Namespace; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Package; import org.eclipse.uml2.uml.PackageImport; import org.eclipse.uml2.uml.Parameter; import org.eclipse.uml2.uml.ParameterDirectionKind; import org.eclipse.uml2.uml.UMLFactory; import org.eclipse.uml2.uml.resource.UMLResource; import org.eclipse.uml2.uml.resource.UMLResource.Factory; import org.eclipse.uml2.uml.util.UMLUtil.UML2EcoreConverter; import org.eclipse.xtext.validation.Check; import org.eclipse.xtext.validation.EValidatorRegistrar; public class AlfJavaValidator extends AbstractAlfJavaValidator { private static Element contextElement ; private static Classifier contextClassifier ; private static Namespace model ; private static Package alfStandardLibrary ; // TODO: include the library as part of the plug-in public static PredefinedBehaviorsAndTypesUtils predefinedBehaviorsAndTypes ; public static Package getAlfStandardLibrary() { return alfStandardLibrary; } public static boolean validate() { return true ; } public static Namespace getModel() { return model; } public static void setModel(Namespace model) { AlfJavaValidator.model = model; } public static void setContextElement(Element _contextElement) { contextElement = _contextElement ; } public static Element getContextElement() { return contextElement ; } public static Classifier getContextClassifier() { return contextClassifier ; } public static void setContextClassifier(Classifier contextClassifier) { AlfJavaValidator.contextClassifier = contextClassifier ; alfStandardLibrary = null ; //if (alfStandardLibrary == null) { for (PackageImport pImport : contextClassifier.getModel().getPackageImports()) { Package p = pImport.getImportedPackage() ; if (p.getQualifiedName().equals("Alf")) { //alfStandardLibrary = (Package)p.getOwnedMembers().get(0) ; alfStandardLibrary = (Package)p ; } } //} if (alfStandardLibrary != null) { predefinedBehaviorsAndTypes = new PredefinedBehaviorsAndTypesUtils() ; predefinedBehaviorsAndTypes.init(alfStandardLibrary) ; } else { String question = "The context model " + contextClassifier.getModel().getName() + " does not import the standard Alf library. This import is required for static validation of Alf expressions and statements. \n\n Do you want to generate this import?" ; boolean doGenerateImport = MessageDialog.openQuestion( new Shell(), "Alf editor", question); if (doGenerateImport) { RegisteredLibrary[] libraries = RegisteredLibrary.getRegisteredLibraries() ; RegisteredLibrary alfLibrary = null ; for (RegisteredLibrary l : libraries) { if (l.getName().equals("AlfLibrary")) alfLibrary = l ; } if (alfLibrary != null) { // Creates and executes the update command UpdateImportCommand updateCommand = new UpdateImportCommand(contextClassifier.getModel(), alfLibrary); try { OperationHistoryFactory.getOperationHistory().execute(updateCommand, new NullProgressMonitor(), null); setContextClassifier(contextClassifier) ; } catch (ExecutionException e) { System.err.println(e); } } else { MessageDialog.openError( new Shell(), "Alf editor", "Could not find standard Alf library") ; } } } } /** * @author CEA LIST * * A command for updating the context UML model */ protected static class UpdateImportCommand extends AbstractTransactionalCommand { private Model model; private RegisteredLibrary library ; /* * (non-Javadoc) * * @see * org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand#doExecuteWithResult(org.eclipse.core.runtime.IProgressMonitor * , org.eclipse.core.runtime.IAdaptable) */ @Override protected CommandResult doExecuteWithResult(IProgressMonitor arg0, IAdaptable arg1) throws ExecutionException { URI libraryUri = library.uri; ResourceSet resourceSet = Util.getResourceSet(contextClassifier) ; Resource libraryResource = resourceSet.getResource(libraryUri, true) ; Package libraryObject = (Package)libraryResource.getContents().get(0) ; model.createPackageImport(libraryObject) ; return CommandResult.newOKCommandResult(model); } public UpdateImportCommand(Model model, RegisteredLibrary library) { super(EditorUtils.getTransactionalEditingDomain(), "Model Update", getWorkspaceFiles(model)); this.model = model ; this.library = library ; //this.operation = operation; } } @Override public void register(EValidatorRegistrar registrar) { // alf validator is not registered for a specific language super.register(registrar) ; } /** * @param tupleElement * * Checks the following rule: * 1. the expression associated with the tuple must not encapsulate any error */ @Check public void checkTupleElement(TupleElement tupleElement) { TypeExpression exp = new TypeUtils().getTypeOfExpression(tupleElement.getArgument()) ; if (exp.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)exp.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } } /** * @param statement LocalNameDeclarationStatement * * Checks the following rules: * 1. the local variable name must be available * 2. the type must be resolved * 3. the init expression must be type/multiplicity compatible with the variable type */ @Check public void checkLocalNameDeclarationStatement(LocalNameDeclarationStatement statement) { // 1. checks that the local variable name is available if (statement.getVarName() != null) { AlfPartialScope variablesScope = AlfScopeProvider.scopingTool.getVisibleVariablesOrParametersOrProperties(statement) ; List<EObject> resolved = variablesScope.resolveByName(statement.getVarName()) ; if (! resolved.isEmpty()) {// name is already used // needs to determine if the scoping level where it is used is conflicting (i.e., it is in the first scoping level) if (variablesScope.getScopingLevel(resolved.get(0)) == 0 && resolved.get(0) != statement) { // There is a name conflict error("Local name " + statement.getVarName() + " is not available" , AlfPackage.eINSTANCE.getLocalNameDeclarationStatement_VarName()) ; } } } // 2. checks that type can be resolved, and that potentially required template bindings are specified TypeFacade variableType = null ; if (statement.getType() != null) { variableType = TypeFacadeFactory.eInstance.createVoidFacade(statement.getType()) ; if (variableType instanceof ErrorTypeFacade) { // Type could not be resolved ErrorTypeFacade error = (ErrorTypeFacade)variableType ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; variableType = null ; } else if (variableType.isATemplate()){ if (statement.getType().getBinding() == null) { String errorMessage = variableType.getLabel() + " is a template. All its parameters shall be bound." ; error(errorMessage, statement, AlfPackage.eINSTANCE.getLocalNameDeclarationStatement_Type(), INSIGNIFICANT_INDEX) ; } } } // 3. checks the init expression if (statement.getInit() != null) { TypeExpression typeOfInit = new TypeUtils().getTypeOfSequenceElement(statement.getInit()) ; // first checks that init expression is valid if (typeOfInit.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfInit.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { // Type expression is valid // In the case where type has been validated at step 2., checks if the type of init expression is compatible with variable type if (variableType != null) { int lowerBound = statement.isMultiplicityIndicator() ? 0 : 1 ; int upperBound = statement.isMultiplicityIndicator() ? -1 : 1 ; boolean isUnique = statement.isMultiplicityIndicator() ? false : true ; boolean isOrdered = statement.isMultiplicityIndicator() ? true : false ; TypeExpression expectedType = TypeExpressionFactory.eInstance.createTypeExpression(variableType, lowerBound, upperBound, isUnique, isOrdered) ; if (expectedType.isCompatibleWithMe(typeOfInit) == 0) { error("Found an expression of type " + typeOfInit.getLabel() + ". Expecting an expression of type " + expectedType.getLabel(), AlfPackage.eINSTANCE.getLocalNameDeclarationStatement_Init()) ; } } } } } /** * @param statement IfStatement * * Checks the following rule: * 1. The condition associated with each clause must be a boolean expression */ @Check public void checkIfStatement(IfStatement statement) { for (ConcurrentClauses concurrentClause : statement.getSequentialClausses().getConccurentClauses()) { for (NonFinalClause nonFinalClause : concurrentClause.getNonFinalClause()) { TypeExpression typeOfCondition = new TypeUtils().getTypeOfExpression(nonFinalClause.getCondition()) ; if (typeOfCondition.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfCondition.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { if (TypeExpressionFactory.eInstance.createTypeExpression(TypeUtils._boolean).isCompatibleWithMe(typeOfCondition) == 0) { String errorMessage = "Expecting an expression of type Boolean. Found an expression of type " + typeOfCondition.getLabel() ; error(errorMessage, nonFinalClause, AlfPackage.eINSTANCE.getNonFinalClause_Condition(), INSIGNIFICANT_INDEX) ; } } } } } /** * @param statement WhileStatement * * Checks the following rule: * 1. The condition associated with the while must be a boolean expression */ @Check public void checkWhileStatement(WhileStatement statement) { TypeExpression typeOfCondition = new TypeUtils().getTypeOfExpression(statement.getCondition()) ; if (typeOfCondition.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfCondition.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { if (TypeExpressionFactory.eInstance.createTypeExpression(TypeUtils._boolean).isCompatibleWithMe(typeOfCondition) == 0) { String errorMessage = "Expecting an expression of type Boolean. Found an expression of type " + typeOfCondition.getLabel() ; error(errorMessage, AlfPackage.eINSTANCE.getWhileStatement_Condition()) ; } } } /** * @param statement DoStatement * * Checks the following rule: * 1. The condition associated with the DoStatement must be a boolean expression */ @Check public void checkDoStatement(DoStatement statement) { TypeExpression typeOfCondition = new TypeUtils().getTypeOfExpression(statement.getCondition()) ; if (typeOfCondition.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfCondition.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { if (TypeExpressionFactory.eInstance.createTypeExpression(TypeUtils._boolean).isCompatibleWithMe(typeOfCondition) == 0) { String errorMessage = "Expecting an expression of type Boolean. Found an expression of type " + typeOfCondition.getLabel() ; error(errorMessage, AlfPackage.eINSTANCE.getDoStatement_Condition()) ; } } } /** * @param statement ForStatement * * Checks the following rule: * 1. Loop variable names must not be available (i.e., not used in the scope of the statement) * 2. Loop variable names must not duplicate (i.e., two loop variables for this statement must not have the same name) * 3. if the loop variable definition uses the syntax option with keyword "in", following rules must be checked: * 3.a. if only expression1 is specified, the upper bound of expression 1 must be greater than 1 * 3.b. if both expression1 and expression2 are specified, they must be type compatible and represent number values (TODO: check other constraints in the spec) * 4. if the loop variable definition uses the syntax option with keyword ":", following rules must be checked: * 4.a. type must be resolved * 4.b. the domain value "expression" must be type-compatible with the variable and must be a collection */ @Check public void checkForStatement(ForStatement statement) { // 1. Loop variable names must not be available (i.e., not used in the scope of the statement) AlfPartialScope variablesScope = AlfScopeProvider.scopingTool.getVisibleVariablesOrParametersOrProperties(statement) ; Map<String, Integer> declaredVariables = new HashMap<String, Integer>() ; for (LoopVariableDefinition loopVariable : statement.getControl().getLoopVariableDefinition()) { Integer variableDefinitionCounter = declaredVariables.get(loopVariable.getName()) ; if (variableDefinitionCounter == null) { declaredVariables.put(loopVariable.getName(), new Integer(1)) ; } else { declaredVariables.put(loopVariable.getName(), variableDefinitionCounter.intValue() + 1) ; } List<EObject> visibleVariables = variablesScope.resolveByName(loopVariable.getName()) ; if (! visibleVariables.isEmpty()) { // potentially conflicting name if (variablesScope.getScopingLevel(visibleVariables.get(0)) == 0) { // There is actually a conflict error("Local name " + loopVariable.getName() + " is not available" , loopVariable, AlfPackage.eINSTANCE.getLocalNameDeclarationStatement_VarName(), statement.getControl().getLoopVariableDefinition().indexOf(loopVariable)) ; } } } // 2. Loop variable names must not duplicate (i.e., two loop variables for this statement must not have the same name) boolean duplicationFound = false ; for (Integer i : declaredVariables.values()) { if (i.intValue() > 1) duplicationFound = true ; } if (duplicationFound) { error("Duplicate loop variable definitions" , AlfPackage.eINSTANCE.getForStatement_Control()) ; } for (LoopVariableDefinition loopVariable : statement.getControl().getLoopVariableDefinition()) { // 3. if the loop variable definition uses the syntax option with keyword "in", following rules must be checked: // 3.a. if only expression1 is specified, the upper bound of expression 1 must be greater than 1 if (loopVariable.getExpression1() != null && loopVariable.getExpression2() == null) { TypeExpression typeOfExpression1 = new TypeUtils().getTypeOfExpression(loopVariable.getExpression1()) ; if (typeOfExpression1.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfExpression1.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { int upperBound = typeOfExpression1.getMultiplicity().getUpperBound() ; if (! (upperBound > 1 || upperBound == -1)) { String errorMessage = "Invalid upper bound multiplicity (" + upperBound + "). A collection is expected." ; error(errorMessage, loopVariable, AlfPackage.eINSTANCE.getLoopVariableDefinition_Expression1(), INSIGNIFICANT_INDEX) ; } } } // 3.b. if both expression1 and expression2 are specified, they must be type compatible and represent number values (TODO: check other constraints in the spec) else if (loopVariable.getExpression1() != null && loopVariable.getExpression2() != null) { TypeExpression typeOfExpression1 = new TypeUtils().getTypeOfExpression(loopVariable.getExpression1()) ; boolean errorInExpressions = false ; if (typeOfExpression1.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfExpression1.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorInExpressions = true ; } TypeExpression typeOfExpression2 = new TypeUtils().getTypeOfExpression(loopVariable.getExpression2()) ; if (typeOfExpression2.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfExpression2.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorInExpressions = true ; } if (! errorInExpressions) { // both Expression must be type compatible and resolve to a scalar number value TypeFacade typeFacadeOfExpression1 = typeOfExpression1.getTypeFacade() ; int upperBoundExpression1 = typeOfExpression1.getMultiplicityFacade().getUpperBound() ; String errorMessageForExpression1 = "" ; if (!isANumberType(typeFacadeOfExpression1)) { if (upperBoundExpression1 > 1 || upperBoundExpression1 == -1) errorMessageForExpression1 += "A scalar number value is expected" ; else errorMessageForExpression1 += "A number value is expected" ; } else { if (upperBoundExpression1 > 1 || upperBoundExpression1 == -1) errorMessageForExpression1 += "A scalar value is expected" ; } if (!(errorMessageForExpression1.length() == 0)) { error(errorMessageForExpression1, loopVariable, AlfPackage.eINSTANCE.getLoopVariableDefinition_Expression1(), INSIGNIFICANT_INDEX) ; } TypeFacade typeFacadeOfExpression2 = typeOfExpression2.getTypeFacade() ; int upperBoundExpression2 = typeOfExpression2.getMultiplicityFacade().getUpperBound() ; String errorMessageForExpression2 = "" ; if (!isANumberType(typeFacadeOfExpression2)) { if (upperBoundExpression2 > 1 || upperBoundExpression2 == -1) errorMessageForExpression2 += "A scalar number value is expected" ; else errorMessageForExpression2 += "A number value is expected" ; } else { if (upperBoundExpression2 > 1 || upperBoundExpression2 == -1) errorMessageForExpression2 += "A scalar value is expected" ; } if (!(errorMessageForExpression2.length() == 0)) { error(errorMessageForExpression2, loopVariable, AlfPackage.eINSTANCE.getLoopVariableDefinition_Expression2(), INSIGNIFICANT_INDEX) ; } } } //4. if the loop variable definition uses the syntax option with keyword ":", following rules must be checked: else if (loopVariable.getType() != null) { // 4.a. type must be resolved TypeFacade typeOfVariable = TypeFacadeFactory.eInstance.createVoidFacade(loopVariable.getType()) ; if (typeOfVariable instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfVariable ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { // 4.b. the domain value "expression" must be type-compatible with the variable and must be a collection TypeExpression typeOfDomain = new TypeUtils().getTypeOfExpression(loopVariable.getExpression()) ; if (typeOfDomain.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfDomain.getTypeFacade() ; error (error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { int upperBound = typeOfDomain.getMultiplicity().getUpperBound() ; if (! (upperBound > 1 || upperBound == -1)) { String errorMessage = "Invalid upper bound multiplicity (" + upperBound + "). A collection is expected." ; error(errorMessage, loopVariable, AlfPackage.eINSTANCE.getLoopVariableDefinition_Expression(), INSIGNIFICANT_INDEX) ; } if (typeOfVariable.isCompatibleWithMe(typeOfDomain.getTypeFacade()) == 0) { String errorMessage = "Cannot convert from " + typeOfDomain.getTypeFacade().getLabel() + " to " + typeOfVariable.getLabel() ; error(errorMessage, loopVariable, AlfPackage.eINSTANCE.getLoopVariableDefinition_Type(), INSIGNIFICANT_INDEX) ; } } } } } } /** * @param statement * * Checks the following rule: * 1. That a return value is actually expected from the context of the ALF specification * 2. If a return value is expected, the returned value must be type/multiplicity compatible * 3. There must be no statement in the containing statement sequence after the return statement. */ @Check public void checkReturnStatement(ReturnStatement statement) { // 1. Checks that a return value is actually expected from the context of the ALF specification boolean returnStatementExpected = AlfScopeProvider.scopingTool.isAReturnStatementExpected(statement) ; if (returnStatementExpected == false) { String errorMessage = "No return statement expected" ; error (errorMessage, AlfPackage.eINSTANCE.getReturnStatement_Expression()) ; } else { // 2. If a return value is expected, the returned value must be type/multiplicity compatible TypeExpression expectedReturnType = AlfScopeProvider.scopingTool.getExpectedReturnType(statement) ; TypeExpression actualReturnType = new TypeUtils().getTypeOfExpression(statement.getExpression()) ; if (actualReturnType.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)actualReturnType.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { if (expectedReturnType.isCompatibleWithMe(actualReturnType) == 0) { String errorMessage = "An expression of type " + expectedReturnType.getLabel() + " is expected. Found an expression of type " + actualReturnType.getLabel() ; error (errorMessage, AlfPackage.eINSTANCE.getReturnStatement_Expression()) ; } } } // 3. There must be no statement (in the containing statement sequence) after the return statement. EObject cddStatementSequence = statement.eContainer() ; DocumentedStatement contextDocumentedStatement = null ; while (cddStatementSequence != null && ! ((cddStatementSequence instanceof StatementSequence) || (cddStatementSequence instanceof NonEmptyStatementSequence))) { if (contextDocumentedStatement == null && cddStatementSequence instanceof DocumentedStatement) contextDocumentedStatement = (DocumentedStatement)cddStatementSequence ; cddStatementSequence = cddStatementSequence.eContainer() ; } if (cddStatementSequence != null && contextDocumentedStatement != null) { int statementIndex = 0 ; int numberOfStatements = 0 ; if (cddStatementSequence instanceof StatementSequence) { statementIndex = ((StatementSequence)cddStatementSequence).getStatements().indexOf(contextDocumentedStatement) ; numberOfStatements = ((StatementSequence)cddStatementSequence).getStatements().size() ; } else { statementIndex = ((NonEmptyStatementSequence)cddStatementSequence).getStatement().indexOf(contextDocumentedStatement) ; numberOfStatements = ((NonEmptyStatementSequence)cddStatementSequence).getStatement().size() ; } String errorMessage = "The statement cannot be reached" ; for (int i = statementIndex + 1 ; i < numberOfStatements ; i++) { DocumentedStatement current = null ; if (cddStatementSequence instanceof StatementSequence) current = ((StatementSequence)cddStatementSequence).getStatements().get(i) ; else current = ((NonEmptyStatementSequence)cddStatementSequence).getStatement().get(i) ; error(errorMessage, current, AlfPackage.eINSTANCE.getDocumentedStatement_Statement(), INSIGNIFICANT_INDEX) ; } } } /** * @param statement * * Checks the following rules: * 1. Checks that the context classifier is active * 2. Each AcceptClause.name (if specified) must be available. * 3. There must be a Reception for each Signal identified in each AcceptClause.qualifiedNameList * 4. Each Signal must be used only once */ @Check public void checkAcceptStatement(AcceptStatement statement) { //1. Checks that the context classifier is active if (! (contextClassifier instanceof Class) || !((Class)contextClassifier).isActive()) { error("The context classifier must be an active class", AlfPackage.eINSTANCE.getAcceptStatement_Clause()) ; } //2. Each AcceptClause.name (if specified) must be available. AlfPartialScope vppScope = AlfScopeProvider.scopingTool.getVisibleVariablesOrParametersOrProperties(statement) ; if (statement.getClause().getName() != null ) { List<EObject> visibleElements = vppScope.resolveByName(statement.getClause().getName()) ; if (! visibleElements.isEmpty() && vppScope.getScopingLevel(visibleElements.get(0)) == 0) { String errorMessage = "Local name " + statement.getClause().getName() + " is not available" ; error(errorMessage, statement.getClause(), AlfPackage.eINSTANCE.getAcceptClause_Name(), INSIGNIFICANT_INDEX) ; } } if (statement.getCompoundAccept() != null) { for (AcceptBlock block : statement.getCompoundAccept().getAcceptBlock()) { if (block.getClause() != null && block.getClause().getName() != null) { List<EObject> visibleElements = vppScope.resolveByName(block.getClause().getName()) ; if (! visibleElements.isEmpty() && vppScope.getScopingLevel(visibleElements.get(0)) == 0) { String errorMessage = "Local name " + block.getClause().getName() + " is not available" ; error(errorMessage, block.getClause(), AlfPackage.eINSTANCE.getAcceptClause_Name(), INSIGNIFICANT_INDEX) ; } } } } //3. There must be a Reception for each Signal identified in each AcceptClause.qualifiedNameList AlfPartialScope signalReceptionScope = AlfScopeProvider.scopingTool.getVisibleSignalReceptions(statement) ; List<TypeFacade> signalReceptionTypeFacade = new ArrayList<TypeFacade>() ; for (List<EObject> l : signalReceptionScope.getScopeDetails()) { for (EObject m : l) { signalReceptionTypeFacade.add(TypeFacadeFactory.eInstance.createTypeFacade(m)) ; } } Map<Classifier, List<QualifiedNameWithBinding>> allReferencedSignals= new HashMap<Classifier, List<QualifiedNameWithBinding>>() ; if (statement.getClause().getQualifiedNameList() != null) { QualifiedNameList list = statement.getClause().getQualifiedNameList() ; int index = 0 ; for (QualifiedNameWithBinding qualifiedName : list.getQualifiedName()) { TypeFacade type = TypeFacadeFactory.eInstance.createVoidFacade(qualifiedName) ; if (type instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)type ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { Classifier actualReferencedClassifier = type.extractActualType() ; List<QualifiedNameWithBinding> existingReferences = allReferencedSignals.get(actualReferencedClassifier) ; if (existingReferences == null) { existingReferences = new ArrayList<QualifiedNameWithBinding>() ; } existingReferences.add(qualifiedName) ; allReferencedSignals.put(actualReferencedClassifier, existingReferences) ; boolean found = false ; Iterator<TypeFacade> i = signalReceptionTypeFacade.iterator() ; while (i.hasNext() && !found) { TypeFacade current = i.next() ; if (current.isCompatibleWithMe(type)!=0) found = true ; } if (!found) { String errorMessage = "The context classifier does not define any reception for " + type.getLabel() ; error(errorMessage, list, AlfPackage.eINSTANCE.getQualifiedNameList_QualifiedName(), index) ; } } index ++ ; } } //4. Each Signal must be used only once for (Classifier key : allReferencedSignals.keySet()) { List<QualifiedNameWithBinding> referencesToKey = allReferencedSignals.get(key) ; if (referencesToKey.size()>1) { for (QualifiedNameWithBinding qualifiedName : referencesToKey) { String errorMessage = "No signal may be named in more than one accept clause" ; QualifiedNameList containingList = (QualifiedNameList)qualifiedName.eContainer() ; int index = containingList.getQualifiedName().indexOf(qualifiedName) ; error(errorMessage, containingList, AlfPackage.eINSTANCE.getQualifiedNameList_QualifiedName(), index - 1 ) ; } } } } /** * @param statement * * Checks the following rules: * 1. The static type of the target expression must be a Class * and it must evaluate to a single object * 2. All qualified names in from or to lists must resolve to classes * 3. All the classes in the from and to lists must be subclasses of the static type of the target expression * 4. None of them may have a common superclass of the static type of the target expression (i.e., disjoint subclasses) * */ @Check public void checkClassifyStatement(ClassifyStatement statement) { // 1. The static type of the target expression must be a Class... Classifier actualStaticType = null ; TypeExpression staticType = new TypeUtils().getTypeOfExpression(statement.getExpression()) ; boolean errorFound = false ; if (staticType.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)staticType.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorFound = true ; } else { actualStaticType = staticType.getTypeFacade().extractActualType() ; } if (actualStaticType == null || !(actualStaticType instanceof Class)) { String errorMessage = "The type of the target expression must be a class" ; error(errorMessage, AlfPackage.eINSTANCE.getClassifyStatement_Expression()) ; errorFound = true ; } else { //... and it must evaluate to a single object int upperBound = staticType.getMultiplicityFacade().getUpperBound() ; if (upperBound > 1 || upperBound == -1) { String errorMessage = "The target expression must evaluate to a single object" ; error(errorMessage, AlfPackage.eINSTANCE.getClassifyStatement_Expression()) ; errorFound = true ; } } // 2. All qualified names in from or to lists must resolve to classes ClassificationClause classificationClause = statement.getClause() ; List<Class> fromClasses = new ArrayList<Class>() ; List<Class> toClasses = new ArrayList<Class>() ; if (classificationClause == null) return ; boolean isAReclassifyFromAll = false ; if (classificationClause.getClassifyFromClause() == null && classificationClause.getClassifyToClause() == null && classificationClause.getReclassyAllClause() == null) return ; if (classificationClause.getReclassyAllClause() != null) // nothing to do with the from list isAReclassifyFromAll = true ; if (classificationClause.getClassifyFromClause() != null) { ClassificationFromClause fromClause = classificationClause.getClassifyFromClause() ; for (QualifiedNameWithBinding name : fromClause.getQualifiedNameList().getQualifiedName()) { TypeFacade type = TypeFacadeFactory.eInstance.createVoidFacade(name) ; if (type instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)type ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorFound = true ; } else { Classifier actualType = type.extractActualType(type) ; if (actualType == null || !(actualType instanceof Class)) { String errorMessage = "All types in the 'from' list must be Classes" ; int index = fromClause.getQualifiedNameList().getQualifiedName().indexOf(name) ; error(errorMessage, fromClause.getQualifiedNameList(), AlfPackage.eINSTANCE.getQualifiedNameList_QualifiedName(), index) ; } else { fromClasses.add((Class)actualType) ; } } } } if (classificationClause.getClassifyToClause() != null) { ClassificationToClause toClause = classificationClause.getClassifyToClause() ; for (QualifiedNameWithBinding name : toClause.getQualifiedNameList().getQualifiedName()) { TypeFacade type = TypeFacadeFactory.eInstance.createVoidFacade(name) ; if (type instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)type ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorFound = true ; } else { Classifier actualType = type.extractActualType(type) ; if (actualType == null || !(actualType instanceof Class)) { String errorMessage = "All types in the 'from' list must be Classes" ; int index = toClause.getQualifiedNameList().getQualifiedName().indexOf(name) ; error(errorMessage, toClause.getQualifiedNameList(), AlfPackage.eINSTANCE.getQualifiedNameList_QualifiedName(), index) ; } else { toClasses.add((Class)actualType) ; } } } } // 3. All the classes in the from and to lists must be subclasses of the static type of the target expression if (errorFound) return ; // not necessary to check the remaining List<Classifier> fromGenerals = new ArrayList<Classifier>() ; List<Classifier> toGenerals = new ArrayList<Classifier>() ; if (actualStaticType != null && !errorFound) { String errorMessage = "All classes in the 'from' list must be subclasses of the target expression type" ; int index = 0 ; for (Class c : fromClasses) { if (! c.allParents().contains(actualStaticType)) { error(errorMessage, classificationClause.getClassifyFromClause(), AlfPackage.eINSTANCE.getClassificationFromClause_QualifiedNameList(), index) ; errorFound = true ; } fromGenerals.addAll(c.allParents()) ; index++ ; } errorMessage = "All classes in the 'to' list must be subclasses of the target expression type" ; index = 0 ; for (Class c : toClasses) { if (! c.allParents().contains(actualStaticType)) { error(errorMessage, classificationClause.getClassifyToClause(), AlfPackage.eINSTANCE.getClassificationToClause_QualifiedNameList(), index) ; errorFound = true ; } index++ ; toGenerals.addAll(c.allParents()) ; } } // 4. None of them may have a common superclass of the static type of the target expression (i.e., disjoint subclasses) if (errorFound) return ; // not necessary to go further fromGenerals.retainAll(toGenerals) ; for (Classifier c : fromGenerals) { if (c.allParents().contains(actualStaticType)) { String errorMessage = "Superclasses of classes in 'to' and 'from' lists must be disjoint subclasses of the target expression type" ; error(errorMessage, AlfPackage.eINSTANCE.getClassifyStatement_Clause()) ; return ; } } } /** * @param statement * Checks the following rules: * 1. The statement must respect construction rules of: * 1.a Invocation, or * 1.b Variable declaration, or * 1.c Assignment expression * 2. According to the construction rule, delegate to the appropriate check method */ @Check public void checkInvocationOrAssignmentOrDeclarationStatement(InvocationOrAssignementOrDeclarationStatement statement) { //1. The statement must respect construction rules of: // 1.b Variable declaration if (statement.getVariableDeclarationCompletion() != null) { checkVariableDeclarationStatement(statement) ; } // 1.c Assignment expression else if (statement.getAssignmentCompletion() != null) { checkAssignmentExpression(statement) ; } // 1.a Invocation or prefix or suffix else { checkInvocationOrPrefixOrSuffixStatement(statement) ; } } /** * @param statement * Checks the following rules: * 1. typePart_OR_assignedPart_OR_invocationPart must have an invocation completion, or a postfixOp, or a prefixOp * 2. if it is an invocation: * 2.a The name must resolve to a Behavior or an Operation * 2.b Arguments must be type compatibles with the parameters of referenced behavior or operation * 3. if it is a prefixOp: * 3.a the name must resolve to an assignable property, parameter or local variable * 3.b the operator must be available for the type of the nameExpression * 4. if it is a postfixOp: * 4.a the name must resolve to an assignable property, parameter or local variable * 4.b the operator must be available for the type of the nameExpression */ private void checkInvocationOrPrefixOrSuffixStatement(InvocationOrAssignementOrDeclarationStatement statement) { // 1. typePart_OR_assignedPart_OR_invocationPart must have an invocation completion, or a postfixOp, or a prefixOp boolean isAnInvocation = statement.getTypePart_OR_assignedPart_OR_invocationPart().getInvocationCompletion() != null ; boolean isAPrefixExpression = statement.getTypePart_OR_assignedPart_OR_invocationPart().getPrefixOp() != null ; boolean isAPostfixExpression = statement.getTypePart_OR_assignedPart_OR_invocationPart().getPostfixOp() != null ; boolean hasASuffix = statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() != null ; int resolvedKindOfExpression = 0 ; resolvedKindOfExpression += isAnInvocation ? 1 : 0 ; resolvedKindOfExpression += isAPrefixExpression ? 1 : 0 ; resolvedKindOfExpression += isAPostfixExpression ? 1 : 0 ; resolvedKindOfExpression += hasASuffix ? 1 : 0 ; if ((resolvedKindOfExpression > 1 && !hasASuffix) || resolvedKindOfExpression==0) { String errorMessage = "An invocation expression, OR a prefix expression, OR a postfix expression is expected." ; error(errorMessage, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; return ; } //2. if it is an invocation: if (isAnInvocation || hasASuffix) { // 2.a The name must resolve to a Variable, a Parameter, a Property, a Behavior or an Operation (TODO: can also be an association) NameExpression varOrParamOrPropOrOpOrBehav = statement.getTypePart_OR_assignedPart_OR_invocationPart() ; TypeExpression typeOfPrefix = new TypeUtils().getTypeOfNameExpression(varOrParamOrPropOrOpOrBehav) ; if (typeOfPrefix.getTypeFacade() != null && typeOfPrefix.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfPrefix.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; return ; } if (hasASuffix) { // The last suffix must be an invocation SuffixExpression suffix = statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() ; SuffixExpression lastSuffix = suffix ; boolean suffixHasASuffix = false ; do { suffixHasASuffix = false ; for (Iterator<EObject> content = suffix.eContents().iterator() ; content.hasNext() && !suffixHasASuffix ; ) { EObject cddSuffix = content.next() ; if (cddSuffix instanceof SuffixExpression) { lastSuffix = (SuffixExpression)cddSuffix ; suffixHasASuffix = true ; } } if (suffixHasASuffix) suffix = lastSuffix ; } while (suffixHasASuffix) ; if (lastSuffix instanceof PropertyCallExpression) { error("An invocation is expected", AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; return ; } } //} } // 3. if it is a prefixOp: or // 4. if it is a postfixOp: else if (isAPrefixExpression || isAPostfixExpression) { TypeExpression typeOfAssignedElement = new TypeUtils().getTypeOfNameExpression(statement.getTypePart_OR_assignedPart_OR_invocationPart()) ; if (typeOfAssignedElement.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfAssignedElement.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; return ; } //1. The assigned part must be assignable, i.e.: if (statement.getTypePart_OR_assignedPart_OR_invocationPart().getPath() != null || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getInvocationCompletion() != null && statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() == null) || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getSequenceConstructionCompletion() != null) || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getId()==null) || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getId().length() == 0)) { error("The assigned part must resolve to an assignable property, parameter or local variable", AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; return ; } //1.a It must resolve to a property, or //1.b It must resolve to a local variable, or //1.c It must resolve to an out or inout parameter if (statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() == null) { AlfPartialScope varParamPropScope = AlfScopeProvider.scopingTool.getVisibleVariablesOrParametersOrProperties(statement) ; EObject resolved = varParamPropScope.resolveByName(statement.getTypePart_OR_assignedPart_OR_invocationPart().getId()).get(0) ; String potentialAssignmentError = AssignmentPolicy.eInstance.isAssignable(resolved) ; if (!(potentialAssignmentError.length() == 0)) { error(potentialAssignmentError, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; return ; } } else {// a suffix is specified // The last suffix must be a property call expression SuffixExpression suffix = statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() ; SuffixExpression lastSuffix = suffix ; boolean suffixHasASuffix = false ; do { suffixHasASuffix = false ; for (Iterator<EObject> content = suffix.eContents().iterator() ; content.hasNext() && !suffixHasASuffix ; ) { EObject cddSuffix = content.next() ; if (cddSuffix instanceof SuffixExpression) { lastSuffix = (SuffixExpression)cddSuffix ; suffixHasASuffix = true ; } } if (suffixHasASuffix) suffix = lastSuffix ; } while (suffixHasASuffix) ; if (! (lastSuffix instanceof PropertyCallExpression)) { error("The assigned part must resolve to an assignable property, parameter or local variable", AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; return ; } } TypeExpression integerExpression = TypeExpressionFactory.eInstance.createTypeExpression(TypeUtils._integer) ; TypeExpression naturalExpression = TypeExpressionFactory.eInstance.createTypeExpression(TypeUtils._natural) ; TypeExpression unlimitedExpression = TypeExpressionFactory.eInstance.createTypeExpression(TypeUtils._unlimited) ; String operator = isAPrefixExpression ? statement.getTypePart_OR_assignedPart_OR_invocationPart().getPrefixOp() : statement.getTypePart_OR_assignedPart_OR_invocationPart().getPostfixOp() ; EStructuralFeature feature = isAPrefixExpression ? AlfPackage.eINSTANCE.getNameExpression_PrefixOp() : AlfPackage.eINSTANCE.getNameExpression_PostfixOp() ; if (! (integerExpression.isCompatibleWithMe(typeOfAssignedElement)!= 0 || naturalExpression.isCompatibleWithMe(typeOfAssignedElement)!= 0 || unlimitedExpression.isCompatibleWithMe(typeOfAssignedElement)!= 0)) { error("Operator " + operator + " does not apply to " + typeOfAssignedElement.getLabel() , statement.getTypePart_OR_assignedPart_OR_invocationPart(), feature, INSIGNIFICANT_INDEX) ; } } } /** * @param statement * * Checks the following rule: * 1. typePart_OR_assignedPart_OR_invocationPart must resolve to a type * 2. the variable name must be available * 3. initValue must respect the following sub-rules: * 3.a the type of the expression must be compatible with the type of the variable * 3.b the assignment operator must be '=' */ private void checkVariableDeclarationStatement(InvocationOrAssignementOrDeclarationStatement statement) { // 1. typePart_OR_assignedPart_OR_invocationPart must resolve to a type VoidFacade actualVariableType = null ; TypeFacade cddVariableType = TypeFacadeFactory.eInstance.createVoidFacade(statement.getTypePart_OR_assignedPart_OR_invocationPart()) ; if (cddVariableType instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)cddVariableType ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; } else { actualVariableType = (VoidFacade)cddVariableType ; } VariableDeclarationCompletion declaration = statement.getVariableDeclarationCompletion() ; TypeExpression variableTypeExpression = null ; if (actualVariableType != null) { if (declaration.isMultiplicityIndicator()) variableTypeExpression = TypeExpressionFactory.eInstance.createTypeExpression(actualVariableType, 0, -1, false, true) ; else variableTypeExpression = TypeExpressionFactory.eInstance.createTypeExpression(actualVariableType, 1, 1, true, false) ; } // 2. the variable name must be available AlfPartialScope vppScope = AlfScopeProvider.scopingTool.getVisibleVariablesOrParametersOrProperties(statement) ; if (declaration.getVariableName() != null ) { List<EObject> visibleElements = vppScope.resolveByName(declaration.getVariableName()) ; if (! visibleElements.isEmpty() && vppScope.getScopingLevel(visibleElements.get(0)) == 0) { String errorMessage = "Local name " + declaration.getVariableName() + " is not available" ; error(errorMessage, declaration, AlfPackage.eINSTANCE.getVariableDeclarationCompletion_VariableName(), INSIGNIFICANT_INDEX) ; } } //3. initValue must respect the following sub-rules: if (declaration.getInitValue() != null && declaration.getInitValue().getRightHandSide() != null) { TypeExpression typeOfInit = new TypeUtils().getTypeOfSequenceElement(declaration.getInitValue().getRightHandSide()) ; if (typeOfInit == null) { String errorMessage = "Init value is missing" ; error(errorMessage, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_VariableDeclarationCompletion()) ; return ; } if (typeOfInit.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfInit.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; return ; } if (variableTypeExpression != null) { // 3.a the type of the expression must be compatible with the type of the variable if (variableTypeExpression.isCompatibleWithMe(typeOfInit) == 0) { String errorMessage = "Expecting an expression of type " + variableTypeExpression.getLabel() + ". Found an expression of type " + typeOfInit.getLabel() + "." ; error(errorMessage, declaration.getInitValue(), AlfPackage.eINSTANCE.getAssignmentCompletion_RightHandSide(), INSIGNIFICANT_INDEX) ; } // 3.b the assignment operator must be '=' else if (declaration.getInitValue().getOp() != AssignmentOperator.ASSIGN) { String errorMessage = "Expecting assignment operator '='" ; error(errorMessage, declaration.getInitValue(), AlfPackage.eINSTANCE.getAssignmentCompletion_Op(), INSIGNIFICANT_INDEX) ; } } } } /** * * @param statement * * Checks the following rules: * 1. The left part must be assignable, i.e.: * 1.a It must resolve to a property, or * 1.b It must resolve to a local variable, or * 1.c It must resolve to an out or inout parameter * 1.d It resolves to an association end TODO: Not supported yet * 2. If the assignment operator is "=", the right part must be type compatible with the left part * 3. If the assignment operator is not "=" (e.g. +=, -=, etc.), there must be a matching signature for this operator */ private void checkAssignmentExpression(InvocationOrAssignementOrDeclarationStatement statement) { boolean errorInExpressions = false ; // first infer type of the left part TypeExpression typeOfLeft = new TypeUtils().getTypeOfNameExpression(statement.getTypePart_OR_assignedPart_OR_invocationPart()) ; if (typeOfLeft.getTypeFacade() == null || typeOfLeft.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfLeft.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorInExpressions = true ; } // then infer type of the right part TypeExpression typeOfRight = null ; if (statement.getAssignmentCompletion().getRightHandSide() != null) { typeOfRight = new TypeUtils().getTypeOfSequenceElement(statement.getAssignmentCompletion().getRightHandSide()) ; if (typeOfRight == null || typeOfRight.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfRight.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; errorInExpressions = true ; } } if (errorInExpressions) // not necessary to validate further return ; //1. The left part must be assignable, i.e.: if (statement.getTypePart_OR_assignedPart_OR_invocationPart().getPath() != null || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getInvocationCompletion() != null && statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() == null) || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getPrefixOp()!=null) || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getPostfixOp()!=null) || (statement.getTypePart_OR_assignedPart_OR_invocationPart().getSequenceConstructionCompletion() != null)) { if (statement.getTypePart_OR_assignedPart_OR_invocationPart().getSequenceConstructionCompletion() != null) { if (statement.getTypePart_OR_assignedPart_OR_invocationPart().getSequenceConstructionCompletion().getAccessCompletion() != null) return ; } error("The left part of the assignment must resolve to an assignable property, parameter or local variable", AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; // NOTE: this an approximation. Cf. => rule 1.d It resolves to an association end TODO: Not supported yet return ; } //1.a It must resolve to a property, or //1.b It must resolve to a local variable, or //1.c It must resolve to an out or inout parameter if (statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() == null) { AlfPartialScope varParamPropScope = AlfScopeProvider.scopingTool.getVisibleVariablesOrParametersOrProperties(statement) ; EObject resolved = varParamPropScope.resolveByName(statement.getTypePart_OR_assignedPart_OR_invocationPart().getId()).get(0) ; String potentialAssignmentError = AssignmentPolicy.eInstance.isAssignable(resolved) ; if (!(potentialAssignmentError.length() == 0)) { error(potentialAssignmentError, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; } } else {// a suffix is specified // The last suffix must be a property call expression SuffixExpression suffix = statement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() ; SuffixExpression lastSuffix = suffix ; boolean suffixHasASuffix = false ; do { suffixHasASuffix = false ; for (Iterator<EObject> content = suffix.eContents().iterator() ; content.hasNext() && !suffixHasASuffix ; ) { EObject cddSuffix = content.next() ; if (cddSuffix instanceof SuffixExpression) { lastSuffix = (SuffixExpression)cddSuffix ; suffixHasASuffix = true ; } } if (suffixHasASuffix) suffix = lastSuffix ; } while (suffixHasASuffix) ; if (! (lastSuffix instanceof PropertyCallExpression)) { error("The left part of the assignment must resolve to an assignable property, parameter or local variable", AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; return ; } } // 2. If the assignment operator is "=", the right part must be type compatible with the left part if (statement.getAssignmentCompletion().getOp() == AssignmentOperator.ASSIGN) { if (typeOfRight.getTypeFacade() == TypeUtils._undefined) { String errorMessage = "Right part is untyped" ; error(errorMessage, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_AssignmentCompletion()) ; } else if (typeOfLeft.isCompatibleWithMe(typeOfRight) == 0) { String errorMessage = "Cannot assign " + typeOfRight.getLabel() + " to " + typeOfLeft.getLabel() ; error(errorMessage, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_AssignmentCompletion()) ; } } else { // 3. If the assignment operator is not "=" (e.g. +=, -=, etc.), there must be a matching signature for this operator String assignmentOp = assignmentOpToString(statement.getAssignmentCompletion().getOp()) ; List<SignatureFacade> candidates = AlfJavaValidator.predefinedBehaviorsAndTypes.getSignatures(assignmentOp) ; List<TypeExpression> arguments = new ArrayList<TypeExpression>() ; arguments.add(typeOfLeft) ; arguments.add(typeOfRight) ; List<SignatureFacade> matchingSignatures = SignatureFacade.findNearestSignature(arguments, candidates) ; if (matchingSignatures.isEmpty()) { String errorMessage = "Operator " + assignmentOp + " does not apply to arguments (" ; boolean first = true ; for (TypeExpression argType : arguments) { if (!first) errorMessage += ", " ; else first = false ; errorMessage += argType.getLabel() ; } errorMessage += ")" ; error(errorMessage, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_AssignmentCompletion()) ; } } } /** * @param statement * * Checks the following rule: * 1. The associated SuperInvocationExpression must be valid */ @Check public void checkSuperInvocationStatement(SuperInvocationStatement statement) { TypeExpression typeOfSuperInvocationExp = new TypeUtils().getTypeOfSuperInvocationExpression(statement.get_super()) ; if (typeOfSuperInvocationExp.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfSuperInvocationExp.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; return ; } } /** * @param statement * * Checks the following rules: * 1. The associated ThisExpression must be valid * 2. If an assignment is specified: * 2.a The left part must resolve to a property call expression * 2.b If the assignment operator is "=", the right part must be type compatible with the left part * 2.c If the assignment operator is not "=" (e.g. +=, -=, etc.), there must be a matching signature for this operator * 3. If no assignment is specified, the suffix must resolve to an invocation */ @Check public void checkThisInvocationStatement(ThisInvocationStatement statement) { // 1. The associated ThisExpression must be valid TypeExpression typeOfThisExpression = new TypeUtils().getTypeOfThisExpression(statement.get_this()) ; if (typeOfThisExpression.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfThisExpression.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; // return ; } // 2. If an assignment is specified if (statement.getAssignmentCompletion() != null ) { // 2.a The left part must resolve to a property call expression if (statement.get_this().getSuffix() == null) { error("A Property call expression is missing", AlfPackage.eINSTANCE.getThisInvocationStatement__this()) ; //return ; } else { // The last suffix must be an invocation SuffixExpression suffix = statement.get_this().getSuffix() ; SuffixExpression lastSuffix = suffix ; boolean suffixHasASuffix = false ; do { suffixHasASuffix = false ; for (Iterator<EObject> content = suffix.eContents().iterator() ; content.hasNext() && !suffixHasASuffix ; ) { EObject cddSuffix = content.next() ; if (cddSuffix instanceof SuffixExpression) { lastSuffix = (SuffixExpression)cddSuffix ; suffixHasASuffix = true ; } } if (suffixHasASuffix) suffix = lastSuffix ; } while (suffixHasASuffix) ; if (!(lastSuffix instanceof PropertyCallExpression)) { error("The expression should resolve to a Property", AlfPackage.eINSTANCE.getThisInvocationStatement__this()) ; //return ; } } TypeExpression typeOfAssignment = null ; if (statement.getAssignmentCompletion().getRightHandSide() == null) return ; else { typeOfAssignment = new TypeUtils().getTypeOfSequenceElement(statement.getAssignmentCompletion().getRightHandSide()) ; if (typeOfAssignment.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfAssignment.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; return ; } if (typeOfThisExpression.getTypeFacade() instanceof ErrorTypeFacade) return ; } // 2.b If the assignment operator is "=", the right part must be type compatible with the left part if (statement.getAssignmentCompletion().getOp() == AssignmentOperator.ASSIGN) { if (typeOfAssignment.getTypeFacade() == TypeUtils._undefined) { String errorMessage = "Right part is untyped" ; error(errorMessage, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_AssignmentCompletion()) ; } else if (typeOfThisExpression.isCompatibleWithMe(typeOfAssignment) == 0) { String errorMessage = "Cannot assign " + typeOfAssignment.getLabel() + " to " + typeOfThisExpression.getLabel() ; error(errorMessage, AlfPackage.eINSTANCE.getThisInvocationStatement_AssignmentCompletion()) ; } } else { // 2.c If the assignment operator is not "=" (e.g. +=, -=, etc.), there must be a matching signature for this operator String assignmentOp = assignmentOpToString(statement.getAssignmentCompletion().getOp()) ; List<SignatureFacade> candidates = AlfJavaValidator.predefinedBehaviorsAndTypes.getSignatures(assignmentOp) ; List<TypeExpression> arguments = new ArrayList<TypeExpression>() ; arguments.add(typeOfThisExpression) ; arguments.add(typeOfAssignment) ; List<SignatureFacade> matchingSignatures = SignatureFacade.findNearestSignature(arguments, candidates) ; if (matchingSignatures.isEmpty()) { String errorMessage = "Operator " + assignmentOp + " does not apply to arguments (" ; boolean first = true ; for (TypeExpression argType : arguments) { if (!first) errorMessage += ", " ; else first = false ; errorMessage += argType.getLabel() ; } errorMessage += ")" ; error(errorMessage, AlfPackage.eINSTANCE.getThisInvocationStatement_AssignmentCompletion()) ; } } } else { // 3. If no assignment is specified, the suffix must resolve to an invocation if (statement.get_this().getSuffix() == null) { error("An invocation expression is expected", AlfPackage.eINSTANCE.getThisInvocationStatement__this()) ; return ; } else { // The last suffix must be an invocation SuffixExpression suffix = statement.get_this().getSuffix() ; SuffixExpression lastSuffix = suffix ; boolean suffixHasASuffix = false ; do { suffixHasASuffix = false ; for (Iterator<EObject> content = suffix.eContents().iterator() ; content.hasNext() && !suffixHasASuffix ; ) { EObject cddSuffix = content.next() ; if (cddSuffix instanceof SuffixExpression) { lastSuffix = (SuffixExpression)cddSuffix ; suffixHasASuffix = true ; } } if (suffixHasASuffix) suffix = lastSuffix ; } while (suffixHasASuffix) ; if (lastSuffix instanceof PropertyCallExpression) { error("An assignment is expected", AlfPackage.eINSTANCE.getThisInvocationStatement__this()) ; return ; } } } } /** * @param statement * * Checks the following rule: * 1. The InstanceCreationExpression must be valid * 2. If a suffix is specified, it must resolve to an invocation */ @Check public void checkInstanceCreationInvocationStatement(InstanceCreationInvocationStatement statement) { // 1. The InstanceCreationExpression must be valid TypeExpression typeOfInstanceCreationExpression = new TypeUtils().getTypeOfInstanceCreationExpression(statement.get_new()) ; if (typeOfInstanceCreationExpression.getTypeFacade() instanceof ErrorTypeFacade) { ErrorTypeFacade error = (ErrorTypeFacade)typeOfInstanceCreationExpression.getTypeFacade() ; error(error.getLabel(), error.getErrorSource(), error.getStructuralFeature(), INSIGNIFICANT_INDEX) ; return ; } // 2. If a suffix is specified, it must resolve to an invocation // The last suffix must be an invocation SuffixExpression suffix = statement.get_new().getSuffix() ; SuffixExpression lastSuffix = suffix ; boolean suffixHasASuffix = false ; do { suffixHasASuffix = false ; for (Iterator<EObject> content = suffix.eContents().iterator() ; content.hasNext() && !suffixHasASuffix ; ) { EObject cddSuffix = content.next() ; if (cddSuffix instanceof SuffixExpression) { lastSuffix = (SuffixExpression)cddSuffix ; suffixHasASuffix = true ; } } if (suffixHasASuffix) suffix = lastSuffix ; } while (suffixHasASuffix) ; if (lastSuffix instanceof PropertyCallExpression) { error("An invocation is expected", AlfPackage.eINSTANCE.getInstanceCreationInvocationStatement__new()) ; return ; } } private String assignmentOpToString(AssignmentOperator operator) { switch (operator) { case ANDASSIGN: return "&" ; case DIVASSIGN: return "/" ; case LSHIFTASSIGN: return "<<" ; case MINUSASSIGN: return "-" ; case MODASSIGN: return "%" ; case MULTASSIGN: return "*" ; case ORASSIGN: return "|" ; case PLUSASSIGN: return "+" ; case RSHIFTASSIGN: return ">>" ; case URSHIFTASSIGN: return ">>>" ; case XORASSIGN: return "^" ; case ASSIGN: return "=" ; } return "" ; // not reachable } private boolean isANumberType(TypeFacade typeFacade) { return TypeUtils._integer.isCompatibleWithMe(typeFacade) == 3 || TypeUtils._natural.isCompatibleWithMe(typeFacade) == 3 || TypeUtils._unlimited.isCompatibleWithMe(typeFacade) == 3 ; } @Check public void checkTemplateBindingInNameExpression(UnqualifiedName expression) { if (expression.getTemplateBinding() != null) { String errorMessage = "Template bindings are not supported in name expressions." ; // TODO warning(errorMessage, AlfPackage.eINSTANCE.getUnqualifiedName_TemplateBinding()) ; } } @Check public void checkTemplateBindingInQualifiedNameWithBinding(QualifiedNameWithBinding expression) { //if (expression.getBinding() != null) { // String errorMessage = "Template bindings are not supported in this version of the Alf editor." ; // TODO // warning(errorMessage, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Binding()) ; //} } }