-/*\r
- * XML utils\r
- *\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.BufferedOutputStream;\r
-import java.io.BufferedWriter;\r
-import java.io.File;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.OutputStream;\r
-import java.io.OutputStreamWriter;\r
-import java.io.Writer;\r
-import java.text.MessageFormat;\r
-import java.util.Calendar;\r
-import java.util.GregorianCalendar;\r
-import java.util.List;\r
-import java.util.TimeZone;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.parser.DecodeErrorInfo;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-\r
-/**\r
- * XML用各種ユーティリティ\r
- */\r
-public final class XmlUtils{\r
-\r
- public static final String ORIG_DTD =\r
- "http://jindolf.sourceforge.jp/xml/dtd/bbsArchive-110420.dtd";\r
- public static final String ORIG_NS =\r
- "http://jindolf.sourceforge.jp/xml/ns/501";\r
- public static final String ORIG_SCHEME =\r
- "http://jindolf.sourceforge.jp/xml/xsd/bbsArchive-110420.xsd";\r
- public static final String SCHEMA_NS =\r
- "http://www.w3.org/2001/XMLSchema-instance";\r
-\r
- public static final String OUTPATH = "D:\\TEMP\\zxzx\\";\r
-\r
- private static final String INDENT_UNIT = "\u0020\u0020";\r
-\r
- private static final TimeZone TZ_TOKYO =\r
- TimeZone.getTimeZone("Asia/Tokyo");\r
- private static final GregorianCalendar calendar =\r
- new GregorianCalendar(TZ_TOKYO);\r
-\r
- /**\r
- * DOCTYPE宣言を出力する。\r
- * @param writer 出力先\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpDocType(Writer writer) throws IOException{\r
- writer.append("<!DOCTYPE village SYSTEM ");\r
- writer.append('"');\r
- writer.append(ORIG_DTD);\r
- writer.append('"');\r
- writer.append(" >");\r
- return;\r
- }\r
-\r
- /**\r
- * オリジナルNameSpace宣言を出力する。\r
- * @param writer 出力先\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpNameSpaceDecl(Writer writer)\r
- throws IOException{\r
- attrOut(writer, "xmlns", ORIG_NS);\r
- return;\r
- }\r
-\r
- /**\r
- * スキーマNameSpace宣言を出力する。\r
- * @param writer 出力先\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpSiNameSpaceDecl(Writer writer)\r
- throws IOException{\r
- attrOut(writer, "xmlns:xsi", SCHEMA_NS);\r
- return;\r
- }\r
-\r
- /**\r
- * スキーマ位置指定を出力する。\r
- * @param writer 出力先\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpSchemeLocation(Writer writer)\r
- throws IOException{\r
- attrOut(writer,\r
- "xsi:schemaLocation",\r
- ORIG_NS + " " + ORIG_SCHEME);\r
- return;\r
- }\r
-\r
- /**\r
- * インデント用空白を出力する。\r
- * ネスト単位は空白2文字\r
- * @param writer 出力先\r
- * @param level ネストレベル\r
- * @throws IOException 出力エラー\r
- */\r
- public static void indent(Writer writer, int level) throws IOException{\r
- for(int ct = 1; ct <= level; ct++){\r
- writer.append(INDENT_UNIT);\r
- }\r
- return;\r
- }\r
-\r
- /**\r
- * XML数値文字参照を出力する。\r
- * @param writer 出力先\r
- * @param chVal 出力文字\r
- * @throws IOException 出力エラー\r
- */\r
- public static void charRefOut(Writer writer, char chVal)\r
- throws IOException{\r
- if(chVal == '\u0020'){\r
- writer.append(" ");\r
- return;\r
- }\r
-\r
- if(chVal == '\u0009'){\r
- writer.append("	");\r
- return;\r
- }\r
-\r
- int ival = 0xffff & ((int) chVal);\r
- String hex = Integer.toHexString(ival);\r
- if(hex.length() % 2 != 0) hex = "0" + hex;\r
-\r
- writer.append("&#x");\r
- writer.append(hex);\r
- writer.append(";");\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * 不正文字をXML出力する。\r
- * @param writer 出力先\r
- * @param chVal 不正文字\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpInvalidChar(Writer writer, char chVal)\r
- throws IOException{\r
- int hexVal;\r
- hexVal = chVal & 0xff;\r
- String hexBin = Integer.toHexString(hexVal);\r
- if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;\r
-\r
- char replaceChar = '\ufffd';\r
- if('\u0000' <= chVal && chVal <= '\u001f'){\r
- replaceChar = (char)( chVal + '\u2400' );\r
- }\r
-\r
- writer.append("<rawdata");\r
-\r
- writer.append(' ');\r
- attrOut(writer, "encoding", "Shift_JIS");\r
-\r
- writer.append(' ');\r
- attrOut(writer, "hexBin", hexBin);\r
-\r
- writer.append(" >");\r
- writer.append(replaceChar);\r
- writer.append("</rawdata>");\r
- }\r
-\r
- /**\r
- * 任意の文字がXML規格上のホワイトスペースに属するか判定する。\r
- * @param chVal 文字\r
- * @return ホワイトスペースならtrue\r
- */\r
- public static boolean isWhiteSpace(char chVal){\r
- switch(chVal){\r
- case '\u0020':\r
- case '\t':\r
- case '\n':\r
- case '\r':\r
- return true;\r
- default:\r
- break;\r
- }\r
-\r
- return false;\r
- }\r
-\r
- /**\r
- * 文字列を出力する。\r
- * <ul>\r
- * <li>先頭および末尾のホワイトスペースは強制的に文字参照化される。\r
- * <li>連続したホワイトスペースの2文字目以降は文字参照化される。\r
- * <li>スペースでないホワイトスペースは無条件に文字参照化される。\r
- * <li>{@literal &, <, >, "}は無条件に文字参照化される。\r
- * </ul>\r
- * 参考:XML 1.0 規格 3.3.3節\r
- * @param writer 出力先\r
- * @param seq CDATA文字列\r
- * @throws IOException 出力エラー\r
- */\r
- public static void textOut(Writer writer, CharSequence seq)\r
- throws IOException{\r
- int len = seq.length();\r
-\r
- boolean leadSpace = false;\r
-\r
- for(int pos = 0; pos < len; pos++){\r
- char chVal = seq.charAt(pos);\r
-\r
- if(isWhiteSpace(chVal)){\r
- if(pos == 0 || pos >= len - 1 || leadSpace){\r
- charRefOut(writer, chVal);\r
- }else if(chVal != '\u0020'){\r
- charRefOut(writer, chVal);\r
- }else{\r
- writer.append(chVal);\r
- }\r
- leadSpace = true;\r
- }else{\r
- if(chVal == '&'){\r
- writer.append("&");\r
- }else if(chVal == '<'){\r
- writer.append("<");\r
- }else if(chVal == '>'){\r
- writer.append(">");\r
- }else if(chVal == '"'){\r
- writer.append(""");\r
- }else if(chVal == '\''){\r
- writer.append("'");\r
- }else if(chVal == '\u005c\u005c'){\r
- writer.append('\u00a5');\r
- }else if(chVal == '\u007e'){\r
- writer.append('\u203e');\r
- }else if(Character.isISOControl(chVal)){\r
- dumpInvalidChar(writer, chVal);\r
- }else{\r
- writer.append(chVal);\r
- }\r
- leadSpace = false;\r
- }\r
- }\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * 属性を出力する。\r
- * @param writer 出力先\r
- * @param name 属性名\r
- * @param value 属性値\r
- * @throws IOException 出力エラー\r
- */\r
- public static void attrOut(Writer writer,\r
- CharSequence name,\r
- CharSequence value)\r
- throws IOException{\r
- StringBuilder newValue = new StringBuilder(value);\r
- for(int pt = 0; pt < newValue.length(); pt++){\r
- char chVal = newValue.charAt(pt);\r
- if(chVal == '\n' || chVal == '\r' || chVal == '\t') continue;\r
- if(Character.isISOControl(chVal)){\r
- newValue.setCharAt(pt, (char)('\u2400' + chVal));\r
- }\r
- }\r
-\r
- writer.append(name);\r
- writer.append('=');\r
- writer.append('"');\r
- textOut(writer, newValue);\r
- writer.append('"');\r
- return;\r
- }\r
-\r
- /**\r
- * xsd:time形式の時刻属性を出力する。\r
- * タイムゾーンは「+09:00」固定\r
- * @param writer 出力先\r
- * @param name 属性名\r
- * @param hour 時間\r
- * @param minute 分\r
- * @throws IOException 出力エラー\r
- */\r
- public static void timeAttrOut(Writer writer,\r
- CharSequence name,\r
- int hour, int minute)\r
- throws IOException{\r
- String cmtTime =\r
- MessageFormat\r
- .format("{0,number,#00}:{1,number,#00}:00+09:00",\r
- hour, minute);\r
- attrOut(writer, name, cmtTime);\r
- return;\r
- }\r
-\r
- /**\r
- * xsd:gMonthDay形式の日付属性を出力する。\r
- * タイムゾーンは「+09:00」固定\r
- * @param writer 出力先\r
- * @param name 属性名\r
- * @param month 月\r
- * @param day 日\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dateAttrOut(Writer writer,\r
- CharSequence name,\r
- int month, int day)\r
- throws IOException{\r
- String dateAttr =\r
- MessageFormat.format("--{0,number,#00}-{1,number,#00}+09:00",\r
- month, day);\r
- attrOut(writer, name, dateAttr);\r
- return;\r
- }\r
-\r
- /**\r
- * xsd:dateTime形式の日付時刻属性を出力する。\r
- * タイムゾーンは「+09:00」固定\r
- * @param writer 出力先\r
- * @param name 属性名\r
- * @param epochMs エポック時刻\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dateTimeAttr(Writer writer,\r
- CharSequence name,\r
- long epochMs)\r
- throws IOException{\r
- synchronized(calendar){\r
- calendar.setTimeInMillis(epochMs);\r
- int year = calendar.get(Calendar.YEAR);\r
- int month = calendar.get(Calendar.MONTH) + 1;\r
- int day = calendar.get(Calendar.DATE);\r
- int hour = calendar.get(Calendar.HOUR_OF_DAY);\r
- int minute = calendar.get(Calendar.MINUTE);\r
- int sec = calendar.get(Calendar.SECOND);\r
- int msec = calendar.get(Calendar.MILLISECOND);\r
-\r
- String attrVal = MessageFormat.format(\r
- "{0,number,#0000}-{1,number,#00}-{2,number,#00}"\r
- +"T{3,number,#00}:{4,number,#00}:{5,number,#00}"\r
- +".{6,number,#000}+09:00",\r
- year, month, day, hour, minute, sec, msec);\r
-\r
- attrOut(writer, name, attrVal);\r
- }\r
- return;\r
- }\r
-\r
- /**\r
- * デコードエラー情報をrawdataタグで出力する。\r
- * 文字列集合に関するエラーの場合、windows31jでのデコード出力を試みる。\r
- * @param writer 出力先\r
- * @param errorInfo デコードエラー\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpErrorInfo(Writer writer,\r
- DecodeErrorInfo errorInfo)\r
- throws IOException{\r
- int hexVal;\r
- hexVal = errorInfo.getRawByte1st() & 0xff;\r
- if(errorInfo.has2nd()){\r
- hexVal <<= 8;\r
- hexVal |= errorInfo.getRawByte2nd() & 0xff;\r
- }\r
-\r
- String hexBin = Integer.toHexString(hexVal);\r
- if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;\r
-\r
- char replaceChar = Win31j.getWin31jChar(errorInfo);\r
-\r
- writer.append("<rawdata");\r
-\r
- writer.append(' ');\r
- attrOut(writer, "encoding", "Shift_JIS");\r
-\r
- writer.append(' ');\r
- attrOut(writer, "hexBin", hexBin);\r
-\r
- writer.append(" >");\r
- writer.append(replaceChar);\r
- writer.append("</rawdata>");\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * デコードエラー込みのテキストを出力する。\r
- * @param writer 出力先\r
- * @param content テキスト\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpDecodedContent(Writer writer,\r
- DecodedContent content)\r
- throws IOException{\r
- if( ! content.hasDecodeError() ){\r
- textOut(writer, content);\r
- return;\r
- }\r
-\r
- int last = 0;\r
-\r
- List<DecodeErrorInfo> errList = content.getDecodeErrorList();\r
- for(DecodeErrorInfo err : errList){\r
- int charPos = err.getCharPosition();\r
- CharSequence line = content.subSequence(last, charPos);\r
- textOut(writer, line);\r
- dumpErrorInfo(writer, err);\r
- last = charPos + 1;\r
- }\r
-\r
- CharSequence line = content.subSequence(last, content.length());\r
- textOut(writer, line);\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * 村情報をXML形式で出力する。\r
- * @param writer 出力先\r
- * @param villageData 村情報\r
- * @throws IOException 出力エラー\r
- */\r
- public static void dumpVillageData(Writer writer,\r
- VillageData villageData)\r
- throws IOException{\r
- writer.append("<?xml");\r
- writer.append(' ');\r
- attrOut(writer, "version", "1.0");\r
- writer.append(' ');\r
- attrOut(writer, "encoding", "UTF-8");\r
- writer.append(" ?>\n\n");\r
-\r
- writer.append("<!--\n");\r
- writer.append(" 人狼BBSアーカイブ\n");\r
- writer.append(" http://jindolf.sourceforge.jp/\n");\r
- writer.append("-->\n\n");\r
-\r
- dumpDocType(writer);\r
- writer.append("\n\n");\r
-\r
- villageData.dumpXml(writer);\r
-\r
- writer.append("\n<!-- EOF -->\n");\r
-\r
- writer.flush();\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * 村情報を反映した出力ファイル名を生成する。\r
- * @param village 村情報\r
- * @return XML出力ファイル名\r
- */\r
- public static String createOutFileName(VillageData village){\r
- LandDef landDef = village.getLandDef();\r
- String landId = landDef.getLandId();\r
- int vid = village.getVillageId();\r
-\r
- String fname =\r
- MessageFormat.format(\r
- "{0}jin_{1}_{2,number,#00000}.xml", OUTPATH, landId, vid);\r
- return fname;\r
- }\r
-\r
- /**\r
- * 村情報を反映した出力ファイルへの文字ストリームを生成する。\r
- * @param village 村情報\r
- * @return 出力先文字ストリーム\r
- * @throws IOException 出力エラー\r
- */\r
- public static Writer createFileWriter(VillageData village)\r
- throws IOException{\r
- String fname = createOutFileName(village);\r
- File file = new File(fname);\r
-\r
- OutputStream ostream;\r
- ostream = new FileOutputStream(file);\r
- ostream = new BufferedOutputStream(ostream, 10000);\r
- Writer writer;\r
- writer = new OutputStreamWriter(ostream, "UTF-8");\r
- writer = new BufferedWriter(writer, 10000);\r
- return writer;\r
- }\r
-\r
- /**\r
- * 隠れコンストラクタ\r
- */\r
- private XmlUtils(){\r
- throw new Error();\r
- }\r
-\r
-}\r
+/*
+ * XML utils
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.xml.sax.SAXException;
+
+/**
+ * XML用各種ユーティリティ。
+ */
+public final class XmlUtils{
+
+ /**
+ * 隠れコンストラクタ。
+ */
+ private XmlUtils(){
+ assert false;
+ throw new AssertionError();
+ }
+
+
+ /**
+ * XML読み込み用DocumentBuilderを生成する。
+ * @return DocumentBuilder
+ * @throws ParserConfigurationException 実装が要求に応えられない。
+ */
+ public static DocumentBuilder createDocumentBuilder()
+ throws ParserConfigurationException {
+ DocumentBuilderFactory factory;
+ factory = DocumentBuilderFactory.newInstance();
+
+ DocumentBuilder builder;
+ builder = factory.newDocumentBuilder();
+
+ return builder;
+ }
+
+ /**
+ * バリデータを生成する。
+ * @return バリデータ
+ * @throws SAXException 実装が要求に応えられない。
+ */
+ public static Validator createValidator() throws SAXException{
+ SchemaFactory factory;
+ String nsuri = XMLConstants.W3C_XML_SCHEMA_NS_URI;
+ factory = SchemaFactory.newInstance(nsuri);
+
+ Schema schema;
+ schema = factory.newSchema();
+
+ Validator validator = schema.newValidator();
+
+ LSResourceResolver resolver = new XmlResolver();
+ validator.setResourceResolver(resolver);
+
+ return validator;
+ }
+
+}