001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.lang3.builder;
019
020import java.lang.reflect.AccessibleObject;
021import java.lang.reflect.Field;
022import java.util.Set;
023import java.util.function.Supplier;
024
025import org.apache.commons.lang3.SystemProperties;
026import org.apache.commons.lang3.tuple.Pair;
027
028/**
029 * Abstracts reflection access for reflection-based classes in this package.
030 * <p>
031 * See {@link AbstractBuilder#setForceAccessible(boolean)} for details.
032 * </p>
033 *
034 * @since 3.21.0
035 * @see AbstractBuilder#setForceAccessible(boolean)
036 * @see AccessibleObject#setAccessible(boolean)
037 */
038public abstract class AbstractReflection {
039
040    /**
041     * Builds an instance of a subclass of {@link AbstractReflection}.
042     *
043     * @param <B> An AbstractBuilder subclass.
044     */
045    public abstract static class AbstractBuilder<B extends AbstractBuilder<B>> implements Supplier<AbstractReflection> {
046
047        /**
048         * Whether the {@link AbstractReflection} subclass will call {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)} on
049         * inaccessible fields.
050         */
051        private boolean forceAccessible = getForceAccessible();
052
053        /**
054         * Constructs a new instance for a subclass.
055         */
056        AbstractBuilder() {
057            // Empty.
058        }
059
060        /**
061         * Returns {@code this} instance typed as its subclass.
062         *
063         * @return {@code this} instance typed as its subclass.
064         */
065        @SuppressWarnings("unchecked")
066        protected B asThis() {
067            return (B) this;
068        }
069
070        /**
071         * Sets whether inaccessible fields are made accessible by calling {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
072         * <p>
073         * In general, controls whether the instances built by this builder will force the accessible flag for reflection.
074         * </p>
075         * <p>
076         * Defaults to {@code getForceAccessible()}, which defaults to true for compatibility.
077         * </p>
078         * <p>
079         * This default is read from the system property {@code "AbstractReflection.forceAccessible"}, which defaults to true for compatibility.
080         * </p>
081         * <p>
082         * The parsing rules are defined by {@link Boolean#parseBoolean(String)}.
083         * </p>
084         * <p>
085         * See subclasses for specific behavior.
086         * </p>
087         *
088         * @param forceAccessible Whether to force accessibility by calling {@link AccessibleObject#setAccessible(boolean)
089         *                        AccessibleObject#setAccessible(true)}.
090         * @return {@code this} instance.
091         * @see AccessibleObject#setAccessible(boolean)
092         */
093        public B setForceAccessible(final boolean forceAccessible) {
094            this.forceAccessible = forceAccessible;
095            return asThis();
096        }
097    }
098
099    /**
100     * Gets whether the system property {@code "AbstractReflection.forceAccessible"} is set to true.
101     * <p>
102     * The parsing rules are defined by {@link Boolean#parseBoolean(String)}.
103     * </p>
104     * <p>
105     * If the property is not set, return true.
106     * </p>
107     *
108     * @return whether the system property {@code "AbstractReflection.forceAccessible"} is set to true with true as the default.
109     * @see Boolean#parseBoolean(String)
110     * @since 3.21.0
111     */
112    public static boolean getForceAccessible() {
113        return SystemProperties.getBoolean(AbstractReflection.class, "forceAccessible", () -> true);
114    }
115
116    static boolean isRegistered(final Object lhs, final Object rhs, final Set<Pair<IDKey, IDKey>> registry) {
117        final Pair<IDKey, IDKey> pair = toRegisterPair(lhs, rhs);
118        final Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getRight(), pair.getLeft());
119        return registry != null && (registry.contains(pair) || registry.contains(swappedPair));
120    }
121
122    static void register(final Object lhs, final Object rhs, final Set<Pair<IDKey, IDKey>> registry) {
123        registry.add(toRegisterPair(lhs, rhs));
124    }
125
126    /**
127     * Sets {@code accessibleObject} to be accessible if {@code forceAccessible} is true and the object is not already accessible. Calls
128     * {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
129     *
130     * @param forceAccessible  Whether to call {@link AccessibleObject#setAccessible(boolean)} if the object is not already accessible.
131     * @param accessibleObject The accessible object to set; may be {@code null}.
132     * @return {@code true} if {@code accessibleObject} is non-null and accessible after this call; {@code false} otherwise (including when
133     *         {@code accessibleObject} is {@code null}, or when it is inaccessible and {@code forceAccessible} is {@code false}).
134     * @throws SecurityException Thrown if {@code forceAccessible} is true and the request is denied.
135     * @see AccessibleObject#setAccessible(boolean)
136     * @see SecurityManager#checkPermission
137     */
138    public static boolean setAccessible(final boolean forceAccessible, final AccessibleObject accessibleObject) {
139        return accessibleObject != null && (accessibleObject.isAccessible() || forceAccessible && setAccessibleTrue(accessibleObject));
140    }
141
142    /**
143     * Sets the accessible object as accessible by calling {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
144     * <p>
145     * Callers must ensure {@code accessibleObject} is non-null before calling this method.
146     * </p>
147     *
148     * @param accessibleObject The accessible object to set; must be non-null.
149     * @return {@code true} if {@code accessibleObject} is accessible after this call; {@code false} otherwise.
150     * @throws SecurityException Thrown if the request is denied.
151     * @see AccessibleObject#setAccessible(boolean)
152     * @see SecurityManager#checkPermission
153     */
154    private static boolean setAccessibleTrue(final AccessibleObject accessibleObject) {
155        // Test isAccessible() to avoid the permission check.
156        if (!accessibleObject.isAccessible()) {
157            accessibleObject.setAccessible(true);
158        }
159        return accessibleObject.isAccessible();
160    }
161
162    /**
163     * Converters value pair into a register pair.
164     *
165     * @param lhs {@code this} object.
166     * @param rhs The other object.
167     * @return The pair.
168     */
169    static Pair<IDKey, IDKey> toRegisterPair(final Object lhs, final Object rhs) {
170        return Pair.of(new IDKey(lhs), new IDKey(rhs));
171    }
172
173    static void unregister(final Object lhs, final Object rhs, final Set<Pair<IDKey, IDKey>> registry, final ThreadLocal<Set<Pair<IDKey, IDKey>>> registryTL) {
174        registry.remove(toRegisterPair(lhs, rhs));
175        if (registry.isEmpty()) {
176            registryTL.remove();
177        }
178    }
179
180    /**
181     * Whether to call {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)} on inaccessible fields.
182     */
183    private final boolean forceAccessible;
184
185    /**
186     * Constructs a new instance.
187     *
188     * @param <T>     The type to build.
189     * @param builder The builder.
190     */
191    <T extends AbstractBuilder<T>> AbstractReflection(final AbstractBuilder<T> builder) {
192        this.forceAccessible = builder.forceAccessible;
193    }
194
195    /**
196     * Tests whether fields should be made accessible with {@link AccessibleObject#setAccessible(boolean)}.
197     *
198     * @return whether fields should be made accessible with {@link AccessibleObject#setAccessible(boolean)}.
199     */
200    protected boolean isForceAccessible() {
201        return forceAccessible;
202    }
203
204    /**
205     * Sets the field to be accessible if {@code forceAccessible} is true and the field is not already accessible. Calls
206     * {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
207     *
208     * @param field The field to set; may be {@code null}.
209     * @return {@code true} if {@code field} is non-null and accessible after this call; {@code false} otherwise.
210     * @throws SecurityException Thrown if {@code forceAccessible} flag is true and the request is denied.
211     * @see AccessibleObject#setAccessible(boolean)
212     * @see SecurityManager#checkPermission
213     */
214    boolean setAccessible(final Field field) {
215        return setAccessible(isForceAccessible(), field);
216    }
217}