• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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