1
2
3
4
5
6
7
8 package jp.sourceforge.jindolf.corelib;
9
10 import java.io.IOException;
11 import java.net.URI;
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;
19 import java.util.Map;
20 import java.util.Set;
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;
27
28
29
30
31 public final class LandDef{
32
33
34 public static final String IMAGE_RELPATH = "./plugin_wolf/img/";
35
36
37 public static final String DEF_FACE_URI_TMPL =
38 IMAGE_RELPATH + "face{0,number,#00}.jpg";
39
40 public static final String DEF_BODY_URI_TMPL =
41 IMAGE_RELPATH + "body{0,number,#00}.jpg";
42
43
44 public static final URI DEF_TOMBFACE_URI =
45 URI.create(IMAGE_RELPATH + "face99.jpg").normalize();
46
47 public static final URI DEF_TOMBBODY_URI =
48 URI.create(IMAGE_RELPATH + "body99.jpg").normalize();
49
50 private static final Map<String, LandState> STATE_MAP;
51
52
53 private static final String REG_POSIXBLANK = "\\p{Blank}";
54
55 private static final char HYPHEN_CH = '-';
56 private static final String HYPHEN = "-";
57 private static final String COMMA = ",";
58
59
60 static{
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);
65 }
66
67
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;
75 private URI webURI;
76 private URI cgiURI;
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;
89
90
91
92
93
94 private LandDef(){
95 super();
96 return;
97 }
98
99
100
101
102
103
104
105
106
107 private static void parseIntPair(Set<Integer> intSet, CharSequence seq)
108 throws IllegalArgumentException{
109 String token = seq.toString();
110
111 String[] ivalues = token.split(HYPHEN);
112 assert ivalues.length >= 1;
113 if(ivalues.length >= 3){
114 throw new IllegalArgumentException(token);
115 }
116
117 int ivalStart;
118 int ivalEnd;
119 try{
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);
125 }
126
127 if(ivalStart > ivalEnd){
128 int dummy = ivalStart;
129 ivalStart = ivalEnd;
130 ivalEnd = dummy;
131 assert ivalStart <= ivalEnd;
132 }
133
134 for(int ival = ivalStart; ival <= ivalEnd; ival++){
135 intSet.add(ival);
136 }
137
138 return;
139 }
140
141
142
143
144
145
146
147
148 public static SortedSet<Integer> parseIntList(CharSequence seq)
149 throws IllegalArgumentException{
150 SortedSet<Integer> result = new TreeSet<>();
151
152 if(seq.length() <= 0 ) return result;
153 String str = seq.toString();
154 str = str.replaceAll(REG_POSIXBLANK, "");
155
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);
162 }
163 parseIntPair(result, token);
164 }
165
166 return result;
167 }
168
169
170
171
172
173
174
175
176 public static List<LandDef> buildLandDefList(DocumentBuilder builder)
177 throws IOException,
178 SAXException{
179 List<Element> elemList = DomUtils.loadElemList(
180 builder, XmlResource.I_URL_LANDDEF, "landDef");
181
182 List<LandDef> result = new ArrayList<>(elemList.size());
183
184 for(Element elem : elemList){
185 LandDef landDef = buildLandDef(elem);
186 result.add(landDef);
187 }
188
189 result = Collections.unmodifiableList(result);
190
191 return result;
192 }
193
194
195
196
197
198
199 public static Locale buildLocale(CharSequence attrVal){
200 String tag = attrVal.toString();
201 Locale locale = Locale.forLanguageTag(tag);
202 return locale;
203 }
204
205
206
207
208
209
210
211 private static void fillIdInfo(LandDef result, Element elem)
212 throws SAXException{
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");
217
218 if( landName .length() <= 0
219 || landId .length() <= 0
220 || formalName.length() <= 0 ){
221 throw new SAXException("no identification info");
222 }
223
224 result.landName = landName;
225 result.landId = landId;
226 result.formalName = formalName;
227 result.landPrefix = landPrefix;
228
229 return;
230 }
231
232
233
234
235
236
237
238 private static void fillMemberInfo(LandDef result, Element elem)
239 throws SAXException{
240 String minStr = DomUtils.attrRequired(elem, "minMembers");
241 String maxStr = DomUtils.attrRequired(elem, "maxMembers");
242
243 int minMembers = Integer.parseInt(minStr);
244 int maxMembers = Integer.parseInt(maxStr);
245
246 if( minMembers <= 0
247 || minMembers > maxMembers ){
248 throw new SAXException("invalid member limitation");
249 }
250
251 result.minMembers = minMembers;
252 result.maxMembers = maxMembers;
253
254 return;
255 }
256
257
258
259
260
261
262
263 private static void fillUriInfo(LandDef result, Element elem)
264 throws SAXException{
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");
269 }
270 if( ! webURI.isAbsolute()
271 || ! cgiURI.isAbsolute() ){
272 throw new SAXException("relative URI");
273 }
274
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;
279
280 result.webURI = webURI;
281 result.cgiURI = cgiURI;
282 result.tombFaceIconURI = tombFaceIconURI;
283 result.tombBodyIconURI = tombBodyIconURI;
284
285 return;
286 }
287
288
289
290
291
292
293
294 private static void fillTemplateInfo(LandDef result, Element elem)
295 throws SAXException{
296 String faceURITemplate;
297 String bodyURITemplate;
298
299 faceURITemplate = DomUtils.attrValue(elem, "faceIconURITemplate");
300 bodyURITemplate = DomUtils.attrValue(elem, "bodyIconURITemplate");
301
302 if(faceURITemplate == null) faceURITemplate = DEF_FACE_URI_TMPL;
303 if(bodyURITemplate == null) bodyURITemplate = DEF_BODY_URI_TMPL;
304
305 result.faceURITemplate = faceURITemplate;
306 result.bodyURITemplate = bodyURITemplate;
307
308 return;
309 }
310
311
312
313
314
315
316
317 private static void fillI18NInfo(LandDef result, Element elem)
318 throws SAXException{
319 String localeText = DomUtils.attrRequired(elem, "locale");
320 String encodingText = DomUtils.attrRequired(elem, "encoding");
321 String timeZoneText = DomUtils.attrRequired(elem, "timeZone");
322
323 Locale locale = buildLocale(localeText);
324 Charset encoding = Charset.forName(encodingText);
325 TimeZone timeZone = TimeZone.getTimeZone(timeZoneText);
326
327 result.locale = locale;
328 result.encoding = encoding;
329 result.timeZone = timeZone;
330
331 return;
332 }
333
334
335
336
337
338
339
340 private static void fillDateInfo(LandDef result, Element elem)
341 throws SAXException{
342 long startDateTime;
343 long endDateTime;
344
345 String startDateText = DomUtils.attrRequired(elem, "startDate");
346 String endDateText = elem.getAttribute("endDate");
347
348 startDateTime = DateUtils.parseISO8601(startDateText);
349
350 if(endDateText.length() > 0){
351 endDateTime = DateUtils.parseISO8601(endDateText);
352 }else{
353 endDateTime = -1;
354 }
355
356 if(startDateTime < 0){
357 throw new SAXException("illegal start date " + startDateText);
358 }
359
360 if(endDateTime >= 0 && startDateTime > endDateTime){
361 throw new SAXException("start date is too old " + startDateText);
362 }
363
364 result.startDateTime = startDateTime;
365 result.endDateTime = endDateTime;
366
367 return;
368 }
369
370
371
372
373
374
375
376 private static void fillLandInfo(LandDef result, Element elem)
377 throws SAXException{
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);
382 }
383
384 String description = DomUtils.attrRequired(elem, "description");
385 String contactInfo = DomUtils.attrRequired(elem, "contactInfo");
386
387 String invalidVid = elem.getAttribute("invalidVid");
388 SortedSet<Integer> invalidSet = parseIntList(invalidVid);
389 int[] invalidArray = new int[invalidSet.size()];
390 int pos = 0;
391 for(int vid : invalidSet){
392 invalidArray[pos++] = vid;
393 }
394
395 result.landState = landState;
396 result.description = description;
397 result.contactInfo = contactInfo;
398 result.invalidVid = invalidArray;
399
400 return;
401 }
402
403
404
405
406
407
408
409 private static LandDef buildLandDef(Element elem)
410 throws SAXException{
411 LandDef result = new LandDef();
412
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);
420
421 return result;
422 }
423
424
425
426
427
428
429 public String getLandName(){
430 return this.landName;
431 }
432
433
434
435
436
437 public String getLandId(){
438 return this.landId;
439 }
440
441
442
443
444
445 public String getFormalName(){
446 return this.formalName;
447 }
448
449
450
451
452
453
454 public String getLandPrefix(){
455 return this.landPrefix;
456 }
457
458
459
460
461
462 public LandState getLandState(){
463 return this.landState;
464 }
465
466
467
468
469
470 public int getMinMembers(){
471 return this.minMembers;
472 }
473
474
475
476
477
478 public int getMaxMembers(){
479 return this.maxMembers;
480 }
481
482
483
484
485
486 public URI getWebURI(){
487 return this.webURI;
488 }
489
490
491
492
493
494 public URI getCgiURI(){
495 return this.cgiURI;
496 }
497
498
499
500
501
502 public URI getTombFaceIconURI(){
503 return this.tombFaceIconURI;
504 }
505
506
507
508
509
510 public URI getTombBodyIconURI(){
511 return this.tombBodyIconURI;
512 }
513
514
515
516
517
518 public String getFaceURITemplate(){
519 return this.faceURITemplate;
520 }
521
522
523
524
525
526 public String getBodyURITemplate(){
527 return this.bodyURITemplate;
528 }
529
530
531
532
533
534 public Locale getLocale(){
535 return this.locale;
536 }
537
538
539
540
541
542 public Charset getEncoding(){
543 return this.encoding;
544 }
545
546
547
548
549
550 public TimeZone getTimeZone(){
551 Object copy = this.timeZone.clone();
552 TimeZone result = (TimeZone) copy;
553 return result;
554 }
555
556
557
558
559
560 public long getStartDateTime(){
561 return this.startDateTime;
562 }
563
564
565
566
567
568 public long getEndDateTime(){
569 return this.endDateTime;
570 }
571
572
573
574
575
576 public String getDescription(){
577 return this.description;
578 }
579
580
581
582
583
584 public String getContactInfo(){
585 return this.contactInfo;
586 }
587
588
589
590
591
592
593 public boolean isValidVillageId(int vid){
594 int pos = Arrays.binarySearch(this.invalidVid, vid);
595 if(pos >= 0) return false;
596 return true;
597 }
598
599 }