Classworking 工具箱: 分析泛型数据结构
2010-03-18 00:00:00 来源:WEB开发网清单 3 代码中有意思的部分在于内部的 DescriptionBuilderVisitor 类。这个类是 ASM 的 EmptyVisitor 类的扩展,只覆盖了两个方法。覆盖的 visit() 方法处理实际的类信息,创建嵌套内部类 ClassSignatureVisitor 的实例,处理有签名的类(即泛型类)。这个嵌套内部类的要点就在于扫描泛型类的类型参数,找到每个泛型类的最佳上部绑定,并在包含它们的 DescriptionBuilderVisitor 类实例所拥有的哈希映射中设置这个绑定。DescriptionBuilderVisitor 覆盖的 visitField() 方法只检查字段有没有签名。如果字段没有签名,visitField() 就只用字段描述符字符串从类型目录获得类型信息。如果字段确实 有签名,那么 visitField() 就使用类型目录查找的备选形式(接受签名和替换类型的哈希映射)。
DescriptionBuilderVisitor 类代码的实际效果是构建一组字段定义,与提供给 SimpleClassDescription 构造函数的类的字段匹配。如果提供的类是泛型类,那么即使为这个类提供了最通用的参数类型定义,也会处理类的所有字段。对于类型参数定义的最简单形式,这个最通用的参数类型就是 java.lang.Object;如果类型参数定义包含类或接口绑定(例如 public class MyClass<T implements java.util.List>),那么最通用的类型与指定绑定匹配。
处理类型替换
现在我们已经完成了最简单的处理部分,剩下的惟一部分是实际的类型替换处理。清单 4 显示了执行这个处理的代码,包括 清单 2 所示的 TypeDirectory 类中遗漏的部分和新的 GenericTemplate 类:
清单 4. 类型替换代码
public class TypeDirectory
{
...
/** Map from generic classes descriptor to generic class information. */
private HashMap<String,GenericTemplate> m_templateMap =
new HashMap<String,GenericTemplate>();
...
/**
* Get description for generic class with specific type substitutions.
*
* @param sig field signature with type substitutions
* @param types actual types used for instance (values may be
* <code>null</code> if no substitution defined)
* @return type description
*/
public TypeDescription getSignatureInstance(String sig,
TypeDescription[] types) {
// first check for direct match on substituted signature
TypeDescription desc = (TypeDescription)m_typeMap.get(sig);
if (desc == null) {
// no direct match, first handle array
if (sig.charAt(0) == '[') {
desc = new ArrayClassDescriptor(sig,
getSignatureInstance(sig.substring(1), types));
} else {
// not an array, get the generic class descriptor
String dtor = sig;
int split = dtor.indexOf('<');
if (split >= 0) {
dtor = dtor.substring(0, split) + ';';
}
GenericTemplate gdesc = m_templateMap.get(dtor);
if (gdesc == null) {
byte[] byts = m_loader.getBytes(dtor);
gdesc = new GenericTemplate(dtor, byts, this);
}
// handle type substitution to generic version of class
desc = gdesc.getParameterized(sig, types);
}
}
return desc;
}
/**
* Get description for signature with type mapping.
*
* @param sig field signature for type variables
* @param tmap type mapping for variables
* @return type description
*/
public TypeDescription getMappedSignatureInstance(String sig,
HashMap<String,TypeDescription> tmap) {
SignatureDecompositionVisitor vtor =
new SignatureDecompositionVisitor(tmap);
new SignatureReader(sig).acceptType(vtor);
return vtor.getDescription();
}
/**
* Visitor for processing a signature with type substitutions. This uses
* itself recursively to process nested signatures, substituting actual
* types for any type variable references found.
*/
public class SignatureDecompositionVisitor extends EmptySignatureVisitor
{
private final HashMap<String,TypeDescription> m_parameterMap;
private int m_arrayCount;
private String m_className;
private char m_baseClass;
private ArrayList<TypeDescription> m_parameterTypes;
private boolean m_isNested;
private SignatureDecompositionVisitor m_nestedVisitor;
private void reset() {
m_arrayCount = 0;
m_className = null;
m_parameterTypes.clear();
m_isNested = false;
}
private SignatureDecompositionVisitor(HashMap<String,TypeDescription> tmap) {
m_parameterMap = tmap;
m_parameterTypes = new ArrayList<TypeDescription>();
}
private void checkParameter() {
if (m_isNested) {
m_parameterTypes.add(m_nestedVisitor.getDescription());
m_isNested = false;
}
}
public SignatureVisitor visitArrayType() {
m_arrayCount++;
return this;
}
public void visitBaseType(char desc) {
m_baseClass = desc;
}
public void visitTypeVariable(String name) {
String dtor = m_parameterMap.get(name).getDescriptor();
if (dtor == null) {
throw new IllegalStateException("Undefined type variable " + name);
} else if (dtor.length() < 3 || dtor.charAt(0) != 'L' ||
dtor.charAt(dtor.length()-1) != ';') {
throw new IllegalArgumentException
("Not a valid class descriptor: " + dtor);
} else {
m_className = dtor.substring(1, dtor.length()-1);
}
}
public void visitClassType(String name) {
m_className = name;
}
public SignatureVisitor visitTypeArgument(char wildcard) {
checkParameter();
if (wildcard == '=' || wildcard == '+') {
if (m_nestedVisitor == null) {
m_nestedVisitor = new SignatureDecompositionVisitor(m_parameterMap);
} else {
m_nestedVisitor.reset();
}
m_isNested = true;
return m_nestedVisitor;
} else {
m_parameterTypes.add(null);
return new EmptySignatureVisitor();
}
}
public void visitTypeArgument() {
checkParameter();
m_parameterTypes.add(null);
}
public void visitEnd() {
checkParameter();
}
public TypeDescription getDescription() {
// create array signature prefix
StringBuffer buff = new StringBuffer();
for (int i = 0; i < m_arrayCount; i++) {
buff.append('[');
}
// get the actual type description
if (m_className == null) {
buff.append(m_baseClass);
return getTypeInstance(buff.toString());
} else {
// construct both descriptor and full signature for type
buff.append('L');
buff.append(m_className);
if (m_parameterTypes.size() > 0) {
buff.append('<');
for (int i = 0; i < m_parameterTypes.size(); i++) {
TypeDescription pdesc = m_parameterTypes.get(i);
if (pdesc == null) {
buff.append('*');
} else {
buff.append(pdesc.getDescriptor());
}
}
buff.append('>');
}
buff.append(';');
// get actual class description
if (m_parameterTypes.size() == 0) {
return getTypeInstance(buff.toString());
} else {
TypeDescription[] ptypes =
new TypeDescription[m_parameterTypes.size()];
ptypes = m_parameterTypes.toArray(ptypes);
return getSignatureInstance(buff.toString(), ptypes);
}
}
}
}
...
}
public class GenericTemplate
{
private final String m_descriptor;
private final String m_baseName;
private final TypeDirectory m_typeDirectory;
private final FieldDescription[] m_genericFields;
private final String[] m_typeParameters;
private final TypeDescription[] m_upperBounds;
protected GenericTemplate(String dtor, byte[] byts, TypeDirectory dir) {
m_descriptor = dtor;
m_baseName = BinaryClassLoader.nameFromDescriptor(dtor);
m_typeDirectory = dir;
dir.addTemplate(this);
DescriptionBuilderVisitor vtor = new DescriptionBuilderVisitor(dir);
ClassReader creader = new ClassReader(byts);
creader.accept(vtor, true);
m_genericFields = vtor.getFields();
m_typeParameters = vtor.getTypeParameters();
m_upperBounds = vtor.getUpperBounds();
}
public String getDescriptor() {
return m_descriptor;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof GenericTemplate) {
return m_descriptor.equals(((GenericTemplate)obj).m_descriptor);
} else {
return false;
}
}
public int hashCode() {
return m_descriptor.hashCode();
}
/**
* Get description for parameterized type with type substitutions.
*
* @param sig signature including all type substitutions
* @param types actual types used for instance (values may be
* <code>null</code> if no substitution defined)
* @param tdir type directory
* @return substituted type description
*/
public TypeDescription getParameterized(String sig, TypeDescription[] types) {
return new ParameterizedClassDescription(sig, types);
}
/**
* Visitor for generating the description information for a generic class.
*/
public class DescriptionBuilderVisitor extends EmptyVisitor
{
private final TypeDirectory m_typeDirectory;
private ArrayList<FieldDescription> m_fields;
private ArrayList<String> m_typeParameters;
private ArrayList<TypeDescription> m_upperBounds;
private DescriptionBuilderVisitor(TypeDirectory dir) {
m_typeDirectory = dir;
m_fields = new ArrayList<FieldDescription>();
m_typeParameters = new ArrayList<String>();
m_upperBounds = new ArrayList<TypeDescription>();
}
public void visit(int version, int access, String name, String sig,
String sname, String[] inames) {
if (sig != null) {
new SignatureReader(sig).accept(new ClassSignatureVisitor());
}
super.visit(version, access, name, sig, sname, inames);
}
public FieldVisitor visitField(int access, String name, String desc,
String sig, Object value) {
TypeDescription type = null;
if (sig == null) {
type = m_typeDirectory.getTypeInstance(desc);
}
m_fields.add(new FieldDescription(name, sig, type));
return super.visitField(access, name, desc, sig, value);
}
private FieldDescription[] getFields() {
return m_fields.toArray(new FieldDescription[m_fields.size()]);
}
private String[] getTypeParameters() {
return m_typeParameters.toArray(new String[m_typeParameters.size()]);
}
private TypeDescription[] getUpperBounds() {
return m_upperBounds.toArray(new TypeDescription[m_upperBounds.size()]);
}
private class ClassSignatureVisitor extends EmptySignatureVisitor
{
private boolean m_isBounded;
public void visitFormalTypeParameter(String name) {
m_typeParameters.add(name);
m_upperBounds.add(m_typeDirectory.getTypeInstance("Ljava/lang/Object;"));
}
public SignatureVisitor visitClassBound() {
return new EmptySignatureVisitor() {
public void visitClassType(String name) {
m_upperBounds.set(m_upperBounds.size()-1,
m_typeDirectory.getTypeInstance("L" + name + ';'));
m_isBounded = true;
}
};
}
public SignatureVisitor visitInterfaceBound() {
return new EmptySignatureVisitor() {
public void visitClassType(String name) {
if (!m_isBounded) {
m_upperBounds.set(m_upperBounds.size()-1,
m_typeDirectory.getTypeInstance(name));
}
}
};
}
}
}
/**
* Parameterized type description, with actual types substituted for all
* type parameters. The actual substitution of type parameters into the
* fields is done at the time of first use to avoid issues with
* recursive class references.
*/
private class ParameterizedClassDescription extends TypeDescription
{
private final TypeDescription[] m_types;
private final String m_name;
private FieldDescription[] m_fields;
/**
* Constructor. This creates the description for a parameterized type
* with type substitutions from the generic instance. This has to add
* itself to the type directory during construction to handle
* recursive references.
*
* @param sig signature including all type substitutions
* @param types actual types used for instance (values may be
* <code>null</code> if no substitution defined)
* @return substituted type description
*/
public ParameterizedClassDescription(String sig, TypeDescription[] types) {
super(sig);
StringBuffer buff = new StringBuffer();
buff.append(m_baseName);
buff.append('<');
for (int i = 0; i < types.length; i++) {
if (i > 0) {
buff.append(',');
}
if (types[i] == null) {
buff.append('*');
} else {
buff.append(types[i]);
}
}
buff.append('>');
m_name = buff.toString();
m_types = types;
m_typeDirectory.addType(this);
}
public FieldDescription[] getFields() {
if (m_fields == null) {
// make sure the number of parameter types is correct
if (m_typeParameters.length != m_types.length) {
throw new IllegalArgumentException("Wrong number of
parameter types");
}
// build substitution map for type parameters
HashMap<String,TypeDescription> tmap =
new HashMap<String,TypeDescription>();
for (int i = 0; i < m_typeParameters.length; i++) {
TypeDescription type = m_types[i];
if (type == null) {
type = m_upperBounds[i];
}
tmap.put(m_typeParameters[i], type);
}
// handle the actual type substitutions
m_fields = new FieldDescription[m_genericFields.length];
for (int i = 0; i < m_genericFields.length; i++) {
FieldDescription field = m_genericFields[i];
if (field.getType() == null) {
String sig = field.getSignature();
TypeDescription desc =
m_typeDirectory.getMappedSignatureInstance(sig, tmap);
m_fields[i] = new FieldDescription(field.getName(), sig, desc);
} else {
m_fields[i] = field;
}
}
}
return m_fields;
}
public String toString() {
return m_name;
}
}
}
Tags:Classworking 工具箱 分析
编辑录入:爽爽 [复制链接] [打 印]更多精彩
赞助商链接