1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.gradle; 22 23 import groovy.lang.Closure; 24 import org.gradle.api.*; 25 import org.gradle.api.file.*; 26 import org.gradle.api.logging.*; 27 import org.gradle.api.tasks.*; 28 import proguard.*; 29 import proguard.classfile.*; 30 import proguard.classfile.util.ClassUtil; 31 import proguard.util.ListUtil; 32 33 import java.io.*; 34 import java.util.*; 35 36 /** 37 * This Task allows to configure and run ProGuard from Gradle. 38 * 39 * @author Eric Lafortune 40 */ 41 public class ProGuardTask extends DefaultTask 42 { 43 // Accumulated input and output, for the sake of Gradle's lazy file 44 // resolution and lazy task execution. 45 private final List inJarFiles = new ArrayList(); 46 private final List inJarFilters = new ArrayList(); 47 private final List outJarFiles = new ArrayList(); 48 private final List outJarFilters = new ArrayList(); 49 private final List inJarCounts = new ArrayList(); 50 private final List libraryJarFiles = new ArrayList(); 51 private final List libraryJarFilters = new ArrayList(); 52 private final List configurationFiles = new ArrayList(); 53 54 // Accumulated configuration. 55 private final Configuration configuration = new Configuration(); 56 57 // Field acting as a parameter for the class member specification methods. 58 private ClassSpecification classSpecification; 59 60 61 // Gradle task inputs and outputs, because annotations on the List fields 62 // (private or not) don't seem to work. Private methods don't work either, 63 // but package visible or protected methods are ok. 64 65 @InputFiles getInJarFiles()66 protected FileCollection getInJarFiles() throws ParseException 67 { 68 return getProject().files(inJarFiles); 69 } 70 71 @Optional @OutputFiles getOutJarFiles()72 protected FileCollection getOutJarFiles() throws ParseException 73 { 74 return getProject().files(outJarFiles); 75 } 76 77 @InputFiles getLibraryJarFiles()78 protected FileCollection getLibraryJarFiles() throws ParseException 79 { 80 return getProject().files(libraryJarFiles); 81 } 82 83 @InputFiles getConfigurationFiles()84 protected FileCollection getConfigurationFiles() throws ParseException 85 { 86 return getProject().files(configurationFiles); 87 } 88 89 90 // Gradle task settings corresponding to all ProGuard options. 91 configuration(Object configurationFiles)92 public void configuration(Object configurationFiles) 93 throws ParseException, IOException 94 { 95 // Just collect the arguments, so they can be resolved lazily. 96 this.configurationFiles.add(configurationFiles); 97 } 98 injars(Object inJarFiles)99 public void injars(Object inJarFiles) 100 throws ParseException 101 { 102 injars(null, inJarFiles); 103 } 104 injars(Map filterArgs, Object inJarFiles)105 public void injars(Map filterArgs, Object inJarFiles) 106 throws ParseException 107 { 108 // Just collect the arguments, so they can be resolved lazily. 109 this.inJarFiles.add(inJarFiles); 110 this.inJarFilters.add(filterArgs); 111 } 112 outjars(Object outJarFiles)113 public void outjars(Object outJarFiles) 114 throws ParseException 115 { 116 outjars(null, outJarFiles); 117 } 118 outjars(Map filterArgs, Object outJarFiles)119 public void outjars(Map filterArgs, Object outJarFiles) 120 throws ParseException 121 { 122 // Just collect the arguments, so they can be resolved lazily. 123 this.outJarFiles.add(getProject().file(outJarFiles)); 124 this.outJarFilters.add(filterArgs); 125 this.inJarCounts.add(Integer.valueOf(inJarFiles.size())); 126 } 127 libraryjars(Object libraryJarFiles)128 public void libraryjars(Object libraryJarFiles) 129 throws ParseException 130 { 131 libraryjars(null, libraryJarFiles); 132 } 133 libraryjars(Map filterArgs, Object libraryJarFiles)134 public void libraryjars(Map filterArgs, Object libraryJarFiles) 135 throws ParseException 136 { 137 // Just collect the arguments, so they can be resolved lazily. 138 this.libraryJarFiles.add(libraryJarFiles); 139 this.libraryJarFilters.add(filterArgs); 140 } 141 142 // Hack: support the keyword without parentheses in Groovy. getskipnonpubliclibraryclasses()143 public Object getskipnonpubliclibraryclasses() 144 { 145 skipnonpubliclibraryclasses(); 146 return null; 147 } 148 skipnonpubliclibraryclasses()149 public void skipnonpubliclibraryclasses() 150 { 151 configuration.skipNonPublicLibraryClasses = true; 152 } 153 154 // Hack: support the keyword without parentheses in Groovy. getdontskipnonpubliclibraryclassmembers()155 public Object getdontskipnonpubliclibraryclassmembers() 156 { 157 dontskipnonpubliclibraryclassmembers(); 158 return null; 159 } 160 dontskipnonpubliclibraryclassmembers()161 public void dontskipnonpubliclibraryclassmembers() 162 { 163 configuration.skipNonPublicLibraryClassMembers = false; 164 } 165 166 // Hack: support the keyword without parentheses in Groovy. getkeepdirectories()167 public Object getkeepdirectories() 168 { 169 keepdirectories(); 170 return null; 171 } 172 keepdirectories()173 public void keepdirectories() 174 { 175 keepdirectories(null); 176 } 177 keepdirectories(String filter)178 public void keepdirectories(String filter) 179 { 180 configuration.keepDirectories = 181 extendFilter(configuration.keepDirectories, filter); 182 } 183 target(String targetClassVersion)184 public void target(String targetClassVersion) 185 { 186 configuration.targetClassVersion = 187 ClassUtil.internalClassVersion(targetClassVersion); 188 } 189 190 // Hack: support the keyword without parentheses in Groovy. getforceprocessing()191 public Object getforceprocessing() 192 { 193 forceprocessing(); 194 return null; 195 } 196 forceprocessing()197 public void forceprocessing() 198 { 199 configuration.lastModified = Long.MAX_VALUE; 200 } 201 keep(String classSpecificationString)202 public void keep(String classSpecificationString) 203 throws ParseException 204 { 205 keep(null, classSpecificationString); 206 } 207 keep(Map keepArgs, String classSpecificationString)208 public void keep(Map keepArgs, 209 String classSpecificationString) 210 throws ParseException 211 { 212 configuration.keep = 213 extendClassSpecifications(configuration.keep, 214 createKeepClassSpecification(false, 215 true, 216 false, 217 keepArgs, 218 classSpecificationString)); 219 } 220 keep(Map keepClassSpecificationArgs)221 public void keep(Map keepClassSpecificationArgs) 222 throws ParseException 223 { 224 keep(keepClassSpecificationArgs, (Closure)null); 225 } 226 keep(Map keepClassSpecificationArgs, Closure classMembersClosure)227 public void keep(Map keepClassSpecificationArgs, 228 Closure classMembersClosure) 229 throws ParseException 230 { 231 configuration.keep = 232 extendClassSpecifications(configuration.keep, 233 createKeepClassSpecification(false, 234 true, 235 false, 236 keepClassSpecificationArgs, 237 classMembersClosure)); 238 } 239 keepclassmembers(String classSpecificationString)240 public void keepclassmembers(String classSpecificationString) 241 throws ParseException 242 { 243 keepclassmembers(null, classSpecificationString); 244 } 245 keepclassmembers(Map keepArgs, String classSpecificationString)246 public void keepclassmembers(Map keepArgs, 247 String classSpecificationString) 248 throws ParseException 249 { 250 configuration.keep = 251 extendClassSpecifications(configuration.keep, 252 createKeepClassSpecification(false, 253 false, 254 false, 255 keepArgs, 256 classSpecificationString)); 257 } 258 keepclassmembers(Map keepClassSpecificationArgs)259 public void keepclassmembers(Map keepClassSpecificationArgs) 260 throws ParseException 261 { 262 keepclassmembers(keepClassSpecificationArgs, (Closure)null); 263 } 264 keepclassmembers(Map keepClassSpecificationArgs, Closure classMembersClosure)265 public void keepclassmembers(Map keepClassSpecificationArgs, 266 Closure classMembersClosure) 267 throws ParseException 268 { 269 configuration.keep = 270 extendClassSpecifications(configuration.keep, 271 createKeepClassSpecification(false, 272 false, 273 false, 274 keepClassSpecificationArgs, 275 classMembersClosure)); 276 } 277 keepclasseswithmembers(String classSpecificationString)278 public void keepclasseswithmembers(String classSpecificationString) 279 throws ParseException 280 { 281 keepclasseswithmembers(null, classSpecificationString); 282 } 283 keepclasseswithmembers(Map keepArgs, String classSpecificationString)284 public void keepclasseswithmembers(Map keepArgs, 285 String classSpecificationString) 286 throws ParseException 287 { 288 configuration.keep = 289 extendClassSpecifications(configuration.keep, 290 createKeepClassSpecification(false, 291 false, 292 true, 293 keepArgs, 294 classSpecificationString)); 295 } 296 keepclasseswithmembers(Map keepClassSpecificationArgs)297 public void keepclasseswithmembers(Map keepClassSpecificationArgs) 298 throws ParseException 299 { 300 keepclasseswithmembers(keepClassSpecificationArgs, (Closure)null); 301 } 302 keepclasseswithmembers(Map keepClassSpecificationArgs, Closure classMembersClosure)303 public void keepclasseswithmembers(Map keepClassSpecificationArgs, 304 Closure classMembersClosure) 305 throws ParseException 306 { 307 configuration.keep = 308 extendClassSpecifications(configuration.keep, 309 createKeepClassSpecification(false, 310 false, 311 true, 312 keepClassSpecificationArgs, 313 classMembersClosure)); 314 } 315 keepnames(String classSpecificationString)316 public void keepnames(String classSpecificationString) 317 throws ParseException 318 { 319 keepnames(null, classSpecificationString); 320 } 321 keepnames(Map keepArgs, String classSpecificationString)322 public void keepnames(Map keepArgs, 323 String classSpecificationString) 324 throws ParseException 325 { 326 configuration.keep = 327 extendClassSpecifications(configuration.keep, 328 createKeepClassSpecification(true, 329 true, 330 false, 331 keepArgs, 332 classSpecificationString)); 333 } 334 keepnames(Map keepClassSpecificationArgs)335 public void keepnames(Map keepClassSpecificationArgs) 336 throws ParseException 337 { 338 keepnames(keepClassSpecificationArgs, (Closure)null); 339 } 340 keepnames(Map keepClassSpecificationArgs, Closure classMembersClosure)341 public void keepnames(Map keepClassSpecificationArgs, 342 Closure classMembersClosure) 343 throws ParseException 344 { 345 configuration.keep = 346 extendClassSpecifications(configuration.keep, 347 createKeepClassSpecification(true, 348 true, 349 false, 350 keepClassSpecificationArgs, 351 classMembersClosure)); 352 } 353 keepclassmembernames(String classSpecificationString)354 public void keepclassmembernames(String classSpecificationString) 355 throws ParseException 356 { 357 keepclassmembernames(null, classSpecificationString); 358 } 359 keepclassmembernames(Map keepArgs, String classSpecificationString)360 public void keepclassmembernames(Map keepArgs, 361 String classSpecificationString) 362 throws ParseException 363 { 364 configuration.keep = 365 extendClassSpecifications(configuration.keep, 366 createKeepClassSpecification(true, 367 false, 368 false, 369 keepArgs, 370 classSpecificationString)); 371 } 372 keepclassmembernames(Map keepClassSpecificationArgs)373 public void keepclassmembernames(Map keepClassSpecificationArgs) 374 throws ParseException 375 { 376 keepclassmembernames(keepClassSpecificationArgs, (Closure)null); 377 } 378 keepclassmembernames(Map keepClassSpecificationArgs, Closure classMembersClosure)379 public void keepclassmembernames(Map keepClassSpecificationArgs, 380 Closure classMembersClosure) 381 throws ParseException 382 { 383 configuration.keep = 384 extendClassSpecifications(configuration.keep, 385 createKeepClassSpecification(true, 386 false, 387 false, 388 keepClassSpecificationArgs, 389 classMembersClosure)); 390 } 391 keepclasseswithmembernames(String classSpecificationString)392 public void keepclasseswithmembernames(String classSpecificationString) 393 throws ParseException 394 { 395 keepclasseswithmembernames(null, classSpecificationString); 396 } 397 keepclasseswithmembernames(Map keepArgs, String classSpecificationString)398 public void keepclasseswithmembernames(Map keepArgs, 399 String classSpecificationString) 400 throws ParseException 401 { 402 configuration.keep = 403 extendClassSpecifications(configuration.keep, 404 createKeepClassSpecification(true, 405 false, 406 true, 407 keepArgs, 408 classSpecificationString)); 409 } 410 keepclasseswithmembernames(Map keepClassSpecificationArgs)411 public void keepclasseswithmembernames(Map keepClassSpecificationArgs) 412 throws ParseException 413 { 414 keepclasseswithmembernames(keepClassSpecificationArgs, (Closure)null); 415 } 416 keepclasseswithmembernames(Map keepClassSpecificationArgs, Closure classMembersClosure)417 public void keepclasseswithmembernames(Map keepClassSpecificationArgs, 418 Closure classMembersClosure) 419 throws ParseException 420 { 421 configuration.keep = 422 extendClassSpecifications(configuration.keep, 423 createKeepClassSpecification(true, 424 false, 425 true, 426 keepClassSpecificationArgs, 427 classMembersClosure)); 428 } 429 430 // Hack: support the keyword without parentheses in Groovy. getprintseeds()431 public Object getprintseeds() 432 { 433 printseeds(); 434 return null; 435 } 436 printseeds()437 public void printseeds() 438 { 439 configuration.printSeeds = Configuration.STD_OUT; 440 } 441 printseeds(Object printSeeds)442 public void printseeds(Object printSeeds) 443 throws ParseException 444 { 445 configuration.printSeeds = getProject().file(printSeeds); 446 } 447 448 // Hack: support the keyword without parentheses in Groovy. getdontshrink()449 public Object getdontshrink() 450 { 451 dontshrink(); 452 return null; 453 } 454 dontshrink()455 public void dontshrink() 456 { 457 configuration.shrink = false; 458 } 459 460 // Hack: support the keyword without parentheses in Groovy. getprintusage()461 public Object getprintusage() 462 { 463 printusage(); 464 return null; 465 } 466 printusage()467 public void printusage() 468 { 469 configuration.printUsage = Configuration.STD_OUT; 470 } 471 printusage(Object printUsage)472 public void printusage(Object printUsage) 473 throws ParseException 474 { 475 configuration.printUsage = getProject().file(printUsage); 476 } 477 whyareyoukeeping(String classSpecificationString)478 public void whyareyoukeeping(String classSpecificationString) 479 throws ParseException 480 { 481 configuration.whyAreYouKeeping = 482 extendClassSpecifications(configuration.whyAreYouKeeping, 483 createClassSpecification(classSpecificationString)); 484 } 485 whyareyoukeeping(Map classSpecificationArgs)486 public void whyareyoukeeping(Map classSpecificationArgs) 487 throws ParseException 488 { 489 whyareyoukeeping(classSpecificationArgs, null); 490 } 491 whyareyoukeeping(Map classSpecificationArgs, Closure classMembersClosure)492 public void whyareyoukeeping(Map classSpecificationArgs, 493 Closure classMembersClosure) 494 throws ParseException 495 { 496 configuration.whyAreYouKeeping = 497 extendClassSpecifications(configuration.whyAreYouKeeping, 498 createClassSpecification(classSpecificationArgs, 499 classMembersClosure)); 500 } 501 502 // Hack: support the keyword without parentheses in Groovy. getdontoptimize()503 public Object getdontoptimize() 504 { 505 dontoptimize(); 506 return null; 507 } 508 dontoptimize()509 public void dontoptimize() 510 { 511 configuration.optimize = false; 512 } 513 optimizations(String filter)514 public void optimizations(String filter) 515 { 516 configuration.optimizations = 517 extendFilter(configuration.optimizations, filter); 518 } 519 520 optimizationpasses(int optimizationPasses)521 public void optimizationpasses(int optimizationPasses) 522 { 523 configuration.optimizationPasses = optimizationPasses; 524 } 525 assumenosideeffects(String classSpecificationString)526 public void assumenosideeffects(String classSpecificationString) 527 throws ParseException 528 { 529 configuration.assumeNoSideEffects = 530 extendClassSpecifications(configuration.assumeNoSideEffects, 531 createClassSpecification(classSpecificationString)); 532 } 533 assumenosideeffects(Map classSpecificationArgs, Closure classMembersClosure)534 public void assumenosideeffects(Map classSpecificationArgs, 535 Closure classMembersClosure) 536 throws ParseException 537 { 538 configuration.assumeNoSideEffects = 539 extendClassSpecifications(configuration.assumeNoSideEffects, 540 createClassSpecification(classSpecificationArgs, 541 classMembersClosure)); 542 } 543 544 // Hack: support the keyword without parentheses in Groovy. getallowaccessmodification()545 public Object getallowaccessmodification() 546 { 547 allowaccessmodification(); 548 return null; 549 } 550 allowaccessmodification()551 public void allowaccessmodification() 552 { 553 configuration.allowAccessModification = true; 554 } 555 556 // Hack: support the keyword without parentheses in Groovy. getmergeinterfacesaggressively()557 public Object getmergeinterfacesaggressively() 558 { 559 mergeinterfacesaggressively(); 560 return null; 561 } 562 mergeinterfacesaggressively()563 public void mergeinterfacesaggressively() 564 { 565 configuration.mergeInterfacesAggressively = true; 566 } 567 568 // Hack: support the keyword without parentheses in Groovy. getdontobfuscate()569 public Object getdontobfuscate() 570 { 571 dontobfuscate(); 572 return null; 573 } 574 dontobfuscate()575 public void dontobfuscate() 576 { 577 configuration.obfuscate = false; 578 } 579 580 // Hack: support the keyword without parentheses in Groovy. getprintmapping()581 public Object getprintmapping() 582 { 583 printmapping(); 584 return null; 585 } 586 printmapping()587 public void printmapping() 588 { 589 configuration.printMapping = Configuration.STD_OUT; 590 } 591 printmapping(Object printMapping)592 public void printmapping(Object printMapping) 593 throws ParseException 594 { 595 configuration.printMapping = getProject().file(printMapping); 596 } 597 applymapping(Object applyMapping)598 public void applymapping(Object applyMapping) 599 throws ParseException 600 { 601 configuration.applyMapping = getProject().file(applyMapping); 602 } 603 obfuscationdictionary(Object obfuscationDictionary)604 public void obfuscationdictionary(Object obfuscationDictionary) 605 throws ParseException 606 { 607 configuration.obfuscationDictionary = 608 getProject().file(obfuscationDictionary); 609 } 610 classobfuscationdictionary(Object classObfuscationDictionary)611 public void classobfuscationdictionary(Object classObfuscationDictionary) 612 throws ParseException 613 { 614 configuration.classObfuscationDictionary = 615 getProject().file(classObfuscationDictionary); 616 } 617 packageobfuscationdictionary(Object packageObfuscationDictionary)618 public void packageobfuscationdictionary(Object packageObfuscationDictionary) 619 throws ParseException 620 { 621 configuration.packageObfuscationDictionary = 622 getProject().file(packageObfuscationDictionary); 623 } 624 625 // Hack: support the keyword without parentheses in Groovy. getoverloadaggressively()626 public Object getoverloadaggressively() 627 { 628 overloadaggressively(); 629 return null; 630 } 631 overloadaggressively()632 public void overloadaggressively() 633 { 634 configuration.overloadAggressively = true; 635 } 636 637 // Hack: support the keyword without parentheses in Groovy. getuseuniqueclassmembernames()638 public Object getuseuniqueclassmembernames() 639 { 640 useuniqueclassmembernames(); 641 return null; 642 } 643 useuniqueclassmembernames()644 public void useuniqueclassmembernames() 645 { 646 configuration.useUniqueClassMemberNames = true; 647 } 648 649 // Hack: support the keyword without parentheses in Groovy. getdontusemixedcaseclassnames()650 public Object getdontusemixedcaseclassnames() 651 { 652 dontusemixedcaseclassnames(); 653 return null; 654 } 655 dontusemixedcaseclassnames()656 public void dontusemixedcaseclassnames() 657 { 658 configuration.useMixedCaseClassNames = false; 659 } 660 661 // Hack: support the keyword without parentheses in Groovy. getkeeppackagenames()662 public Object getkeeppackagenames() 663 { 664 keeppackagenames(); 665 return null; 666 } 667 keeppackagenames()668 public void keeppackagenames() 669 { 670 keeppackagenames(null); 671 } 672 keeppackagenames(String filter)673 public void keeppackagenames(String filter) 674 { 675 configuration.keepPackageNames = 676 extendFilter(configuration.keepPackageNames, filter, true); 677 } 678 679 // Hack: support the keyword without parentheses in Groovy. getflattenpackagehierarchy()680 public Object getflattenpackagehierarchy() 681 { 682 flattenpackagehierarchy(); 683 return null; 684 } 685 flattenpackagehierarchy()686 public void flattenpackagehierarchy() 687 { 688 flattenpackagehierarchy(""); 689 } 690 flattenpackagehierarchy(String flattenPackageHierarchy)691 public void flattenpackagehierarchy(String flattenPackageHierarchy) 692 { 693 configuration.flattenPackageHierarchy = 694 ClassUtil.internalClassName(flattenPackageHierarchy); 695 } 696 697 // Hack: support the keyword without parentheses in Groovy. getrepackageclasses()698 public Object getrepackageclasses() 699 { 700 repackageclasses(); 701 return null; 702 } 703 repackageclasses()704 public void repackageclasses() 705 { 706 repackageclasses(""); 707 } 708 repackageclasses(String repackageClasses)709 public void repackageclasses(String repackageClasses) 710 { 711 configuration.repackageClasses = 712 ClassUtil.internalClassName(repackageClasses); 713 } 714 715 // Hack: support the keyword without parentheses in Groovy. getkeepattributes()716 public Object getkeepattributes() 717 { 718 keepattributes(); 719 return null; 720 } 721 keepattributes()722 public void keepattributes() 723 { 724 keepattributes(null); 725 } 726 keepattributes(String filter)727 public void keepattributes(String filter) 728 { 729 configuration.keepAttributes = 730 extendFilter(configuration.keepAttributes, filter); 731 } 732 733 // Hack: support the keyword without parentheses in Groovy. getkeepparameternames()734 public Object getkeepparameternames() 735 { 736 keepparameternames(); 737 return null; 738 } 739 keepparameternames()740 public void keepparameternames() 741 { 742 configuration.keepParameterNames = true; 743 } 744 745 // Hack: support the keyword without parentheses in Groovy. getrenamesourcefileattribute()746 public Object getrenamesourcefileattribute() 747 { 748 renamesourcefileattribute(); 749 return null; 750 } 751 renamesourcefileattribute()752 public void renamesourcefileattribute() 753 { 754 renamesourcefileattribute(""); 755 } 756 renamesourcefileattribute(String newSourceFileAttribute)757 public void renamesourcefileattribute(String newSourceFileAttribute) 758 { 759 configuration.newSourceFileAttribute = newSourceFileAttribute; 760 } 761 762 // Hack: support the keyword without parentheses in Groovy. getadaptclassstrings()763 public Object getadaptclassstrings() 764 { 765 adaptclassstrings(); 766 return null; 767 } 768 adaptclassstrings()769 public void adaptclassstrings() 770 { 771 adaptclassstrings(null); 772 } 773 adaptclassstrings(String filter)774 public void adaptclassstrings(String filter) 775 { 776 configuration.adaptClassStrings = 777 extendFilter(configuration.adaptClassStrings, filter, true); 778 } 779 780 // Hack: support the keyword without parentheses in Groovy. getadaptresourcefilenames()781 public Object getadaptresourcefilenames() 782 { 783 adaptresourcefilenames(); 784 return null; 785 } 786 adaptresourcefilenames()787 public void adaptresourcefilenames() 788 { 789 adaptresourcefilenames(null); 790 } 791 adaptresourcefilenames(String filter)792 public void adaptresourcefilenames(String filter) 793 { 794 configuration.adaptResourceFileNames = 795 extendFilter(configuration.adaptResourceFileNames, filter); 796 } 797 798 // Hack: support the keyword without parentheses in Groovy. getadaptresourcefilecontents()799 public Object getadaptresourcefilecontents() 800 { 801 adaptresourcefilecontents(); 802 return null; 803 } 804 adaptresourcefilecontents()805 public void adaptresourcefilecontents() 806 { 807 adaptresourcefilecontents(null); 808 } 809 adaptresourcefilecontents(String filter)810 public void adaptresourcefilecontents(String filter) 811 { 812 configuration.adaptResourceFileContents = 813 extendFilter(configuration.adaptResourceFileContents, filter); 814 } 815 816 // Hack: support the keyword without parentheses in Groovy. getdontpreverify()817 public Object getdontpreverify() 818 { 819 dontpreverify(); 820 return null; 821 } 822 dontpreverify()823 public void dontpreverify() 824 { 825 configuration.preverify = false; 826 } 827 828 // Hack: support the keyword without parentheses in Groovy. getmicroedition()829 public Object getmicroedition() 830 { 831 microedition(); 832 return null; 833 } 834 microedition()835 public void microedition() 836 { 837 configuration.microEdition = true; 838 } 839 840 // Hack: support the keyword without parentheses in Groovy. getverbose()841 public Object getverbose() 842 { 843 verbose(); 844 return null; 845 } 846 verbose()847 public void verbose() 848 { 849 configuration.verbose = true; 850 } 851 852 // Hack: support the keyword without parentheses in Groovy. getdontnote()853 public Object getdontnote() 854 { 855 dontnote(); 856 return null; 857 } 858 dontnote()859 public void dontnote() 860 { 861 dontnote(null); 862 } 863 dontnote(String filter)864 public void dontnote(String filter) 865 { 866 configuration.note = extendFilter(configuration.note, filter, true); 867 } 868 869 870 // Hack: support the keyword without parentheses in Groovy. getdontwarn()871 public Object getdontwarn() 872 { 873 dontwarn(); 874 return null; 875 } 876 dontwarn()877 public void dontwarn() 878 { 879 dontwarn(null); 880 } 881 dontwarn(String filter)882 public void dontwarn(String filter) 883 { 884 configuration.warn = extendFilter(configuration.warn, filter, true); 885 } 886 887 888 // Hack: support the keyword without parentheses in Groovy. getignorewarnings()889 public Object getignorewarnings() 890 { 891 ignorewarnings(); 892 return null; 893 } 894 ignorewarnings()895 public void ignorewarnings() 896 { 897 configuration.ignoreWarnings = true; 898 } 899 900 // Hack: support the keyword without parentheses in Groovy. getprintconfiguration()901 public Object getprintconfiguration() 902 { 903 printconfiguration(); 904 return null; 905 } 906 printconfiguration()907 public void printconfiguration() 908 { 909 configuration.printConfiguration = Configuration.STD_OUT; 910 } 911 printconfiguration(Object printConfiguration)912 public void printconfiguration(Object printConfiguration) 913 throws ParseException 914 { 915 configuration.printConfiguration = 916 getProject().file(printConfiguration); 917 } 918 919 // Hack: support the keyword without parentheses in Groovy. getdump()920 public Object getdump() 921 { 922 dump(); 923 return null; 924 } 925 dump()926 public void dump() 927 { 928 configuration.dump = Configuration.STD_OUT; 929 } 930 dump(Object dump)931 public void dump(Object dump) 932 throws ParseException 933 { 934 configuration.dump = getProject().file(dump); 935 } 936 937 938 // Class member methods. 939 field(Map memberSpecificationArgs)940 public void field(Map memberSpecificationArgs) 941 throws ParseException 942 { 943 if (classSpecification == null) 944 { 945 throw new IllegalArgumentException("The 'field' method can only be used nested inside a class specification."); 946 } 947 948 classSpecification.addField(createMemberSpecification(false, 949 false, 950 memberSpecificationArgs)); 951 } 952 953 constructor(Map memberSpecificationArgs)954 public void constructor(Map memberSpecificationArgs) 955 throws ParseException 956 { 957 if (classSpecification == null) 958 { 959 throw new IllegalArgumentException("The 'constructor' method can only be used nested inside a class specification."); 960 } 961 962 classSpecification.addMethod(createMemberSpecification(true, 963 true, 964 memberSpecificationArgs)); 965 } 966 967 method(Map memberSpecificationArgs)968 public void method(Map memberSpecificationArgs) 969 throws ParseException 970 { 971 if (classSpecification == null) 972 { 973 throw new IllegalArgumentException("The 'method' method can only be used nested inside a class specification."); 974 } 975 976 classSpecification.addMethod(createMemberSpecification(true, 977 false, 978 memberSpecificationArgs)); 979 } 980 981 982 // Gradle task execution. 983 984 @TaskAction proguard()985 public void proguard() 986 throws ParseException, IOException 987 { 988 // Weave the input jars and the output jars into a single class path, 989 // with lazy resolution of the files. 990 configuration.programJars = new ClassPath(); 991 992 int outJarIndex = 0; 993 994 int inJarCount = inJarCounts.size() == 0 ? -1 : 995 ((Integer)inJarCounts.get(0)).intValue(); 996 997 for (int inJarIndex = 0; inJarIndex < inJarFiles.size(); inJarIndex++) 998 { 999 configuration.programJars = 1000 extendClassPath(configuration.programJars, 1001 inJarFiles.get(inJarIndex), 1002 (Map)inJarFilters.get(inJarIndex), 1003 false); 1004 1005 while (inJarIndex == inJarCount - 1) 1006 { 1007 configuration.programJars = 1008 extendClassPath(configuration.programJars, 1009 outJarFiles.get(outJarIndex), 1010 (Map)outJarFilters.get(outJarIndex), 1011 true); 1012 1013 outJarIndex++; 1014 1015 inJarCount = inJarCounts.size() == outJarIndex ? -1 : 1016 ((Integer)inJarCounts.get(outJarIndex)).intValue(); 1017 } 1018 } 1019 1020 // Copy the library jars into a single class path, with lazy resolution 1021 // of the files. 1022 configuration.libraryJars = new ClassPath(); 1023 1024 for (int libraryJarIndex = 0; libraryJarIndex < libraryJarFiles.size(); libraryJarIndex++) 1025 { 1026 configuration.libraryJars = 1027 extendClassPath(configuration.libraryJars, 1028 libraryJarFiles.get(libraryJarIndex), 1029 (Map)libraryJarFilters.get(libraryJarIndex), 1030 false); 1031 } 1032 1033 // Lazily apply the external configuration files. 1034 ConfigurableFileCollection fileCollection = 1035 getProject().files(configurationFiles); 1036 1037 Iterator<File> files = fileCollection.iterator(); 1038 while (files.hasNext()) 1039 { 1040 ConfigurationParser parser = 1041 new ConfigurationParser(files.next(), System.getProperties()); 1042 1043 try 1044 { 1045 parser.parse(configuration); 1046 } 1047 finally 1048 { 1049 parser.close(); 1050 } 1051 } 1052 1053 // Make sure the code is processed. Gradle has already checked that it 1054 // was necessary. 1055 configuration.lastModified = Long.MAX_VALUE; 1056 1057 // Let the logging manager capture the standard output and errors from 1058 // ProGuard. 1059 LoggingManager loggingManager = getLogging(); 1060 loggingManager.captureStandardOutput(LogLevel.INFO); 1061 loggingManager.captureStandardError(LogLevel.WARN); 1062 1063 // Run ProGuard with the collected configuration. 1064 new ProGuard(configuration).execute(); 1065 1066 } 1067 1068 1069 // Small utility methods. 1070 1071 /** 1072 * Extends the given class path with the given filtered input or output 1073 * files. 1074 */ extendClassPath(ClassPath classPath, Object files, Map filterArgs, boolean output)1075 private ClassPath extendClassPath(ClassPath classPath, 1076 Object files, 1077 Map filterArgs, 1078 boolean output) 1079 { 1080 ConfigurableFileCollection fileCollection = getProject().files(files); 1081 1082 if (classPath == null) 1083 { 1084 classPath = new ClassPath(); 1085 } 1086 1087 Iterator fileIterator = fileCollection.iterator(); 1088 while (fileIterator.hasNext()) 1089 { 1090 File file = (File)fileIterator.next(); 1091 if (output || file.exists()) 1092 { 1093 // Create the class path entry. 1094 ClassPathEntry classPathEntry = new ClassPathEntry(file, output); 1095 1096 // Add any filters to the class path entry. 1097 if (filterArgs != null) 1098 { 1099 classPathEntry.setFilter(ListUtil.commaSeparatedList((String)filterArgs.get("filter"))); 1100 classPathEntry.setJarFilter(ListUtil.commaSeparatedList((String)filterArgs.get("jarfilter"))); 1101 classPathEntry.setWarFilter(ListUtil.commaSeparatedList((String)filterArgs.get("warfilter"))); 1102 classPathEntry.setEarFilter(ListUtil.commaSeparatedList((String)filterArgs.get("earfilter"))); 1103 classPathEntry.setZipFilter(ListUtil.commaSeparatedList((String)filterArgs.get("zipfilter"))); 1104 } 1105 1106 classPath.add(classPathEntry); 1107 } 1108 } 1109 1110 return classPath; 1111 } 1112 1113 1114 /** 1115 * Creates specifications to keep classes and class members, based on the 1116 * given parameters. 1117 */ createKeepClassSpecification(boolean allowShrinking, boolean markClasses, boolean markConditionally, Map keepArgs, String classSpecificationString)1118 private KeepClassSpecification createKeepClassSpecification(boolean allowShrinking, 1119 boolean markClasses, 1120 boolean markConditionally, 1121 Map keepArgs, 1122 String classSpecificationString) 1123 throws ParseException 1124 { 1125 ClassSpecification classSpecification = 1126 createClassSpecification(classSpecificationString); 1127 1128 return 1129 createKeepClassSpecification(allowShrinking, 1130 markClasses, 1131 markConditionally, 1132 keepArgs, 1133 classSpecification); 1134 } 1135 1136 1137 /** 1138 * Creates specifications to keep classes and class members, based on the 1139 * given parameters. 1140 */ createKeepClassSpecification(boolean allowShrinking, boolean markClasses, boolean markConditionally, Map classSpecificationArgs, Closure classMembersClosure)1141 private KeepClassSpecification createKeepClassSpecification(boolean allowShrinking, 1142 boolean markClasses, 1143 boolean markConditionally, 1144 Map classSpecificationArgs, 1145 Closure classMembersClosure) 1146 throws ParseException 1147 { 1148 ClassSpecification classSpecification = 1149 createClassSpecification(classSpecificationArgs, 1150 classMembersClosure); 1151 return 1152 createKeepClassSpecification(allowShrinking, 1153 markClasses, 1154 markConditionally, 1155 classSpecificationArgs, 1156 classSpecification); 1157 } 1158 1159 1160 /** 1161 * Creates specifications to keep classes and class members, based on the 1162 * given parameters. 1163 */ createKeepClassSpecification(boolean allowShrinking, boolean markClasses, boolean markConditionally, Map keepArgs, ClassSpecification classSpecification)1164 private KeepClassSpecification createKeepClassSpecification(boolean allowShrinking, 1165 boolean markClasses, 1166 boolean markConditionally, 1167 Map keepArgs, 1168 ClassSpecification classSpecification) 1169 { 1170 return 1171 new KeepClassSpecification(markClasses, 1172 markConditionally, 1173 retrieveBoolean(keepArgs, "allowshrinking", allowShrinking), 1174 retrieveBoolean(keepArgs, "allowoptimization", false), 1175 retrieveBoolean(keepArgs, "allowobfuscation", false), 1176 classSpecification); 1177 } 1178 1179 1180 /** 1181 * Creates specifications to keep classes and class members, based on the 1182 * given ProGuard-style class specification. 1183 */ createClassSpecification(String classSpecificationString)1184 private ClassSpecification createClassSpecification(String classSpecificationString) 1185 throws ParseException 1186 { 1187 try 1188 { 1189 ConfigurationParser parser = 1190 new ConfigurationParser(new String[] { classSpecificationString }, null); 1191 1192 try 1193 { 1194 return parser.parseClassSpecificationArguments(); 1195 } 1196 finally 1197 { 1198 parser.close(); 1199 } 1200 } 1201 catch (IOException e) 1202 { 1203 throw new ParseException(e.getMessage()); 1204 } 1205 } 1206 1207 1208 /** 1209 * Creates a specification of classes and class members, based on the 1210 * given parameters. 1211 */ createClassSpecification(Map classSpecificationArgs, Closure classMembersClosure)1212 private ClassSpecification createClassSpecification(Map classSpecificationArgs, 1213 Closure classMembersClosure) 1214 throws ParseException 1215 { 1216 // Extract the arguments. 1217 String access = (String)classSpecificationArgs.get("access"); 1218 String annotation = (String)classSpecificationArgs.get("annotation"); 1219 String type = (String)classSpecificationArgs.get("type"); 1220 String name = (String)classSpecificationArgs.get("name"); 1221 String extendsAnnotation = (String)classSpecificationArgs.get("extendsannotation"); 1222 String extends_ = (String)classSpecificationArgs.get("extends"); 1223 if (extends_ == null) 1224 { 1225 extends_ = (String)classSpecificationArgs.get("implements"); 1226 } 1227 1228 // Create the class specification. 1229 ClassSpecification classSpecification = 1230 new ClassSpecification(null, 1231 requiredClassAccessFlags(true, access, type), 1232 requiredClassAccessFlags(false, access, type), 1233 annotation != null ? ClassUtil.internalType(annotation) : null, 1234 name != null ? ClassUtil.internalClassName(name) : null, 1235 extendsAnnotation != null ? ClassUtil.internalType(extendsAnnotation) : null, 1236 extends_ != null ? ClassUtil.internalClassName(extends_) : null); 1237 1238 // Initialize the class specification with its closure. 1239 if (classMembersClosure != null) 1240 { 1241 // Temporarily remember the class specification, so we can add 1242 // class member specifications. 1243 this.classSpecification = classSpecification; 1244 classMembersClosure.call(classSpecification); 1245 this.classSpecification = null; 1246 } 1247 1248 return classSpecification; 1249 } 1250 1251 1252 /** 1253 * Parses the class access flags that must be set (or not), based on the 1254 * given ProGuard-style flag specification. 1255 */ requiredClassAccessFlags(boolean set, String access, String type)1256 private int requiredClassAccessFlags(boolean set, 1257 String access, 1258 String type) 1259 throws ParseException 1260 { 1261 int accessFlags = 0; 1262 1263 if (access != null) 1264 { 1265 StringTokenizer tokenizer = new StringTokenizer(access, " ,"); 1266 while (tokenizer.hasMoreTokens()) 1267 { 1268 String token = tokenizer.nextToken(); 1269 1270 if (token.startsWith("!") ^ set) 1271 { 1272 String strippedToken = token.startsWith("!") ? 1273 token.substring(1) : 1274 token; 1275 1276 int accessFlag = 1277 strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : 1278 strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : 1279 strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : 1280 strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : 1281 strippedToken.equals(ClassConstants.EXTERNAL_ACC_ANNOTATION) ? ClassConstants.INTERNAL_ACC_ANNOTATTION : 1282 0; 1283 1284 if (accessFlag == 0) 1285 { 1286 throw new ParseException("Incorrect class access modifier ["+strippedToken+"]"); 1287 } 1288 1289 accessFlags |= accessFlag; 1290 } 1291 } 1292 } 1293 1294 if (type != null && (type.startsWith("!") ^ set)) 1295 { 1296 int accessFlag = 1297 type.equals("class") ? 0 : 1298 type.equals( ClassConstants.EXTERNAL_ACC_INTERFACE) || 1299 type.equals("!" + ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE : 1300 type.equals( ClassConstants.EXTERNAL_ACC_ENUM) || 1301 type.equals("!" + ClassConstants.EXTERNAL_ACC_ENUM) ? ClassConstants.INTERNAL_ACC_ENUM : 1302 -1; 1303 if (accessFlag == -1) 1304 { 1305 throw new ParseException("Incorrect class type ["+type+"]"); 1306 } 1307 1308 accessFlags |= accessFlag; 1309 } 1310 1311 return accessFlags; 1312 } 1313 1314 1315 /** 1316 * Creates a specification of class members, based on the given parameters. 1317 */ createMemberSpecification(boolean isMethod, boolean isConstructor, Map classSpecificationArgs)1318 private MemberSpecification createMemberSpecification(boolean isMethod, 1319 boolean isConstructor, 1320 Map classSpecificationArgs) 1321 throws ParseException 1322 { 1323 // Extract the arguments. 1324 String access = (String)classSpecificationArgs.get("access"); 1325 String type = (String)classSpecificationArgs.get("type"); 1326 String annotation = (String)classSpecificationArgs.get("annotation"); 1327 String name = (String)classSpecificationArgs.get("name"); 1328 String parameters = (String)classSpecificationArgs.get("parameters"); 1329 1330 // Perform some basic conversions and checks on the attributes. 1331 if (annotation != null) 1332 { 1333 annotation = ClassUtil.internalType(annotation); 1334 } 1335 1336 if (isMethod) 1337 { 1338 if (isConstructor) 1339 { 1340 if (type != null) 1341 { 1342 throw new ParseException("Type attribute not allowed in constructor specification ["+type+"]"); 1343 } 1344 1345 if (parameters != null) 1346 { 1347 type = ClassConstants.EXTERNAL_TYPE_VOID; 1348 } 1349 1350 name = ClassConstants.INTERNAL_METHOD_NAME_INIT; 1351 } 1352 else if ((type != null) ^ (parameters != null)) 1353 { 1354 throw new ParseException("Type and parameters attributes must always be present in combination in method specification"); 1355 } 1356 } 1357 else 1358 { 1359 if (parameters != null) 1360 { 1361 throw new ParseException("Parameters attribute not allowed in field specification ["+parameters+"]"); 1362 } 1363 } 1364 1365 List parameterList = ListUtil.commaSeparatedList(parameters); 1366 1367 String descriptor = 1368 parameters != null ? ClassUtil.internalMethodDescriptor(type, parameterList) : 1369 type != null ? ClassUtil.internalType(type) : 1370 null; 1371 1372 return new MemberSpecification(requiredMemberAccessFlags(true, access), 1373 requiredMemberAccessFlags(false, access), 1374 annotation, 1375 name, 1376 descriptor); 1377 } 1378 1379 1380 /** 1381 * Parses the class member access flags that must be set (or not), based on 1382 * the given ProGuard-style flag specification. 1383 */ requiredMemberAccessFlags(boolean set, String access)1384 private int requiredMemberAccessFlags(boolean set, 1385 String access) 1386 throws ParseException 1387 { 1388 int accessFlags = 0; 1389 1390 if (access != null) 1391 { 1392 StringTokenizer tokenizer = new StringTokenizer(access, " ,"); 1393 while (tokenizer.hasMoreTokens()) 1394 { 1395 String token = tokenizer.nextToken(); 1396 1397 if (token.startsWith("!") ^ set) 1398 { 1399 String strippedToken = token.startsWith("!") ? 1400 token.substring(1) : 1401 token; 1402 1403 int accessFlag = 1404 strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : 1405 strippedToken.equals(ClassConstants.EXTERNAL_ACC_PRIVATE) ? ClassConstants.INTERNAL_ACC_PRIVATE : 1406 strippedToken.equals(ClassConstants.EXTERNAL_ACC_PROTECTED) ? ClassConstants.INTERNAL_ACC_PROTECTED : 1407 strippedToken.equals(ClassConstants.EXTERNAL_ACC_STATIC) ? ClassConstants.INTERNAL_ACC_STATIC : 1408 strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : 1409 strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED : 1410 strippedToken.equals(ClassConstants.EXTERNAL_ACC_VOLATILE) ? ClassConstants.INTERNAL_ACC_VOLATILE : 1411 strippedToken.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT) ? ClassConstants.INTERNAL_ACC_TRANSIENT : 1412 strippedToken.equals(ClassConstants.EXTERNAL_ACC_BRIDGE) ? ClassConstants.INTERNAL_ACC_BRIDGE : 1413 strippedToken.equals(ClassConstants.EXTERNAL_ACC_VARARGS) ? ClassConstants.INTERNAL_ACC_VARARGS : 1414 strippedToken.equals(ClassConstants.EXTERNAL_ACC_NATIVE) ? ClassConstants.INTERNAL_ACC_NATIVE : 1415 strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : 1416 strippedToken.equals(ClassConstants.EXTERNAL_ACC_STRICT) ? ClassConstants.INTERNAL_ACC_STRICT : 1417 strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : 1418 0; 1419 1420 if (accessFlag == 0) 1421 { 1422 throw new ParseException("Incorrect class member access modifier ["+strippedToken+"]"); 1423 } 1424 1425 accessFlags |= accessFlag; 1426 } 1427 } 1428 } 1429 1430 return accessFlags; 1431 } 1432 1433 1434 /** 1435 * Retrieves a specified boolean flag from the given map. 1436 */ retrieveBoolean(Map args, String name, boolean defaultValue)1437 private boolean retrieveBoolean(Map args, String name, boolean defaultValue) 1438 { 1439 if (args == null) 1440 { 1441 return defaultValue; 1442 } 1443 1444 Object arg = args.get(name); 1445 1446 return arg == null ? defaultValue : ((Boolean)arg).booleanValue(); 1447 } 1448 1449 1450 /** 1451 * Adds the given class specification to the given list, creating a new list 1452 * if necessary. 1453 */ extendClassSpecifications(List classSpecifications, ClassSpecification classSpecification)1454 private List extendClassSpecifications(List classSpecifications, 1455 ClassSpecification classSpecification) 1456 { 1457 if (classSpecifications == null) 1458 { 1459 classSpecifications = new ArrayList(); 1460 } 1461 1462 classSpecifications.add(classSpecification); 1463 1464 return classSpecifications; 1465 } 1466 1467 1468 /** 1469 * Adds the given class specifications to the given list, creating a new 1470 * list if necessary. 1471 */ extendClassSpecifications(List classSpecifications, List additionalClassSpecifications)1472 private List extendClassSpecifications(List classSpecifications, 1473 List additionalClassSpecifications) 1474 { 1475 if (additionalClassSpecifications != null) 1476 { 1477 if (classSpecifications == null) 1478 { 1479 classSpecifications = new ArrayList(); 1480 } 1481 1482 classSpecifications.addAll(additionalClassSpecifications); 1483 } 1484 1485 return classSpecifications; 1486 } 1487 1488 1489 /** 1490 * Adds the given filter to the given list, creating a new list if 1491 * necessary. 1492 */ extendFilter(List filter, String filterString)1493 private List extendFilter(List filter, 1494 String filterString) 1495 { 1496 return extendFilter(filter, filterString, false); 1497 } 1498 1499 1500 /** 1501 * Adds the given filter to the given list, creating a new list if 1502 * necessary. External class names are converted to internal class names, 1503 * if requested. 1504 */ extendFilter(List filter, String filterString, boolean convertExternalClassNames)1505 private List extendFilter(List filter, 1506 String filterString, 1507 boolean convertExternalClassNames) 1508 { 1509 if (filter == null) 1510 { 1511 filter = new ArrayList(); 1512 } 1513 1514 if (filterString == null) 1515 { 1516 // Clear the filter to keep all names. 1517 filter.clear(); 1518 } 1519 else 1520 { 1521 if (convertExternalClassNames) 1522 { 1523 filterString = ClassUtil.internalClassName(filterString); 1524 } 1525 1526 // Append the filter. 1527 filter.addAll(ListUtil.commaSeparatedList(filterString)); 1528 } 1529 1530 return filter; 1531 } 1532 } 1533