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