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