package com.telerik.metadata.dx; import com.android.dex.Annotation; import com.android.dex.ClassData; import com.android.dex.ClassDef; import com.android.dex.Dex; import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.Type; import com.telerik.metadata.desc.MetadataInfoAnnotationDescriptor; import com.telerik.metadata.desc.ClassDescriptor; import com.telerik.metadata.desc.FieldDescriptor; import com.telerik.metadata.desc.MethodDescriptor; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; public class ClassInfo implements ClassDescriptor { private final DexFile dexFile; private final ClassDef classDef; private final Comparator METHOD_NAME_COMPARATOR = new Comparator<MethodDescriptor>() { @Override public int compare(MethodDescriptor m1, MethodDescriptor m2) { return m1.getName().compareTo(m2.getName()); } }; public ClassInfo(DexFile dexFile, ClassDef classDef) { this.dexFile = dexFile; this.classDef = classDef; } @Override public boolean isClass() { return !isInterface() && !isEnum(); } @Override public boolean isInterface() { return AccessFlags.isInterface(classDef.getAccessFlags()); } @Override public boolean isEnum() { return AccessFlags.isEnum(classDef.getAccessFlags()); } @Override public boolean isStatic() { return AccessFlags.isStatic(classDef.getAccessFlags()); } @Override public boolean isPublic() { return AccessFlags.isPublic(classDef.getAccessFlags()); } @Override public boolean isProtected() { return AccessFlags.isProtected(classDef.getAccessFlags()); } @Override public MethodDescriptor[] getMethods() { MethodDescriptor[] methods; Dex dex = dexFile.getDex(); int off = classDef.getClassDataOffset(); if (off != 0) { ClassData data = dex.readClassData(classDef); ClassData.Method[] allMethods = data.allMethods(); methods = new MethodDescriptor[allMethods.length]; for (int i=0; i<allMethods.length; i++) { methods[i] = new MethodInfo(dexFile, allMethods[i]); } } else { methods = new MethodDescriptor[0]; } Arrays.sort(methods, METHOD_NAME_COMPARATOR); return methods; } @Override public FieldDescriptor[] getFields() { FieldDescriptor[] fields; Dex dex = dexFile.getDex(); int off = classDef.getClassDataOffset(); if (off != 0) { ClassData data = dex.readClassData(classDef); ClassData.Field[] allFields = data.allFields(); fields = new FieldDescriptor[allFields.length]; for (int i=0; i<fields.length; i++) { fields[i] = new FieldInfo(dexFile, allFields[i]); } } else { fields = new FieldDescriptor[0]; } return fields; } @Override public MetadataInfoAnnotationDescriptor getMetadataInfoAnnotation() { int annotationsOffset = classDef.getAnnotationsOffset(); if (annotationsOffset == 0) { return null; } Dex dex = dexFile.getDex(); int classDefIndex = dex.findClassDefIndexFromTypeIndex(classDef.getTypeIndex()); int dexAnnotationDirectoryOffset = dex.annotationDirectoryOffsetFromClassDefIndex(classDefIndex); if (dexAnnotationDirectoryOffset == 0) { return null; } Dex.Section dirSection = dex.open(dexAnnotationDirectoryOffset); int classSetOffset = dirSection.readInt(); int annotationSetOffset = classSetOffset; if (annotationSetOffset == 0) { return null; } Dex.Section annSetSection = dex.open(annotationSetOffset); int size = annSetSection.readInt(); for (int i = 0; i < size; i++) { int annotationOffset = annSetSection.readInt(); Dex.Section annSection = dex.open(annotationOffset); Annotation annotation = annSection.readAnnotation(); String annotationName = dex.strings().get(dex.typeIds().get(annotation.getTypeIndex())); if (annotationName.equals("Lcom/telerik/metadata/MetadataInfo;")) { return new MetadataInfoAnnotationInfo(dexFile, annotation); } } return null; } @Override public String[] getInterfaceNames() { ArrayList<String> names = new ArrayList<String>(); short[] ifaceIndexes = classDef.getInterfaces(); if ((ifaceIndexes != null) && (ifaceIndexes.length > 0)) { List<String> typeNames = dexFile.getDex().typeNames(); for (short idx: ifaceIndexes) { String interfaceName = typeNames.get(idx); names.add(interfaceName); } } return names.toArray(new String[names.size()]); } @Override public String getPackageName() { String className = getClassName(); String name = className.replace('.', '/'); Type t = Type.internClassName(name); CstType c = CstType.intern(t); String packageName = c.getPackageName(); return packageName; } @Override public String getClassName() { List<String> typeNames = dexFile.getDex().typeNames(); int cdIdx = classDef.getTypeIndex(); String typeName = typeNames.get(cdIdx); String name = typeName.substring(1, typeName.length()-1).replace('/', '.'); return name; } @Override public String getSuperclassName() { List<String> typeNames = dexFile.getDex().typeNames(); int idx = classDef.getSupertypeIndex(); String superClassname = typeNames.get(idx); String name = superClassname.substring(1, superClassname.length()-1).replace('/', '.'); return name; } }