1313package org .eclipse .syson .util ;
1414
1515import java .io .Serializable ;
16+ import java .lang .invoke .MethodType ;
1617import java .lang .invoke .SerializedLambda ;
1718import java .lang .reflect .InvocationTargetException ;
1819import java .lang .reflect .Method ;
7273 * }
7374 * </pre>
7475 * <p>
76+ * Overloaded services: if several service methods share the same name, use the factory overloads that also take the
77+ * service class and Java parameter types, for example
78+ * {@code ServiceMethod.of1(EObjectServices.class, EObjectServices::eGet, EObject.class, EStructuralFeature.class)}.
79+ * <p>
7580 * Performance: this uses reflection once per reference at startup to read a method name. The cost is negligible
7681 * compared to normal init work.
7782 *
8085 */
8186public final class ServiceMethod {
8287
88+ private final Method declaration ;
89+
8390 private final String name ;
8491
8592 private final int arity ;
8693
87- private ServiceMethod (String name , int arity ) {
88- this .name = name ;
94+ private ServiceMethod (Method declaration , int arity ) {
95+ this .declaration = declaration ;
96+ this .name = declaration .getName ();
8997 this .arity = arity ;
9098 }
9199
@@ -98,6 +106,15 @@ public String name() {
98106 return this .name ;
99107 }
100108
109+ /**
110+ * the Java declaration that will be called from AQL.
111+ *
112+ * @return the declaration.
113+ */
114+ public Method declaration () {
115+ return this .declaration ;
116+ }
117+
101118 /**
102119 * Build {@code aql:self.method(...)} for the captured service name.
103120 * <p>
@@ -149,79 +166,230 @@ public String aql(String var, String... params) {
149166 * Instance method with signature {@code R method(T self)}.
150167 */
151168 public static <S , T > ServiceMethod of0 (Inst0 <S , T > ref ) {
152- return new ServiceMethod (methodName (ref ), 0 );
169+ return new ServiceMethod (method (ref ), 0 );
170+ }
171+
172+ /**
173+ * Instance method with signature {@code R method(T self)}.
174+ * <p>
175+ * Use this overload when the referenced Java service is overloaded and you need to disambiguate on the
176+ * {@code self} type.
177+ */
178+ public static <S , T > ServiceMethod of0 (Class <T > selfType , Inst0 <S , T > ref ) {
179+ return new ServiceMethod (method (ref , selfType ), 0 );
180+ }
181+
182+ /**
183+ * Instance method with signature {@code R method(T self)}.
184+ * <p>
185+ * Use this overload when the referenced Java service is overloaded and you need to disambiguate on the declaring
186+ * service and {@code self} types.
187+ */
188+ public static <S , T > ServiceMethod of0 (Class <S > serviceType , Inst0 <S , T > ref , Class <T > selfType ) {
189+ return new ServiceMethod (method (serviceType , ref , selfType ), 0 );
153190 }
154191
155192 /**
156193 * Instance method with signature {@code R method(T self, P1 p1)}.
157194 */
158195 public static <S , T , P1 > ServiceMethod of1 (Inst1 <S , T , P1 > ref ) {
159- return new ServiceMethod (methodName (ref ), 1 );
196+ return new ServiceMethod (method (ref ), 1 );
197+ }
198+
199+ /**
200+ * Instance method with signature {@code R method(T self, P1 p1)}.
201+ * <p>
202+ * Use this overload when the referenced Java service is overloaded and you need to disambiguate on parameter
203+ * types.
204+ */
205+ public static <S , T , P1 > ServiceMethod of1 (Class <T > selfType , Class <P1 > p1Type , Inst1 <S , T , P1 > ref ) {
206+ return new ServiceMethod (method (ref , selfType , p1Type ), 1 );
207+ }
208+
209+ /**
210+ * Instance method with signature {@code R method(T self, P1 p1)}.
211+ * <p>
212+ * Use this overload when the referenced Java service is overloaded and you need to disambiguate on the declaring
213+ * service and parameter types.
214+ */
215+ public static <S , T , P1 > ServiceMethod of1 (Class <S > serviceType , Inst1 <S , T , P1 > ref , Class <T > selfType , Class <P1 > p1Type ) {
216+ return new ServiceMethod (method (serviceType , ref , selfType , p1Type ), 1 );
160217 }
161218
162219 /**
163220 * Instance method with signature {@code R method(T self, P1 p1, P2 p2)}.
164221 */
165222 public static <S , T , P1 , P2 > ServiceMethod of2 (Inst2 <S , T , P1 , P2 > ref ) {
166- return new ServiceMethod (methodName (ref ), 2 );
223+ return new ServiceMethod (method (ref ), 2 );
224+ }
225+
226+ /**
227+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2)}.
228+ */
229+ public static <S , T , P1 , P2 > ServiceMethod of2 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , Inst2 <S , T , P1 , P2 > ref ) {
230+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type ), 2 );
231+ }
232+
233+ /**
234+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2)}.
235+ */
236+ public static <S , T , P1 , P2 > ServiceMethod of2 (Class <S > serviceType , Inst2 <S , T , P1 , P2 > ref , Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type ) {
237+ return new ServiceMethod (method (serviceType , ref , selfType , p1Type , p2Type ), 2 );
167238 }
168239
169240 /**
170241 * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
171242 */
172243 public static <S , T , P1 , P2 , P3 > ServiceMethod of3 (Inst3 <S , T , P1 , P2 , P3 > ref ) {
173- return new ServiceMethod (methodName (ref ), 3 );
244+ return new ServiceMethod (method (ref ), 3 );
245+ }
246+
247+ /**
248+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
249+ */
250+ public static <S , T , P1 , P2 , P3 > ServiceMethod of3 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , Class <P3 > p3Type , Inst3 <S , T , P1 , P2 , P3 > ref ) {
251+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type , p3Type ), 3 );
252+ }
253+
254+ /**
255+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
256+ */
257+ public static <S , T , P1 , P2 , P3 > ServiceMethod of3 (Class <S > serviceType , Inst3 <S , T , P1 , P2 , P3 > ref , Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type ,
258+ Class <P3 > p3Type ) {
259+ return new ServiceMethod (method (serviceType , ref , selfType , p1Type , p2Type , p3Type ), 3 );
174260 }
175261
176262 /**
177263 * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4)}.
178264 */
179265 public static <S , T , P1 , P2 , P3 , P4 > ServiceMethod of4 (Inst4 <S , T , P1 , P2 , P3 , P4 > ref ) {
180- return new ServiceMethod (methodName (ref ), 4 );
266+ return new ServiceMethod (method (ref ), 4 );
267+ }
268+
269+ /**
270+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4)}.
271+ */
272+ public static <S , T , P1 , P2 , P3 , P4 > ServiceMethod of4 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , Class <P3 > p3Type , Class <P4 > p4Type ,
273+ Inst4 <S , T , P1 , P2 , P3 , P4 > ref ) {
274+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type , p3Type , p4Type ), 4 );
275+ }
276+
277+ /**
278+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4)}.
279+ */
280+ public static <S , T , P1 , P2 , P3 , P4 > ServiceMethod of4 (Class <S > serviceType , Inst4 <S , T , P1 , P2 , P3 , P4 > ref , Class <T > selfType , Class <P1 > p1Type ,
281+ Class <P2 > p2Type , Class <P3 > p3Type , Class <P4 > p4Type ) {
282+ return new ServiceMethod (method (serviceType , ref , selfType , p1Type , p2Type , p3Type , p4Type ), 4 );
181283 }
182284
183285 /**
184286 * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)}.
185287 */
186288 public static <S , T , P1 , P2 , P3 , P4 , P5 > ServiceMethod of5 (Inst5 <S , T , P1 , P2 , P3 , P4 , P5 > ref ) {
187- return new ServiceMethod (methodName (ref ), 5 );
289+ return new ServiceMethod (method (ref ), 5 );
290+ }
291+
292+ /**
293+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)}.
294+ */
295+ public static <S , T , P1 , P2 , P3 , P4 , P5 > ServiceMethod of5 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , Class <P3 > p3Type , Class <P4 > p4Type ,
296+ Class <P5 > p5Type , Inst5 <S , T , P1 , P2 , P3 , P4 , P5 > ref ) {
297+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type , p3Type , p4Type , p5Type ), 5 );
188298 }
189299
300+ /**
301+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)}.
302+ */
303+ // CHECKSTYLE:OFF
304+ public static <S , T , P1 , P2 , P3 , P4 , P5 > ServiceMethod of5 (Class <S > serviceType , Inst5 <S , T , P1 , P2 , P3 , P4 , P5 > ref , Class <T > selfType , Class <P1 > p1Type ,
305+ Class <P2 > p2Type , Class <P3 > p3Type , Class <P4 > p4Type , Class <P5 > p5Type ) {
306+ return new ServiceMethod (method (serviceType , ref , selfType , p1Type , p2Type , p3Type , p4Type , p5Type ), 5 );
307+ }
308+ // CHECKSTYLE:ON
309+
190310 /**
191311 * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)}.
192312 */
193313 public static <S , T , P1 , P2 , P3 , P4 , P5 , P6 > ServiceMethod of6 (Inst6 <S , T , P1 , P2 , P3 , P4 , P5 , P6 > ref ) {
194- return new ServiceMethod (methodName (ref ), 6 );
314+ return new ServiceMethod (method (ref ), 6 );
315+ }
316+
317+ /**
318+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)}.
319+ */
320+ // CHECKSTYLE:OFF
321+ public static <S , T , P1 , P2 , P3 , P4 , P5 , P6 > ServiceMethod of6 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , Class <P3 > p3Type , Class <P4 > p4Type ,
322+ Class <P5 > p5Type , Class <P6 > p6Type , Inst6 <S , T , P1 , P2 , P3 , P4 , P5 , P6 > ref ) {
323+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type , p3Type , p4Type , p5Type , p6Type ), 6 );
195324 }
325+ // CHECKSTYLE:ON
326+
327+ /**
328+ * Instance method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)}.
329+ */
330+ // CHECKSTYLE:OFF
331+ public static <S , T , P1 , P2 , P3 , P4 , P5 , P6 > ServiceMethod of6 (Class <S > serviceType , Inst6 <S , T , P1 , P2 , P3 , P4 , P5 , P6 > ref , Class <T > selfType ,
332+ Class <P1 > p1Type , Class <P2 > p2Type , Class <P3 > p3Type , Class <P4 > p4Type , Class <P5 > p5Type , Class <P6 > p6Type ) {
333+ return new ServiceMethod (method (serviceType , ref , selfType , p1Type , p2Type , p3Type , p4Type , p5Type , p6Type ), 6 );
334+ }
335+ // CHECKSTYLE:ON
196336
197337 // ---------------------- Factories for static methods ----------------------
198338
199339 /**
200340 * Static method with signature {@code R method(T self)}.
201341 */
202342 public static <T > ServiceMethod ofStatic0 (IStat0 <T > ref ) {
203- return new ServiceMethod (methodName (ref ), 0 );
343+ return new ServiceMethod (method (ref ), 0 );
344+ }
345+
346+ /**
347+ * Static method with signature {@code R method(T self)}.
348+ */
349+ public static <T > ServiceMethod ofStatic0 (Class <T > selfType , IStat0 <T > ref ) {
350+ return new ServiceMethod (method (ref , selfType ), 0 );
204351 }
205352
206353 /**
207354 * Static method with signature {@code R method(T self, P1 p1)}.
208355 */
209356 public static <T , P1 > ServiceMethod ofStatic1 (IStat1 <T , P1 > ref ) {
210- return new ServiceMethod (methodName (ref ), 1 );
357+ return new ServiceMethod (method (ref ), 1 );
358+ }
359+
360+ /**
361+ * Static method with signature {@code R method(T self, P1 p1)}.
362+ */
363+ public static <T , P1 > ServiceMethod ofStatic1 (Class <T > selfType , Class <P1 > p1Type , IStat1 <T , P1 > ref ) {
364+ return new ServiceMethod (method (ref , selfType , p1Type ), 1 );
211365 }
212366
213367 /**
214368 * Static method with signature {@code R method(T self, P1 p1, P2 p2)}.
215369 */
216370 public static <T , P1 , P2 > ServiceMethod ofStatic2 (IStat2 <T , P1 , P2 > ref ) {
217- return new ServiceMethod (methodName (ref ), 2 );
371+ return new ServiceMethod (method (ref ), 2 );
372+ }
373+
374+ /**
375+ * Static method with signature {@code R method(T self, P1 p1, P2 p2)}.
376+ */
377+ public static <T , P1 , P2 > ServiceMethod ofStatic2 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , IStat2 <T , P1 , P2 > ref ) {
378+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type ), 2 );
218379 }
219380
220381 /**
221382 * Static method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
222383 */
223384 public static <T , P1 , P2 , P3 > ServiceMethod ofStatic3 (IStat3 <T , P1 , P2 , P3 > ref ) {
224- return new ServiceMethod (methodName (ref ), 3 );
385+ return new ServiceMethod (method (ref ), 3 );
386+ }
387+
388+ /**
389+ * Static method with signature {@code R method(T self, P1 p1, P2 p2, P3 p3)}.
390+ */
391+ public static <T , P1 , P2 , P3 > ServiceMethod ofStatic3 (Class <T > selfType , Class <P1 > p1Type , Class <P2 > p2Type , Class <P3 > p3Type , IStat3 <T , P1 , P2 , P3 > ref ) {
392+ return new ServiceMethod (method (ref , selfType , p1Type , p2Type , p3Type ), 3 );
225393 }
226394
227395 // ---------------------- SAMs for method references ----------------------
@@ -427,14 +595,42 @@ public interface IStat3<T, P1, P2, P3> extends Serializable {
427595
428596 // ---------------------- Lambda -> method name ----------------------
429597
430- private static String methodName (Serializable lambdaRef ) {
598+ private static Method method (Serializable lambdaRef , Class <?>... expectedParameterTypes ) {
599+ try {
600+ SerializedLambda lambda = serializedLambda (lambdaRef );
601+ Class <?> implementationClass = Class .forName (lambda .getImplClass ().replace ('/' , '.' ), false , lambdaRef .getClass ().getClassLoader ());
602+ MethodType methodType = MethodType .fromMethodDescriptorString (lambda .getImplMethodSignature (), implementationClass .getClassLoader ());
603+ Method method = thisClassMethod (implementationClass , lambda .getImplMethodName (), methodType .parameterArray ());
604+ if (expectedParameterTypes .length > 0 && !Arrays .equals (method .getParameterTypes (), expectedParameterTypes )) {
605+ throw new IllegalArgumentException (
606+ MessageFormat .format ("Resolved method {0} has parameters {1} but expected {2}" , method , Arrays .toString (method .getParameterTypes ()), Arrays .toString (expectedParameterTypes )));
607+ }
608+ return method ;
609+ } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | SecurityException | IllegalAccessException e ) {
610+ throw new IllegalStateException ("Cannot resolve method declaration from lambda" , e );
611+ }
612+ }
613+
614+ private static Method method (Class <?> expectedServiceType , Serializable lambdaRef , Class <?>... expectedParameterTypes ) {
615+ Method method = method (lambdaRef , expectedParameterTypes );
616+ if (!expectedServiceType .isAssignableFrom (method .getDeclaringClass ())) {
617+ throw new IllegalArgumentException (MessageFormat .format ("Resolved method {0} is declared on {1} but expected a service assignable to {2}" , method ,
618+ method .getDeclaringClass ().getName (), expectedServiceType .getName ()));
619+ }
620+ return method ;
621+ }
622+
623+ private static SerializedLambda serializedLambda (Serializable lambdaRef ) throws NoSuchMethodException , IllegalAccessException , InvocationTargetException {
624+ Method writeReplace = lambdaRef .getClass ().getDeclaredMethod ("writeReplace" );
625+ writeReplace .setAccessible (true );
626+ return (SerializedLambda ) writeReplace .invoke (lambdaRef );
627+ }
628+
629+ private static Method thisClassMethod (Class <?> implementationClass , String methodName , Class <?>[] parameterTypes ) throws NoSuchMethodException {
431630 try {
432- Method m = lambdaRef .getClass ().getDeclaredMethod ("writeReplace" );
433- m .setAccessible (true );
434- SerializedLambda sl = (SerializedLambda ) m .invoke (lambdaRef );
435- return sl .getImplMethodName ();
436- } catch (InvocationTargetException | NoSuchMethodException | SecurityException | IllegalAccessException e ) {
437- throw new IllegalStateException ("Cannot resolve method name from lambda" , e );
631+ return implementationClass .getDeclaredMethod (methodName , parameterTypes );
632+ } catch (NoSuchMethodException exception ) {
633+ return implementationClass .getMethod (methodName , parameterTypes );
438634 }
439635 }
440636
@@ -457,4 +653,3 @@ private void checkArity(String... params) {
457653 }
458654 }
459655}
460-
0 commit comments