proguard.classfile.ClassPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-core Show documentation
Show all versions of proguard-core Show documentation
ProGuardCORE is a free library to read, analyze, modify, and write Java class files.
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2021 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.classfile;
import java.util.*;
import proguard.classfile.visitor.*;
import proguard.util.*;
/**
* This is a set of {@link Clazz} instances. They can be enumerated or retrieved by name. They can
* also be accessed by means of class visitors.
*
* @author Eric Lafortune
*/
public class ClassPool {
// We're using a sorted tree map instead of a hash map to store the classes,
// in order to make the processing more deterministic.
private final TreeMap classes = new TreeMap<>();
// Keep a separate set of the classes to speed up `contains(Clazz)`.
private final Set clazzSet = new HashSet<>();
/** Creates a new empty ClassPool. */
public ClassPool() {}
/**
* Creates a new ClassPool with the given classes.
*
* @param classes the classes to be added.
*/
public ClassPool(Clazz... classes) {
for (Clazz clazz : classes) {
addClass(clazz);
}
}
/**
* Creates a new ClassPool with the given classes.
*
* @param classes the classes to be added.
*/
public ClassPool(Iterable extends Clazz> classes) {
for (Clazz clazz : classes) {
addClass(clazz);
}
}
/**
* Creates a new ClassPool with the given classes. The keys are taken from the Clazz instances.
*
* @param classPool the classes to be added.
*/
public ClassPool(ClassPool classPool) {
this(classPool.classes());
}
/** Clears the class pool. */
public void clear() {
classes.clear();
clazzSet.clear();
}
/** Adds the given Clazz to the class pool. */
public void addClass(Clazz clazz) {
addClass(clazz.getName(), clazz);
}
/** Adds the given Clazz with the given name to the class pool. */
public void addClass(String name, Clazz clazz) {
classes.put(name, clazz);
clazzSet.add(clazz);
}
/** Removes the given Clazz from the class pool. */
public void removeClass(Clazz clazz) {
removeClass(clazz.getName());
}
/** Removes the Class with the specified name from the class pool. */
public Clazz removeClass(String className) {
Clazz removed = classes.remove(className);
clazzSet.remove(removed);
return removed;
}
/**
* Returns a Clazz from the class pool based on its name. Returns null
if the class
* with the given name is not in the class pool.
*
* @param className a class name (e.g., "java/lang/String").
* @return the corresponding {@link Clazz}. {@code null} if the class pool does not contain a
* class with the specified name.
*/
public Clazz getClass(String className) {
return classes.get(className);
}
/** Checks whether the given class exists in the class pool. */
public boolean contains(Clazz clazz) {
return clazzSet.contains(clazz);
}
// Note: for consistency, use visitors whenever possible.
/** Returns an Iterator of all class names in the class pool. */
public Iterator classNames() {
return classes.keySet().iterator();
}
// Note: for consistency, use visitors whenever possible.
/** Returns an Iterable of all classes in the class pool. */
public Iterable classes() {
return classes.values();
}
/** Returns the number of classes in the class pool. */
public int size() {
return classes.size();
}
/**
* Returns a ClassPool with the same classes, but with the keys that correspond to the names of
* the class instances. This can be useful to create a class pool with obfuscated names.
*/
public ClassPool refreshedCopy() {
return new ClassPool(this);
}
/**
* Returns a Map with the same contents as the given map, but with keys that have been mapped
* based from the names in the class pool to the names in the corresponding classes. This can be
* useful to create a map with obfuscated names as keys.
*/
public Map refreshedKeysCopy(Map map) {
Map refreshedMap = new HashMap(map.size());
// Iterate over all entries.
Iterator> entries = map.entrySet().iterator();
while (entries.hasNext()) {
// Find the class.
Map.Entry entry = entries.next();
String className = entry.getKey();
Clazz clazz = classes.get(className);
if (clazz != null) {
// Add the mapped entry.
refreshedMap.put(clazz.getName(), entry.getValue());
}
}
return refreshedMap;
}
/**
* Returns a Map with the same contents as the given map, but with values that have been mapped
* based from the names in the class pool to the names in the corresponding classes. This can be
* useful to create a map with obfuscated names as values.
*/
public Map refreshedValuesCopy(Map map) {
Map refreshedMap = new HashMap(map.size());
// Iterate over all entries.
Iterator> entries = map.entrySet().iterator();
while (entries.hasNext()) {
// Find the class.
Map.Entry entry = entries.next();
String className = entry.getValue();
Clazz clazz = classes.get(className);
if (clazz != null) {
// Add the mapped entry.
refreshedMap.put(entry.getKey(), clazz.getName());
}
}
return refreshedMap;
}
/**
* Returns a Map that represents a mapping from every Clazz in the ClassPool to its original name.
* This can be useful to retrieve the original name of classes after name obfuscation has been
* applied.
*/
public Map reverseMapping() {
Map reversedMap = new HashMap(classes.size());
// Reverse each entry.
for (String originalClassName : classes.keySet()) {
Clazz processedClazz = classes.get(originalClassName);
reversedMap.put(processedClazz, originalClassName);
}
return reversedMap;
}
/** Applies the given ClassPoolVisitor to the class pool. */
public void accept(ClassPoolVisitor classPoolVisitor) {
classPoolVisitor.visitClassPool(this);
}
/** Applies the given ClassVisitor to all classes in the class pool, in random order. */
public void classesAccept(ClassVisitor classVisitor) {
Iterator iterator = classes.values().iterator();
while (iterator.hasNext()) {
Clazz clazz = (Clazz) iterator.next();
clazz.accept(classVisitor);
}
}
/** Applies the given ClassVisitor to all classes in the class pool, in sorted order. */
public void classesAcceptAlphabetically(ClassVisitor classVisitor) {
// We're already using a tree map.
// TreeMap sortedClasses = new TreeMap(classes);
// Iterator iterator = sortedClasses.values().iterator();
Iterator iterator = classes.values().iterator();
while (iterator.hasNext()) {
Clazz clazz = (Clazz) iterator.next();
clazz.accept(classVisitor);
}
}
/** Applies the given ClassVisitor to all matching classes in the class pool. */
public void classesAccept(String classNameFilter, ClassVisitor classVisitor) {
classesAccept(new ListParser(new ClassNameParser()).parse(classNameFilter), classVisitor);
}
/** Applies the given ClassVisitor to all matching classes in the class pool. */
public void classesAccept(List classNameFilter, ClassVisitor classVisitor) {
classesAccept(new ListParser(new ClassNameParser()).parse(classNameFilter), classVisitor);
}
/** Applies the given ClassVisitor to all matching classes in the class pool. */
public void classesAccept(StringMatcher classNameFilter, ClassVisitor classVisitor) {
String prefix = classNameFilter.prefix();
if ("".equals(prefix)) {
// It is more efficient to avoid using higherEntry when we're traversing over the complete
// ClassPool.
for (Map.Entry entry : classes.entrySet()) {
String className = entry.getKey();
if (classNameFilter.matches(className)) {
Clazz clazz = entry.getValue();
clazz.accept(classVisitor);
}
}
} else {
// If we can skip towards a specific entry using the prefix and handle a smaller part of the
// ClassPool,
// then it becomes worthwhile to traverse using higherEntry.
Map.Entry classEntry = classes.ceilingEntry(prefix);
while (classEntry != null && classEntry.getKey().startsWith(prefix)) {
if (classNameFilter.matches(classEntry.getKey())) {
classEntry.getValue().accept(classVisitor);
}
classEntry = classes.higherEntry(classEntry.getKey());
}
}
}
/**
* Applies the given ClassVisitor to the class with the given name, if it is present in the class
* pool.
*/
public void classAccept(String className, ClassVisitor classVisitor) {
Clazz clazz = getClass(className);
if (clazz != null) {
clazz.accept(classVisitor);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy