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