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