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 ParamInfo[] params;
034   private 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      return new UnmodifiableArray<>(_getParams());
136   }
137
138   /**
139    * Returns parameter information at the specified index.
140    *
141    * @param index The parameter index.
142    * @return The parameter information, never <jk>null</jk>.
143    */
144   public final ParamInfo getParam(int index) {
145      checkIndex(index);
146      return _getParams()[index];
147   }
148
149   /**
150    * Returns the parameter types on this executable.
151    *
152    * @return The parameter types on this executable.
153    */
154   public final List<ClassInfo> getParamTypes() {
155      return new UnmodifiableArray<>(_getParamTypes());
156   }
157
158   /**
159    * Returns the parameter type of the parameter at the specified index.
160    *
161    * @param index The parameter index.
162    * @return The parameter type of the parameter at the specified index.
163    */
164   public final ClassInfo getParamType(int index) {
165      checkIndex(index);
166      return _getParamTypes()[index];
167   }
168
169   /**
170    * Returns the raw parameter types on this executable.
171    *
172    * @return The raw parameter types on this executable.
173    */
174   public final List<Class<?>> getRawParamTypes() {
175      return new UnmodifiableArray<>(_getRawParamTypes());
176   }
177
178   /**
179    * Returns the raw parameter type of the parameter at the specified index.
180    *
181    * @param index The parameter index.
182    * @return The raw parameter type of the parameter at the specified index.
183    */
184   public final Class<?> getRawParamType(int index) {
185      checkIndex(index);
186      return _getRawParamTypes()[index];
187   }
188
189   /**
190    * Returns the raw generic parameter types on this executable.
191    *
192    * @return The raw generic parameter types on this executable.
193    */
194   public final List<Type> getRawGenericParamTypes() {
195      return new UnmodifiableArray<>(_getRawGenericParamTypes());
196   }
197
198   /**
199    * Returns the raw generic parameter type of the parameter at the specified index.
200    *
201    * @param index The parameter index.
202    * @return The raw generic parameter type of the parameter at the specified index.
203    */
204   public final Type getRawGenericParamType(int index) {
205      checkIndex(index);
206      return _getRawGenericParamTypes()[index];
207   }
208
209   /**
210    * Returns an array of raw {@link Parameter} objects that represent all the parameters to the underlying executable represented by this object.
211    *
212    * @return An array of raw {@link Parameter} objects, or an empty array if executable has no parameters.
213    * @see Executable#getParameters()
214    */
215   public final List<Parameter> getRawParameters() {
216      return new UnmodifiableArray<>(_getRawParameters());
217   }
218
219   /**
220    * Returns the raw {@link Parameter} object that represents the parameter at the specified index.
221    *
222    * @param index The parameter index.
223    * @return The raw {@link Parameter} object that represents the parameter at the specified index.
224    * @see Executable#getParameters()
225    */
226   public final Parameter getRawParameter(int index) {
227      checkIndex(index);
228      return _getRawParameters()[index];
229   }
230
231   private ParamInfo[] _getParams() {
232      if (params == null) {
233         Parameter[] rp = _getRawParameters();
234         ParamInfo[] l = new ParamInfo[rp.length];
235         for (int i = 0; i < rp.length; i++)
236            l[i] = new ParamInfo(this, rp[i], i);
237         params = l;
238      }
239      return params;
240   }
241
242   private ClassInfo[] _getParamTypes() {
243      if (paramTypes == null) {
244         Class<?>[] ptc = _getRawParamTypes();
245         // Note that due to a bug involving Enum constructors, getGenericParameterTypes() may
246         // always return an empty array.  This appears to be fixed in Java 8 b75.
247         Type[] ptt = _getRawGenericParamTypes();
248         if (ptt.length != ptc.length)
249            ptt = ptc;
250         ClassInfo[] l = new ClassInfo[ptc.length];
251         for (int i = 0; i < ptc.length; i++)
252            l[i] = ClassInfo.of(ptc[i], ptt[i]);
253         paramTypes = l;
254      }
255      return paramTypes;
256   }
257
258   Class<?>[] _getRawParamTypes() {
259      if (rawParamTypes == null)
260         rawParamTypes = e.getParameterTypes();
261      return rawParamTypes;
262   }
263
264   private Type[] _getRawGenericParamTypes() {
265      if (rawGenericParamTypes == null)
266         rawGenericParamTypes = re.getGenericParameterTypes();
267      return rawGenericParamTypes;
268   }
269
270   private Parameter[] _getRawParameters() {
271      if (rawParameters == null)
272         rawParameters = re.getParameters();
273      return rawParameters;
274   }
275
276   private void checkIndex(int index) {
277      int pc = getParamCount();
278      if (pc == 0)
279         throw new IndexOutOfBoundsException(format("Invalid index ''{0}''.  No parameters.", index));
280      if (index < 0 || index >= pc)
281         throw new IndexOutOfBoundsException(format("Invalid index ''{0}''.  Parameter count: {1}", index, pc));
282   }
283
284   //-----------------------------------------------------------------------------------------------------------------
285   // Annotations
286   //-----------------------------------------------------------------------------------------------------------------
287
288   /**
289    * Returns the parameter annotations on this executable.
290    *
291    * @return The parameter annotations on this executable.
292    */
293   public final Annotation[][] getParameterAnnotations() {
294      return e.getParameterAnnotations();
295   }
296
297   /**
298    * Returns the parameter annotations on the parameter at the specified index.
299    *
300    * @param index The parameter index.
301    * @return The parameter annotations on the parameter at the specified index.
302    */
303   public final Annotation[] getParameterAnnotations(int index) {
304      checkIndex(index);
305      return e.getParameterAnnotations()[index];
306   }
307
308   //-----------------------------------------------------------------------------------------------------------------
309   // Exceptions
310   //-----------------------------------------------------------------------------------------------------------------
311
312   /**
313    * Returns the exception types on this executable.
314    *
315    * @return The exception types on this executable.
316    */
317   public final List<ClassInfo> getExceptionTypes() {
318      return new UnmodifiableArray<>(_getExceptionTypes());
319   }
320
321   /**
322    * Returns the raw exception types on this executable.
323    *
324    * @return The raw exception types on this executable.
325    */
326   public final Class<?>[] getRawExceptionTypes() {
327      return _getRawExceptionTypes().clone();
328   }
329
330   private ClassInfo[] _getExceptionTypes() {
331      if (exceptionInfos == null) {
332         Class<?>[] exceptionTypes = _getRawExceptionTypes();
333         ClassInfo[] l = new ClassInfo[exceptionTypes.length];
334         for (int i = 0; i < exceptionTypes.length; i++)
335            l[i] = ClassInfo.of(exceptionTypes[i]);
336         exceptionInfos = l;
337      }
338      return exceptionInfos;
339   }
340
341   private Class<?>[] _getRawExceptionTypes() {
342      if (rawExceptionTypes == null)
343         rawExceptionTypes = e.getExceptionTypes();
344      return rawExceptionTypes;
345   }
346
347   //-----------------------------------------------------------------------------------------------------------------
348   // Characteristics
349   //-----------------------------------------------------------------------------------------------------------------
350
351   /**
352    * Returns <jk>true</jk> if all specified flags are applicable to this method.
353    *
354    * @param flags The flags to test for.
355    * @return <jk>true</jk> if all specified flags are applicable to this method.
356    */
357   public final boolean isAll(ReflectFlags...flags) {
358      for (ReflectFlags f : flags) {
359         switch (f) {
360            case DEPRECATED:
361               if (isNotDeprecated())
362                  return false;
363               break;
364            case NOT_DEPRECATED:
365               if (isDeprecated())
366                  return false;
367               break;
368            case HAS_PARAMS:
369               if (hasNoParams())
370                  return false;
371               break;
372            case HAS_NO_PARAMS:
373               if (hasParams())
374                  return false;
375               break;
376            case PUBLIC:
377               if (isNotPublic())
378                  return false;
379               break;
380            case NOT_PUBLIC:
381               if (isPublic())
382                  return false;
383               break;
384            case STATIC:
385               if (isNotStatic())
386                  return false;
387               break;
388            case NOT_STATIC:
389               if (isStatic())
390                  return false;
391               break;
392            case ABSTRACT:
393               if (isNotAbstract())
394                  return false;
395               break;
396            case NOT_ABSTRACT:
397               if (isAbstract())
398                  return false;
399               break;
400            default:
401               throw new RuntimeException("Invalid flag for executable: " + f);
402         }
403      }
404      return true;
405   }
406
407   /**
408    * Returns <jk>true</jk> if all specified flags are applicable to this method.
409    *
410    * @param flags The flags to test for.
411    * @return <jk>true</jk> if all specified flags are applicable to this method.
412    */
413   public final boolean isAny(ReflectFlags...flags) {
414      for (ReflectFlags f : flags) {
415         switch (f) {
416            case DEPRECATED:
417               if (isDeprecated())
418                  return true;
419               break;
420            case NOT_DEPRECATED:
421               if (isNotDeprecated())
422                  return true;
423               break;
424            case HAS_PARAMS:
425               if (hasParams())
426                  return true;
427               break;
428            case HAS_NO_PARAMS:
429               if (hasNoParams())
430                  return true;
431               break;
432            case PUBLIC:
433               if (isPublic())
434                  return true;
435               break;
436            case NOT_PUBLIC:
437               if (isNotPublic())
438                  return true;
439               break;
440            case STATIC:
441               if (isStatic())
442                  return true;
443               break;
444            case NOT_STATIC:
445               if (isNotStatic())
446                  return true;
447               break;
448            case ABSTRACT:
449               if (isAbstract())
450                  return true;
451               break;
452            case NOT_ABSTRACT:
453               if (isNotAbstract())
454                  return true;
455               break;
456            default:
457               throw new RuntimeException("Invalid flag for executable: " + f);
458         }
459      }
460      return false;
461   }
462
463   /**
464    * Returns <jk>true</jk> if this method has the specified arguments.
465    *
466    * @param args The arguments to test for.
467    * @return <jk>true</jk> if this method has this arguments in the exact order.
468    */
469   public final boolean hasParamTypes(Class<?>...args) {
470      Class<?>[] pt = _getRawParamTypes();
471      if (pt.length == args.length) {
472         for (int i = 0; i < pt.length; i++)
473            if (! pt[i].equals(args[i]))
474               return false;
475         return true;
476      }
477      return false;
478   }
479
480   /**
481    * Returns <jk>true</jk> if this method has the specified arguments.
482    *
483    * @param args The arguments to test for.
484    * @return <jk>true</jk> if this method has this arguments in the exact order.
485    */
486   public final boolean hasParamTypes(ClassInfo...args) {
487      Class<?>[] pt = _getRawParamTypes();
488      if (pt.length == args.length) {
489         for (int i = 0; i < pt.length; i++)
490            if (! pt[i].equals(args[i].inner()))
491               return false;
492         return true;
493      }
494      return false;
495   }
496
497   /**
498    * Returns <jk>true</jk> if this method has the specified argument parent classes.
499    *
500    * @param args The arguments to test for.
501    * @return <jk>true</jk> if this method has this arguments in the exact order.
502    */
503   public final boolean hasMatchingParamTypes(Class<?>...args) {
504      ClassInfo[] pt = _getParamTypes();
505      if (pt.length != args.length)
506         return false;
507      for (int i = 0; i < pt.length; i++) {
508         boolean matched = false;
509         for (int j = 0; j < args.length; j++)
510            if (pt[i].isParentOfFuzzyPrimitives(args[j]))
511               matched = true;
512         if (! matched)
513            return false;
514      }
515      return true;
516   }
517
518   /**
519    * Returns <jk>true</jk> if this method has the specified argument parent classes.
520    *
521    * @param args The arguments to test for.
522    * @return <jk>true</jk> if this method has this arguments in the exact order.
523    */
524   public final boolean hasMatchingParamTypes(ClassInfo...args) {
525      ClassInfo[] pt = _getParamTypes();
526      if (pt.length != args.length)
527         return false;
528      for (int i = 0; i < pt.length; i++) {
529         boolean matched = false;
530         for (int j = 0; j < args.length; j++)
531            if (pt[i].isParentOfFuzzyPrimitives(args[j].inner()))
532               matched = true;
533         if (! matched)
534            return false;
535      }
536      return true;
537   }
538
539   /**
540    * Returns <jk>true</jk> if this method has at most only this arguments in any order.
541    *
542    * @param args The arguments to test for.
543    * @return <jk>true</jk> if this method has at most only this arguments in any order.
544    */
545   public final boolean hasFuzzyParamTypes(Class<?>...args) {
546      return fuzzyArgsMatch(args) != -1;
547   }
548
549   /**
550    * Returns how well this method matches the specified arg types.
551    *
552    * <p>
553    * The number returned is the number of method arguments that match the passed in arg types.
554    * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
555    *
556    * @param argTypes The arg types to check against.
557    * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
558    */
559   public int fuzzyArgsMatch(Class<?>... argTypes) {
560      int matches = 0;
561      outer: for (ClassInfo pi : getParamTypes()) {
562         for (Class<?> a : argTypes) {
563            if (pi.isParentOfFuzzyPrimitives(a)) {
564               matches++;
565               continue outer;
566            }
567         }
568         return -1;
569      }
570      return matches;
571   }
572
573
574   /**
575    * Returns <jk>true</jk> if this method has at most only this arguments in any order.
576    *
577    * @param args The arguments to test for.
578    * @return <jk>true</jk> if this method has at most only this arguments in any order.
579    */
580   public boolean hasFuzzyParamTypes(ClassInfo...args) {
581      return fuzzyArgsMatch(args) != -1;
582   }
583
584   /**
585    * Returns how well this method matches the specified arg types.
586    *
587    * <p>
588    * The number returned is the number of method arguments that match the passed in arg types.
589    * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
590    *
591    * @param argTypes The arg types to check against.
592    * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
593    */
594   public int fuzzyArgsMatch(ClassInfo... argTypes) {
595      int matches = 0;
596      outer: for (ClassInfo pi : getParamTypes()) {
597         for (ClassInfo a : argTypes) {
598            if (pi.isParentOfFuzzyPrimitives(a)) {
599               matches++;
600               continue outer;
601            }
602         }
603         return -1;
604      }
605      return matches;
606   }
607
608   /**
609    * Returns <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
610    *
611    * @return <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
612    */
613   public final boolean isDeprecated() {
614      return e.isAnnotationPresent(Deprecated.class);
615
616   }
617
618   /**
619    * Returns <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
620    *
621    * @return <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
622    */
623   public final boolean isNotDeprecated() {
624      return ! e.isAnnotationPresent(Deprecated.class);
625
626   }
627
628   /**
629    * Returns <jk>true</jk> if this method is abstract.
630    *
631    * @return <jk>true</jk> if this method is abstract.
632    */
633   public final boolean isAbstract() {
634      return Modifier.isAbstract(e.getModifiers());
635   }
636
637   /**
638    * Returns <jk>true</jk> if this method is not abstract.
639    *
640    * @return <jk>true</jk> if this method is not abstract.
641    */
642   public final boolean isNotAbstract() {
643      return ! Modifier.isAbstract(e.getModifiers());
644   }
645
646   /**
647    * Returns <jk>true</jk> if this method is public.
648    *
649    * @return <jk>true</jk> if this method is public.
650    */
651   public final boolean isPublic() {
652      return Modifier.isPublic(e.getModifiers());
653   }
654
655   /**
656    * Returns <jk>true</jk> if this method is not public.
657    *
658    * @return <jk>true</jk> if this method is not public.
659    */
660   public final boolean isNotPublic() {
661      return ! Modifier.isPublic(e.getModifiers());
662   }
663
664   /**
665    * Returns <jk>true</jk> if this method is static.
666    *
667    * @return <jk>true</jk> if this method is static.
668    */
669   public final boolean isStatic() {
670      return Modifier.isStatic(e.getModifiers());
671   }
672
673   /**
674    * Returns <jk>true</jk> if this method is not static.
675    *
676    * @return <jk>true</jk> if this method is not static.
677    */
678   public final boolean isNotStatic() {
679      return ! Modifier.isStatic(e.getModifiers());
680   }
681
682   //-----------------------------------------------------------------------------------------------------------------
683   // Visibility
684   //-----------------------------------------------------------------------------------------------------------------
685
686   /**
687    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
688    *
689    * @return This object (for method chaining).
690    */
691   @FluentSetter
692   public ExecutableInfo accessible() {
693      setAccessible();
694      return this;
695   }
696
697   /**
698    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
699    *
700    * @return <jk>true</jk> if call was successful.
701    */
702   public final boolean setAccessible() {
703      try {
704         if (! (e.isAccessible()))
705            e.setAccessible(true);
706         return true;
707      } catch (SecurityException e) {
708         return false;
709      }
710   }
711
712   /**
713    * Identifies if the specified visibility matches this method.
714    *
715    * @param v The visibility to validate against.
716    * @return <jk>true</jk> if this visibility matches the modifier attribute of this method.
717    */
718   public final boolean isVisible(Visibility v) {
719      return v.isVisible(e);
720   }
721
722   //-----------------------------------------------------------------------------------------------------------------
723   // Labels
724   //-----------------------------------------------------------------------------------------------------------------
725
726   /**
727    * Returns <jk>true</jk> if this method has this name.
728    *
729    * @param name The name to test for.
730    * @return <jk>true</jk> if this method has this name.
731    */
732   public final boolean hasName(String name) {
733      return getSimpleName().equals(name);
734   }
735
736   /**
737    * Returns <jk>true</jk> if this method has a name in the specified list.
738    *
739    * @param names The names to test for.
740    * @return <jk>true</jk> if this method has one of the names.
741    */
742   public final boolean hasName(String...names) {
743      for (String n : names)
744         if (getSimpleName().equals(n))
745            return true;
746      return false;
747   }
748
749   /**
750    * Returns <jk>true</jk> if this method has a name in the specified set.
751    *
752    * @param names The names to test for.
753    * @return <jk>true</jk> if this method has one of the names.
754    */
755   public final boolean hasName(Set<String> names) {
756      return names.contains(getSimpleName());
757   }
758
759   //-----------------------------------------------------------------------------------------------------------------
760   // Labels
761   //-----------------------------------------------------------------------------------------------------------------
762
763   /**
764    * Returns the full name of this executable.
765    *
766    * <h5 class='section'>Examples:</h5>
767    * <ul>
768    *    <li><js>"com.foo.MyClass.get(java.util.String)"</js> - Method.
769    *    <li><js>"com.foo.MyClass(java.util.String)"</js> - Constructor.
770    * </ul>
771    *
772    * @return The underlying executable name.
773    */
774   public final String getFullName() {
775      StringBuilder sb = new StringBuilder(128);
776      ClassInfo dc = declaringClass;
777      Package p = dc.getPackage();
778      if (p != null)
779         sb.append(p.getName()).append('.');
780      dc.appendShortName(sb);
781      if (! isConstructor)
782         sb.append('.').append(getSimpleName());
783      sb.append('(');
784      List<ClassInfo> pt = getParamTypes();
785      for (int i = 0; i < pt.size(); i++) {
786         if (i > 0)
787            sb.append(',');
788         pt.get(i).appendFullName(sb);
789      }
790      sb.append(')');
791      return sb.toString();
792   }
793
794   /**
795    * Returns the short name of this executable.
796    *
797    * <h5 class='section'>Examples:</h5>
798    * <ul>
799    *    <li><js>"MyClass.get(String)"</js> - Method.
800    *    <li><js>"MyClass(String)"</js> - Constructor.
801    * </ul>
802    *
803    * @return The underlying executable name.
804    */
805   public final String getShortName() {
806      StringBuilder sb = new StringBuilder(64);
807      sb.append(getSimpleName()).append('(');
808      Class<?>[] pt = _getRawParamTypes();
809      for (int i = 0; i < pt.length; i++) {
810         if (i > 0)
811            sb.append(',');
812         sb.append(pt[i].getSimpleName());
813      }
814      sb.append(')');
815      return sb.toString();
816   }
817
818   /**
819    * Returns the simple name of the underlying method.
820    *
821    * @return The simple name of the underlying method;
822    */
823   public final String getSimpleName() {
824      return isConstructor ? e.getDeclaringClass().getSimpleName() : e.getName();
825   }
826
827   @Override
828   public String toString() {
829      return getShortName();
830   }
831
832   // <FluentSetters>
833
834   // </FluentSetters>
835}