import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
-import jp.osdn.jindolf.parser.HtmlAdapter;
-import jp.osdn.jindolf.parser.HtmlParseException;
-import jp.osdn.jindolf.parser.HtmlParser;
-import jp.osdn.jindolf.parser.PageType;
-import jp.osdn.jindolf.parser.SeqRange;
-import jp.osdn.jindolf.parser.content.DecodedContent;
-import jp.sfjp.jindolf.net.HtmlSequence;
import jp.sfjp.jindolf.net.ServerAccess;
import jp.sourceforge.jindolf.corelib.LandDef;
-import jp.sourceforge.jindolf.corelib.LandState;
-import jp.sourceforge.jindolf.corelib.VillageState;
/**
* いわゆる「国」。
*/
public class Land {
- // 古国ID
- private static final String ID_VANILLAWOLF = "wolf";
-
private static final Logger LOGGER = Logger.getAnonymousLogger();
private final LandDef landDef;
private final ServerAccess serverAccess;
- private final HtmlParser parser = new HtmlParser();
- private final VillageListHandler handler = new VillageListHandler();
private final List<Village> villageList = new LinkedList<>();
}
this.serverAccess = new ServerAccess(url, this.landDef.getEncoding());
- this.parser.setBasicHandler(this.handler);
-
return;
}
/**
- * クエリー文字列から特定キーの値を得る。
- * クエリーの書式例:「{@literal a=b&c=d&e=f}」この場合キーcの値はd
- * @param key キー
- * @param allQuery クエリー
- * @return 値
- */
- public static String getValueFromCGIQueries(String key,
- String allQuery){
- String result = null;
-
- String[] queries = allQuery.split("\\Q&\\E");
-
- for(String pair : queries){
- if(pair == null) continue;
- String[] namevalue = pair.split("\\Q=\\E");
- if(namevalue == null) continue;
- if(namevalue.length != 2) continue;
- String name = namevalue[0];
- String value = namevalue[1];
- if(name == null) continue;
- if( name.equals(key) ){
- result = value;
- if(result == null) continue;
- if(result.length() <= 0) continue;
- break;
- }
- }
-
- return result;
- }
-
- /**
- * AタグのHREF属性値からクエリー部を抽出する。
- * 「{@literal &}」は「{@literal &}」に解釈される。
- * @param hrefValue HREF属性値
- * @return クエリー文字列
- */
- public static String getRawQueryFromHREF(CharSequence hrefValue){
- if(hrefValue == null) return null;
-
- // HTML 4.01 B.2.2 rule
- String pureHREF = hrefValue.toString().replace("&", "&");
-
- URI uri;
- try{
- uri = new URI(pureHREF);
- }catch(URISyntaxException e){
- LOGGER.warning(
- "不正なURI["
- + hrefValue
- + "]を検出しました");
- return null;
- }
-
- String rawQuery = uri.getRawQuery();
-
- return rawQuery;
- }
-
- /**
- * AタグのHREF属性値から村IDを得る。
- * @param hrefValue HREF値
- * @return village 村ID
- */
- public static String getVillageIDFromHREF(CharSequence hrefValue){
- String rawQuery = getRawQueryFromHREF(hrefValue);
- if(rawQuery == null) return null;
-
- String villageID = getValueFromCGIQueries("vid", rawQuery);
- if(villageID == null) return null;
- if(villageID.length() <= 0) return null;
-
- return villageID;
- }
-
- /**
* 国定義を得る。
* @return 国定義
*/
}
/**
- * 村一覧情報をダウンロードする。
- * リスト元情報は国のトップページと村一覧ページ。
- * 古国の場合は村一覧にアクセスせずトップページのみ。
- * 古国以外で村建てをやめた国はトップページにアクセスしない。
- * 村リストはVillageの実装に従いソートされる。重複する村は排除。
- *
- * @return ソートされた村一覧
- * @throws java.io.IOException ネットワーク入出力の異常
- */
- public SortedSet<Village> downloadVillageList() throws IOException {
- LandDef thisLand = getLandDef();
- LandState state = thisLand.getLandState();
- boolean isVanillaWolf = thisLand.getLandId().equals(ID_VANILLAWOLF);
-
- ServerAccess server = getServerAccess();
-
- // たまに同じ村が複数回出現するので注意!
- SortedSet<Village> result = new TreeSet<>();
-
- // トップページ
- if(state.equals(LandState.ACTIVE) || isVanillaWolf){
- HtmlSequence html = server.getHTMLTopPage();
- DecodedContent content = html.getContent();
- try{
- this.parser.parseAutomatic(content);
- }catch(HtmlParseException e){
- LOGGER.log(Level.WARNING, "トップページを認識できない", e);
- }
- List<Village> list = this.handler.getVillageList();
- if(list != null){
- result.addAll(list);
- }
- }
-
- // 村一覧ページ
- if( ! isVanillaWolf ){
- HtmlSequence html = server.getHTMLLandList();
- DecodedContent content = html.getContent();
- try{
- this.parser.parseAutomatic(content);
- }catch(HtmlParseException e){
- LOGGER.log(Level.WARNING, "村一覧ページを認識できない", e);
- }
- List<Village> list = this.handler.getVillageList();
- if(list != null){
- result.addAll(list);
- }
- }
-
- this.parser.reset();
- this.handler.reset();
-
- return result;
- }
-
- /**
* 村リストを更新する。
* @param vset ソート済みの村一覧
*/
- public void updateVillageList(SortedSet<Village> vset){
+ public void updateVillageList(List<Village> vset){
// TODO 村リスト更新のイベントリスナがあると便利か?
this.villageList.clear();
this.villageList.addAll(vset);
return getLandDef().getLandName();
}
- /**
- * 村一覧取得用ハンドラ。
- */
- private class VillageListHandler extends HtmlAdapter{
-
- private List<Village> villageList = null;
-
- /**
- * コンストラクタ。
- */
- public VillageListHandler(){
- super();
- return;
- }
-
- /**
- * 村一覧を返す。
- * 再度パースを行うまで呼んではいけない。
- * @return 村一覧
- * @throws IllegalStateException パース前に呼び出された。
- * あるいはパース後すでにリセットされている。
- */
- public List<Village> getVillageList() throws IllegalStateException{
- if(this.villageList == null){
- throw new IllegalStateException("パースが必要です。");
- }
-
- List<Village> result = this.villageList;
-
- return result;
- }
-
- /**
- * リセットを行う。
- * 村一覧は空になる。
- */
- public void reset(){
- this.villageList = null;
- return;
- }
-
- /**
- * {@inheritDoc}
- * 村一覧リストが初期化される。
- * @param content {@inheritDoc}
- * @throws HtmlParseException {@inheritDoc}
- */
- @Override
- public void startParse(DecodedContent content)
- throws HtmlParseException{
- reset();
- this.villageList = new LinkedList<>();
- return;
- }
-
- /**
- * {@inheritDoc}
- * 自動判定の結果がトップページでも村一覧ページでもなければ
- * 例外を投げる。
- * @param type {@inheritDoc}
- * @throws HtmlParseException {@inheritDoc} 意図しないページが来た。
- */
- @Override
- public void pageType(PageType type) throws HtmlParseException{
- if( type != PageType.VILLAGELIST_PAGE
- && type != PageType.TOP_PAGE ){
- throw new HtmlParseException(
- "トップページか村一覧ページが必要です。");
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param content {@inheritDoc}
- * @param anchorRange {@inheritDoc}
- * @param villageRange {@inheritDoc}
- * @param hour {@inheritDoc}
- * @param minute {@inheritDoc}
- * @param villageState {@inheritDoc}
- * @throws HtmlParseException {@inheritDoc}
- */
- @Override
- public void villageRecord(DecodedContent content,
- SeqRange anchorRange,
- SeqRange villageRange,
- int hour, int minute,
- VillageState villageState)
- throws HtmlParseException{
- LandDef landdef = getLandDef();
- LandState landState = landdef.getLandState();
-
- CharSequence href = anchorRange.sliceSequence(content);
- String villageID = getVillageIDFromHREF(href);
- if( villageID == null
- || villageID.length() <= 0 ){
- LOGGER.warning(
- "認識できないURL[" + href + "]に遭遇しました。");
- return;
- }
-
- CharSequence fullVillageName =
- villageRange.sliceSequence(content);
-
- // TODO 既に出来ているかもしれないVillageを再度作るのは無駄?
- Village village = new Village(Land.this,
- villageID,
- fullVillageName.toString() );
-
- if(landState == LandState.HISTORICAL){
- village.setState(VillageState.GAMEOVER);
- }else{
- village.setState(villageState);
- }
-
- this.villageList.add(village);
-
- return;
- }
-
- }
-
}