瀏覽代碼

Allow optional parameters (#1854)

* Allow optional parameters

* Add tests for optional parameters

* Add more tests
Rene Beckmann 7 年之前
父節點
當前提交
c67bad3d2e

+ 22 - 2
plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/validation/ExpressionsJavaValidator.java

@@ -11,6 +11,7 @@
  */
 package org.yakindu.base.expressions.validation;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -279,12 +280,31 @@ public class ExpressionsJavaValidator extends org.yakindu.base.expressions.valid
 
 	protected 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()) {
+		List<Parameter> optionalParameters = filterOptionalParameters(parameters);
+		if (
+			(operation.isVariadic() && operation.getVarArgIndex() > args.size())
+			|| 
+			(!operation.isVariadic() &&
+					!(args.size() <= parameters.size() && args.size() >= parameters.size() - optionalParameters.size()))
+			) {
 			error(String.format(ERROR_WRONG_NUMBER_OF_ARGUMENTS_MSG, parameters), null, ERROR_WRONG_NUMBER_OF_ARGUMENTS_CODE);
 		}
 	}
 	
+	/**
+	 * @param parameters
+	 * @return
+	 */
+	protected List<Parameter> filterOptionalParameters(EList<Parameter> parameters) {
+		List<Parameter> optionalParameters = new ArrayList<>();
+		for(Parameter p : parameters) {
+			if(p.isOptional()) {
+				optionalParameters.add(p);
+			}
+		}
+		return optionalParameters;
+	}
+
 	@Check(CheckType.FAST)
 	public void checkVarArgParameterIsLast(Operation operation) {
 		if (operation.isVariadic() && operation.getVarArgIndex() != operation.getParameters().size() - 1) {

+ 48 - 0
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/STextTestScopeProvider.xtend

@@ -24,6 +24,7 @@ import org.eclipse.xtext.scoping.impl.SimpleScope
 import org.yakindu.base.types.ComplexType
 import org.yakindu.base.types.Declaration
 import org.yakindu.base.types.EnumerationType
+import org.yakindu.base.types.Operation
 import org.yakindu.base.types.Package
 import org.yakindu.base.types.TypesFactory
 import org.yakindu.sct.model.sgraph.SGraphFactory
@@ -93,6 +94,11 @@ class STextTestScopeProvider extends STextScopeProvider {
 		addToIndex(descriptions, createPrimitiveType("SubInt", INTEGER))
 		addToIndex(descriptions, createPrimitiveType("SubString", STRING))
 		
+		addToIndex(descriptions, createOperationWithOptionalParameter)
+		addToIndex(descriptions, createOperationWithMultipleOptionalParameters)
+		addToIndex(descriptions, createOperationWithMixedOptionalParameters)
+		addToIndex(descriptions, createOperationWithMixedOptionalAndVarargsParameters)
+		
 		return new SimpleScope(descriptions)
 	}
 	
@@ -105,6 +111,48 @@ class STextTestScopeProvider extends STextScopeProvider {
 		}
 	}
 	
+	def protected Operation createOperationWithOptionalParameter() {
+		val op = createOperation("optOp1", ts.getType(VOID))
+		val p1 = createParameter("p1", ts.getType(INTEGER))
+		p1.optional = true
+		op.parameters += p1
+		op
+	}
+	
+	def protected Operation createOperationWithMultipleOptionalParameters() {
+		val op = createOperation("optOp2", ts.getType(VOID))
+		val p1 = createParameter("p1", ts.getType(INTEGER))
+		val p2 = createParameter("p2", ts.getType(INTEGER))
+		p1.optional = true
+		p2.optional = true
+		op.parameters += p1
+		op.parameters += p2
+		op
+	}
+	
+	def protected Operation createOperationWithMixedOptionalParameters() {
+		val op = createOperation("optOp3", ts.getType(VOID))
+		val p1 = createParameter("p1", ts.getType(INTEGER))
+		val p2 = createParameter("p2", ts.getType(INTEGER))
+		p2.optional = true
+		op.parameters += p1
+		op.parameters += p2
+		op
+	}
+	
+	def protected Operation createOperationWithMixedOptionalAndVarargsParameters() {
+		val op = createOperation("optOp4", ts.getType(VOID))
+		val p1 = createParameter("p1", ts.getType(INTEGER))
+		val p2 = createParameter("p2", ts.getType(INTEGER))
+		val p3 = createParameter("...", ts.getType(ANY))
+		p2.optional = true
+		p3.varArgs = true
+		op.parameters += p1
+		op.parameters += p2
+		op.parameters += p3
+		op
+	}
+	
 	def protected State createDummyModel() {
 		val stateA = createState => [
 			name = "A"

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

@@ -22,6 +22,8 @@ import static org.yakindu.base.expressions.validation.ExpressionsJavaValidator.E
 import static org.yakindu.base.expressions.validation.ExpressionsJavaValidator.ERROR_WRONG_NUMBER_OF_ARGUMENTS_CODE;
 import static org.yakindu.sct.test.models.AbstractTestModelsUtil.VALIDATION_TESTMODEL_DIR;
 
+import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
+
 import java.util.HashMap;
 import java.util.Iterator;
 
@@ -45,7 +47,6 @@ import org.yakindu.sct.model.sgraph.State;
 import org.yakindu.sct.model.sgraph.Statechart;
 import org.yakindu.sct.model.sgraph.Transition;
 import org.yakindu.sct.model.sgraph.Trigger;
-import org.yakindu.sct.model.sgraph.impl.StatechartImpl;
 import org.yakindu.sct.model.stext.inferrer.STextTypeInferrer;
 import org.yakindu.sct.model.stext.stext.InterfaceScope;
 import org.yakindu.sct.model.stext.stext.InternalScope;
@@ -821,9 +822,114 @@ public class STextJavaValidatorTest extends AbstractSTextValidationTest implemen
 		assertIssueCount(diagnostics, 3);
 		assertWarning(diagnostics, INTERNAL_DECLARATION_UNUSED);
 	}
-
+	
 	@Test
 	public void transitionsWithNoTrigger() {
 	}
+	
+	@Test
+	public void testOptionalParameter() {
+		EObject model;
+		String rule = Expression.class.getSimpleName();
+		AssertableDiagnostics result;
+		
+		model = parseExpression("optOp1()", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp1(3)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp1(true)", rule);
+		result = tester.validate(model);
+		result.assertError(ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
+	
+	@Test
+	public void testMultipleOptionalParameters() {
+		EObject model;
+		String rule = Expression.class.getSimpleName();
+		AssertableDiagnostics result;
+		
+		model = parseExpression("optOp2()", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp2(3)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp2(3, 4)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp2(true)", rule);
+		result = tester.validate(model);
+		result.assertError(ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+
+		model = parseExpression("optOp2(true, 3, 4)", rule);
+		result = tester.validate(model);
+		result.assertAll(
+				errorCode(ITypeSystemInferrer.NOT_COMPATIBLE_CODE),
+				errorCode(ERROR_WRONG_NUMBER_OF_ARGUMENTS_CODE)
+				);
+	}
+	
+	@Test
+	public void testMixedOptionalParameters() {
+		EObject model;
+		String rule = Expression.class.getSimpleName();
+		AssertableDiagnostics result;
+		
+		model = parseExpression("optOp3(3)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp3(3, 4)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+
+		model = parseExpression("optOp3(3, 4, true)", rule);
+		result = tester.validate(model);
+		result.assertError(ERROR_WRONG_NUMBER_OF_ARGUMENTS_CODE);
+		
+		model = parseExpression("optOp3()", rule);
+		result = tester.validate(model);
+		result.assertError(ERROR_WRONG_NUMBER_OF_ARGUMENTS_CODE);
+		
+		model = parseExpression("optOp3(3, true)", rule);
+		result = tester.validate(model);
+		result.assertError(ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
+	
+	@Test
+	@Ignore("Why should anyone do that anyways")
+	public void testMixedOptionalAndVarArgsParameters() {
+		EObject model;
+		String rule = Expression.class.getSimpleName();
+		AssertableDiagnostics result;
+		
+		model = parseExpression("optOp4(3)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp4(3, 4)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp4(3, 4, 5, 6)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp4(3, 4, true)", rule);
+		result = tester.validate(model);
+		result.assertOK();
+		
+		model = parseExpression("optOp4(true)", rule);
+		result = tester.validate(model);
+		result.assertError(ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
+	
 
 }