4 * Copyright(c) 2008 olyutorskii
\r
7 package jp.sourceforge.jindolf.archiver;
\r
9 import java.io.BufferedOutputStream;
\r
10 import java.io.BufferedWriter;
\r
11 import java.io.File;
\r
12 import java.io.FileOutputStream;
\r
13 import java.io.IOException;
\r
14 import java.io.OutputStream;
\r
15 import java.io.OutputStreamWriter;
\r
16 import java.io.Writer;
\r
17 import java.text.MessageFormat;
\r
18 import java.util.Calendar;
\r
19 import java.util.GregorianCalendar;
\r
20 import java.util.List;
\r
21 import java.util.TimeZone;
\r
22 import jp.sourceforge.jindolf.corelib.LandDef;
\r
23 import jp.sourceforge.jindolf.parser.DecodeErrorInfo;
\r
24 import jp.sourceforge.jindolf.parser.DecodedContent;
\r
29 public final class XmlUtils{
\r
31 public static final String ORIG_DTD =
\r
32 "http://jindolf.sourceforge.jp/xml/dtd/bbsArchive-110420.dtd";
\r
33 public static final String ORIG_NS =
\r
34 "http://jindolf.sourceforge.jp/xml/ns/501";
\r
35 public static final String ORIG_SCHEME =
\r
36 "http://jindolf.sourceforge.jp/xml/xsd/bbsArchive-110420.xsd";
\r
37 public static final String SCHEMA_NS =
\r
38 "http://www.w3.org/2001/XMLSchema-instance";
\r
40 public static final String OUTPATH = "D:\\TEMP\\zxzx\\";
\r
42 private static final String INDENT_UNIT = "\u0020\u0020";
\r
44 private static final TimeZone TZ_TOKYO =
\r
45 TimeZone.getTimeZone("Asia/Tokyo");
\r
46 private static final GregorianCalendar calendar =
\r
47 new GregorianCalendar(TZ_TOKYO);
\r
52 * @throws IOException 出力エラー
\r
54 public static void dumpDocType(Writer writer) throws IOException{
\r
55 writer.append("<!DOCTYPE village SYSTEM ");
\r
57 writer.append(ORIG_DTD);
\r
59 writer.append(" >");
\r
64 * オリジナルNameSpace宣言を出力する。
\r
66 * @throws IOException 出力エラー
\r
68 public static void dumpNameSpaceDecl(Writer writer)
\r
70 attrOut(writer, "xmlns", ORIG_NS);
\r
75 * スキーマNameSpace宣言を出力する。
\r
77 * @throws IOException 出力エラー
\r
79 public static void dumpSiNameSpaceDecl(Writer writer)
\r
81 attrOut(writer, "xmlns:xsi", SCHEMA_NS);
\r
88 * @throws IOException 出力エラー
\r
90 public static void dumpSchemeLocation(Writer writer)
\r
93 "xsi:schemaLocation",
\r
94 ORIG_NS + " " + ORIG_SCHEME);
\r
101 * @param writer 出力先
\r
102 * @param level ネストレベル
\r
103 * @throws IOException 出力エラー
\r
105 public static void indent(Writer writer, int level) throws IOException{
\r
106 for(int ct = 1; ct <= level; ct++){
\r
107 writer.append(INDENT_UNIT);
\r
114 * @param writer 出力先
\r
115 * @param chVal 出力文字
\r
116 * @throws IOException 出力エラー
\r
118 public static void charRefOut(Writer writer, char chVal)
\r
119 throws IOException{
\r
120 if(chVal == '\u0020'){
\r
121 writer.append(" ");
\r
125 if(chVal == '\u0009'){
\r
126 writer.append("	");
\r
130 int ival = 0xffff & ((int) chVal);
\r
131 String hex = Integer.toHexString(ival);
\r
132 if(hex.length() % 2 != 0) hex = "0" + hex;
\r
134 writer.append("&#x");
\r
135 writer.append(hex);
\r
136 writer.append(";");
\r
143 * @param writer 出力先
\r
144 * @param chVal 不正文字
\r
145 * @throws IOException 出力エラー
\r
147 public static void dumpInvalidChar(Writer writer, char chVal)
\r
148 throws IOException{
\r
150 hexVal = chVal & 0xff;
\r
151 String hexBin = Integer.toHexString(hexVal);
\r
152 if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;
\r
154 char replaceChar = '\ufffd';
\r
155 if('\u0000' <= chVal && chVal <= '\u001f'){
\r
156 replaceChar = (char)( chVal + '\u2400' );
\r
159 writer.append("<rawdata");
\r
161 writer.append(' ');
\r
162 attrOut(writer, "encoding", "Shift_JIS");
\r
164 writer.append(' ');
\r
165 attrOut(writer, "hexBin", hexBin);
\r
167 writer.append(" >");
\r
168 writer.append(replaceChar);
\r
169 writer.append("</rawdata>");
\r
173 * 任意の文字がXML規格上のホワイトスペースに属するか判定する。
\r
175 * @return ホワイトスペースならtrue
\r
177 public static boolean isWhiteSpace(char chVal){
\r
194 * <li>先頭および末尾のホワイトスペースは強制的に文字参照化される。
\r
195 * <li>連続したホワイトスペースの2文字目以降は文字参照化される。
\r
196 * <li>スペースでないホワイトスペースは無条件に文字参照化される。
\r
197 * <li>{@literal &, <, >, "}は無条件に文字参照化される。
\r
199 * 参考:XML 1.0 規格 3.3.3節
\r
200 * @param writer 出力先
\r
201 * @param seq CDATA文字列
\r
202 * @throws IOException 出力エラー
\r
204 public static void textOut(Writer writer, CharSequence seq)
\r
205 throws IOException{
\r
206 int len = seq.length();
\r
208 boolean leadSpace = false;
\r
210 for(int pos = 0; pos < len; pos++){
\r
211 char chVal = seq.charAt(pos);
\r
213 if(isWhiteSpace(chVal)){
\r
214 if(pos == 0 || pos >= len - 1 || leadSpace){
\r
215 charRefOut(writer, chVal);
\r
216 }else if(chVal != '\u0020'){
\r
217 charRefOut(writer, chVal);
\r
219 writer.append(chVal);
\r
224 writer.append("&");
\r
225 }else if(chVal == '<'){
\r
226 writer.append("<");
\r
227 }else if(chVal == '>'){
\r
228 writer.append(">");
\r
229 }else if(chVal == '"'){
\r
230 writer.append(""");
\r
231 }else if(chVal == '\''){
\r
232 writer.append("'");
\r
233 }else if(chVal == '\u005c\u005c'){
\r
234 writer.append('\u00a5');
\r
235 }else if(chVal == '\u007e'){
\r
236 writer.append('\u203e');
\r
237 }else if(Character.isISOControl(chVal)){
\r
238 dumpInvalidChar(writer, chVal);
\r
240 writer.append(chVal);
\r
251 * @param writer 出力先
\r
254 * @throws IOException 出力エラー
\r
256 public static void attrOut(Writer writer,
\r
258 CharSequence value)
\r
259 throws IOException{
\r
260 StringBuilder newValue = new StringBuilder(value);
\r
261 for(int pt = 0; pt < newValue.length(); pt++){
\r
262 char chVal = newValue.charAt(pt);
\r
263 if(chVal == '\n' || chVal == '\r' || chVal == '\t') continue;
\r
264 if(Character.isISOControl(chVal)){
\r
265 newValue.setCharAt(pt, (char)('\u2400' + chVal));
\r
269 writer.append(name);
\r
270 writer.append('=');
\r
271 writer.append('"');
\r
272 textOut(writer, newValue);
\r
273 writer.append('"');
\r
278 * xsd:time形式の時刻属性を出力する。
\r
279 * タイムゾーンは「+09:00」固定
\r
280 * @param writer 出力先
\r
284 * @throws IOException 出力エラー
\r
286 public static void timeAttrOut(Writer writer,
\r
288 int hour, int minute)
\r
289 throws IOException{
\r
292 .format("{0,number,#00}:{1,number,#00}:00+09:00",
\r
294 attrOut(writer, name, cmtTime);
\r
299 * xsd:gMonthDay形式の日付属性を出力する。
\r
300 * タイムゾーンは「+09:00」固定
\r
301 * @param writer 出力先
\r
305 * @throws IOException 出力エラー
\r
307 public static void dateAttrOut(Writer writer,
\r
309 int month, int day)
\r
310 throws IOException{
\r
312 MessageFormat.format("--{0,number,#00}-{1,number,#00}+09:00",
\r
314 attrOut(writer, name, dateAttr);
\r
319 * xsd:dateTime形式の日付時刻属性を出力する。
\r
320 * タイムゾーンは「+09:00」固定
\r
321 * @param writer 出力先
\r
323 * @param epochMs エポック時刻
\r
324 * @throws IOException 出力エラー
\r
326 public static void dateTimeAttr(Writer writer,
\r
329 throws IOException{
\r
330 synchronized(calendar){
\r
331 calendar.setTimeInMillis(epochMs);
\r
332 int year = calendar.get(Calendar.YEAR);
\r
333 int month = calendar.get(Calendar.MONTH) + 1;
\r
334 int day = calendar.get(Calendar.DATE);
\r
335 int hour = calendar.get(Calendar.HOUR_OF_DAY);
\r
336 int minute = calendar.get(Calendar.MINUTE);
\r
337 int sec = calendar.get(Calendar.SECOND);
\r
338 int msec = calendar.get(Calendar.MILLISECOND);
\r
340 String attrVal = MessageFormat.format(
\r
341 "{0,number,#0000}-{1,number,#00}-{2,number,#00}"
\r
342 +"T{3,number,#00}:{4,number,#00}:{5,number,#00}"
\r
343 +".{6,number,#000}+09:00",
\r
344 year, month, day, hour, minute, sec, msec);
\r
346 attrOut(writer, name, attrVal);
\r
352 * デコードエラー情報をrawdataタグで出力する。
\r
353 * 文字列集合に関するエラーの場合、windows31jでのデコード出力を試みる。
\r
354 * @param writer 出力先
\r
355 * @param errorInfo デコードエラー
\r
356 * @throws IOException 出力エラー
\r
358 public static void dumpErrorInfo(Writer writer,
\r
359 DecodeErrorInfo errorInfo)
\r
360 throws IOException{
\r
362 hexVal = errorInfo.getRawByte1st() & 0xff;
\r
363 if(errorInfo.has2nd()){
\r
365 hexVal |= errorInfo.getRawByte2nd() & 0xff;
\r
368 String hexBin = Integer.toHexString(hexVal);
\r
369 if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;
\r
371 char replaceChar = Win31j.getWin31jChar(errorInfo);
\r
373 writer.append("<rawdata");
\r
375 writer.append(' ');
\r
376 attrOut(writer, "encoding", "Shift_JIS");
\r
378 writer.append(' ');
\r
379 attrOut(writer, "hexBin", hexBin);
\r
381 writer.append(" >");
\r
382 writer.append(replaceChar);
\r
383 writer.append("</rawdata>");
\r
389 * デコードエラー込みのテキストを出力する。
\r
390 * @param writer 出力先
\r
391 * @param content テキスト
\r
392 * @throws IOException 出力エラー
\r
394 public static void dumpDecodedContent(Writer writer,
\r
395 DecodedContent content)
\r
396 throws IOException{
\r
397 if( ! content.hasDecodeError() ){
\r
398 textOut(writer, content);
\r
404 List<DecodeErrorInfo> errList = content.getDecodeErrorList();
\r
405 for(DecodeErrorInfo err : errList){
\r
406 int charPos = err.getCharPosition();
\r
407 CharSequence line = content.subSequence(last, charPos);
\r
408 textOut(writer, line);
\r
409 dumpErrorInfo(writer, err);
\r
410 last = charPos + 1;
\r
413 CharSequence line = content.subSequence(last, content.length());
\r
414 textOut(writer, line);
\r
421 * @param writer 出力先
\r
422 * @param villageData 村情報
\r
423 * @throws IOException 出力エラー
\r
425 public static void dumpVillageData(Writer writer,
\r
426 VillageData villageData)
\r
427 throws IOException{
\r
428 writer.append("<?xml");
\r
429 writer.append(' ');
\r
430 attrOut(writer, "version", "1.0");
\r
431 writer.append(' ');
\r
432 attrOut(writer, "encoding", "UTF-8");
\r
433 writer.append(" ?>\n\n");
\r
435 writer.append("<!--\n");
\r
436 writer.append(" 人狼BBSアーカイブ\n");
\r
437 writer.append(" http://jindolf.sourceforge.jp/\n");
\r
438 writer.append("-->\n\n");
\r
440 dumpDocType(writer);
\r
441 writer.append("\n\n");
\r
443 villageData.dumpXml(writer);
\r
445 writer.append("\n<!-- EOF -->\n");
\r
453 * 村情報を反映した出力ファイル名を生成する。
\r
454 * @param village 村情報
\r
455 * @return XML出力ファイル名
\r
457 public static String createOutFileName(VillageData village){
\r
458 LandDef landDef = village.getLandDef();
\r
459 String landId = landDef.getLandId();
\r
460 int vid = village.getVillageId();
\r
463 MessageFormat.format(
\r
464 "{0}jin_{1}_{2,number,#00000}.xml", OUTPATH, landId, vid);
\r
469 * 村情報を反映した出力ファイルへの文字ストリームを生成する。
\r
470 * @param village 村情報
\r
471 * @return 出力先文字ストリーム
\r
472 * @throws IOException 出力エラー
\r
474 public static Writer createFileWriter(VillageData village)
\r
475 throws IOException{
\r
476 String fname = createOutFileName(village);
\r
477 File file = new File(fname);
\r
479 OutputStream ostream;
\r
480 ostream = new FileOutputStream(file);
\r
481 ostream = new BufferedOutputStream(ostream, 10000);
\r
483 writer = new OutputStreamWriter(ostream, "UTF-8");
\r
484 writer = new BufferedWriter(writer, 10000);
\r
491 private XmlUtils(){
\r