• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 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;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.util.*;
25 import proguard.classfile.visitor.*;
26 
27 import java.util.List;
28 
29 /**
30  * This class checks if the user has forgotten to fully qualify any classes
31  * in the configuration.
32  *
33  * @author Eric Lafortune
34  */
35 public class FullyQualifiedClassNameChecker
36 extends      SimplifiedVisitor
37 implements   ClassVisitor
38 {
39     private static final String INVALID_CLASS_EXTENSION = ClassUtil.internalClassName(ClassConstants.CLASS_FILE_EXTENSION);
40 
41 
42     private final ClassPool      programClassPool;
43     private final ClassPool      libraryClassPool;
44     private final WarningPrinter notePrinter;
45 
46 
47     /**
48      * Creates a new FullyQualifiedClassNameChecker.
49      */
FullyQualifiedClassNameChecker(ClassPool programClassPool, ClassPool libraryClassPool, WarningPrinter notePrinter)50     public FullyQualifiedClassNameChecker(ClassPool      programClassPool,
51                                           ClassPool      libraryClassPool,
52                                           WarningPrinter notePrinter)
53     {
54         this.programClassPool = programClassPool;
55         this.libraryClassPool = libraryClassPool;
56         this.notePrinter      = notePrinter;
57     }
58 
59 
60     /**
61      * Checks the classes mentioned in the given class specifications, printing
62      * notes if necessary.
63      */
checkClassSpecifications(List classSpecifications)64     public void checkClassSpecifications(List classSpecifications)
65     {
66         if (classSpecifications != null)
67         {
68             for (int index = 0; index < classSpecifications.size(); index++)
69             {
70                 ClassSpecification classSpecification =
71                     (ClassSpecification)classSpecifications.get(index);
72 
73                 checkType(classSpecification.annotationType);
74                 checkClassName(classSpecification.className);
75                 checkType(classSpecification.extendsAnnotationType);
76                 checkClassName(classSpecification.extendsClassName);
77 
78                 checkMemberSpecifications(classSpecification.fieldSpecifications,  true);
79                 checkMemberSpecifications(classSpecification.methodSpecifications, false);
80             }
81         }
82     }
83 
84 
85     /**
86      * Checks the classes mentioned in the given class member specifications,
87      * printing notes if necessary.
88      */
checkMemberSpecifications(List memberSpecifications, boolean isField)89     private void checkMemberSpecifications(List memberSpecifications, boolean isField)
90     {
91         if (memberSpecifications != null)
92         {
93             for (int index = 0; index < memberSpecifications.size(); index++)
94             {
95                 MemberSpecification memberSpecification =
96                     (MemberSpecification)memberSpecifications.get(index);
97 
98                 checkType(memberSpecification.annotationType);
99 
100                 if (isField)
101                 {
102                      checkType(memberSpecification.descriptor);
103                 }
104                 else
105                 {
106                     checkDescriptor(memberSpecification.descriptor);
107                 }
108             }
109         }
110     }
111 
112 
113     /**
114      * Checks the classes mentioned in the given class member descriptor,
115      * printing notes if necessary.
116      */
checkDescriptor(String descriptor)117     private void checkDescriptor(String descriptor)
118     {
119         if (descriptor != null)
120         {
121             InternalTypeEnumeration internalTypeEnumeration =
122                 new InternalTypeEnumeration(descriptor);
123 
124             checkType(internalTypeEnumeration.returnType());
125 
126             while (internalTypeEnumeration.hasMoreTypes())
127             {
128                 checkType(internalTypeEnumeration.nextType());
129             }
130         }
131     }
132 
133 
134     /**
135      * Checks the class mentioned in the given type (if any),
136      * printing notes if necessary.
137      */
checkType(String type)138     private void checkType(String type)
139     {
140         if (type != null)
141         {
142             checkClassName(ClassUtil.internalClassNameFromType(type));
143         }
144     }
145 
146 
147     /**
148      * Checks the specified class (if any),
149      * printing notes if necessary.
150      */
checkClassName(String className)151     private void checkClassName(String className)
152     {
153         if (className != null                            &&
154             !containsWildCards(className)                &&
155             programClassPool.getClass(className) == null &&
156             libraryClassPool.getClass(className) == null &&
157             notePrinter.accepts(className))
158         {
159             notePrinter.print(className,
160                               "Note: the configuration refers to the unknown class '" +
161                               ClassUtil.externalClassName(className) + "'");
162 
163             // Strip "/class" or replace the package name by a wildcard.
164             int lastSeparatorIndex =
165                 className.lastIndexOf(ClassConstants.PACKAGE_SEPARATOR);
166 
167             String fullyQualifiedClassName =
168                 className.endsWith(INVALID_CLASS_EXTENSION) ?
169                     className.substring(0, lastSeparatorIndex) :
170                     "**" + ClassConstants.PACKAGE_SEPARATOR + className.substring(lastSeparatorIndex + 1);
171 
172             // Suggest matching classes.
173             ClassNameFilter classNameFilter =
174                 new ClassNameFilter(fullyQualifiedClassName, this);
175 
176             programClassPool.classesAccept(classNameFilter);
177             libraryClassPool.classesAccept(classNameFilter);
178         }
179     }
180 
181 
containsWildCards(String string)182     private static boolean containsWildCards(String string)
183     {
184         return string != null &&
185             (string.indexOf('!')   >= 0 ||
186              string.indexOf('*')   >= 0 ||
187              string.indexOf('?')   >= 0 ||
188              string.indexOf(',')   >= 0 ||
189              string.indexOf("///") >= 0);
190     }
191 
192 
193     // Implementations for ClassVisitor.
194 
visitAnyClass(Clazz clazz)195     public void visitAnyClass(Clazz clazz)
196     {
197         System.out.println("      Maybe you meant the fully qualified name '" +
198                            ClassUtil.externalClassName(clazz.getName()) + "'?");
199     }
200 }
201