[파이썬] 안드로이드 프로젝트에서 자바 클래스 레퍼런스 카운트 구하기

2017. 10. 14. 09:06개발/파이썬

요즘 파이썬으로 공부하면서 그동안 필요했던 기능들을 파이썬으로 구현중인데, 배운 지 얼마 안됐지만 파이썬 언어 자체가 정말 매력적이네요 

C++ 과는 비교가 안될 정도로 코딩량도 적고 로직에 집중할 수 있어 좋은 듯. 

예제 코드의 목적

안드로이드 프로젝트가 커지면서 사용하지 않는 클래스가 점점 늘어남, 다른 파일에서 호출하지 않는 클래스를 찾기 위해 만듦

1. 모든 파일에서 클래스 이름을 찾아 저장 (한 파일에서 하나의 public class 만 있다고 가정)

2. 다시 모든 파일을 단어 단위로 읽어 '1' 에서 저장한 클래스 이름이 있으면 ref count 를 증가 시킴. (단, 클래스가 선언된 파일은 ref count 에서 제외

3. 참조하는 파일 리스트도 저장

4. csv 로 저장할 수 있도록 ',' 형식으로print --> 긁어서 메모장 같은데서 대충 편집 후 엑셀에서 불러와 ref count 0 인 클래스 확인 가능

import os
import codecs # utf-8 읽어 오기 위해
import re
 
gfilemap = {}
defPath = "c:\\myProject\\"
# key : class name value : path, ref count, ref path
gclassmap = {}
splitText = '[ .()[:<>,\t\n]'
 
 
def listallFile(path) :
    for fname in os.listdir(path) :
        fullname = os.path.join(path, fname)
        if os.path.isdir(fullname) :
            listallFile(fullname)
        else:
            gfilemap[fullname] = [fname, 0]
 
def readClassinJavaFile(path, isUTF8):
    f = None
    if (isUTF8):
        f = codecs.open(path, "r", 'utf-8')
    else:
        f = open(path, "r")
    while True:
        line = ""
        try:
            line = f.readline()
        except:
            print("코드파일 읽기 실패", path)
            return False;
 
        if not line : break;
 
        if line.find('public class ') < 0 :
            continue
 
        # 주석이면 skip
        if line.find('//') == 0 :
            continue
 
#        llist = line.split()
        llist = re.split(splitText, line)
        classname = ''
        for i in range(len(llist)) :
            if llist[i] == 'class':
                classname = llist[i+1]
                break;
 
        if len(classname) == 0:
            continue
 
        if classname in gclassmap.keys() :
            print('중복 클래스' , classname)
 
        classname = classname.replace('\n','')
        gclassmap[classname] = [path, 0, None]
        print(classname, path)
        return True # 한 파일에서 하나만 처리
    f.close()
    return True
 
 
def readClassRefinJavaFile(path, isUTF8):
    f = None
    if (isUTF8):
        f = codecs.open(path, "r", 'utf-8')
    else:
        f = open(path, "r")
    while True:
        line = ""
        try:
            line = f.readline()
        except:
            print("코드파일 읽기 실패", path)
            return False;
 
        if not line : break;
 
        # 주석이면 skip
        if line.find('//') == 0 :
            continue
 
        llist = re.split(splitText, line)
        for name in llist:
            if name in gclassmap.keys():
                # 파일이 동일 하지 않은 경우만 Ref count++ 한다
                if (gclassmap[name][0] != path) :
                    gclassmap[name][1] += 1;
                # 클래스를 Ref 한 소스 리스트를 추가한다
                # 중복을 피하기 위해 set() 으로 구성
                if (gclassmap[name][2] == None) :
                    gclassmap[name][2] = set()
                gclassmap[name][2].add(path)
                continue;
    f.close()
    return True
 
 
if __name__ == "__main__":
    listallFile(defPath)
 
 
    for key, value in gfilemap.items():
        if readClassinJavaFile(key,False) == False:
            readClassinJavaFile(key, True)
 
 
    for key, value in gfilemap.items():
        if readClassRefinJavaFile(key,False) == False:
            readClassRefinJavaFile(key, True)
 
 
    print('class ref count 정리')
    print('class,path,ref count,ref path')
    for key, value in gclassmap.items():
        i = 0
        path = ''
        for p in value[2]:
            path += p
            path += ' | '
            i += 1
            if (i > 5):
                break;
 
 
        msg = '%s,%s,%s,%s'%(key,value[0],value[1], path)
        print(msg)
splitText = '[ .()[:<>,\t\n]'
llist = re.split(splitText, line)
for i in range(len(llist)) :

단어 단위 분리는 re.split 을 이용했습니다.

 gclassmap[classname] = [path, 0, None]

클래스 이름을 key 로 해서 딕셔너리를 구성했는데 key,value[] 는 요런 식입니다.

 

key(클래스이름) = [클래스가 있는 파일 path, 참조 카운터, 참조하는 파일들(set)]

C++ 같으면 작은 아이템이라도 서로 다른 변수형을 담기 위해 항상 구조체/클래스부터 선언해야 하는데 그럴 필요가 없어서 확실히 코딩량이 줄어드네요

        for name in llist:
            if name in gclassmap.keys():
                # 파일이 동일 하지 않은 경우만 Ref count++ 한다
                if (gclassmap[name][0] != path) :
                    gclassmap[name][1] += 1;
                # 클래스를 Ref 한 소스 리스트를 추가한다
                # 중복을 피하기 위해 set() 으로 구성
                if (gclassmap[name][2] == None) :
                    gclassmap[name][2] = set()
                gclassmap[name][2].add(path)
                continue;

 

key[name] 에 해당하는 value 배열의 [1] 값을 1 증가 시킵니다. 요것도 C++ 사용자에게는 조금 낯선 표현인데, 확실히 편하긴 하네요 (단 [1] [2] 식으로 표현하니 변수 명으로 이름을 파악할 수가 없어서 가독성은 조금 떨어지는 듯.