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