ClassHelper.java
/*
* Copyright (C) 2017 Red Hat, Inc.
*
* 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 io.atlasmap.java.core;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.atlasmap.core.AtlasUtil;
import io.atlasmap.java.core.accessor.FieldAccessor;
import io.atlasmap.java.core.accessor.GetterAccessor;
import io.atlasmap.java.core.accessor.JavaChildAccessor;
public class ClassHelper {
public static List<String> getterMethodNames(String fieldName) {
List<String> opts = new ArrayList<String>();
opts.add(getMethodNameFromFieldName(fieldName));
opts.add(isMethodNameFromFieldName(fieldName));
return opts;
}
public static String getMethodNameFromFieldName(String fieldName) {
return "get" + StringUtil.capitalizeFirstLetter(fieldName);
}
public static String isMethodNameFromFieldName(String fieldName) {
return "is" + StringUtil.capitalizeFirstLetter(fieldName);
}
public static Method detectGetterMethod(Class<?> clazz, String methodName) throws NoSuchMethodException {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().equals(methodName) && method.getParameterCount() == 0) {
return method;
}
}
throw new NoSuchMethodException(
String.format("No matching getter method for class=%s method=%s", clazz.getName(), methodName));
}
public static Map<String, Method> detectAllGetterMethods(Class<?> clazz) throws Exception {
Map<String, Method> answer = new HashMap<>();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().startsWith("get") && method.getParameterTypes().length == 0
&& method.getReturnType() != Void.class) {
answer.put(StringUtil.getFieldNameFromGetter(method.getName()), method);
} else if (method.getName().startsWith("is") && method.getParameterTypes().length == 0
&& (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class)) {
answer.put(StringUtil.getFieldNameFromGetter(method.getName()), method);
}
}
return answer;
}
public static List<Field> detectAllJavaFields(Class<?> clazz) {
List<Field> answer = new ArrayList<>();
Class<?> targetClazz = clazz;
while (targetClazz != null && targetClazz != Object.class) {
try {
Field[] fields = targetClazz.getDeclaredFields();
answer.addAll(Arrays.asList(fields));
} catch (Exception e) {
e.getMessage(); // ignore
targetClazz = targetClazz.getSuperclass();
}
}
return answer;
}
public static Method detectSetterMethod(Class<?> clazz, String methodName, Class<?> paramType)
throws NoSuchMethodException {
List<Method> candidates = new ArrayList<Method>();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().equals(methodName) && method.getParameterCount() == 1) {
candidates.add(method);
}
}
String paramTypeClassName = paramType == null ? null : paramType.getName();
if (candidates.size() == 0) {
throw new NoSuchMethodException(
String.format("No matching setter found for class=%s method=%s paramType=%s", clazz.getName(),
methodName, paramTypeClassName));
}
if (paramType != null) {
for (Method candidate : candidates) {
// Getter and setter w/ same returnType & paramType
if (candidate.getParameterTypes()[0].isAssignableFrom(paramType)) {
return candidate;
}
}
throw new NoSuchMethodException(
String.format("No matching setter found for class=%s method=%s paramType=%s", clazz.getName(),
methodName, paramTypeClassName));
}
// paramType is null, let's do some more digging...
if (candidates.size() == 1) {
return candidates.get(0);
}
Method getter = null;
Class<?> returnType = null;
for (String prefix : Arrays.asList("get", "is")) {
try {
getter = detectGetterMethod(clazz, methodName.replace("set", prefix));
returnType = getter.getReturnType();
break;
} catch (NoSuchMethodException nsme) {
// System.out.println("\t\t could not find getter " + methodName.replace("set",
// prefix));
}
}
// Solid match
for (Method candidate : candidates) {
// Getter and setter w/ same returnType & paramType
Class<?> candidateReturnType = candidate.getParameterTypes()[0];
if (returnType == null) {
if (candidateReturnType == null) {
return candidate;
}
} else if (returnType.isAssignableFrom(candidateReturnType)) {
return candidate;
}
}
// Not as good of a match .. find one with a matching converter
/*
* for (Method candidate : candidates) {
* if(candidate.getParameterTypes()[0].equals(String.class)) { return candidate;
* } }
*/
// Yikes! User should specify type, or provide a converter
throw new NoSuchMethodException(String.format("Unable to auto-detect setter class=%s method=%s paramType=%s",
clazz.getName(), methodName, paramTypeClassName));
}
public static Class<?> detectClassFromTypeArgument(Type type) {
return detectClassFromTypeArgumentAt(type, 0);
}
public static Class<?> detectClassFromTypeArgumentAt(Type type, int pos) {
if (type == null || !(type instanceof ParameterizedType)) {
return Object.class;
}
ParameterizedType genericType = (ParameterizedType) type;
Type[] typeArgs = genericType.getActualTypeArguments();
if (typeArgs == null || typeArgs.length <= pos) {
return Object.class;
}
return typeArgs[pos] instanceof Class ? (Class<?>) typeArgs[pos] : Object.class;
}
public static Method lookupGetterMethod(Object object, String name) {
List<String> getters = getterMethodNames(name);
Method getterMethod = null;
for (String getter : getters) {
try {
getterMethod = detectGetterMethod(object.getClass(), getter);
break;
} catch (NoSuchMethodException e) {
// exhaust options
}
}
if (getterMethod == null) {
return null;
} else {
getterMethod.setAccessible(true);
return getterMethod;
}
}
public static Field lookupJavaField(Object source, String fieldName) {
Class<?> targetClazz = source != null ? source.getClass() : null;
while (targetClazz != null && targetClazz != Object.class) {
try {
Field field = targetClazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (Exception e) {
e.getMessage(); // ignore
targetClazz = targetClazz.getSuperclass();
}
}
return null;
}
public static JavaChildAccessor lookupAccessor(Object source, String name) {
if (source == null || AtlasUtil.isEmpty(name)) {
return null;
}
Method m = lookupGetterMethod(source, name);
if (m != null) {
return new GetterAccessor(source, name, m);
}
Field f = lookupJavaField(source, name);
if (f != null) {
return new FieldAccessor(source, name, f);
}
return null;
}
public static List<JavaChildAccessor> lookupAllAccessors(Object source) throws Exception {
List<JavaChildAccessor> answer = new ArrayList<>();
if (source == null) {
return answer;
}
Set<String> names = new HashSet<>();
Map<String, Method> getters = detectAllGetterMethods(source.getClass());
getters.forEach((k, v) -> {
answer.add(new GetterAccessor(source, k, v));
names.add(k);
});
List<Field> fields = detectAllJavaFields(source.getClass());
fields.forEach(f -> {
if (!names.contains(f.getName())) {
answer.add(new FieldAccessor(source, f.getName(), f));
}
});
return answer;
}
}