| /** |
| * Copyright (c) 2011, 2015 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Florian Pirchner - Initial implementation |
| */ |
| package org.eclipse.osbp.runtime.common.annotations; |
| |
| import java.beans.BeanInfo; |
| import java.beans.IntrospectionException; |
| import java.beans.Introspector; |
| import java.beans.PropertyChangeListener; |
| import java.beans.PropertyChangeSupport; |
| import java.beans.PropertyDescriptor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.commons.lang.reflect.MethodUtils; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * The Class DtoUtils. |
| */ |
| public class DtoUtils { |
| |
| /** The Constant LOGGER. */ |
| private static final Logger LOGGER = LoggerFactory.getLogger(DtoUtils.class); |
| |
| /** The infos. */ |
| private static Map<Class<?>, Info> INFOS = Collections.synchronizedMap(new HashMap<Class<?>, DtoUtils.Info>()); |
| |
| /** |
| * Returns the dispose field. Field annotated with {@link Dispose}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the dispose field |
| */ |
| public static Field getDisposeField(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDisposeField(); |
| } |
| |
| /** |
| * Returns the dispose field. Field annotated with {@link Dirty}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the dirty field |
| */ |
| public static Field getDirtyField(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDirtyField(); |
| } |
| |
| /** |
| * Returns the domain key field. Field annotated with {@link DomainKey}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the domain key field |
| */ |
| public static Field getDomainKeyField(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDomainKeyField(); |
| } |
| |
| /** |
| * Returns the id field. Field annotated with {@link Id}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the id field |
| */ |
| public static Field getIdField(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getIdField(); |
| } |
| |
| /** |
| * Returns the id key field. Field annotated with {@link Id}. |
| * |
| * @param instance |
| * the instance |
| * @param idProperty |
| * the id property |
| * @return the value |
| */ |
| public static Object getValue(Object instance, String idProperty) { |
| try { |
| BeanInfo beanInfo = Introspector.getBeanInfo(instance.getClass()); |
| for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { |
| if (pd.getName().equals(idProperty)) { |
| Method idMethod = pd.getReadMethod(); |
| return idMethod.invoke(instance); |
| } |
| } |
| } catch (Exception e) { |
| LOGGER.error("{}", e); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the domain description field. Field annotated with |
| * #DomainDescription. |
| * |
| * @param clazz |
| * the clazz |
| * @return the domain description field |
| */ |
| public static Field getDomainDescriptionField(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDomainDescriptionField(); |
| } |
| |
| /** |
| * Returns the dispose field. Field annotated with {@link Dirty}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the dirty getter |
| */ |
| public static Method getDirtyGetter(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDirtyGetter(); |
| } |
| |
| /** |
| * Returns the dispose field. Field annotated with {@link Dirty}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the dirty setter |
| */ |
| public static Method getDirtySetter(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDirtySetter(); |
| } |
| |
| /** |
| * Returns the dispose method. Method annotated with {@link Dispose}. |
| * |
| * @param clazz |
| * the clazz |
| * @return the dispose method |
| */ |
| public static Method getDisposeMethod(Class<?> clazz) { |
| Info info = getInfo(clazz); |
| return info.getDisposeMethod(); |
| } |
| |
| /** |
| * Returns true, if the given field is a dispose field. |
| * |
| * @param clazz |
| * the clazz |
| * @param fieldName |
| * the field name |
| * @return true, if is dispose field |
| */ |
| public static boolean isDisposeField(Class<?> clazz, String fieldName) { |
| Info info = getInfo(clazz); |
| return info.getDisposeField() != null ? info.getDisposeField().getName().equals(fieldName) : false; |
| } |
| |
| /** |
| * Returns true, if the given field is a dirty field. Dirty fields indicate |
| * that the dto is dirty. |
| * |
| * @param clazz |
| * the clazz |
| * @param fieldName |
| * the field name |
| * @return true, if is dirty field |
| */ |
| public static boolean isDirtyField(Class<?> clazz, String fieldName) { |
| Info info = getInfo(clazz); |
| return info.getDirtyField() != null ? info.getDirtyField().getName().equals(fieldName) : false; |
| } |
| |
| /** |
| * Returns true, if the given method is a dispose method. |
| * |
| * @param clazz |
| * the clazz |
| * @param methodName |
| * the method name |
| * @return true, if is dispose method |
| */ |
| public static boolean isDisposeMethod(Class<?> clazz, String methodName) { |
| Info info = getInfo(clazz); |
| return info.getDisposeField() != null ? info.getDisposeMethod().getName().equals(methodName) : false; |
| } |
| |
| /** |
| * Tries to invoke the dispose method. |
| * |
| * @param obj |
| * the obj |
| * @return true, if the method could be invoked. False otherwise. |
| */ |
| public static boolean invokeDisposeMethod(Object obj) { |
| Info info = getInfo(obj.getClass()); |
| if (info != null && info.getDisposeMethod() != null) { |
| try { |
| info.getDisposeMethod().invoke(obj, new Object[0]); |
| } catch (IllegalAccessException e) { |
| return false; |
| } catch (IllegalArgumentException e) { |
| return false; |
| } catch (InvocationTargetException e) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Tries to invoke the setDirty method. |
| * |
| * @param obj |
| * the obj |
| * @param value |
| * the value |
| * @return true, if the method could be invoked. False otherwise. |
| */ |
| public static boolean invokeDirtySetter(Object obj, boolean value) { |
| Info info = getInfo(obj.getClass()); |
| if (info != null && info.getDirtySetter() != null) { |
| try { |
| info.getDirtySetter().invoke(obj, new Object[] { value }); |
| } catch (IllegalAccessException e) { |
| return false; |
| } catch (IllegalArgumentException e) { |
| return false; |
| } catch (InvocationTargetException e) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Tries to invoke the dirty getter. If there is no dirty getter available, |
| * the method throws an {@link IllegalAccessException}. |
| * |
| * @param obj |
| * the obj |
| * @return true, if is dirty |
| * @throws IllegalAccessException |
| * the illegal access exception |
| */ |
| public static boolean isDirty(Object obj) throws IllegalAccessException { |
| return invokeDirtyGetter(obj); |
| } |
| |
| /** |
| * Tries to invoke the isDirty method. |
| * |
| * @param obj |
| * the obj |
| * @return true, if the method could be invoked. False otherwise. |
| * @throws IllegalAccessException |
| * the illegal access exception |
| */ |
| public static boolean invokeDirtyGetter(Object obj) throws IllegalAccessException { |
| Info info = getInfo(obj.getClass()); |
| if (info != null && info.getDirtySetter() != null) { |
| try { |
| return (Boolean) info.getDirtyGetter().invoke(obj, new Object[0]); |
| } catch (IllegalAccessException e) { |
| } catch (IllegalArgumentException e) { |
| } catch (InvocationTargetException e) { |
| } |
| } |
| throw new IllegalAccessException("Not a valid call"); |
| } |
| |
| /** |
| * Returns the info for the given class. |
| * |
| * @param clazz |
| * the clazz |
| * @return the info |
| */ |
| protected static Info getInfo(Class<?> clazz) { |
| Info info = INFOS.get(clazz); |
| if (info == null) { |
| info = createInfo(clazz); |
| } |
| return info; |
| } |
| |
| /** |
| * Creates a new info. |
| * |
| * @param clazz |
| * the clazz |
| * @return info |
| */ |
| private static Info createInfo(Class<?> clazz) { |
| Info info = new Info(); |
| applyFieldInfo(clazz, info); |
| applyMethodInfo(clazz, info); |
| |
| INFOS.put(clazz, info); |
| |
| return info; |
| } |
| |
| /** |
| * Returns the adapter which has proper type from the given dto. Or |
| * <code>null</code> if no adapter is available. |
| * |
| * @param <A> |
| * the generic type |
| * @param type |
| * the type |
| * @param dto |
| * the dto |
| * @return the adapter |
| */ |
| @SuppressWarnings("unchecked") |
| public static <A> A getAdapter(Class<A> type, Object dto) { |
| if (type == null || dto == null) { |
| return null; |
| } |
| Info info = getInfo(dto.getClass()); |
| if (info.getPropertyChangeSupportField() == null) { |
| return null; |
| } |
| |
| try { |
| info.getPropertyChangeSupportField().setAccessible(true); |
| PropertyChangeSupport changeSupport = (PropertyChangeSupport) info.getPropertyChangeSupportField().get(dto); |
| if (changeSupport == null) { |
| return null; |
| } |
| |
| for (PropertyChangeListener listener : changeSupport.getPropertyChangeListeners()) { |
| if (type.isAssignableFrom(listener.getClass())) { |
| return (A) listener; |
| } |
| } |
| } catch (IllegalArgumentException e) { |
| LOGGER.error("{}", e); |
| } catch (IllegalAccessException e) { |
| LOGGER.error("{}", e); |
| } |
| return null; |
| } |
| |
| /** |
| * Registers the adapter as a property changed listener. |
| * |
| * @param adapter |
| * the adapter |
| * @param dto |
| * the dto |
| */ |
| public static void registerAdapter(PropertyChangeListener adapter, Object dto) { |
| if (adapter == null || dto == null) { |
| return; |
| } |
| |
| try { |
| MethodUtils.invokeMethod(dto, "addPropertyChangeListener", adapter); |
| } catch (SecurityException e) { |
| LOGGER.info("Observer for dirtyState handling could not be added for " + dto.getClass().getName()); |
| } catch (IllegalAccessException e) { |
| LOGGER.info("Observer for dirtyState handling could not be added for " + dto.getClass().getName()); |
| } catch (IllegalArgumentException e) { |
| LOGGER.info("Observer for dirtyState handling could not be added for " + dto.getClass().getName()); |
| } catch (InvocationTargetException e) { |
| LOGGER.info("Observer for dirtyState handling could not be added for " + dto.getClass().getName()); |
| } catch (NoSuchMethodException e) { |
| LOGGER.info("Observer for dirtyState handling could not be added for " + dto.getClass().getName()); |
| } |
| } |
| |
| /** |
| * Applies all required field infos to the info object. |
| * |
| * @param clazz |
| * the clazz |
| * @param info |
| * the info |
| */ |
| private static void applyFieldInfo(Class<?> clazz, Info info) { |
| try { |
| for (Field field : clazz.getDeclaredFields()) { |
| if (field.getAnnotation(Dispose.class) != null) { |
| info.disposeField = field; |
| } |
| if (field.getAnnotation(DomainKey.class) != null) { |
| info.domainKeyField = field; |
| } |
| if (field.getAnnotation(DomainDescription.class) != null) { |
| info.domainDescriptionField = field; |
| } |
| if (field.getAnnotation(Id.class) != null) { |
| info.idField = field; |
| } |
| |
| if (field.getType() == PropertyChangeSupport.class) { |
| info.propertyChangeSupportField = field; |
| } |
| |
| if (field.getAnnotation(Dirty.class) != null) { |
| info.dirtyField = field; |
| |
| try { |
| BeanInfo beanInfo = Introspector.getBeanInfo(clazz); |
| for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { |
| if (pd.getName().equals(info.dirtyField.getName())) { |
| info.dirtyPropertyDescriptor = pd; |
| break; |
| } |
| } |
| } catch (IntrospectionException e) { |
| LOGGER.error("{}", e); |
| } |
| } |
| if (info.disposeField != null && info.dirtyField != null && info.domainKeyField != null |
| && info.domainDescriptionField != null && info.idField != null) { |
| break; |
| } |
| } |
| |
| if (info.disposeField == null || info.domainKeyField == null || info.domainDescriptionField == null |
| || info.idField == null) { |
| Class<?> superClass = clazz.getSuperclass(); |
| if (superClass != null) { |
| applyFieldInfo(superClass, info); |
| } |
| } |
| } catch (SecurityException e) { |
| LOGGER.error("{}", e); |
| } |
| } |
| |
| /** |
| * Applies all required field infos to the info object. |
| * |
| * @param clazz |
| * the clazz |
| * @param info |
| * the info |
| */ |
| private static void applyMethodInfo(Class<?> clazz, Info info) { |
| try { |
| for (Method method : clazz.getDeclaredMethods()) { |
| if (method.getAnnotation(Dispose.class) != null) { |
| info.disposeMethod = method; |
| break; |
| } |
| } |
| |
| if (info.disposeMethod == null) { |
| Class<?> superClass = clazz.getSuperclass(); |
| if (superClass != null) { |
| applyMethodInfo(superClass, info); |
| } |
| } |
| } catch (SecurityException e) { |
| LOGGER.error("{}", e); |
| } |
| } |
| |
| /** |
| * Find field. |
| * |
| * @param clazz |
| * the clazz |
| * @param name |
| * the name |
| * @return the field |
| */ |
| protected Field findField(Class<?> clazz, String name) { |
| try { |
| Field field = clazz.getDeclaredField(name); |
| return field; |
| } catch (NoSuchFieldException e) { |
| Class<?> superClass = clazz.getSuperclass(); |
| if (superClass != null) { |
| return findField(superClass, name); |
| } |
| } catch (SecurityException e) { |
| LOGGER.error("{}", e); |
| } |
| return null; |
| } |
| |
| /** |
| * The Class Info. |
| */ |
| private static class Info { |
| |
| /** The property change support field. */ |
| private Field propertyChangeSupportField; |
| |
| /** The dispose field. */ |
| private Field disposeField; |
| |
| /** The dispose method. */ |
| private Method disposeMethod; |
| |
| /** The id field. */ |
| private Field idField; |
| |
| /** The dirty field. */ |
| private Field dirtyField; |
| |
| /** The dirty property descriptor. */ |
| private PropertyDescriptor dirtyPropertyDescriptor; |
| |
| /** The domain key field. */ |
| private Field domainKeyField; |
| |
| /** The domain description field. */ |
| private Field domainDescriptionField; |
| |
| /** |
| * Gets the dispose field. |
| * |
| * @return the dispose field |
| */ |
| public Field getDisposeField() { |
| return disposeField; |
| } |
| |
| /** |
| * Gets the dirty field. |
| * |
| * @return the dirty field |
| */ |
| public Field getDirtyField() { |
| return dirtyField; |
| } |
| |
| /** |
| * Gets the dirty getter. |
| * |
| * @return the dirty getter |
| */ |
| public Method getDirtyGetter() { |
| return dirtyPropertyDescriptor != null ? dirtyPropertyDescriptor.getReadMethod() : null; |
| } |
| |
| /** |
| * Gets the dirty setter. |
| * |
| * @return the dirty setter |
| */ |
| public Method getDirtySetter() { |
| return dirtyPropertyDescriptor != null ? dirtyPropertyDescriptor.getWriteMethod() : null; |
| } |
| |
| /** |
| * Gets the dispose method. |
| * |
| * @return the dispose method |
| */ |
| public Method getDisposeMethod() { |
| return disposeMethod; |
| } |
| |
| /** |
| * Gets the domain key field. |
| * |
| * @return the domain key field |
| */ |
| public Field getDomainKeyField() { |
| return domainKeyField; |
| } |
| |
| /** |
| * Gets the domain description field. |
| * |
| * @return the domain description field |
| */ |
| public Field getDomainDescriptionField() { |
| return domainDescriptionField; |
| } |
| |
| /** |
| * Gets the property change support field. |
| * |
| * @return the property change support field |
| */ |
| public Field getPropertyChangeSupportField() { |
| return propertyChangeSupportField; |
| } |
| |
| public Field getIdField() { |
| return idField; |
| } |
| |
| } |
| |
| } |