Java tutorial
/** * Copyright 2014 Tomas Rodriguez * * 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.github.talberto.easybeans.atg; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import atg.adapter.gsa.EnumPropertyDescriptor; import atg.adapter.gsa.EnumPropertyDescriptor.EnumeratedOption; import atg.beans.DynamicPropertyDescriptor; import atg.repository.MutableRepositoryItem; import atg.repository.RepositoryItem; import atg.repository.RepositoryItemDescriptor; import com.github.talberto.easybeans.api.MappingException; import com.github.talberto.easybeans.api.RepositoryEnumCode; import com.github.talberto.easybeans.api.RepositoryEnumValue; import com.github.talberto.easybeans.api.RepositoryProperty; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * A PropertyMapper handles the mapping between a given property from a Bean to a RepositoryItem and the other way around. * * @author Tomas Rodriguez (rodriguez@progiweb.com) * */ public abstract class PropertyMapper { protected final static Logger sLog = LoggerFactory.getLogger(BeanMapper.class); protected Logger mLog = LoggerFactory.getLogger(this.getClass()); protected Method mReader; protected Method mWriter; protected PropertyDescriptor mBeanPropertyDescriptor; protected DynamicPropertyDescriptor mRepositoryPropertyDescriptor; protected NucleusEntityManager mEntityManager; public static PropertyMapper create(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, RepositoryItemDescriptor pItemDescriptor) { sLog.debug("Extracting information from property [{}]", pBeanPropertyDescriptor.getName()); Method reader = pBeanPropertyDescriptor.getReadMethod(); Method writer = pBeanPropertyDescriptor.getWriteMethod(); checkArgument(reader != null || writer != null, "Neither reader nor writer for property [{}]", pBeanPropertyDescriptor.getName()); RepositoryProperty readerAnnotation = reader != null ? reader.getAnnotation(RepositoryProperty.class) : null; RepositoryProperty writerAnnotation = writer != null ? writer.getAnnotation(RepositoryProperty.class) : null; checkArgument(readerAnnotation != null || writerAnnotation != null, "Neither reader nor writer are annotated with @RepositoryProperty for property {}", pBeanPropertyDescriptor.getName()); checkArgument(!(readerAnnotation != null && writerAnnotation != null), "Both reader and writer are annotated with @RepositoryProperty for property {}", pBeanPropertyDescriptor.getName()); boolean configureWithReader = readerAnnotation == null ? false : true; Method methodToUse = configureWithReader ? reader : writer; RepositoryProperty propertyAnnotation = configureWithReader ? readerAnnotation : writerAnnotation; if (configureWithReader) { sLog.debug("Configuring property using reader method"); } else { sLog.debug("Configuring property using writer method"); } propertyAnnotation = methodToUse.getAnnotation(RepositoryProperty.class); // Extract the repository property descriptor DynamicPropertyDescriptor repositoryPropertyDescriptor = pItemDescriptor .getPropertyDescriptor(propertyAnnotation.propertyName()); checkNotNull(repositoryPropertyDescriptor, "The repository property descriptor for the property [%s] is null", pBeanPropertyDescriptor.getName()); sLog.debug("Property configured: [propertyName=[{}]]", repositoryPropertyDescriptor.getName()); if (repositoryPropertyDescriptor instanceof EnumPropertyDescriptor) { return new EnumPropertyMapper(pEntityManager, pBeanPropertyDescriptor, repositoryPropertyDescriptor); } else { PropertyGetter getter = PropertyGetter.create(pEntityManager, pBeanPropertyDescriptor, repositoryPropertyDescriptor); PropertySetter setter = PropertySetter.create(pEntityManager, pBeanPropertyDescriptor, repositoryPropertyDescriptor); PropertyDeleter deleter = PropertyDeleter.create(pEntityManager, pBeanPropertyDescriptor, repositoryPropertyDescriptor); return new RegularPropertyMapper(pEntityManager, pBeanPropertyDescriptor, repositoryPropertyDescriptor, getter, setter, deleter); } } static abstract class PropertyGetter { protected NucleusEntityManager mEntityManager; protected Class<?> mType; public static PropertyGetter create(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, DynamicPropertyDescriptor pItemPropertyDescriptor) { Class<?> repositoryClass = pItemPropertyDescriptor.getPropertyType(); Class<?> repositoryComponentClass = pItemPropertyDescriptor.getComponentPropertyType(); Class<?> beanClass = pBeanPropertyDescriptor.getPropertyType(); Class<?> beanComponentClass = null; if (Collection.class.isAssignableFrom(beanClass)) { // User getter or setter to obtain the component's type if (pBeanPropertyDescriptor.getReadMethod() != null) { Type returnType = pBeanPropertyDescriptor.getReadMethod().getGenericReturnType(); ParameterizedType paramType = (ParameterizedType) returnType; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[0]; } else { Type[] types = pBeanPropertyDescriptor.getWriteMethod().getGenericParameterTypes(); ParameterizedType paramType = (ParameterizedType) types[0]; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[0]; } } else if (Map.class.isAssignableFrom(beanClass)) { // User getter or setter to obtain the component's type if (pBeanPropertyDescriptor.getReadMethod() != null) { Type returnType = pBeanPropertyDescriptor.getReadMethod().getGenericReturnType(); ParameterizedType paramType = (ParameterizedType) returnType; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[1]; } else { Type[] types = pBeanPropertyDescriptor.getWriteMethod().getGenericParameterTypes(); ParameterizedType paramType = (ParameterizedType) types[0]; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[1]; } } sLog.debug( "Creating PropertyGetter. repositoryClass = [{}], repositoryComponentClass = [{}], beanClass = [{}], beanComponentClass = [{}]", repositoryClass, repositoryComponentClass, beanClass, beanComponentClass); if (Collection.class.isAssignableFrom(repositoryClass)) { if (repositoryComponentClass.equals(RepositoryItem.class)) { return CollectionPropertyGetterDecorator.decorate(repositoryClass, new RepositoryItemPropertyGetter(pEntityManager, beanComponentClass)); } else { return CollectionPropertyGetterDecorator.decorate(repositoryClass, new SimplePropertyGetter(pEntityManager, beanComponentClass)); } } else if (Map.class.isAssignableFrom(repositoryClass)) { if (repositoryComponentClass.equals(RepositoryItem.class)) { return MapPropertyGetterDecorator .decorate(new RepositoryItemPropertyGetter(pEntityManager, beanComponentClass)); } else { return MapPropertyGetterDecorator .decorate(new SimplePropertyGetter(pEntityManager, beanComponentClass)); } } else if (RepositoryItem.class.isAssignableFrom(repositoryClass)) { return new RepositoryItemPropertyGetter(pEntityManager, beanClass); } else { return new SimplePropertyGetter(pEntityManager, beanClass); } } protected PropertyGetter(NucleusEntityManager pEntityManager, Class<?> pType) { mEntityManager = pEntityManager; mType = pType; } public Object extractValue(RepositoryItem pItem, String pPropertyName) { return processValue(pItem.getPropertyValue(pPropertyName), mType); } protected abstract Object processValue(Object pPropertyValue, Class<?> pType); } static class SimplePropertyGetter extends PropertyGetter { public SimplePropertyGetter(NucleusEntityManager pEntityManager, Class<?> pType) { super(pEntityManager, pType); } @Override protected Object processValue(Object pPropertyValue, Class<?> pType) { return pType.cast(pPropertyValue); } } static class RepositoryItemPropertyGetter extends PropertyGetter { public RepositoryItemPropertyGetter(NucleusEntityManager pEntityManager, Class<?> pType) { super(pEntityManager, pType); } @Override protected Object processValue(Object pPropertyValue, Class<?> pType) { RepositoryItem item = (RepositoryItem) pPropertyValue; return mEntityManager.toBean(item, pType); } } static class CollectionPropertyGetterDecorator extends PropertyGetter { protected final Function<Object, Object> mTransformer = new Function<Object, Object>() { @Override public Object apply(Object pInput) { return mDecorated.processValue(pInput, mType); } }; protected PropertyGetter mDecorated; protected CollectionCreator mCollectionCreator; public static PropertyGetter decorate(Class<?> pCollectionType, PropertyGetter pDecorated) { sLog.debug("Decorating PropertyGetter [{}] with collection type [{}]", pDecorated, pCollectionType); return new CollectionPropertyGetterDecorator(pDecorated, CollectionCreator.forCollection(pCollectionType)); } protected CollectionPropertyGetterDecorator(PropertyGetter pDecorated, CollectionCreator pCollectionCreator) { super(pDecorated.mEntityManager, pDecorated.mType); mDecorated = pDecorated; mCollectionCreator = pCollectionCreator; } @Override protected Object processValue(Object pPropertyValue, Class<?> pType) { @SuppressWarnings("unchecked") Collection<Object> originalCollection = (Collection<Object>) pPropertyValue; Collection<Object> targetCollection = Collections2.transform(originalCollection, mTransformer); return mCollectionCreator.create(targetCollection); } } static class MapPropertyGetterDecorator extends PropertyGetter { protected final Function<Object, Object> mTransformer = new Function<Object, Object>() { @Override public Object apply(Object pInput) { return mDecorated.processValue(pInput, mType); } }; protected PropertyGetter mDecorated; public static PropertyGetter decorate(PropertyGetter pDecorated) { sLog.debug("Decorating PropertyGetter [{}]", pDecorated); return new MapPropertyGetterDecorator(pDecorated); } protected MapPropertyGetterDecorator(PropertyGetter pDecorated) { super(pDecorated.mEntityManager, pDecorated.mType); mDecorated = pDecorated; } @Override protected Object processValue(Object pPropertyValue, Class<?> pType) { @SuppressWarnings("unchecked") Map<String, Object> originalMap = (Map<String, Object>) pPropertyValue; Map<String, Object> transformedMap = Maps.transformValues(originalMap, mTransformer); return Maps.newHashMap(transformedMap); } } static abstract class CollectionCreator { public static Map<Class<?>, CollectionCreator> CREATOR_FOR_COLLECTION = ImmutableMap .<Class<?>, CollectionCreator>builder().put(Set.class, SetCreator.instance) .put(List.class, ListCreator.instance).build(); public static CollectionCreator forCollection(Class<?> pCollectionClass) { CollectionCreator creator = CREATOR_FOR_COLLECTION.get(pCollectionClass); checkNotNull(creator, "CollectionCreator for collection [%s] is null", pCollectionClass); return creator; } public abstract Collection<?> create(); public abstract Collection<?> create(Collection<?> pElements); } static class SetCreator extends CollectionCreator { public static CollectionCreator instance = new SetCreator(); private SetCreator() { } @Override public Collection<?> create() { return Sets.newHashSet(); } @Override public Collection<?> create(Collection<?> pElements) { return Sets.newHashSet(pElements); } } static class ListCreator extends CollectionCreator { public static CollectionCreator instance = new ListCreator(); private ListCreator() { } @Override public Collection<?> create() { return Lists.newArrayList(); } @Override public Collection<?> create(Collection<?> pElements) { return Lists.newArrayList(pElements); } } static abstract class PropertySetter { protected NucleusEntityManager mEntityManager; public static PropertySetter create(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, DynamicPropertyDescriptor pItemPropertyDescriptor) { Class<?> repositoryClass = pItemPropertyDescriptor.getPropertyType(); Class<?> repositoryComponentClass = pItemPropertyDescriptor.getComponentPropertyType(); Class<?> beanClass = pBeanPropertyDescriptor.getPropertyType(); Class<?> beanComponentClass = null; if (!pItemPropertyDescriptor.isWritable()) { return new NoOpPropertySetter(); } if (Collection.class.isAssignableFrom(beanClass)) { // User getter or setter to obtain the component's type if (pBeanPropertyDescriptor.getReadMethod() != null) { Type returnType = pBeanPropertyDescriptor.getReadMethod().getGenericReturnType(); ParameterizedType paramType = (ParameterizedType) returnType; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[0]; } else { Type[] types = pBeanPropertyDescriptor.getWriteMethod().getGenericParameterTypes(); ParameterizedType paramType = (ParameterizedType) types[0]; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[0]; } } else if (Map.class.isAssignableFrom(beanClass)) { // User getter or setter to obtain the component's type if (pBeanPropertyDescriptor.getReadMethod() != null) { Type returnType = pBeanPropertyDescriptor.getReadMethod().getGenericReturnType(); ParameterizedType paramType = (ParameterizedType) returnType; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[1]; } else { Type[] types = pBeanPropertyDescriptor.getWriteMethod().getGenericParameterTypes(); ParameterizedType paramType = (ParameterizedType) types[0]; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[1]; } } sLog.debug( "Creating PropertySetter. repositoryClass = [{}], repositoryComponentClass = [{}], beanClass = [{}], beanComponentClass = [{}]", repositoryClass, repositoryComponentClass, beanClass, beanComponentClass); if (Collection.class.isAssignableFrom(repositoryClass)) { if (repositoryComponentClass.equals(RepositoryItem.class)) { return CollectionPropertySetterDecorator.decorate(repositoryClass, new BeanPropertySetter(pEntityManager)); } else { return CollectionPropertySetterDecorator.decorate(repositoryClass, new SimplePropertySetter(pEntityManager)); } } else if (Map.class.isAssignableFrom(repositoryClass)) { if (repositoryComponentClass.equals(RepositoryItem.class)) { return MapPropertySetterDecorator.decorate(new BeanPropertySetter(pEntityManager)); } else { return MapPropertySetterDecorator.decorate(new SimplePropertySetter(pEntityManager)); } } else if (RepositoryItem.class.isAssignableFrom(repositoryClass)) { return new BeanPropertySetter(pEntityManager); } else { return new SimplePropertySetter(pEntityManager); } } public PropertySetter(NucleusEntityManager pEntityManager) { mEntityManager = pEntityManager; } public void setItemProperty(MutableRepositoryItem pItem, String pPropertyName, Object pPropertyValue) { pItem.setPropertyValue(pPropertyName, processValue(pPropertyValue)); } public abstract Object processValue(Object pPropertyValue); } static class NoOpPropertySetter extends PropertySetter { public NoOpPropertySetter() { super(null); } @Override public void setItemProperty(MutableRepositoryItem pItem, String pPropertyName, Object pPropertyValue) { } @Override public Object processValue(Object pPropertyValue) { return null; } } static class SimplePropertySetter extends PropertySetter { public SimplePropertySetter(NucleusEntityManager pEntityManager) { super(pEntityManager); } @Override public Object processValue(Object pPropertyValue) { return pPropertyValue; } } static class BeanPropertySetter extends PropertySetter { public BeanPropertySetter(NucleusEntityManager pEntityManager) { super(pEntityManager); } @Override public Object processValue(Object pBean) { @SuppressWarnings("unchecked") BeanMapper<Object> beanMapper = (BeanMapper<Object>) mEntityManager.findMapperFor(pBean.getClass()); String beanId = beanMapper.getBeanId(pBean); if (beanId == null) { // Create new repository item beanId = mEntityManager.create(pBean); beanMapper.setBeanId(pBean, beanId); } else { mEntityManager.update(pBean); } return mEntityManager.repositoryItemForBean(pBean); } } static class CollectionPropertySetterDecorator extends PropertySetter { protected final Function<Object, Object> sTransformPropertyValue = new Function<Object, Object>() { @Override public Object apply(Object pPropertyValue) { return mDecorated.processValue(pPropertyValue); } }; protected PropertySetter mDecorated; protected CollectionCreator mCollectionCreator; public static PropertySetter decorate(Class<?> pCollectionType, PropertySetter pDecorated) { sLog.debug("Decorating PropertyGetter [{}] with collection type [{}]", pDecorated, pCollectionType); return new CollectionPropertySetterDecorator(pDecorated, CollectionCreator.forCollection(pCollectionType)); } protected CollectionPropertySetterDecorator(PropertySetter pDecorated, CollectionCreator pCollectionCreator) { super(pDecorated.mEntityManager); mDecorated = pDecorated; mCollectionCreator = pCollectionCreator; } @Override public Object processValue(Object pPropertyValue) { @SuppressWarnings("unchecked") Collection<Object> beanCollection = (Collection<Object>) pPropertyValue; Collection<Object> transformedCollection = Collections2.transform(beanCollection, sTransformPropertyValue); return mCollectionCreator.create(transformedCollection); } } static class MapPropertySetterDecorator extends PropertySetter { protected final Function<Object, Object> mTransformPropertyValue = new Function<Object, Object>() { @Override public Object apply(Object pPropertyValue) { return mDecorated.processValue(pPropertyValue); } }; protected PropertySetter mDecorated; protected CollectionCreator mCollectionCreator; public static PropertySetter decorate(PropertySetter pDecorated) { sLog.debug("Decorating PropertyGetter [{}]", pDecorated); return new MapPropertySetterDecorator(pDecorated); } protected MapPropertySetterDecorator(PropertySetter pDecorated) { super(pDecorated.mEntityManager); mDecorated = pDecorated; } @Override public Object processValue(Object pPropertyValue) { @SuppressWarnings("unchecked") Map<String, Object> originalMap = (Map<String, Object>) pPropertyValue; Map<String, Object> transformedMap = Maps.transformValues(originalMap, mTransformPropertyValue); return Maps.newHashMap(transformedMap); } } static abstract class PropertyDeleter { protected NucleusEntityManager mEntityManager; public static PropertyDeleter create(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, DynamicPropertyDescriptor pItemPropertyDescriptor) { Class<?> repositoryClass = pItemPropertyDescriptor.getPropertyType(); Class<?> repositoryComponentClass = pItemPropertyDescriptor.getComponentPropertyType(); Class<?> beanClass = pBeanPropertyDescriptor.getPropertyType(); Class<?> beanComponentClass = null; if (Collection.class.isAssignableFrom(beanClass)) { // User getter or setter to obtain the component's type if (pBeanPropertyDescriptor.getReadMethod() != null) { Type returnType = pBeanPropertyDescriptor.getReadMethod().getGenericReturnType(); ParameterizedType paramType = (ParameterizedType) returnType; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[0]; } else { Type[] types = pBeanPropertyDescriptor.getWriteMethod().getGenericParameterTypes(); ParameterizedType paramType = (ParameterizedType) types[0]; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[0]; } } else if (Map.class.isAssignableFrom(beanClass)) { // User getter or setter to obtain the component's type if (pBeanPropertyDescriptor.getReadMethod() != null) { Type returnType = pBeanPropertyDescriptor.getReadMethod().getGenericReturnType(); ParameterizedType paramType = (ParameterizedType) returnType; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[1]; } else { Type[] types = pBeanPropertyDescriptor.getWriteMethod().getGenericParameterTypes(); ParameterizedType paramType = (ParameterizedType) types[0]; beanComponentClass = (Class<?>) paramType.getActualTypeArguments()[1]; } } sLog.debug( "Creating PropertyDeleter. repositoryClass = [{}], repositoryComponentClass = [{}], beanClass = [{}], beanComponentClass = [{}]", repositoryClass, repositoryComponentClass, beanClass, beanComponentClass); if (Collection.class.isAssignableFrom(repositoryClass)) { if (repositoryComponentClass.equals(RepositoryItem.class)) { return CollectionPropertyDeleterDecorator.decorate(repositoryClass, new BeanPropertyDeleter(pEntityManager)); } else { return new NoOpPropertyDeleter(pEntityManager); } } else if (Map.class.isAssignableFrom(repositoryClass)) { if (repositoryComponentClass.equals(RepositoryItem.class)) { return MapPropertyDeleterDecorator.decorate(new BeanPropertyDeleter(pEntityManager)); } else { return new NoOpPropertyDeleter(pEntityManager); } } else if (RepositoryItem.class.isAssignableFrom(repositoryClass)) { return new BeanPropertyDeleter(pEntityManager); } else { return new NoOpPropertyDeleter(pEntityManager); } } protected PropertyDeleter(NucleusEntityManager pEntityManager) { mEntityManager = pEntityManager; } public abstract void delete(Object pPropertyValue); } static class NoOpPropertyDeleter extends PropertyDeleter { protected NoOpPropertyDeleter(NucleusEntityManager pEntityManager) { super(pEntityManager); } @Override public void delete(Object pPropertyValue) { } } static class BeanPropertyDeleter extends PropertyDeleter { protected BeanPropertyDeleter(NucleusEntityManager pEntityManager) { super(pEntityManager); } @Override public void delete(Object pBean) { mEntityManager.delete(pBean, true); } } static class CollectionPropertyDeleterDecorator extends PropertyDeleter { protected PropertyDeleter mDecorated; protected CollectionCreator mCollectionCreator; public static PropertyDeleter decorate(Class<?> pCollectionType, PropertyDeleter pDecorated) { sLog.debug("Decorating PropertyDeleter [{}] with collection type [{}]", pDecorated, pCollectionType); return new CollectionPropertyDeleterDecorator(pDecorated, CollectionCreator.forCollection(pCollectionType)); } protected CollectionPropertyDeleterDecorator(PropertyDeleter pDecorated, CollectionCreator pCollectionCreator) { super(pDecorated.mEntityManager); mDecorated = pDecorated; mCollectionCreator = pCollectionCreator; } @Override public void delete(Object pPropertyValue) { @SuppressWarnings("unchecked") Collection<Object> beanCollection = (Collection<Object>) pPropertyValue; for (Object bean : beanCollection) { mDecorated.delete(bean); } } } static class MapPropertyDeleterDecorator extends PropertyDeleter { protected PropertyDeleter mDecorated; protected CollectionCreator mCollectionCreator; public static PropertyDeleter decorate(PropertyDeleter pDecorated) { sLog.debug("Decorating PropertyDeleter [{}]", pDecorated); return new MapPropertyDeleterDecorator(pDecorated); } protected MapPropertyDeleterDecorator(PropertyDeleter pDecorated) { super(pDecorated.mEntityManager); mDecorated = pDecorated; } @Override public void delete(Object pPropertyValue) { @SuppressWarnings("unchecked") Map<String, Object> originalMap = (Map<String, Object>) pPropertyValue; for (Object bean : originalMap.values()) { mDecorated.delete(bean); } } } protected PropertyMapper(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, DynamicPropertyDescriptor pRepositoryPropertyDescriptor) { mEntityManager = pEntityManager; mBeanPropertyDescriptor = pBeanPropertyDescriptor; mRepositoryPropertyDescriptor = pRepositoryPropertyDescriptor; mReader = mBeanPropertyDescriptor.getReadMethod(); mWriter = mBeanPropertyDescriptor.getWriteMethod(); } /** * @return the repositoryPropertyName */ public String getRepositoryPropertyName() { return mRepositoryPropertyDescriptor.getName(); } public String getBeanPropertyName() { return mBeanPropertyDescriptor.getName(); } public void setBeanProperty(Object pBean, Object pPropertyValue) { try { mWriter.invoke(pBean, pPropertyValue); } catch (Exception e) { throw new MappingException(String.format("Couldn't set bean property [%s] with value [%s]", mBeanPropertyDescriptor.getName(), pPropertyValue), e); } } public Object getBeanProperty(Object pBean) { try { return mReader.invoke(pBean); } catch (Exception e) { throw new MappingException( String.format("Couldn't get bean property [%s]", mBeanPropertyDescriptor.getName()), e); } } public Class<?> getPropertyBeanType() { return mBeanPropertyDescriptor.getPropertyType(); } /** * @return the repositoryPropertyDescriptor */ public DynamicPropertyDescriptor getRepositoryPropertyDescriptor() { return mRepositoryPropertyDescriptor; } public abstract void removeProperty(Object pBean); public abstract void updateRepositoryItemProperty(MutableRepositoryItem pItem, Object pBean); public abstract Object mapRepositoryProperty(RepositoryItem pItem); } class EnumPropertyMapper extends PropertyMapper { protected final static Predicate<Method> sIsCodeGetterCandidate = new Predicate<Method>() { @Override public boolean apply(Method pMethod) { return pMethod.isAnnotationPresent(RepositoryEnumCode.class) && // Check it has the corresponding annotation !Modifier.isStatic(pMethod.getModifiers()); // Check it isn't static } }; protected final static Predicate<Method> sIsValueGetterCandidate = new Predicate<Method>() { @Override public boolean apply(Method pMethod) { // Check it has the corresponding annotation return pMethod.isAnnotationPresent(RepositoryEnumValue.class) && // Check it has the corresponding annotation !Modifier.isStatic(pMethod.getModifiers()); // Check it isn't static } }; protected final static Predicate<Method> sIsFromCodeCandidate = new Predicate<Method>() { @Override public boolean apply(Method pMethod) { // Check it has the corresponding annotation return pMethod.isAnnotationPresent(RepositoryEnumCode.class) && // Check it has the corresponding annotation Modifier.isStatic(pMethod.getModifiers()); // Check it is static; } }; protected final static Predicate<Method> sIsFromValueCandidate = new Predicate<Method>() { @Override public boolean apply(Method pMethod) { // Check it has the corresponding annotation return pMethod.isAnnotationPresent(RepositoryEnumValue.class) && // Check it has the corresponding annotation Modifier.isStatic(pMethod.getModifiers()); // Check it is static } }; protected final static Function<EnumeratedOption, RepositoryEnumItem> sEnumOptToRepoEnumItem = new Function<EnumeratedOption, RepositoryEnumItem>() { @Override public RepositoryEnumItem apply(EnumeratedOption pInput) { return new RepositoryEnumItem(pInput.getCode(), pInput.getValue()); } }; protected final Method mCodeGetter; protected final Method mValueGetter; protected final Method mFromCode; protected final Method mFromValue; protected EnumPropertyDescriptor mEnumRepositoryType; protected EnumPropertyMapper(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, DynamicPropertyDescriptor pRepositoryPropertyDescriptor) { super(pEntityManager, pBeanPropertyDescriptor, pRepositoryPropertyDescriptor); checkArgument(pRepositoryPropertyDescriptor instanceof EnumPropertyDescriptor, "The DynamycPropertyDescriptor isn't an instance of EnumPropertyDescriptor"); mEnumRepositoryType = (EnumPropertyDescriptor) pRepositoryPropertyDescriptor; @SuppressWarnings("unchecked") Class<? extends Enum<?>> enumBeanType = (Class<? extends Enum<?>>) pBeanPropertyDescriptor .getPropertyType(); checkArgument(enumBeanType.isEnum(), "The bean property [%s] isn't of type enum", pBeanPropertyDescriptor.getName()); // Extract the methods or fail fast mCodeGetter = getCodeGetterMethod(enumBeanType); mValueGetter = getValueGetterMethod(enumBeanType); mFromCode = getFromCodeMethod(enumBeanType); mFromValue = getFromValueMethod(enumBeanType); // Check that the two enumerations are compatible List<EnumeratedOption> repoEnumOptions = Arrays.asList(mEnumRepositoryType.getEnumeratedOptions()); List<? extends Enum<?>> beanEnumConstants = Arrays.asList(enumBeanType.getEnumConstants()); List<RepositoryEnumItem> repoEnumItems = RepositoryEnumItem.ORDERING .immutableSortedCopy((Lists.transform(repoEnumOptions, sEnumOptToRepoEnumItem))); List<RepositoryEnumItem> beanEnumItems = RepositoryEnumItem.ORDERING.immutableSortedCopy( (Lists.transform(beanEnumConstants, new Function<Enum<?>, RepositoryEnumItem>() { @Override public RepositoryEnumItem apply(Enum<?> pInput) { try { int code = (Integer) mCodeGetter.invoke(pInput); String value = (String) mValueGetter.invoke(pInput); return new RepositoryEnumItem(code, value); } catch (Exception e) { throw new MappingException(String.format("Error transforming enum [%s]", pInput), e); } } }))); if (!Iterables.elementsEqual(repoEnumItems, beanEnumItems)) { throw new MappingException("The repository enumeration and the bean enumeration are not compatible"); } } private Method getFromValueMethod(Class<? extends Enum<?>> pEnumBeanType) { Collection<Method> methods = Arrays.asList(pEnumBeanType.getMethods()); Collection<Method> fromValueCandidates = Collections2.filter(methods, sIsFromValueCandidate); if (fromValueCandidates.isEmpty()) { throw new MappingException("Couldn't find any class method annotated with @RepositoryEnumValue"); } else if (fromValueCandidates.size() > 1) { throw new MappingException("Found more than one class method annotated with @RepositoryEnumValue"); } Method fromValueCandidate = Iterables.getOnlyElement(fromValueCandidates); // Check that the method has the correct signature if (!(fromValueCandidate.getParameterTypes().length == 1 && fromValueCandidate.getParameterTypes()[0].equals(String.class) && fromValueCandidate.getReturnType().equals(pEnumBeanType))) { throw new MappingException( String.format("The method [%s] doesn't have the expected signature", fromValueCandidate)); } return fromValueCandidate; } private Method getFromCodeMethod(Class<? extends Enum<?>> pEnumBeanType) { Collection<Method> methods = Arrays.asList(pEnumBeanType.getMethods()); Collection<Method> fromCodeCandidates = Collections2.filter(methods, sIsFromCodeCandidate); if (fromCodeCandidates.isEmpty()) { throw new MappingException("Couldn't find any class method annotated with @RepositoryEnumCode"); } else if (fromCodeCandidates.size() > 1) { throw new MappingException("Found more than one class method annotated with @RepositoryEnumCode"); } Method fromCodeCandidate = Iterables.getOnlyElement(fromCodeCandidates); // Check that the method has the correct signature if (!(fromCodeCandidate.getParameterTypes().length == 1 && fromCodeCandidate.getParameterTypes()[0].equals(Integer.class) && fromCodeCandidate.getReturnType().equals(pEnumBeanType))) { throw new MappingException( String.format("The method [%s] doesn't have the expected signature", fromCodeCandidate)); } return fromCodeCandidate; } private Method getValueGetterMethod(Class<? extends Enum<?>> pEnumBeanType) { Collection<Method> methods = Arrays.asList(pEnumBeanType.getMethods()); Collection<Method> valueGetterCandidates = Collections2.filter(methods, sIsValueGetterCandidate); if (valueGetterCandidates.isEmpty()) { throw new MappingException("Couldn't find any instance method annotated with @RepositoryEnumValue"); } else if (valueGetterCandidates.size() > 1) { throw new MappingException("Found more than one instance method annotated with @RepositoryEnumValue"); } Method valueGetterCandidate = Iterables.getOnlyElement(valueGetterCandidates); // Check that the method has the correct signature if (!(valueGetterCandidate.getParameterTypes().length == 0 && valueGetterCandidate.getReturnType().equals(String.class))) { throw new MappingException( String.format("The method [%s] doesn't have the expected signature", valueGetterCandidate)); } return valueGetterCandidate; } private Method getCodeGetterMethod(Class<? extends Enum<?>> pEnumBeanType) { Collection<Method> methods = Arrays.asList(pEnumBeanType.getMethods()); Collection<Method> codeGetterCandidates = Collections2.filter(methods, sIsCodeGetterCandidate); if (codeGetterCandidates.isEmpty()) { throw new MappingException("Couldn't find any instance method annotated with @RepositoryEnumCode"); } else if (codeGetterCandidates.size() > 1) { throw new MappingException("Found more than one instance method annotated with @RepositoryEnumCode"); } Method codeGetterCandidate = Iterables.getOnlyElement(codeGetterCandidates); // Check that the method has the correct signature if (!(codeGetterCandidate.getParameterTypes().length == 0 && codeGetterCandidate.getReturnType().equals(Integer.class))) { throw new MappingException( String.format("The method [%s] doesn't have the expected signature", codeGetterCandidate)); } return codeGetterCandidate; } /* (non-Javadoc) * @see com.github.talberto.easybeans.atg.PropertyMapper#removeProperty(java.lang.Object) */ @Override public void removeProperty(Object pBean) { } /* (non-Javadoc) * @see com.github.talberto.easybeans.atg.PropertyMapper#updateRepositoryItemProperty(atg.repository.MutableRepositoryItem, java.lang.Object) */ @Override public void updateRepositoryItemProperty(MutableRepositoryItem pItem, Object pBean) { Object enumItem = getBeanProperty(pBean); try { Object enumCodeOrValue; if (mEnumRepositoryType.getUseCodeForValue()) { enumCodeOrValue = mCodeGetter.invoke(enumItem); } else { enumCodeOrValue = mValueGetter.invoke(enumItem); } pItem.setPropertyValue(getRepositoryPropertyName(), enumCodeOrValue); } catch (Exception e) { throw new MappingException(String.format("Error extracting and converting [%s] enum", enumItem), e); } } /* (non-Javadoc) * @see com.github.talberto.easybeans.atg.PropertyMapper#mapRepositoryProperty(atg.repository.RepositoryItem) */ @Override public Object mapRepositoryProperty(RepositoryItem pItem) { Object propertyValue = pItem.getPropertyValue(getRepositoryPropertyName()); try { Object enumItem; if (mEnumRepositoryType.getUseCodeForValue()) { enumItem = mFromCode.invoke(null, propertyValue); } else { enumItem = mFromValue.invoke(null, propertyValue); } return mBeanPropertyDescriptor.getPropertyType().cast(enumItem); } catch (Exception e) { throw new MappingException(String.format("Error extracting and converting [%s] enum", propertyValue), e); } } } class RegularPropertyMapper extends PropertyMapper { protected PropertyGetter mGetter; protected PropertySetter mSetter; protected PropertyDeleter mDeleter; protected RegularPropertyMapper(NucleusEntityManager pEntityManager, PropertyDescriptor pBeanPropertyDescriptor, DynamicPropertyDescriptor pRepositoryPropertyDescriptor, PropertyGetter pPropertyGetter, PropertySetter pPropertySetter, PropertyDeleter pPropertyDeleter) { super(pEntityManager, pBeanPropertyDescriptor, pRepositoryPropertyDescriptor); mGetter = pPropertyGetter; mSetter = pPropertySetter; mDeleter = pPropertyDeleter; } /** * Maps a RepositoryItem property to a bean property * * @param pItem * @return */ @Override public Object mapRepositoryProperty(RepositoryItem pItem) { return mGetter.extractValue(pItem, getRepositoryPropertyName()); } @Override public void updateRepositoryItemProperty(MutableRepositoryItem pItem, Object pBean) { Object beanPropertyValue = getBeanProperty(pBean); mSetter.setItemProperty(pItem, getRepositoryPropertyName(), beanPropertyValue); } @Override public void removeProperty(Object pBean) { Object beanPropertyValue = getBeanProperty(pBean); mDeleter.delete(beanPropertyValue); } }