1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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 */
21package proguard;
22
23import proguard.classfile.attribute.annotation.visitor.*;
24import proguard.classfile.attribute.visitor.AllAttributeVisitor;
25import proguard.classfile.visitor.*;
26
27import java.util.List;
28
29/**
30 * This factory creates visitors to efficiently travel to specified classes and
31 * class members.
32 *
33 * @author Eric Lafortune
34 */
35public class ClassSpecificationVisitorFactory
36{
37    /**
38     * Constructs a ClassPoolVisitor to efficiently travel to the specified
39     * classes and class members.
40     *
41     * @param keepClassSpecifications the list of KeepClassSpecification
42     *                                instances, defining of the classes and
43     *                                class members to visit.
44     * @param classVisitor            the ClassVisitor to be applied to matching
45     *                                classes.
46     * @param memberVisitor           the MemberVisitor to be applied to matching
47     *                                class members.
48     */
49    public static ClassPoolVisitor createClassPoolVisitor(List          keepClassSpecifications,
50                                                          ClassVisitor  classVisitor,
51                                                          MemberVisitor memberVisitor,
52                                                          boolean       shrinking,
53                                                          boolean       optimizing,
54                                                          boolean       obfuscating)
55    {
56        MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
57
58        if (keepClassSpecifications != null)
59        {
60            for (int index = 0; index < keepClassSpecifications.size(); index++)
61            {
62                KeepClassSpecification keepClassSpecification =
63                    (KeepClassSpecification)keepClassSpecifications.get(index);
64
65                if ((shrinking   && !keepClassSpecification.allowShrinking)    ||
66                    (optimizing  && !keepClassSpecification.allowOptimization) ||
67                    (obfuscating && !keepClassSpecification.allowObfuscation))
68                {
69                    multiClassPoolVisitor.addClassPoolVisitor(
70                        createClassPoolVisitor(keepClassSpecification,
71                                               classVisitor,
72                                               memberVisitor));
73                }
74            }
75        }
76
77        return multiClassPoolVisitor;
78    }
79
80
81    /**
82     * Constructs a ClassPoolVisitor to efficiently travel to the specified
83     * classes and class members.
84     *
85     * @param classSpecifications the list of ClassSpecification instances,
86     *                            defining of the classes and class members to
87     *                            visit.
88     * @param classVisitor        the ClassVisitor to be applied to matching
89     *                            classes.
90     * @param memberVisitor       the MemberVisitor to be applied to matching
91     *                            class members.
92     */
93    public static ClassPoolVisitor createClassPoolVisitor(List          classSpecifications,
94                                                          ClassVisitor  classVisitor,
95                                                          MemberVisitor memberVisitor)
96    {
97        MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
98
99        if (classSpecifications != null)
100        {
101            for (int index = 0; index < classSpecifications.size(); index++)
102            {
103                ClassSpecification classSpecification =
104                    (ClassSpecification)classSpecifications.get(index);
105
106                multiClassPoolVisitor.addClassPoolVisitor(
107                    createClassPoolVisitor(classSpecification,
108                                           classVisitor,
109                                           memberVisitor));
110            }
111        }
112
113        return multiClassPoolVisitor;
114    }
115
116
117    /**
118     * Constructs a ClassPoolVisitor to efficiently travel to the specified
119     * classes and class members.
120     *
121     * @param keepClassSpecification the specifications of the class(es) and class
122     *                          members to visit.
123     * @param classVisitor      the ClassVisitor to be applied to matching
124     *                          classes.
125     * @param memberVisitor     the MemberVisitor to be applied to matching
126     *                          class members.
127     */
128    private static ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification,
129                                                           ClassVisitor      classVisitor,
130                                                           MemberVisitor     memberVisitor)
131    {
132        // Don't  visit the classes if not specified.
133        if (!keepClassSpecification.markClasses &&
134            !keepClassSpecification.markConditionally)
135        {
136            classVisitor = null;
137        }
138
139        // If specified, let the marker visit the class and its class
140        // members conditionally.
141        if (keepClassSpecification.markConditionally)
142        {
143            // Combine both visitors.
144            ClassVisitor composedClassVisitor =
145                createCombinedClassVisitor(keepClassSpecification,
146                                           classVisitor,
147                                           memberVisitor);
148
149            // Replace the class visitor.
150            classVisitor =
151                createClassMemberTester(keepClassSpecification,
152                                        composedClassVisitor);
153
154            // Discard the member visitor, because it has already been included.
155            memberVisitor = null;
156        }
157
158        return createClassPoolVisitor((ClassSpecification)keepClassSpecification,
159                                      classVisitor,
160                                      memberVisitor);
161    }
162
163
164    /**
165     * Constructs a ClassPoolVisitor to efficiently travel to the specified
166     * classes and class members.
167     *
168     * @param classSpecification the specifications of the class(es) and class
169     *                           members to visit.
170     * @param classVisitor       the ClassVisitor to be applied to matching
171     *                           classes.
172     * @param memberVisitor      the MemberVisitor to be applied to matching
173     *                           class members.
174     */
175    private static ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification,
176                                                           ClassVisitor       classVisitor,
177                                                           MemberVisitor      memberVisitor)
178    {
179        // Combine both visitors.
180        ClassVisitor composedClassVisitor =
181            createCombinedClassVisitor(classSpecification,
182                                       classVisitor,
183                                       memberVisitor);
184
185        // By default, start visiting from the named class name, if specified.
186        String className = classSpecification.className;
187
188        // Although we may have to start from the extended class.
189        String extendsAnnotationType = classSpecification.extendsAnnotationType;
190        String extendsClassName      = classSpecification.extendsClassName;
191
192        // If wildcarded, only visit classes with matching names.
193        if (className != null &&
194            (extendsAnnotationType != null ||
195             extendsClassName      != null ||
196             containsWildCards(className)))
197        {
198            composedClassVisitor =
199                new ClassNameFilter(className, composedClassVisitor);
200
201            // We'll have to visit all classes now.
202            className = null;
203        }
204
205        // If specified, only visit classes with the right annotation.
206        String annotationType = classSpecification.annotationType;
207
208        if (annotationType != null)
209        {
210            composedClassVisitor =
211                new AllAttributeVisitor(
212                new AllAnnotationVisitor(
213                new AnnotationTypeFilter(annotationType,
214                new AnnotatedClassVisitor(composedClassVisitor))));
215        }
216
217        // If specified, only visit classes with the right access flags.
218        if (classSpecification.requiredSetAccessFlags   != 0 ||
219            classSpecification.requiredUnsetAccessFlags != 0)
220        {
221            composedClassVisitor =
222                new ClassAccessFilter(classSpecification.requiredSetAccessFlags,
223                                      classSpecification.requiredUnsetAccessFlags,
224                                      composedClassVisitor);
225        }
226
227        // If it's specified, start visiting from the extended class.
228        if (extendsAnnotationType != null ||
229            extendsClassName      != null)
230        {
231            // Start visiting from the extended class.
232            composedClassVisitor =
233                new ClassHierarchyTraveler(false, false, false, true,
234                                           composedClassVisitor);
235
236            // If specified, only visit extended classes with the right annotation.
237            if (extendsAnnotationType != null)
238            {
239                composedClassVisitor =
240                    new AllAttributeVisitor(
241                    new AllAnnotationVisitor(
242                    new AnnotationTypeFilter(extendsAnnotationType,
243                    new AnnotatedClassVisitor(composedClassVisitor))));
244            }
245
246            // If specified, only visit extended classes with matching names.
247            if (extendsClassName != null)
248            {
249                // If wildcarded, only visit extended classes with matching names.
250                if (containsWildCards(extendsClassName))
251                {
252                    composedClassVisitor =
253                        new ClassNameFilter(extendsClassName,
254                                            composedClassVisitor);
255                }
256                else
257                {
258                    // Start visiting from the named extended class.
259                    className = extendsClassName;
260                }
261            }
262        }
263
264        // If specified, visit a single named class, otherwise visit all classes.
265        return className != null ?
266            (ClassPoolVisitor)new NamedClassVisitor(composedClassVisitor, className) :
267            (ClassPoolVisitor)new AllClassVisitor(composedClassVisitor);
268    }
269
270
271    /**
272     * Constructs a ClassVisitor to efficiently travel to the specified
273     * classes and class members.
274     *
275     * @param classSpecification the specifications of the class(es) and class
276     *                           members to visit.
277     * @param classVisitor       the ClassVisitor to be applied to matching
278     *                           classes.
279     * @param memberVisitor      the MemberVisitor to be applied to matching
280     *                           class members.
281     */
282    private static ClassVisitor createCombinedClassVisitor(ClassSpecification classSpecification,
283                                                           ClassVisitor       classVisitor,
284                                                           MemberVisitor      memberVisitor)
285    {
286        // Don't visit any members if there aren't any member specifications.
287        if (classSpecification.fieldSpecifications  == null &&
288            classSpecification.methodSpecifications == null)
289        {
290            memberVisitor = null;
291        }
292
293        // The class visitor for classes and their members.
294        MultiClassVisitor multiClassVisitor = new MultiClassVisitor();
295
296        // If specified, let the class visitor visit the class itself.
297        if (classVisitor != null)
298        {
299            // This class visitor may be the only one.
300            if (memberVisitor == null)
301            {
302                return classVisitor;
303            }
304
305            multiClassVisitor.addClassVisitor(classVisitor);
306        }
307
308        // If specified, let the member info visitor visit the class members.
309        if (memberVisitor != null)
310        {
311            ClassVisitor memberClassVisitor =
312                createClassVisitor(classSpecification, memberVisitor);
313
314            // This class visitor may be the only one.
315            if (classVisitor == null)
316            {
317                return memberClassVisitor;
318            }
319
320            multiClassVisitor.addClassVisitor(memberClassVisitor);
321        }
322
323        return multiClassVisitor;
324    }
325
326
327    /**
328     * Constructs a ClassVisitor to efficiently travel to the specified class
329     * members.
330     *
331     * @param classSpecification the specifications of the class members to visit.
332     * @param memberVisitor      the MemberVisitor to be applied to matching
333     *                           class members.
334     */
335    private static ClassVisitor createClassVisitor(ClassSpecification classSpecification,
336                                                   MemberVisitor      memberVisitor)
337    {
338        MultiClassVisitor multiClassVisitor = new MultiClassVisitor();
339
340        addMemberVisitors(classSpecification.fieldSpecifications,  true,  multiClassVisitor, memberVisitor);
341        addMemberVisitors(classSpecification.methodSpecifications, false, multiClassVisitor, memberVisitor);
342
343        // Mark the class member in this class and in super classes.
344        return new ClassHierarchyTraveler(true, true, false, false,
345                                          multiClassVisitor);
346    }
347
348
349    /**
350     * Adds elements to the given MultiClassVisitor, to apply the given
351     * MemberVisitor to all class members that match the given List
352     * of options (of the given type).
353     */
354    private static void addMemberVisitors(List              memberSpecifications,
355                                          boolean           isField,
356                                          MultiClassVisitor multiClassVisitor,
357                                          MemberVisitor     memberVisitor)
358    {
359        if (memberSpecifications != null)
360        {
361            for (int index = 0; index < memberSpecifications.size(); index++)
362            {
363                MemberSpecification memberSpecification =
364                    (MemberSpecification)memberSpecifications.get(index);
365
366                multiClassVisitor.addClassVisitor(
367                    createClassVisitor(memberSpecification,
368                                       isField,
369                                       memberVisitor));
370            }
371        }
372    }
373
374
375    /**
376     * Constructs a ClassVisitor that conditionally applies the given
377     * ClassVisitor to all classes that contain the given class members.
378     */
379    private static ClassVisitor createClassMemberTester(ClassSpecification classSpecification,
380                                                        ClassVisitor       classVisitor)
381    {
382        // Create a linked list of conditional visitors, for fields and for
383        // methods.
384        return createClassMemberTester(classSpecification.fieldSpecifications,
385                                       true,
386               createClassMemberTester(classSpecification.methodSpecifications,
387                                       false,
388                                       classVisitor));
389    }
390
391
392    /**
393     * Constructs a ClassVisitor that conditionally applies the given
394     * ClassVisitor to all classes that contain the given List of class
395     * members (of the given type).
396     */
397    private static ClassVisitor createClassMemberTester(List         memberSpecifications,
398                                                        boolean      isField,
399                                                        ClassVisitor classVisitor)
400    {
401        // Create a linked list of conditional visitors.
402        if (memberSpecifications != null)
403        {
404            for (int index = 0; index < memberSpecifications.size(); index++)
405            {
406                MemberSpecification memberSpecification =
407                    (MemberSpecification)memberSpecifications.get(index);
408
409                classVisitor =
410                    createClassVisitor(memberSpecification,
411                                       isField,
412                                       new MemberToClassVisitor(classVisitor));
413            }
414        }
415
416        return classVisitor;
417    }
418
419
420    /**
421     * Creates a new ClassVisitor to efficiently travel to the specified class
422     * members.
423     *
424     * @param memberSpecification the specification of the class member(s) to
425     *                            visit.
426     * @param memberVisitor       the MemberVisitor to be applied to matching
427     *                            class member(s).
428     */
429    private static ClassVisitor createClassVisitor(MemberSpecification memberSpecification,
430                                                   boolean             isField,
431                                                   MemberVisitor       memberVisitor)
432    {
433        String name       = memberSpecification.name;
434        String descriptor = memberSpecification.descriptor;
435
436        // If name or descriptor are not fully specified, only visit matching
437        // class members.
438        boolean fullySpecified =
439            name       != null &&
440            descriptor != null &&
441            !containsWildCards(name) &&
442            !containsWildCards(descriptor);
443
444        if (!fullySpecified)
445        {
446            if (descriptor != null)
447            {
448                memberVisitor =
449                    new MemberDescriptorFilter(descriptor, memberVisitor);
450            }
451
452            if (name != null)
453            {
454                memberVisitor =
455                    new MemberNameFilter(name, memberVisitor);
456            }
457        }
458
459        // If specified, only visit class members with the right annotation.
460        if (memberSpecification.annotationType != null)
461        {
462            memberVisitor =
463                new AllAttributeVisitor(
464                new AllAnnotationVisitor(
465                new AnnotationTypeFilter(memberSpecification.annotationType,
466                new AnnotationToMemberVisitor(memberVisitor))));
467        }
468
469        // If any access flags are specified, only visit matching class members.
470        if (memberSpecification.requiredSetAccessFlags   != 0 ||
471            memberSpecification.requiredUnsetAccessFlags != 0)
472        {
473            memberVisitor =
474                new MemberAccessFilter(memberSpecification.requiredSetAccessFlags,
475                                       memberSpecification.requiredUnsetAccessFlags,
476                                       memberVisitor);
477        }
478
479        // Depending on what's specified, visit a single named class member,
480        // or all class members, filtering the matching ones.
481        return isField ?
482            fullySpecified ?
483                (ClassVisitor)new NamedFieldVisitor(name, descriptor, memberVisitor) :
484                (ClassVisitor)new AllFieldVisitor(memberVisitor) :
485            fullySpecified ?
486                (ClassVisitor)new NamedMethodVisitor(name, descriptor, memberVisitor) :
487                (ClassVisitor)new AllMethodVisitor(memberVisitor);
488    }
489
490
491    // Small utility methods.
492
493    private static boolean containsWildCards(String string)
494    {
495        return string != null &&
496            (string.indexOf('*')   >= 0 ||
497             string.indexOf('?')   >= 0 ||
498             string.indexOf('%')   >= 0 ||
499             string.indexOf(',')   >= 0 ||
500             string.indexOf("///") >= 0);
501    }
502}
503