8165646: (jdeprscan) adjust tool output to improve clarity

Reviewed-by: jjg, psandoz
This commit is contained in:
Stuart Marks 2016-11-01 11:28:16 -07:00
parent d23149e827
commit 2380985895
6 changed files with 147 additions and 115 deletions

View File

@ -638,6 +638,8 @@ public class Main implements DiagnosticListener<JavaFileObject> {
// now the scanning phase
boolean scanStatus = true;
switch (scanMode) {
case LIST:
for (DeprData dd : deprList) {
@ -661,24 +663,22 @@ public class Main implements DiagnosticListener<JavaFileObject> {
Scan scan = new Scan(out, err, cp, db, verbose);
for (String a : args) {
boolean success;
boolean s;
if (a.endsWith(".jar")) {
success = scan.scanJar(a);
s = scan.scanJar(a);
} else if (a.endsWith(".class")) {
s = scan.processClassFile(a);
} else if (Files.isDirectory(Paths.get(a))) {
success = scan.scanDir(a);
s = scan.scanDir(a);
} else {
success = scan.processClassName(a.replace('.', '/'));
}
if (!success) {
return false;
s = scan.processClassName(a.replace('.', '/'));
}
scanStatus = scanStatus && s;
}
break;
}
return true;
return scanStatus;
}
/**

View File

@ -34,6 +34,10 @@ import java.util.ResourceBundle;
* Message handling class for localization.
*/
public class Messages {
/** Indicates whether line separators in messages need replacement. */
static final boolean REPLACE_LINESEP = ! System.lineSeparator().equals("\n");
/** The resource bundle, must be non-null. */
static final ResourceBundle bundle;
static {
@ -41,13 +45,25 @@ public class Messages {
try {
bundle = ResourceBundle.getBundle("com.sun.tools.jdeprscan.resources.jdeprscan", locale);
} catch (MissingResourceException e) {
throw new InternalError("Cannot find jdeps resource bundle for locale " + locale, e);
throw new InternalError("Cannot find jdeprscan resource bundle for locale " + locale, e);
}
}
/**
* Gets a message from the resource bundle. If necessary, translates "\n",
* the line break string used in the message file, to the system-specific
* line break string.
*
* @param key the message key
* @param args the message arguments
*/
public static String get(String key, Object... args) {
try {
return MessageFormat.format(bundle.getString(key), args);
String msg = MessageFormat.format(bundle.getString(key), args);
if (REPLACE_LINESEP) {
msg = msg.replace("\n", System.lineSeparator());
}
return msg;
} catch (MissingResourceException e) {
throw new InternalError("Missing message: " + key, e);
}

View File

@ -92,6 +92,9 @@ Given a jar file, **jdeprscan** will scan the classes found within
that jar file and report information about how those classes use
deprecated APIs.
Given a class file, **jdeprscan** will scan that class and report
its use of deprecated APIs.
Given a class name, **jdeprscan** will search for that class on the
classpath, scan that class, and report information about how that
class uses deprecated APIs. The class name must use the fully

View File

@ -14,9 +14,9 @@ options:\n\
main.help=\
Scans each argument for usages of deprecated APIs. An argument\n\
may be a directory specifying the root of a package hierarchy,\n\
a JAR file, or a class name. The class name must be specified\n\
using a fully qualified class name using the $ separator character\n\
for nested classes, for example,\n\
a JAR file, a class file, or a class name. The class name must be\n\
specified using a fully qualified class name using the $ separator\n\
character for nested classes, for example,\n\
\n\
\ java.lang.Thread$State\n\
\n\
@ -73,24 +73,26 @@ Unsupported options:\n\
\ Prints a CSV file containing the loaded deprecation information\n\
\ instead of scanning any classes or JAR files.
error.prefix=Error:
scan.process.class=Processing class {0}...
scan.dep.normal=deprecated
scan.dep.removal=deprecated FOR REMOVAL
scan.dep.normal=
scan.dep.removal=(forRemoval=true)
scan.out.extends={0} {1} extends class {2} {3}
scan.out.implements={0} {1} implements interface {2} {3}
scan.out.usestype={0} {1} uses type {2} {3}
scan.out.usesmethodintype={0} {1} uses method in type {2} {3}
scan.out.usesmethod={0} {1} uses method {2} {3} {4} {5}
scan.out.usesintfmethodintype={0} {1} uses interface method in type {2} {3}
scan.out.usesintfmethod={0} {1} uses interface method {2} {3} {4} {5}
scan.out.usesfieldintype={0} {1} uses field in type {2} {3}
scan.out.usesfield={0} {1} uses field {2} {3} {4}
scan.out.usesfieldoftype={0} {1} uses field of type {2} {3} {4} {5}
scan.out.hasfield={0} {1} has field {2} of type {3} {4}
scan.out.methodparmtype={0} {1} method {2} has parameter type {3} {4}
scan.out.methodrettype={0} {1} method {2} has return type {3} {4}
scan.out.methodoverride={0} {1} overrides method {2} {3} {4} {5}
scan.err.exception=error: unexpected exception {0}
scan.err.noclass=error: cannot find class {0}
scan.err.nofile=error: cannot find file {0}
scan.err.nomethod=error: cannot resolve Methodref {0}.{1}:{2}
scan.head.jar=Jar file {0}:
scan.head.dir=Directory {0}:
scan.out.extends={0} {1} extends deprecated class {2} {3}
scan.out.implements={0} {1} implements deprecated interface {2} {3}
scan.out.usesclass={0} {1} uses deprecated class {2} {3}
scan.out.usesmethod={0} {1} uses deprecated method {2}::{3}{4} {5}
scan.out.usesintfmethod={0} {1} uses deprecated method {2}::{3}{4} {5}
scan.out.usesfield={0} {1} uses deprecated field {2}::{3} {4}
scan.out.hasfield={0} {1} has field named {2} of deprecated type {3} {4}
scan.out.methodparmtype={0} {1} has method named {2} having deprecated parameter type {3} {4}
scan.out.methodrettype={0} {1} has method named {2} having deprecated return type {3} {4}
scan.out.methodoverride={0} {1} overrides deprecated method {2}::{3}{4} {5}

View File

@ -28,6 +28,7 @@ package com.sun.tools.jdeprscan.scan;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
@ -62,7 +63,7 @@ public class Scan {
final boolean verbose;
final ClassFinder finder;
boolean error = false;
boolean errorOccurred = false;
public Scan(PrintStream out,
PrintStream err,
@ -124,73 +125,74 @@ public class Scan {
}
}
void printType(String key, ClassFile cf, String cname, boolean forRemoval)
String dep(boolean forRemoval) {
return Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
}
void printType(String key, ClassFile cf, String cname, boolean r)
throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep));
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep(r)));
}
void printMethod(String key, ClassFile cf, String cname, String mname, String rtype,
boolean forRemoval) throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep));
boolean r) throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep(r)));
}
void printField(String key, ClassFile cf, String cname, String fname,
boolean forRemoval) throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep));
boolean r) throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep(r)));
}
void printFieldType(String key, ClassFile cf, String cname, String fname, String type,
boolean forRemoval) throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep));
boolean r) throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep(r)));
}
void printHasField(ClassFile cf, String fname, String type, boolean forRemoval)
void printHasField(ClassFile cf, String fname, String type, boolean r)
throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep));
out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep(r)));
}
void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean forRemoval)
void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean r)
throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep));
out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep(r)));
}
void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean forRemoval)
void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean r)
throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep));
out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep(r)));
}
void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean forRemoval)
void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean r)
throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.getName(), overridden,
mname, desc, dep));
mname, desc, dep(r)));
}
// format should not have a newline
void err(String format, Object... args) {
error = true;
err.print("error: ");
err.printf(format, args);
err.println();
}
void printException(Exception ex) {
err.print(Messages.get("error.prefix"));
err.print(" ");
void errorException(Exception ex) {
errorOccurred = true;
err.println(Messages.get("scan.err.exception", ex.toString()));
if (verbose) {
ex.printStackTrace(err);
} else {
err.print(ex);
}
}
void errorNoClass(String className) {
errorOccurred = true;
err.println(Messages.get("scan.err.noclass", className));
}
void errorNoFile(String fileName) {
errorOccurred = true;
err.println(Messages.get("scan.err.nofile", fileName));
}
void errorNoMethod(String className, String methodName, String desc) {
errorOccurred = true;
err.println(Messages.get("scan.err.nomethod", className, methodName, desc));
}
/**
* Checks whether a member (method or field) is present in a class.
* The checkMethod parameter determines whether this checks for a method
@ -271,7 +273,7 @@ public class Scan {
} else {
startClass = finder.find(startClassName);
if (startClass == null) {
err("can't find class %s", startClassName);
errorNoClass(startClassName);
return startClassName;
}
}
@ -295,7 +297,7 @@ public class Scan {
String superName = curClass.getSuperclassName();
curClass = finder.find(superName);
if (curClass == null) {
err("can't find class %s", superName);
errorNoClass(superName);
break;
}
addInterfaces(intfs, curClass);
@ -310,7 +312,7 @@ public class Scan {
String intf = intfs.removeFirst();
curClass = finder.find(intf);
if (curClass == null) {
err("can't find interface %s", intf);
errorNoClass(intf);
break;
}
@ -324,8 +326,7 @@ public class Scan {
if (curClass == null) {
if (checkStartClass) {
err("can't resolve methodref %s %s %s",
startClassName, findName, findDesc);
errorNoMethod(startClassName, findName, findDesc);
return startClassName;
} else {
// TODO: refactor this
@ -372,18 +373,18 @@ public class Scan {
}
/**
* Checks types referred to from the constant pool.
* Checks Class_info entries in the constant pool.
*
* @param cf the ClassFile of this class
* @param entries constant pool entries collected from this class
* @throws ConstantPoolException if a constant pool entry cannot be found
*/
void checkTypes(ClassFile cf, CPEntries entries) throws ConstantPoolException {
void checkClasses(ClassFile cf, CPEntries entries) throws ConstantPoolException {
for (ConstantPool.CONSTANT_Class_info ci : entries.classes) {
String typeName = ci.getName();
DeprData dd = db.getTypeDeprecated(flatten(typeName));
String className = ci.getName();
DeprData dd = db.getTypeDeprecated(flatten(className));
if (dd != null) {
printType("scan.out.usestype", cf, typeName, dd.isForRemoval());
printType("scan.out.usesclass", cf, className, dd.isForRemoval());
}
}
}
@ -394,26 +395,19 @@ public class Scan {
* @param cf the ClassFile of this class
* @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry
* @param clname the class name
* @param typeKey key for the type message
* @param methKey key for the method message
* @param msgKey message key for localization
* @throws ConstantPoolException if a constant pool entry cannot be found
*/
void checkMethodRef(ClassFile cf,
CONSTANT_NameAndType_info nti,
String clname,
String typeKey,
String methKey) throws ConstantPoolException {
DeprData dd = db.getTypeDeprecated(flatten(clname));
if (dd != null) {
printType(typeKey, cf, clname, dd.isForRemoval());
}
CONSTANT_NameAndType_info nti,
String msgKey) throws ConstantPoolException {
String name = nti.getName();
String type = nti.getType();
clname = resolveMember(cf, flatten(clname), name, type, true, true);
dd = db.getMethodDeprecated(clname, name, type);
DeprData dd = db.getMethodDeprecated(clname, name, type);
if (dd != null) {
printMethod(methKey, cf, clname, name, type, dd.isForRemoval());
printMethod(msgKey, cf, clname, name, type, dd.isForRemoval());
}
}
@ -425,26 +419,16 @@ public class Scan {
*/
void checkFieldRef(ClassFile cf,
ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException {
CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
String clname = fri.getClassName();
CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
String name = nti.getName();
String type = nti.getType();
DeprData dd = db.getTypeDeprecated(clname);
if (dd != null) {
printType("scan.out.usesfieldintype", cf, clname, dd.isForRemoval());
}
clname = resolveMember(cf, flatten(clname), name, type, false, true);
dd = db.getFieldDeprecated(clname, name);
DeprData dd = db.getFieldDeprecated(clname, name);
if (dd != null) {
printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval());
}
dd = db.getTypeDeprecated(flatten(type));
if (dd != null) {
printFieldType("scan.out.usesfieldoftype", cf, clname, name, type, dd.isForRemoval());
}
}
/**
@ -515,18 +499,18 @@ public class Scan {
checkSuper(cf);
checkInterfaces(cf);
checkTypes(cf, entries);
checkClasses(cf, entries);
for (ConstantPool.CONSTANT_Methodref_info mri : entries.methodRefs) {
CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
String clname = mri.getClassName();
checkMethodRef(cf, nti, clname, "scan.out.usesmethodintype", "scan.out.usesmethod");
CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
checkMethodRef(cf, clname, nti, "scan.out.usesmethod");
}
for (ConstantPool.CONSTANT_InterfaceMethodref_info imri : entries.intfMethodRefs) {
CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
String clname = imri.getClassName();
checkMethodRef(cf, nti, clname, "scan.out.usesintfmethodintype", "scan.out.usesintfmethod");
CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
checkMethodRef(cf, clname, nti, "scan.out.usesintfmethod");
}
for (ConstantPool.CONSTANT_Fieldref_info fri : entries.fieldRefs) {
@ -545,6 +529,7 @@ public class Scan {
*/
public boolean scanJar(String jarname) {
try (JarFile jf = new JarFile(jarname)) {
out.println(Messages.get("scan.head.jar", jarname));
finder.addJar(jarname);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
@ -557,10 +542,12 @@ public class Scan {
}
}
return true;
} catch (NoSuchFileException nsfe) {
errorNoFile(jarname);
} catch (IOException | ConstantPoolException ex) {
printException(ex);
return false;
errorException(ex);
}
return false;
}
/**
@ -580,12 +567,15 @@ public class Scan {
.filter(path -> !path.toString().endsWith("package-info.class"))
.filter(path -> !path.toString().endsWith("module-info.class"))
.collect(Collectors.toList());
out.println(Messages.get("scan.head.dir", dirname));
for (Path p : classes) {
processClass(ClassFile.read(p));
}
return true;
} catch (IOException | ConstantPoolException ex) {
printException(ex);
errorException(ex);
return false;
}
}
@ -600,15 +590,35 @@ public class Scan {
try {
ClassFile cf = finder.find(className);
if (cf == null) {
err("can't find class %s", className);
errorNoClass(className);
return false;
} else {
processClass(cf);
return true;
}
} catch (ConstantPoolException ex) {
printException(ex);
errorException(ex);
return false;
}
}
/**
* Scans the named class file for uses of deprecated APIs.
*
* @param fileName the class file to scan
* @return true on success, false on failure
*/
public boolean processClassFile(String fileName) {
Path path = Paths.get(fileName);
try {
ClassFile cf = ClassFile.read(path);
processClass(cf);
return true;
} catch (NoSuchFileException nsfe) {
errorNoFile(fileName);
} catch (IOException | ConstantPoolException ex) {
errorException(ex);
}
return false;
}
}

View File

@ -89,6 +89,7 @@ public class TestScan {
new InputStreamReader(
new ByteArrayInputStream(bytes), StandardCharsets.UTF_8))
.lines()
.filter(line -> !line.endsWith(":"))
.map(line -> line.split(" +"))
.map(array -> array[1])
.collect(Collectors.toSet());