OSDN Git Service

Merge commit '2234b50cfbe7c86237086a3bf4e62397814a390e'
[jindolf/JinParser.git] / src / main / java / jp / sourceforge / jindolf / parser / AbstractParser.java
index f1fbfd0..ac5dbb6 100644 (file)
-/*\r
- * abstract XHTML parser\r
- *\r
- * Copyright(c) 2009 olyutorskii\r
- * $Id: AbstractParser.java 894 2009-11-04 07:26:59Z olyutorskii $\r
- */\r
-\r
-package jp.sourceforge.jindolf.parser;\r
-\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-import jp.sourceforge.jindolf.corelib.GameRole;\r
-\r
-/**\r
- * 人狼BBS生成のXHTML文書を解釈するパーサの抽象基底クラス。\r
- * {@link DecodedContent}の内容をパースし、\r
- * 各種ハンドラへ通知する処理の基盤を構成する。\r
- * 正規表現エンジンを実装基盤とする。\r
- * 親パーサを指定することにより、検索対象文字列とマッチエンジンを\r
- * 親パーサと共有することができる。\r
- * @see Matcher\r
- */\r
-public abstract class AbstractParser implements ChainedParser{\r
-\r
-    /** ホワイトスペース。 */\r
-    protected static final String SPCHAR = "\u0020\\t\\n\\r";\r
-    /** 0回以上連続するホワイトスペースの正規表現。 */\r
-    protected static final String SP_I = "[" +SPCHAR+ "]*";\r
-\r
-    private static final Pattern DUMMY_PATTERN = compile("\u0000");\r
-\r
-    /**\r
-     * 正規表現のコンパイルを行う。\r
-     * デフォルトで{@link java.util.regex.Pattern#DOTALL}が\r
-     * オプション指定される。\r
-     * @param regex 正規表現文字列\r
-     * @return マッチエンジン\r
-     */\r
-    protected static Pattern compile(CharSequence regex){\r
-        Pattern result = Pattern.compile(regex.toString(), Pattern.DOTALL);\r
-        return result;\r
-    }\r
-\r
-    private final ChainedParser parent;\r
-\r
-    private DecodedContent content;\r
-    private Matcher matcher;\r
-    private String contextErrorMessage;\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     */\r
-    protected AbstractParser(){\r
-        this(null);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param parent 親パーサ\r
-     */\r
-    protected AbstractParser(ChainedParser parent){\r
-        super();\r
-        this.parent = parent;\r
-        resetImpl();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * パーサの状態をコンストラクタ直後の状態にリセットする。\r
-     * ※コンストラクタから呼ばせるためにオーバーライド不可\r
-     */\r
-    private void resetImpl(){\r
-        this.content = null;\r
-        this.matcher = null;\r
-        this.contextErrorMessage = null;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * パーサの状態をリセットする。\r
-     */\r
-    public void reset(){\r
-        if(this.parent != null){\r
-            throw new UnsupportedOperationException();\r
-        }\r
-        resetImpl();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     */\r
-    /**\r
-     * パース対象文字列をセットする。\r
-     * パースが終わるまでこの文字列の内容を変更してはならない。\r
-     * @param content パース対象文字列\r
-     */\r
-    public void setContent(DecodedContent content){\r
-        if(this.parent != null){\r
-            throw new UnsupportedOperationException();\r
-        }\r
-\r
-        CharSequence rawContent = content.getRawContent();\r
-\r
-        this.content = content;\r
-        this.matcher = DUMMY_PATTERN.matcher(rawContent);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     */\r
-    public DecodedContent getContent(){\r
-        if(this.parent != null){\r
-            return this.parent.getContent();\r
-        }\r
-\r
-        return this.content;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     */\r
-    public Matcher getMatcher(){\r
-        if(this.parent != null){\r
-            return this.parent.getMatcher();\r
-        }\r
-\r
-        return this.matcher;\r
-    }\r
-\r
-    /**\r
-     * 文脈依存のエラーメッセージを設定する。\r
-     * {@link #buildParseException}で利用される。\r
-     * 設定内容は親へ委譲されない。\r
-     * @param errorMessage エラーメッセージ。nullも可能。\r
-     */\r
-    protected void setContextErrorMessage(String errorMessage){\r
-        this.contextErrorMessage = errorMessage;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 文脈状況に応じたパース例外を生成する。\r
-     * 例外にはリージョン開始位置が埋め込まれる。\r
-     * @return パース例外\r
-     */\r
-    protected HtmlParseException buildParseException(){\r
-        HtmlParseException result;\r
-        result = new HtmlParseException(this.contextErrorMessage,\r
-                                        regionStart() );\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * パースに使う正規表現パターンを切り替える。\r
-     * @param pattern 正規表現パターン\r
-     */\r
-    protected void switchPattern(Pattern pattern){\r
-        getMatcher().usePattern(pattern);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 最後のマッチに成功した文字領域以前をパース対象から外す。\r
-     */\r
-    protected void shrinkRegion(){\r
-        int lastMatchedEnd;\r
-        try{\r
-            lastMatchedEnd = matchEnd();\r
-        }catch(IllegalStateException e){\r
-            return;\r
-        }\r
-\r
-        int regionEnd   = regionEnd();\r
-\r
-        getMatcher().region(lastMatchedEnd, regionEnd);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 検査対象の一部が指定パターンにマッチするか判定する。\r
-     * @param pattern 指定パターン\r
-     * @return マッチすればtrue\r
-     */\r
-    protected boolean findProbe(Pattern pattern){\r
-        switchPattern(pattern);\r
-        if( getMatcher().find() ) return true;\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * 検査対象先頭が指定パターンにマッチするか判定する。\r
-     * @param pattern 指定パターン\r
-     * @return マッチすればtrue\r
-     */\r
-    protected boolean lookingAtProbe(Pattern pattern){\r
-        switchPattern(pattern);\r
-        if( getMatcher().lookingAt() ) return true;\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * 検査対象全体が指定パターンにマッチするか判定する。\r
-     * @param pattern 指定パターン\r
-     * @return マッチすればtrue\r
-     */\r
-    protected boolean matchesProbe(Pattern pattern){\r
-        switchPattern(pattern);\r
-        if( getMatcher().matches() ) return true;\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * 残りの検索対象領域からパターンがマッチする部分を探す。\r
-     * 見つからなければ例外をスローする。\r
-     * @param pattern 正規表現パターン\r
-     * @throws HtmlParseException\r
-     * マッチしなかった\r
-     */\r
-    protected void findAffirm(Pattern pattern)\r
-            throws HtmlParseException{\r
-        if( ! findProbe(pattern) ){\r
-            throw buildParseException();\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 残りの検索対象領域先頭からパターンがマッチする部分を探す。\r
-     * 見つからなければ例外をスローする。\r
-     * @param pattern 正規表現パターン\r
-     * @throws HtmlParseException\r
-     * マッチしなかった\r
-     */\r
-    protected void lookingAtAffirm(Pattern pattern)\r
-            throws HtmlParseException{\r
-        if( ! lookingAtProbe(pattern) ){\r
-            throw buildParseException();\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 残りの検索対象領域全体がパターンにマッチするか調べる。\r
-     * マッチしなければ例外をスローする。\r
-     * @param pattern 正規表現パターン\r
-     * @throws HtmlParseException\r
-     * マッチしなかった\r
-     */\r
-    protected void matchesAffirm(Pattern pattern)\r
-            throws HtmlParseException{\r
-        if( ! matchesProbe(pattern) ){\r
-            throw buildParseException();\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 最後のマッチで任意の前方参照グループがヒットしたか判定する。\r
-     * @param group グループ番号\r
-     * @return ヒットしていたらtrue\r
-     */\r
-    protected boolean isGroupMatched(int group){\r
-        if(matchStart(group) >= 0) return true;\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * 最後にマッチした前方参照グループを数値化する。\r
-     * 0以上の整数のみサポート。\r
-     * @param group グループ番号\r
-     * @return 数値\r
-     */\r
-    protected int parseGroupedInt(int group){\r
-        int result = 0;\r
-\r
-        CharSequence rawContent = getContent().getRawContent();\r
-        int start = matchStart(group);\r
-        int end   = matchEnd(group);\r
-        for(int pos = start; pos < end; pos++){\r
-            char letter = rawContent.charAt(pos);\r
-            int digit = Character.digit(letter, 10);\r
-            result = result * 10 + digit;\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 最後にマッチした前方参照グループの開始位置を得る。\r
-     * @param group 前方参照識別番号\r
-     * @return 開始位置\r
-     */\r
-    protected int matchStart(int group){\r
-        return getMatcher().start(group);\r
-    }\r
-\r
-    /**\r
-     * 最後にマッチした前方参照グループの終了位置を得る。\r
-     * @param group 前方参照識別番号\r
-     * @return 終了位置\r
-     */\r
-    protected int matchEnd(int group){\r
-        return getMatcher().end(group);\r
-    }\r
-\r
-    /**\r
-     * 最後にマッチした全領域の開始位置を得る。\r
-     * @return 開始位置\r
-     */\r
-    protected int matchStart(){\r
-        return getMatcher().start();\r
-    }\r
-\r
-    /**\r
-     * 最後にマッチした全領域の終了位置を得る。\r
-     * @return 終了位置\r
-     */\r
-    protected int matchEnd(){\r
-        return getMatcher().end();\r
-    }\r
-\r
-    /**\r
-     * 検索領域の先頭位置を返す。\r
-     * @return 先頭位置\r
-     */\r
-    protected int regionStart(){\r
-        return getMatcher().regionStart();\r
-    }\r
-\r
-    /**\r
-     * 検索領域の末尾位置を返す。\r
-     * @return 末尾位置\r
-     */\r
-    protected int regionEnd(){\r
-        return getMatcher().regionEnd();\r
-    }\r
-\r
-    /**\r
-     * 0個以上のホワイトスペースを読み飛ばす。\r
-     * 具体的には検索対象領域の先頭が進むだけ。\r
-     */\r
-    protected void sweepSpace(){\r
-        CharSequence rawContent = getContent().getRawContent();\r
-\r
-        boolean hasSpace = false;\r
-        int regionStart = regionStart();\r
-        int regionEnd   = regionEnd();\r
-\r
-        for( ; regionStart < regionEnd; regionStart++){\r
-            char letter = rawContent.charAt(regionStart);\r
-\r
-            switch(letter){\r
-            case '\u0020':\r
-            case '\t':\r
-            case '\n':\r
-            case '\r':\r
-                hasSpace = true;\r
-                continue;\r
-            default:\r
-                break;\r
-            }\r
-\r
-            break;\r
-        }\r
-\r
-        if(hasSpace){\r
-            getMatcher().region(regionStart, regionEnd);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 検索領域の先頭から各種役職名のマッチを試みる。\r
-     * @return 役職。何もマッチしなければnullを返す。\r
-     */\r
-    protected GameRole lookingAtRole(){\r
-        GameRole role = GameRole.lookingAtRole(getMatcher());\r
-        return role;\r
-    }\r
-\r
-}\r
+/*
+ * abstract XHTML parser
+ *
+ * License : The MIT License
+ * Copyright(c) 2009 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.parser;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import jp.sourceforge.jindolf.corelib.GameRole;
+
+/**
+ * 人狼BBS生成のXHTML文書を解釈するパーサの抽象基底クラス。
+ * {@link DecodedContent}の内容をパースし、
+ * 各種ハンドラへ通知する処理の基盤を構成する。
+ * 正規表現エンジンを実装基盤とする。
+ * 親パーサを指定することにより、検索対象文字列とマッチエンジンを
+ * 親パーサと共有することができる。
+ * @see Matcher
+ */
+public abstract class AbstractParser implements ChainedParser{
+
+    /** ホワイトスペース。 */
+    protected static final String SPCHAR = "\u0020\\t\\n\\r";
+    /** 0回以上連続するホワイトスペースの正規表現。 */
+    protected static final String SP_I = "[" +SPCHAR+ "]*";
+
+    private static final Pattern DUMMY_PATTERN = compile("\u0000");
+
+
+    private final ChainedParser parent;
+
+    private DecodedContent content;
+    private Matcher matcher;
+    private String contextErrorMessage;
+
+
+    /**
+     * コンストラクタ。
+     */
+    protected AbstractParser(){
+        this(null);
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     * @param parent 親パーサ
+     */
+    protected AbstractParser(ChainedParser parent){
+        super();
+        this.parent = parent;
+        resetImpl();
+        return;
+    }
+
+
+    /**
+     * 正規表現のコンパイルを行う。
+     * デフォルトで{@link java.util.regex.Pattern#DOTALL}が
+     * オプション指定される。
+     * @param regex 正規表現文字列
+     * @return マッチエンジン
+     */
+    protected static Pattern compile(CharSequence regex){
+        Pattern result = Pattern.compile(regex.toString(), Pattern.DOTALL);
+        return result;
+    }
+
+    /**
+     * パーサの状態をコンストラクタ直後の状態にリセットする。
+     * ※コンストラクタから呼ばせるためにオーバーライド不可
+     */
+    private void resetImpl(){
+        this.content = null;
+        this.matcher = null;
+        this.contextErrorMessage = null;
+        return;
+    }
+
+    /**
+     * パーサの状態をリセットする。
+     */
+    public void reset(){
+        if(this.parent != null){
+            throw new UnsupportedOperationException();
+        }
+        resetImpl();
+        return;
+    }
+
+    /**
+     * パース対象文字列をセットする。
+     * パースが終わるまでこの文字列の内容を変更してはならない。
+     * @param content パース対象文字列
+     */
+    public void setContent(DecodedContent content){
+        if(this.parent != null){
+            throw new UnsupportedOperationException();
+        }
+
+        CharSequence rawContent = content.getRawContent();
+
+        this.content = content;
+        this.matcher = DUMMY_PATTERN.matcher(rawContent);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public DecodedContent getContent(){
+        if(this.parent != null){
+            return this.parent.getContent();
+        }
+
+        return this.content;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public Matcher getMatcher(){
+        if(this.parent != null){
+            return this.parent.getMatcher();
+        }
+
+        return this.matcher;
+    }
+
+    /**
+     * 文脈依存のエラーメッセージを設定する。
+     * {@link #buildParseException}で利用される。
+     * 設定内容は親へ委譲されない。
+     * @param errorMessage エラーメッセージ。nullも可能。
+     */
+    protected void setContextErrorMessage(String errorMessage){
+        this.contextErrorMessage = errorMessage;
+        return;
+    }
+
+    /**
+     * 文脈状況に応じたパース例外を生成する。
+     * 例外にはリージョン開始位置が埋め込まれる。
+     * @return パース例外
+     */
+    protected HtmlParseException buildParseException(){
+        HtmlParseException result;
+        result = new HtmlParseException(this.contextErrorMessage,
+                                        regionStart() );
+        return result;
+    }
+
+    /**
+     * パースに使う正規表現パターンを切り替える。
+     * @param pattern 正規表現パターン
+     */
+    protected void switchPattern(Pattern pattern){
+        getMatcher().usePattern(pattern);
+        return;
+    }
+
+    /**
+     * 最後のマッチに成功した文字領域以前をパース対象から外す。
+     */
+    protected void shrinkRegion(){
+        int lastMatchedEnd;
+        try{
+            lastMatchedEnd = matchEnd();
+        }catch(IllegalStateException e){
+            return;
+        }
+
+        int regionEnd   = regionEnd();
+
+        getMatcher().region(lastMatchedEnd, regionEnd);
+
+        return;
+    }
+
+    /**
+     * 検査対象の一部が指定パターンにマッチするか判定する。
+     * @param pattern 指定パターン
+     * @return マッチすればtrue
+     */
+    protected boolean findProbe(Pattern pattern){
+        switchPattern(pattern);
+        if( getMatcher().find() ) return true;
+        return false;
+    }
+
+    /**
+     * 検査対象先頭が指定パターンにマッチするか判定する。
+     * @param pattern 指定パターン
+     * @return マッチすればtrue
+     */
+    protected boolean lookingAtProbe(Pattern pattern){
+        switchPattern(pattern);
+        if( getMatcher().lookingAt() ) return true;
+        return false;
+    }
+
+    /**
+     * 検査対象全体が指定パターンにマッチするか判定する。
+     * @param pattern 指定パターン
+     * @return マッチすればtrue
+     */
+    protected boolean matchesProbe(Pattern pattern){
+        switchPattern(pattern);
+        if( getMatcher().matches() ) return true;
+        return false;
+    }
+
+    /**
+     * 残りの検索対象領域からパターンがマッチする部分を探す。
+     * 見つからなければ例外をスローする。
+     * @param pattern 正規表現パターン
+     * @throws HtmlParseException マッチしなかった
+     */
+    protected void findAffirm(Pattern pattern)
+            throws HtmlParseException{
+        if( ! findProbe(pattern) ){
+            throw buildParseException();
+        }
+        return;
+    }
+
+    /**
+     * 残りの検索対象領域先頭からパターンがマッチする部分を探す。
+     * 見つからなければ例外をスローする。
+     * @param pattern 正規表現パターン
+     * @throws HtmlParseException マッチしなかった
+     */
+    protected void lookingAtAffirm(Pattern pattern)
+            throws HtmlParseException{
+        if( ! lookingAtProbe(pattern) ){
+            throw buildParseException();
+        }
+        return;
+    }
+
+    /**
+     * 残りの検索対象領域全体がパターンにマッチするか調べる。
+     * マッチしなければ例外をスローする。
+     * @param pattern 正規表現パターン
+     * @throws HtmlParseException マッチしなかった
+     */
+    protected void matchesAffirm(Pattern pattern)
+            throws HtmlParseException{
+        if( ! matchesProbe(pattern) ){
+            throw buildParseException();
+        }
+        return;
+    }
+
+    /**
+     * 最後のマッチで任意の前方参照グループがヒットしたか判定する。
+     * @param group グループ番号
+     * @return ヒットしていたらtrue
+     */
+    protected boolean isGroupMatched(int group){
+        if(matchStart(group) >= 0) return true;
+        return false;
+    }
+
+    /**
+     * 最後にマッチした前方参照グループを数値化する。
+     * 0以上の整数のみサポート。
+     * @param group グループ番号
+     * @return 数値
+     */
+    protected int parseGroupedInt(int group){
+        int result = 0;
+
+        CharSequence rawContent = getContent().getRawContent();
+        int start = matchStart(group);
+        int end   = matchEnd(group);
+        for(int pos = start; pos < end; pos++){
+            char letter = rawContent.charAt(pos);
+            int digit = Character.digit(letter, 10);
+            result = result * 10 + digit;
+        }
+
+        return result;
+    }
+
+    /**
+     * 最後にマッチした前方参照グループの開始位置を得る。
+     * @param group 前方参照識別番号
+     * @return 開始位置
+     */
+    protected int matchStart(int group){
+        return getMatcher().start(group);
+    }
+
+    /**
+     * 最後にマッチした全領域の開始位置を得る。
+     * @return 開始位置
+     */
+    protected int matchStart(){
+        return getMatcher().start();
+    }
+
+    /**
+     * 最後にマッチした前方参照グループの終了位置を得る。
+     * @param group 前方参照識別番号
+     * @return 終了位置
+     */
+    protected int matchEnd(int group){
+        return getMatcher().end(group);
+    }
+
+    /**
+     * 最後にマッチした全領域の終了位置を得る。
+     * @return 終了位置
+     */
+    protected int matchEnd(){
+        return getMatcher().end();
+    }
+
+    /**
+     * 検索領域の先頭位置を返す。
+     * @return 先頭位置
+     */
+    protected int regionStart(){
+        return getMatcher().regionStart();
+    }
+
+    /**
+     * 検索領域の末尾位置を返す。
+     * @return 末尾位置
+     */
+    protected int regionEnd(){
+        return getMatcher().regionEnd();
+    }
+
+    /**
+     * 0個以上のホワイトスペースを読み飛ばす。
+     * 具体的には検索対象領域の先頭が進むだけ。
+     */
+    protected void sweepSpace(){
+        CharSequence rawContent = getContent().getRawContent();
+
+        boolean hasSpace = false;
+        int regionStart = regionStart();
+        int regionEnd   = regionEnd();
+
+        for( ; regionStart < regionEnd; regionStart++){
+            char letter = rawContent.charAt(regionStart);
+
+            switch(letter){
+            case '\u0020':
+            case '\t':
+            case '\n':
+            case '\r':
+                hasSpace = true;
+                continue;
+            default:
+                break;
+            }
+
+            break;
+        }
+
+        if(hasSpace){
+            getMatcher().region(regionStart, regionEnd);
+        }
+
+        return;
+    }
+
+    /**
+     * 検索領域の先頭から各種役職名のマッチを試みる。
+     * @return 役職。何もマッチしなければnullを返す。
+     */
+    protected GameRole lookingAtRole(){
+        GameRole role = GameRole.lookingAtRole(getMatcher());
+        return role;
+    }
+
+}