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.StringUtils.*;
016
017import java.lang.annotation.*;
018import java.lang.reflect.*;
019import java.util.*;
020
021import org.apache.juneau.*;
022import org.apache.juneau.internal.*;
023
024/**
025 * Contains common methods between {@link ConstructorInfo} and {@link MethodInfo}.
026 */
027public abstract class ExecutableInfo {
028
029   final ClassInfo declaringClass;
030   final Executable e, re;
031   final boolean isConstructor;
032
033   private List<ParamInfo> params;
034   private List<ClassInfo> paramTypes, exceptionInfos;
035   private Class<?>[] rawParamTypes, rawExceptionTypes;
036   private Type[] rawGenericParamTypes;
037   private Parameter[] rawParameters;
038
039   /**
040    * Constructor.
041    *
042    * @param declaringClass The class that declares this method or constructor.
043    * @param e The constructor or method that this info represents.
044    * @param re The "real" constructor if the constructor above is defined against a CGLIB proxy.
045    */
046   protected ExecutableInfo(ClassInfo declaringClass, Executable e, Executable re) {
047      this.declaringClass = declaringClass;
048      this.e = e;
049      this.re = re == null ? e : re;
050      this.isConstructor = e instanceof Constructor;
051   }
052
053   /**
054    * Returns metadata about the class that declared this method or constructor.
055    *
056    * @return Metadata about the class that declared this method or constructor.
057    */
058   public final ClassInfo getDeclaringClass() {
059      return declaringClass;
060   }
061
062   /**
063    * Returns <jk>true</jk> if this executable represents a {@link Constructor}.
064    *
065    * @return
066    *    <jk>true</jk> if this executable represents a {@link Constructor} and can be cast to {@link ConstructorInfo}.
067    *    <jk>false</jk> if this executable represents a {@link Method} and can be cast to {@link MethodInfo}.
068    */
069   public final boolean isConstructor() {
070      return isConstructor;
071   }
072
073   //-----------------------------------------------------------------------------------------------------------------
074   // Parameters
075   //-----------------------------------------------------------------------------------------------------------------
076
077   /**
078    * Returns the number of parameters in this executable.
079    *
080    * <p>
081    * Same as calling {@link Executable#getParameterCount()}.
082    *
083    * @return The number of parameters in this executable.
084    */
085   public final int getParamCount() {
086      return e.getParameterCount();
087   }
088
089   /**
090    * Returns <jk>true</jk> if this executable has at least one parameter.
091    *
092    * <p>
093    * Same as calling {@link Executable#getParameterCount()} and comparing with zero.
094    *
095    * @return <jk>true</jk> if this executable has at least one parameter.
096    */
097   public final boolean hasParams() {
098      return getParamCount() != 0;
099   }
100
101   /**
102    * Returns <jk>true</jk> if this executable has no parameters.
103    *
104    * <p>
105    * Same as calling {@link Executable#getParameterCount()} and comparing with zero.
106    *
107    * @return <jk>true</jk> if this executable has no parameters.
108    */
109   public final boolean hasNoParams() {
110      return getParamCount() == 0;
111   }
112
113   /**
114    * Returns <jk>true</jk> if this executable has this number of arguments.
115    *
116    * <p>
117    * Same as calling {@link Executable#getParameterCount()} and comparing the count.
118    *
119    * @param number The number of expected arguments.
120    * @return <jk>true</jk> if this executable has this number of arguments.
121    */
122   public final boolean hasNumParams(int number) {
123      return getParamCount() == number;
124   }
125
126   /**
127    * Returns the parameters defined on this executable.
128    *
129    * <p>
130    * Same as calling {@link Executable#getParameters()} but wraps the results
131    *
132    * @return An array of parameter information, never <jk>null</jk>.
133    */
134   public final List<ParamInfo> getParams() {
135      if (params == null) {
136         Parameter[] rp = rawParameters();
137         List<ParamInfo> l = new ArrayList<>(rp.length);
138         for (int i = 0; i < rp.length; i++)
139            l.add(new ParamInfo(this, rp[i], i));
140         params = Collections.unmodifiableList(l);
141      }
142      return params;
143   }
144
145   /**
146    * Returns parameter information at the specified index.
147    *
148    * @param index The parameter index.
149    * @return The parameter information, never <jk>null</jk>.
150    */
151   public final ParamInfo getParam(int index) {
152      checkIndex(index);
153      if (params != null)
154         return params.get(index);
155      return new ParamInfo(this, rawParameters()[index], index);
156   }
157
158   /**
159    * Returns the parameter types on this executable.
160    *
161    * @return The parameter types on this executable.
162    */
163   public final List<ClassInfo> getParamTypes() {
164      if (paramTypes == null) {
165         Class<?>[] ptc = rawParamTypes();
166         // Note that due to a bug involving Enum constructors, getGenericParameterTypes() may
167         // always return an empty array.  This appears to be fixed in Java 8 b75.
168         Type[] ptt = rawGenericParamTypes();
169         if (ptt.length != ptc.length)
170            ptt = ptc;
171         List<ClassInfo> l = new ArrayList<>(ptc.length);
172         for (int i = 0; i < ptc.length; i++)
173            l.add(ClassInfo.of(ptc[i], ptt[i]));
174         paramTypes = Collections.unmodifiableList(l);
175      }
176      return paramTypes;
177   }
178
179   /**
180    * Returns the parameter type of the parameter at the specified index.
181    *
182    * @param index The parameter index.
183    * @return The parameter type of the parameter at the specified index.
184    */
185   public final ClassInfo getParamType(int index) {
186      checkIndex(index);
187      if (paramTypes != null)
188         return getParamTypes().get(index);
189      return ClassInfo.of(getRawParamType(index), getRawGenericParamType(index));
190   }
191
192   /**
193    * Returns the raw parameter types on this executable.
194    *
195    * @return The raw parameter types on this executable.
196    */
197   public final Class<?>[] getRawParamTypes() {
198      return rawParamTypes().clone();
199   }
200
201   /**
202    * Returns the raw parameter type of the parameter at the specified index.
203    *
204    * @param index The parameter index.
205    * @return The raw parameter type of the parameter at the specified index.
206    */
207   public final Class<?> getRawParamType(int index) {
208      checkIndex(index);
209      return rawParamTypes()[index];
210   }
211
212   /**
213    * Returns the raw generic parameter types on this executable.
214    *
215    * @return The raw generic parameter types on this executable.
216    */
217   public final Type[] getRawGenericParamTypes() {
218      return rawGenericParamTypes().clone();
219   }
220
221   /**
222    * Returns the raw generic parameter type of the parameter at the specified index.
223    *
224    * @param index The parameter index.
225    * @return The raw generic parameter type of the parameter at the specified index.
226    */
227   public final Type getRawGenericParamType(int index) {
228      checkIndex(index);
229      return rawGenericParamTypes()[index];
230   }
231
232   /**
233    * Returns an array of raw {@link Parameter} objects that represent all the parameters to the underlying executable represented by this object.
234    *
235    * @return An array of raw {@link Parameter} objects, or an empty array if executable has no parameters.
236    * @see Executable#getParameters()
237    */
238   public final Parameter[] getRawParameters() {
239      return rawParameters().clone();
240   }
241
242   /**
243    * Returns the raw {@link Parameter} object that represents the parameter at the specified index.
244    *
245    * @param index The parameter index.
246    * @return The raw {@link Parameter} object that represents the parameter at the specified index.
247    * @see Executable#getParameters()
248    */
249   public final Parameter getRawParameter(int index) {
250      checkIndex(index);
251      return rawParameters()[index];
252   }
253
254   Class<?>[] rawParamTypes() {
255      if (rawParamTypes == null)
256         rawParamTypes = e.getParameterTypes();
257      return rawParamTypes;
258   }
259
260   Type[] rawGenericParamTypes() {
261      if (rawGenericParamTypes == null)
262         rawGenericParamTypes = re.getGenericParameterTypes();
263      return rawGenericParamTypes;
264   }
265
266   Parameter[] rawParameters() {
267      if (rawParameters == null)
268         rawParameters = re.getParameters();
269      return rawParameters;
270   }
271
272   private void checkIndex(int index) {
273      int pc = getParamCount();
274      if (pc == 0)
275         throw new IndexOutOfBoundsException(format("Invalid index ''{0}''.  No parameters.", index));
276      if (index < 0 || index >= pc)
277         throw new IndexOutOfBoundsException(format("Invalid index ''{0}''.  Parameter count: {1}", index, pc));
278   }
279
280   //-----------------------------------------------------------------------------------------------------------------
281   // Annotations
282   //-----------------------------------------------------------------------------------------------------------------
283
284   /**
285    * Returns the parameter annotations on this executable.
286    *
287    * @return The parameter annotations on this executable.
288    */
289   public final Annotation[][] getParameterAnnotations() {
290      return e.getParameterAnnotations();
291   }
292
293   /**
294    * Returns the parameter annotations on the parameter at the specified index.
295    *
296    * @param index The parameter index.
297    * @return The parameter annotations on the parameter at the specified index.
298    */
299   public final Annotation[] getParameterAnnotations(int index) {
300      checkIndex(index);
301      return e.getParameterAnnotations()[index];
302   }
303
304   //-----------------------------------------------------------------------------------------------------------------
305   // Exceptions
306   //-----------------------------------------------------------------------------------------------------------------
307
308   /**
309    * Returns the exception types on this executable.
310    *
311    * @return The exception types on this executable.
312    */
313   public final List<ClassInfo> getExceptionTypes() {
314      if (exceptionInfos == null) {
315         Class<?>[] exceptionTypes = rawExceptionTypes();
316         List<ClassInfo> l = new ArrayList<>(exceptionTypes.length);
317         for (Class<?> et : exceptionTypes)
318            l.add(ClassInfo.of(et));
319         exceptionInfos = Collections.unmodifiableList(l);
320      }
321      return exceptionInfos;
322   }
323
324   /**
325    * Returns the raw exception types on this executable.
326    *
327    * @return The raw exception types on this executable.
328    */
329   public final Class<?>[] getRawExceptionTypes() {
330      return rawExceptionTypes().clone();
331   }
332
333   private Class<?>[] rawExceptionTypes() {
334      if (rawExceptionTypes == null)
335         rawExceptionTypes = e.getExceptionTypes();
336      return rawExceptionTypes;
337   }
338
339   //-----------------------------------------------------------------------------------------------------------------
340   // Characteristics
341   //-----------------------------------------------------------------------------------------------------------------
342
343   /**
344    * Returns <jk>true</jk> if all specified flags are applicable to this method.
345    *
346    * @param flags The flags to test for.
347    * @return <jk>true</jk> if all specified flags are applicable to this method.
348    */
349   public final boolean isAll(ReflectFlags...flags) {
350      for (ReflectFlags f : flags) {
351         switch (f) {
352            case DEPRECATED:
353               if (isNotDeprecated())
354                  return false;
355               break;
356            case NOT_DEPRECATED:
357               if (isDeprecated())
358                  return false;
359               break;
360            case HAS_PARAMS:
361               if (hasNoParams())
362                  return false;
363               break;
364            case HAS_NO_PARAMS:
365               if (hasParams())
366                  return false;
367               break;
368            case PUBLIC:
369               if (isNotPublic())
370                  return false;
371               break;
372            case NOT_PUBLIC:
373               if (isPublic())
374                  return false;
375               break;
376            case STATIC:
377               if (isNotStatic())
378                  return false;
379               break;
380            case NOT_STATIC:
381               if (isStatic())
382                  return false;
383               break;
384            case ABSTRACT:
385               if (isNotAbstract())
386                  return false;
387               break;
388            case NOT_ABSTRACT:
389               if (isAbstract())
390                  return false;
391               break;
392            default:
393               throw new RuntimeException("Invalid flag for executable: " + f);
394         }
395      }
396      return true;
397   }
398
399   /**
400    * Returns <jk>true</jk> if all specified flags are applicable to this method.
401    *
402    * @param flags The flags to test for.
403    * @return <jk>true</jk> if all specified flags are applicable to this method.
404    */
405   public final boolean isAny(ReflectFlags...flags) {
406      for (ReflectFlags f : flags) {
407         switch (f) {
408            case DEPRECATED:
409               if (isDeprecated())
410                  return true;
411               break;
412            case NOT_DEPRECATED:
413               if (isNotDeprecated())
414                  return true;
415               break;
416            case HAS_PARAMS:
417               if (hasParams())
418                  return true;
419               break;
420            case HAS_NO_PARAMS:
421               if (hasNoParams())
422                  return true;
423               break;
424            case PUBLIC:
425               if (isPublic())
426                  return true;
427               break;
428            case NOT_PUBLIC:
429               if (isNotPublic())
430                  return true;
431               break;
432            case STATIC:
433               if (isStatic())
434                  return true;
435               break;
436            case NOT_STATIC:
437               if (isNotStatic())
438                  return true;
439               break;
440            case ABSTRACT:
441               if (isAbstract())
442                  return true;
443               break;
444            case NOT_ABSTRACT:
445               if (isNotAbstract())
446                  return true;
447               break;
448            default:
449               throw new RuntimeException("Invalid flag for executable: " + f);
450         }
451      }
452      return false;
453   }
454
455   /**
456    * Returns <jk>true</jk> if this method has the specified arguments.
457    *
458    * @param args The arguments to test for.
459    * @return <jk>true</jk> if this method has this arguments in the exact order.
460    */
461   public final boolean hasParamTypes(Class<?>...args) {
462      Class<?>[] pt = rawParamTypes();
463      if (pt.length == args.length) {
464         for (int i = 0; i < pt.length; i++)
465            if (! pt[i].equals(args[i]))
466               return false;
467         return true;
468      }
469      return false;
470   }
471
472   /**
473    * Returns <jk>true</jk> if this method has the specified arguments.
474    *
475    * @param args The arguments to test for.
476    * @return <jk>true</jk> if this method has this arguments in the exact order.
477    */
478   public final boolean hasParamTypes(ClassInfo...args) {
479      Class<?>[] pt = rawParamTypes();
480      if (pt.length == args.length) {
481         for (int i = 0; i < pt.length; i++)
482            if (! pt[i].equals(args[i].inner()))
483               return false;
484         return true;
485      }
486      return false;
487   }
488
489   /**
490    * Returns <jk>true</jk> if this method has the specified argument parent classes.
491    *
492    * @param args The arguments to test for.
493    * @return <jk>true</jk> if this method has this arguments in the exact order.
494    */
495   public final boolean hasParamTypeParents(Class<?>...args) {
496      Class<?>[] pt = rawParamTypes();
497      if (pt.length == args.length) {
498         for (int i = 0; i < pt.length; i++)
499            if (! args[i].isAssignableFrom(pt[i]))
500               return false;
501         return true;
502      }
503      return false;
504   }
505
506   /**
507    * Returns <jk>true</jk> if this method has the specified argument parent classes.
508    *
509    * @param args The arguments to test for.
510    * @return <jk>true</jk> if this method has this arguments in the exact order.
511    */
512   public final boolean hasParamTypeParents(ClassInfo...args) {
513      Class<?>[] pt = rawParamTypes();
514      if (pt.length == args.length) {
515         for (int i = 0; i < pt.length; i++)
516            if (! args[i].inner().isAssignableFrom(pt[i]))
517               return false;
518         return true;
519      }
520      return false;
521   }
522
523   /**
524    * Returns <jk>true</jk> if this method has at most only this arguments in any order.
525    *
526    * @param args The arguments to test for.
527    * @return <jk>true</jk> if this method has at most only this arguments in any order.
528    */
529   public final boolean hasFuzzyParamTypes(Class<?>...args) {
530      return ClassUtils.fuzzyArgsMatch(rawParamTypes(), args) != -1;
531   }
532
533   /**
534    * Returns <jk>true</jk> if this method has at most only this arguments in any order.
535    *
536    * @param args The arguments to test for.
537    * @return <jk>true</jk> if this method has at most only this arguments in any order.
538    */
539   public boolean hasFuzzyParamTypes(ClassInfo...args) {
540      return ClassUtils.fuzzyArgsMatch(rawParamTypes(), args) != -1;
541   }
542
543   /**
544    * Returns <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
545    *
546    * @return <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
547    */
548   public final boolean isDeprecated() {
549      return e.isAnnotationPresent(Deprecated.class);
550
551   }
552
553   /**
554    * Returns <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
555    *
556    * @return <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
557    */
558   public final boolean isNotDeprecated() {
559      return ! e.isAnnotationPresent(Deprecated.class);
560
561   }
562
563   /**
564    * Returns <jk>true</jk> if this method is abstract.
565    *
566    * @return <jk>true</jk> if this method is abstract.
567    */
568   public final boolean isAbstract() {
569      return Modifier.isAbstract(e.getModifiers());
570   }
571
572   /**
573    * Returns <jk>true</jk> if this method is not abstract.
574    *
575    * @return <jk>true</jk> if this method is not abstract.
576    */
577   public final boolean isNotAbstract() {
578      return ! Modifier.isAbstract(e.getModifiers());
579   }
580
581   /**
582    * Returns <jk>true</jk> if this method is public.
583    *
584    * @return <jk>true</jk> if this method is public.
585    */
586   public final boolean isPublic() {
587      return Modifier.isPublic(e.getModifiers());
588   }
589
590   /**
591    * Returns <jk>true</jk> if this method is not public.
592    *
593    * @return <jk>true</jk> if this method is not public.
594    */
595   public final boolean isNotPublic() {
596      return ! Modifier.isPublic(e.getModifiers());
597   }
598
599   /**
600    * Returns <jk>true</jk> if this method is static.
601    *
602    * @return <jk>true</jk> if this method is static.
603    */
604   public final boolean isStatic() {
605      return Modifier.isStatic(e.getModifiers());
606   }
607
608   /**
609    * Returns <jk>true</jk> if this method is not static.
610    *
611    * @return <jk>true</jk> if this method is not static.
612    */
613   public final boolean isNotStatic() {
614      return ! Modifier.isStatic(e.getModifiers());
615   }
616
617   //-----------------------------------------------------------------------------------------------------------------
618   // Visibility
619   //-----------------------------------------------------------------------------------------------------------------
620
621   /**
622    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
623    *
624    * @return <jk>true</jk> if call was successful.
625    */
626   public final boolean setAccessible() {
627      try {
628         if (! (e.isAccessible()))
629            e.setAccessible(true);
630         return true;
631      } catch (SecurityException e) {
632         return false;
633      }
634   }
635
636   /**
637    * Identifies if the specified visibility matches this method.
638    *
639    * @param v The visibility to validate against.
640    * @return <jk>true</jk> if this visibility matches the modifier attribute of this method.
641    */
642   public final boolean isVisible(Visibility v) {
643      return v.isVisible(e);
644   }
645
646   //-----------------------------------------------------------------------------------------------------------------
647   // Labels
648   //-----------------------------------------------------------------------------------------------------------------
649
650   /**
651    * Returns <jk>true</jk> if this method has this name.
652    *
653    * @param name The name to test for.
654    * @return <jk>true</jk> if this method has this name.
655    */
656   public final boolean hasName(String name) {
657      return getSimpleName().equals(name);
658   }
659
660   /**
661    * Returns <jk>true</jk> if this method has a name in the specified list.
662    *
663    * @param names The names to test for.
664    * @return <jk>true</jk> if this method has one of the names.
665    */
666   public final boolean hasName(String...names) {
667      for (String n : names)
668         if (getSimpleName().equals(n))
669            return true;
670      return false;
671   }
672
673   /**
674    * Returns <jk>true</jk> if this method has a name in the specified set.
675    *
676    * @param names The names to test for.
677    * @return <jk>true</jk> if this method has one of the names.
678    */
679   public final boolean hasName(Set<String> names) {
680      return names.contains(getSimpleName());
681   }
682
683   //-----------------------------------------------------------------------------------------------------------------
684   // Labels
685   //-----------------------------------------------------------------------------------------------------------------
686
687   /**
688    * Returns the full name of this executable.
689    *
690    * <h5 class='section'>Examples:</h5>
691    * <ul>
692    *    <li><js>"com.foo.MyClass.get(java.util.String)"</js> - Method.
693    *    <li><js>"com.foo.MyClass(java.util.String)"</js> - Constructor.
694    * </ul>
695    *
696    * @return The underlying executable name.
697    */
698   public final String getFullName() {
699      StringBuilder sb = new StringBuilder(128);
700      ClassInfo dc = declaringClass;
701      Package p = dc.getPackage();
702      if (p != null)
703         sb.append(p.getName()).append('.');
704      dc.appendShortName(sb);
705      if (! isConstructor)
706         sb.append('.').append(getSimpleName());
707      sb.append('(');
708      List<ClassInfo> pt = getParamTypes();
709      for (int i = 0; i < pt.size(); i++) {
710         if (i > 0)
711            sb.append(',');
712         pt.get(i).appendFullName(sb);
713      }
714      sb.append(')');
715      return sb.toString();
716   }
717
718   /**
719    * Returns the short name of this executable.
720    *
721    * <h5 class='section'>Examples:</h5>
722    * <ul>
723    *    <li><js>"MyClass.get(String)"</js> - Method.
724    *    <li><js>"MyClass(String)"</js> - Constructor.
725    * </ul>
726    *
727    * @return The underlying executable name.
728    */
729   public final String getShortName() {
730      StringBuilder sb = new StringBuilder(64);
731      sb.append(getSimpleName()).append('(');
732      Class<?>[] pt = rawParamTypes();
733      for (int i = 0; i < pt.length; i++) {
734         if (i > 0)
735            sb.append(',');
736         sb.append(pt[i].getSimpleName());
737      }
738      sb.append(')');
739      return sb.toString();
740   }
741
742   /**
743    * Returns the simple name of the underlying method.
744    *
745    * @return The simple name of the underlying method;
746    */
747   public final String getSimpleName() {
748      return isConstructor ? e.getDeclaringClass().getSimpleName() : e.getName();
749   }
750
751   @Override
752   public String toString() {
753      return getShortName();
754   }
755}