사진찍는 프로그래머

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

개발/파이썬

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

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

예제 코드의 목적

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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)
cs


1
2
3
4
splitText = '[ .()[:<>,\t\n]'
llist = re.split(splitText, line)
for i in range(len(llist)) :
 
cs

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

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

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

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

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


1
2
3
4
5
6
7
8
9
10
11
        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;
cs


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



저작자 표시
신고