001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.tools.property;
017
018import static java.util.Objects.requireNonNull;
019
020import java.beans.PropertyDescriptor;
021
022import de.cuioss.tools.lang.MoreObjects;
023import de.cuioss.tools.logging.CuiLogger;
024import de.cuioss.tools.reflect.MoreReflection;
025import lombok.Getter;
026import lombok.RequiredArgsConstructor;
027
028/**
029 * Defines the read write permissions for a given property. It is defined for
030 * properties java Bean properties.
031 *
032 * @author Oliver Wolff
033 */
034@RequiredArgsConstructor
035public enum PropertyReadWrite {
036
037    /** The corresponding property is read only. */
038    READ_ONLY(true, false),
039    /** The property can be read and written to. */
040    READ_WRITE(true, true),
041    /** The property can only be written to. */
042    WRITE_ONLY(false, true),
043    /** The property can neither be read nor written to. */
044    NONE(false, false);
045
046    private static final CuiLogger log = new CuiLogger(PropertyReadWrite.class);
047
048    @Getter
049    private final boolean readable;
050
051    @Getter
052    private final boolean writeable;
053
054    /**
055     * Resolves {@link PropertyReadWrite} for a given property with
056     * {@link MoreReflection}
057     *
058     * @param beanType     to be checked, must not be null
059     * @param propertyName to be checked, must not be null
060     * @return the corresponding {@link PropertyReadWrite} for a given property
061     */
062    public static PropertyReadWrite resolveForBean(final Class<?> beanType, final String propertyName) {
063        final var readable = MoreReflection.retrieveAccessMethod(beanType, propertyName).isPresent();
064        final var writeable = !MoreReflection.retrieveWriteMethodCandidates(beanType, propertyName).isEmpty();
065        if (readable && writeable) {
066            return READ_WRITE;
067        }
068        if (readable) {
069            return READ_ONLY;
070        }
071        if (writeable) {
072            return WRITE_ONLY;
073        }
074        return NONE;
075    }
076
077    /**
078     * Resolves {@link PropertyReadWrite} form the given {@link PropertyDescriptor}.
079     * If this provides unclear result it will call
080     * {@link #resolveForBean(Class, String)}
081     *
082     * @param descriptor   to be read from
083     * @param beanType     to be checked, must not be null
084     * @param propertyName to be checked, must not be null
085     * @return the corresponding {@link PropertyReadWrite} for a given property
086     */
087    public static PropertyReadWrite fromPropertyDescriptor(PropertyDescriptor descriptor, final Class<?> beanType,
088            final String propertyName) {
089        requireNonNull(descriptor);
090        if (MoreObjects.allNonNull(descriptor.getReadMethod(), descriptor.getWriteMethod())) {
091            return READ_WRITE;
092        }
093        log.debug(
094                "PropertyDescriptor '%s' does not describe a standard bean-structure for property '%s' on type '%s', switching to reflection",
095                descriptor, propertyName, beanType);
096        return resolveForBean(beanType, propertyName);
097    }
098}