Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Publishing a project that contains representations now produces libraries that a
- https://github.com/eclipse-syson/syson/issues/1970[#1970] [export] Implement textual export of `AllocationUsage` and `AllocationDefinition`.
- https://github.com/eclipse-syson/syson/issues/1948[#1948] [diagrams] Add _New Satisfy_ tool on `RequirementUsage` graphical nodes. This tool allows to create a `SatisfyRequirementUsage` on the selected `RequirementUsage`.
Also add a _New Satisfy Requirement_ graphical edge tool between `Feature` graphical nodes and `RequirementUsage` graphical nodes.
- https://github.com/eclipse-syson/syson/issues/1986[#1986] [services] Make `ServiceMethod#aql` and `ServiceMethod#aqlSelf` throw an exception if the provided parameters don't match the service method arity.
As a result, the application won't start if a service call is constructed with an invalid number of arguments.

=== New features

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2025 Obeo.
* Copyright (c) 2025, 2026 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
Expand All @@ -16,6 +16,7 @@
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Arrays;

/**
Expand Down Expand Up @@ -81,8 +82,11 @@ public final class ServiceMethod {

private final String name;

private ServiceMethod(String name) {
private final int arity;

private ServiceMethod(String name, int arity) {
this.name = name;
this.arity = arity;
}

/**
Expand All @@ -105,6 +109,7 @@ public String name() {
* @return A full AQL expression string
*/
public String aqlSelf(String... params) {
this.checkArity(params);
if (params == null || params.length == 0) {
return AQLUtils.getSelfServiceCallExpression(this.name);
}
Expand All @@ -128,6 +133,7 @@ public String aql(String var, String... params) {
if (var == null || var.isEmpty()) {
throw new IllegalArgumentException("var must be a non empty AQL variable name");
} else {
this.checkArity(params);
if (params == null || params.length == 0) {
aqlString = AQLUtils.getServiceCallExpression(var, this.name);
}
Expand All @@ -143,49 +149,49 @@ public String aql(String var, String... params) {
* Instance method with signature {@code R method(T self)}.
*/
public static <S, T> ServiceMethod of0(Inst0<S, T> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 0);
}

/**
* Instance method with signature {@code R method(T self, P1 p1)}.
*/
public static <S, T, P1> ServiceMethod of1(Inst1<S, T, P1> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 1);
}

/**
* Instance method with signature {@code R method(T self, P1 p1, P2 p2)}.
*/
public static <S, T, P1, P2> ServiceMethod of2(Inst2<S, T, P1, P2> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 2);
}

/**
* Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
*/
public static <S, T, P1, P2, P3> ServiceMethod of3(Inst3<S, T, P1, P2, P3> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 3);
}

/**
* Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4)}.
*/
public static <S, T, P1, P2, P3, P4> ServiceMethod of4(Inst4<S, T, P1, P2, P3, P4> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 4);
}

/**
* Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)}.
*/
public static <S, T, P1, P2, P3, P4, P5> ServiceMethod of5(Inst5<S, T, P1, P2, P3, P4, P5> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 5);
}

/**
* Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)}.
*/
public static <S, T, P1, P2, P3, P4, P5, P6> ServiceMethod of6(Inst6<S, T, P1, P2, P3, P4, P5, P6> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 6);
}

// ---------------------- Factories for static methods ----------------------
Expand All @@ -194,28 +200,28 @@ public static <S, T, P1, P2, P3, P4, P5, P6> ServiceMethod of6(Inst6<S, T, P1, P
* Static method with signature {@code R method(T self)}.
*/
public static <T> ServiceMethod ofStatic0(IStat0<T> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 0);
}

/**
* Static method with signature {@code R method(T self, P1 p1)}.
*/
public static <T, P1> ServiceMethod ofStatic1(IStat1<T, P1> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 1);
}

/**
* Static method with signature {@code R method(T self, P1 p1, P2 p2)}.
*/
public static <T, P1, P2> ServiceMethod ofStatic2(IStat2<T, P1, P2> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 2);
}

/**
* Static method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
*/
public static <T, P1, P2, P3> ServiceMethod ofStatic3(IStat3<T, P1, P2, P3> ref) {
return new ServiceMethod(methodName(ref));
return new ServiceMethod(methodName(ref), 3);
}

// ---------------------- SAMs for method references ----------------------
Expand Down Expand Up @@ -431,5 +437,24 @@ private static String methodName(Serializable lambdaRef) {
throw new IllegalStateException("Cannot resolve method name from lambda", e);
}
}

/**
* Checks that the provided {@code params} array matches the arity of the service method.
*
* @param params the parameters passed to the service method (excluding self)
*
* @throws IllegalArgumentException if the provided {@code params} doesn't match the arity of the service method.
*/
private void checkArity(String... params) {
int paramLength;
if (params == null) {
paramLength = 0;
} else {
paramLength = params.length;
}
if (this.arity != paramLength) {
throw new IllegalArgumentException(MessageFormat.format("Service {0} has an arity of {1} but {2} parameters were provided", this.name, this.arity, paramLength));
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2026 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.syson.util;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
* ServiceMethod-related tests.
*
* @author gdaniel
*/
public class ServiceMethodTest {

@Test
@DisplayName("GIVEN a service method with arity 0, WHEN an AQL expression is constructed with 0 parameter, THEN the expression is returned")
public void givenServiceMethodWithArity0WhenAQLExpressionIsConstructedWith0ParameterThenExpressionIsReturned() {
String expression = ServiceMethod.of0(ServiceMethodTest::serviceWithArity0).aqlSelf();
assertThat(expression).isEqualTo("aql:self.serviceWithArity0()");
expression = ServiceMethod.of0(ServiceMethodTest::serviceWithArity0).aql("var");
assertThat(expression).isEqualTo("aql:var.serviceWithArity0()");
}

@Test
@DisplayName("GIVEN a service method with arity 0, WHEN an AQL expression is constructed with 1 parameter, THEN an exception is thrown")
public void givenServiceMethodWithArity0WhenAQLExpressionIsConstructedWith1ParameterThenExceptionIsThrown() {
assertThatThrownBy(() -> ServiceMethod.of0(ServiceMethodTest::serviceWithArity0).aqlSelf("param1")).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> ServiceMethod.of0(ServiceMethodTest::serviceWithArity0).aql("var", "param1")).isInstanceOf(IllegalArgumentException.class);
}

private Object serviceWithArity0(Object self) {
return null;
}
}
Loading