com.lambdaworks.redis.dynamic.DefaultRedisCommandsMetadata Maven / Gradle / Ivy
/*
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lambdaworks.redis.dynamic;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.lambdaworks.redis.internal.LettuceAssert;
/**
* Default implementation of {@link RedisCommandsMetadata}.
*
* @author Mark Paluch
* @since 5.0
*/
class DefaultRedisCommandsMetadata implements RedisCommandsMetadata {
/** The package separator character: '.' */
private static final char PACKAGE_SEPARATOR = '.';
private final Class> apiInterface;
/**
* Create {@link DefaultRedisCommandsMetadata} given a {@link Class command interface}.
*
* @param apiInterface must not be {@literal null}.
*/
DefaultRedisCommandsMetadata(Class> apiInterface) {
this.apiInterface = apiInterface;
}
@Override
public Class> getCommandsInterface() {
return apiInterface;
}
@Override
public Collection getMethods() {
Set result = new HashSet();
for (Method method : getCommandsInterface().getMethods()) {
method = getMostSpecificMethod(method, getCommandsInterface());
if (isQueryMethodCandidate(method)) {
result.add(method);
}
}
return Collections.unmodifiableSet(result);
}
/**
* Checks whether the given method is a query method candidate.
*
* @param method
* @return
*/
private boolean isQueryMethodCandidate(Method method) {
return !method.isBridge() && !method.isDefault();
}
/**
* Given a method, which may come from an interface, and a target class used in the current reflective invocation, find the
* corresponding target method if there is one. E.g. the method may be {@code IFoo.bar()} and the target class may be
* {@code DefaultFoo}. In this case, the method may be {@code DefaultFoo.bar()}. This enables attributes on that method to
* be found.
*
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation. May be {@code null} or may not even implement the method.
* @return the specific target method, or the original method if the {@code targetClass} doesn't implement it or is
* {@code null}
*/
public static Method getMostSpecificMethod(Method method, Class> targetClass) {
if (method != null && isOverridable(method, targetClass) && targetClass != null
&& targetClass != method.getDeclaringClass()) {
try {
try {
return targetClass.getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException ex) {
return method;
}
} catch (SecurityException ex) {
}
}
return method;
}
/**
* Determine whether the given method is overridable in the given target class.
*
* @param method the method to check
* @param targetClass the target class to check against
*/
private static boolean isOverridable(Method method, Class> targetClass) {
if (Modifier.isPrivate(method.getModifiers())) {
return false;
}
if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
return true;
}
return getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass));
}
/**
* Determine the name of the package of the given class, e.g. "java.lang" for the {@code java.lang.String} class.
*
* @param clazz the class
* @return the package name, or the empty String if the class is defined in the default package
*/
private static String getPackageName(Class> clazz) {
LettuceAssert.notNull(clazz, "Class must not be null");
return getPackageName(clazz.getName());
}
/**
* Determine the name of the package of the given fully-qualified class name, e.g. "java.lang" for the
* {@code java.lang.String} class name.
*
* @param fqClassName the fully-qualified class name
* @return the package name, or the empty String if the class is defined in the default package
*/
private static String getPackageName(String fqClassName) {
LettuceAssert.notNull(fqClassName, "Class name must not be null");
int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy