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.utils;
014
015import static java.lang.Character.*;
016import static org.apache.juneau.collections.JsonMap.*;
017import static org.apache.juneau.common.internal.StringUtils.*;
018import static org.apache.juneau.internal.CollectionUtils.*;
019
020import java.lang.reflect.*;
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.*;
025
026/**
027 * Allows arbitrary objects to be mapped to classes and methods base on class/method name keys.
028 *
029 * <p>
030 * The valid pattern matches are:
031 * <ul class='spaced-list'>
032 *  <li>Classes:
033 *       <ul>
034 *          <li>Fully qualified:
035 *             <ul>
036 *                <li><js>"com.foo.MyClass"</js>
037 *             </ul>
038 *          <li>Fully qualified inner class:
039 *             <ul>
040 *                <li><js>"com.foo.MyClass$Inner1$Inner2"</js>
041 *             </ul>
042 *          <li>Simple:
043 *             <ul>
044 *                <li><js>"MyClass"</js>
045 *             </ul>
046 *          <li>Simple inner:
047 *             <ul>
048 *                <li><js>"MyClass$Inner1$Inner2"</js>
049 *                <li><js>"Inner1$Inner2"</js>
050 *                <li><js>"Inner2"</js>
051 *             </ul>
052 *       </ul>
053 *    <li>Methods:
054 *       <ul>
055 *          <li>Fully qualified with args:
056 *             <ul>
057 *                <li><js>"com.foo.MyClass.myMethod(String,int)"</js>
058 *                <li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js>
059 *                <li><js>"com.foo.MyClass.myMethod()"</js>
060 *             </ul>
061 *          <li>Fully qualified:
062 *             <ul>
063 *                <li><js>"com.foo.MyClass.myMethod"</js>
064 *             </ul>
065 *          <li>Simple with args:
066 *             <ul>
067 *                <li><js>"MyClass.myMethod(String,int)"</js>
068 *                <li><js>"MyClass.myMethod(java.lang.String,int)"</js>
069 *                <li><js>"MyClass.myMethod()"</js>
070 *             </ul>
071 *          <li>Simple:
072 *             <ul>
073 *                <li><js>"MyClass.myMethod"</js>
074 *             </ul>
075 *          <li>Simple inner class:
076 *             <ul>
077 *                <li><js>"MyClass$Inner1$Inner2.myMethod"</js>
078 *                <li><js>"Inner1$Inner2.myMethod"</js>
079 *                <li><js>"Inner2.myMethod"</js>
080 *             </ul>
081 *       </ul>
082 *    <li>Fields:
083 *       <ul>
084 *          <li>Fully qualified:
085 *             <ul>
086 *                <li><js>"com.foo.MyClass.myField"</js>
087 *             </ul>
088 *          <li>Simple:
089 *             <ul>
090 *                <li><js>"MyClass.myField"</js>
091 *             </ul>
092 *          <li>Simple inner class:
093 *             <ul>
094 *                <li><js>"MyClass$Inner1$Inner2.myField"</js>
095 *                <li><js>"Inner1$Inner2.myField"</js>
096 *                <li><js>"Inner2.myField"</js>
097 *             </ul>
098 *       </ul>
099 *    <li>Constructors:
100 *       <ul>
101 *          <li>Fully qualified with args:
102 *             <ul>
103 *                <li><js>"com.foo.MyClass(String,int)"</js>
104 *                <li><js>"com.foo.MyClass(java.lang.String,int)"</js>
105 *                <li><js>"com.foo.MyClass()"</js>
106 *             </ul>
107 *          <li>Simple with args:
108 *             <ul>
109 *                <li><js>"MyClass(String,int)"</js>
110 *                <li><js>"MyClass(java.lang.String,int)"</js>
111 *                <li><js>"MyClass()"</js>
112 *             </ul>
113 *          <li>Simple inner class:
114 *             <ul>
115 *                <li><js>"MyClass$Inner1$Inner2()"</js>
116 *                <li><js>"Inner1$Inner2()"</js>
117 *                <li><js>"Inner2()"</js>
118 *             </ul>
119 *       </ul>
120 *    <li>A comma-delimited list of anything on this list.
121 * </ul>
122 *
123 * <h5 class='section'>See Also:</h5><ul>
124 * </ul>
125 *
126 * @param <V> The type of object in this map.
127 */
128public class ReflectionMap<V> {
129
130   //-----------------------------------------------------------------------------------------------------------------
131   // Static
132   //-----------------------------------------------------------------------------------------------------------------
133
134   /**
135    * Static builder creator.
136    *
137    * @param <V> The type of object in this map.
138    * @param c The type of object in this map.
139    * @return A new instance of this object.
140    */
141   public static <V> Builder<V> create(Class<V> c) {
142      return new Builder<>();
143   }
144
145   //-----------------------------------------------------------------------------------------------------------------
146   // Builder
147   //-----------------------------------------------------------------------------------------------------------------
148
149   /**
150    * Builder class.
151    * @param <V> The type of object in this map.
152    */
153   public static class Builder<V> {
154      final List<ClassEntry<V>> classEntries;
155      final List<MethodEntry<V>> methodEntries;
156      final List<FieldEntry<V>> fieldEntries;
157      final List<ConstructorEntry<V>> constructorEntries;
158
159      /**
160       * Constructor.
161       */
162      protected Builder() {
163         classEntries = list();
164         methodEntries = list();
165         fieldEntries = list();
166         constructorEntries = list();
167      }
168
169      /**
170       * Copy constructor.
171       *
172       * @param copyFrom The builder being copied.
173       */
174      protected Builder(Builder<V> copyFrom) {
175         classEntries = copyOf(copyFrom.classEntries);
176         methodEntries = copyOf(copyFrom.methodEntries);
177         fieldEntries = copyOf(copyFrom.fieldEntries);
178         constructorEntries = copyOf(copyFrom.constructorEntries);
179      }
180
181      /**
182       * Adds a mapping to this builder.
183       *
184       * @param key
185       *    The mapping key.
186       *    <br>Can be any of the following:
187       *    <ul>
188       *       <li>Full class name (e.g. <js>"com.foo.MyClass"</js>).
189       *       <li>Simple class name (e.g. <js>"MyClass"</js>).
190       *       <li>All classes (e.g. <js>"*"</js>).
191       *       <li>Full method name (e.g. <js>"com.foo.MyClass.myMethod"</js>).
192       *       <li>Simple method name (e.g. <js>"MyClass.myMethod"</js>).
193       *       <li>A comma-delimited list of anything on this list.
194       *    </ul>
195       * @param value The value for this mapping.
196       * @return This object.
197       */
198      public Builder<V> append(String key, V value) {
199         if (isEmpty(key))
200            throw new BasicRuntimeException("Invalid reflection signature: [{0}]", key);
201         try {
202            splitNames(key, k -> {
203               if (k.endsWith(")")) {
204                  int i = k.substring(0, k.indexOf('(')).lastIndexOf('.');
205                  if (i == -1 || isUpperCase(k.charAt(i+1))) {
206                     constructorEntries.add(new ConstructorEntry<>(k, value));
207                  } else {
208                     methodEntries.add(new MethodEntry<>(k, value));
209                  }
210               } else {
211                  int i = k.lastIndexOf('.');
212                  if (i == -1) {
213                     classEntries.add(new ClassEntry<>(k, value));
214                  } else if (isUpperCase(k.charAt(i+1))) {
215                     classEntries.add(new ClassEntry<>(k, value));
216                     fieldEntries.add(new FieldEntry<>(k, value));
217                  } else {
218                     methodEntries.add(new MethodEntry<>(k, value));
219                     fieldEntries.add(new FieldEntry<>(k, value));
220                  }
221               }
222            });
223         } catch (IndexOutOfBoundsException e) {
224            throw new BasicRuntimeException("Invalid reflection signature: [{0}]", key);
225         }
226
227         return this;
228      }
229
230      /**
231       * Create new instance of {@link ReflectionMap} based on the contents of this builder.
232       *
233       * @return A new {@link ReflectionMap} object.
234       */
235      public ReflectionMap<V> build() {
236         return new ReflectionMap<>(this);
237      }
238
239      /**
240       * Creates a copy of this builder.
241       *
242       * @return A copy of this builder.
243       */
244      public Builder<V> copy() {
245         return new Builder<>(this);
246      }
247   }
248
249   //-----------------------------------------------------------------------------------------------------------------
250   // Instance
251   //-----------------------------------------------------------------------------------------------------------------
252
253   final ClassEntry<V>[] classEntries;
254   final MethodEntry<V>[] methodEntries;
255   final FieldEntry<V>[] fieldEntries;
256   final ConstructorEntry<V>[] constructorEntries;
257
258   /**
259    * Constructor.
260    *
261    * @param b Initializer object.
262    */
263   protected ReflectionMap(Builder<V> b) {
264      this.classEntries = b.classEntries.toArray(new ClassEntry[b.classEntries.size()]);
265      this.methodEntries = b.methodEntries.toArray(new MethodEntry[b.methodEntries.size()]);
266      this.fieldEntries = b.fieldEntries.toArray(new FieldEntry[b.fieldEntries.size()]);
267      this.constructorEntries = b.constructorEntries.toArray(new ConstructorEntry[b.constructorEntries.size()]);
268   }
269
270   static void splitNames(String key, Consumer<String> consumer) {
271      if (key.indexOf(',') == -1) {
272         consumer.accept(key);
273      } else {
274         int m = 0;
275         boolean escaped = false;
276         for (int i = 0; i < key.length(); i++) {
277            char c = key.charAt(i);
278            if (c == '(')
279               escaped = true;
280            else if (c == ')')
281               escaped = false;
282            else if (c == ',' && ! escaped) {
283               consumer.accept(key.substring(m, i).trim());
284               m = i+1;
285            }
286         }
287         consumer.accept(key.substring(m).trim());
288      }
289   }
290
291   /**
292    * Finds first value in this map that matches the specified class.
293    *
294    * @param c The class to test for.
295    * @param ofType Only return objects of the specified type.
296    * @return The matching object.  Never <jk>null</jk>.
297    */
298   public Optional<V> find(Class<?> c, Class<? extends V> ofType) {
299      for (ClassEntry<V> e : classEntries)
300         if (e.matches(c))
301            if (ofType == null || ofType.isInstance(e.value))
302               return optional(e.value);
303      return empty();
304   }
305
306   /**
307    * Finds first value in this map that matches the specified class.
308    *
309    * @param c The class to test for.
310    * @return The matching object.  Never <jk>null</jk>.
311    */
312   public Optional<V> find(Class<?> c) {
313      return find(c, null);
314   }
315
316   /**
317    * Finds all values in this map that matches the specified class.
318    *
319    * @param c The class to test for.
320    * @param ofType Only return objects of the specified type.
321    * @return A modifiable list of matching values.  Never <jk>null</jk>.
322    */
323   public List<V> findAll(Class<?> c, Class<? extends V> ofType) {
324      List<V> list = null;
325      for (ClassEntry<V> e : classEntries)
326         if (e.matches(c) && e.value != null)
327            if (ofType == null || ofType.isInstance(e.value))
328               list = lazyAdd(list, e.value);
329      return lazyList(list);
330   }
331
332   /**
333    * Finds all values in this map that matches the specified class.
334    *
335    * @param c The class to test for.
336    * @return A modifiable list of matching values.  Never <jk>null</jk>.
337    */
338   public List<V> findAll(Class<?> c) {
339      List<V> list = null;
340      for (ClassEntry<V> e : classEntries)
341         if (e.matches(c) && e.value != null)
342            list = lazyAdd(list, e.value);
343      return lazyList(list);
344   }
345
346   /**
347    * Finds all values in this map that matches the specified class.
348    *
349    * @param c The class to test for.
350    * @param ofType Only return objects of the specified type.
351    * @param array The array to append values to.
352    * @return The same list passed in or a new modifiable list if <jk>null</jk>.
353    */
354   public V[] appendAll(Class<?> c, Class<? extends V> ofType, V[] array) {
355      List<V> list = null;
356      for (ClassEntry<V> e : classEntries)
357         if (e.matches(c) && e.value != null)
358            if (ofType == null || ofType.isInstance(e.value))
359               list = lazyAdd(array, list, e.value);
360      return lazyArray(array, list);
361   }
362
363   private static <V> List<V> lazyAdd(List<V> list, V v) {
364      if (list == null)
365         list = list();
366      list.add(v);
367      return list;
368   }
369
370   /**
371    * Finds first value in this map that matches the specified method.
372    *
373    * @param m The method to test for.
374    * @param ofType Only return objects of the specified type.
375    * @return The matching object.  Never <jk>null</jk>.
376    */
377   public Optional<V> find(Method m, Class<? extends V> ofType) {
378      for (MethodEntry<V> e : methodEntries)
379         if (e.matches(m))
380            if (ofType == null || ofType.isInstance(e.value))
381               return optional(e.value);
382      return empty();
383   }
384
385   /**
386    * Finds first value in this map that matches the specified method.
387    *
388    * @param m The method to test for.
389    * @return The matching object.  Never <jk>null</jk>.
390    */
391   public Optional<V> find(Method m) {
392      return find(m, null);
393   }
394
395   /**
396    * Finds all values in this map that matches the specified method.
397    *
398    * @param m The method to test for.
399    * @param ofType Only return objects of the specified type.
400    * @return A modifiable list of matching values.  Never <jk>null</jk>.
401    */
402   public List<V> findAll(Method m, Class<? extends V> ofType) {
403      List<V> list = null;
404      for (MethodEntry<V> e : methodEntries)
405         if (e.matches(m) && e.value != null)
406            if (ofType == null || ofType.isInstance(e.value))
407               list = lazyAdd(list, e.value);
408      return lazyList(list);
409   }
410
411   /**
412    * Finds all values in this map that matches the specified method.
413    *
414    * @param m The method to test for.
415    * @return A modifiable list of matching values.  Never <jk>null</jk>.
416    */
417   public List<V> findAll(Method m) {
418      List<V> list = null;
419      for (MethodEntry<V> e : methodEntries)
420         if (e.matches(m) && e.value != null)
421            list = lazyAdd(list, e.value);
422      return lazyList(list);
423   }
424
425   /**
426    * Finds all values in this map that matches the specified method.
427    *
428    * @param m The method to test for.
429    * @param ofType Only return objects of the specified type.
430    * @param array The array to append values to.
431    * @return The same list passed in or a new modifiable list if <jk>null</jk>.
432    */
433   public V[] appendAll(Method m, Class<? extends V> ofType, V[] array) {
434      List<V> list = null;
435      for (MethodEntry<V> e : methodEntries)
436         if (e.matches(m) && e.value != null)
437            if (ofType == null || ofType.isInstance(e.value))
438               list = lazyAdd(array, list, e.value);
439      return lazyArray(array, list);
440   }
441
442   /**
443    * Finds first value in this map that matches the specified field.
444    *
445    * @param f The field to test for.
446    * @param ofType Only return objects of the specified type.
447    * @return The matching object.  Never <jk>null</jk>.
448    */
449   public Optional<V> find(Field f, Class<? extends V> ofType) {
450      for (FieldEntry<V> e : fieldEntries)
451         if (e.matches(f))
452            if (ofType == null || ofType.isInstance(e.value))
453               return optional(e.value);
454      return empty();
455   }
456
457   /**
458    * Finds first value in this map that matches the specified field.
459    *
460    * @param f The field to test for.
461    * @return The matching object.  Never <jk>null</jk>.
462    */
463   public Optional<V> find(Field f) {
464      return find(f, null);
465   }
466
467   /**
468    * Finds all values in this map that matches the specified field.
469    *
470    * @param f The field to test for.
471    * @param ofType Only return objects of the specified type.
472    * @return A modifiable list of matching values.  Never <jk>null</jk>.
473    */
474   public List<V> findAll(Field f, Class<? extends V> ofType) {
475      List<V> list = null;
476      for (FieldEntry<V> e : fieldEntries)
477         if (e.matches(f) && e.value != null)
478            if (ofType == null || ofType.isInstance(e.value))
479               list = lazyAdd(list, e.value);
480      return lazyList(list);
481   }
482
483   /**
484    * Finds all values in this map that matches the specified field.
485    *
486    * @param f The field to test for.
487    * @return A modifiable list of matching values.  Never <jk>null</jk>.
488    */
489   public List<V> findAll(Field f) {
490      List<V> list = null;
491      for (FieldEntry<V> e : fieldEntries)
492         if (e.matches(f) && e.value != null)
493            list = lazyAdd(list, e.value);
494      return lazyList(list);
495   }
496
497   /**
498    * Finds all values in this map that matches the specified field.
499    *
500    * @param f The field to test for.
501    * @param ofType Only return objects of the specified type.
502    * @param array The array to append values to.
503    * @return The same list passed in or a new modifiable list if <jk>null</jk>.
504    */
505   public V[] appendAll(Field f, Class<? extends V> ofType, V[] array) {
506      List<V> list = null;
507      for (FieldEntry<V> e : fieldEntries)
508         if (e.matches(f) && e.value != null)
509            if (ofType == null || ofType.isInstance(e.value))
510               list = lazyAdd(array, list, e.value);
511      return lazyArray(array, list);
512   }
513
514   /**
515    * Finds first value in this map that matches the specified constructor.
516    *
517    * @param c The constructor to test for.
518    * @param ofType Only return objects of the specified type.
519    * @return The matching object.  Never <jk>null</jk>.
520    */
521   public Optional<V> find(Constructor<?> c, Class<? extends V> ofType) {
522      for (ConstructorEntry<V> e : constructorEntries)
523         if (e.matches(c))
524            if (ofType == null || ofType.isInstance(e.value))
525               return optional(e.value);
526      return empty();
527   }
528
529   /**
530    * Finds first value in this map that matches the specified constructor.
531    *
532    * @param c The constructor to test for.
533    * @return The matching object.  Never <jk>null</jk>.
534    */
535   public Optional<V> find(Constructor<?> c) {
536      return find(c, null);
537   }
538
539   /**
540    * Finds all values in this map that matches the specified constructor.
541    *
542    * @param c The constructor to test for.
543    * @param ofType Only return objects of the specified type.
544    * @return A modifiable list of matching values.  Never <jk>null</jk>.
545    */
546   public List<V> findAll(Constructor<?> c, Class<? extends V> ofType) {
547      List<V> list = null;
548      for (ConstructorEntry<V> e : constructorEntries)
549         if (e.matches(c) && e.value != null)
550            if (ofType == null || ofType.isInstance(e.value))
551               list = lazyAdd(list, e.value);
552      return lazyList(list);
553   }
554
555   /**
556    * Finds all values in this map that matches the specified constructor.
557    *
558    * @param c The constructor to test for.
559    * @return A modifiable list of matching values.  Never <jk>null</jk>.
560    */
561   public List<V> findAll(Constructor<?> c) {
562      List<V> list = null;
563      for (ConstructorEntry<V> e : constructorEntries)
564         if (e.matches(c) && e.value != null)
565            list = lazyAdd(list, e.value);
566      return lazyList(list);
567   }
568
569   /**
570    * Finds all values in this map that matches the specified constructor.
571    *
572    * @param c The constructor to test for.
573    * @param ofType Only return objects of the specified type.
574    * @param array The array to append values to.
575    * @return The same list passed in or a new modifiable list if <jk>null</jk>.
576    */
577   public V[] appendAll(Constructor<?> c, Class<? extends V> ofType, V[] array) {
578      List<V> list = null;
579      for (ConstructorEntry<V> e : constructorEntries)
580         if (e.matches(c) && e.value != null)
581            if (ofType == null || ofType.isInstance(e.value))
582               list = lazyAdd(array, list, e.value);
583      return lazyArray(array, list);
584   }
585
586   static class ClassEntry<V> {
587      final String simpleName, fullName;
588      final V value;
589
590      ClassEntry(String name, V value) {
591         this.simpleName = simpleClassName(name);
592         this.fullName = name;
593         this.value = value;
594      }
595
596      public boolean matches(Class<?> c) {
597         if (c == null)
598            return false;
599         return classMatches(simpleName, fullName, c);
600      }
601
602      @Override
603      public String toString() {
604         return filteredMap()
605            .append("simpleName", simpleName)
606            .append("fullName", fullName)
607            .append("value", value)
608            .asString();
609      }
610   }
611
612   static class MethodEntry<V> {
613      String simpleClassName, fullClassName, methodName, args[];
614      V value;
615
616      MethodEntry(String name, V value) {
617         int i = name.indexOf('(');
618         this.args = i == -1 ? null : splitMethodArgs(name.substring(i+1, name.length()-1));
619         if (args != null) {
620            for (int j = 0; j < args.length; j++) {
621
622               // Strip off generic parameters.
623               int k = args[j].indexOf('<');
624               if (k > 0)
625                  args[j] = args[j].substring(0, k);
626
627               // Convert from xxx[][] to [[Lxxx; notation.
628               if (args[j].endsWith("[]")) {
629                  int l = 0;
630                  while (args[j].endsWith("[]")) {
631                     l++;
632                     args[j] = args[j].substring(0, args[j].length()-2);
633                  }
634                  StringBuilder sb = new StringBuilder(args[j].length() + l + 2);
635                  for (int m = 0; m < l; m++)
636                     sb.append('[');
637                  sb.append('L').append(args[j]).append(';');
638                  args[j] = sb.toString();
639               }
640            }
641         }
642         name = i == -1 ? name : name.substring(0, i);
643         i = name.lastIndexOf('.');
644         String s1 = name.substring(0, i).trim(), s2 = name.substring(i+1).trim();
645         this.simpleClassName = simpleClassName(s1);
646         this.fullClassName = s1;
647         this.methodName = s2;
648         this.value = value;
649      }
650
651      public boolean matches(Method m) {
652         if (m == null)
653            return false;
654         Class<?> c = m.getDeclaringClass();
655         return
656            classMatches(simpleClassName, fullClassName, c)
657            && (eq(m.getName(), methodName))
658            && (argsMatch(args, m.getParameterTypes()));
659      }
660
661      @Override
662      public String toString() {
663         return filteredMap()
664            .append("simpleClassName", simpleClassName)
665            .append("fullClassName", fullClassName)
666            .append("methodName", methodName)
667            .append("args", args)
668            .append("value", value)
669            .asString();
670      }
671   }
672
673   static class ConstructorEntry<V> {
674      String simpleClassName, fullClassName, args[];
675      V value;
676
677      ConstructorEntry(String name, V value) {
678         int i = name.indexOf('(');
679         this.args = split(name.substring(i+1, name.length()-1));
680         name = name.substring(0, i).trim();
681         this.simpleClassName = simpleClassName(name);
682         this.fullClassName = name;
683         this.value = value;
684      }
685
686      public boolean matches(Constructor<?> m) {
687         if (m == null)
688            return false;
689         Class<?> c = m.getDeclaringClass();
690         return
691            classMatches(simpleClassName, fullClassName, c)
692            && (argsMatch(args, m.getParameterTypes()));
693      }
694
695      @Override
696      public String toString() {
697         return filteredMap()
698            .append("simpleClassName", simpleClassName)
699            .append("fullClassName", fullClassName)
700            .append("args", args)
701            .append("value", value)
702            .asString();
703      }
704   }
705
706   static class FieldEntry<V> {
707      String simpleClassName, fullClassName, fieldName;
708      V value;
709
710      FieldEntry(String name, V value) {
711         int i = name.lastIndexOf('.');
712         String s1 = name.substring(0, i), s2 = name.substring(i+1);
713         this.simpleClassName = simpleClassName(s1);
714         this.fullClassName = s1;
715         this.fieldName = s2;
716         this.value = value;
717      }
718
719      public boolean matches(Field f) {
720         if (f == null)
721            return false;
722         Class<?> c = f.getDeclaringClass();
723         return
724            classMatches(simpleClassName, fullClassName, c)
725            && (eq(f.getName(), fieldName));
726      }
727
728      @Override
729      public String toString() {
730         return filteredMap()
731            .append("simpleClassName", simpleClassName)
732            .append("fullClassName", fullClassName)
733            .append("fieldName", fieldName)
734            .append("value", value)
735            .asString();
736      }
737   }
738
739   static boolean argsMatch(String[] names, Class<?>[] args) {
740      if (names == null)
741         return true;
742      if (names.length != args.length)
743         return false;
744      for (int i = 0; i < args.length; i++) {
745         String n = names[i];
746         Class<?> a = args[i];
747         if (! (eq(n, a.getSimpleName()) || eq(n, a.getName())))
748            return false;
749      }
750      return true;
751   }
752
753   static String simpleClassName(String name) {
754      int i = name.indexOf('.');
755      if (i == -1)
756         return name;
757      return null;
758   }
759
760   static boolean classMatches(String simpleName, String fullName, Class<?> c) {
761      // For class org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder
762      // c.getSimpleName() == "Builder"
763      // c.getFullName() == "org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder"
764      // c.getPackage() == "org.apache.juneau.a.rttests"
765      String cSimple = c.getSimpleName(), cFull = c.getName();
766      if (eq(simpleName, cSimple) || eq(fullName, cFull) || "*".equals(simpleName))
767         return true;
768      if (cFull.indexOf('$') != -1) {
769         Package p = c.getPackage();
770         if (p != null)
771            cFull = cFull.substring(p.getName().length() + 1);
772         if (eq(simpleName, cFull))
773            return true;
774         int i = cFull.indexOf('$');
775         while (i != -1) {
776            cFull = cFull.substring(i+1);
777            if (eq(simpleName, cFull))
778               return true;
779            i = cFull.indexOf('$');
780         }
781      }
782      return false;
783   }
784
785   @Override /* Object */
786   public String toString() {
787      return filteredMap()
788         .append("classEntries", classEntries)
789         .append("methodEntries", methodEntries)
790         .append("fieldEntries", fieldEntries)
791         .append("constructorEntries", constructorEntries)
792         .asString();
793   }
794
795   //-----------------------------------------------------------------------------------------------------------------
796   // Utility methods
797   //-----------------------------------------------------------------------------------------------------------------
798
799   private static <V> List<V> lazyList(List<V> list) {
800      return list == null ? Collections.emptyList() : list;
801   }
802
803   private static <V> List<V> lazyAdd(V[] array, List<V> list, V v) {
804      if (list == null)
805         list = list(array);
806      list.add(v);
807      return list;
808   }
809
810   @SuppressWarnings("unchecked")
811   private static <V> V[] lazyArray(V[] array, List<V> list) {
812      if (list == null)
813         return array;
814      array = (V[])Array.newInstance(array.getClass().getComponentType(), list.size());
815      list.toArray(array);
816      return array;
817   }
818}