Project

General

Profile

Download (4.52 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2018 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.model;
10

    
11
import java.io.Serializable;
12
import java.lang.reflect.InvocationTargetException;
13
import java.lang.reflect.Method;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.List;
17

    
18
import org.apache.commons.lang3.StringUtils;
19

    
20
import eu.etaxonomy.cdm.model.common.CdmBase;
21

    
22
/**
23
 * In many cases the add*() and remove*() methods of entity classes contain important business
24
 * logic which would be short-circuited if the set*() method would be public. This adapter allows
25
 * providing setter methods to make the bean property writable.
26
 * <p>
27
 * The {{@link #setCollection(CdmBase, Collection)} method uses the add*() and remove*() methods
28
 * in order to update the collection field of the bean.
29
 * <p>
30
 * Usage example:
31
 * <pre>
32
 *   public void setTeamMembers(List<Person> teamMembers) throws SetterAdapterException {
33
 *      new EntityCollectionSetterAdapter<Team, Person>(Team.class, Person.class, "teamMembers").setCollection(this, teamMembers);
34
 *   }
35
 * </pre>
36
 *
37
 * see https://dev.e-taxonomy.eu/redmine/issues/7600
38
 *
39
 * @author a.kohlbecker
40
 * @since Nov 15, 2018
41
 *
42
 */
43
public class EntityCollectionSetterAdapter<CDM extends CdmBase, T extends CdmBase> implements Serializable {
44

    
45
    private static final long serialVersionUID = 1L;
46

    
47
    private static final String GETTER_PREFIX = "get";
48

    
49
    private Class<T> propertyItemType;
50

    
51
    private Class<CDM> beanClass;
52

    
53
    private String propertyName;
54
    private String addMethodName;
55
    private String removMethodName;
56

    
57

    
58

    
59
    public EntityCollectionSetterAdapter(Class<CDM> beanClass, Class<T> propertyItemType, String propertyName){
60
        this(beanClass,
61
             propertyItemType,
62
             propertyName,
63
             "add" + StringUtils.capitalize(propertyName.substring(0, propertyName.length() - 1)),
64
             "remove" + StringUtils.capitalize(propertyName.substring(0, propertyName.length() - 1))
65
             );
66
    }
67

    
68
    public EntityCollectionSetterAdapter(Class<CDM> beanClass, Class<T> propertyItemType, String propertyName, String addMethodName, String removMethodName){
69

    
70
        this.beanClass = beanClass;
71
        this.propertyItemType = propertyItemType;
72

    
73
        this.propertyName = propertyName;
74
        this.addMethodName = addMethodName;
75
        this.removMethodName = removMethodName;
76
    }
77

    
78
    public void setCollection(CDM bean, Collection<T> items) throws SetterAdapterException {
79

    
80
        Method getterMethod = null;
81
        Method addMethod;
82
        Method removeMethod;
83

    
84
        try{
85
            getterMethod = beanClass.getDeclaredMethod(GETTER_PREFIX + StringUtils.capitalize(propertyName));
86
            addMethod = beanClass.getDeclaredMethod(addMethodName, propertyItemType);
87
            removeMethod = beanClass.getDeclaredMethod(removMethodName, propertyItemType);
88

    
89
            Collection<T> getterCollection = (Collection<T>) getterMethod.invoke(bean);
90
            List<T> currentItems = new ArrayList<>(getterCollection);
91
            List<T> itemsSeen = new ArrayList<>();
92
            for(T a : items){
93
                if(a == null){
94
                    continue;
95
                }
96
                if(!currentItems.contains(a)){
97
                    addMethod.invoke(bean, a);
98
                }
99
                itemsSeen.add(a);
100
            }
101
            for(T a : currentItems){
102
                if(!itemsSeen.contains(a)){
103
                    removeMethod.invoke(bean, a);
104
                }
105
        }
106
        } catch(ClassCastException e){
107
            throw new SetterAdapterException("getter return type (" + (getterMethod != null ? getterMethod.getReturnType() : "NULL") + ") incompatible with expected  property type " + propertyItemType, e);
108
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
109
            throw new SetterAdapterException("error invoking method", e);
110
        } catch (NoSuchMethodException | SecurityException e) {
111
            throw new SetterAdapterException("Property related method not found", e);
112
        }
113
    }
114

    
115
    public static class SetterAdapterException extends Exception {
116

    
117
        private static final long serialVersionUID = 1L;
118

    
119
        /**
120
         * @param message
121
         * @param cause
122
         */
123
        public SetterAdapterException(String message, Throwable cause) {
124
            super(message, cause);
125
        }
126
    }
127

    
128
}
(5-5/10)