반응형
public String correctEnglishTypo(TypoVo typoVo) {

    List<String> correctedWords = new ArrayList<>();
    int choCount = 0; int joongCount = 0; int jongCount = 0; 

    String englishTypo = typoVo.getKeyword(); //"DnlsehDNDptJQKFMStkrwp";
    String koreanLetters = convertEnglishToKorean(englishTypo); // 변환된 한글 자/모 집합


    /** 초성 / 중성 / 종성 분리 **/
    for(int i = 0; i < koreanLetters.length(); i++) {

        boolean isCurrentJoong = false; // 중성
        boolean isChoJoong = false;

        String currentLetter = String.valueOf(koreanLetters.charAt(i)); // 현) 자/모

        String nextLetter = "";       // 다음)  자/모
        String secondNextLetter = ""; // 다다음) 자/모

        int beforeIndex = i - 1;
        int nextIndex = i + 1;
        int secondNextIndex = i + 2;


        /** 마지막인덱스 -1 범위에 속할 경우만 다음 값을 가져온다 **/
        if(isWithinBounds(i + 1, koreanLetters.length())) {
            nextLetter = String.valueOf(koreanLetters.charAt(i + 1));
        }

        /** 마지막인덱스 -2 범위에 속할 경우만 다음 값을 가져온다 **/
        if(isWithinBounds(i + 2, koreanLetters.length())) { 
            secondNextLetter = String.valueOf(koreanLetters.charAt(i + 2));
        }


        /** 중성 체크 **/
        for(String JOONG : Korean.JOONGS) {

            isCurrentJoong = (currentLetter.equals(JOONG))? true : false;

            /** (초성 + 중성)의 단어인지 체크 **/
            if(isCurrentJoong) {

                isChoJoong = checkChoJoong(nextLetter, secondNextLetter);

            }

            /** (초성 + 중성)의 단어일 경우 해당 단어 추가 후 초기화  **/
            if(isChoJoong && isCurrentJoong) {
                correctedWords.add(koreanLetters.substring(beforeIndex, nextIndex));

                choCount = 0; joongCount = 0; jongCount = 0;
            }

            /** (초성 + 중성 + 종성)의 단어일 경우  **/
            if(isChoJoong == false && isCurrentJoong) {
                joongCount = 1;
                break;
            }

        }


        /** 종성 체크 **/
        for(String jong : Korean.JONGS) {

            // 종성에 포함되는가?
            boolean isContainsJong = (currentLetter.equals(jong))? true : false;

            // 종성에 포함되면서 초성이 나온 경우는 종성 [초성과 종성의 겹치는 단어가 존재하기 때문에 체크 필요]
            boolean isJong = (choCount == 1 && isContainsJong) ? true : false;

            if(isJong) { jongCount = 1; break; }
        }


        /** 초성 체크 **/
        for(String cho : Korean.CHOS) {

            // 초성에 포함되는가?
            boolean isContainsCho = (currentLetter.equals(cho))? true : false;

            // 초성에 포함되면서 초성이 한번도 안 나온 경우는 초성 [초성과 종성의 겹치는 단어가 존재하기 때문에 체크 필요]
            boolean isCho = (choCount == 0 && joongCount == 0 && isContainsCho) ? true : false;

            if(isCho) { choCount = 1; break; }
        }




        /** 초성 + 중성 + 종성의 단어인 경우 조합 **/
        if((choCount == 1 && joongCount == 1 && jongCount == 1)) {

            correctedWords.add(koreanLetters.substring(i - 2, i + 1)); // [초 + 중 + 종] 값 가져오기
            choCount = 0; joongCount = 0; jongCount = 0;               // 한글자가 완성되어 초기화

        }


        /** 마지막 글자인 경우 (초성 + 중성) **/
        boolean isLastChoJoong = nextIndex == koreanLetters.length() 
                              && secondNextLetter.equals("") 
                              && nextLetter.equals("") 
                              && isCurrentJoong == true 
                              && choCount == 1 && joongCount == 1 && jongCount == 0 ? true : false; 

        /** 마지막 글자인 경우 (초성 + 중성 + 종성) **/
        boolean isLastChoJoongJong = secondNextIndex == koreanLetters.length() 
                                  && secondNextLetter.equals("") 
                                  && isCurrentJoong == true 
                                  && choCount == 1 && joongCount == 1 && jongCount == 0 ? true : false;



        if (isLastChoJoong) {
            correctedWords.add(koreanLetters.substring(beforeIndex, nextIndex));
            break;
        }else if (isLastChoJoongJong) {
            correctedWords.add(koreanLetters.substring(beforeIndex, secondNextIndex));
            break;
        }



    }

    StringBuilder words = new StringBuilder();

    /** 자/모 합쳐서 단어 생성 **/
    for(String correctWord : correctedWords) {

        int cho = 0; int joong = 0; int jong = 0;

        /** 한글자 자/모 **/
        for(int i = 0; i < correctWord.length(); i++) {

            String letter = Character.valueOf(correctWord.charAt(i)).toString();

            if(i == 0) cho = (int) Korean.CHO_CONVERT_MAP.get(letter);
            if(i == 1) joong = (int) Korean.JOONG_CONVERT_MAP.get(letter);
            if(i == 2) jong = (int) Korean.JONG_MAP.get(letter);
        }

        // 자/모 합치기 공식
        char word = (char) ((cho * 21 + joong) * 28 + jong + Korean.HANGUL_BASE); 
        words.append(word);

    }

    return words.toString();

    // 1. 한글자씩 나눈다 (종성인 경우 분리)
    // 2. 계산식으로 합친 한글자로 변환
    // 3. 한글자 변환된 항목 합치기

    // * 완전 이상한 오타인 경우 무시 정상적인 오타만 취급

//		char a = ((초성 * 21 + 중성) * 28 + (종성) + 0xAC00)
//		char a = ((3 * 21 + 8) * 28 + 4 + 0xAC00);

//		System.out.println(a);
}



/**
 * @param nextLetter       - 다음 자/모
 * @param secondNextLetter - 다다음 자/모
 * @return 초성/중성 여부
 */
private boolean checkChoJoong(String nextLetter, String secondNextLetter) {

    boolean isChoJoong = false;

    /** 종성 체크 (초성 + 중성으로 끝나는 단어인지 체크) **/
    for(String JONG_CHAR : Korean.JONGS) {

        // 두번째 자/모가 중성인가?
        boolean isSecondNextJoong = checkJoong(secondNextLetter); 

        // 다음 자/모가 종성인가?
        boolean isNextJong = (nextLetter.equals(JONG_CHAR))? true : false;

        if(isNextJong == false && isSecondNextJoong) {

            isChoJoong = true;
            break;
        }
    }
    return isChoJoong;
}


/**
 * @param  secondNextLetter - 다다음 자/모
 * @return 다다음 자/모 중성 여부
 */
private boolean checkJoong(String secondNextLetter) {

    boolean isSecondNextJoong = false;

    for(String Joong : Korean.JOONGS) {
        isSecondNextJoong = (secondNextLetter.equals(Joong))? true : false;
        if(isSecondNextJoong) break;
    }

    return isSecondNextJoong;
}



/**
 * @param  index  - 현재 인덱스
 * @param  length - Array 및 String 인덱스의 전체 크기
 * @return 정상 범위 체크 (out of bounds 체크)
 */
private boolean isWithinBounds(int index, int length) {
    return index >= 0 && index < length;
}

/**
 * @param  englishTypo - 영문 오타
 * @return 영문 오타를 한글로 변환 (rk → ㄱㅏ)
 */
private String convertEnglishToKorean(String englishTypo) {

    StringBuilder korean = new StringBuilder(englishTypo);
    String volew = "";

    /** 이중모음 변환 **/
    for (Map.Entry<String, String> entry : Korean.DOUBLE_VOWELS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        korean.replace(0, korean.length(), korean.toString().replace(key, value));
    }

    /** 단모음 변환 **/
    for (Map.Entry<String, String> entry : Korean.SINGLE_VOWELS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        korean.replace(0, korean.length(), korean.toString().replace(key, value));
    }


    /** 쌍자음 변환 **/
    for (Map.Entry<String, String> entry : Korean.DOUBLE_CONSONANTS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();

        int isContain = korean.indexOf(key);

        // 다다음 글자가 모음이 아니여야 쌍자음 변환
        if(isContain != -1) {

            try {volew = String.valueOf(korean.charAt(isContain + 2));}
            catch(Exception e) {volew = String.valueOf(korean.charAt(isContain + 1));}

            boolean hasNextVolew = Pattern.matches(koreanRegex, volew);

            if(!hasNextVolew) korean.replace(0, korean.length(), korean.toString().replace(key, value));

        }


    }


    /** 단자음 변환 **/
    for (Map.Entry<String, String> entry : Korean.SINGLE_CONSONANTS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        korean.replace(0, korean.length(), korean.toString().replace(key, value));
    }


    return korean.toString();
}
반응형