• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aspectran / aspectran / #4075

19 Feb 2025 12:58PM CUT coverage: 35.181% (-0.004%) from 35.185%
#4075

push

github

topframe
Update

20 of 155 new or added lines in 9 files covered. (12.9%)

5 existing lines in 3 files now uncovered.

14252 of 40510 relevant lines covered (35.18%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/utils/src/main/java/com/aspectran/utils/BeanUtils.java
1
/*
2
 * Copyright (c) 2008-2025 The Aspectran Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.aspectran.utils;
17

18
import com.aspectran.utils.annotation.jsr305.NonNull;
19

20
import java.lang.reflect.InvocationTargetException;
21
import java.lang.reflect.Method;
22
import java.util.List;
23
import java.util.Map;
24
import java.util.Properties;
25
import java.util.StringTokenizer;
26

27
/**
28
 * BeanUtils provides methods that allow simple, reflective access to
29
 * JavaBeans style properties. Methods are provided for object types.
30
 *
31
 * <p>Created: 2008. 04. 22 PM 3:47:15</p>
32
 */
33
public abstract class BeanUtils {
×
34

35
    /** An empty immutable {@code Object} array. */
36
    static final Object[] NO_ARGUMENTS = new Object[0];
×
37

38
    /**
39
     * Return the value of the specified property of the specified bean,
40
     * no matter which property reference format is used, with no type
41
     * conversions.
42
     * @param bean the bean whose property is to be extracted
43
     * @param name possibly indexed and/or nested name of the property to be extracted
44
     * @return the property value (as an Object)
45
     * @throws InvocationTargetException if the property accessor method throws an exception
46
     */
47
    public static Object getProperty(Object bean, @NonNull String name) throws InvocationTargetException {
48
        Object value;
49
        if (bean instanceof Properties props) {
×
50
            value = getProperty(props, name);
×
51
            if (value != null) {
×
52
                return value;
×
53
            }
54
        }
55
        if (name.contains(".")) {
×
56
            StringTokenizer parser = new StringTokenizer(name, ".");
×
57
            value = bean;
×
58
            while (parser.hasMoreTokens()) {
×
59
                value = getSimpleProperty(value, parser.nextToken());
×
60
                if (value == null) {
×
61
                    break;
×
62
                }
63
            }
64
            return value;
×
65
        } else {
66
            return getSimpleProperty(bean, name);
×
67
        }
68
    }
69

70
    /**
71
     * Return the value of the specified property of the specified bean,
72
     * no matter which property reference format is used, with no type
73
     * conversions.
74
     * @param bean the bean whose property is to be extracted
75
     * @param name the name of the property to be extracted that does not allow nesting
76
     * @return the property value (as an Object)
77
     * @throws InvocationTargetException if the property accessor method throws an exception
78
     */
79
    public static Object getSimpleProperty(Object bean, @NonNull String name) throws InvocationTargetException {
80
        try {
81
            Object value;
82
            if (name.contains("[")) {
×
83
                value = getIndexedProperty(bean, name);
×
84
            } else if (bean instanceof Properties props) {
×
85
                value = getProperty(props, name);
×
86
            } else if (bean instanceof Map<?, ?> map) {
×
87
                value = map.get(name);
×
88
            } else {
89
                BeanDescriptor bd = BeanDescriptor.getInstance(bean.getClass());
×
90
                Method method = bd.getGetter(name);
×
91
                try {
92
                    value = method.invoke(bean, NO_ARGUMENTS);
×
93
                } catch (Throwable t) {
×
94
                    throw ExceptionUtils.unwrapThrowable(t);
×
95
                }
×
96
            }
97
            return value;
×
98
        } catch (InvocationTargetException e) {
×
99
            throw e;
×
100
        } catch (Throwable t) {
×
101
            if (bean == null) {
×
102
                throw new InvocationTargetException(t, "Could not get property '" + name +
×
103
                        "' from null reference. Cause: " + t);
104
            } else {
105
                throw new InvocationTargetException(t, "Could not get property '" + name +
×
106
                        "' from " + bean.getClass().getName() + ". Cause: " + t);
×
107
            }
108
        }
109
    }
110

111
    private static Object getProperty(@NonNull Properties props, @NonNull String name) {
112
        Object value = props.get(name);
×
113
        if (value == null) {
×
114
            // Allow for defaults fallback or potentially overridden accessor...
115
            value = props.getProperty(name);
×
116
        }
117
        return value;
×
118
    }
119

120
    /**
121
     * Sets the value of the specified property of the specified bean.
122
     * @param bean the bean whose property is to be modified
123
     * @param name possibly indexed and/or nested name of the property to be modified
124
     * @param value the value to which this property is to be set
125
     * @throws InvocationTargetException if the property accessor method throws an exception
126
     * @throws NoSuchMethodException if an accessor method for this property cannot be found
127
     */
128
    public static void setProperty(Object bean, @NonNull String name, Object value)
129
            throws InvocationTargetException, NoSuchMethodException {
130
        if (bean instanceof Properties props) {
×
131
            props.put(name, value);
×
132
        } else if (name.contains(".")) {
×
133
            StringTokenizer parser = new StringTokenizer(name, ".");
×
134
            String newName = parser.nextToken();
×
135
            Object child = bean;
×
136
            while (parser.hasMoreTokens()) {
×
NEW
137
                Class<?> type = BeanTypeUtils.getPropertyTypeForSetter(child, newName);
×
138
                Object parent = child;
×
139
                child = getSimpleProperty(parent, newName);
×
140
                if (child == null) {
×
141
                    if (value == null) {
×
142
                        return; // don't instantiate child path if value is null
×
143
                    } else {
144
                        try {
145
                            child = ClassUtils.createInstance(type);
×
146
                            setProperty(parent, newName, child);
×
147
                        } catch (Exception e) {
×
148
                            throw new InvocationTargetException(e, "Cannot set value of property '" + name
×
149
                                    + "' because '" + newName + "' is null and cannot be instantiated on instance of "
150
                                    + type.getName() + ". Cause: " + e);
×
151
                        }
×
152
                    }
153
                }
154
                newName = parser.nextToken();
×
155
            }
×
156
            setSimpleProperty(child, newName, value);
×
157
        } else {
×
158
            setSimpleProperty(bean, name, value);
×
159
        }
160
    }
×
161

162
    /**
163
     * Sets the value of the specified property of the specified bean.
164
     * @param bean the bean whose property is to be modified
165
     * @param name the name of the property to be modified that does not allow nesting
166
     * @param value the value to which this property is to be set
167
     * @throws InvocationTargetException if the property accessor method throws an exception
168
     */
169
    public static void setSimpleProperty(Object bean, @NonNull String name, Object value) throws InvocationTargetException {
170
        if (bean instanceof Properties props) {
×
171
            props.put(name, value);
×
172
            return;
×
173
        }
174
        try {
175
            if (name.contains("[")) {
×
176
                setIndexedProperty(bean, name, value);
×
177
            } else {
178
                if (bean instanceof Map<?, ?>) {
×
179
                    @SuppressWarnings("unchecked")
180
                    Map<String, Object> map = (Map<String, Object>)bean;
×
181
                    map.put(name, value);
×
182
                } else {
×
183
                    BeanDescriptor bd = BeanDescriptor.getInstance(bean.getClass());
×
184
                    Method method = bd.getSetter(name);
×
185
                    Object[] params = new Object[] { value };
×
186
                    try {
187
                        method.invoke(bean, params);
×
188
                    } catch (Throwable t) {
×
189
                        throw ExceptionUtils.unwrapThrowable(t);
×
190
                    }
×
191
                }
192
            }
193
        } catch (InvocationTargetException e) {
×
194
            throw e;
×
195
        } catch (Throwable t) {
×
196
            try {
197
                MethodUtils.invokeSetter(bean, name, value);
×
198
                return;
×
199
            } catch (Throwable tt) {
×
200
                //ignore
201
            }
202
            if (bean == null) {
×
203
                throw new InvocationTargetException(t, "Could not set property '" + name + "' to value '" +
×
204
                        value + "' for null reference. Cause: " + t);
205
            } else {
206
                throw new InvocationTargetException(t, "Could not set property '" + name + "' to value '" + value +
×
207
                        "' for " + bean.getClass().getName() + ". Cause: " + t);
×
208
            }
209
        }
×
210
    }
×
211

212
    public static Object getIndexedProperty(Object bean, @NonNull String indexedName) throws InvocationTargetException {
213
        try {
214
            String name = indexedName.substring(0, indexedName.indexOf("["));
×
215
            int index = Integer.parseInt(indexedName.substring(indexedName.indexOf("[") + 1, indexedName.indexOf("]")));
×
216
            Object obj = (!name.isEmpty() ? getSimpleProperty(bean, name) : bean);
×
217
            Object value;
218
            if (obj instanceof List<?> list) {
×
219
                value = list.get(index);
×
220
            } else if (obj instanceof Object[] arr) {
×
221
                value = arr[index];
×
222
            } else if (obj instanceof char[] arr) {
×
223
                value = arr[index];
×
224
            } else if (obj instanceof boolean[] arr) {
×
225
                value = arr[index];
×
226
            } else if (obj instanceof byte[] arr) {
×
227
                value = arr[index];
×
228
            } else if (obj instanceof double[] arr) {
×
229
                value = arr[index];
×
230
            } else if (obj instanceof float[] arr) {
×
231
                value = arr[index];
×
232
            } else if (obj instanceof int[] arr) {
×
233
                value = arr[index];
×
234
            } else if (obj instanceof long[] arr) {
×
235
                value = arr[index];
×
236
            } else if (obj instanceof short[] arr) {
×
237
                value = arr[index];
×
238
            } else {
239
                throw new IllegalArgumentException("The '" + name + "' property of the " +
×
240
                        bean.getClass().getName() + " class is not a List or Array");
×
241
            }
242
            return value;
×
243
        } catch (InvocationTargetException e) {
×
244
            throw e;
×
245
        } catch (Exception e) {
×
246
            throw new InvocationTargetException(e, "Error getting ordinal list from JavaBean. Cause: " + e);
×
247
        }
248
    }
249

250
    public static void setIndexedProperty(Object bean, @NonNull String indexedName, Object value)
251
            throws InvocationTargetException {
252
        try {
253
            String name = indexedName.substring(0, indexedName.indexOf("["));
×
254
            int index = Integer.parseInt(indexedName.substring(indexedName.indexOf("[") + 1, indexedName.indexOf("]")));
×
255
            Object obj = getSimpleProperty(bean, name);
×
256
            if (obj instanceof List<?>) {
×
257
                @SuppressWarnings("unchecked")
NEW
258
                List<Object> list = (List<Object>)obj;
×
NEW
259
                list.set(index, value);
×
260
            } else if (obj instanceof Object[] arr) {
×
261
                arr[index] = value;
×
262
            } else if (obj instanceof char[] arr) {
×
263
                arr[index] = (Character)value;
×
264
            } else if (obj instanceof boolean[] arr) {
×
265
                arr[index] = (Boolean)value;
×
266
            } else if (obj instanceof byte[] arr) {
×
267
                arr[index] = (Byte)value;
×
268
            } else if (obj instanceof double[] arr) {
×
269
                arr[index] = (Double)value;
×
270
            } else if (obj instanceof float[] arr) {
×
271
                arr[index] = (Float)value;
×
272
            } else if (obj instanceof int[] arr) {
×
273
                arr[index] = (Integer)value;
×
274
            } else if (obj instanceof long[] arr) {
×
275
                arr[index] = (Long)value;
×
276
            } else if (obj instanceof short[] arr) {
×
277
                arr[index] = (Short)value;
×
278
            } else {
279
                throw new IllegalArgumentException("The '" + name + "' property of the " +
×
280
                        bean.getClass().getName() + " class is not a List or Array");
×
281
            }
282
        } catch (InvocationTargetException e) {
×
283
            throw e;
×
284
        } catch (Exception e) {
×
285
            throw new InvocationTargetException(e, "Error getting ordinal value from JavaBean. Cause: " + e);
×
286
        }
×
287
    }
×
288

289
    /**
290
     * Checks to see if a bean has a readable property be a given name.
291
     * @param bean the bean to check
292
     * @param name the property name to check for
293
     * @return true if the property exists and is readable
294
     * @throws NoSuchMethodException if an accessor method for this property cannot be found
295
     */
296
    public static boolean hasReadableProperty(Object bean, @NonNull String name) throws NoSuchMethodException {
297
        boolean exists = false;
×
298
        if (bean instanceof Map<?, ?>) {
×
299
            exists = true; // ((Map)bean).containsKey(propertyName);
×
300
        } else {
301
            if (name.contains(".")) {
×
302
                StringTokenizer parser = new StringTokenizer(name, ".");
×
303
                Class<?> type = bean.getClass();
×
304
                while (parser.hasMoreTokens()) {
×
305
                    name = parser.nextToken();
×
306
                    type = BeanDescriptor.getInstance(type).getGetterType(name);
×
307
                    exists = BeanDescriptor.getInstance(type).hasReadableProperty(name);
×
308
                }
309
            } else {
×
310
                exists = BeanDescriptor.getInstance(bean.getClass()).hasReadableProperty(name);
×
311
            }
312
        }
313
        return exists;
×
314
    }
315

316
    /**
317
     * Checks to see if a bean has a writable property be a given name.
318
     * @param bean the bean to check
319
     * @param name the property name to check for
320
     * @return true if the property exists and is writable
321
     * @throws NoSuchMethodException if an accessor method for this property cannot be found
322
     */
323
    public static boolean hasWritableProperty(Object bean, @NonNull String name) throws NoSuchMethodException {
324
        boolean exists = false;
×
325
        if (bean instanceof Map<?, ?>) {
×
326
            exists = true; // ((Map)bean).containsKey(propertyName);
×
327
        } else {
328
            if (name.contains(".")) {
×
329
                StringTokenizer parser = new StringTokenizer(name, ".");
×
330
                Class<?> type = bean.getClass();
×
331
                while (parser.hasMoreTokens()) {
×
332
                    name = parser.nextToken();
×
333
                    type = BeanDescriptor.getInstance(type).getGetterType(name);
×
334
                    exists = BeanDescriptor.getInstance(type).hasWritableProperty(name);
×
335
                }
336
            } else {
×
337
                exists = BeanDescriptor.getInstance(bean.getClass()).hasWritableProperty(name);
×
338
            }
339
        }
340
        return exists;
×
341
    }
342

343
    /**
344
     * Returns an array of the readable properties exposed by a bean.
345
     * @param bean the bean
346
     * @return the readable properties
347
     */
348
    public static String[] getReadablePropertyNames(@NonNull Object bean) {
349
        return BeanDescriptor.getInstance(bean.getClass()).getReadablePropertyNames();
×
350
    }
351

352
    /**
353
     * Returns an array of readable properties exposed by the bean,
354
     * except those specified by NonSerializable.
355
     * @param bean the bean
356
     * @return the readable properties without non-serializable
357
     */
358
    public static String[] getReadablePropertyNamesWithoutNonSerializable(@NonNull Object bean) {
359
        return BeanDescriptor.getInstance(bean.getClass()).getReadablePropertyNamesWithoutNonSerializable();
×
360
    }
361

362
    /**
363
     * Returns an array of the writable properties exposed by a bean.
364
     * @param bean the bean
365
     * @return the properties
366
     */
367
    public static String[] getWritablePropertyNames(@NonNull Object bean) {
368
        return BeanDescriptor.getInstance(bean.getClass()).getWritablePropertyNames();
×
369
    }
370

371
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc