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