/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you 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 org.openengsb.core.edbi.jdbc.util; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openengsb.core.api.model.OpenEngSBModel; import org.openengsb.core.api.model.annotation.Model; import org.openengsb.core.api.model.annotation.OpenEngSBModelId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Introspector */ public final class Introspector { private static final Logger LOG = LoggerFactory.getLogger(Introspector.class); private Introspector() { // static class } /** * Returns a create of all property names and their respective type. * * @param clazz the class to introspect * @return a mapping between property names and their type. */ public static Map<String, Class<?>> getPropertyTypeMap(Class<?> clazz) { return getPropertyTypeMap(clazz, "class"); } /** * Returns a create of all property names and their respective type. Any property passed via exclude are removed * from the create before hand. * * @param clazz the class to introspect * @param exclude the properties to exclude * @return a mapping between property names and their type. */ public static Map<String, Class<?>> getPropertyTypeMap(Class<?> clazz, String... exclude) { PropertyDescriptor[] descriptors; try { descriptors = getPropertyDescriptors(clazz); } catch (IntrospectionException e) { LOG.error("Failed to introspect " + clazz, e); return Collections.emptyMap(); } HashMap<String, Class<?>> map = new HashMap<>(descriptors.length); for (PropertyDescriptor pd : descriptors) { map.put(pd.getName(), pd.getPropertyType()); } for (String property : exclude) { map.remove(property); } return map; } /** * Returns a create of all property names and their values contained in the given object. * * @param object the object to visit out of * @return the extracted value create */ public static Map<String, Object> read(Object object) { PropertyDescriptor[] descriptors; Class<?> clazz = object.getClass(); try { descriptors = getPropertyDescriptors(clazz); } catch (IntrospectionException e) { LOG.error("Failed to introspect " + clazz, e); return Collections.emptyMap(); } HashMap<String, Object> map = new HashMap<>(descriptors.length); for (PropertyDescriptor pd : descriptors) { try { Object value = pd.getReadMethod().invoke(object); map.put(pd.getName(), value); } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) { LOG.error("Failed to visit property " + pd.getName(), e); } } map.remove("class"); return map; } /** * Returns all {@link java.lang.reflect.Field}s declared in the given {@link Class} that are annotated with the * given {@link java.lang.annotation.Annotation}. * * @param clazz the class to introspect * @param annotation the annotation to look for * @return a collection of {@link java.lang.reflect.Field}s that are annotated with the given annotation */ public static List<Field> getAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotation) { Field[] fields = clazz.getDeclaredFields(); List<Field> annotatedFields = new ArrayList<>(fields.length); for (Field f : fields) { if (f.isAnnotationPresent(annotation)) { annotatedFields.add(f); } } return annotatedFields; } /** * Uses {@link #getAnnotatedFields(Class, Class)} to look for a field annotated with the {@code OpenEngSBModelId} * annotation, and if present, returns that Field instance. * * @param modelClass the class to introspect * @return the {@link java.lang.reflect.Field} annotated with {@code OpenEngSBModelId}, or null if none exists */ public static Field getOpenEngSBModelIdField(Class<?> modelClass) { List<Field> fields = getAnnotatedFields(modelClass, OpenEngSBModelId.class); if (fields.isEmpty()) { return null; } return fields.get(0); } /** * Uses {@link #getAnnotatedFields(Class, Class)} to look for a field annotated with the {@code OpenEngSBModelId} * annotation, and if present, returns its name. * * @param modelClass the class to introspect * @return the name of the {@link java.lang.reflect.Field} annotated with {@code OpenEngSBModelId}, or null */ public static String getOpenEngSBModelIdProperty(Class<?> modelClass) { Field field = getOpenEngSBModelIdField(modelClass); return (field == null) ? null : field.getName(); } /** * Checks whether the given object is a OpenEngSBModel (i.e. is either assignable by OpenEngSBModel or has the Model * annotation. * * @param object the object to check * @return true if the object is a model */ public static boolean isModel(Object object) { return isModelClass(object.getClass()); } /** * Checks whether the given class is a OpenEngSBModel class (i.e. is either assignable by OpenEngSBModel or has the * Model annotation. * * @param clazz the class to check * @return true if the class is a model */ public static boolean isModelClass(Class<?> clazz) { return OpenEngSBModel.class.isAssignableFrom(clazz) || clazz.isAnnotationPresent(Model.class); } /** * Returns descriptors for all properties of the bean. May return {@code null} if the information should be obtained * by automatic analysis. * * @param beanClass the class to introspect * @return an array of {@code PropertyDescriptor}s describing all properties supported by the bean or {@code null} */ private static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) throws IntrospectionException { BeanInfo beanInfo = java.beans.Introspector.getBeanInfo(beanClass); return beanInfo.getPropertyDescriptors(); } }