namespace ApkMethodCount { import MultiDexFileReader = DexFormat . MultiDexFileReader ; export class Model { private static EMPTY_PACKAGE_NAME : string = "(empty)" ; private static ICON_INTERFACE : string = "interface" ; private static ICON_CLASS : string = "class" ; private static ICON_ENUM : string = "enum" ; private static ICON_REF : string = "reference" ; private static INNER_CLASS_REGEX : RegExp = /^\d+$/ ; public static START_LEVEL_VISIBLE : number = 1 ; public static TREE_CLASS_LEVEL : number = 1 ; private currentTreemap : ClassDataWrapper [ ] ; public getCurrentTreemap ( ) { return this . currentTreemap ; } public loadDexFile ( arrayBuffer ) { var dexFile = new MultiDexFileReader ( arrayBuffer ) ; var methodRefs = dexFile . getMethodRefs ( ) ; var treemap = [ ] ; for ( var i = 0 ; i < methodRefs . length ; i ++ ) { var classData = methodRefs [ i ] . getClassData ( ) ; var classType = methodRefs [ i ] . getClassType ( ) ; var classDescriptor = classType . getDescriptor ( ) ; var packageName = DexFileReader . getPackageNameOnly ( classDescriptor ) ; var packageNameParts = packageName . split ( "." ) ; if ( packageNameParts . length == 1 && packageNameParts [ 0 ] . length == 0 ) { packageNameParts [ 0 ] = Model . EMPTY_PACKAGE_NAME ; } var currentNode = treemap [ packageNameParts [ 0 ] ] ; if ( typeof currentNode === "undefined" ) { currentNode = treemap [ packageNameParts [ 0 ] ] = { } ; } var classIdx = methodRefs [ i ] . getClassIdx ( ) ; for ( var j = 0 ; j < packageNameParts . length ; j ++ ) { if ( j >= 1 ) { if ( typeof currentNode [ packageNameParts [ j ] ] === "undefined" ) { currentNode [ packageNameParts [ j ] ] = [ ] ; } currentNode = currentNode [ packageNameParts [ j ] ] ; } var metaNode ; if ( typeof currentNode [ AppView . TREE_META_NODE ] === "undefined" ) { metaNode = currentNode [ AppView . TREE_META_NODE ] = new PackageNodeMetaData ( ) ; } else { metaNode = currentNode [ AppView . TREE_META_NODE ] ; } metaNode . incrementMethodCount ( ) ; if ( j >= Model . TREE_CLASS_LEVEL ) { metaNode . addClassAndIncrement ( classIdx , new ClassDataWrapper ( classType , classData ) ) ; } } } this . currentTreemap = treemap ; return { tree : treemap , methodsCount : methodRefs . length , multidex : dexFile . isMultidex ( ) } ; } public isInnerClassFromName ( className ) { return className . match ( Model . INNER_CLASS_REGEX ) !== null ; } public formatType ( className , classData ) { var classDef = classData . getClassDef ( ) ; if ( classDef != null ) { if ( classDef . isAccessFlag ( DexFormat . AccessFlags . ACC_INTERFACE ) ) { return Model . ICON_INTERFACE ; } else if ( classDef . isAccessFlag ( DexFormat . AccessFlags . ACC_ENUM ) ) { return Model . ICON_ENUM ; } else { return Model . ICON_CLASS ; } } else if ( className . lastIndexOf ( "[]" ) == className . length - 2 ) { return Model . ICON_ENUM ; } else { return Model . ICON_REF ; } } public extractFileNameWithoutExt ( filename ) { var dotIndex = filename . lastIndexOf ( "." ) ; if ( dotIndex > - 1 ) { return filename . substr ( 0 , dotIndex ) ; } else { return filename ; } } } export class ClassDataWrapper { private classType : TypeDef ; private classDef : ClassDef ; private methodCount : number = 0 ; constructor ( classType , classDef ) { this . classType = classType ; this . classDef = classDef ; } public incrementMethodCount ( ) { this . methodCount ++ ; } public getMethodCount ( ) { return this . methodCount ; } public getClassType ( ) { return this . classType ; } public getClassDef ( ) { return this . classDef ; } } export class PackageNodeMetaData { private classes : ClassDataWrapper [ ] = [ ] ; private count : number = 0 ; public incrementMethodCount ( ) { this . count ++ ; } public addClassAndIncrement ( idx , classWrapper ) { if ( typeof this . classes [ idx ] === "undefined" ) { this . classes [ idx ] = classWrapper ; } this . classes [ idx ] . incrementMethodCount ( ) ; } public getCount ( ) { return this . count ; } public getClassWrapper ( idx ) { return this . classes [ idx ] ; } public getSortedClasses ( ) { return Object . keys ( this . classes ) . sort ( ( a , b ) => { return this . classes [ b ] . getMethodCount ( ) - this . classes [ a ] . getMethodCount ( ) ; } ) ; } } }