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<Visibility> <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<Visibility> <jv>classVisibility</jv> = <jv>map</jv>.find(MyClass.<jk>class</jk>); 053 * Stream<Visibility> <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<String> <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<String> <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<String> <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<String> <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<String> <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<String> <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<?> <jv>ctor</jv> = MyClass.<jk>class</jk>.getConstructor(String.<jk>class</jk>, <jk>int</jk>.<jk>class</jk>); 657 * Stream<String> <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<String> <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<String> <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<String> <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<String> <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}