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