2 * land information definition
4 * License : The MIT License
5 * Copyright(c) 2009 olyutorskii
8 package jp.sourceforge.jindolf.corelib;
10 import java.io.IOException;
12 import java.nio.charset.Charset;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Locale;
21 import java.util.SortedSet;
22 import java.util.TimeZone;
23 import java.util.TreeSet;
24 import javax.xml.parsers.DocumentBuilder;
25 import org.w3c.dom.Element;
26 import org.xml.sax.SAXException;
31 public final class LandDef{
33 /** 各種イメージの相対なベースURI。 */
34 public static final String IMAGE_RELPATH = "./plugin_wolf/img/";
36 /** 顔アイコンURIのテンプレート。 */
37 public static final String DEF_FACE_URI_TMPL =
38 IMAGE_RELPATH + "face{0,number,#00}.jpg";
39 /** デカキャラURIのテンプレート。 */
40 public static final String DEF_BODY_URI_TMPL =
41 IMAGE_RELPATH + "body{0,number,#00}.jpg";
43 /** 墓小アイコンのデフォルト相対URI。 */
44 public static final URI DEF_TOMBFACE_URI =
45 URI.create(IMAGE_RELPATH + "face99.jpg").normalize();
46 /** 墓大アイコンのデフォルト相対URI。 */
47 public static final URI DEF_TOMBBODY_URI =
48 URI.create(IMAGE_RELPATH + "body99.jpg").normalize();
50 private static final Map<String, LandState> STATE_MAP;
53 private static final String REG_POSIXBLANK = "\\p{Blank}";
55 private static final char HYPHEN_CH = '-';
56 private static final String HYPHEN = "-";
57 private static final String COMMA = ",";
61 STATE_MAP = new HashMap<>();
62 STATE_MAP.put("closed", LandState.CLOSED);
63 STATE_MAP.put("historical", LandState.HISTORICAL);
64 STATE_MAP.put("active", LandState.ACTIVE);
68 private String landName;
69 private String landId;
70 private String formalName;
71 private String landPrefix;
72 private LandState landState;
73 private int minMembers;
74 private int maxMembers;
77 private URI tombFaceIconURI;
78 private URI tombBodyIconURI;
79 private String faceURITemplate;
80 private String bodyURITemplate;
81 private Locale locale;
82 private Charset encoding;
83 private TimeZone timeZone;
84 private long startDateTime;
85 private long endDateTime;
86 private String description;
87 private String contactInfo;
88 private int[] invalidVid;
101 * ハイフンで区切られた整数範囲をパースする。
102 * 「1-3」なら1,2,3を結果に格納する。
103 * @param intSet 格納先Set
105 * @throws IllegalArgumentException 形式が変
107 private static void parseIntPair(Set<Integer> intSet, CharSequence seq)
108 throws IllegalArgumentException{
109 String token = seq.toString();
111 String[] ivalues = token.split(HYPHEN);
112 assert ivalues.length >= 1;
113 if(ivalues.length >= 3){
114 throw new IllegalArgumentException(token);
120 ivalStart = Integer.parseInt(ivalues[0]);
121 if(ivalues.length >= 2) ivalEnd = Integer.parseInt(ivalues[1]);
122 else ivalEnd = ivalStart;
123 }catch(NumberFormatException e){
124 throw new IllegalArgumentException(token, e);
127 if(ivalStart > ivalEnd){
128 int dummy = ivalStart;
131 assert ivalStart <= ivalEnd;
134 for(int ival = ivalStart; ival <= ivalEnd; ival++){
142 * コンマとハイフンで区切られた整数の羅列をパースする。
143 * 「10,23-25」なら10,23,24,25を結果に返す。
144 * @param seq パース対象文字列
145 * @return ソートされたIntegerのList
146 * @throws IllegalArgumentException 形式が変。
148 public static SortedSet<Integer> parseIntList(CharSequence seq)
149 throws IllegalArgumentException{
150 SortedSet<Integer> result = new TreeSet<>();
152 if(seq.length() <= 0 ) return result;
153 String str = seq.toString();
154 str = str.replaceAll(REG_POSIXBLANK, "");
156 String[] tokens = str.split(COMMA);
157 assert tokens.length >= 1;
158 for(String token : tokens){
159 if(token.length() <= 0) continue;
160 if(token.charAt(0) == HYPHEN_CH || token.endsWith(HYPHEN)){
161 throw new IllegalArgumentException(token);
163 parseIntPair(result, token);
171 * @param builder DOMビルダ
172 * @return List 国設定リスト
173 * @throws IOException IOエラー
174 * @throws SAXException パースエラー
176 public static List<LandDef> buildLandDefList(DocumentBuilder builder)
179 List<Element> elemList = DomUtils.loadElemList(
180 builder, XmlResource.I_URL_LANDDEF, "landDef");
182 List<LandDef> result = new ArrayList<>(elemList.size());
184 for(Element elem : elemList){
185 LandDef landDef = buildLandDef(elem);
189 result = Collections.unmodifiableList(result);
195 * ハイフンをデリミタに持つロケール指定文字列からLocaleを生成する。
196 * @param attrVal ロケール指定文字列
199 public static Locale buildLocale(CharSequence attrVal){
200 String tag = attrVal.toString();
201 Locale locale = Locale.forLanguageTag(tag);
206 * XML属性を使って国定義の識別子情報を埋める。
208 * @param elem 個別のXML国定義要素
209 * @throws SAXException XML属性の記述に関する異常系
211 private static void fillIdInfo(LandDef result, Element elem)
213 String landName = DomUtils.attrRequired(elem, "landName");
214 String landId = DomUtils.attrRequired(elem, "landId");
215 String formalName = DomUtils.attrRequired(elem, "formalName");
216 String landPrefix = DomUtils.attrRequired(elem, "landPrefix");
218 if( landName .length() <= 0
219 || landId .length() <= 0
220 || formalName.length() <= 0 ){
221 throw new SAXException("no identification info");
224 result.landName = landName;
225 result.landId = landId;
226 result.formalName = formalName;
227 result.landPrefix = landPrefix;
233 * XML属性を使って国定義の定員情報を埋める。
235 * @param elem 個別のXML国定義要素
236 * @throws SAXException XML属性の記述に関する異常系
238 private static void fillMemberInfo(LandDef result, Element elem)
240 String minStr = DomUtils.attrRequired(elem, "minMembers");
241 String maxStr = DomUtils.attrRequired(elem, "maxMembers");
243 int minMembers = Integer.parseInt(minStr);
244 int maxMembers = Integer.parseInt(maxStr);
247 || minMembers > maxMembers ){
248 throw new SAXException("invalid member limitation");
251 result.minMembers = minMembers;
252 result.maxMembers = maxMembers;
258 * XML属性を使って国定義のURI情報を埋める。
260 * @param elem 個別のXML国定義要素
261 * @throws SAXException XML属性の記述に関する異常系
263 private static void fillUriInfo(LandDef result, Element elem)
265 URI webURI = DomUtils.attrToUri(elem, "webURI");
266 URI cgiURI = DomUtils.attrToUri(elem, "cgiURI");
267 if(webURI == null || cgiURI == null){
268 throw new SAXException("no URI");
270 if( ! webURI.isAbsolute()
271 || ! cgiURI.isAbsolute() ){
272 throw new SAXException("relative URI");
275 URI tombFaceIconURI = DomUtils.attrToUri(elem, "tombFaceIconURI");
276 URI tombBodyIconURI = DomUtils.attrToUri(elem, "tombBodyIconURI");
277 if(tombFaceIconURI == null) tombFaceIconURI = DEF_TOMBFACE_URI;
278 if(tombBodyIconURI == null) tombBodyIconURI = DEF_TOMBBODY_URI;
280 result.webURI = webURI;
281 result.cgiURI = cgiURI;
282 result.tombFaceIconURI = tombFaceIconURI;
283 result.tombBodyIconURI = tombBodyIconURI;
289 * XML属性を使って国定義のURIテンプレート情報を埋める。
291 * @param elem 個別のXML国定義要素
292 * @throws SAXException XML属性の記述に関する異常系
294 private static void fillTemplateInfo(LandDef result, Element elem)
296 String faceURITemplate;
297 String bodyURITemplate;
299 faceURITemplate = DomUtils.attrValue(elem, "faceIconURITemplate");
300 bodyURITemplate = DomUtils.attrValue(elem, "bodyIconURITemplate");
302 if(faceURITemplate == null) faceURITemplate = DEF_FACE_URI_TMPL;
303 if(bodyURITemplate == null) bodyURITemplate = DEF_BODY_URI_TMPL;
305 result.faceURITemplate = faceURITemplate;
306 result.bodyURITemplate = bodyURITemplate;
312 * XML属性を使って国定義の国際化情報を埋める。
314 * @param elem 個別のXML国定義要素
315 * @throws SAXException XML属性の記述に関する異常系
317 private static void fillI18NInfo(LandDef result, Element elem)
319 String localeText = DomUtils.attrRequired(elem, "locale");
320 String encodingText = DomUtils.attrRequired(elem, "encoding");
321 String timeZoneText = DomUtils.attrRequired(elem, "timeZone");
323 Locale locale = buildLocale(localeText);
324 Charset encoding = Charset.forName(encodingText);
325 TimeZone timeZone = TimeZone.getTimeZone(timeZoneText);
327 result.locale = locale;
328 result.encoding = encoding;
329 result.timeZone = timeZone;
335 * XML属性を使って国定義の日付情報を埋める。
337 * @param elem 個別のXML国定義要素
338 * @throws SAXException XML属性の記述に関する異常系
340 private static void fillDateInfo(LandDef result, Element elem)
345 String startDateText = DomUtils.attrRequired(elem, "startDate");
346 String endDateText = elem.getAttribute("endDate");
348 startDateTime = DateUtils.parseISO8601(startDateText);
350 if(endDateText.length() > 0){
351 endDateTime = DateUtils.parseISO8601(endDateText);
356 if(startDateTime < 0){
357 throw new SAXException("illegal start date " + startDateText);
360 if(endDateTime >= 0 && startDateTime > endDateTime){
361 throw new SAXException("start date is too old " + startDateText);
364 result.startDateTime = startDateTime;
365 result.endDateTime = endDateTime;
371 * XML属性を使って国定義の各種ステータス情報を埋める。
373 * @param elem 個別のXML国定義要素
374 * @throws SAXException XML属性の記述に関する異常系
376 private static void fillLandInfo(LandDef result, Element elem)
378 String state = DomUtils.attrRequired(elem, "landState");
379 LandState landState = STATE_MAP.get(state);
380 if(landState == null){
381 throw new SAXException("illegal land status " + state);
384 String description = DomUtils.attrRequired(elem, "description");
385 String contactInfo = DomUtils.attrRequired(elem, "contactInfo");
387 String invalidVid = elem.getAttribute("invalidVid");
388 SortedSet<Integer> invalidSet = parseIntList(invalidVid);
389 int[] invalidArray = new int[invalidSet.size()];
391 for(int vid : invalidSet){
392 invalidArray[pos++] = vid;
395 result.landState = landState;
396 result.description = description;
397 result.contactInfo = contactInfo;
398 result.invalidVid = invalidArray;
404 * 個々の国設定をオブジェクトに変換する。
407 * @throws SAXException パースエラー
409 private static LandDef buildLandDef(Element elem)
411 LandDef result = new LandDef();
413 fillLandInfo (result, elem);
414 fillIdInfo (result, elem);
415 fillMemberInfo (result, elem);
416 fillUriInfo (result, elem);
417 fillTemplateInfo(result, elem);
418 fillI18NInfo (result, elem);
419 fillDateInfo (result, elem);
429 public String getLandName(){
430 return this.landName;
437 public String getLandId(){
445 public String getFormalName(){
446 return this.formalName;
454 public String getLandPrefix(){
455 return this.landPrefix;
462 public LandState getLandState(){
463 return this.landState;
470 public int getMinMembers(){
471 return this.minMembers;
478 public int getMaxMembers(){
479 return this.maxMembers;
483 * Webアクセス用の入り口URIを得る。
486 public URI getWebURI(){
491 * クエリーを投げるCGIのURIを得る。
494 public URI getCgiURI(){
502 public URI getTombFaceIconURI(){
503 return this.tombFaceIconURI;
510 public URI getTombBodyIconURI(){
511 return this.tombBodyIconURI;
515 * 顔アイコンURIのテンプレートを得る。
516 * @return Formatter用テンプレート
518 public String getFaceURITemplate(){
519 return this.faceURITemplate;
523 * 全身像アイコンURIのテンプレートを得る。
524 * @return Formatter用テンプレート
526 public String getBodyURITemplate(){
527 return this.bodyURITemplate;
534 public Locale getLocale(){
542 public Charset getEncoding(){
543 return this.encoding;
547 * この国の時刻表記で使うタイムゾーンのコピーを得る。
550 public TimeZone getTimeZone(){
551 Object copy = this.timeZone.clone();
552 TimeZone result = (TimeZone) copy;
558 * @return 始まった時刻(エポックミリ秒)。
560 public long getStartDateTime(){
561 return this.startDateTime;
566 * @return 打ち切った時刻(エポックミリ秒)。まだ打ち切っていない場合は負。
568 public long getEndDateTime(){
569 return this.endDateTime;
576 public String getDescription(){
577 return this.description;
584 public String getContactInfo(){
585 return this.contactInfo;
591 * @return 無効な村ならfalse
593 public boolean isValidVillageId(int vid){
594 int pos = Arrays.binarySearch(this.invalidVid, vid);
595 if(pos >= 0) return false;