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 */
017package org.apache.juneau.commons.reflect;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.ThrowableUtils.*;
021
022import java.lang.annotation.*;
023import java.lang.reflect.Modifier;
024
025/**
026 * Abstract base class for all reflection wrapper objects providing common modifier checking functionality.
027 *
028 * <p>
029 * This class provides the foundation for all reflection info wrappers (classes, methods, fields, constructors, etc.)
030 * by providing common functionality for checking Java language modifiers and element flags. Subclasses extend this
031 * to provide specific functionality for their element type.
032 *
033 * <h5 class='section'>Features:</h5>
034 * <ul class='spaced-list'>
035 *    <li>Modifier checking - check for public, private, protected, static, final, etc.
036 *    <li>Flag checking - check for element flags using {@link ElementFlag}
037 *    <li>Combined flag checking - check for multiple flags at once
038 *    <li>Extensible - subclasses can add element-specific flag checks
039 * </ul>
040 *
041 * <h5 class='section'>Use Cases:</h5>
042 * <ul class='spaced-list'>
043 *    <li>Checking modifiers on reflection elements
044 *    <li>Filtering elements by flags
045 *    <li>Building frameworks that need to analyze element characteristics
046 * </ul>
047 *
048 * <h5 class='section'>Usage:</h5>
049 * <p class='bjava'>
050 *    <jc>// Check modifiers</jc>
051 *    ElementInfo <jv>ei</jv> = ...;
052 *    <jk>boolean</jk> <jv>isPublic</jv> = <jv>ei</jv>.isPublic();
053 *    <jk>boolean</jk> <jv>isStatic</jv> = <jv>ei</jv>.isStatic();
054 *
055 *    <jc>// Check flags</jc>
056 *    <jk>boolean</jk> <jv>hasFlag</jv> = <jv>ei</jv>.hasFlag(ElementFlag.PUBLIC);
057 *    <jk>boolean</jk> <jv>hasAllFlags</jv> = <jv>ei</jv>.hasAllFlags(ElementFlag.PUBLIC, ElementFlag.STATIC);
058 * </p>
059 *
060 * <h5 class='section'>See Also:</h5><ul>
061 *    <li class='jc'>{@link ElementFlag} - Element flags enumeration
062 *    <li class='jc'>{@link ClassInfo} - Class introspection
063 *    <li class='jc'>{@link MethodInfo} - Method introspection
064 *    <li class='jc'>{@link FieldInfo} - Field introspection
065 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
066 * </ul>
067 */
068public abstract class ElementInfo {
069
070   private final int modifiers;
071
072   /**
073    * Constructor.
074    *
075    * @param modifiers The Java modifiers for this element.
076    */
077   protected ElementInfo(int modifiers) {
078      this.modifiers = modifiers;
079   }
080
081   /**
082    * Returns the Java language modifiers for this element.
083    *
084    * @return The Java language modifiers for this element.
085    */
086   public int getModifiers() { return modifiers; }
087
088   /**
089    * Returns <jk>true</jk> if the specified flag is applicable to this element.
090    *
091    * <p>
092    * Subclasses should override this method and call {@code super.is(flag)} to handle common modifier flags,
093    * then handle their own specific flags.
094    *
095    * @param flag The flag to test for.
096    * @return <jk>true</jk> if the specified flag is applicable to this element.
097    */
098   public boolean is(ElementFlag flag) {
099      return switch (flag) {
100         case PUBLIC -> isPublic();
101         case NOT_PUBLIC -> isNotPublic();
102         case PRIVATE -> isPrivate();
103         case NOT_PRIVATE -> isNotPrivate();
104         case PROTECTED -> isProtected();
105         case NOT_PROTECTED -> isNotProtected();
106         case STATIC -> isStatic();
107         case NOT_STATIC -> isNotStatic();
108         case FINAL -> isFinal();
109         case NOT_FINAL -> isNotFinal();
110         case SYNCHRONIZED -> isSynchronized();
111         case NOT_SYNCHRONIZED -> isNotSynchronized();
112         case VOLATILE -> isVolatile();
113         case NOT_VOLATILE -> isNotVolatile();
114         case TRANSIENT -> isTransient();
115         case NOT_TRANSIENT -> isNotTransient();
116         case NATIVE -> isNative();
117         case NOT_NATIVE -> isNotNative();
118         case INTERFACE -> isInterface();
119         case ABSTRACT -> isAbstract();
120         case NOT_ABSTRACT -> isNotAbstract();
121         default -> throw rex("Invalid flag for element: {0}", flag);
122      };
123   }
124
125   /**
126    * Returns <jk>true</jk> if this element is abstract.
127    *
128    * @return <jk>true</jk> if this element is abstract.
129    */
130   public boolean isAbstract() { return Modifier.isAbstract(modifiers); }
131
132   /**
133    * Returns <jk>true</jk> if all specified flags are applicable to this element.
134    *
135    * <p>
136    * Subclasses should override this method and call {@code super.isAll(flags)} to handle common modifier flags,
137    * then handle their own specific flags.
138    *
139    * @param flags The flags to test for.
140    * @return <jk>true</jk> if all specified flags are applicable to this element.
141    */
142   public boolean isAll(ElementFlag...flags) {
143      return stream(flags).allMatch(this::is);
144   }
145
146   /**
147    * Returns <jk>true</jk> if any of the specified flags are applicable to this element.
148    *
149    * <p>
150    * Subclasses should override this method and call {@code super.isAny(flags)} to handle common modifier flags,
151    * then handle their own specific flags.
152    *
153    * @param flags The flags to test for.
154    * @return <jk>true</jk> if any of the specified flags are applicable to this element.
155    */
156   public boolean isAny(ElementFlag...flags) {
157      return stream(flags).anyMatch(this::is);
158   }
159
160   /**
161    * Returns <jk>true</jk> if this element is final.
162    *
163    * @return <jk>true</jk> if this element is final.
164    */
165   public boolean isFinal() { return Modifier.isFinal(modifiers); }
166
167   /**
168    * Returns <jk>true</jk> if this element is an interface.
169    *
170    * @return <jk>true</jk> if this element is an interface.
171    */
172   public boolean isInterface() { return Modifier.isInterface(modifiers); }
173
174   /**
175    * Returns <jk>true</jk> if this element is native.
176    *
177    * @return <jk>true</jk> if this element is native.
178    */
179   public boolean isNative() { return Modifier.isNative(modifiers); }
180
181   /**
182    * Returns <jk>true</jk> if this element is not abstract.
183    *
184    * @return <jk>true</jk> if this element is not abstract.
185    */
186   public boolean isNotAbstract() { return ! Modifier.isAbstract(modifiers); }
187
188   /**
189    * Returns <jk>true</jk> if this element is not final.
190    *
191    * @return <jk>true</jk> if this element is not final.
192    */
193   public boolean isNotFinal() { return ! Modifier.isFinal(modifiers); }
194
195   /**
196    * Returns <jk>true</jk> if this element is not an interface.
197    *
198    * @return <jk>true</jk> if this element is not an interface.
199    */
200   public boolean isNotInterface() { return ! Modifier.isInterface(modifiers); }
201
202   /**
203    * Returns <jk>true</jk> if this element is not native.
204    *
205    * @return <jk>true</jk> if this element is not native.
206    */
207   public boolean isNotNative() { return ! Modifier.isNative(modifiers); }
208
209   /**
210    * Returns <jk>true</jk> if this element is not private.
211    *
212    * @return <jk>true</jk> if this element is not private.
213    */
214   public boolean isNotPrivate() { return ! Modifier.isPrivate(modifiers); }
215
216   /**
217    * Returns <jk>true</jk> if this element is not protected.
218    *
219    * @return <jk>true</jk> if this element is not protected.
220    */
221   public boolean isNotProtected() { return ! Modifier.isProtected(modifiers); }
222
223   /**
224    * Returns <jk>true</jk> if this element is not public.
225    *
226    * @return <jk>true</jk> if this element is not public.
227    */
228   public boolean isNotPublic() { return ! Modifier.isPublic(modifiers); }
229
230   /**
231    * Returns <jk>true</jk> if this element is not static.
232    *
233    * @return <jk>true</jk> if this element is not static.
234    */
235   public boolean isNotStatic() { return ! Modifier.isStatic(modifiers); }
236
237   /**
238    * Returns <jk>true</jk> if this element is not synchronized.
239    *
240    * @return <jk>true</jk> if this element is not synchronized.
241    */
242   public boolean isNotSynchronized() { return ! Modifier.isSynchronized(modifiers); }
243
244   /**
245    * Returns <jk>true</jk> if this element is not transient.
246    *
247    * @return <jk>true</jk> if this element is not transient.
248    */
249   public boolean isNotTransient() { return ! Modifier.isTransient(modifiers); }
250
251   /**
252    * Returns <jk>true</jk> if this element is not volatile.
253    *
254    * @return <jk>true</jk> if this element is not volatile.
255    */
256   public boolean isNotVolatile() { return ! Modifier.isVolatile(modifiers); }
257
258   /**
259    * Returns <jk>true</jk> if this element is private.
260    *
261    * @return <jk>true</jk> if this element is private.
262    */
263   public boolean isPrivate() { return Modifier.isPrivate(modifiers); }
264
265   /**
266    * Returns <jk>true</jk> if this element is protected.
267    *
268    * @return <jk>true</jk> if this element is protected.
269    */
270   public boolean isProtected() { return Modifier.isProtected(modifiers); }
271
272   /**
273    * Returns <jk>true</jk> if this element is public.
274    *
275    * @return <jk>true</jk> if this element is public.
276    */
277   public boolean isPublic() { return Modifier.isPublic(modifiers); }
278
279   /**
280    * Returns <jk>true</jk> if this element is static.
281    *
282    * @return <jk>true</jk> if this element is static.
283    */
284   public boolean isStatic() { return Modifier.isStatic(modifiers); }
285
286   /**
287    * Returns <jk>true</jk> if this element is synchronized.
288    *
289    * @return <jk>true</jk> if this element is synchronized.
290    */
291   public boolean isSynchronized() { return Modifier.isSynchronized(modifiers); }
292
293   /**
294    * Returns <jk>true</jk> if this element is transient.
295    *
296    * @return <jk>true</jk> if this element is transient.
297    */
298   public boolean isTransient() { return Modifier.isTransient(modifiers); }
299
300   /**
301    * Returns <jk>true</jk> if this element is volatile.
302    *
303    * @return <jk>true</jk> if this element is volatile.
304    */
305   public boolean isVolatile() { return Modifier.isVolatile(modifiers); }
306
307   //-----------------------------------------------------------------------------------------------------------------
308   // Helper methods
309   //-----------------------------------------------------------------------------------------------------------------
310
311   protected <A extends Annotation> AnnotationInfo<A> ai(Annotatable on, A value) {
312      return AnnotationInfo.of(on, value);
313   }
314}