X-Git-Url: http://git.osdn.net/view?p=jindolf%2FJindolf.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fjp%2Fsfjp%2Fjindolf%2Fdata%2Fhtml%2FVillageInfoHandler.java;fp=src%2Fmain%2Fjava%2Fjp%2Fsfjp%2Fjindolf%2Fdata%2Fhtml%2FVillageInfoHandler.java;h=984b5c3b608f6014d437d6ca88b24aaacd5cc0a2;hp=0000000000000000000000000000000000000000;hb=cde32116e69984f1c22380b8ecae9a4d28479adb;hpb=d12d773cc29e5ca5efbb5ad879e9e5b373e4c67d diff --git a/src/main/java/jp/sfjp/jindolf/data/html/VillageInfoHandler.java b/src/main/java/jp/sfjp/jindolf/data/html/VillageInfoHandler.java new file mode 100644 index 0000000..984b5c3 --- /dev/null +++ b/src/main/java/jp/sfjp/jindolf/data/html/VillageInfoHandler.java @@ -0,0 +1,290 @@ +/* + * village info handler + * + * License : The MIT License + * Copyright(c) 2020 olyutorskii + */ + +package jp.sfjp.jindolf.data.html; + +import java.util.logging.Logger; +import jp.osdn.jindolf.parser.HtmlAdapter; +import jp.osdn.jindolf.parser.HtmlParseException; +import jp.osdn.jindolf.parser.PageType; +import jp.osdn.jindolf.parser.SeqRange; +import jp.osdn.jindolf.parser.content.DecodedContent; +import jp.sfjp.jindolf.data.Land; +import jp.sfjp.jindolf.data.Period; +import jp.sfjp.jindolf.data.Village; +import jp.sourceforge.jindolf.corelib.LandDef; +import jp.sourceforge.jindolf.corelib.LandState; +import jp.sourceforge.jindolf.corelib.PeriodType; +import jp.sourceforge.jindolf.corelib.VillageState; + + +/** + * 各村のHTMLをパースし、村情報や日程の通知を受け取るためのハンドラ。 + * + *

パース終了時には、 + * あらかじめ指定したVillageインスタンスに + * 更新時刻などの村情報が適切に更新される。 + * + *

日程は空Periodのリストに反映されるが各Periodのロードはまだ行われない。 + * + *

※人狼BBS:G国におけるG2087村のエピローグが終了した段階で、 + * 人狼BBSは過去ログの提供しか行っていない。 + * だがこのクラスには進行中の村をパースするための冗長な処理が若干残っている。 + */ +class VillageInfoHandler extends HtmlAdapter { + + private static final Logger LOGGER = Logger.getAnonymousLogger(); + + + private Village village = null; + + private boolean hasPrologue; + private boolean hasProgress; + private boolean hasEpilogue; + + private boolean hasDone; + private int maxProgress; + + + /** + * コンストラクタ。 + */ + VillageInfoHandler(){ + super(); + return; + } + + + /** + * 更新対象の村インスタンスを設定する。 + * + * @param village 村インスタンス + */ + void setVillage(Village village){ + this.village = village; + reset(); + return; + } + + /** + * 各種進行コンテキストのリセットを行う。 + */ + void reset() { + this.hasPrologue = false; + this.hasProgress = false; + this.hasEpilogue = false; + this.hasDone = false; + this.maxProgress = 0; + return; + } + + /** + * パース結果から村の状態を算出する。 + * + * @return 村の状態 + */ + private VillageState getVillageState() { + VillageState result = VillageState.UNKNOWN; + + if(this.hasDone){ + result = VillageState.GAMEOVER; + }else if(this.hasEpilogue){ + result = VillageState.EPILOGUE; + }else if(this.hasProgress){ + result = VillageState.PROGRESS; + }else if(this.hasPrologue){ + result = VillageState.PROLOGUE; + } + + return result; + } + + /** + * {@inheritDoc} + * + * @param content {@inheritDoc} + * @throws HtmlParseException {@inheritDoc} + */ + @Override + public void startParse(DecodedContent content) + throws HtmlParseException { + reset(); + return; + } + + /** + * {@inheritDoc} + * + *

HTML自動判定の結果が村の日程ページでなければ例外を投げ、 + * パースを中止する。 + * + * @param type {@inheritDoc} + * @throws HtmlParseException {@inheritDoc} 意図しないページが来た。 + */ + @Override + public void pageType(PageType type) throws HtmlParseException { + if(type != PageType.PERIOD_PAGE){ + throw new HtmlParseException("日ページが必要です。"); + } + return; + } + + /** + * {@inheritDoc} + * + *

更新時刻の通知を受け取る。 + * 更新時刻はVillageインスタンスへ反映される。 + * + * @param month {@inheritDoc} + * @param day {@inheritDoc} + * @param hour {@inheritDoc} + * @param minute {@inheritDoc} + * @throws HtmlParseException {@inheritDoc} + */ + @Override + public void commitTime(int month, int day, int hour, int minute) + throws HtmlParseException { + this.village.setLimit(month, day, hour, minute); + return; + } + + /** + * {@inheritDoc} + * + *

日程ページから各Period(日)へのリンクHTML出現の通知を受け取る。 + * Villageインスタンスの進行状況へ反映される。 + * + * @param content {@inheritDoc} + * @param anchorRange {@inheritDoc} + * @param periodType {@inheritDoc} + * @param day {@inheritDoc} + * @throws HtmlParseException {@inheritDoc} + */ + @Override + public void periodLink(DecodedContent content, + SeqRange anchorRange, + PeriodType periodType, + int day) + throws HtmlParseException { + if(periodType == null){ + this.hasDone = true; + return; + } + + switch(periodType){ + case PROLOGUE: + this.hasPrologue = true; + break; + case PROGRESS: + this.hasProgress = true; + this.maxProgress = day; + break; + case EPILOGUE: + this.hasEpilogue = true; + break; + default: + assert false; + break; + } + + return; + } + + /** + * {@inheritDoc} + * + *

パース終了時の処理を行う。 + * + *

村としての体裁に矛盾が検出されると、 + * 例外を投げパースを中断する。 + * + *

村の進行に従い空Periodのリストを生成する。 + * + * @throws HtmlParseException {@inheritDoc} + */ + @Override + public void endParse() throws HtmlParseException { + VillageState villageState = getVillageState(); + if(villageState == VillageState.UNKNOWN){ + this.village.setState(villageState); + LOGGER.warning("村の状況を読み取れません"); + return; + } + + Land land = this.village.getParentLand(); + LandDef landDef = land.getLandDef(); + LandState landState = landDef.getLandState(); + + if(landState != LandState.ACTIVE){ + villageState = VillageState.GAMEOVER; + } + this.village.setState(villageState); + + modifyPeriodList(); + + return; + } + + /** + * 抽出したPeriod別リンク情報に伴い空Periodリストを準備する。 + * + *

まだPeriodデータのロードは行われない。 + * + *

ゲーム進行中の村で更新時刻をまたいで更新が行われた場合、 + * 既存のPeriodリストが伸張する場合がある。 + */ + private void modifyPeriodList() { + Period lastPeriod = null; + if(this.hasPrologue){ + Period prologue = this.village.getPrologue(); + if(prologue == null){ + lastPeriod = + new Period(this.village, + PeriodType.PROLOGUE, + 0 ); + this.village.setPeriod(0, lastPeriod); + }else{ + lastPeriod = prologue; + } + } + + if(this.hasProgress){ + for(int day = 1; day <= this.maxProgress; day++){ + Period progress = this.village.getProgress(day); + if(progress == null){ + lastPeriod = + new Period(this.village, + PeriodType.PROGRESS, + day ); + this.village.setPeriod(day, lastPeriod); + }else{ + lastPeriod = progress; + } + } + } + + if(this.hasEpilogue){ + Period epilogue = this.village.getEpilogue(); + if(epilogue == null){ + int epilogueDay = this.maxProgress + 1; + lastPeriod = + new Period(this.village, + PeriodType.EPILOGUE, + epilogueDay ); + this.village.setPeriod(epilogueDay, lastPeriod); + } else { + lastPeriod = epilogue; + } + } + + assert this.village.getPeriodSize() > 0; + assert lastPeriod != null; + + return; + } + +}