4 * License : The MIT License
5 * Copyright(c) 2009 olyutorskii
8 package jp.sourceforge.jindolf;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.nio.ByteBuffer;
13 import java.nio.CharBuffer;
14 import java.nio.charset.CharacterCodingException;
15 import java.nio.charset.Charset;
16 import java.nio.charset.CharsetEncoder;
17 import java.util.Collections;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Locale;
21 import java.util.Properties;
23 import java.util.SortedSet;
24 import java.util.TreeSet;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 import jp.sourceforge.jindolf.corelib.Destiny;
28 import jp.sourceforge.jindolf.corelib.GameRole;
31 * まちゅ氏運営のまとめサイト(wolfbbs)に関する諸々。
33 * @see <a href="http://wolfbbs.jp/">まとめサイト</a>
34 * @see <a href="http://pukiwiki.sourceforge.jp/">PukiWiki</a>
36 public final class WolfBBS{
39 public static final String COMMENTLINE;
41 private static final String WIKICHAR = "#&[]()<>+-*:|~/,'%?";
42 private static final Pattern WIKINAME_PATTERN =
43 Pattern.compile("[A-Z][a-z]+([A-Z])[a-z]+");
45 private static final String FACEICONSET =
46 "resources/faceIconSet.properties";
47 private static final String ORDER_PREFIX = "iconset.order.";
48 private static final List<FaceIconSet> FACEICONSET_LIST =
49 new LinkedList<FaceIconSet>();
51 private static final Charset CHARSET_EUC = Charset.forName("EUC-JP");
53 private static final String WOLFBBS_URL = "http://wolfbbs.jp/";
58 StringBuilder wikicomment = new StringBuilder();
59 wikicomment.append("// ");
60 while(wikicomment.length() < 72){
61 wikicomment.append('=');
63 wikicomment.append('\n');
64 COMMENTLINE = wikicomment.toString();
73 throw new AssertionError();
80 private static void loadFaceIconSet(){
81 InputStream is = Jindolf.getResourceAsStream(FACEICONSET);
82 Properties properties = new Properties();
86 }catch(IOException e){
87 Jindolf.logger().fatal(
88 "顔アイコンセットの読み込みに失敗しました", e);
92 loadFaceIconSet(properties);
99 * @param properties プロパティ
101 private static void loadFaceIconSet(Properties properties){
102 String codeCheck = properties.getProperty("codeCheck");
103 if( codeCheck == null
104 || codeCheck.length() != 1
105 || codeCheck.charAt(0) != '\u72fc'){ // 「狼」
106 Jindolf.logger().fatal(
109 +"native2ascii は正しく適用しましたか?");
114 Set<Object> keySet = properties.keySet();
116 SortedSet<Integer> orderSet = new TreeSet<Integer>();
117 for(Object keyObj : keySet){
118 if(keyObj == null) continue;
119 String key = keyObj.toString();
120 if( ! key.startsWith(ORDER_PREFIX) ) continue;
121 key = key.replace(ORDER_PREFIX, "");
124 order = Integer.valueOf(key);
125 }catch(NumberFormatException e){
131 for(Integer orderNum : orderSet){
132 String setName = properties.getProperty(ORDER_PREFIX + orderNum);
133 FaceIconSet iconSet = loadFaceIconSet(properties, setName);
134 FACEICONSET_LIST.add(iconSet);
142 * @param properties プロパティ
143 * @param setName アイコンセット名
146 private static FaceIconSet loadFaceIconSet(Properties properties,
148 String author = properties.getProperty(setName + ".author");
149 String caption = properties.getProperty(setName + ".caption");
150 String urlText = properties.getProperty(setName + ".url");
152 FaceIconSet iconSet = new FaceIconSet(caption, author, urlText);
154 List<Avatar> avatarList = Avatar.getPredefinedAvatarList();
155 for(Avatar avatar : avatarList){
156 String identifier = avatar.getIdentifier();
157 String key = setName + ".iconWiki." + identifier;
158 String wiki = properties.getProperty(key);
159 iconSet.registIconWiki(avatar, wiki);
167 * @return 顔アイコンセットのリスト
169 public static List<FaceIconSet> getFaceIconSetList(){
170 List<FaceIconSet> result =
171 Collections.unmodifiableList(FACEICONSET_LIST);
176 * 任意の文字がWikiの特殊キャラクタか否か判定する。
178 * @return 特殊キャラクタならtrue
180 public static boolean isWikiChar(char ch){
181 if(WIKICHAR.indexOf(ch) < 0) return false;
186 * Wiki特殊文字を数値参照文字でエスケープする。
187 * @param seq Wiki特殊文字を含むかもしれない文字列。
188 * @return エスケープされた文字列
190 public static CharSequence escapeWikiChar(CharSequence seq){
191 StringBuilder result = new StringBuilder();
193 int seqLength = seq.length();
194 for(int pos = 0; pos < seqLength; pos++){
195 char ch = seq.charAt(pos);
198 appendNumCharRef(result, ch);
199 }catch(IOException e){
212 * WikiNameを数値参照文字でエスケープする。
213 * @param seq WikiNameを含むかもしれない文字列
214 * @return エスケープされた文字列。
216 public static CharSequence escapeWikiName(CharSequence seq){
217 StringBuilder result = null;
218 Matcher matcher = WIKINAME_PATTERN.matcher(seq);
221 while(matcher.find(pos)){
222 int matchStart = matcher.start();
223 int matchEnd = matcher.end();
224 int capStart = matcher.start(1);
225 int capEnd = matcher.end(1);
227 if(result == null) result = new StringBuilder();
228 result.append(seq, pos, matchStart);
229 result.append(seq, matchStart, capStart);
231 appendNumCharRef(result, seq.charAt(capStart));
232 }catch(IOException e){
236 result.append(seq, capEnd, matchEnd);
241 if(pos == 0) return seq;
243 result.append(seq, pos, seq.length());
249 * 任意の文字列をWiki表記へ変換する。
253 public static CharSequence escapeWikiSyntax(CharSequence seq){
254 CharSequence result = seq;
255 result = escapeWikiChar(result); // この順番は大事
256 result = escapeWikiName(result);
257 // TODO さらにURLとメールアドレスのエスケープも
262 * ブラケットに入れる文字をエスケープする。
264 * @return エスケープされた文字列
266 public static CharSequence escapeWikiBracket(CharSequence seq){
267 StringBuilder result = new StringBuilder();
269 int seqLength = seq.length();
270 for(int pos = 0; pos < seqLength; pos++){
271 char ch = seq.charAt(pos);
274 case '#': ch = '#'; break;
275 case '&': ch = '&'; break;
276 case '[': ch = '['; break;
277 case ']': ch = ']'; break;
278 case '<': ch = '<'; break;
279 case '>': ch = '>'; break;
288 resultLength = result.length();
289 while(result.length() > 0 && result.charAt(0) == '/'){
290 result.deleteCharAt(0);
293 resultLength = result.length();
294 for(int pos = resultLength - 1; pos >= 0; pos--){
295 char ch = result.charAt(pos);
297 result.deleteCharAt(pos);
300 resultLength = result.length();
301 for(int pos = 1; pos < resultLength - 1; pos++){
302 char ch = result.charAt(pos);
304 result.setCharAt(pos, ':');
308 resultLength = result.length();
309 if(resultLength == 1 && result.charAt(0) == ':'){
310 result.setCharAt(0, ':');
317 * 数値参照文字に変換された文字を追加する。
322 * @throws java.io.IOException 入出力エラー。文字列の場合はありえない。
324 public static Appendable appendNumCharRef(Appendable app, char ch)
329 String hex = Integer.toHexString(ival);
338 * 任意の文字を数値参照文字列に変換する。
343 public static CharSequence toNumCharRef(char ch){
344 StringBuilder result = new StringBuilder(8);
346 appendNumCharRef(result, ch);
347 }catch(IOException e){
359 public static String getTeamWikiColor(GameRole role){
390 public static String getRoleIconWiki(GameRole role){
395 result = "&char(村人,nolink);";
398 result = "&char(人狼,nolink);";
401 result = "&char(占い師,nolink);";
404 result = "&char(霊能者,nolink);";
407 result = "&char(狂人,nolink);";
410 result = "&char(狩人,nolink);";
413 result = "&char(共有者,nolink);";
416 result = "&char(ハムスター人間,nolink);";
432 public static String getDestinyColorWiki(Destiny destiny){
434 if(destiny == Destiny.ALIVE) result = "#ffffff";
435 else result = "#aaaaaa";
440 * そのまままとめサイトパス名に使えそうなシンプルな文字か判定する。
442 * @return まとめサイトパス名に使えそうならtrue
444 private static boolean isSimpleIdToken(char ch){
445 if('0' <= ch && ch <= '9') return true;
446 if('A' <= ch && ch <= 'Z') return true;
447 if('a' <= ch && ch <= 'z') return true;
448 if(ch == '-' || ch == '_') return true;
453 * プレイヤーIDを構成する文字からパス名を組み立てる。
458 private static StringBuilder encodeId(StringBuilder seq, char ch){
459 if(isSimpleIdToken(ch)){
464 CharBuffer cbuf = CharBuffer.allocate(1);
468 CharsetEncoder encoder = CHARSET_EUC.newEncoder();
471 bytebuf = encoder.encode(cbuf);
472 }catch(CharacterCodingException e){
477 int limit = bytebuf.limit();
478 while(bytebuf.position() < limit){
479 int iVal = bytebuf.get();
480 if(iVal < 0) iVal += 0x0100;
481 String hex = Integer.toHexString(iVal).toUpperCase(Locale.JAPAN);
483 if(hex.length() < 2) seq.append('0');
491 * プレイヤーIDからパス名の一部を予測する。
493 * @return .htmlを抜いたパス名
495 private static StringBuilder encodeId(CharSequence id){
496 StringBuilder result = new StringBuilder();
497 int length = id.length();
498 for(int pt = 0; pt < length; pt++){
499 char ch = id.charAt(pt);
500 encodeId(result, ch);
506 * プレイヤーIDからまとめサイト上の個人ページを推測する。
508 * @return 個人ページURL文字列
510 public static String encodeURLFromId(CharSequence id){
511 CharSequence encodedId = encodeId(id);
513 String result = WOLFBBS_URL + encodedId + ".html";