001// Generated by delombok at Sat Feb 25 17:58:34 UTC 2023 002package io.cui.tools.reflect; 003 004import java.lang.reflect.Field; 005import java.lang.reflect.Member; 006import java.util.Optional; 007import io.cui.tools.lang.SecuritySupport; 008import io.cui.tools.logging.CuiLogger; 009import io.cui.tools.string.MoreStrings; 010import lombok.NonNull; 011// owolff: The warning is "Reflection should not be used to 012// increase accessibility of classes, methods, or fields " 013// What is actually the use-case of this type, therefore there 014// is nothing we can do 015/** 016 * Wrapper around a {@link Field} that handles implicitly the accessible flag for access. 017 * 018 * @author Oliver Wolff 019 */ 020@SuppressWarnings("java:S3011") 021public class FieldWrapper { 022 private static final CuiLogger log = new CuiLogger(FieldWrapper.class); 023 @NonNull 024 private final Field field; 025 private final Class<?> declaringClass; 026 027 /** 028 * @param field must not be null 029 */ 030 public FieldWrapper(Field field) { 031 this.field = field; 032 this.declaringClass = ((Member) field).getDeclaringClass(); 033 } 034 035 /** 036 * Reads from the field determined by {@link #getField()}. It implicitly sets and resets the 037 * {@link Field#isAccessible()} flag. 038 * 039 * @param object to be read from 040 * @return an {@link Optional} on the given field value if applicable. May return 041 * {@link Optional#empty()} for cases where: 042 * <ul> 043 * <li>Field value is {@code null}</li> 044 * <li>Given Object is {@code null}</li> 045 * <li>Given Object is improper type</li> 046 * <li>an {@link IllegalAccessException} occurred while accessing</li> 047 * </ul> 048 */ 049 public Optional<Object> readValue(Object object) { 050 if (null == object) { 051 log.trace("No Object given, returning Optional#empty()"); 052 return Optional.empty(); 053 } 054 if (!declaringClass.isAssignableFrom(object.getClass())) { 055 log.trace("Given Object is improper type, returning Optional#empty()"); 056 return Optional.empty(); 057 } 058 var initialAccessible = field.canAccess(object); 059 log.trace("Reading from field \'{}\' with accessibleFlag=\'{}\' ", field, initialAccessible); 060 synchronized (field) { 061 if (!initialAccessible) { 062 log.trace("Explicitly setting accessible flag"); 063 SecuritySupport.setAccessible(field, true); 064 } 065 try { 066 return Optional.ofNullable(field.get(object)); 067 } catch (IllegalArgumentException | IllegalAccessException e) { 068 log.warn(e, "Reading from field \'{}\' with accessible=\'{}\' and parameter =\'{}\' could not complete", field, initialAccessible, object); 069 return Optional.empty(); 070 } finally { 071 if (!initialAccessible) { 072 log.trace("Resetting accessible flag"); 073 SecuritySupport.setAccessible(field, false); 074 } 075 } 076 } 077 } 078 079 /** 080 * Reads the value from the field in the given object. It implicitly sets and resets the 081 * {@link Field#isAccessible()} flag. 082 * 083 * @param fieldName to be read 084 * @param object to be read from 085 * 086 * @return the field value. {@link Optional#empty()} if the field cannot be read. 087 */ 088 public static final Optional<Object> readValue(final String fieldName, final Object object) { 089 final var fieldProvider = from(object.getClass(), fieldName); 090 log.trace("FieldWrapper: {}", fieldProvider); 091 if (fieldProvider.isPresent()) { 092 var fieldWrapper = fieldProvider.get(); 093 return fieldWrapper.readValue(object); 094 } 095 return Optional.empty(); 096 } 097 098 /** 099 * Writes to the field determined by {@link #getField()}. It implicitly sets and resets the 100 * {@link Field#isAccessible()} flag. 101 * 102 * @param object to be written to, must not be null 103 * @param value to be written, may be null 104 * 105 * @throws NullPointerException in case object is {@code null} 106 * @throws IllegalArgumentException in case the value is not applicable to the field 107 * @throws IllegalStateException wrapping an {@link IllegalAccessException} 108 */ 109 public void writeValue(@NonNull Object object, Object value) { 110 if (object == null) { 111 throw new java.lang.NullPointerException("object is marked non-null but is null"); 112 } 113 var initialAccessible = field.canAccess(object); 114 log.trace("Writing to field \'{}\' with accessibleFlag=\'{}\' ", field, initialAccessible); 115 synchronized (field) { 116 if (!initialAccessible) { 117 log.trace("Explicitly setting accessible flag"); 118 SecuritySupport.setAccessible(field, true); 119 } 120 try { 121 field.set(object, value); 122 } catch (IllegalAccessException e) { 123 var message = MoreStrings.lenientFormat("Writing to field \'{}\' with accessible=\'{}\' and parameter =\'{}\' could not complete", field, initialAccessible, object); 124 throw new IllegalStateException(message, e); 125 } finally { 126 if (!initialAccessible) { 127 log.trace("Resetting accessible flag"); 128 SecuritySupport.setAccessible(field, false); 129 } 130 } 131 } 132 } 133 134 /** 135 * Factory Method for creating an {@link FieldWrapper} instance 136 * 137 * @param type must not be null 138 * @param fieldName must not be null 139 * @return a {@link FieldWrapper} if the {@link Field} can be determined, 140 * {@link Optional#empty()} otherwise 141 */ 142 public static final Optional<FieldWrapper> from(final Class<?> type, final String fieldName) { 143 var loaded = MoreReflection.accessField(type, fieldName); 144 if (loaded.isPresent()) { 145 return Optional.of(new FieldWrapper(loaded.get())); 146 } 147 return Optional.empty(); 148 } 149 150 @NonNull 151 @java.lang.SuppressWarnings("all") 152 @lombok.Generated 153 public Field getField() { 154 return this.field; 155 } 156}