OSDN Git Service

Merge topic/xxe into develop
authorOlyutorskii <olyutorskii@users.osdn.me>
Sat, 29 Jun 2019 16:25:42 +0000 (01:25 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Sat, 29 Jun 2019 16:25:42 +0000 (01:25 +0900)
13 files changed:
CHANGELOG.txt
config/checkstyle/checkstyle-suppressions.xml
config/checkstyle/checkstyle.xml
pom.xml
src/main/java/jp/sfjp/mikutoga/typical/I18nAlias.java
src/main/java/jp/sfjp/mikutoga/xml/DomNsUtils.java [deleted file]
src/main/java/jp/sfjp/mikutoga/xml/DomUtils.java [deleted file]
src/main/java/jp/sfjp/mikutoga/xml/LocalXmlResource.java [deleted file]
src/main/java/jp/sfjp/mikutoga/xml/NoopEntityResolver.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/xml/SchemaUtil.java
src/main/java/jp/sfjp/mikutoga/xml/SiblingElemIterator.java [deleted file]
src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java
src/test/java/jp/sfjp/mikutoga/xml/DomUtilsTest.java [deleted file]

index 8577d10..c5293d0 100644 (file)
@@ -4,6 +4,12 @@
 TogaGem 変更履歴
 
 
+X.XXX.X (XXXX-XX-XX)
+    * Prevent XXE vulnerabilities.
+    * Split entity resolver from resource resolver to prevent XXE vulnerability.
+    * Make Schema-factory safe to prevent XXE vulnerability.
+    * Move out xml-xsd info from resolver.
+
 3.121.2 (2019-06-06)
     ・DatatypeIo is public now, for replacing JAXB.
 
index 5f6d0fd..3860edf 100644 (file)
@@ -6,7 +6,7 @@
 
 <!--
     Checkstyle suppressions
-    for Checkstyle 8.20 or later
+    for Checkstyle 8.22 or later
 
     [ https://checkstyle.org/ ]
 
@@ -35,6 +35,7 @@
 
     <!-- Miscellaneous -->
     <suppress files="" checks="FinalParameters" />
+    <suppress files="" checks="OrderedProperties" />
     <suppress files="" checks="TrailingComment" />
 
     <!-- Modifier -->
index ad3deac..0b1ceb5 100644 (file)
@@ -6,7 +6,7 @@
 
 <!--
     Checkstyle modules
-    for Checkstyle 8.21 or later
+    for Checkstyle 8.22 or later
 
     [ https://checkstyle.org/ ]
 
@@ -56,6 +56,7 @@
     <module name="NewlineAtEndOfFile">
         <property name="fileExtensions" value="java, properties, xml, xsd, md, txt" />
     </module>
+    <module name="OrderedProperties" />
     <module name="Translation" />
     <module name="UniqueProperties" />
 
         <module name="SuperClone" />
         <module name="SuperFinalize" />
         <module name="UnnecessaryParentheses" />
+        <module name="UnnecessarySemicolonInEnumeration" />
+        <module name="UnnecessarySemicolonInTryWithResources" />
         <module name="VariableDeclarationUsageDistance">
             <property name="allowedDistance" value="5"/>
         </module>
             <property name="scope" value="protected" />
         </module>
         <module name="MissingJavadocMethod" />
+        <module name="MissingJavadocPackage" />
         <module name="MissingJavadocType" />
         <module name="NonEmptyAtclauseDescription" />
         <module name="SingleLineJavadoc" />
diff --git a/pom.xml b/pom.xml
index 40617d3..6a92582 100644 (file)
--- a/pom.xml
+++ b/pom.xml
         <jacoco-plugin.version>0.8.4</jacoco-plugin.version>
 
         <checkstyle-plugin.version>3.1.0</checkstyle-plugin.version>
-        <checkstyleruntime.version>8.21</checkstyleruntime.version>
+        <checkstyleruntime.version>8.22</checkstyleruntime.version>
         <checkstyle.config.location>${project.basedir}/config/checkstyle/checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}/config/checkstyle/checkstyle-suppressions.xml</checkstyle.suppressions.location>
         <checkstyle.enable.rss>false</checkstyle.enable.rss>
 
         <pmd-plugin.version>3.12.0</pmd-plugin.version>
 
-        <spotbugs-plugin.version>3.1.11</spotbugs-plugin.version>
+        <spotbugs-plugin.version>3.1.12</spotbugs-plugin.version>
         <spotbugs.effort>Max</spotbugs.effort>
         <spotbugs.threshold>Low</spotbugs.threshold>
         <!-- for Jenkins -->
index 4c4de31..5942a41 100644 (file)
@@ -14,6 +14,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
+import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -43,6 +44,15 @@ class I18nAlias {
     public static final Comparator<I18nAlias> ORDER_COMPARATOR =
             new OrderComparator();
 
+    private static final String F_DISALLOW_DOCTYPE_DECL =
+            "http://apache.org/xml/features/disallow-doctype-decl";
+    private static final String F_EXTERNAL_GENERAL_ENTITIES =
+            "http://xml.org/sax/features/external-general-entities";
+    private static final String F_EXTERNAL_PARAMETER_ENTITIES =
+            "http://xml.org/sax/features/external-parameter-entities";
+    private static final String F_LOAD_EXTERNAL_DTD =
+            "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
 
     private int orderNo;
 
@@ -110,6 +120,20 @@ class I18nAlias {
         DocumentBuilderFactory factory;
         factory = DocumentBuilderFactory.newInstance();
 
+        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        factory.setFeature(F_EXTERNAL_GENERAL_ENTITIES, false);
+        factory.setFeature(F_EXTERNAL_PARAMETER_ENTITIES, false);
+        factory.setFeature(F_LOAD_EXTERNAL_DTD, false);
+
+        // unsafe but we use DOCTYPE
+        factory.setFeature(F_DISALLOW_DOCTYPE_DECL, false);
+
+        factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+        factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+
+        factory.setXIncludeAware(false);
+        factory.setExpandEntityReferences(false);
+
         DocumentBuilder builder = factory.newDocumentBuilder();
         Document doc = builder.parse(is);
 
diff --git a/src/main/java/jp/sfjp/mikutoga/xml/DomNsUtils.java b/src/main/java/jp/sfjp/mikutoga/xml/DomNsUtils.java
deleted file mode 100644 (file)
index f36313c..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * XML DOM utilities with namespace
- *
- * License : The MIT License
- * Copyright(c) 2011 MikuToga Partners
- */
-
-package jp.sfjp.mikutoga.xml;
-
-import java.text.MessageFormat;
-import java.util.Iterator;
-import org.w3c.dom.DOMException;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-/**
- * DOMユーティリティ(名前空間対応)。
- *
- * <p>各種名前空間引数にnullが渡された場合、全ての名前空間にマッチする。
- *
- * <p>各種ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。
- *
- * <p>ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。
- */
-public final class DomNsUtils {
-
-    private static final String ERRMSG_NOELEM =
-            "Elem:[{0}] was not found in Elem:[{1}]";
-    private static final String ERRMSG_NOATTR =
-            "Attr:[{0}] was not found in Elem:[{1}]";
-    private static final String ERRMSG_INVATTR =
-            "Invalid attribute form Attr[{0}] Value[{1}]";
-
-
-    /**
-     * 隠しコンストラクタ。
-     */
-    private DomNsUtils(){
-        assert false;
-        throw new AssertionError();
-    }
-
-
-    /**
-     * 名前空間とローカル名が一致するノードか判定する。
-     * @param node ノード
-     * @param nsuri 名前空間URI
-     * @param localName ローカル名。
-     * @return ノードの名前空間およびローカル名が一致したらtrue
-     */
-    public static boolean hasNsLocalNameNode(Node node,
-                                                String nsuri,
-                                                String localName ){
-        String nodeLocalName = node.getLocalName();
-        String nodeNsUri     = node.getNamespaceURI();
-
-        if(localName != null){
-            if( ! localName.equals(nodeLocalName) ) return false;
-        }
-
-        if(nsuri != null && nodeNsUri != null){
-            if( ! nsuri.equals(nodeNsUri) ) return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * 名前空間とローカル名が一致する要素か判定する。
-     * @param node ノード
-     * @param nsuri 名前空間URI
-     * @param localName ローカル名。
-     * @return 名前空間およびローカル名が一致する要素であればtrue
-     */
-    public static boolean hasNsLocalNameElem(Node node,
-                                             String nsuri,
-                                             String localName ){
-        if(node.getNodeType() != Node.ELEMENT_NODE) return false;
-        if( ! hasNsLocalNameNode(node, nsuri, localName) ) return false;
-        return true;
-    }
-
-    /**
-     * 親要素が指定された名前の子要素を持つか判定する。
-     * @param parent 親要素
-     * @param nsuri 名前空間URI
-     * @param localName ローカル名
-     * @return 指定名の子要素が存在すればtrue
-     */
-    public static boolean hasChild(Element parent,
-                                    String nsuri,
-                                    String localName ){
-        for(Node node = parent.getFirstChild();
-            node != null;
-            node = node.getNextSibling() ){
-
-            if(hasNsLocalNameElem(node, nsuri, localName)){
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * 指定された名前空間とローカル名に合致する最初の直下子要素を返す。
-     * @param parent 親要素
-     * @param nsuri 名前空間URI
-     * @param localName ローカル名
-     * @return 最初の直下子要素。見つからなければnull。
-     */
-    public static Element pickFirstChild(Node parent,
-                                         String nsuri,
-                                         String localName ){
-        Node node = parent.getFirstChild();
-        while(node != null){
-            if(hasNsLocalNameElem(node, nsuri, localName)){
-                break;
-            }
-            node = node.getNextSibling();
-        }
-        return (Element) node;
-    }
-
-    /**
-     * 指定された名前空間とローカル名に合致する最初の直下子要素を返す。
-     *
-     * <p>見つからなければ例外を投げる。
-     *
-     * @param parent 親要素
-     * @param nsuri 名前空間URI
-     * @param localName ローカル名
-     * @return 最初の直下子要素。
-     * @throws TogaXmlException 1つも見つからなかった
-     */
-    public static Element getFirstChild(Element parent,
-                                        String nsuri,
-                                        String localName )
-            throws TogaXmlException{
-        Element elem = pickFirstChild(parent, nsuri, localName);
-
-        if(elem == null){
-            String message = MessageFormat.format(ERRMSG_NOELEM,
-                                                  localName,
-                                                  parent.getLocalName() );
-            throw new TogaXmlException(message);
-        }
-
-        return elem;
-    }
-
-    /**
-     * 指定された名前の子要素のforeachを返す。
-     * @param parent 親要素
-     * @param nsuri 名前空間URI
-     * @param localName 子要素名
-     * @return 子要素のforeach
-     */
-    public static Iterable<Element> getEachChild(final Element parent,
-                                                 final String nsuri,
-                                                 final String localName ){
-        Iterable<Element> result = new Iterable<Element>(){
-            @Override
-            public Iterator<Element> iterator(){
-                return new SiblingElemIterator(parent, nsuri, localName);
-            }
-        };
-        return result;
-    }
-
-    /**
-     * 要素に属性が存在するか判定する。
-     * @param elem 要素
-     * @param nsuri 名前空間URI
-     * @param localName ローカル名
-     * @return 存在するならtrue
-     */
-    public static boolean hasAttrNS(Element elem,
-                                    String nsuri,
-                                    String localName ){
-        return elem.hasAttributeNS(nsuri, localName);
-    }
-
-    /**
-     * 要素からxsd:string型属性値を読み取る。
-     * @param elem 要素
-     * @param nsuri 名前空間URI
-     * @param localName 属性名
-     * @return 文字列
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static String getStringAttrNS(Element elem,
-                                         String nsuri,
-                                         String localName )
-            throws TogaXmlException{
-        if( ! hasAttrNS(elem, nsuri, localName) ){
-            String message = MessageFormat.format(ERRMSG_NOATTR,
-                                                  localName,
-                                                  elem.getLocalName() );
-            throw new TogaXmlException(message);
-        }
-
-        String result;
-        try{
-            result = elem.getAttributeNS(nsuri, localName);
-        }catch(DOMException e){
-            assert false;
-            throw new AssertionError(e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素からxsd:boolean型属性値を読み取る。
-     * @param elem 要素
-     * @param nsuri 名前空間URI
-     * @param localName 属性名
-     * @return 真ならtrue
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static boolean getBooleanAttrNS(Element elem,
-                                           String nsuri,
-                                           String localName )
-            throws TogaXmlException{
-        String value = getStringAttrNS(elem, nsuri, localName);
-
-        boolean result;
-        try{
-            result = DatatypeIo.parseBoolean(value);
-        }catch(IllegalArgumentException e){
-            String message = MessageFormat.format(ERRMSG_INVATTR,
-                                                  localName,
-                                                  value );
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素からxsd:integer型属性値を読み取る。
-     * @param elem 要素
-     * @param nsuri 名前空間URI
-     * @param localName 属性名
-     * @return int値
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static int getIntegerAttrNS(Element elem,
-                                       String nsuri,
-                                       String localName )
-            throws TogaXmlException{
-        String value = getStringAttrNS(elem, nsuri, localName);
-
-        int result;
-        try{
-            result = DatatypeIo.parseInt(value);
-        }catch(NumberFormatException e){
-            String message = MessageFormat.format(ERRMSG_INVATTR,
-                                                  localName,
-                                                  value );
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素からxsd:float型属性値を読み取る。
-     * @param elem 要素
-     * @param nsuri 名前空間URI
-     * @param localName 属性名
-     * @return float値
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static float getFloatAttrNS(Element elem,
-                                       String nsuri,
-                                       String localName )
-            throws TogaXmlException{
-        String value = getStringAttrNS(elem, nsuri, localName);
-
-        float result;
-        try{
-            result = DatatypeIo.parseFloat(value);
-        }catch(NumberFormatException e){
-            String message = MessageFormat.format(ERRMSG_INVATTR,
-                                                  localName,
-                                                  value );
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-}
diff --git a/src/main/java/jp/sfjp/mikutoga/xml/DomUtils.java b/src/main/java/jp/sfjp/mikutoga/xml/DomUtils.java
deleted file mode 100644 (file)
index 1861728..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * XML DOM utilities
- *
- * License : The MIT License
- * Copyright(c) 2010 MikuToga Partners
- */
-
-package jp.sfjp.mikutoga.xml;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.NoSuchElementException;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-/**
- * DOMユーティリティ。
- */
-public final class DomUtils {
-
-    // 構文解析バグ回避。
-    private static final char BS_CHAR = (char) 0x005c;
-
-    /**
-     * 隠しコンストラクタ。
-     */
-    private DomUtils(){
-        super();
-        assert false;
-        throw new AssertionError();
-    }
-
-    /**
-     * 要素からxsd:string型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return 文字列
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static String getStringAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        if( ! elem.hasAttribute(attrName) ){
-            String message = "Attr:[" + attrName + "] "
-                    + "was not found in "
-                    + "Elem:[" + elem.getTagName()+"]";
-            throw new TogaXmlException(message);
-        }
-
-        String result;
-        try{
-            result = elem.getAttribute(attrName);
-        }catch(IllegalArgumentException e){
-            String message = "Invalid attribute form [" + attrName + "]";
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素からxsd:boolean型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return 真ならtrue
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static boolean getBooleanAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        String value = getStringAttr(elem, attrName);
-
-        boolean result;
-        try{
-            result = DatatypeIo.parseBoolean(value);
-        }catch(IllegalArgumentException e){
-            String message =
-                    "Invalid boolean attribute form "
-                    + "[" + attrName + "][" + value + "]";
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素からxsd:integer型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return int値
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static int getIntegerAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        String value = getStringAttr(elem, attrName);
-
-        int result;
-        try{
-            result = DatatypeIo.parseInt(value);
-        }catch(IllegalArgumentException e){
-            String message =
-                    "Invalid integer attribute form "
-                    + "[" + attrName + "][" + value + "]";
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素からxsd:float型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return float値
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static float getFloatAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        String value = getStringAttr(elem, attrName);
-
-        float result;
-        try{
-            result = DatatypeIo.parseFloat(value);
-        }catch(IllegalArgumentException e){
-            String message =
-                    "Invalid float attribute form "
-                    + "[" + attrName + "][" + value + "]";
-            throw new TogaXmlException(message, e);
-        }
-
-        return result;
-    }
-
-    /**
-     * 要素から日本語Windows用ファイル名を属性値として読み取る。
-     * 念のため文字U+00A5は文字U-005Cに変換される。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return ファイル名
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    public static String getSjisFileNameAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        String result;
-        try{
-            result = getStringAttr(elem, attrName);
-        }catch(IllegalArgumentException e){
-            String message =
-                    "Invalid winfile attribute form "
-                    + "[" + attrName + "]";
-            throw new TogaXmlException(message, e);
-        }
-
-        result = result.replace("" + '\u00a5', "" + BS_CHAR);
-
-        return result;
-    }
-
-    /**
-     * 指定された名前の子要素を1つだけ返す。
-     * @param parent 親要素
-     * @param tagName 子要素名
-     * @return 子要素
-     * @throws TogaXmlException 1つも見つからなかった
-     */
-    public static Element getChild(Element parent, String tagName)
-            throws TogaXmlException{
-        Element result = null;
-
-        for(Node node = parent.getFirstChild();
-            node != null;
-            node = node.getNextSibling() ){
-
-            if(node.getNodeType() != Node.ELEMENT_NODE) continue;
-            Element elem = (Element) node;
-
-            String elemTagName = elem.getTagName();
-            if( tagName.equals(elemTagName) ){
-                result = elem;
-                break;
-            }
-        }
-
-        if(result == null){
-            String message =
-                    "Elem:[" + tagName + "] was not found in "
-                    +"Elem:[" + parent.getTagName() + "]";
-            throw new TogaXmlException(message);
-        }
-
-        return result;
-    }
-
-    /**
-     * 親要素が指定された名前の子要素を持つか判定する。
-     * @param parent 親要素
-     * @param tagName 子要素名
-     * @return 指定名の子要素が存在すればtrue
-     */
-    public static boolean hasChild(Element parent, String tagName){
-        for(Node node = parent.getFirstChild();
-            node != null;
-            node = node.getNextSibling() ){
-
-            if(node.getNodeType() != Node.ELEMENT_NODE) continue;
-            Element elem = (Element) node;
-
-            String elemTagName = elem.getTagName();
-            if( tagName.equals(elemTagName) ) return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * 指定された名前の子要素のリストを返す。
-     * @param parent 親要素
-     * @param childTag 子要素名
-     * @return 子要素のリスト
-     */
-    public static List<Element> getChildList(Element parent,
-                                               String childTag){
-        List<Element> result = new LinkedList<>();
-
-        for(Node node = parent.getFirstChild();
-            node != null;
-            node = node.getNextSibling() ){
-
-            if(node.getNodeType() != Node.ELEMENT_NODE) continue;
-            Element elem = (Element) node;
-
-            String tagName = elem.getTagName();
-            if( ! childTag.equals(tagName) ) continue;
-
-            result.add(elem);
-        }
-
-        return result;
-    }
-
-    /**
-     * 指定された名前の子要素の列挙子を返す。
-     * @param parent 親要素
-     * @param childTag 子要素名
-     * @return 子要素の列挙子
-     */
-    public static Iterator<Element> getChildIterator(Element parent,
-                                                       String childTag){
-        Element firstElem;
-        try{
-            firstElem = getChild(parent, childTag);
-        }catch(TogaXmlException e){
-            firstElem = null;
-        }
-
-        Iterator<Element> result = new ElemIterator(firstElem);
-
-        return result;
-    }
-
-    /**
-     * 指定された名前の子要素のforeachを返す。
-     * @param parent 親要素
-     * @param childTag 子要素名
-     * @return 子要素のforeach
-     */
-    public static Iterable<Element> getEachChild(Element parent,
-                                                   String childTag){
-        final Iterator<Element> iterator = getChildIterator(parent, childTag);
-        Iterable<Element> result = new Iterable<Element>(){
-            @Override
-            public Iterator<Element> iterator(){
-                return iterator;
-            }
-        };
-        return result;
-    }
-
-    /**
-     * 要素の次の要素を返す。
-     * @param elem 要素
-     * @return 次の要素。なければnull
-     */
-    public static Element nextElement(Element elem){
-        Node nextNode = elem;
-        for(;;){
-            nextNode = nextNode.getNextSibling();
-            if(nextNode == null) break;
-            if(nextNode.getNodeType() == Node.ELEMENT_NODE){
-                break;
-            }
-        }
-
-        return (Element) nextNode;
-    }
-
-    /**
-     * 同じ要素名を持つ次の要素を返す。
-     * @param elem 要素
-     * @return 次の要素。なければnull
-     */
-    public static Element nextNamedElement(Element elem){
-        String tagName = elem.getTagName();
-        Element nextElem = elem;
-        for(;;){
-            nextElem = nextElement(nextElem);
-            if(nextElem == null) break;
-            if(tagName.equals(nextElem.getTagName())) break;
-        }
-
-        return nextElem;
-    }
-
-    /**
-     * 同じ親要素と同じ要素名を持つ兄弟要素を列挙する列挙子。
-     */
-    private static final class ElemIterator implements Iterator<Element> {
-        private Element next;
-
-        /**
-         * コンストラクタ。
-         * @param elem 最初の要素。nullを指定すれば空列挙子となる。
-         */
-        ElemIterator(Element elem){
-            super();
-            this.next = elem;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public boolean hasNext(){
-            if(this.next == null) return false;
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         * @throws NoSuchElementException {@inheritDoc}
-         */
-        @Override
-        public Element next() throws NoSuchElementException{
-            if(this.next == null) throw new NoSuchElementException();
-            Element result = this.next;
-            this.next = nextNamedElement(this.next);
-            return result;
-        }
-
-        /**
-         * {@inheritDoc}
-         * ※ 未サポート。
-         */
-        @Override
-        public void remove(){
-            throw new UnsupportedOperationException();
-        }
-
-    }
-
-}
diff --git a/src/main/java/jp/sfjp/mikutoga/xml/LocalXmlResource.java b/src/main/java/jp/sfjp/mikutoga/xml/LocalXmlResource.java
deleted file mode 100644 (file)
index 0ccccd4..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * xml local resource map
- *
- * License : The MIT License
- * Copyright(c) 2013 olyutorskii
- */
-
-package jp.sfjp.mikutoga.xml;
-
-import java.net.URI;
-
-/**
- * 代用ローカルリソースの管理を行う。
- *
- * <p>ネットワークを介したグローバルなリソースと、
- * アプリ上のローカルな代用リソースとを対応付ける。
- */
-public interface LocalXmlResource {
-
-    /**
-     * オリジナル版XMLリソースのURIを返す。
-     * @return オリジナル版リソースのURL。
-     */
-    public abstract URI getOriginalResource();
-
-    /**
-     * ローカル版XMLリソースのURIを返す。
-     * @return ローカル版リソースのURL。
-     */
-    public abstract URI getLocalResource();
-
-}
diff --git a/src/main/java/jp/sfjp/mikutoga/xml/NoopEntityResolver.java b/src/main/java/jp/sfjp/mikutoga/xml/NoopEntityResolver.java
new file mode 100644 (file)
index 0000000..7048e0e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * No-operation Entity Resolver for XML.
+ *
+ * License : The MIT License
+ * Copyright(c) 2019 olyutorskii
+ */
+
+package jp.sfjp.mikutoga.xml;
+
+import java.io.Reader;
+import java.io.StringReader;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * No-operation Entity Resolver implementation for preventing XXE.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/XML_external_entity_attack">
+ *     XML external entity attack (Wikipedia)
+ *     </a>
+ */
+public final class NoopEntityResolver implements EntityResolver{
+
+    /** Singleton resolver. */
+    public static final EntityResolver NOOP_RESOLVER =
+            new NoopEntityResolver();
+
+
+    /**
+     * Constructor.
+     */
+    private NoopEntityResolver(){
+        super();
+        return;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Prevent any external entity reference XXE.
+     *
+     * @param publicId {@inheritDoc}
+     * @param systemId {@inheritDoc}
+     * @return empty input source
+     */
+    @Override
+    public InputSource resolveEntity(String publicId, String systemId){
+        Reader emptyReader = new StringReader("");
+        InputSource source = new InputSource(emptyReader);
+
+        source.setPublicId(publicId);
+        source.setSystemId(systemId);
+
+        return source;
+    }
+
+}
index bd1541e..c0f9eb2 100644 (file)
@@ -10,11 +10,9 @@ package jp.sfjp.mikutoga.xml;
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
 import javax.xml.XMLConstants;
 import javax.xml.transform.Source;
 import javax.xml.transform.stream.StreamSource;
@@ -22,131 +20,166 @@ import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
 import org.w3c.dom.ls.LSResourceResolver;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
 
 /**
- * XMLスキーマの各種ビルダ。
+ * XML schema (XSD) utilities.
+ *
+ * <p>RELAX NG is not supported.
  */
 public final class SchemaUtil {
 
-    /**
-     * 隠しコンストラクタ。
-     */
-    private SchemaUtil(){
-        assert false;
-        throw new AssertionError();
-    }
 
+    /** XML Schema. */
+    public static final String SCHEMA_XML =
+            "http://www.w3.org/2001/xml.xsd";
 
-    /**
-     * XML Schema 用のスキーマファクトリを返す。
-     * @return スキーマファクトリ
-     */
-    public static SchemaFactory newSchemaFactory(){
-        SchemaFactory result = newSchemaFactory(null);
-        return result;
-    }
+    /** XSD namespace. */
+    public static final String NS_XSD =
+            "http://www.w3.org/2001/XMLSchema-instance";
 
-    /**
-     * XML Schema 用のスキーマファクトリを返す。
-     * @param resolver カスタムリゾルバ。nullも可。
-     * @return スキーマファクトリ
-     */
-    public static SchemaFactory newSchemaFactory(
-            LSResourceResolver resolver ){
-        SchemaFactory schemaFactory =
-                SchemaFactory.newInstance(
-                    XMLConstants.W3C_XML_SCHEMA_NS_URI
-                );
+    private static final String LOCAL_SCHEMA_XML =
+            "resources/xmlspace.xsd";
 
-        // schemaFactory.setFeature(name, value);
-        // schemaFactory.setProperty(name, object);
+    private static final URI URI_XSD_ORIG;
+    private static final URI URI_XSD_LOCAL;
 
-        schemaFactory.setErrorHandler(BotherHandler.HANDLER);
-        schemaFactory.setResourceResolver(resolver);
+    private static final String ALLOWED_USCHEMA = "http";
 
-        return schemaFactory;
+    private static final Class<?> THISCLASS = SchemaUtil.class;
+
+
+    static{
+        URL localXsdUrl = THISCLASS.getResource(LOCAL_SCHEMA_XML);
+        URI localXsdUri;
+        try{
+            localXsdUri = localXsdUrl.toURI();
+        }catch(URISyntaxException e){
+            throw new ExceptionInInitializerError(e);
+        }
+
+        URI_XSD_ORIG  = URI.create(SCHEMA_XML);
+        URI_XSD_LOCAL = localXsdUri;
+
+        assert ALLOWED_USCHEMA.equalsIgnoreCase(URI_XSD_ORIG.getScheme());
     }
 
+
     /**
-     * ローカルリソースをSourceに変換する。
-     * @param resource ローカルリソース
-     * @return XML Source
-     * @throws MalformedURLException 不正なURI
-     * @throws IOException オープンエラー
+     * Hidden constructor.
      */
-    private static Source toLocalSource(LocalXmlResource resource)
-            throws MalformedURLException, IOException{
-        URI localUri = resource.getLocalResource();
-        URL localUrl = localUri.toURL();
+    private SchemaUtil(){
+        assert false;
+        throw new AssertionError();
+    }
 
-        InputStream is = localUrl.openStream();
-        is = new BufferedInputStream(is);
 
-        Source result = new StreamSource(is);
+    /**
+     * build xml.xsd redirection info.
+     *
+     * @return resolver
+     */
+    public static XmlResourceResolver buildXmlXsdResolver(){
+        XmlResourceResolver result = new XmlResourceResolver();
+        result.putRedirected(URI_XSD_ORIG, URI_XSD_LOCAL);
         return result;
     }
 
     /**
-     * ローカルリソース群をSource群に変換する。
-     * @param resArray ローカルリソースURI並び
-     * @return XML Source並び
-     * @throws MalformedURLException 不正なURI
-     * @throws IOException オープンエラー
+     * Build SchemaFactory for XML Schema but safety.
+     *
+     * <p>Includes some considerations for XXE vulnerabilities.
+     *
+     * <p>Restrict access to
+     * External Entity Reference &amp; external DTDs
+     * in xml schema file.
+     *
+     * <p>Restrict access to External schema file access in xml schema file,
+     * but HTTP access is allowed.
+     * This special limit considers access to
+     * importing http://www.w3.org/2001/xml.xsd
+     * in top of common xml schema file.
+     *
+     * @return schema factory
      */
-    private static Source[] toLocalSourceArray(LocalXmlResource... resArray)
-            throws MalformedURLException, IOException{
-        List<Source> sourceList = new ArrayList<>(resArray.length);
+    public static SchemaFactory newSchemaFactory(){
+        SchemaFactory schemaFactory;
+        schemaFactory = SchemaFactory.newInstance(
+                XMLConstants.W3C_XML_SCHEMA_NS_URI);
 
-        for(LocalXmlResource resource : resArray){
-            Source localSource = toLocalSource(resource);
-            sourceList.add(localSource);
+        try{
+            // Prevent denial-of-service attack.
+            schemaFactory.setFeature(
+                    XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        }catch(SAXNotRecognizedException | SAXNotSupportedException e){
+            // THIS FEATURE MUST BE SUPPORTED
+            assert false;
         }
 
-        Source[] result = new Source[sourceList.size()];
-        result = sourceList.toArray(result);
-        return result;
+        try{
+            // Disallow external entity reference & external DTD access.
+            schemaFactory.setProperty(
+                    XMLConstants.ACCESS_EXTERNAL_DTD, "");
+            // Allow only HTTP external schema file.
+            schemaFactory.setProperty(
+                    XMLConstants.ACCESS_EXTERNAL_SCHEMA, ALLOWED_USCHEMA);
+        }catch(SAXNotRecognizedException | SAXNotSupportedException e){
+            // THIS PROPERTY MUST BE SUPPORTED JAXP1.5 or later
+            assert false;
+        }
+
+        LSResourceResolver resolver = buildXmlXsdResolver();
+        schemaFactory.setResourceResolver(resolver);
+
+        schemaFactory.setErrorHandler(BotherHandler.HANDLER);
+
+        return schemaFactory;
     }
 
     /**
-     * ローカルスキーマをロードする。
+     * pre-load &amp; pre-compile local schema files.
      *
-     * <p>任意のリゾルバを指定可能
-     *
-     * @param resolver リゾルバ
-     * @param resArray ローカルスキーマ情報並び
-     * @return スキーマ
+     * @param localSchemaUris local schema resources.
+     * @return schema
+     * @throws SAXException invalid schema definition
+     * @throws IOException local resource i/o error
      */
-    public static Schema newSchema(XmlResourceResolver resolver,
-                                    LocalXmlResource... resArray ){
-        for(LocalXmlResource resource : resArray){
-            resolver.putRedirected(resource);
+    public static Schema newSchema(URI... localSchemaUris)
+            throws SAXException, IOException{
+        SchemaFactory schemaFactory = newSchemaFactory();
+
+        int uris = localSchemaUris.length;
+        if(uris <= 0){
+            // on-demand xml-schema with schemaLocation attribute.
+            return schemaFactory.newSchema();
         }
 
-        Source[] sources;
-        try{
-            sources = toLocalSourceArray(resArray);
-        }catch(IOException e){                   // ビルド障害
-            assert false;
-            throw new AssertionError(e);
+        InputStream[] iss = new InputStream[uris];
+        for(int idx = 0; idx < uris; idx++){
+            URI localUri = localSchemaUris[idx];
+            URL localUrl = localUri.toURL();
+            InputStream is;
+            is = localUrl.openStream();
+            is = new BufferedInputStream(is);
+            iss[idx] = is;
         }
 
-        SchemaFactory schemaFactory = newSchemaFactory(resolver);
+        Source[] sources = new Source[uris];
+        for(int idx = 0; idx < uris; idx++){
+            InputStream is = iss[idx];
+            sources[idx] = new StreamSource(is);
+        }
 
         Schema result;
         try{
-            if(sources.length <= 0){
-                // ドキュメント埋め込みスキーマURLにリゾルバ経由でアクセス
-                result = schemaFactory.newSchema();
-            }else{
-                result = schemaFactory.newSchema(sources);
+            result = schemaFactory.newSchema(sources);
+        }finally{
+            for(InputStream is : iss){
+                is.close();
             }
-        }catch(SAXException e){   // Build error
-            assert false;
-            throw new AssertionError(e);
         }
 
-        // TODO: Sourceを閉めるのは誰の責務?
-
         return result;
     }
 
diff --git a/src/main/java/jp/sfjp/mikutoga/xml/SiblingElemIterator.java b/src/main/java/jp/sfjp/mikutoga/xml/SiblingElemIterator.java
deleted file mode 100644 (file)
index 63912bc..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * sibling element iterator on DOM tree
- *
- * License : The MIT License
- * Copyright(c) 2011 MikuToga Partners
- */
-
-package jp.sfjp.mikutoga.xml;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-/**
- * 兄弟要素間用Iterator。
- *
- * <p>同じ親と名前空間とローカル名を持つ要素同士を「兄弟要素」とする。
- *
- * <p>ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。
- *
- * <p>削除操作は未サポート。
- */
-public class SiblingElemIterator implements Iterator<Element> {
-
-    private Element next;
-    private final String nsuri;
-    private final String localName;
-
-
-    /**
-     * コンストラクタ。
-     * @param first 最初の兄弟要素。nullだと一度もiterateしない。
-     */
-    public SiblingElemIterator(Element first){
-        super();
-
-        this.next = first;
-
-        if(this.next == null){
-            this.nsuri     = null;
-            this.localName = null;
-        }else{
-            this.nsuri     = this.next.getNamespaceURI();
-            this.localName = this.next.getLocalName();
-        }
-
-        return;
-    }
-
-    /**
-     * コンストラクタ。
-     *
-     * <p>名前空間引数にnullが渡された場合、全ての名前空間にマッチする。
-     *
-     * <p>ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。
-     *
-     * @param parent 親要素
-     * @param nsuri 子要素の名前空間URI
-     * @param localName 子要素のローカル名
-     */
-    public SiblingElemIterator(Element parent,
-                               String nsuri,
-                               String localName ){
-        super();
-
-        this.next = DomNsUtils.pickFirstChild(parent, nsuri, localName);
-
-        if(this.next == null){
-            this.nsuri     = null;
-            this.localName = null;
-        }else{
-            this.nsuri     = nsuri;
-            this.localName = localName;
-        }
-
-        return;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     * @return {@inheritDoc}
-     */
-    @Override
-    public boolean hasNext(){
-        if(this.next != null) return true;
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @return {@inheritDoc}
-     * @throws NoSuchElementException {@inheritDoc}
-     */
-    @Override
-    public Element next() throws NoSuchElementException {
-        if(this.next == null) throw new NoSuchElementException();
-
-        Element result = this.next;
-
-        Node sibNode = result;
-        do{
-            sibNode = sibNode.getNextSibling();
-            if(sibNode == null) break;
-        }while( ! matchElemName(sibNode) );
-        this.next = (Element) sibNode;
-
-        return result;
-    }
-
-    /**
-     * 兄弟要素にふさわしい名前を持つか判定する。
-     * @param node 判定対象
-     * @return 兄弟にふさわしい名前を持つならtrue
-     */
-    private boolean matchElemName(Node node){
-        return DomNsUtils.hasNsLocalNameElem(node,
-                                             this.nsuri, this.localName );
-    }
-
-    /**
-     * {@inheritDoc}
-     * ※削除不可。
-     * @throws UnsupportedOperationException 削除を試みたので失敗した
-     */
-    @Override
-    public void remove() throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-}
index 196a900..bf5ba8e 100644 (file)
 
 package jp.sfjp.mikutoga.xml;
 
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.Reader;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import org.w3c.dom.ls.DOMImplementationLS;
 import org.w3c.dom.ls.LSInput;
 import org.w3c.dom.ls.LSResourceResolver;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
 
 /**
- * URL変換マップに従い、XML文書からの外部参照をリダイレクトする。
- * 相対URIはこのクラスをベースに解決される。
- * 主な用途は外部スキーマのリソース化など。
+ * register &amp; redirect original URI to local resource contents.
  */
 public class XmlResourceResolver
-        implements LSResourceResolver, EntityResolver {
-
-    /** XML Schema. */
-    public static final String SCHEMA_XML =
-            "http://www.w3.org/2001/xml.xsd";
-
-    /** XSD名前空間。 */
-    public static final String NS_XSD =
-            "http://www.w3.org/2001/XMLSchema-instance";
-
-    private static final String LOCAL_SCHEMA_XML =
-            "resources/xmlspace.xsd";
+        implements LSResourceResolver{
 
     private static final URI EMPTY_URI = URI.create("");
 
-    private static final Class<?> THISCLASS = XmlResourceResolver.class;
+    private static final DOMImplementationLS DOM_LS;
 
 
     private final Map<URI, URI> uriMap;
 
 
+    static{
+        try{
+            DOM_LS = buildDomImplLS();
+        }catch(   ClassNotFoundException
+                | IllegalAccessException
+                | InstantiationException e){
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+
     /**
-     * コンストラクタ。
+     * Constructor.
      */
     public XmlResourceResolver(){
         super();
 
-        assert this.getClass().equals(THISCLASS);
-
         Map<URI, URI> map;
         map = new HashMap<>();
         map = Collections.synchronizedMap(map);
         this.uriMap = map;
 
-        URL redirectRes = THISCLASS.getResource(LOCAL_SCHEMA_XML);
-        String redirectResName = redirectRes.toString();
-
-        URI originalURI = URI.create(SCHEMA_XML);
-        URI redirectURI = URI.create(redirectResName);
+        return;
+    }
 
-        putRedirectedImpl(originalURI, redirectURI);
 
-        return;
+    /**
+     * return DOMImplementationLS implement.
+     *
+     * @return DOMImplementationLS implement
+     * @throws ClassNotFoundException no class
+     * @throws InstantiationException no object
+     * @throws IllegalAccessException no grant
+     */
+    private static DOMImplementationLS buildDomImplLS() throws
+            ClassNotFoundException,
+            InstantiationException,
+            IllegalAccessException {
+        DOMImplementationRegistry domReg;
+        DOMImplementation         domImp;
+        DOMImplementationLS       domImpLs;
+
+        domReg = DOMImplementationRegistry.newInstance();
+        domImp = domReg.getDOMImplementation("LS 3.0");
+
+        Object feature = domImp.getFeature("LS", "3.0");
+        assert feature instanceof DOMImplementationLS;
+        domImpLs = (DOMImplementationLS) feature;
+
+        return domImpLs;
     }
 
+    /**
+     * return LSInput implement.
+     *
+     * @return LSInput implement
+     */
+    public static LSInput createLSInput(){
+        LSInput input = DOM_LS.createLSInput();
+        return input;
+    }
 
     /**
-     * 絶対URIと相対URIを合成したURIを返す。
-     * 正規化も行われる。
+     * merge base-uri text &amp; relative URI text.
      *
-     * @param base 絶対URIでなければならない。nullでもよい。
-     * @param relative 絶対URIでもよいがその場合baseは無視される。null可。
-     * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
-     * @throws java.net.URISyntaxException URIとして変。
-     * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
+     * @param base base URI text must be absolute or null.
+     * @param relative relative URI text.
+     *     If absolute, base is ignored.
+     *     Ignored if null.
+     * @return merged absolute URI.
+     * @throws java.net.URISyntaxException illegal URI
+     * @throws java.lang.IllegalArgumentException result is not Absolute.
      */
     protected static URI buildBaseRelativeURI(String base, String relative)
             throws URISyntaxException,
@@ -103,19 +129,18 @@ public class XmlResourceResolver
         }else{
             relativeURI = EMPTY_URI;
         }
-        
+
         URI result = buildBaseRelativeURI(baseURI, relativeURI);
         return result;
     }
 
     /**
-     * 絶対URIと相対URIを合成したURIを返す。
-     * 正規化も行われる。
+     * merge base-uri &amp; relative URI.
      *
-     * @param baseURI 絶対URIでなければならない。nullでもよい。
-     * @param relativeURI 絶対URIでもよいがその場合baseは無視される。
-     * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
-     * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
+     * @param baseURI base URI must be absolute or null.
+     * @param relativeURI relative URI. If absolute, baseURI is ignored.
+     * @return merged absolute URI.
+     * @throws java.lang.IllegalArgumentException result is not Absolute.
      */
     private static URI buildBaseRelativeURI(URI baseURI, URI relativeURI)
             throws IllegalArgumentException {
@@ -136,23 +161,14 @@ public class XmlResourceResolver
         return resultURI;
     }
 
-    /**
-     * LSInput実装を生成する。
-     * @return LSInput実装
-     */
-    public static LSInput createLSInput(){
-        LSInput input = new LSInputImpl();
-        return input;
-    }
-
 
     /**
-     * オリジナルURIとリダイレクト先のURIを登録する。
-     * オリジナルURIへのアクセスはリダイレクトされる。
-     * @param original オリジナルURI
-     * @param redirect リダイレクトURI
+     * map original URI &amp; local resource URI.
+     *
+     * @param original original URI
+     * @param redirect local resource URI
      */
-    private void putRedirectedImpl(URI original, URI redirect){
+    public void putRedirected(URI original, URI redirect){
         URI oridinalNorm = original.normalize();
         URI redirectNorm = redirect.normalize();
 
@@ -162,34 +178,9 @@ public class XmlResourceResolver
     }
 
     /**
-     * オリジナルURIとリダイレクト先のURIを登録する。
-     * オリジナルURIへのアクセスはリダイレクトされる。
-     * @param original オリジナルURI
-     * @param redirect リダイレクトURI
-     */
-    public void putRedirected(URI original, URI redirect){
-        putRedirectedImpl(original, redirect);
-        return;
-    }
-
-    /**
-     * ローカル版リソース参照解決を登録する。
-     * @param lsc ローカル版リソース参照解決
-     */
-    public void putRedirected(LocalXmlResource lsc){
-        URI original = lsc.getOriginalResource();
-        if(original == null) return;
-
-        URI local = lsc.getLocalResource();
-
-        putRedirected(original, local);
-
-        return;
-    }
-
-    /**
-     * 別リゾルバの登録内容を追加登録する。
-     * @param other 別リゾルバ
+     * add other resolver mapping.
+     *
+     * @param other resolver
      */
     public void putRedirected(XmlResourceResolver other){
         this.uriMap.putAll(other.uriMap);
@@ -197,9 +188,10 @@ public class XmlResourceResolver
     }
 
     /**
-     * 登録済みリダイレクト先URIを返す。
-     * @param original オリジナルURI
-     * @return リダイレクト先URI。未登録の場合はnull
+     * get redirected local resource URI.
+     *
+     * @param original original URI
+     * @return mapped local resource URI. null if unmapped.
      */
     public URI getRedirected(URI original){
         URI keyURI = original.normalize();
@@ -208,66 +200,67 @@ public class XmlResourceResolver
     }
 
     /**
-     * 登録済みリダイレクト先URIを返す。
-     * @param original オリジナルURI
-     * @return リダイレクト先URI。未登録の場合はオリジナルを返す
-     */
-    public URI resolveRedirected(URI original){
-        URI result = getRedirected(original);
-        if(result == null) result = original;
-        return result;
-    }
-
-    /**
-     * 登録済みリダイレクト先リソースの入力ストリームを得る。
-     * @param originalURI オリジナルURI
-     * @return 入力ストリーム。リダイレクト先が未登録の場合はnull
-     * @throws java.io.IOException 入出力エラー。
-     *     もしくはリソースが見つからない。
+     * get redirected local input stream.
+     *
+     * @param originalURI original URI
+     * @return mapped local resource input stream.
+     *     If no mapping, return zero-length data stream.
+     * @throws java.io.IOException local resource i/o error
      */
     private InputStream getXMLResourceAsStream(URI originalURI)
             throws IOException{
+        InputStream result;
+
         URI resourceURI = getRedirected(originalURI);
-        if(resourceURI == null) return null;
+        if(resourceURI == null){
+            result = new ByteArrayInputStream(new byte[0]);
+        }else{
+            URL resourceURL = resourceURI.toURL();
+            result = resourceURL.openStream();
+        }
 
-        URL resourceURL = resourceURI.toURL();
-        InputStream is = resourceURL.openStream();
+        result = new BufferedInputStream(result);
 
-        return is;
+        return result;
     }
 
     /**
      * {@inheritDoc}
-     * URL変換したあとの入力ソースを返す。
+     *
+     * <p>Return redirected local resource data.
+     *
      * @param type {@inheritDoc}
      * @param namespaceURI {@inheritDoc}
      * @param publicId {@inheritDoc}
      * @param systemId {@inheritDoc}
      * @param baseURI {@inheritDoc}
-     * @return {@inheritDoc}
+     * @return {@inheritDoc} LSInput of local resource.
+     *     If no mapping, return zero-length data.
      */
     @Override
-    public LSInput resolveResource(String type,
-                                     String namespaceURI,
-                                     String publicId,
-                                     String systemId,
-                                     String baseURI ){
+    public LSInput resolveResource(
+            String type,
+            String namespaceURI,
+            String publicId,
+            String systemId,
+            String baseURI ){
         if(systemId == null) return null;
 
         URI originalURI;
         try{
             originalURI = buildBaseRelativeURI(baseURI, systemId);
         }catch(URISyntaxException e){
-            return null;
+            assert false;
+            throw new AssertionError(e);
         }
 
         InputStream is;
         try{
             is = getXMLResourceAsStream(originalURI);
         }catch(IOException e){
-            return null;
+            assert false;
+            throw new AssertionError(e);
         }
-        if(is == null) return null;
 
         LSInput input = createLSInput();
         input.setBaseURI(baseURI);
@@ -278,212 +271,4 @@ public class XmlResourceResolver
         return input;
     }
 
-    /**
-     * {@inheritDoc}
-     * URL変換したあとの入力ソースを返す。
-     * @param publicId {@inheritDoc}
-     * @param systemId {@inheritDoc}
-     * @return {@inheritDoc}
-     * @throws org.xml.sax.SAXException {@inheritDoc}
-     * @throws java.io.IOException {@inheritDoc}
-     */
-    @Override
-    public InputSource resolveEntity(String publicId, String systemId)
-            throws SAXException, IOException{
-        if(systemId == null) return null;
-
-        URI originalUri;
-        try{
-            originalUri = new URI(systemId);
-        }catch(URISyntaxException e){
-            return null;
-        }
-
-        InputStream is = getXMLResourceAsStream(originalUri);
-        if(is == null) return null;
-
-        InputSource source = new InputSource(is);
-        source.setPublicId(publicId);
-        source.setSystemId(systemId);
-
-        return source;
-    }
-
-    /**
-     * JRE1.5用LSInput実装。
-     * JRE1.6なら
-     * org.w3c.dom.ls.DOMImplementationLS#createLSInput()
-     * で生成可能かも。
-     */
-    private static final class LSInputImpl implements LSInput {
-
-        private String baseURI = null;
-        private InputStream byteStream = null;
-        private boolean certifiedText = false;
-        private Reader characterStream = null;
-        private String encoding = null;
-        private String publicId = null;
-        private String stringData = null;
-        private String systemId = null;
-
-        /**
-         * コンストラクタ。
-         */
-        LSInputImpl(){
-            super();
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public String getBaseURI(){
-            return this.baseURI;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param baseURI {@inheritDoc}
-         */
-        @Override
-        public void setBaseURI(String baseURI){
-            this.baseURI = baseURI;
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public InputStream getByteStream(){
-            return this.byteStream;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param byteStream {@inheritDoc}
-         */
-        @Override
-        public void setByteStream(InputStream byteStream){
-            this.byteStream = byteStream;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public boolean getCertifiedText(){
-            return this.certifiedText;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param certifiedText {@inheritDoc}
-         */
-        @Override
-        public void setCertifiedText(boolean certifiedText){
-            this.certifiedText = certifiedText;
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public Reader getCharacterStream(){
-            return this.characterStream;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param characterStream {@inheritDoc}
-         */
-        @Override
-        public void setCharacterStream(Reader characterStream){
-            this.characterStream = characterStream;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public String getEncoding(){
-            return this.encoding;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param encoding {@inheritDoc}
-         */
-        @Override
-        public void setEncoding(String encoding){
-            this.encoding = encoding;
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public String getPublicId(){
-            return this.publicId;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param publicId {@inheritDoc}
-         */
-        @Override
-        public void setPublicId(String publicId){
-            this.publicId = publicId;
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public String getStringData(){
-            return this.stringData;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param stringData {@inheritDoc}
-         */
-        @Override
-        public void setStringData(String stringData){
-            this.stringData = stringData;
-            return;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @return {@inheritDoc}
-         */
-        @Override
-        public String getSystemId(){
-            return this.systemId;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @param systemId {@inheritDoc}
-         */
-        @Override
-        public void setSystemId(String systemId){
-            this.systemId = systemId;
-            return;
-        }
-
-    }
-
 }
diff --git a/src/test/java/jp/sfjp/mikutoga/xml/DomUtilsTest.java b/src/test/java/jp/sfjp/mikutoga/xml/DomUtilsTest.java
deleted file mode 100644 (file)
index f319a31..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
-@see https://www.w3.org/TR/xmlschema-2/
- */
-
-package jp.sfjp.mikutoga.xml;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import static org.junit.Assert.*;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/**
- *
- */
-public class DomUtilsTest {
-
-    private static final DocumentBuilderFactory FACTORY =
-            DocumentBuilderFactory.newInstance();
-    private static final DocumentBuilder BUILDER;
-
-    private static final String TESTELEM = "testelem";
-    private static final String TESTATTR = "testattr";
-
-    private static Element getTestAttredElem(String attrVal){
-        Document doc = BUILDER.newDocument();
-        Element elem = doc.createElement(TESTELEM);
-        elem.setAttribute(TESTATTR, attrVal);
-        return elem;
-    }
-
-    static{
-        try{
-            BUILDER = FACTORY.newDocumentBuilder();
-        }catch(ParserConfigurationException e){
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
-    public DomUtilsTest() {
-    }
-
-    @BeforeClass
-    public static void setUpClass() throws ParserConfigurationException{
-    }
-
-    @AfterClass
-    public static void tearDownClass() {
-    }
-
-    @Before
-    public void setUp() {
-    }
-
-    @After
-    public void tearDown() {
-    }
-
-    /**
-     * Test of getBooleanAttr method, of class DomUtils.
-     */
-    @Test
-    public void testGetBooleanAttr() throws Exception {
-        System.out.println("getBooleanAttr");
-
-        boolean result;
-        Element elem;
-
-        elem = getTestAttredElem("true");
-        result = DomUtils.getBooleanAttr(elem, TESTATTR);
-        assertTrue(result);
-
-        elem = getTestAttredElem("false");
-        result = DomUtils.getBooleanAttr(elem, TESTATTR);
-        assertFalse(result);
-
-        elem = getTestAttredElem("0");
-        result = DomUtils.getBooleanAttr(elem, TESTATTR);
-        assertFalse(result);
-
-        elem = getTestAttredElem("1");
-        result = DomUtils.getBooleanAttr(elem, TESTATTR);
-        assertTrue(result);
-
-        elem = getTestAttredElem("\n\rtrue\u0020\t");
-        result = DomUtils.getBooleanAttr(elem, TESTATTR);
-        assertTrue(result);
-
-        elem = getTestAttredElem("?");
-        try{
-            DomUtils.getBooleanAttr(elem, TESTATTR);
-            fail();
-        }catch(TogaXmlException e){
-            assert true;
-        }
-
-        return;
-    }
-
-    /**
-     * Test of getIntegerAttr method, of class DomUtils.
-     */
-    @Test
-    public void testGetIntegerAttr() throws TogaXmlException {
-        System.out.println("getIntegerAttr");
-
-        int result;
-        Element elem;
-
-        elem = getTestAttredElem("0");
-        result = DomUtils.getIntegerAttr(elem, TESTATTR);
-        assertEquals(0, result);
-
-        elem = getTestAttredElem("1");
-        result = DomUtils.getIntegerAttr(elem, TESTATTR);
-        assertEquals(1, result);
-
-        elem = getTestAttredElem("-1");
-        result = DomUtils.getIntegerAttr(elem, TESTATTR);
-        assertEquals(-1, result);
-
-        elem = getTestAttredElem("999");
-        result = DomUtils.getIntegerAttr(elem, TESTATTR);
-        assertEquals(999, result);
-
-        elem = getTestAttredElem("-9999");
-        result = DomUtils.getIntegerAttr(elem, TESTATTR);
-        assertEquals(-9999, result);
-
-        elem = getTestAttredElem("\n\r999\u0020\t");
-        result = DomUtils.getIntegerAttr(elem, TESTATTR);
-        assertEquals(999, result);
-
-        elem = getTestAttredElem("?");
-        try{
-            result = DomUtils.getIntegerAttr(elem, TESTATTR);
-            fail();
-        }catch(TogaXmlException e){
-            assert true;
-        }
-
-        return;
-    }
-
-    /**
-     * Test of getFloatAttr method, of class DomUtils.
-     */
-    @Test
-    public void testGetFloatAttr() throws TogaXmlException {
-        System.out.println("getFloatAttr");
-
-        float result;
-        Element elem;
-
-        elem = getTestAttredElem("0.0");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(0.0f, result, 0.0f);
-
-        elem = getTestAttredElem("-0.0");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(0.0f, result, 0.0f);
-        assertEquals("-0.0", Float.toString(result));
-
-        elem = getTestAttredElem("-123.456");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(-123.456f, result, 0.0f);
-
-        elem = getTestAttredElem("654.321");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(654.321f, result, 0.0f);
-
-        elem = getTestAttredElem("2.3E4");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(23000.0f, result, 0.0f);
-
-        elem = getTestAttredElem("INF");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(Float.POSITIVE_INFINITY, result, 0.0f);
-
-        elem = getTestAttredElem("+INF");
-        try{
-            DomUtils.getFloatAttr(elem, TESTATTR);
-            fail();
-        }catch(TogaXmlException e){
-            assert true;
-        }
-
-        elem = getTestAttredElem("NaN");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertTrue(Float.isNaN(result));
-
-        elem = getTestAttredElem("\n\r123.456\u0020\t");
-        result = DomUtils.getFloatAttr(elem, TESTATTR);
-        assertEquals(123.456f, result, 0.0f);
-
-        elem = getTestAttredElem("?");
-        try{
-            DomUtils.getFloatAttr(elem, TESTATTR);
-            fail();
-        }catch(TogaXmlException e){
-            assert true;
-        }
-
-        return;
-    }
-
-}