001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.reflect;
014
015import static org.apache.juneau.internal.ConsumerUtils.*;
016
017import java.lang.annotation.*;
018import java.lang.reflect.*;
019import java.util.function.*;
020
021import org.apache.juneau.*;
022import org.apache.juneau.internal.*;
023
024/**
025 * Lightweight utility class for introspecting information about a constructor.
026 *
027 * <h5 class='section'>See Also:</h5><ul>
028 * </ul>
029 */
030@FluentSetters
031public final class ConstructorInfo extends ExecutableInfo implements Comparable<ConstructorInfo> {
032
033   //-----------------------------------------------------------------------------------------------------------------
034   // Static
035   //-----------------------------------------------------------------------------------------------------------------
036
037   /**
038    * Convenience method for instantiating a {@link ConstructorInfo};
039    *
040    * @param declaringClass The class that declares this method.
041    * @param c The constructor being wrapped.
042    * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if the method was null;
043    */
044   public static ConstructorInfo of(ClassInfo declaringClass, Constructor<?> c) {
045      if (c == null)
046         return null;
047      return ClassInfo.of(declaringClass).getConstructorInfo(c);
048   }
049
050   /**
051    * Convenience method for instantiating a {@link ConstructorInfo};
052    *
053    * @param c The constructor being wrapped.
054    * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if the method was null;
055    */
056   public static ConstructorInfo of(Constructor<?> c) {
057      if (c == null)
058         return null;
059      return ClassInfo.of(c.getDeclaringClass()).getConstructorInfo(c);
060   }
061
062   //-----------------------------------------------------------------------------------------------------------------
063   // Instance
064   //-----------------------------------------------------------------------------------------------------------------
065
066   private final Constructor<?> c;
067
068   /**
069    * Constructor.
070    *
071    * @param declaringClass The class that declares this method.
072    * @param c The constructor being wrapped.
073    */
074   protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> c) {
075      super(declaringClass, c);
076      this.c = c;
077   }
078
079   /**
080    * Returns the wrapped method.
081    *
082    * @param <T> The inner class type.
083    * @return The wrapped method.
084    */
085   @SuppressWarnings("unchecked")
086   public <T> Constructor<T> inner() {
087      return (Constructor<T>)c;
088   }
089
090   //-----------------------------------------------------------------------------------------------------------------
091   // Annotations
092   //-----------------------------------------------------------------------------------------------------------------
093
094   /**
095    * Finds the annotation of the specified type defined on this constructor.
096    *
097    * @param <A> The annotation type to look for.
098    * @param type The annotation to look for.
099    * @return The annotation if found, or <jk>null</jk> if not.
100    */
101   public <A extends Annotation> A getAnnotation(Class<A> type) {
102      return getAnnotation(AnnotationProvider.DEFAULT, type);
103   }
104
105   /**
106    * Finds the annotation of the specified type defined on this constructor.
107    *
108    * @param <A> The annotation type to look for.
109    * @param annotationProvider The annotation provider.
110    * @param type The annotation to look for.
111    * @return The first annotation found, or <jk>null</jk> if it doesn't exist.
112    */
113   public <A extends Annotation> A getAnnotation(AnnotationProvider annotationProvider, Class<A> type) {
114      Value<A> t = Value.empty();
115      annotationProvider.forEachAnnotation(type, c, x -> true, x -> t.set(x));
116      return t.orElse(null);
117   }
118
119   /**
120    * Returns <jk>true</jk> if the specified annotation is present on this constructor.
121    *
122    * @param <A> The annotation type to look for.
123    * @param type The annotation to look for.
124    * @return <jk>true</jk> if the specified annotation is present on this constructor.
125    */
126   public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
127      return hasAnnotation(AnnotationProvider.DEFAULT, type);
128   }
129
130   /**
131    * Returns <jk>true</jk> if the specified annotation is present on this constructor.
132    *
133    * @param <A> The annotation type to look for.
134    * @param annotationProvider The annotation provider.
135    * @param type The annotation to look for.
136    * @return <jk>true</jk> if the specified annotation is present on this constructor.
137    */
138   public <A extends Annotation> boolean hasAnnotation(AnnotationProvider annotationProvider, Class<A> type) {
139      return annotationProvider.firstAnnotation(type, c, x -> true) != null;
140   }
141
142   /**
143    * Returns <jk>true</jk> if the specified annotation is not present on this constructor.
144    *
145    * @param <A> The annotation type to look for.
146    * @param annotationProvider The annotation provider.
147    * @param type The annotation to look for.
148    * @return <jk>true</jk> if the specified annotation is not present on this constructor.
149    */
150   public <A extends Annotation> boolean hasNoAnnotation(AnnotationProvider annotationProvider, Class<A> type) {
151      return ! hasAnnotation(annotationProvider, type);
152   }
153
154   //-----------------------------------------------------------------------------------------------------------------
155   // Other methods
156   //-----------------------------------------------------------------------------------------------------------------
157
158   /**
159    * Returns <jk>true</jk> if this object passes the specified predicate test.
160    *
161    * @param test The test to perform.
162    * @return <jk>true</jk> if this object passes the specified predicate test.
163    */
164   public boolean matches(Predicate<ConstructorInfo> test) {
165      return test(test, this);
166   }
167
168   /**
169    * Performs an action on this object if the specified predicate test passes.
170    *
171    * @param test A test to apply to determine if action should be executed.  Can be <jk>null</jk>.
172    * @param action An action to perform on this object.
173    * @return This object.
174    */
175   public ConstructorInfo accept(Predicate<ConstructorInfo> test, Consumer<ConstructorInfo> action) {
176      if (matches(test))
177         action.accept(this);
178      return this;
179   }
180
181   /**
182    * Returns <jk>true</jk> if this constructor can accept the specified arguments in the specified order.
183    *
184    * @param args The arguments to check.
185    * @return <jk>true</jk> if this constructor can accept the specified arguments in the specified order.
186    */
187   public boolean canAccept(Object...args) {
188      Class<?>[] pt = c.getParameterTypes();
189      if (pt.length != args.length)
190         return false;
191      for (int i = 0; i < pt.length; i++)
192         if (! pt[i].isInstance(args[i]))
193            return false;
194      return true;
195   }
196
197   /**
198    * Shortcut for calling the new-instance method on the underlying constructor.
199    *
200    * @param <T> The constructor class type.
201    * @param args the arguments used for the method call.
202    *    <br>Extra parameters are ignored.
203    *    <br>Missing parameters are set to null.
204    * @return The object returned from the constructor.
205    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
206    */
207   public <T> T invokeFuzzy(Object...args) throws ExecutableException {
208      return invoke(ClassUtils.getMatchingArgs(c.getParameterTypes(), args));
209   }
210
211   /**
212    * Shortcut for calling the new-instance method on the underlying constructor.
213    *
214    * @param <T> The constructor class type.
215    * @param args the arguments used for the method call.
216    * @return The object returned from the constructor.
217    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
218    */
219   @SuppressWarnings("unchecked")
220   public <T> T invoke(Object...args) throws ExecutableException {
221      try {
222         return (T)c.newInstance(args);
223      } catch (InvocationTargetException e) {
224         throw new ExecutableException(e.getTargetException());
225      } catch (Exception e) {
226         throw new ExecutableException(e);
227      }
228   }
229
230   /**
231    * Makes constructor accessible if it matches the visibility requirements, or returns <jk>null</jk> if it doesn't.
232    *
233    * <p>
234    * Security exceptions thrown on the call to {@link Constructor#setAccessible(boolean)} are quietly ignored.
235    *
236    * @param v The minimum visibility.
237    * @return
238    *    The same constructor if visibility requirements met, or <jk>null</jk> if visibility requirement not
239    *    met or call to {@link Constructor#setAccessible(boolean)} throws a security exception.
240    */
241   public ConstructorInfo accessible(Visibility v) {
242      if (v.transform(c) == null)
243         return null;
244      return this;
245   }
246
247   @Override
248   public int compareTo(ConstructorInfo o) {
249      int i = getSimpleName().compareTo(o.getSimpleName());
250      if (i == 0) {
251         i = getParamCount() - o.getParamCount();
252         if (i == 0) {
253            for (int j = 0; j < getParamCount() && i == 0; j++) {
254               Class<?>[] tpt = _getRawParamTypes(), opt = o._getRawParamTypes();
255               i = tpt[j].getName().compareTo(opt[j].getName());
256            }
257         }
258      }
259      return i;
260   }
261
262   // <FluentSetters>
263
264   @Override /* GENERATED - org.apache.juneau.reflect.ExecutableInfo */
265   public ConstructorInfo accessible() {
266      super.accessible();
267      return this;
268   }
269
270   // </FluentSetters>
271}