001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.reflect;
018
019import static java.lang.Character.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.commons.utils.StringUtils.*;
023
024import java.lang.reflect.*;
025import java.util.*;
026import java.util.function.*;
027import java.util.stream.*;
028
029import org.apache.juneau.commons.collections.*;
030import org.apache.juneau.commons.utils.*;
031
032/**
033 * Maps arbitrary values to classes, methods, fields, and constructors based on name-based patterns.
034 *
035 * <p>
036 * This utility allows you to create flexible mappings between reflection elements and custom values.
037 * It supports various pattern formats for identifying classes and their members, with intelligent
038 * matching that handles inner classes, method signatures, and field names.
039 *
040 * <h5 class='section'>Example:</h5>
041 * <p class='bjava'>
042 *    <jc>// Create a map that associates visibility levels with specific classes/methods</jc>
043 *    ReflectionMap&lt;Visibility&gt; <jv>map</jv> = ReflectionMap
044 *       .<jsm>create</jsm>(Visibility.<jk>class</jk>)
045 *       .append(<js>"com.foo.MyClass"</js>, Visibility.<jsf>PUBLIC</jsf>)
046 *       .append(<js>"com.foo.MyClass.secretMethod"</js>, Visibility.<jsf>PRIVATE</jsf>)
047 *       .append(<js>"com.foo.MyClass.internalField"</js>, Visibility.<jsf>PACKAGE</jsf>)
048 *       .build();
049 *
050 *    <jc>// Find values for specific reflection elements</jc>
051 *    Stream&lt;Visibility&gt; <jv>classVisibility</jv> = <jv>map</jv>.find(MyClass.<jk>class</jk>);
052 *    Stream&lt;Visibility&gt; <jv>methodVisibility</jv> = <jv>map</jv>.find(<jv>secretMethod</jv>);
053 * </p>
054 *
055 * <h5 class='section'>Supported Pattern Formats:</h5>
056 *
057 * <p>
058 * The following pattern formats are supported for mapping keys:
059 *
060 * <ul class='spaced-list'>
061 *  <li><b>Classes:</b>
062 *       <ul>
063 *          <li>Fully qualified:
064 *             <ul>
065 *                <li><js>"com.foo.MyClass"</js>
066 *             </ul>
067 *          <li>Fully qualified inner class:
068 *             <ul>
069 *                <li><js>"com.foo.MyClass$Inner1$Inner2"</js>
070 *             </ul>
071 *          <li>Simple class name:
072 *             <ul>
073 *                <li><js>"MyClass"</js>
074 *             </ul>
075 *          <li>Simple inner class:
076 *             <ul>
077 *                <li><js>"MyClass$Inner1$Inner2"</js>
078 *                <li><js>"Inner1$Inner2"</js>
079 *                <li><js>"Inner2"</js>
080 *             </ul>
081 *          <li>All classes wildcard:
082 *             <ul>
083 *                <li><js>"*"</js>
084 *             </ul>
085 *       </ul>
086 *    <li><b>Methods:</b>
087 *       <ul>
088 *          <li>Fully qualified with args:
089 *             <ul>
090 *                <li><js>"com.foo.MyClass.myMethod(String,int)"</js>
091 *                <li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js>
092 *                <li><js>"com.foo.MyClass.myMethod()"</js>
093 *             </ul>
094 *          <li>Fully qualified without args (matches any args):
095 *             <ul>
096 *                <li><js>"com.foo.MyClass.myMethod"</js>
097 *             </ul>
098 *          <li>Simple with args:
099 *             <ul>
100 *                <li><js>"MyClass.myMethod(String,int)"</js>
101 *                <li><js>"MyClass.myMethod(java.lang.String,int)"</js>
102 *                <li><js>"MyClass.myMethod()"</js>
103 *             </ul>
104 *          <li>Simple without args (matches any args):
105 *             <ul>
106 *                <li><js>"MyClass.myMethod"</js>
107 *             </ul>
108 *          <li>Simple inner class:
109 *             <ul>
110 *                <li><js>"MyClass$Inner1$Inner2.myMethod"</js>
111 *                <li><js>"Inner1$Inner2.myMethod"</js>
112 *                <li><js>"Inner2.myMethod"</js>
113 *             </ul>
114 *       </ul>
115 *    <li><b>Fields:</b>
116 *       <ul>
117 *          <li>Fully qualified:
118 *             <ul>
119 *                <li><js>"com.foo.MyClass.myField"</js>
120 *             </ul>
121 *          <li>Simple:
122 *             <ul>
123 *                <li><js>"MyClass.myField"</js>
124 *             </ul>
125 *          <li>Simple inner class:
126 *             <ul>
127 *                <li><js>"MyClass$Inner1$Inner2.myField"</js>
128 *                <li><js>"Inner1$Inner2.myField"</js>
129 *                <li><js>"Inner2.myField"</js>
130 *             </ul>
131 *       </ul>
132 *    <li><b>Constructors:</b>
133 *       <ul>
134 *          <li>Fully qualified with args:
135 *             <ul>
136 *                <li><js>"com.foo.MyClass(String,int)"</js>
137 *                <li><js>"com.foo.MyClass(java.lang.String,int)"</js>
138 *                <li><js>"com.foo.MyClass()"</js>
139 *             </ul>
140 *          <li>Simple with args:
141 *             <ul>
142 *                <li><js>"MyClass(String,int)"</js>
143 *                <li><js>"MyClass(java.lang.String,int)"</js>
144 *                <li><js>"MyClass()"</js>
145 *             </ul>
146 *          <li>Simple inner class:
147 *             <ul>
148 *                <li><js>"MyClass$Inner1$Inner2()"</js>
149 *                <li><js>"Inner1$Inner2()"</js>
150 *                <li><js>"Inner2()"</js>
151 *             </ul>
152 *       </ul>
153 *    <li><b>Multiple patterns:</b>
154 *       <ul>
155 *          <li>A comma-delimited list of any patterns above:
156 *             <ul>
157 *                <li><js>"com.foo.MyClass, com.bar.OtherClass"</js>
158 *                <li><js>"MyClass.method1, MyClass.method2, MyClass.field1"</js>
159 *             </ul>
160 *       </ul>
161 * </ul>
162 *
163 * <h5 class='section'>Notes:</h5>
164 * <ul class='spaced-list'>
165 *    <li>Method and constructor patterns without parentheses match any method signature.
166 *    <li>Method and constructor argument types can be specified with simple or fully qualified names.
167 *    <li>Array types in signatures use either <js>"Type[]"</js> or JVM notation like <js>"[LType;"</js>.
168 *    <li>Generic type parameters are stripped from method signatures during matching.
169 *    <li>Patterns are case-sensitive.
170 * </ul>
171 *
172 * <h5 class='section'>See Also:</h5>
173 * <ul class='seealso'>
174 *    <li class='jc'>{@link ClassInfo}
175 *    <li class='jc'>{@link MethodInfo}
176 *    <li class='jc'>{@link FieldInfo}
177 *    <li class='jc'>{@link ConstructorInfo}
178 * </ul>
179 *
180 * @param <V> The type of values stored in this map.
181 */
182@SuppressWarnings({
183   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
184})
185public class ReflectionMap<V> {
186
187   /**
188    * Builder for creating {@link ReflectionMap} instances.
189    *
190    * <h5 class='section'>Example:</h5>
191    * <p class='bjava'>
192    *    ReflectionMap&lt;String&gt; <jv>map</jv> = ReflectionMap
193    *       .<jsm>create</jsm>(String.<jk>class</jk>)
194    *       .append(<js>"MyClass"</js>, <js>"value1"</js>)
195    *       .append(<js>"MyClass.myMethod"</js>, <js>"value2"</js>)
196    *       .build();
197    * </p>
198    *
199    * @param <V> The type of values stored in this map.
200    */
201   public static class Builder<V> {
202      final List<ClassEntry<V>> classEntries;
203      final List<MethodEntry<V>> methodEntries;
204      final List<FieldEntry<V>> fieldEntries;
205      final List<ConstructorEntry<V>> constructorEntries;
206
207      /**
208       * Constructor.
209       */
210      protected Builder() {
211         classEntries = list();
212         methodEntries = list();
213         fieldEntries = list();
214         constructorEntries = list();
215      }
216
217      /**
218       * Adds one or more mappings to this builder.
219       *
220       * <p>
221       * This method accepts pattern strings that identify classes, methods, fields, or constructors,
222       * and associates them with the specified value. Multiple patterns can be specified in a single
223       * key using comma-delimited format.
224       *
225       * <h5 class='section'>Example:</h5>
226       * <p class='bjava'>
227       *    Builder&lt;String&gt; <jv>builder</jv> = ReflectionMap.<jsm>create</jsm>(String.<jk>class</jk>);
228       *
229       *    <jc>// Map a class</jc>
230       *    <jv>builder</jv>.append(<js>"com.foo.MyClass"</js>, <js>"classValue"</js>);
231       *
232       *    <jc>// Map a specific method</jc>
233       *    <jv>builder</jv>.append(<js>"MyClass.myMethod(String,int)"</js>, <js>"methodValue"</js>);
234       *
235       *    <jc>// Map multiple patterns at once</jc>
236       *    <jv>builder</jv>.append(<js>"MyClass.field1, MyClass.field2, MyClass.field3"</js>, <js>"fieldValue"</js>);
237       * </p>
238       *
239       * @param key
240       *    The mapping key pattern(s).  Must not be <jk>null</jk>.
241       *    <br>Can be any of the following:
242       *    <ul>
243       *       <li>Fully qualified class name (e.g., <js>"com.foo.MyClass"</js>)
244       *       <li>Simple class name (e.g., <js>"MyClass"</js>)
245       *       <li>All classes wildcard (e.g., <js>"*"</js>)
246       *       <li>Method with signature (e.g., <js>"com.foo.MyClass.myMethod(String,int)"</js>)
247       *       <li>Method without signature (e.g., <js>"MyClass.myMethod"</js>) - matches any signature
248       *       <li>Field (e.g., <js>"MyClass.myField"</js>)
249       *       <li>Constructor (e.g., <js>"MyClass(String,int)"</js>)
250       *       <li>Comma-delimited list of any of the above (e.g., <js>"MyClass, MyClass.method1, MyClass.field1"</js>)
251       *    </ul>
252       * @param value
253       *    The value to associate with the matching reflection element(s).
254       *    <br>Can be <jk>null</jk> (the key pattern will then map to a <jk>null</jk> value).
255       * @return This object.
256       * @throws RuntimeException If the key pattern is invalid or empty.
257       */
258      public Builder<V> append(String key, V value) {
259         if (Shorts.ie(key))  // NOAI
260            throw rex("Invalid reflection signature: [%s]", key);
261         try {
262            splitNames(key, k -> {
263               if (k.endsWith(")")) {
264                  int i = k.substring(0, k.indexOf('(')).lastIndexOf('.');
265                  if (i == -1 || isUpperCase(k.charAt(i + 1))) {
266                     constructorEntries.add(ConstructorEntry.parse(k, value));
267                  } else {
268                     methodEntries.add(MethodEntry.parse(k, value));
269                  }
270               } else {
271                  var i = k.lastIndexOf('.');
272                  if (i == -1) {
273                     classEntries.add(ClassEntry.parse(k, value));
274                  } else if (isUpperCase(k.charAt(i + 1))) {
275                     classEntries.add(ClassEntry.parse(k, value));
276                     fieldEntries.add(FieldEntry.parse(k, value));
277                  } else {
278                     methodEntries.add(MethodEntry.parse(k, value));
279                     fieldEntries.add(FieldEntry.parse(k, value));
280                  }
281               }
282         });
283         } catch (@SuppressWarnings("unused") IndexOutOfBoundsException e) {
284         throw rex("Invalid reflection signature: [%s]", key);
285         }
286
287         return this;
288      }
289
290      /**
291       * Builds a new {@link ReflectionMap} from the current state of this builder.
292       *
293       * <p>
294       * Multiple calls to this method will create independent copies of the map.
295       *
296       * @return A new immutable {@link ReflectionMap} instance.
297       */
298      public ReflectionMap<V> build() {
299         return new ReflectionMap<>(this);
300      }
301
302      /**
303       * Splits a comma-delimited key string into individual keys, respecting escaped commas within parentheses.
304       *
305       * @param key The key string to split.
306       * @param consumer The consumer to accept each split key.
307       */
308      private static void splitNames(String key, Consumer<String> consumer) {
309         if (key.indexOf(',') == -1) {
310            consumer.accept(key);
311         } else {
312            var m = 0;
313            var escaped = false;
314            for (var i = 0; i < key.length(); i++) {
315               var c = key.charAt(i);
316               if (c == '(')
317                  escaped = true;
318               else if (c == ')')
319                  escaped = false;
320               else if (c == ',' && ! escaped) {
321                  consumer.accept(key.substring(m, i).trim());
322                  m = i + 1;
323               }
324            }
325            consumer.accept(key.substring(m).trim());
326         }
327      }
328   }
329
330   private record ClassEntry<V>(String simpleName, String fullName, V value) {
331
332      static <V> ClassEntry<V> parse(String name, V value) {
333         return new ClassEntry<>(simpleClassName(name), name, value);
334      }
335
336      public boolean matches(Class<?> c) {
337         if (c == null)
338            return false;
339         return classMatches(simpleName, fullName, c);
340      }
341
342      protected FluentMap<String,Object> properties() {
343         // @formatter:off
344         return filteredBeanPropertyMap()
345            .a("fullName", fullName)
346            .a("simpleName", simpleName)
347            .a("value", value);
348         // @formatter:on
349      }
350
351      @Override
352      public String toString() {
353         return r(properties());
354      }
355   }
356
357   record ConstructorEntry<V>(String simpleClassName, String fullClassName, String[] args, V value) {
358
359      static <V> ConstructorEntry<V> parse(String name, V value) {
360         var i = name.indexOf('(');
361         var args = splitMethodArgs(name.substring(i + 1, name.length() - 1));
362         for (var j = 0; j < args.length; j++) {
363            // Strip off generic parameters (e.g., "List<String>[]" -> "List[]")
364            args[j] = stripGenerics(args[j]);
365         }
366         var cn = name.substring(0, i).trim();
367         return new ConstructorEntry<>(ReflectionMap.simpleClassName(cn), cn, args, value);
368      }
369
370      public boolean matches(Constructor<?> m) {
371         if (m == null)
372            return false;
373         var c = m.getDeclaringClass();
374         return classMatches(simpleClassName, fullClassName, c) && (argsMatch(args, m.getParameterTypes()));
375      }
376
377      @Override /* Content-based: the 'args' array must compare by content, not identity (S6218). */
378      public boolean equals(Object o) {
379         return o instanceof ConstructorEntry<?> e
380            && eq(simpleClassName, e.simpleClassName)
381            && eq(fullClassName, e.fullClassName)
382            && Arrays.equals(args, e.args)
383            && eq(value, e.value);
384      }
385
386      @Override
387      public int hashCode() {
388         return 31 * Objects.hash(simpleClassName, fullClassName, value) + Arrays.hashCode(args);
389      }
390
391      protected FluentMap<String,Object> properties() {
392         // @formatter:off
393         return filteredBeanPropertyMap()
394            .a("args", args)
395            .a("fullClassName", fullClassName)
396            .a("simpleClassName", simpleClassName)
397            .a("value", value);
398         // @formatter:on
399      }
400
401      @Override
402      public String toString() {
403         return r(properties());
404      }
405   }
406
407   private record FieldEntry<V>(String simpleClassName, String fullClassName, String fieldName, V value) {
408
409      static <V> FieldEntry<V> parse(String name, V value) {
410         var i = name.lastIndexOf('.');
411         var s1 = name.substring(0, i);
412         var s2 = name.substring(i + 1);
413         return new FieldEntry<>(ReflectionMap.simpleClassName(s1), s1, s2, value);
414      }
415
416      public boolean matches(Field f) {
417         if (f == null)
418            return false;
419         var c = f.getDeclaringClass();
420         return classMatches(simpleClassName, fullClassName, c) && (eq(f.getName(), fieldName));
421      }
422
423      protected FluentMap<String,Object> properties() {
424         // @formatter:off
425         return filteredBeanPropertyMap()
426            .a("fieldName", fieldName)
427            .a("fullClassName", fullClassName)
428            .a("simpleClassName", simpleClassName)
429            .a("value", value);
430         // @formatter:on
431      }
432
433      @Override
434      public String toString() {
435         return r(properties());
436      }
437   }
438
439   record MethodEntry<V>(String simpleClassName, String fullClassName, String methodName, String[] args, V value) {
440
441      static <V> MethodEntry<V> parse(String name, V value) {
442         var i = name.indexOf('(');
443         var args = i == -1 ? null : splitMethodArgs(name.substring(i + 1, name.length() - 1));
444         if (nn(args)) {
445            for (var j = 0; j < args.length; j++) {
446               // Strip off generic parameters (e.g., "List<String>[]" -> "List[]")
447               args[j] = stripGenerics(args[j]);
448            }
449         }
450         var n = i == -1 ? name : name.substring(0, i);
451         i = n.lastIndexOf('.');
452         var s1 = n.substring(0, i).trim();
453         var s2 = n.substring(i + 1).trim();
454         return new MethodEntry<>(ReflectionMap.simpleClassName(s1), s1, s2, args, value);
455      }
456
457      public boolean matches(Method m) {
458         if (m == null)
459            return false;
460         var c = m.getDeclaringClass();
461         // @formatter:off
462         return
463            classMatches(simpleClassName, fullClassName, c)
464            && (eq(m.getName(), methodName))
465            && (argsMatch(args, m.getParameterTypes()));
466         // @formatter:on
467      }
468
469      @Override /* Content-based: the 'args' array must compare by content, not identity (S6218). */
470      public boolean equals(Object o) {
471         return o instanceof MethodEntry<?> e
472            && eq(simpleClassName, e.simpleClassName)
473            && eq(fullClassName, e.fullClassName)
474            && eq(methodName, e.methodName)
475            && Arrays.equals(args, e.args)
476            && eq(value, e.value);
477      }
478
479      @Override
480      public int hashCode() {
481         return 31 * Objects.hash(simpleClassName, fullClassName, methodName, value) + Arrays.hashCode(args);
482      }
483
484      protected FluentMap<String,Object> properties() {
485         // @formatter:off
486         return filteredBeanPropertyMap()
487            .a("args", o(args).map(x -> '[' + toCdl(x) + "]").orElse(null))
488            .a("fullClassName", fullClassName)
489            .a("methodName", methodName)
490            .a("simpleClassName", simpleClassName)
491            .a("value", value);
492         // @formatter:on
493      }
494
495      @Override
496      public String toString() {
497         return r(properties());
498      }
499   }
500
501   /**
502    * Creates a new builder for constructing a {@link ReflectionMap}.
503    *
504    * <h5 class='section'>Example:</h5>
505    * <p class='bjava'>
506    *    ReflectionMap&lt;String&gt; <jv>map</jv> = ReflectionMap
507    *       .<jsm>create</jsm>(String.<jk>class</jk>)
508    *       .append(<js>"com.foo.MyClass"</js>, <js>"value1"</js>)
509    *       .append(<js>"com.foo.MyClass.myMethod"</js>, <js>"value2"</js>)
510    *       .build();
511    * </p>
512    *
513    * @param <V> The type of values stored in the map.
514    * @param c The class type of values (used for type safety, not stored). Can be <jk>null</jk>.
515    * @return A new builder instance.
516    */
517   @SuppressWarnings({
518      "java:S1172" // Parameter c is used for type inference, not runtime behavior
519   })
520   public static <V> Builder<V> create(Class<V> c) {
521      return new Builder<>();
522   }
523
524   private static boolean argMatches(String pattern, Class<?> type) {
525      // Extract base type and dimensions from pattern
526      var patternDims = 0;
527      var patternBase = pattern;
528      while (patternBase.endsWith("[]")) {
529         patternDims++;
530         patternBase = patternBase.substring(0, patternBase.length() - 2);
531      }
532
533      // Extract base type and dimensions from actual type
534      var typeDims = 0;
535      var typeBase = type;
536      while (typeBase.isArray()) {
537         typeDims++;
538         typeBase = typeBase.getComponentType();
539      }
540
541      // Array dimensions must match
542      if (patternDims != typeDims)
543         return false;
544
545      // If non-array, use simple comparison
546      if (patternDims == 0)
547         return eq(pattern, type.getSimpleName()) || eq(pattern, type.getName());
548
549      // For arrays, compare the component types (simple name or full name)
550      return eq(patternBase, typeBase.getSimpleName()) || eq(patternBase, typeBase.getName());
551   }
552
553   private static boolean argsMatch(String[] names, Class<?>[] args) {
554      if (names == null)
555         return true;
556      if (names.length != args.length)
557         return false;
558      for (var i = 0; i < args.length; i++) {
559         if (! argMatches(names[i], args[i]))
560            return false;
561      }
562      return true;
563   }
564
565   private static boolean classMatches(String simpleName, String fullName, Class<?> c) {
566      // For class org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder
567      // c.getSimpleName() == "Builder"
568      // c.getFullName() == "org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder"
569      // c.getPackage() == "org.apache.juneau.a.rttests"
570      var cSimple = cns(c);
571      var cFull = cn(c);
572      if (eq(simpleName, cSimple) || eq(fullName, cFull) || eq(simpleName, "*"))
573         return true;
574      if (cFull.indexOf('$') != -1) {
575         var p = c.getPackage();
576         if (nn(p))
577            cFull = cFull.substring(p.getName().length() + 1);
578         if (eq(simpleName, cFull))
579            return true;
580         var i = cFull.indexOf('$');
581         while (i != -1) {
582            cFull = cFull.substring(i + 1);
583            if (eq(simpleName, cFull))
584               return true;
585            i = cFull.indexOf('$');
586         }
587      }
588      return false;
589   }
590
591   private static String simpleClassName(String name) {
592      var i = name.indexOf('.');
593      if (i == -1)
594         return name;
595      // Return null for fully qualified names to ensure they only match by fullName, not by simpleName.
596      // This prevents ambiguous matches where a simple name pattern might accidentally match
597      // a fully qualified pattern's simple part.
598      return null;
599   }
600
601
602   private static String stripGenerics(String type) {
603      if (type.indexOf('<') == -1)
604         return type;
605      var sb = new StringBuilder(type.length());
606      var depth = 0;
607      for (var i = 0; i < type.length(); i++) {
608         var c = type.charAt(i);
609         if (c == '<') {
610            depth++;
611         } else if (c == '>') {
612            depth--;
613         } else if (depth == 0) {
614            sb.append(c);
615         }
616      }
617      return sb.toString();
618   }
619
620   final List<ClassEntry<V>> classEntries;
621   final List<MethodEntry<V>> methodEntries;
622   final List<FieldEntry<V>> fieldEntries;
623   final List<ConstructorEntry<V>> constructorEntries;
624
625   /**
626    * Constructor.
627    *
628    * @param b The builder containing the mappings to initialize this map with. Must not be <jk>null</jk>.
629    */
630   protected ReflectionMap(Builder<V> b) {
631      this.classEntries = u(cp(b.classEntries));
632      this.methodEntries = u(cp(b.methodEntries));
633      this.fieldEntries = u(cp(b.fieldEntries));
634      this.constructorEntries = u(cp(b.constructorEntries));
635   }
636
637   /**
638    * Finds all values associated with the specified class.
639    *
640    * <p>
641    * This method searches for mappings that match the given class, including patterns for
642    * the fully qualified name, simple name, inner class names, and wildcard patterns.
643    *
644    * <h5 class='section'>Example:</h5>
645    * <p class='bjava'>
646    *    ReflectionMap&lt;String&gt; <jv>map</jv> = ReflectionMap
647    *       .<jsm>create</jsm>(String.<jk>class</jk>)
648    *       .append(<js>"com.foo.MyClass"</js>, <js>"value1"</js>)
649    *       .append(<js>"MyClass"</js>, <js>"value2"</js>)
650    *       .build();
651    *
652    *    <jc>// Find all values for MyClass</jc>
653    *    Stream&lt;String&gt; <jv>values</jv> = <jv>map</jv>.find(MyClass.<jk>class</jk>);
654    *    <jc>// Returns stream containing ["value1", "value2"]</jc>
655    * </p>
656    *
657    * @param c The class to find mappings for. Can be <jk>null</jk>.
658    * @return A stream of all values associated with the class. Empty stream if no matches found.
659    */
660   public Stream<V> find(Class<?> c) {
661      return classEntries.stream().filter(x -> x.matches(c)).map(x -> x.value());
662   }
663
664   /**
665    * Finds all values associated with the specified constructor.
666    *
667    * <p>
668    * This method searches for mappings that match the given constructor, considering both
669    * the declaring class and parameter types.
670    *
671    * <h5 class='section'>Example:</h5>
672    * <p class='bjava'>
673    *    ReflectionMap&lt;String&gt; <jv>map</jv> = ReflectionMap
674    *       .<jsm>create</jsm>(String.<jk>class</jk>)
675    *       .append(<js>"MyClass(String,int)"</js>, <js>"value1"</js>)
676    *       .build();
677    *
678    *    <jc>// Find value for specific constructor</jc>
679    *    Constructor&lt;?&gt; <jv>ctor</jv> = MyClass.<jk>class</jk>.getConstructor(String.<jk>class</jk>, <jk>int</jk>.<jk>class</jk>);
680    *    Stream&lt;String&gt; <jv>values</jv> = <jv>map</jv>.find(<jv>ctor</jv>);
681    * </p>
682    *
683    * @param c The constructor to find mappings for. Can be <jk>null</jk>.
684    * @return A stream of all values associated with the constructor. Empty stream if no matches found.
685    */
686   public Stream<V> find(Constructor<?> c) {
687      return constructorEntries.stream().filter(x -> x.matches(c)).map(x -> x.value());
688   }
689
690   /**
691    * Finds all values associated with the specified field.
692    *
693    * <p>
694    * This method searches for mappings that match the given field, considering both
695    * the declaring class and field name.
696    *
697    * <h5 class='section'>Example:</h5>
698    * <p class='bjava'>
699    *    ReflectionMap&lt;String&gt; <jv>map</jv> = ReflectionMap
700    *       .<jsm>create</jsm>(String.<jk>class</jk>)
701    *       .append(<js>"MyClass.myField"</js>, <js>"value1"</js>)
702    *       .build();
703    *
704    *    <jc>// Find value for specific field</jc>
705    *    Field <jv>field</jv> = MyClass.<jk>class</jk>.getField(<js>"myField"</js>);
706    *    Stream&lt;String&gt; <jv>values</jv> = <jv>map</jv>.find(<jv>field</jv>);
707    * </p>
708    *
709    * @param f The field to find mappings for. Can be <jk>null</jk>.
710    * @return A stream of all values associated with the field. Empty stream if no matches found.
711    */
712   public Stream<V> find(Field f) {
713      return fieldEntries.stream().filter(x -> x.matches(f)).map(x -> x.value());
714   }
715
716   /**
717    * Finds all values associated with the specified method.
718    *
719    * <p>
720    * This method searches for mappings that match the given method, considering the
721    * declaring class, method name, and parameter types.
722    *
723    * <h5 class='section'>Example:</h5>
724    * <p class='bjava'>
725    *    ReflectionMap&lt;String&gt; <jv>map</jv> = ReflectionMap
726    *       .<jsm>create</jsm>(String.<jk>class</jk>)
727    *       .append(<js>"MyClass.myMethod"</js>, <js>"value1"</js>)
728    *       .append(<js>"MyClass.myMethod(String)"</js>, <js>"value2"</js>)
729    *       .build();
730    *
731    *    <jc>// Find values for specific method</jc>
732    *    Method <jv>method</jv> = MyClass.<jk>class</jk>.getMethod(<js>"myMethod"</js>, String.<jk>class</jk>);
733    *    Stream&lt;String&gt; <jv>values</jv> = <jv>map</jv>.find(<jv>method</jv>);
734    *    <jc>// Returns stream containing ["value1", "value2"] - first matches any signature</jc>
735    * </p>
736    *
737    * @param m The method to find mappings for. Can be <jk>null</jk>.
738    * @return A stream of all values associated with the method. Empty stream if no matches found.
739    */
740   public Stream<V> find(Method m) {
741      return methodEntries.stream().filter(x -> x.matches(m)).map(x -> x.value());
742   }
743
744   protected FluentMap<String,Object> properties() {
745      // @formatter:off
746      return filteredBeanPropertyMap()
747         .a("classEntries", classEntries)
748         .a("methodEntries", methodEntries)
749         .a("fieldEntries", fieldEntries)
750         .a("constructorEntries", constructorEntries);
751      // @formatter:on
752   }
753
754   @Override /* Overridden from Object */
755   public String toString() {
756      return r(properties());
757   }
758}