Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ jobs:
steps:
- uses: actions/checkout@v1

- name: set up JDK 11
- name: set up JDK 21
uses: actions/setup-java@v1
with:
java-version: 11
java-version: 21

- name: Unit Test
run: ./gradlew testDebugUnitTest
Expand Down
2 changes: 1 addition & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,15 @@
모아키의 기본적인 동작에서 크게 벗어난 완전히 새로운 입력 방법을 구현하는 것은
현재로서는 목표가 아님을 이해해주시기 바랍니다.

### 오픈 소스 라이선스

이 프로젝트는 다음 오픈 소스 프로젝트의 코드를 포함합니다:

- [HangulParser][5] (MIT License)

[1]: https://www.youtube.com/watch?v=Mcz0sSz1Ky4
[2]: https://doi.org/10.8080/1020110078022
[3]: https://www.law.go.kr/%EB%B2%95%EB%A0%B9/%ED%8A%B9%ED%97%88%EB%B2%95
[4]: https://developer.android.com/studio
[5]: https://github.com/kimkevin/HangulParser
[101]: https://github.com/AiOO/OpenMoa/actions/workflows/test.yml/badge.svg
1 change: 0 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation "androidx.autofill:autofill:1.1.0"
implementation 'com.github.kimkevin:hangulparser:1.0.0'
implementation 'com.google.android.material:material:1.7.0'
implementation 'io.insert-koin:koin-android:3.3.0'
testImplementation 'junit:junit:4.13.2'
Expand Down
157 changes: 157 additions & 0 deletions app/src/main/java/com/github/kimkevin/hangulparser/HangulParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.github.kimkevin.hangulparser;

import java.util.ArrayList;
import java.util.List;

/**
* HangulParser is to seperate Hangul to basic consonant and vowel by using Unicode
* @see HangulParserException
*
* ref : Hangul Syllables http://www.unicode.org/charts/PDF/UAC00.pdf
*/

public class HangulParser {
private static final String TAG = HangulParser.class.getSimpleName();

// First '가' : 0xAC00(44032), 끝 '힟' : 0xD79F(55199)
private static final int FIRST_HANGUL = 44032;

// 19 initial consonants
private static final char[] CHOSUNG_LIST = {
'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ',
'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
};

private static int JUNGSUNG_COUNT = 21;

// 21 vowels
private static final char[] JUNGSUNG_LIST = {
'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ',
'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ',
'ㅣ'
};

private static int JONGSUNG_COUNT = 28;

// 28 consonants placed under a vowel(plus one empty character)
private static final char[] JONGSUNG_LIST = {
' ', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ',
'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ',
'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
};

public static List<String> disassemble(char hangul) throws HangulParserException {
List<String> jasoList = new ArrayList<>();

String hangulStr = String.valueOf(hangul);

if (hangulStr.matches(".*[가-힣]+.*")) {
int baseCode = hangulStr.charAt(0) - FIRST_HANGUL;

final int chosungIndex = baseCode / (JONGSUNG_COUNT * JUNGSUNG_COUNT);
jasoList.add(Character.toString(CHOSUNG_LIST[chosungIndex]));

final int jungsungIndex = (baseCode - ((JONGSUNG_COUNT * JUNGSUNG_COUNT) * chosungIndex)) / JONGSUNG_COUNT;
jasoList.add(Character.toString(JUNGSUNG_LIST[jungsungIndex]));

final int jongsungIndex = (baseCode - ((JONGSUNG_COUNT * JUNGSUNG_COUNT) * chosungIndex) - (JONGSUNG_COUNT * jungsungIndex));
if (jongsungIndex > 0) {
jasoList.add(Character.toString(JONGSUNG_LIST[jongsungIndex]));
}
} else if (hangulStr.matches(".*[ㄱ-ㅎ]+.*")) {
throw new HangulParserException("음절이 아닌 자음입니다");
} else if (hangulStr.matches(".*[ㅏ-ㅣ]+.*")) {
throw new HangulParserException("음절이 아닌 모음입니다");
} else {
throw new HangulParserException("한글이 아닙니다");
}

return jasoList;
}

public static List<String> disassemble(String hangul) throws HangulParserException {
List<String> jasoList = new ArrayList<String>();

for (int i = 0, li = hangul.length(); i < li; i++) {
try {
jasoList.addAll(disassemble(hangul.charAt(i)));
} catch (HangulParserException e) {
throw new HangulParserException((i+1) + "번째 글자 분리 오류 : " + e.getMessage());
}
}

return jasoList;
}

public static String assemble(List<String> jasoList) throws HangulParserException {
if (jasoList.size() > 0) {
String result = "";
int startIdx = 0;

while (true) {
if(startIdx < jasoList.size()) {
final int assembleSize = getNextAssembleSize(jasoList, startIdx);
result += assemble(jasoList, startIdx, assembleSize);
startIdx += assembleSize;
} else {
break;
}
}

return result;
} else {
throw new HangulParserException("자소가 없습니다");
}
}

private static String assemble(List<String> jasoList, final int startIdx, final int assembleSize) throws HangulParserException {
int unicode = FIRST_HANGUL;

final int chosungIndex = new String(CHOSUNG_LIST).indexOf(jasoList.get(startIdx));

if (chosungIndex >= 0) {
unicode += JONGSUNG_COUNT * JUNGSUNG_COUNT * chosungIndex;
} else {
throw new HangulParserException((startIdx + 1) + "번째 자소가 한글 초성이 아닙니다");
}

final int jungsungIndex = new String(JUNGSUNG_LIST).indexOf(jasoList.get(startIdx + 1));

if(jungsungIndex >= 0) {
unicode += JONGSUNG_COUNT * jungsungIndex;
} else {
throw new HangulParserException((startIdx + 2) + "번째 자소가 한글 중성이 아닙니다");
}

if (assembleSize > 2) {
final int jongsungIndex = new String(JONGSUNG_LIST).indexOf(jasoList.get(startIdx + 2));

if (jongsungIndex >= 0) {
unicode += jongsungIndex;
} else {
throw new HangulParserException((startIdx + 3) + "번째 자소가 한글 종성이 아닙니다");
}
}

return Character.toString((char) unicode);
}

private static int getNextAssembleSize(List<String> jasoList, final int startIdx) throws HangulParserException {
final int remainJasoLength = jasoList.size() - startIdx;
final int assembleSize;

if (remainJasoLength > 3) {
if (new String(JUNGSUNG_LIST).contains(jasoList.get(startIdx + 3))) {
assembleSize = 2;
} else {
assembleSize = 3;
}
} else if(remainJasoLength == 3 || remainJasoLength == 2) {
assembleSize = remainJasoLength;
} else {
throw new HangulParserException("한글을 구성할 자소가 부족하거나 한글이 아닌 문자가 있습니다");
}

return assembleSize;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.kimkevin.hangulparser;

public class HangulParserException extends Exception{
public HangulParserException() {
super();
}

public HangulParserException(String message) {
super(message);
}
}

4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.4.1' apply false
id 'com.android.library' version '7.4.1' apply false
id 'com.android.application' version '8.8.1' apply false
id 'com.android.library' version '8.8.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
}
4 changes: 3 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 4 additions & 3 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#Sun Oct 23 01:32:23 KST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading