|
Java example source code file (Dependencies.java)
This example Java source code file (Dependencies.java) is included in the alvinalexander.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Learn more about this Java project at its project page.
The Dependencies.java Java example source code
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.classfile;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import com.sun.tools.classfile.Dependency.Filter;
import com.sun.tools.classfile.Dependency.Finder;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.classfile.Type.ArrayType;
import com.sun.tools.classfile.Type.ClassSigType;
import com.sun.tools.classfile.Type.ClassType;
import com.sun.tools.classfile.Type.MethodType;
import com.sun.tools.classfile.Type.SimpleType;
import com.sun.tools.classfile.Type.TypeParamType;
import com.sun.tools.classfile.Type.WildcardType;
import static com.sun.tools.classfile.ConstantPool.*;
/**
* A framework for determining {@link Dependency dependencies} between class files.
*
* A {@link Dependency.Finder finder} is used to identify the dependencies of
* individual classes. Some finders may return subtypes of {@code Dependency} to
* further characterize the type of dependency, such as a dependency on a
* method within a class.
*
* A {@link Dependency.Filter filter} may be used to restrict the set of
* dependencies found by a finder.
*
* Dependencies that are found may be passed to a {@link Dependencies.Recorder
* recorder} so that the dependencies can be stored in a custom data structure.
*/
public class Dependencies {
/**
* Thrown when a class file cannot be found.
*/
public static class ClassFileNotFoundException extends Exception {
private static final long serialVersionUID = 3632265927794475048L;
public ClassFileNotFoundException(String className) {
super(className);
this.className = className;
}
public ClassFileNotFoundException(String className, Throwable cause) {
this(className);
initCause(cause);
}
public final String className;
}
/**
* Thrown when an exception is found processing a class file.
*/
public static class ClassFileError extends Error {
private static final long serialVersionUID = 4111110813961313203L;
public ClassFileError(Throwable cause) {
initCause(cause);
}
}
/**
* Service provider interface to locate and read class files.
*/
public interface ClassFileReader {
/**
* Get the ClassFile object for a specified class.
* @param className the name of the class to be returned.
* @return the ClassFile for the given class
* @throws Dependencies.ClassFileNotFoundException if the classfile cannot be
* found
*/
public ClassFile getClassFile(String className)
throws ClassFileNotFoundException;
}
/**
* Service provide interface to handle results.
*/
public interface Recorder {
/**
* Record a dependency that has been found.
* @param d
*/
public void addDependency(Dependency d);
}
/**
* Get the default finder used to locate the dependencies for a class.
* @return the default finder
*/
public static Finder getDefaultFinder() {
return new APIDependencyFinder(AccessFlags.ACC_PRIVATE);
}
/**
* Get a finder used to locate the API dependencies for a class.
* These include the superclass, superinterfaces, and classes referenced in
* the declarations of fields and methods. The fields and methods that
* are checked can be limited according to a specified access.
* The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC},
* {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE},
* {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for
* package private access. Members with greater than or equal accessibility
* to that specified will be searched for dependencies.
* @param access the access of members to be checked
* @return an API finder
*/
public static Finder getAPIFinder(int access) {
return new APIDependencyFinder(access);
}
/**
* Get a finder to do class dependency analysis.
*
* @return a Class dependency finder
*/
public static Finder getClassDependencyFinder() {
return new ClassDependencyFinder();
}
/**
* Get the finder used to locate the dependencies for a class.
* @return the finder
*/
public Finder getFinder() {
if (finder == null)
finder = getDefaultFinder();
return finder;
}
/**
* Set the finder used to locate the dependencies for a class.
* @param f the finder
*/
public void setFinder(Finder f) {
f.getClass(); // null check
finder = f;
}
/**
* Get the default filter used to determine included when searching
* the transitive closure of all the dependencies.
* Unless overridden, the default filter accepts all dependencies.
* @return the default filter.
*/
public static Filter getDefaultFilter() {
return DefaultFilter.instance();
}
/**
* Get a filter which uses a regular expression on the target's class name
* to determine if a dependency is of interest.
* @param pattern the pattern used to match the target's class name
* @return a filter for matching the target class name with a regular expression
*/
public static Filter getRegexFilter(Pattern pattern) {
return new TargetRegexFilter(pattern);
}
/**
* Get a filter which checks the package of a target's class name
* to determine if a dependency is of interest. The filter checks if the
* package of the target's class matches any of a set of given package
* names. The match may optionally match subpackages of the given names as well.
* @param packageNames the package names used to match the target's class name
* @param matchSubpackages whether or not to match subpackages as well
* @return a filter for checking the target package name against a list of package names
*/
public static Filter getPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
return new TargetPackageFilter(packageNames, matchSubpackages);
}
/**
* Get the filter used to determine the dependencies included when searching
* the transitive closure of all the dependencies.
* Unless overridden, the default filter accepts all dependencies.
* @return the filter
*/
public Filter getFilter() {
if (filter == null)
filter = getDefaultFilter();
return filter;
}
/**
* Set the filter used to determine the dependencies included when searching
* the transitive closure of all the dependencies.
* @param f the filter
*/
public void setFilter(Filter f) {
f.getClass(); // null check
filter = f;
}
/**
* Find the dependencies of a class, using the current
* {@link Dependencies#getFinder finder} and
* {@link Dependencies#getFilter filter}.
* The search may optionally include the transitive closure of all the
* filtered dependencies, by also searching in the classes named in those
* dependencies.
* @param classFinder a finder to locate class files
* @param rootClassNames the names of the root classes from which to begin
* searching
* @param transitiveClosure whether or not to also search those classes
* named in any filtered dependencies that are found.
* @return the set of dependencies that were found
* @throws ClassFileNotFoundException if a required class file cannot be found
* @throws ClassFileError if an error occurs while processing a class file,
* such as an error in the internal class file structure.
*/
public Set<Dependency> findAllDependencies(
ClassFileReader classFinder, Set<String> rootClassNames,
boolean transitiveClosure)
throws ClassFileNotFoundException {
final Set<Dependency> results = new HashSet();
Recorder r = new Recorder() {
public void addDependency(Dependency d) {
results.add(d);
}
};
findAllDependencies(classFinder, rootClassNames, transitiveClosure, r);
return results;
}
/**
* Find the dependencies of a class, using the current
* {@link Dependencies#getFinder finder} and
* {@link Dependencies#getFilter filter}.
* The search may optionally include the transitive closure of all the
* filtered dependencies, by also searching in the classes named in those
* dependencies.
* @param classFinder a finder to locate class files
* @param rootClassNames the names of the root classes from which to begin
* searching
* @param transitiveClosure whether or not to also search those classes
* named in any filtered dependencies that are found.
* @param recorder a recorder for handling the results
* @throws ClassFileNotFoundException if a required class file cannot be found
* @throws ClassFileError if an error occurs while processing a class file,
* such as an error in the internal class file structure.
*/
public void findAllDependencies(
ClassFileReader classFinder, Set<String> rootClassNames,
boolean transitiveClosure, Recorder recorder)
throws ClassFileNotFoundException {
Set<String> doneClasses = new HashSet();
getFinder(); // ensure initialized
getFilter(); // ensure initialized
// Work queue of names of classfiles to be searched.
// Entries will be unique, and for classes that do not yet have
// dependencies in the results map.
Deque<String> deque = new LinkedList(rootClassNames);
String className;
while ((className = deque.poll()) != null) {
assert (!doneClasses.contains(className));
doneClasses.add(className);
ClassFile cf = classFinder.getClassFile(className);
// The following code just applies the filter to the dependencies
// followed for the transitive closure.
for (Dependency d: finder.findDependencies(cf)) {
recorder.addDependency(d);
if (transitiveClosure && filter.accepts(d)) {
String cn = d.getTarget().getClassName();
if (!doneClasses.contains(cn))
deque.add(cn);
}
}
}
}
private Filter filter;
private Finder finder;
/**
* A location identifying a class.
*/
static class SimpleLocation implements Location {
public SimpleLocation(String name) {
this.name = name;
this.className = name.replace('/', '.');
}
public String getName() {
return name;
}
public String getClassName() {
return className;
}
public String getPackageName() {
int i = name.lastIndexOf('/');
return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
}
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof SimpleLocation))
return false;
return (name.equals(((SimpleLocation) other).name));
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
private String name;
private String className;
}
/**
* A dependency of one class on another.
*/
static class SimpleDependency implements Dependency {
public SimpleDependency(Location origin, Location target) {
this.origin = origin;
this.target = target;
}
public Location getOrigin() {
return origin;
}
public Location getTarget() {
return target;
}
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof SimpleDependency))
return false;
SimpleDependency o = (SimpleDependency) other;
return (origin.equals(o.origin) && target.equals(o.target));
}
@Override
public int hashCode() {
return origin.hashCode() * 31 + target.hashCode();
}
@Override
public String toString() {
return origin + ":" + target;
}
private Location origin;
private Location target;
}
/**
* This class accepts all dependencies.
*/
static class DefaultFilter implements Filter {
private static DefaultFilter instance;
static DefaultFilter instance() {
if (instance == null)
instance = new DefaultFilter();
return instance;
}
public boolean accepts(Dependency dependency) {
return true;
}
}
/**
* This class accepts those dependencies whose target's class name matches a
* regular expression.
*/
static class TargetRegexFilter implements Filter {
TargetRegexFilter(Pattern pattern) {
this.pattern = pattern;
}
public boolean accepts(Dependency dependency) {
return pattern.matcher(dependency.getTarget().getClassName()).matches();
}
private final Pattern pattern;
}
/**
* This class accepts those dependencies whose class name is in a given
* package.
*/
static class TargetPackageFilter implements Filter {
TargetPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
for (String pn: packageNames) {
if (pn.length() == 0) // implies null check as well
throw new IllegalArgumentException();
}
this.packageNames = packageNames;
this.matchSubpackages = matchSubpackages;
}
public boolean accepts(Dependency dependency) {
String pn = dependency.getTarget().getPackageName();
if (packageNames.contains(pn))
return true;
if (matchSubpackages) {
for (String n: packageNames) {
if (pn.startsWith(n + "."))
return true;
}
}
return false;
}
private final Set<String> packageNames;
private final boolean matchSubpackages;
}
/**
* This class identifies class names directly or indirectly in the constant pool.
*/
static class ClassDependencyFinder extends BasicDependencyFinder {
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
Visitor v = new Visitor(classfile);
for (CPInfo cpInfo: classfile.constant_pool.entries()) {
v.scan(cpInfo);
}
try {
v.addClass(classfile.super_class);
v.addClasses(classfile.interfaces);
v.scan(classfile.attributes);
for (Field f : classfile.fields) {
v.scan(f.descriptor, f.attributes);
}
for (Method m : classfile.methods) {
v.scan(m.descriptor, m.attributes);
Exceptions_attribute e =
(Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
if (e != null) {
v.addClasses(e.exception_index_table);
}
}
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
return v.deps;
}
}
/**
* This class identifies class names in the signatures of classes, fields,
* and methods in a class.
*/
static class APIDependencyFinder extends BasicDependencyFinder {
APIDependencyFinder(int access) {
switch (access) {
case AccessFlags.ACC_PUBLIC:
case AccessFlags.ACC_PROTECTED:
case AccessFlags.ACC_PRIVATE:
case 0:
showAccess = access;
break;
default:
throw new IllegalArgumentException("invalid access 0x"
+ Integer.toHexString(access));
}
}
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
try {
Visitor v = new Visitor(classfile);
v.addClass(classfile.super_class);
v.addClasses(classfile.interfaces);
// inner classes?
for (Field f : classfile.fields) {
if (checkAccess(f.access_flags))
v.scan(f.descriptor, f.attributes);
}
for (Method m : classfile.methods) {
if (checkAccess(m.access_flags)) {
v.scan(m.descriptor, m.attributes);
Exceptions_attribute e =
(Exceptions_attribute) m.attributes.get(Attribute.Exceptions);
if (e != null)
v.addClasses(e.exception_index_table);
}
}
return v.deps;
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
boolean checkAccess(AccessFlags flags) {
// code copied from javap.Options.checkAccess
boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC);
boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED);
boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);
boolean isPackage = !(isPublic || isProtected || isPrivate);
if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
return false;
else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))
return false;
else if ((showAccess == 0) && (isPrivate))
return false;
else
return true;
}
private int showAccess;
}
static abstract class BasicDependencyFinder implements Finder {
private Map<String,Location> locations = new HashMap();
Location getLocation(String className) {
Location l = locations.get(className);
if (l == null)
locations.put(className, l = new SimpleLocation(className));
return l;
}
class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor {
private ConstantPool constant_pool;
private Location origin;
Set<Dependency> deps;
Visitor(ClassFile classFile) {
try {
constant_pool = classFile.constant_pool;
origin = getLocation(classFile.getName());
deps = new HashSet<Dependency>();
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
void scan(Descriptor d, Attributes attrs) {
try {
scan(new Signature(d.index).getType(constant_pool));
scan(attrs);
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
void scan(CPInfo cpInfo) {
cpInfo.accept(this, null);
}
void scan(Type t) {
t.accept(this, null);
}
void scan(Attributes attrs) {
try {
Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
if (sa != null)
scan(sa.getParsedSignature().getType(constant_pool));
scan((RuntimeVisibleAnnotations_attribute)
attrs.get(Attribute.RuntimeVisibleAnnotations));
scan((RuntimeVisibleParameterAnnotations_attribute)
attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
if (attr == null) {
return;
}
for (int i = 0; i < attr.annotations.length; i++) {
int index = attr.annotations[i].type_index;
scan(new Signature(index).getType(constant_pool));
}
}
private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
if (attr == null) {
return;
}
for (int param = 0; param < attr.parameter_annotations.length; param++) {
for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
int index = attr.parameter_annotations[param][i].type_index;
scan(new Signature(index).getType(constant_pool));
}
}
}
void addClass(int index) throws ConstantPoolException {
if (index != 0) {
String name = constant_pool.getClassInfo(index).getBaseName();
if (name != null)
addDependency(name);
}
}
void addClasses(int[] indices) throws ConstantPoolException {
for (int i: indices)
addClass(i);
}
private void addDependency(String name) {
deps.add(new SimpleDependency(origin, getLocation(name)));
}
// ConstantPool.Visitor methods
public Void visitClass(CONSTANT_Class_info info, Void p) {
try {
if (info.getName().startsWith("["))
new Signature(info.name_index).getType(constant_pool).accept(this, null);
else
addDependency(info.getBaseName());
return null;
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
public Void visitDouble(CONSTANT_Double_info info, Void p) {
return null;
}
public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) {
return visitRef(info, p);
}
public Void visitFloat(CONSTANT_Float_info info, Void p) {
return null;
}
public Void visitInteger(CONSTANT_Integer_info info, Void p) {
return null;
}
public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
return visitRef(info, p);
}
public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
return null;
}
public Void visitLong(CONSTANT_Long_info info, Void p) {
return null;
}
public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
return null;
}
public Void visitMethodType(CONSTANT_MethodType_info info, Void p) {
return null;
}
public Void visitMethodref(CONSTANT_Methodref_info info, Void p) {
return visitRef(info, p);
}
public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
try {
new Signature(info.type_index).getType(constant_pool).accept(this, null);
return null;
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
public Void visitString(CONSTANT_String_info info, Void p) {
return null;
}
public Void visitUtf8(CONSTANT_Utf8_info info, Void p) {
return null;
}
private Void visitRef(CPRefInfo info, Void p) {
try {
visitClass(info.getClassInfo(), p);
return null;
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
// Type.Visitor methods
private void findDependencies(Type t) {
if (t != null)
t.accept(this, null);
}
private void findDependencies(List<? extends Type> ts) {
if (ts != null) {
for (Type t: ts)
t.accept(this, null);
}
}
public Void visitSimpleType(SimpleType type, Void p) {
return null;
}
public Void visitArrayType(ArrayType type, Void p) {
findDependencies(type.elemType);
return null;
}
public Void visitMethodType(MethodType type, Void p) {
findDependencies(type.paramTypes);
findDependencies(type.returnType);
findDependencies(type.throwsTypes);
findDependencies(type.typeParamTypes);
return null;
}
public Void visitClassSigType(ClassSigType type, Void p) {
findDependencies(type.superclassType);
findDependencies(type.superinterfaceTypes);
return null;
}
public Void visitClassType(ClassType type, Void p) {
findDependencies(type.outerType);
addDependency(type.getBinaryName());
findDependencies(type.typeArgs);
return null;
}
public Void visitTypeParamType(TypeParamType type, Void p) {
findDependencies(type.classBound);
findDependencies(type.interfaceBounds);
return null;
}
public Void visitWildcardType(WildcardType type, Void p) {
findDependencies(type.boundType);
return null;
}
}
}
}
Other Java examples (source code examples)
Here is a short list of links related to this Java Dependencies.java source code file:
|