package jp.ac.waseda.info.kake.moca.syllable; import java.io.IOException; import java.util.ArrayList; import jp.ac.waseda.info.kake.system.InputMain; import jp.ac.waseda.info.kake.system.PrintIntegerMaker; /** * 音節をまとめて管理します。 * * @author Tony * */ public class SyllabifiedString { private final SyllablePart[] syllables; /** * 文字列からSyllabifiedStringインスタンスを生成します。 * * @param string */ public SyllabifiedString(String string) { Syllable[] syl_temp = Syllable.createSyllables(string); syllables = new SyllablePart[syl_temp.length]; int start = 0; for (int i = 0; i < syllables.length; i++) { syllables[i] = new SyllablePart(syl_temp[i], start, this); start += syl_temp[i].length; } } @Override public String toString() { StringBuffer res = new StringBuffer(); for (SyllablePart syl : syllables) res.append(syl).append("\n"); return res.toString(); } public int length() { return syllables[syllables.length - 1].end; } /** * 変換候補それぞれの部分文字列を、文字列配列として返します。beginIndex文字目が音節の先頭でない場合、beginIndex文字目が属する音節は変換されません。 * beginIndexとendIndexが異なる場合、結果に長さ0の文字列が含まれることはありません。 * * 結果の先頭には、表層表現の部分文字列が入ります。 * * @param beginIndex * @param endIndex * @return 部分文字列を要素に持つ文字列配列。beginIndexとendIndexが等しい場合、要素数0の文字列配列 * @throws IndexOutOfBoundsException */ public String[] substrings(int beginIndex, int endIndex) throws IndexOutOfBoundsException { return substrings(beginIndex, endIndex, false); } /** * 変換候補それぞれの部分文字列を、文字列配列として返します。beginIndex文字目が省略される候補は無視されます。 * また、fixesBeginIndexがfalseであり、かつbeginIndex文字目が音節の先頭でない場合、beginIndex文字目が属する音節は変換されません。 * beginIndexとendIndexが異なる場合、結果に長さ0の文字列が含まれることはありません。 * * 結果の先頭には、表層表現の部分文字列が入ります。 * * @param beginIndex * @param endIndex * @param fixesBeginIndex * @return 部分文字列を要素に持つ文字列配列。beginIndexとendIndexが等しい場合、要素数0の文字列配列 * @throws IndexOutOfBoundsException */ public String[] substrings(int beginIndex, int endIndex, boolean fixesBeginIndex) throws IndexOutOfBoundsException { ArrayList<String> res = new ArrayList<String>(); int len = length(); if (beginIndex < 0 || beginIndex > len) throw new IndexOutOfBoundsException("beginIndex: " + beginIndex); if (endIndex < 0 || endIndex > len) throw new IndexOutOfBoundsException("endIndex: " + endIndex); if (beginIndex > endIndex) throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + ", endIndex: " + endIndex); if (beginIndex == endIndex) return new String[0]; int sylBegin = getSyllableNumber(beginIndex); int sylEnd = getSyllableNumber(endIndex - 1); ArrayList<String> temp; int sylSubBegin; int sylSubEnd; for (int i = sylBegin; i <= sylEnd; i++) { sylSubBegin = beginIndex > syllables[i].start ? beginIndex : syllables[i].start; sylSubEnd = endIndex < syllables[i].end ? endIndex : syllables[i].end; if (res.size() == 0) { if (fixesBeginIndex || beginIndex == syllables[i].start) { for (String si : syllables[i].substrings(sylSubBegin, sylSubEnd)) if (si.length() > 0) // 空文字列が含まれるのを防ぐ res.add(si); } else { res.add(syllables[i].substrings(sylSubBegin, sylSubEnd)[0]); } continue; } temp = res; res = new ArrayList<String>(); for (String ti : temp) for (String si : syllables[i].substrings(sylSubBegin, sylSubEnd)) res.add(ti + si); // tempに空文字列はないので、ここでは特に防ぐ必要はない // TODO この仕様だと、長音記号や促音撥音が文字列先頭にある時に対応できない } return res.toArray(new String[0]); } /** * charPos文字目( 0<=charPos<length() )を含む音節の、syllablesにおける添字を返します。 * * @param charPos * @return * @throws IndexOutOfBoundsException * 0<=charPos<length() を満たさなかった場合 */ private int getSyllableNumber(int charPos) throws IndexOutOfBoundsException { if (charPos < 0) throw new IndexOutOfBoundsException("charPos: " + charPos); for (int i = 0; i < syllables.length; i++) if (charPos < syllables[i].end) return i; throw new IndexOutOfBoundsException("charPos: " + charPos); } /** * charPos文字目( 0<=charPos<length() )を含む音節を返します。 * * @param charPos * @return * @throws IndexOutOfBoundsException * 0<=charPos<length() を満たさなかった場合 */ public Syllable getSyllable(int charPos) throws IndexOutOfBoundsException { if (charPos < 0) throw new IndexOutOfBoundsException("charPos: " + charPos); for (int i = 0; i < syllables.length; i++) if (charPos < syllables[i].end) return syllables[i].syllable; throw new IndexOutOfBoundsException("charPos: " + charPos); } /** * Syllableに開始/終了位置情報を追加したものです。SyllabifiedString内で使用します。 * * @author Tony * */ class SyllablePart { public final SyllabifiedString parent; public final Syllable syllable; public final int start; public final int end; public SyllablePart(Syllable syllable, int start, SyllabifiedString parent) { this.syllable = syllable; this.start = start; this.parent = parent; end = start + syllable.length; } @Override public String toString() { return new StringBuffer( new PrintIntegerMaker(parent.syllables[parent.syllables.length - 1].start).getPrintInteger(start)) .append(": ").append(syllable).toString(); } /** * SyllabifiedString内での文字位置を利用し、変換候補それぞれの部分文字列を、文字列配列として返します。 * * 結果の先頭には、表層表現の部分文字列が入ります。 * * @param beginIndex * @param endIndex * @return * @throws IndexOutOfBoundsException */ public String[] substrings(int beginIndex, int endIndex) throws IndexOutOfBoundsException { return syllable.substrings(beginIndex - start, endIndex - start); } } public static void main(String[] args) throws IOException { new InputMain(args, "文字列を入力してください") { @Override public void run(String line) { SyllabifiedString syl = new SyllabifiedString(line); System.out.println(syl); StringBuffer space = new StringBuffer(); for (int i = 0; i < syl.length(); i++) { for (int j = i + 1; j <= syl.length(); j++) { String[] subs = syl.substrings(i, j); for (String si : subs) { System.out.println(space + si + " (" + i + ", " + j + ")"); } } space.append(" "); } System.out.println(); } }.main(); } }