Project

General

Profile

Download (4.71 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
public class EntityCollectionSetterAdapter<CDM extends CdmBase, T extends CdmBase> implements Serializable {
43

    
44
    private static final long serialVersionUID = -996721690408449555L;
45

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

    
48
    private Class<T> propertyItemType;
49

    
50
    private Class<CDM> beanClass;
51

    
52
    private String propertyName;
53
    private String addMethodName;
54

    
55
    private String removMethodName;
56

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

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

    
68
        this.beanClass = beanClass;
69
        this.propertyItemType = propertyItemType;
70

    
71
        this.propertyName = propertyName;
72
        this.addMethodName = addMethodName;
73
        this.removMethodName = removMethodName;
74
    }
75

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

    
78
        Method getterMethod = null;
79
        Method addMethod;
80
        Method removeMethod;
81

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

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

    
113
    public static class SetterAdapterException extends Exception {
114

    
115
        private static final long serialVersionUID = 6011462992846535903L;
116

    
117
        public SetterAdapterException(String message, Throwable cause) {
118
            super(message, cause);
119
        }
120
    }
121

    
122
    public void setAddMethodName(String addMethodName) {
123
        this.addMethodName = addMethodName;
124
    }
125

    
126
    public void setRemovMethodName(String removMethodName) {
127
        this.removMethodName = removMethodName;
128
    }
129

    
130

    
131
}
(5-5/10)