Project

General

Profile

Download (4.75 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 = 1L;
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

    
56
    private String removMethodName;
57

    
58

    
59

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

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

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

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

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

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

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

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

    
116
    public static class SetterAdapterException extends Exception {
117

    
118
        private static final long serialVersionUID = 1L;
119

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

    
129
    public void setAddMethodName(String addMethodName) {
130
        this.addMethodName = addMethodName;
131
    }
132

    
133
    public void setRemovMethodName(String removMethodName) {
134
        this.removMethodName = removMethodName;
135
    }
136

    
137

    
138
}
(5-5/10)