-
Notifications
You must be signed in to change notification settings - Fork 224
Expand file tree
/
Copy pathBeanPropertyValueChangeConsumer.java
More file actions
236 lines (219 loc) · 10.4 KB
/
BeanPropertyValueChangeConsumer.java
File metadata and controls
236 lines (219 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.beanutils2;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Consumer;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* <p>{@code Closure} that sets a property.</p>
* <p>
* An implementation of {@code java.util.function.Consumer} that updates
* a specified property on the object provided with a specified value.
* The {@code BeanPropertyValueChangeClosure} constructor takes two parameters which determine
* what property will be updated and with what value.
* <dl>
* <dt>
* {@code public BeanPropertyValueChangeClosure( String propertyName, Object propertyValue )}
* </dt>
* <dd>
* Will create a {@code Closure} that will update an object by setting the property
* specified by {@code propertyName</code> to the value specified by <code>propertyValue}.
* </dd>
* </dl>
*
* <p>
* <strong>Note:</strong> Property names can be a simple, nested, indexed, or mapped property as defined by
* {@code org.apache.commons.beanutils2.PropertyUtils}. If any object in the property path
* specified by {@code propertyName</code> is <code>null} then the outcome is based on the
* value of the {@code ignoreNull} attribute.
* </p>
* <p>
* A typical usage might look like:
* </p>
* <pre><code>
* // create the closure
* BeanPropertyValueChangeClosure closure =
* new BeanPropertyValueChangeClosure( "activeEmployee", Boolean.TRUE );
*
* // update the Collection
* CollectionUtils.forAllDo( peopleCollection, closure );
* </code></pre>
*
* This would take a {@code Collection} of person objects and update the
* {@code activeEmployee</code> property of each object in the <code>Collection} to
* {@code true}. Assuming...
* <ul>
* <li>
* The top level object in the {@code peopleCollection} is an object which represents a
* person.
* </li>
* <li>
* The person object has a {@code setActiveEmployee( boolean )} method which updates
* the value for the object's {@code activeEmployee} property.
* </li>
* </ul>
*
* @param <T> The type of the input to the operation
* @param <V> The property value type.
*
* @see org.apache.commons.beanutils2.PropertyUtils
* @see java.util.function.Consumer
*/
public class BeanPropertyValueChangeConsumer<T, V> implements Consumer<T> {
/** For logging. Each subclass gets its own log instance. */
private final Logger log = LogManager.getLogger(this.getClass());
/**
* The name of the property which will be updated when this {@code Closure} executes.
*/
private final String propertyName;
/**
* The value that the property specified by {@code propertyName}
* will be updated to when this {@code Closure} executes.
*/
private final V propertyValue;
/**
* Determines whether {@code null} objects in the property path will generate an
* {@code IllegalArgumentException</code> or not. If set to <code>true} then if any objects
* in the property path leading up to the target property evaluate to {@code null} then the
* {@code IllegalArgumentException</code> throw by <code>PropertyUtils} will be logged but
* not re-thrown. If set to {@code false} then if any objects in the property path leading
* up to the target property evaluate to {@code null} then the
* {@code IllegalArgumentException</code> throw by <code>PropertyUtils} will be logged and
* re-thrown.
*/
private final boolean ignoreNull;
/**
* Constructor which takes the name of the property to be changed, the new value to set
* the property to, and assumes {@code ignoreNull</code> to be <code>false}.
*
* @param propertyName The name of the property that will be updated with the value specified by
* {@code propertyValue}.
* @param propertyValue The value that {@code propertyName} will be set to on the target
* object.
* @throws IllegalArgumentException If the propertyName provided is null or empty.
*/
public BeanPropertyValueChangeConsumer(final String propertyName, final V propertyValue) {
this(propertyName, propertyValue, false);
}
/**
* Constructor which takes the name of the property to be changed, the new value to set
* the property to and a boolean which determines whether {@code null} objects in the
* property path will generate an {@code IllegalArgumentException} or not.
*
* @param propertyName The name of the property that will be updated with the value specified by
* {@code propertyValue}.
* @param propertyValue The value that {@code propertyName} will be set to on the target
* object.
* @param ignoreNull Determines whether {@code null} objects in the property path will
* generate an {@code IllegalArgumentException} or not.
* @throws IllegalArgumentException If the propertyName provided is null or empty.
*/
public BeanPropertyValueChangeConsumer(final String propertyName, final V propertyValue, final boolean ignoreNull) {
if (propertyName == null || propertyName.isEmpty()) {
throw new IllegalArgumentException("propertyName cannot be null or empty");
}
this.propertyName = propertyName;
this.propertyValue = propertyValue;
this.ignoreNull = ignoreNull;
}
/**
* Updates the target object provided using the property update criteria provided when this
* {@code BeanPropertyValueChangeClosure} was constructed. If any object in the property
* path leading up to the target property is {@code null} then the outcome will be based on
* the value of the {@code ignoreNull</code> attribute. By default, <code>ignoreNull} is
* {@code false</code> and would result in an <code>IllegalArgumentException} if an object
* in the property path leading up to the target property is {@code null}.
*
* @param object The object to be updated.
* @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or
* NoSuchMethodException is thrown when trying to access the property specified on the object
* provided. Or if an object in the property path provided is {@code null} and
* {@code ignoreNull</code> is set to <code>false}.
*/
@Override
public void accept(final Object object) {
try {
PropertyUtils.setProperty(object, propertyName, propertyValue);
} catch (final IllegalArgumentException e) {
final String errorMsg = "Unable to execute Closure. Null value encountered in property path...";
if (!ignoreNull) {
final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
if (!BeanUtils.initCause(iae, e)) {
log.error(errorMsg, e);
}
throw iae;
}
log.warn(errorMsg, e);
} catch (final IllegalAccessException e) {
final String errorMsg = "Unable to access the property provided.";
final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
if (!BeanUtils.initCause(iae, e)) {
log.error(errorMsg, e);
}
throw iae;
} catch (final InvocationTargetException e) {
final String errorMsg = "Exception occurred in property's getter";
final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
if (!BeanUtils.initCause(iae, e)) {
log.error(errorMsg, e);
}
throw iae;
} catch (final NoSuchMethodException e) {
final String errorMsg = "Property not found";
final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
if (!BeanUtils.initCause(iae, e)) {
log.error(errorMsg, e);
}
throw iae;
}
}
/**
* Returns the name of the property which will be updated when this {@code Closure} executes.
*
* @return The name of the property which will be updated when this {@code Closure} executes.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Returns the value that the property specified by {@code propertyName}
* will be updated to when this {@code Closure} executes.
*
* @return The value that the property specified by {@code propertyName}
* will be updated to when this {@code Closure} executes.
*/
public V getPropertyValue() {
return propertyValue;
}
/**
* Returns the flag that determines whether {@code null} objects in the property path will
* generate an {@code IllegalArgumentException</code> or not. If set to <code>true} then
* if any objects in the property path leading up to the target property evaluate to
* {@code null</code> then the <code>IllegalArgumentException} throw by
* {@code PropertyUtils</code> will be logged but not re-thrown. If set to <code>false} then
* if any objects in the property path leading up to the target property evaluate to
* {@code null</code> then the <code>IllegalArgumentException} throw by
* {@code PropertyUtils} will be logged and re-thrown.
*
* @return The flag that determines whether {@code null} objects in the property path will
* generate an {@code IllegalArgumentException} or not.
*/
public boolean isIgnoreNull() {
return ignoreNull;
}
}