proguard.classfile.util.DescriptorClassEnumeration 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-2020 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.util;
import java.util.Stack;
import proguard.classfile.*;
/**
* A {@link DescriptorClassEnumeration} provides an enumeration of all classes mentioned in a given
* descriptor or signature.
*
* @author Eric Lafortune
*/
public class DescriptorClassEnumeration {
private String descriptor;
private int index;
private int nestingLevel;
private boolean isInnerClassName;
private String accumulatedClassName;
private Stack accumulatedClassNames;
/** Creates a new DescriptorClassEnumeration for the given descriptor. */
public DescriptorClassEnumeration(String descriptor) {
this.descriptor = descriptor;
}
/**
* Returns the number of classes contained in the descriptor. This is the number of class names
* that the enumeration will return.
*/
public int classCount() {
int count = 0;
reset();
nextFluff();
while (hasMoreClassNames()) {
count++;
nextClassName();
nextFluff();
}
reset();
return count;
}
/** Resets the enumeration. */
private void reset() {
index = 0;
nestingLevel = 0;
isInnerClassName = false;
accumulatedClassName = null;
accumulatedClassNames = null;
}
/** Returns whether the enumeration can provide more class names from the descriptor. */
public boolean hasMoreClassNames() {
return index < descriptor.length();
}
/** Returns the next fluff (surrounding class names) from the descriptor. */
public String nextFluff() {
int fluffStartIndex = index;
// Find the first token marking the start of a class name 'L' or '.'.
loop:
while (index < descriptor.length()) {
switch (descriptor.charAt(index++)) {
case TypeConstants.GENERIC_START:
{
nestingLevel++;
// Make sure we have a stack.
if (accumulatedClassNames == null) {
accumulatedClassNames = new Stack();
}
// Remember the accumulated class name.
accumulatedClassNames.push(accumulatedClassName);
break;
}
case TypeConstants.GENERIC_END:
{
nestingLevel--;
// Return to the accumulated class name outside the
// generic block.
accumulatedClassName = (String) accumulatedClassNames.pop();
continue loop;
}
case TypeConstants.GENERIC_BOUND:
case TypeConstants.ARRAY:
{
continue loop;
}
case TypeConstants.CLASS_START:
{
// We've found the start of an ordinary class name.
nestingLevel += 2;
isInnerClassName = false;
break loop;
}
case TypeConstants.CLASS_END:
{
nestingLevel -= 2;
break;
}
case JavaTypeConstants.INNER_CLASS_SEPARATOR:
{
// We've found the start of an inner class name in a signature.
isInnerClassName = true;
break loop;
}
case TypeConstants.GENERIC_VARIABLE_START:
{
// We've found the start of a type identifier. Skip to the end.
while (descriptor.charAt(index++) != TypeConstants.CLASS_END)
;
break;
}
}
if (nestingLevel == 1 && descriptor.charAt(index) != TypeConstants.GENERIC_END) {
// We're at the start of a type parameter. Skip to the start
// of the bounds.
while (descriptor.charAt(index++) != TypeConstants.GENERIC_BOUND)
;
}
}
return descriptor.substring(fluffStartIndex, index);
}
/** Returns the next class name from the descriptor. */
public String nextClassName() {
int classNameStartIndex = index;
// Find the first token marking the end of a class name '<' or ';'.
loop:
while (true) {
switch (descriptor.charAt(index)) {
case TypeConstants.GENERIC_START:
case TypeConstants.CLASS_END:
case JavaTypeConstants.INNER_CLASS_SEPARATOR:
{
break loop;
}
}
index++;
}
String className = descriptor.substring(classNameStartIndex, index);
// Recompose the inner class name if necessary.
accumulatedClassName =
isInnerClassName
? accumulatedClassName + TypeConstants.INNER_CLASS_SEPARATOR + className
: className;
return accumulatedClassName;
}
/**
* Returns whether the most recently returned class name was a recomposed inner class name from a
* signature.
*/
public boolean isInnerClassName() {
return isInnerClassName;
}
/** A main method for testing the class name enumeration. */
public static void main(String[] args) {
try {
for (int index = 0; index < args.length; index++) {
String descriptor = args[index];
System.out.println("Descriptor [" + descriptor + "]");
DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor);
System.out.println(" Fluff: [" + enumeration.nextFluff() + "]");
while (enumeration.hasMoreClassNames()) {
System.out.println(" Name: [" + enumeration.nextClassName() + "]");
System.out.println(" Fluff: [" + enumeration.nextFluff() + "]");
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy