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.CollectionUtils.*;
016import static org.apache.juneau.internal.ConsumerUtils.*;
017
018import java.beans.*;
019import java.lang.annotation.*;
020import java.lang.reflect.*;
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.*;
025import org.apache.juneau.internal.*;
026
027/**
028 * Lightweight utility class for introspecting information about a method.
029 *
030 * <h5 class='section'>See Also:</h5><ul>
031 * </ul>
032 */
033@FluentSetters
034public final class MethodInfo extends ExecutableInfo implements Comparable<MethodInfo> {
035
036   //-----------------------------------------------------------------------------------------------------------------
037   // Static
038   //-----------------------------------------------------------------------------------------------------------------
039
040   /**
041    * Convenience method for instantiating a {@link MethodInfo};
042    *
043    * @param declaringClass The class that declares this method.
044    * @param m The method being wrapped.
045    * @return A new {@link MethodInfo} object, or <jk>null</jk> if the method was null;
046    */
047   public static MethodInfo of(ClassInfo declaringClass, Method m) {
048      if (m == null)
049         return null;
050      return declaringClass.getMethodInfo(m);
051   }
052
053   /**
054    * Convenience method for instantiating a {@link MethodInfo};
055    *
056    * @param declaringClass The class that declares this method.
057    * @param m The method being wrapped.
058    * @return A new {@link MethodInfo} object, or <jk>null</jk> if the method was null;
059    */
060   public static MethodInfo of(Class<?> declaringClass, Method m) {
061      if (m == null)
062         return null;
063      return ClassInfo.of(declaringClass).getMethodInfo(m);
064   }
065
066   /**
067    * Convenience method for instantiating a {@link MethodInfo};
068    *
069    * @param m The method being wrapped.
070    * @return A new {@link MethodInfo} object, or <jk>null</jk> if the method was null;
071    */
072   public static MethodInfo of(Method m) {
073      if (m == null)
074         return null;
075      return ClassInfo.of(m.getDeclaringClass()).getMethodInfo(m);
076   }
077
078   //-----------------------------------------------------------------------------------------------------------------
079   // Instance
080   //-----------------------------------------------------------------------------------------------------------------
081
082   private final Method m;
083   private volatile ClassInfo returnType;
084   private volatile MethodInfo[] matching;
085
086   /**
087    * Constructor.
088    *
089    * @param declaringClass The class that declares this method.
090    * @param m The method being wrapped.
091    */
092   protected MethodInfo(ClassInfo declaringClass, Method m) {
093      super(declaringClass, m);
094      this.m = m;
095   }
096
097   /**
098    * Returns the wrapped method.
099    *
100    * @return The wrapped method.
101    */
102   public Method inner() {
103      return m;
104   }
105
106   //-----------------------------------------------------------------------------------------------------------------
107   // Matching methods.
108   //-----------------------------------------------------------------------------------------------------------------
109
110   /**
111    * Returns <jk>true</jk> if this constructor can accept the specified arguments in the specified order.
112    *
113    * @param args The arguments to check.
114    * @return <jk>true</jk> if this constructor can accept the specified arguments in the specified order.
115    */
116   public boolean canAccept(Object...args) {
117      Class<?>[] pt = m.getParameterTypes();
118      if (pt.length != args.length)
119         return false;
120      for (int i = 0; i < pt.length; i++)
121         if (! pt[i].isInstance(args[i]))
122            return false;
123      return true;
124   }
125
126   /**
127    * Returns the number of matching arguments for this method.
128    *
129    * @param args The arguments to check.
130    * @return the number of matching arguments for this method.
131    */
132   public int canAcceptFuzzy(Object...args) {
133      int matches = 0;
134      outer: for (ClassInfo pi : _getParameterTypes()) {
135         for (Object a : args) {
136            if (pi.canAcceptArg(a)) {
137               matches++;
138               continue outer;
139            }
140         }
141         return -1;
142      }
143      return matches;
144   }
145
146   /**
147    * Performs an action on all matching declared methods with the same name and arguments on all superclasses and interfaces.
148    *
149    * <p>
150    * Methods are accessed from child-to-parent order.
151    *
152    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk>.
153    * @param action An action to perform on the entry.
154    * @return This object.
155    */
156   public MethodInfo forEachMatching(Predicate<MethodInfo> filter, Consumer<MethodInfo> action) {
157      for (MethodInfo m : _getMatching())
158         consume(filter, action, m);
159      return this;
160   }
161
162   /**
163    * Performs an action on all matching declared methods with the same name and arguments on all superclasses and interfaces.
164    *
165    * <p>
166    * Methods are accessed from parent-to-child order.
167    *
168    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk>.
169    * @param action An action to perform on the entry.
170    * @return This object.
171    */
172   public MethodInfo forEachMatchingParentFirst(Predicate<MethodInfo> filter, Consumer<MethodInfo> action) {
173      MethodInfo[] m = _getMatching();
174      for (int i = m.length-1; i >= 0; i--)
175         consume(filter, action, m[i]);
176      return this;
177   }
178
179   private static List<MethodInfo> findMatching(List<MethodInfo> l, MethodInfo m, ClassInfo c) {
180      for (MethodInfo m2 : c._getDeclaredMethods())
181         if (m.hasName(m2.getName()) && Arrays.equals(m._getParameterTypes(), m2._getParameterTypes()))
182            l.add(m2);
183      ClassInfo pc = c.getSuperclass();
184      if (pc != null)
185         findMatching(l, m, pc);
186      for (ClassInfo ic : c._getDeclaredInterfaces())
187         findMatching(l, m, ic);
188      return l;
189   }
190
191   private MethodInfo findMatchingOnClass(ClassInfo c) {
192      for (MethodInfo m2 : c._getDeclaredMethods())
193         if (hasName(m2.getName()) && Arrays.equals(_getParameterTypes(), m2._getParameterTypes()))
194            return m2;
195      return null;
196   }
197
198   MethodInfo[] _getMatching() {
199      if (matching == null) {
200         synchronized(this) {
201            List<MethodInfo> l = findMatching(list(), this, getDeclaringClass());
202            matching = l.toArray(new MethodInfo[l.size()]);
203         }
204      }
205      return matching;
206   }
207
208   //-----------------------------------------------------------------------------------------------------------------
209   // Annotations
210   //-----------------------------------------------------------------------------------------------------------------
211
212   /**
213    * Finds the annotation of the specified type defined on this method.
214    *
215    * <p>
216    * If this is a method and the annotation cannot be found on the immediate method, searches methods with the same
217    * signature on the parent classes or interfaces.
218    * <br>The search is performed in child-to-parent order.
219    *
220    * @param <A> The annotation type to look for.
221    * @param type The annotation to look for.
222    * @return The annotation if found, or <jk>null</jk> if not.
223    */
224   public final <A extends Annotation> A getAnnotation(Class<A> type) {
225      return getAnnotation(AnnotationProvider.DEFAULT, type);
226   }
227
228   /**
229    * Finds the annotation of the specified type defined on this method.
230    *
231    * <p>
232    * Searches all methods with the same signature on the parent classes or interfaces
233    * and the return type on the method.
234    *
235    * @param <A> The annotation type to look for.
236    * @param annotationProvider The annotation provider.
237    * @param type The annotation to look for.
238    * @return The first annotation found, or <jk>null</jk> if it doesn't exist.
239    */
240   public final <A extends Annotation> A getAnnotation(AnnotationProvider annotationProvider, Class<A> type) {
241      if (type == null)
242         return null;
243      Value<A> t = Value.empty();
244      for (MethodInfo m2 : _getMatching()) {
245         annotationProvider.forEachAnnotation(type, m2.inner(), x -> true, x -> t.set(x));
246         if (t.isPresent())
247            return t.get();
248      }
249      return null;
250   }
251
252   /**
253    * Returns <jk>true</jk> if the specified annotation is present on this method.
254    *
255    * @param <A> The annotation type to look for.
256    * @param type The annotation to look for.
257    * @return <jk>true</jk> if the specified annotation is present on this method.
258    */
259   public final <A extends Annotation> boolean hasAnnotation(Class<A> type) {
260      return hasAnnotation(AnnotationProvider.DEFAULT, type);
261   }
262
263   /**
264    * Returns <jk>true</jk> if the specified annotation is present on this method.
265    *
266    * @param <A> The annotation type to look for.
267    * @param annotationProvider The annotation provider.
268    * @param type The annotation to look for.
269    * @return <jk>true</jk> if the specified annotation is present on this method.
270    */
271   public final <A extends Annotation> boolean hasAnnotation(AnnotationProvider annotationProvider, Class<A> type) {
272      for (MethodInfo m2 : _getMatching())
273         if (annotationProvider.firstAnnotation(type, m2.inner(), x -> true) != null)
274            return true;
275      return false;
276   }
277
278   /**
279    * Returns <jk>true</jk> if the specified annotation is not present on this method.
280    *
281    * @param <A> The annotation type to look for.
282    * @param annotationProvider The annotation provider.
283    * @param type The annotation to look for.
284    * @return <jk>true</jk> if the specified annotation is not present on this method.
285    */
286   public final <A extends Annotation> boolean hasNoAnnotation(AnnotationProvider annotationProvider, Class<A> type) {
287      return ! hasAnnotation(annotationProvider, type);
288   }
289
290   /**
291    * Returns <jk>true</jk> if the specified annotation is not present on this method.
292    *
293    * @param <A> The annotation type to look for.
294    * @param type The annotation to look for.
295    * @return <jk>true</jk> if the specified annotation is not present on this method.
296    */
297   public final <A extends Annotation> boolean hasNoAnnotation(Class<A> type) {
298      return getAnnotation(type) == null;
299   }
300
301   /**
302    * Returns <jk>true</jk> if at least one of the specified annotation is present on this method.
303    *
304    * @param types The annotation to look for.
305    * @return <jk>true</jk> if at least one of the specified annotation is present on this method.
306    */
307   @SafeVarargs
308   public final boolean hasAnyAnnotations(Class<? extends Annotation>...types) {
309      for (Class<? extends Annotation> a : types)
310         if (hasAnnotation(a))
311            return true;
312      return false;
313   }
314
315   /**
316    * Performs an action on all matching annotations defined on this method.
317    *
318    * <p>
319    * Searches all methods with the same signature on the parent classes or interfaces
320    * and the return type on the method.
321    * <br>Results are parent-to-child ordered.
322    *
323    * @param <A> The annotation type to look for.
324    * @param type The annotation to look for.
325    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk>.
326    * @param action An action to perform on the entry.
327    * @return This object.
328    */
329   public <A extends Annotation> MethodInfo forEachAnnotation(Class<A> type, Predicate<A> filter, Consumer<A> action) {
330      return forEachAnnotation(AnnotationProvider.DEFAULT, type, filter, action);
331   }
332
333   /**
334    * Performs an action on all matching annotations defined on this method.
335    *
336    * <p>
337    * Searches all methods with the same signature on the parent classes or interfaces
338    * and the return type on the method.
339    * <br>Results are parent-to-child ordered.
340    *
341    * @param <A> The annotation type to look for.
342    * @param annotationProvider The annotation provider.
343    * @param type The annotation type.
344    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk>.
345    * @param action An action to perform on the entry.
346    * @return This object.
347    */
348   public <A extends Annotation> MethodInfo forEachAnnotation(AnnotationProvider annotationProvider, Class<A> type, Predicate<A> filter, Consumer<A> action) {
349      declaringClass.forEachAnnotation(annotationProvider, type, filter, action);
350      MethodInfo[] m = _getMatching();
351      for (int i = m.length-1; i >= 0; i--)
352         for (Annotation a2 : m[i]._getDeclaredAnnotations())
353            consume(type, filter, action, a2);
354      getReturnType().unwrap(Value.class,Optional.class).forEachAnnotation(annotationProvider, type, filter, action);
355      return this;
356   }
357
358   /**
359    * Returns the first annotation in the specified list on this method.
360    *
361    * @param types The annotations to look for.
362    * @return The first matching annotation.
363    */
364   @SafeVarargs
365   public final Annotation getAnyAnnotation(Class<? extends Annotation>...types) {
366      for (Class<? extends Annotation> cc : types) {
367         Annotation a = getAnnotation(cc);
368         if (a != null)
369            return a;
370      }
371      return null;
372   }
373
374   /**
375    * Constructs an {@link AnnotationList} of all annotations found on this method.
376    *
377    * <p>
378    * Annotations are appended in the following orders:
379    * <ol>
380    *    <li>On the package of this class.
381    *    <li>On interfaces ordered parent-to-child.
382    *    <li>On parent classes ordered parent-to-child.
383    *    <li>On this class.
384    *    <li>On this method and matching methods ordered parent-to-child.
385    * </ol>
386    *
387    * @return A new {@link AnnotationList} object on every call.
388    */
389   public AnnotationList getAnnotationList() {
390      return getAnnotationList(x -> true);
391   }
392
393   /**
394    * Constructs an {@link AnnotationList} of all matching annotations found on this method.
395    *
396    * <p>
397    * Annotations are appended in the following orders:
398    * <ol>
399    *    <li>On the package of this class.
400    *    <li>On interfaces ordered parent-to-child.
401    *    <li>On parent classes ordered parent-to-child.
402    *    <li>On this class.
403    *    <li>On this method and matching methods ordered parent-to-child.
404    * </ol>
405    *
406    * @param filter A predicate to apply to the entries to determine if value should be added.  Can be <jk>null</jk>.
407    * @return A new {@link AnnotationList} object on every call.
408    */
409   public AnnotationList getAnnotationList(Predicate<AnnotationInfo<?>> filter) {
410      AnnotationList al = new AnnotationList();
411      forEachAnnotationInfo(filter, x -> al.add(x));
412      return al;
413   }
414
415   /**
416    * Same as {@link #getAnnotationList(Predicate)} except only returns annotations defined on methods.
417    *
418    * @param filter A predicate to apply to the entries to determine if value should be added.  Can be <jk>null</jk>.
419    * @return A new {@link AnnotationList} object on every call.
420    */
421   public AnnotationList getAnnotationListMethodOnly(Predicate<AnnotationInfo<?>> filter) {
422      AnnotationList al = new AnnotationList();
423      forEachAnnotationInfoMethodOnly(filter, x -> al.add(x));
424      return al;
425   }
426
427   /**
428    * Perform an action on all matching annotations on this method.
429    *
430    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk>.
431    * @param action An action to perform on the entry.
432    * @return This object.
433    */
434   public MethodInfo forEachAnnotationInfo(Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) {
435      ClassInfo c = this.declaringClass;
436      forEachDeclaredAnnotationInfo(c.getPackage(), filter, action);
437      ClassInfo[] interfaces = c._getInterfaces();
438      for (int i = interfaces.length-1; i >= 0; i--) {
439         forEachDeclaredAnnotationInfo(interfaces[i], filter, action);
440         forEachDeclaredMethodAnnotationInfo(interfaces[i], filter, action);
441      }
442      ClassInfo[] parents = c._getParents();
443      for (int i = parents.length-1; i >= 0; i--) {
444         forEachDeclaredAnnotationInfo(parents[i], filter, action);
445         forEachDeclaredMethodAnnotationInfo(parents[i], filter, action);
446      }
447      return this;
448   }
449
450   private void forEachAnnotationInfoMethodOnly(Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) {
451      ClassInfo c = this.declaringClass;
452      ClassInfo[] interfaces = c._getInterfaces();
453      for (int i = interfaces.length-1; i >= 0; i--)
454         forEachDeclaredMethodAnnotationInfo(interfaces[i], filter, action);
455      ClassInfo[] parents = c._getParents();
456      for (int i = parents.length-1; i >= 0; i--)
457         forEachDeclaredMethodAnnotationInfo(parents[i], filter, action);
458   }
459
460   private void forEachDeclaredAnnotationInfo(Package p, Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) {
461      if (p != null)
462         for (Annotation a : p.getDeclaredAnnotations())
463            AnnotationInfo.of(p, a).accept(filter, action);
464   }
465
466   private void forEachDeclaredAnnotationInfo(ClassInfo ci, Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) {
467      if (ci != null)
468         for (Annotation a : ci._getDeclaredAnnotations())
469            AnnotationInfo.of(ci, a).accept(filter, action);
470   }
471
472   private void forEachDeclaredMethodAnnotationInfo(ClassInfo ci, Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) {
473      MethodInfo m = findMatchingOnClass(ci);
474      if (m != null)
475         for (Annotation a : m._getDeclaredAnnotations())
476            AnnotationInfo.of(m, a).accept(filter, action);
477   }
478
479   //-----------------------------------------------------------------------------------------------------------------
480   // Return type.
481   //-----------------------------------------------------------------------------------------------------------------
482
483   /**
484    * Returns the generic return type of this method as a {@link ClassInfo} object.
485    *
486    * @return The generic return type of this method.
487    */
488   public ClassInfo getReturnType() {
489      if (returnType == null) {
490         synchronized(this) {
491            returnType = ClassInfo.of(m.getReturnType(), m.getGenericReturnType());
492         }
493      }
494      return returnType;
495   }
496
497   /**
498    * Returns <jk>true</jk> if this method has this return type.
499    *
500    * @param c The return type to test for.
501    * @return <jk>true</jk> if this method has this return type.
502    */
503   public boolean hasReturnType(Class<?> c) {
504      return m.getReturnType() == c;
505   }
506
507   /**
508    * Returns <jk>true</jk> if this method has this return type.
509    *
510    * @param ci The return type to test for.
511    * @return <jk>true</jk> if this method has this return type.
512    */
513   public boolean hasReturnType(ClassInfo ci) {
514      return hasReturnType(ci.inner());
515   }
516
517   /**
518    * Returns <jk>true</jk> if this method has this parent return type.
519    *
520    * @param c The return type to test for.
521    * @return <jk>true</jk> if this method has this parent return type.
522    */
523   public boolean hasReturnTypeParent(Class<?> c) {
524      return ClassInfo.of(c).isParentOf(m.getReturnType());
525   }
526
527   /**
528    * Returns <jk>true</jk> if this method has this parent return type.
529    *
530    * @param ci The return type to test for.
531    * @return <jk>true</jk> if this method has this parent return type.
532    */
533   public boolean hasReturnTypeParent(ClassInfo ci) {
534      return hasReturnTypeParent(ci.inner());
535   }
536
537   //-----------------------------------------------------------------------------------------------------------------
538   // Other methods
539   //-----------------------------------------------------------------------------------------------------------------
540
541   /**
542    * Returns <jk>true</jk> if this object passes the specified predicate test.
543    *
544    * @param test The test to perform.
545    * @return <jk>true</jk> if this object passes the specified predicate test.
546    */
547   public boolean matches(Predicate<MethodInfo> test) {
548      return test(test, this);
549   }
550
551   /**
552    * Performs an action on this object if the specified predicate test passes.
553    *
554    * @param test A test to apply to determine if action should be executed.  Can be <jk>null</jk>.
555    * @param action An action to perform on this object.
556    * @return This object.
557    */
558   public MethodInfo accept(Predicate<MethodInfo> test, Consumer<MethodInfo> action) {
559      if (matches(test))
560         action.accept(this);
561      return this;
562   }
563
564   /**
565    * Shortcut for calling the invoke method on the underlying method.
566    *
567    * @param <T> The method return type.
568    * @param obj the object the underlying method is invoked from.
569    * @param args the arguments used for the method call
570    * @return The object returned from the method.
571    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
572    */
573   @SuppressWarnings("unchecked")
574   public <T> T invoke(Object obj, Object...args) throws ExecutableException {
575      try {
576         return (T)m.invoke(obj, args);
577      } catch (IllegalAccessException e) {
578         throw new ExecutableException(e);
579      } catch (InvocationTargetException e) {
580         throw new ExecutableException(e.getTargetException());
581      }
582   }
583
584   /**
585    * Invokes the specified method using fuzzy-arg matching.
586    *
587    * <p>
588    * Arguments will be matched to the parameters based on the parameter types.
589    * <br>Arguments can be in any order.
590    * <br>Extra arguments will be ignored.
591    * <br>Missing arguments will be left <jk>null</jk>.
592    *
593    * <p>
594    * Note that this only works for methods that have distinguishable argument types.
595    * <br>It's not going to work on methods with generic argument types like <c>Object</c>
596    *
597    * @param pojo
598    *    The POJO the method is being called on.
599    *    <br>Can be <jk>null</jk> for static methods.
600    * @param args
601    *    The arguments to pass to the method.
602    * @return
603    *    The results of the method invocation.
604    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
605    */
606   public Object invokeFuzzy(Object pojo, Object...args) throws ExecutableException {
607      try {
608         return m.invoke(pojo, ClassUtils.getMatchingArgs(m.getParameterTypes(), args));
609      } catch (IllegalAccessException | InvocationTargetException e) {
610         throw new ExecutableException(e);
611      }
612   }
613
614   /**
615    * Returns the signature of this method.
616    *
617    * <p>
618    * For no-arg methods, the signature will be a simple string such as <js>"toString"</js>.
619    * For methods with one or more args, the arguments will be fully-qualified class names (e.g.
620    * <js>"append(java.util.StringBuilder,boolean)"</js>)
621    *
622    * @return The methods signature.
623    */
624   public String getSignature() {
625      StringBuilder sb = new StringBuilder(128);
626      sb.append(m.getName());
627      Class<?>[] pt = _getRawParamTypes();
628      if (pt.length > 0) {
629         sb.append('(');
630         List<ParamInfo> mpi = getParams();
631         for (int i = 0; i < pt.length; i++) {
632            if (i > 0)
633               sb.append(',');
634            mpi.get(i).getParameterType().appendFullName(sb);
635         }
636         sb.append(')');
637      }
638      return sb.toString();
639   }
640
641   /**
642    * Returns the bean property name if this is a getter or setter.
643    *
644    * @return The bean property name, or <jk>null</jk> if this isn't a getter or setter.
645    */
646   public String getPropertyName() {
647      String n = m.getName();
648      if ((n.startsWith("get") || n.startsWith("set")) && n.length() > 3)
649         return Introspector.decapitalize(n.substring(3));
650      if (n.startsWith("is") && n.length() > 2)
651         return Introspector.decapitalize(n.substring(2));
652      return n;
653   }
654
655   /**
656    * Returns <jk>true</jk> if the parameters on the method only consist of the types specified in the list.
657    *
658    * <h5 class='figure'>Example:</h5>
659    * <p class='bjava'>
660    *
661    *    <jc>// Example method:</jc>
662    *    <jk>public void</jk> foo(String <jv>bar</jv>, Integer <jv>baz</jv>);
663    *
664    *    argsOnlyOfType(<jv>fooMethod</jv>, String.<jk>class</jk>, Integer.<jk>class</jk>);  <jc>// True.</jc>
665    *    argsOnlyOfType(<jv>fooMethod</jv>, String.<jk>class</jk>, Integer.<jk>class</jk>, Map.<jk>class</jk>);  <jc>// True.</jc>
666    *    argsOnlyOfType(<jv>fooMethod</jv>, String.<jk>class</jk>);  <jc>// False.</jc>
667    * </p>
668    *
669    * @param args The valid class types (exact) for the arguments.
670    * @return <jk>true</jk> if the method parameters only consist of the types specified in the list.
671    */
672   public boolean argsOnlyOfType(Class<?>...args) {
673      for (Class<?> c1 : _getRawParamTypes()) {
674         boolean foundMatch = false;
675         for (Class<?> c2 : args)
676            if (c1 == c2)
677               foundMatch = true;
678         if (! foundMatch)
679            return false;
680      }
681      return true;
682   }
683
684   /**
685    * Returns <jk>true</jk> if this method has at least the specified parameters.
686    *
687    * <p>
688    * Method may or may not have additional parameters besides those specified.
689    *
690    * @param requiredParams The parameter types to check for.
691    * @return <jk>true</jk> if this method has at least the specified parameters.
692    */
693   public boolean hasAllArgs(Class<?>...requiredParams) {
694      List<Class<?>> rawParamTypes = getRawParamTypes();
695
696      for (Class<?> c : requiredParams)
697         if (! rawParamTypes.contains(c))
698            return false;
699
700      return true;
701   }
702
703   /**
704    * Returns <jk>true</jk> if this method has the specified parameter.
705    *
706    * <p>
707    * Method may or may not have additional parameters besides the one specified.
708    *
709    * @param requiredParam The parameter type to check for.
710    * @return <jk>true</jk> if this method has at least the specified parameter.
711    */
712   public boolean hasArg(Class<?> requiredParam) {
713      return hasAllArgs(requiredParam);
714   }
715
716   /**
717    * Returns <jk>true</jk> if this method is a bridge method.
718    *
719    * @return <jk>true</jk> if this method is a bridge method.
720    */
721   public boolean isBridge() {
722      return m.isBridge();
723   }
724
725   /**
726    * Returns the name of this method.
727    *
728    * @return The name of this method
729    */
730   public String getName() {
731      return m.getName();
732   }
733
734   @Override
735   public int compareTo(MethodInfo o) {
736      int i = getSimpleName().compareTo(o.getSimpleName());
737      if (i == 0) {
738         i = _getRawParamTypes().length - o._getRawParamTypes().length;
739         if (i == 0) {
740            for (int j = 0; j < _getRawParamTypes().length && i == 0; j++) {
741               i = _getRawParamTypes()[j].getName().compareTo(o._getRawParamTypes()[j].getName());
742            }
743         }
744      }
745      return i;
746   }
747
748   // <FluentSetters>
749
750   @Override /* GENERATED - org.apache.juneau.reflect.ExecutableInfo */
751   public MethodInfo accessible() {
752      super.accessible();
753      return this;
754   }
755
756   // </FluentSetters>
757}