blob: 48f6fc2f30ed04594cdea1bf381e051d3ae8aeaa [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2016 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
******************************************************************************/
package org.eclipse.lsp4j.jsonrpc.services;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Set;
import java.util.function.Consumer;
public final class AnnotationUtil {
private AnnotationUtil() {}
public static void findDelegateSegments(Class<?> clazz, Set<Class<?>> visited, Consumer<Method> acceptor) {
if (clazz == null || !visited.add(clazz))
return;
findDelegateSegments(clazz.getSuperclass(), visited, acceptor);
for (Class<?> interf : clazz.getInterfaces()) {
findDelegateSegments(interf, visited, acceptor);
}
for (Method method : clazz.getDeclaredMethods()) {
if (isDelegateMethod(method)) {
acceptor.accept(method);
}
}
}
public static boolean isDelegateMethod(Method method) {
if (!method.isSynthetic()) {
JsonDelegate jsonDelegate = method.getAnnotation(JsonDelegate.class);
if (jsonDelegate != null) {
if (!(method.getParameterCount() == 0 && method.getReturnType().isInterface())) {
throw new IllegalStateException(
"The method " + method.toString() + " is not a proper @JsonDelegate method.");
}
return true;
}
}
return false;
}
/**
* Depth first search for annotated methods in hierarchy.
*/
public static void findRpcMethods(Class<?> clazz, Set<Class<?>> visited, Consumer<MethodInfo> acceptor) {
if (clazz == null || !visited.add(clazz))
return;
findRpcMethods(clazz.getSuperclass(), visited, acceptor);
for (Class<?> interf : clazz.getInterfaces()) {
findRpcMethods(interf, visited, acceptor);
}
String segment = getSegment(clazz);
for (Method method : clazz.getDeclaredMethods()) {
MethodInfo methodInfo = createMethodInfo(method, segment);
if (methodInfo != null) {
acceptor.accept(methodInfo);
}
}
}
protected static String getSegment(Class<?> clazz) {
JsonSegment jsonSegment = clazz.getAnnotation(JsonSegment.class);
return jsonSegment == null ? "" : jsonSegment.value() + "/";
}
protected static MethodInfo createMethodInfo(Method method, String segment) {
if (!method.isSynthetic()) {
JsonRequest jsonRequest = method.getAnnotation(JsonRequest.class);
if (jsonRequest != null) {
return createRequestInfo(method, segment, jsonRequest);
}
JsonNotification jsonNotification = method.getAnnotation(JsonNotification.class);
if (jsonNotification != null) {
return createNotificationInfo(method, segment, jsonNotification);
}
}
return null;
}
protected static MethodInfo createNotificationInfo(Method method, String segment, JsonNotification jsonNotification) {
MethodInfo methodInfo = createMethodInfo(method, jsonNotification.useSegment(), segment, jsonNotification.value());
methodInfo.isNotification = true;
return methodInfo;
}
protected static MethodInfo createRequestInfo(Method method, String segment, JsonRequest jsonRequest) {
return createMethodInfo(method, jsonRequest.useSegment(), segment, jsonRequest.value());
}
protected static MethodInfo createMethodInfo(Method method, boolean useSegment, String segment, String value) {
method.setAccessible(true);
MethodInfo methodInfo = new MethodInfo();
methodInfo.method = method;
methodInfo.parameterTypes = getParameterTypes(method);
methodInfo.name = getMethodName(method, useSegment, segment, value);
return methodInfo;
}
protected static String getMethodName(Method method, boolean useSegment, String segment, String value) {
String name = value != null && value.length() > 0 ? value : method.getName();
return useSegment ? segment + name : name;
}
protected static Type[] getParameterTypes(Method method) {
return Arrays.stream(method.getParameters()).map(t -> t.getParameterizedType()).toArray(Type[]::new);
}
static class MethodInfo {
private static Type[] EMPTY_TYPE_ARRAY = {};
public String name;
public Method method;
public Type[] parameterTypes = EMPTY_TYPE_ARRAY;
public boolean isNotification = false;
}
static class DelegateInfo {
public Method method;
public Object delegate;
}
}