ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [파이썬] 안드로이드 프로젝트에서 자바 클래스 레퍼런스 카운트 구하기
    개발/파이썬 2017.10.14 09:06

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

    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] 식으로 표현하니 변수 명으로 이름을 파악할 수가 없어서 가독성은 조금 떨어지는 듯. 



    댓글 0

Designed by black7375.