OSDN Git Service

Merge commit 'cbf79f1f735efd8eacdb6f10beb9c2705225ac30'
[jindolf/JinArchiver.git] / src / main / java / jp / sourceforge / jindolf / archiver / XmlUtils.java
index 6ed857e..b3e8594 100644 (file)
-/*\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("&#x20;");\r
-            return;\r
-        }\r
-\r
-        if(chVal == '\u0009'){\r
-            writer.append("&#x09;");\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("&amp;");\r
-                }else if(chVal == '<'){\r
-                    writer.append("&lt;");\r
-                }else if(chVal == '>'){\r
-                    writer.append("&gt;");\r
-                }else if(chVal == '"'){\r
-                    writer.append("&quot;");\r
-                }else if(chVal == '\''){\r
-                    writer.append("&apos;");\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;
+    }
+
+}