• R/O
  • HTTP
  • SSH
  • HTTPS

TogaGem: Commit

TogaGemは、3D動画制作ツール、MikuMikuDance(MMD)で用いられる各種データファイルを読み書きするためのJavaライブラリです。
旧TogaParserライブラリの資産は、TogaGemライブラリに吸収されました。


Commit MetaInfo

Revisioned542b5a7123483f5abaa08d326cb42b32e4b465 (tree)
Zeit2019-06-30 01:25:42
AutorOlyutorskii <olyutorskii@user...>
CommiterOlyutorskii

Log Message

Merge topic/xxe into develop

Ändern Zusammenfassung

Diff

--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,6 +4,12 @@
44 TogaGem 変更履歴
55
66
7+X.XXX.X (XXXX-XX-XX)
8+ * Prevent XXE vulnerabilities.
9+ * Split entity resolver from resource resolver to prevent XXE vulnerability.
10+ * Make Schema-factory safe to prevent XXE vulnerability.
11+ * Move out xml-xsd info from resolver.
12+
713 3.121.2 (2019-06-06)
814 ・DatatypeIo is public now, for replacing JAXB.
915
--- a/config/checkstyle/checkstyle-suppressions.xml
+++ b/config/checkstyle/checkstyle-suppressions.xml
@@ -6,7 +6,7 @@
66
77 <!--
88 Checkstyle suppressions
9- for Checkstyle 8.20 or later
9+ for Checkstyle 8.22 or later
1010
1111 [ https://checkstyle.org/ ]
1212
@@ -35,6 +35,7 @@
3535
3636 <!-- Miscellaneous -->
3737 <suppress files="" checks="FinalParameters" />
38+ <suppress files="" checks="OrderedProperties" />
3839 <suppress files="" checks="TrailingComment" />
3940
4041 <!-- Modifier -->
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -6,7 +6,7 @@
66
77 <!--
88 Checkstyle modules
9- for Checkstyle 8.21 or later
9+ for Checkstyle 8.22 or later
1010
1111 [ https://checkstyle.org/ ]
1212
@@ -56,6 +56,7 @@
5656 <module name="NewlineAtEndOfFile">
5757 <property name="fileExtensions" value="java, properties, xml, xsd, md, txt" />
5858 </module>
59+ <module name="OrderedProperties" />
5960 <module name="Translation" />
6061 <module name="UniqueProperties" />
6162
@@ -199,6 +200,8 @@
199200 <module name="SuperClone" />
200201 <module name="SuperFinalize" />
201202 <module name="UnnecessaryParentheses" />
203+ <module name="UnnecessarySemicolonInEnumeration" />
204+ <module name="UnnecessarySemicolonInTryWithResources" />
202205 <module name="VariableDeclarationUsageDistance">
203206 <property name="allowedDistance" value="5"/>
204207 </module>
@@ -234,6 +237,7 @@
234237 <property name="scope" value="protected" />
235238 </module>
236239 <module name="MissingJavadocMethod" />
240+ <module name="MissingJavadocPackage" />
237241 <module name="MissingJavadocType" />
238242 <module name="NonEmptyAtclauseDescription" />
239243 <module name="SingleLineJavadoc" />
--- a/pom.xml
+++ b/pom.xml
@@ -99,14 +99,14 @@
9999 <jacoco-plugin.version>0.8.4</jacoco-plugin.version>
100100
101101 <checkstyle-plugin.version>3.1.0</checkstyle-plugin.version>
102- <checkstyleruntime.version>8.21</checkstyleruntime.version>
102+ <checkstyleruntime.version>8.22</checkstyleruntime.version>
103103 <checkstyle.config.location>${project.basedir}/config/checkstyle/checkstyle.xml</checkstyle.config.location>
104104 <checkstyle.suppressions.location>${project.basedir}/config/checkstyle/checkstyle-suppressions.xml</checkstyle.suppressions.location>
105105 <checkstyle.enable.rss>false</checkstyle.enable.rss>
106106
107107 <pmd-plugin.version>3.12.0</pmd-plugin.version>
108108
109- <spotbugs-plugin.version>3.1.11</spotbugs-plugin.version>
109+ <spotbugs-plugin.version>3.1.12</spotbugs-plugin.version>
110110 <spotbugs.effort>Max</spotbugs.effort>
111111 <spotbugs.threshold>Low</spotbugs.threshold>
112112 <!-- for Jenkins -->
--- a/src/main/java/jp/sfjp/mikutoga/typical/I18nAlias.java
+++ b/src/main/java/jp/sfjp/mikutoga/typical/I18nAlias.java
@@ -14,6 +14,7 @@ import java.util.Collections;
1414 import java.util.Comparator;
1515 import java.util.LinkedList;
1616 import java.util.List;
17+import javax.xml.XMLConstants;
1718 import javax.xml.parsers.DocumentBuilder;
1819 import javax.xml.parsers.DocumentBuilderFactory;
1920 import javax.xml.parsers.ParserConfigurationException;
@@ -43,6 +44,15 @@ class I18nAlias {
4344 public static final Comparator<I18nAlias> ORDER_COMPARATOR =
4445 new OrderComparator();
4546
47+ private static final String F_DISALLOW_DOCTYPE_DECL =
48+ "http://apache.org/xml/features/disallow-doctype-decl";
49+ private static final String F_EXTERNAL_GENERAL_ENTITIES =
50+ "http://xml.org/sax/features/external-general-entities";
51+ private static final String F_EXTERNAL_PARAMETER_ENTITIES =
52+ "http://xml.org/sax/features/external-parameter-entities";
53+ private static final String F_LOAD_EXTERNAL_DTD =
54+ "http://apache.org/xml/features/nonvalidating/load-external-dtd";
55+
4656
4757 private int orderNo;
4858
@@ -110,6 +120,20 @@ class I18nAlias {
110120 DocumentBuilderFactory factory;
111121 factory = DocumentBuilderFactory.newInstance();
112122
123+ factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
124+ factory.setFeature(F_EXTERNAL_GENERAL_ENTITIES, false);
125+ factory.setFeature(F_EXTERNAL_PARAMETER_ENTITIES, false);
126+ factory.setFeature(F_LOAD_EXTERNAL_DTD, false);
127+
128+ // unsafe but we use DOCTYPE
129+ factory.setFeature(F_DISALLOW_DOCTYPE_DECL, false);
130+
131+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
132+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
133+
134+ factory.setXIncludeAware(false);
135+ factory.setExpandEntityReferences(false);
136+
113137 DocumentBuilder builder = factory.newDocumentBuilder();
114138 Document doc = builder.parse(is);
115139
--- a/src/main/java/jp/sfjp/mikutoga/xml/DomNsUtils.java
+++ /dev/null
@@ -1,295 +0,0 @@
1-/*
2- * XML DOM utilities with namespace
3- *
4- * License : The MIT License
5- * Copyright(c) 2011 MikuToga Partners
6- */
7-
8-package jp.sfjp.mikutoga.xml;
9-
10-import java.text.MessageFormat;
11-import java.util.Iterator;
12-import org.w3c.dom.DOMException;
13-import org.w3c.dom.Element;
14-import org.w3c.dom.Node;
15-
16-/**
17- * DOMユーティリティ(名前空間対応)。
18- *
19- * <p>各種名前空間引数にnullが渡された場合、全ての名前空間にマッチする。
20- *
21- * <p>各種ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。
22- *
23- * <p>ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。
24- */
25-public final class DomNsUtils {
26-
27- private static final String ERRMSG_NOELEM =
28- "Elem:[{0}] was not found in Elem:[{1}]";
29- private static final String ERRMSG_NOATTR =
30- "Attr:[{0}] was not found in Elem:[{1}]";
31- private static final String ERRMSG_INVATTR =
32- "Invalid attribute form Attr[{0}] Value[{1}]";
33-
34-
35- /**
36- * 隠しコンストラクタ。
37- */
38- private DomNsUtils(){
39- assert false;
40- throw new AssertionError();
41- }
42-
43-
44- /**
45- * 名前空間とローカル名が一致するノードか判定する。
46- * @param node ノード
47- * @param nsuri 名前空間URI
48- * @param localName ローカル名。
49- * @return ノードの名前空間およびローカル名が一致したらtrue
50- */
51- public static boolean hasNsLocalNameNode(Node node,
52- String nsuri,
53- String localName ){
54- String nodeLocalName = node.getLocalName();
55- String nodeNsUri = node.getNamespaceURI();
56-
57- if(localName != null){
58- if( ! localName.equals(nodeLocalName) ) return false;
59- }
60-
61- if(nsuri != null && nodeNsUri != null){
62- if( ! nsuri.equals(nodeNsUri) ) return false;
63- }
64-
65- return true;
66- }
67-
68- /**
69- * 名前空間とローカル名が一致する要素か判定する。
70- * @param node ノード
71- * @param nsuri 名前空間URI
72- * @param localName ローカル名。
73- * @return 名前空間およびローカル名が一致する要素であればtrue
74- */
75- public static boolean hasNsLocalNameElem(Node node,
76- String nsuri,
77- String localName ){
78- if(node.getNodeType() != Node.ELEMENT_NODE) return false;
79- if( ! hasNsLocalNameNode(node, nsuri, localName) ) return false;
80- return true;
81- }
82-
83- /**
84- * 親要素が指定された名前の子要素を持つか判定する。
85- * @param parent 親要素
86- * @param nsuri 名前空間URI
87- * @param localName ローカル名
88- * @return 指定名の子要素が存在すればtrue
89- */
90- public static boolean hasChild(Element parent,
91- String nsuri,
92- String localName ){
93- for(Node node = parent.getFirstChild();
94- node != null;
95- node = node.getNextSibling() ){
96-
97- if(hasNsLocalNameElem(node, nsuri, localName)){
98- return true;
99- }
100- }
101-
102- return false;
103- }
104-
105- /**
106- * 指定された名前空間とローカル名に合致する最初の直下子要素を返す。
107- * @param parent 親要素
108- * @param nsuri 名前空間URI
109- * @param localName ローカル名
110- * @return 最初の直下子要素。見つからなければnull。
111- */
112- public static Element pickFirstChild(Node parent,
113- String nsuri,
114- String localName ){
115- Node node = parent.getFirstChild();
116- while(node != null){
117- if(hasNsLocalNameElem(node, nsuri, localName)){
118- break;
119- }
120- node = node.getNextSibling();
121- }
122- return (Element) node;
123- }
124-
125- /**
126- * 指定された名前空間とローカル名に合致する最初の直下子要素を返す。
127- *
128- * <p>見つからなければ例外を投げる。
129- *
130- * @param parent 親要素
131- * @param nsuri 名前空間URI
132- * @param localName ローカル名
133- * @return 最初の直下子要素。
134- * @throws TogaXmlException 1つも見つからなかった
135- */
136- public static Element getFirstChild(Element parent,
137- String nsuri,
138- String localName )
139- throws TogaXmlException{
140- Element elem = pickFirstChild(parent, nsuri, localName);
141-
142- if(elem == null){
143- String message = MessageFormat.format(ERRMSG_NOELEM,
144- localName,
145- parent.getLocalName() );
146- throw new TogaXmlException(message);
147- }
148-
149- return elem;
150- }
151-
152- /**
153- * 指定された名前の子要素のforeachを返す。
154- * @param parent 親要素
155- * @param nsuri 名前空間URI
156- * @param localName 子要素名
157- * @return 子要素のforeach
158- */
159- public static Iterable<Element> getEachChild(final Element parent,
160- final String nsuri,
161- final String localName ){
162- Iterable<Element> result = new Iterable<Element>(){
163- @Override
164- public Iterator<Element> iterator(){
165- return new SiblingElemIterator(parent, nsuri, localName);
166- }
167- };
168- return result;
169- }
170-
171- /**
172- * 要素に属性が存在するか判定する。
173- * @param elem 要素
174- * @param nsuri 名前空間URI
175- * @param localName ローカル名
176- * @return 存在するならtrue
177- */
178- public static boolean hasAttrNS(Element elem,
179- String nsuri,
180- String localName ){
181- return elem.hasAttributeNS(nsuri, localName);
182- }
183-
184- /**
185- * 要素からxsd:string型属性値を読み取る。
186- * @param elem 要素
187- * @param nsuri 名前空間URI
188- * @param localName 属性名
189- * @return 文字列
190- * @throws TogaXmlException 属性値が見つからなかった。
191- */
192- public static String getStringAttrNS(Element elem,
193- String nsuri,
194- String localName )
195- throws TogaXmlException{
196- if( ! hasAttrNS(elem, nsuri, localName) ){
197- String message = MessageFormat.format(ERRMSG_NOATTR,
198- localName,
199- elem.getLocalName() );
200- throw new TogaXmlException(message);
201- }
202-
203- String result;
204- try{
205- result = elem.getAttributeNS(nsuri, localName);
206- }catch(DOMException e){
207- assert false;
208- throw new AssertionError(e);
209- }
210-
211- return result;
212- }
213-
214- /**
215- * 要素からxsd:boolean型属性値を読み取る。
216- * @param elem 要素
217- * @param nsuri 名前空間URI
218- * @param localName 属性名
219- * @return 真ならtrue
220- * @throws TogaXmlException 属性値が見つからなかった。
221- */
222- public static boolean getBooleanAttrNS(Element elem,
223- String nsuri,
224- String localName )
225- throws TogaXmlException{
226- String value = getStringAttrNS(elem, nsuri, localName);
227-
228- boolean result;
229- try{
230- result = DatatypeIo.parseBoolean(value);
231- }catch(IllegalArgumentException e){
232- String message = MessageFormat.format(ERRMSG_INVATTR,
233- localName,
234- value );
235- throw new TogaXmlException(message, e);
236- }
237-
238- return result;
239- }
240-
241- /**
242- * 要素からxsd:integer型属性値を読み取る。
243- * @param elem 要素
244- * @param nsuri 名前空間URI
245- * @param localName 属性名
246- * @return int値
247- * @throws TogaXmlException 属性値が見つからなかった。
248- */
249- public static int getIntegerAttrNS(Element elem,
250- String nsuri,
251- String localName )
252- throws TogaXmlException{
253- String value = getStringAttrNS(elem, nsuri, localName);
254-
255- int result;
256- try{
257- result = DatatypeIo.parseInt(value);
258- }catch(NumberFormatException e){
259- String message = MessageFormat.format(ERRMSG_INVATTR,
260- localName,
261- value );
262- throw new TogaXmlException(message, e);
263- }
264-
265- return result;
266- }
267-
268- /**
269- * 要素からxsd:float型属性値を読み取る。
270- * @param elem 要素
271- * @param nsuri 名前空間URI
272- * @param localName 属性名
273- * @return float値
274- * @throws TogaXmlException 属性値が見つからなかった。
275- */
276- public static float getFloatAttrNS(Element elem,
277- String nsuri,
278- String localName )
279- throws TogaXmlException{
280- String value = getStringAttrNS(elem, nsuri, localName);
281-
282- float result;
283- try{
284- result = DatatypeIo.parseFloat(value);
285- }catch(NumberFormatException e){
286- String message = MessageFormat.format(ERRMSG_INVATTR,
287- localName,
288- value );
289- throw new TogaXmlException(message, e);
290- }
291-
292- return result;
293- }
294-
295-}
--- a/src/main/java/jp/sfjp/mikutoga/xml/DomUtils.java
+++ /dev/null
@@ -1,362 +0,0 @@
1-/*
2- * XML DOM utilities
3- *
4- * License : The MIT License
5- * Copyright(c) 2010 MikuToga Partners
6- */
7-
8-package jp.sfjp.mikutoga.xml;
9-
10-import java.util.Iterator;
11-import java.util.LinkedList;
12-import java.util.List;
13-import java.util.NoSuchElementException;
14-import org.w3c.dom.Element;
15-import org.w3c.dom.Node;
16-
17-/**
18- * DOMユーティリティ。
19- */
20-public final class DomUtils {
21-
22- // 構文解析バグ回避。
23- private static final char BS_CHAR = (char) 0x005c;
24-
25- /**
26- * 隠しコンストラクタ。
27- */
28- private DomUtils(){
29- super();
30- assert false;
31- throw new AssertionError();
32- }
33-
34- /**
35- * 要素からxsd:string型属性値を読み取る。
36- * @param elem 要素
37- * @param attrName 属性名
38- * @return 文字列
39- * @throws TogaXmlException 属性値が見つからなかった。
40- */
41- public static String getStringAttr(Element elem, String attrName)
42- throws TogaXmlException{
43- if( ! elem.hasAttribute(attrName) ){
44- String message = "Attr:[" + attrName + "] "
45- + "was not found in "
46- + "Elem:[" + elem.getTagName()+"]";
47- throw new TogaXmlException(message);
48- }
49-
50- String result;
51- try{
52- result = elem.getAttribute(attrName);
53- }catch(IllegalArgumentException e){
54- String message = "Invalid attribute form [" + attrName + "]";
55- throw new TogaXmlException(message, e);
56- }
57-
58- return result;
59- }
60-
61- /**
62- * 要素からxsd:boolean型属性値を読み取る。
63- * @param elem 要素
64- * @param attrName 属性名
65- * @return 真ならtrue
66- * @throws TogaXmlException 属性値が見つからなかった。
67- */
68- public static boolean getBooleanAttr(Element elem, String attrName)
69- throws TogaXmlException{
70- String value = getStringAttr(elem, attrName);
71-
72- boolean result;
73- try{
74- result = DatatypeIo.parseBoolean(value);
75- }catch(IllegalArgumentException e){
76- String message =
77- "Invalid boolean attribute form "
78- + "[" + attrName + "][" + value + "]";
79- throw new TogaXmlException(message, e);
80- }
81-
82- return result;
83- }
84-
85- /**
86- * 要素からxsd:integer型属性値を読み取る。
87- * @param elem 要素
88- * @param attrName 属性名
89- * @return int値
90- * @throws TogaXmlException 属性値が見つからなかった。
91- */
92- public static int getIntegerAttr(Element elem, String attrName)
93- throws TogaXmlException{
94- String value = getStringAttr(elem, attrName);
95-
96- int result;
97- try{
98- result = DatatypeIo.parseInt(value);
99- }catch(IllegalArgumentException e){
100- String message =
101- "Invalid integer attribute form "
102- + "[" + attrName + "][" + value + "]";
103- throw new TogaXmlException(message, e);
104- }
105-
106- return result;
107- }
108-
109- /**
110- * 要素からxsd:float型属性値を読み取る。
111- * @param elem 要素
112- * @param attrName 属性名
113- * @return float値
114- * @throws TogaXmlException 属性値が見つからなかった。
115- */
116- public static float getFloatAttr(Element elem, String attrName)
117- throws TogaXmlException{
118- String value = getStringAttr(elem, attrName);
119-
120- float result;
121- try{
122- result = DatatypeIo.parseFloat(value);
123- }catch(IllegalArgumentException e){
124- String message =
125- "Invalid float attribute form "
126- + "[" + attrName + "][" + value + "]";
127- throw new TogaXmlException(message, e);
128- }
129-
130- return result;
131- }
132-
133- /**
134- * 要素から日本語Windows用ファイル名を属性値として読み取る。
135- * 念のため文字U+00A5は文字U-005Cに変換される。
136- * @param elem 要素
137- * @param attrName 属性名
138- * @return ファイル名
139- * @throws TogaXmlException 属性値が見つからなかった。
140- */
141- public static String getSjisFileNameAttr(Element elem, String attrName)
142- throws TogaXmlException{
143- String result;
144- try{
145- result = getStringAttr(elem, attrName);
146- }catch(IllegalArgumentException e){
147- String message =
148- "Invalid winfile attribute form "
149- + "[" + attrName + "]";
150- throw new TogaXmlException(message, e);
151- }
152-
153- result = result.replace("" + '\u00a5', "" + BS_CHAR);
154-
155- return result;
156- }
157-
158- /**
159- * 指定された名前の子要素を1つだけ返す。
160- * @param parent 親要素
161- * @param tagName 子要素名
162- * @return 子要素
163- * @throws TogaXmlException 1つも見つからなかった
164- */
165- public static Element getChild(Element parent, String tagName)
166- throws TogaXmlException{
167- Element result = null;
168-
169- for(Node node = parent.getFirstChild();
170- node != null;
171- node = node.getNextSibling() ){
172-
173- if(node.getNodeType() != Node.ELEMENT_NODE) continue;
174- Element elem = (Element) node;
175-
176- String elemTagName = elem.getTagName();
177- if( tagName.equals(elemTagName) ){
178- result = elem;
179- break;
180- }
181- }
182-
183- if(result == null){
184- String message =
185- "Elem:[" + tagName + "] was not found in "
186- +"Elem:[" + parent.getTagName() + "]";
187- throw new TogaXmlException(message);
188- }
189-
190- return result;
191- }
192-
193- /**
194- * 親要素が指定された名前の子要素を持つか判定する。
195- * @param parent 親要素
196- * @param tagName 子要素名
197- * @return 指定名の子要素が存在すればtrue
198- */
199- public static boolean hasChild(Element parent, String tagName){
200- for(Node node = parent.getFirstChild();
201- node != null;
202- node = node.getNextSibling() ){
203-
204- if(node.getNodeType() != Node.ELEMENT_NODE) continue;
205- Element elem = (Element) node;
206-
207- String elemTagName = elem.getTagName();
208- if( tagName.equals(elemTagName) ) return true;
209- }
210-
211- return false;
212- }
213-
214- /**
215- * 指定された名前の子要素のリストを返す。
216- * @param parent 親要素
217- * @param childTag 子要素名
218- * @return 子要素のリスト
219- */
220- public static List<Element> getChildList(Element parent,
221- String childTag){
222- List<Element> result = new LinkedList<>();
223-
224- for(Node node = parent.getFirstChild();
225- node != null;
226- node = node.getNextSibling() ){
227-
228- if(node.getNodeType() != Node.ELEMENT_NODE) continue;
229- Element elem = (Element) node;
230-
231- String tagName = elem.getTagName();
232- if( ! childTag.equals(tagName) ) continue;
233-
234- result.add(elem);
235- }
236-
237- return result;
238- }
239-
240- /**
241- * 指定された名前の子要素の列挙子を返す。
242- * @param parent 親要素
243- * @param childTag 子要素名
244- * @return 子要素の列挙子
245- */
246- public static Iterator<Element> getChildIterator(Element parent,
247- String childTag){
248- Element firstElem;
249- try{
250- firstElem = getChild(parent, childTag);
251- }catch(TogaXmlException e){
252- firstElem = null;
253- }
254-
255- Iterator<Element> result = new ElemIterator(firstElem);
256-
257- return result;
258- }
259-
260- /**
261- * 指定された名前の子要素のforeachを返す。
262- * @param parent 親要素
263- * @param childTag 子要素名
264- * @return 子要素のforeach
265- */
266- public static Iterable<Element> getEachChild(Element parent,
267- String childTag){
268- final Iterator<Element> iterator = getChildIterator(parent, childTag);
269- Iterable<Element> result = new Iterable<Element>(){
270- @Override
271- public Iterator<Element> iterator(){
272- return iterator;
273- }
274- };
275- return result;
276- }
277-
278- /**
279- * 要素の次の要素を返す。
280- * @param elem 要素
281- * @return 次の要素。なければnull
282- */
283- public static Element nextElement(Element elem){
284- Node nextNode = elem;
285- for(;;){
286- nextNode = nextNode.getNextSibling();
287- if(nextNode == null) break;
288- if(nextNode.getNodeType() == Node.ELEMENT_NODE){
289- break;
290- }
291- }
292-
293- return (Element) nextNode;
294- }
295-
296- /**
297- * 同じ要素名を持つ次の要素を返す。
298- * @param elem 要素
299- * @return 次の要素。なければnull
300- */
301- public static Element nextNamedElement(Element elem){
302- String tagName = elem.getTagName();
303- Element nextElem = elem;
304- for(;;){
305- nextElem = nextElement(nextElem);
306- if(nextElem == null) break;
307- if(tagName.equals(nextElem.getTagName())) break;
308- }
309-
310- return nextElem;
311- }
312-
313- /**
314- * 同じ親要素と同じ要素名を持つ兄弟要素を列挙する列挙子。
315- */
316- private static final class ElemIterator implements Iterator<Element> {
317- private Element next;
318-
319- /**
320- * コンストラクタ。
321- * @param elem 最初の要素。nullを指定すれば空列挙子となる。
322- */
323- ElemIterator(Element elem){
324- super();
325- this.next = elem;
326- }
327-
328- /**
329- * {@inheritDoc}
330- * @return {@inheritDoc}
331- */
332- @Override
333- public boolean hasNext(){
334- if(this.next == null) return false;
335- return true;
336- }
337-
338- /**
339- * {@inheritDoc}
340- * @return {@inheritDoc}
341- * @throws NoSuchElementException {@inheritDoc}
342- */
343- @Override
344- public Element next() throws NoSuchElementException{
345- if(this.next == null) throw new NoSuchElementException();
346- Element result = this.next;
347- this.next = nextNamedElement(this.next);
348- return result;
349- }
350-
351- /**
352- * {@inheritDoc}
353- * ※ 未サポート。
354- */
355- @Override
356- public void remove(){
357- throw new UnsupportedOperationException();
358- }
359-
360- }
361-
362-}
--- a/src/main/java/jp/sfjp/mikutoga/xml/LocalXmlResource.java
+++ /dev/null
@@ -1,32 +0,0 @@
1-/*
2- * xml local resource map
3- *
4- * License : The MIT License
5- * Copyright(c) 2013 olyutorskii
6- */
7-
8-package jp.sfjp.mikutoga.xml;
9-
10-import java.net.URI;
11-
12-/**
13- * 代用ローカルリソースの管理を行う。
14- *
15- * <p>ネットワークを介したグローバルなリソースと、
16- * アプリ上のローカルな代用リソースとを対応付ける。
17- */
18-public interface LocalXmlResource {
19-
20- /**
21- * オリジナル版XMLリソースのURIを返す。
22- * @return オリジナル版リソースのURL。
23- */
24- public abstract URI getOriginalResource();
25-
26- /**
27- * ローカル版XMLリソースのURIを返す。
28- * @return ローカル版リソースのURL。
29- */
30- public abstract URI getLocalResource();
31-
32-}
--- /dev/null
+++ b/src/main/java/jp/sfjp/mikutoga/xml/NoopEntityResolver.java
@@ -0,0 +1,58 @@
1+/*
2+ * No-operation Entity Resolver for XML.
3+ *
4+ * License : The MIT License
5+ * Copyright(c) 2019 olyutorskii
6+ */
7+
8+package jp.sfjp.mikutoga.xml;
9+
10+import java.io.Reader;
11+import java.io.StringReader;
12+import org.xml.sax.EntityResolver;
13+import org.xml.sax.InputSource;
14+
15+/**
16+ * No-operation Entity Resolver implementation for preventing XXE.
17+ *
18+ * @see <a href="https://en.wikipedia.org/wiki/XML_external_entity_attack">
19+ * XML external entity attack (Wikipedia)
20+ * </a>
21+ */
22+public final class NoopEntityResolver implements EntityResolver{
23+
24+ /** Singleton resolver. */
25+ public static final EntityResolver NOOP_RESOLVER =
26+ new NoopEntityResolver();
27+
28+
29+ /**
30+ * Constructor.
31+ */
32+ private NoopEntityResolver(){
33+ super();
34+ return;
35+ }
36+
37+
38+ /**
39+ * {@inheritDoc}
40+ *
41+ * <p>Prevent any external entity reference XXE.
42+ *
43+ * @param publicId {@inheritDoc}
44+ * @param systemId {@inheritDoc}
45+ * @return empty input source
46+ */
47+ @Override
48+ public InputSource resolveEntity(String publicId, String systemId){
49+ Reader emptyReader = new StringReader("");
50+ InputSource source = new InputSource(emptyReader);
51+
52+ source.setPublicId(publicId);
53+ source.setSystemId(systemId);
54+
55+ return source;
56+ }
57+
58+}
--- a/src/main/java/jp/sfjp/mikutoga/xml/SchemaUtil.java
+++ b/src/main/java/jp/sfjp/mikutoga/xml/SchemaUtil.java
@@ -10,11 +10,9 @@ package jp.sfjp.mikutoga.xml;
1010 import java.io.BufferedInputStream;
1111 import java.io.IOException;
1212 import java.io.InputStream;
13-import java.net.MalformedURLException;
1413 import java.net.URI;
14+import java.net.URISyntaxException;
1515 import java.net.URL;
16-import java.util.ArrayList;
17-import java.util.List;
1816 import javax.xml.XMLConstants;
1917 import javax.xml.transform.Source;
2018 import javax.xml.transform.stream.StreamSource;
@@ -22,131 +20,166 @@ import javax.xml.validation.Schema;
2220 import javax.xml.validation.SchemaFactory;
2321 import org.w3c.dom.ls.LSResourceResolver;
2422 import org.xml.sax.SAXException;
23+import org.xml.sax.SAXNotRecognizedException;
24+import org.xml.sax.SAXNotSupportedException;
2525
2626 /**
27- * XMLスキーマの各種ビルダ。
27+ * XML schema (XSD) utilities.
28+ *
29+ * <p>RELAX NG is not supported.
2830 */
2931 public final class SchemaUtil {
3032
31- /**
32- * 隠しコンストラクタ。
33- */
34- private SchemaUtil(){
35- assert false;
36- throw new AssertionError();
37- }
3833
34+ /** XML Schema. */
35+ public static final String SCHEMA_XML =
36+ "http://www.w3.org/2001/xml.xsd";
3937
40- /**
41- * XML Schema 用のスキーマファクトリを返す。
42- * @return スキーマファクトリ
43- */
44- public static SchemaFactory newSchemaFactory(){
45- SchemaFactory result = newSchemaFactory(null);
46- return result;
47- }
38+ /** XSD namespace. */
39+ public static final String NS_XSD =
40+ "http://www.w3.org/2001/XMLSchema-instance";
4841
49- /**
50- * XML Schema 用のスキーマファクトリを返す。
51- * @param resolver カスタムリゾルバ。nullも可。
52- * @return スキーマファクトリ
53- */
54- public static SchemaFactory newSchemaFactory(
55- LSResourceResolver resolver ){
56- SchemaFactory schemaFactory =
57- SchemaFactory.newInstance(
58- XMLConstants.W3C_XML_SCHEMA_NS_URI
59- );
42+ private static final String LOCAL_SCHEMA_XML =
43+ "resources/xmlspace.xsd";
6044
61- // schemaFactory.setFeature(name, value);
62- // schemaFactory.setProperty(name, object);
45+ private static final URI URI_XSD_ORIG;
46+ private static final URI URI_XSD_LOCAL;
6347
64- schemaFactory.setErrorHandler(BotherHandler.HANDLER);
65- schemaFactory.setResourceResolver(resolver);
48+ private static final String ALLOWED_USCHEMA = "http";
6649
67- return schemaFactory;
50+ private static final Class<?> THISCLASS = SchemaUtil.class;
51+
52+
53+ static{
54+ URL localXsdUrl = THISCLASS.getResource(LOCAL_SCHEMA_XML);
55+ URI localXsdUri;
56+ try{
57+ localXsdUri = localXsdUrl.toURI();
58+ }catch(URISyntaxException e){
59+ throw new ExceptionInInitializerError(e);
60+ }
61+
62+ URI_XSD_ORIG = URI.create(SCHEMA_XML);
63+ URI_XSD_LOCAL = localXsdUri;
64+
65+ assert ALLOWED_USCHEMA.equalsIgnoreCase(URI_XSD_ORIG.getScheme());
6866 }
6967
68+
7069 /**
71- * ローカルリソースをSourceに変換する。
72- * @param resource ローカルリソース
73- * @return XML Source
74- * @throws MalformedURLException 不正なURI
75- * @throws IOException オープンエラー
70+ * Hidden constructor.
7671 */
77- private static Source toLocalSource(LocalXmlResource resource)
78- throws MalformedURLException, IOException{
79- URI localUri = resource.getLocalResource();
80- URL localUrl = localUri.toURL();
72+ private SchemaUtil(){
73+ assert false;
74+ throw new AssertionError();
75+ }
8176
82- InputStream is = localUrl.openStream();
83- is = new BufferedInputStream(is);
8477
85- Source result = new StreamSource(is);
78+ /**
79+ * build xml.xsd redirection info.
80+ *
81+ * @return resolver
82+ */
83+ public static XmlResourceResolver buildXmlXsdResolver(){
84+ XmlResourceResolver result = new XmlResourceResolver();
85+ result.putRedirected(URI_XSD_ORIG, URI_XSD_LOCAL);
8686 return result;
8787 }
8888
8989 /**
90- * ローカルリソース群をSource群に変換する。
91- * @param resArray ローカルリソースURI並び
92- * @return XML Source並び
93- * @throws MalformedURLException 不正なURI
94- * @throws IOException オープンエラー
90+ * Build SchemaFactory for XML Schema but safety.
91+ *
92+ * <p>Includes some considerations for XXE vulnerabilities.
93+ *
94+ * <p>Restrict access to
95+ * External Entity Reference &amp; external DTDs
96+ * in xml schema file.
97+ *
98+ * <p>Restrict access to External schema file access in xml schema file,
99+ * but HTTP access is allowed.
100+ * This special limit considers access to
101+ * importing http://www.w3.org/2001/xml.xsd
102+ * in top of common xml schema file.
103+ *
104+ * @return schema factory
95105 */
96- private static Source[] toLocalSourceArray(LocalXmlResource... resArray)
97- throws MalformedURLException, IOException{
98- List<Source> sourceList = new ArrayList<>(resArray.length);
106+ public static SchemaFactory newSchemaFactory(){
107+ SchemaFactory schemaFactory;
108+ schemaFactory = SchemaFactory.newInstance(
109+ XMLConstants.W3C_XML_SCHEMA_NS_URI);
99110
100- for(LocalXmlResource resource : resArray){
101- Source localSource = toLocalSource(resource);
102- sourceList.add(localSource);
111+ try{
112+ // Prevent denial-of-service attack.
113+ schemaFactory.setFeature(
114+ XMLConstants.FEATURE_SECURE_PROCESSING, true);
115+ }catch(SAXNotRecognizedException | SAXNotSupportedException e){
116+ // THIS FEATURE MUST BE SUPPORTED
117+ assert false;
103118 }
104119
105- Source[] result = new Source[sourceList.size()];
106- result = sourceList.toArray(result);
107- return result;
120+ try{
121+ // Disallow external entity reference & external DTD access.
122+ schemaFactory.setProperty(
123+ XMLConstants.ACCESS_EXTERNAL_DTD, "");
124+ // Allow only HTTP external schema file.
125+ schemaFactory.setProperty(
126+ XMLConstants.ACCESS_EXTERNAL_SCHEMA, ALLOWED_USCHEMA);
127+ }catch(SAXNotRecognizedException | SAXNotSupportedException e){
128+ // THIS PROPERTY MUST BE SUPPORTED JAXP1.5 or later
129+ assert false;
130+ }
131+
132+ LSResourceResolver resolver = buildXmlXsdResolver();
133+ schemaFactory.setResourceResolver(resolver);
134+
135+ schemaFactory.setErrorHandler(BotherHandler.HANDLER);
136+
137+ return schemaFactory;
108138 }
109139
110140 /**
111- * ローカルスキーマをロードする。
141+ * pre-load &amp; pre-compile local schema files.
112142 *
113- * <p>任意のリゾルバを指定可能
114- *
115- * @param resolver リゾルバ
116- * @param resArray ローカルスキーマ情報並び
117- * @return スキーマ
143+ * @param localSchemaUris local schema resources.
144+ * @return schema
145+ * @throws SAXException invalid schema definition
146+ * @throws IOException local resource i/o error
118147 */
119- public static Schema newSchema(XmlResourceResolver resolver,
120- LocalXmlResource... resArray ){
121- for(LocalXmlResource resource : resArray){
122- resolver.putRedirected(resource);
148+ public static Schema newSchema(URI... localSchemaUris)
149+ throws SAXException, IOException{
150+ SchemaFactory schemaFactory = newSchemaFactory();
151+
152+ int uris = localSchemaUris.length;
153+ if(uris <= 0){
154+ // on-demand xml-schema with schemaLocation attribute.
155+ return schemaFactory.newSchema();
123156 }
124157
125- Source[] sources;
126- try{
127- sources = toLocalSourceArray(resArray);
128- }catch(IOException e){ // ビルド障害
129- assert false;
130- throw new AssertionError(e);
158+ InputStream[] iss = new InputStream[uris];
159+ for(int idx = 0; idx < uris; idx++){
160+ URI localUri = localSchemaUris[idx];
161+ URL localUrl = localUri.toURL();
162+ InputStream is;
163+ is = localUrl.openStream();
164+ is = new BufferedInputStream(is);
165+ iss[idx] = is;
131166 }
132167
133- SchemaFactory schemaFactory = newSchemaFactory(resolver);
168+ Source[] sources = new Source[uris];
169+ for(int idx = 0; idx < uris; idx++){
170+ InputStream is = iss[idx];
171+ sources[idx] = new StreamSource(is);
172+ }
134173
135174 Schema result;
136175 try{
137- if(sources.length <= 0){
138- // ドキュメント埋め込みスキーマURLにリゾルバ経由でアクセス
139- result = schemaFactory.newSchema();
140- }else{
141- result = schemaFactory.newSchema(sources);
176+ result = schemaFactory.newSchema(sources);
177+ }finally{
178+ for(InputStream is : iss){
179+ is.close();
142180 }
143- }catch(SAXException e){ // Build error
144- assert false;
145- throw new AssertionError(e);
146181 }
147182
148- // TODO: Sourceを閉めるのは誰の責務?
149-
150183 return result;
151184 }
152185
--- a/src/main/java/jp/sfjp/mikutoga/xml/SiblingElemIterator.java
+++ /dev/null
@@ -1,132 +0,0 @@
1-/*
2- * sibling element iterator on DOM tree
3- *
4- * License : The MIT License
5- * Copyright(c) 2011 MikuToga Partners
6- */
7-
8-package jp.sfjp.mikutoga.xml;
9-
10-import java.util.Iterator;
11-import java.util.NoSuchElementException;
12-import org.w3c.dom.Element;
13-import org.w3c.dom.Node;
14-
15-/**
16- * 兄弟要素間用Iterator。
17- *
18- * <p>同じ親と名前空間とローカル名を持つ要素同士を「兄弟要素」とする。
19- *
20- * <p>ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。
21- *
22- * <p>削除操作は未サポート。
23- */
24-public class SiblingElemIterator implements Iterator<Element> {
25-
26- private Element next;
27- private final String nsuri;
28- private final String localName;
29-
30-
31- /**
32- * コンストラクタ。
33- * @param first 最初の兄弟要素。nullだと一度もiterateしない。
34- */
35- public SiblingElemIterator(Element first){
36- super();
37-
38- this.next = first;
39-
40- if(this.next == null){
41- this.nsuri = null;
42- this.localName = null;
43- }else{
44- this.nsuri = this.next.getNamespaceURI();
45- this.localName = this.next.getLocalName();
46- }
47-
48- return;
49- }
50-
51- /**
52- * コンストラクタ。
53- *
54- * <p>名前空間引数にnullが渡された場合、全ての名前空間にマッチする。
55- *
56- * <p>ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。
57- *
58- * @param parent 親要素
59- * @param nsuri 子要素の名前空間URI
60- * @param localName 子要素のローカル名
61- */
62- public SiblingElemIterator(Element parent,
63- String nsuri,
64- String localName ){
65- super();
66-
67- this.next = DomNsUtils.pickFirstChild(parent, nsuri, localName);
68-
69- if(this.next == null){
70- this.nsuri = null;
71- this.localName = null;
72- }else{
73- this.nsuri = nsuri;
74- this.localName = localName;
75- }
76-
77- return;
78- }
79-
80-
81- /**
82- * {@inheritDoc}
83- * @return {@inheritDoc}
84- */
85- @Override
86- public boolean hasNext(){
87- if(this.next != null) return true;
88- return false;
89- }
90-
91- /**
92- * {@inheritDoc}
93- * @return {@inheritDoc}
94- * @throws NoSuchElementException {@inheritDoc}
95- */
96- @Override
97- public Element next() throws NoSuchElementException {
98- if(this.next == null) throw new NoSuchElementException();
99-
100- Element result = this.next;
101-
102- Node sibNode = result;
103- do{
104- sibNode = sibNode.getNextSibling();
105- if(sibNode == null) break;
106- }while( ! matchElemName(sibNode) );
107- this.next = (Element) sibNode;
108-
109- return result;
110- }
111-
112- /**
113- * 兄弟要素にふさわしい名前を持つか判定する。
114- * @param node 判定対象
115- * @return 兄弟にふさわしい名前を持つならtrue
116- */
117- private boolean matchElemName(Node node){
118- return DomNsUtils.hasNsLocalNameElem(node,
119- this.nsuri, this.localName );
120- }
121-
122- /**
123- * {@inheritDoc}
124- * ※削除不可。
125- * @throws UnsupportedOperationException 削除を試みたので失敗した
126- */
127- @Override
128- public void remove() throws UnsupportedOperationException {
129- throw new UnsupportedOperationException();
130- }
131-
132-}
--- a/src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java
+++ b/src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java
@@ -7,82 +7,108 @@
77
88 package jp.sfjp.mikutoga.xml;
99
10+import java.io.BufferedInputStream;
11+import java.io.ByteArrayInputStream;
1012 import java.io.IOException;
1113 import java.io.InputStream;
12-import java.io.Reader;
1314 import java.net.URI;
1415 import java.net.URISyntaxException;
1516 import java.net.URL;
1617 import java.util.Collections;
1718 import java.util.HashMap;
1819 import java.util.Map;
20+import org.w3c.dom.DOMImplementation;
21+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
22+import org.w3c.dom.ls.DOMImplementationLS;
1923 import org.w3c.dom.ls.LSInput;
2024 import org.w3c.dom.ls.LSResourceResolver;
21-import org.xml.sax.EntityResolver;
22-import org.xml.sax.InputSource;
23-import org.xml.sax.SAXException;
2425
2526 /**
26- * URL変換マップに従い、XML文書からの外部参照をリダイレクトする。
27- * 相対URIはこのクラスをベースに解決される。
28- * 主な用途は外部スキーマのリソース化など。
27+ * register &amp; redirect original URI to local resource contents.
2928 */
3029 public class XmlResourceResolver
31- implements LSResourceResolver, EntityResolver {
32-
33- /** XML Schema. */
34- public static final String SCHEMA_XML =
35- "http://www.w3.org/2001/xml.xsd";
36-
37- /** XSD名前空間。 */
38- public static final String NS_XSD =
39- "http://www.w3.org/2001/XMLSchema-instance";
40-
41- private static final String LOCAL_SCHEMA_XML =
42- "resources/xmlspace.xsd";
30+ implements LSResourceResolver{
4331
4432 private static final URI EMPTY_URI = URI.create("");
4533
46- private static final Class<?> THISCLASS = XmlResourceResolver.class;
34+ private static final DOMImplementationLS DOM_LS;
4735
4836
4937 private final Map<URI, URI> uriMap;
5038
5139
40+ static{
41+ try{
42+ DOM_LS = buildDomImplLS();
43+ }catch( ClassNotFoundException
44+ | IllegalAccessException
45+ | InstantiationException e){
46+ throw new ExceptionInInitializerError(e);
47+ }
48+ }
49+
50+
5251 /**
53- * コンストラクタ。
52+ * Constructor.
5453 */
5554 public XmlResourceResolver(){
5655 super();
5756
58- assert this.getClass().equals(THISCLASS);
59-
6057 Map<URI, URI> map;
6158 map = new HashMap<>();
6259 map = Collections.synchronizedMap(map);
6360 this.uriMap = map;
6461
65- URL redirectRes = THISCLASS.getResource(LOCAL_SCHEMA_XML);
66- String redirectResName = redirectRes.toString();
67-
68- URI originalURI = URI.create(SCHEMA_XML);
69- URI redirectURI = URI.create(redirectResName);
62+ return;
63+ }
7064
71- putRedirectedImpl(originalURI, redirectURI);
7265
73- return;
66+ /**
67+ * return DOMImplementationLS implement.
68+ *
69+ * @return DOMImplementationLS implement
70+ * @throws ClassNotFoundException no class
71+ * @throws InstantiationException no object
72+ * @throws IllegalAccessException no grant
73+ */
74+ private static DOMImplementationLS buildDomImplLS() throws
75+ ClassNotFoundException,
76+ InstantiationException,
77+ IllegalAccessException {
78+ DOMImplementationRegistry domReg;
79+ DOMImplementation domImp;
80+ DOMImplementationLS domImpLs;
81+
82+ domReg = DOMImplementationRegistry.newInstance();
83+ domImp = domReg.getDOMImplementation("LS 3.0");
84+
85+ Object feature = domImp.getFeature("LS", "3.0");
86+ assert feature instanceof DOMImplementationLS;
87+ domImpLs = (DOMImplementationLS) feature;
88+
89+ return domImpLs;
7490 }
7591
92+ /**
93+ * return LSInput implement.
94+ *
95+ * @return LSInput implement
96+ */
97+ public static LSInput createLSInput(){
98+ LSInput input = DOM_LS.createLSInput();
99+ return input;
100+ }
76101
77102 /**
78- * 絶対URIと相対URIを合成したURIを返す。
79- * 正規化も行われる。
103+ * merge base-uri text &amp; relative URI text.
80104 *
81- * @param base 絶対URIでなければならない。nullでもよい。
82- * @param relative 絶対URIでもよいがその場合baseは無視される。null可。
83- * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
84- * @throws java.net.URISyntaxException URIとして変。
85- * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
105+ * @param base base URI text must be absolute or null.
106+ * @param relative relative URI text.
107+ * If absolute, base is ignored.
108+ * Ignored if null.
109+ * @return merged absolute URI.
110+ * @throws java.net.URISyntaxException illegal URI
111+ * @throws java.lang.IllegalArgumentException result is not Absolute.
86112 */
87113 protected static URI buildBaseRelativeURI(String base, String relative)
88114 throws URISyntaxException,
@@ -103,19 +129,18 @@ public class XmlResourceResolver
103129 }else{
104130 relativeURI = EMPTY_URI;
105131 }
106-
132+
107133 URI result = buildBaseRelativeURI(baseURI, relativeURI);
108134 return result;
109135 }
110136
111137 /**
112- * 絶対URIと相対URIを合成したURIを返す。
113- * 正規化も行われる。
138+ * merge base-uri &amp; relative URI.
114139 *
115- * @param baseURI 絶対URIでなければならない。nullでもよい。
116- * @param relativeURI 絶対URIでもよいがその場合baseは無視される。
117- * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
118- * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
140+ * @param baseURI base URI must be absolute or null.
141+ * @param relativeURI relative URI. If absolute, baseURI is ignored.
142+ * @return merged absolute URI.
143+ * @throws java.lang.IllegalArgumentException result is not Absolute.
119144 */
120145 private static URI buildBaseRelativeURI(URI baseURI, URI relativeURI)
121146 throws IllegalArgumentException {
@@ -136,23 +161,14 @@ public class XmlResourceResolver
136161 return resultURI;
137162 }
138163
139- /**
140- * LSInput実装を生成する。
141- * @return LSInput実装
142- */
143- public static LSInput createLSInput(){
144- LSInput input = new LSInputImpl();
145- return input;
146- }
147-
148164
149165 /**
150- * オリジナルURIとリダイレクト先のURIを登録する。
151- * オリジナルURIへのアクセスはリダイレクトされる。
152- * @param original オリジナルURI
153- * @param redirect リダイレクトURI
166+ * map original URI &amp; local resource URI.
167+ *
168+ * @param original original URI
169+ * @param redirect local resource URI
154170 */
155- private void putRedirectedImpl(URI original, URI redirect){
171+ public void putRedirected(URI original, URI redirect){
156172 URI oridinalNorm = original.normalize();
157173 URI redirectNorm = redirect.normalize();
158174
@@ -162,34 +178,9 @@ public class XmlResourceResolver
162178 }
163179
164180 /**
165- * オリジナルURIとリダイレクト先のURIを登録する。
166- * オリジナルURIへのアクセスはリダイレクトされる。
167- * @param original オリジナルURI
168- * @param redirect リダイレクトURI
169- */
170- public void putRedirected(URI original, URI redirect){
171- putRedirectedImpl(original, redirect);
172- return;
173- }
174-
175- /**
176- * ローカル版リソース参照解決を登録する。
177- * @param lsc ローカル版リソース参照解決
178- */
179- public void putRedirected(LocalXmlResource lsc){
180- URI original = lsc.getOriginalResource();
181- if(original == null) return;
182-
183- URI local = lsc.getLocalResource();
184-
185- putRedirected(original, local);
186-
187- return;
188- }
189-
190- /**
191- * 別リゾルバの登録内容を追加登録する。
192- * @param other 別リゾルバ
181+ * add other resolver mapping.
182+ *
183+ * @param other resolver
193184 */
194185 public void putRedirected(XmlResourceResolver other){
195186 this.uriMap.putAll(other.uriMap);
@@ -197,9 +188,10 @@ public class XmlResourceResolver
197188 }
198189
199190 /**
200- * 登録済みリダイレクト先URIを返す。
201- * @param original オリジナルURI
202- * @return リダイレクト先URI。未登録の場合はnull
191+ * get redirected local resource URI.
192+ *
193+ * @param original original URI
194+ * @return mapped local resource URI. null if unmapped.
203195 */
204196 public URI getRedirected(URI original){
205197 URI keyURI = original.normalize();
@@ -208,66 +200,67 @@ public class XmlResourceResolver
208200 }
209201
210202 /**
211- * 登録済みリダイレクト先URIを返す。
212- * @param original オリジナルURI
213- * @return リダイレクト先URI。未登録の場合はオリジナルを返す
214- */
215- public URI resolveRedirected(URI original){
216- URI result = getRedirected(original);
217- if(result == null) result = original;
218- return result;
219- }
220-
221- /**
222- * 登録済みリダイレクト先リソースの入力ストリームを得る。
223- * @param originalURI オリジナルURI
224- * @return 入力ストリーム。リダイレクト先が未登録の場合はnull
225- * @throws java.io.IOException 入出力エラー。
226- * もしくはリソースが見つからない。
203+ * get redirected local input stream.
204+ *
205+ * @param originalURI original URI
206+ * @return mapped local resource input stream.
207+ * If no mapping, return zero-length data stream.
208+ * @throws java.io.IOException local resource i/o error
227209 */
228210 private InputStream getXMLResourceAsStream(URI originalURI)
229211 throws IOException{
212+ InputStream result;
213+
230214 URI resourceURI = getRedirected(originalURI);
231- if(resourceURI == null) return null;
215+ if(resourceURI == null){
216+ result = new ByteArrayInputStream(new byte[0]);
217+ }else{
218+ URL resourceURL = resourceURI.toURL();
219+ result = resourceURL.openStream();
220+ }
232221
233- URL resourceURL = resourceURI.toURL();
234- InputStream is = resourceURL.openStream();
222+ result = new BufferedInputStream(result);
235223
236- return is;
224+ return result;
237225 }
238226
239227 /**
240228 * {@inheritDoc}
241- * URL変換したあとの入力ソースを返す。
229+ *
230+ * <p>Return redirected local resource data.
231+ *
242232 * @param type {@inheritDoc}
243233 * @param namespaceURI {@inheritDoc}
244234 * @param publicId {@inheritDoc}
245235 * @param systemId {@inheritDoc}
246236 * @param baseURI {@inheritDoc}
247- * @return {@inheritDoc}
237+ * @return {@inheritDoc} LSInput of local resource.
238+ * If no mapping, return zero-length data.
248239 */
249240 @Override
250- public LSInput resolveResource(String type,
251- String namespaceURI,
252- String publicId,
253- String systemId,
254- String baseURI ){
241+ public LSInput resolveResource(
242+ String type,
243+ String namespaceURI,
244+ String publicId,
245+ String systemId,
246+ String baseURI ){
255247 if(systemId == null) return null;
256248
257249 URI originalURI;
258250 try{
259251 originalURI = buildBaseRelativeURI(baseURI, systemId);
260252 }catch(URISyntaxException e){
261- return null;
253+ assert false;
254+ throw new AssertionError(e);
262255 }
263256
264257 InputStream is;
265258 try{
266259 is = getXMLResourceAsStream(originalURI);
267260 }catch(IOException e){
268- return null;
261+ assert false;
262+ throw new AssertionError(e);
269263 }
270- if(is == null) return null;
271264
272265 LSInput input = createLSInput();
273266 input.setBaseURI(baseURI);
@@ -278,212 +271,4 @@ public class XmlResourceResolver
278271 return input;
279272 }
280273
281- /**
282- * {@inheritDoc}
283- * URL変換したあとの入力ソースを返す。
284- * @param publicId {@inheritDoc}
285- * @param systemId {@inheritDoc}
286- * @return {@inheritDoc}
287- * @throws org.xml.sax.SAXException {@inheritDoc}
288- * @throws java.io.IOException {@inheritDoc}
289- */
290- @Override
291- public InputSource resolveEntity(String publicId, String systemId)
292- throws SAXException, IOException{
293- if(systemId == null) return null;
294-
295- URI originalUri;
296- try{
297- originalUri = new URI(systemId);
298- }catch(URISyntaxException e){
299- return null;
300- }
301-
302- InputStream is = getXMLResourceAsStream(originalUri);
303- if(is == null) return null;
304-
305- InputSource source = new InputSource(is);
306- source.setPublicId(publicId);
307- source.setSystemId(systemId);
308-
309- return source;
310- }
311-
312- /**
313- * JRE1.5用LSInput実装。
314- * JRE1.6なら
315- * org.w3c.dom.ls.DOMImplementationLS#createLSInput()
316- * で生成可能かも。
317- */
318- private static final class LSInputImpl implements LSInput {
319-
320- private String baseURI = null;
321- private InputStream byteStream = null;
322- private boolean certifiedText = false;
323- private Reader characterStream = null;
324- private String encoding = null;
325- private String publicId = null;
326- private String stringData = null;
327- private String systemId = null;
328-
329- /**
330- * コンストラクタ。
331- */
332- LSInputImpl(){
333- super();
334- return;
335- }
336-
337- /**
338- * {@inheritDoc}
339- * @return {@inheritDoc}
340- */
341- @Override
342- public String getBaseURI(){
343- return this.baseURI;
344- }
345-
346- /**
347- * {@inheritDoc}
348- * @param baseURI {@inheritDoc}
349- */
350- @Override
351- public void setBaseURI(String baseURI){
352- this.baseURI = baseURI;
353- return;
354- }
355-
356- /**
357- * {@inheritDoc}
358- * @return {@inheritDoc}
359- */
360- @Override
361- public InputStream getByteStream(){
362- return this.byteStream;
363- }
364-
365- /**
366- * {@inheritDoc}
367- * @param byteStream {@inheritDoc}
368- */
369- @Override
370- public void setByteStream(InputStream byteStream){
371- this.byteStream = byteStream;
372- }
373-
374- /**
375- * {@inheritDoc}
376- * @return {@inheritDoc}
377- */
378- @Override
379- public boolean getCertifiedText(){
380- return this.certifiedText;
381- }
382-
383- /**
384- * {@inheritDoc}
385- * @param certifiedText {@inheritDoc}
386- */
387- @Override
388- public void setCertifiedText(boolean certifiedText){
389- this.certifiedText = certifiedText;
390- return;
391- }
392-
393- /**
394- * {@inheritDoc}
395- * @return {@inheritDoc}
396- */
397- @Override
398- public Reader getCharacterStream(){
399- return this.characterStream;
400- }
401-
402- /**
403- * {@inheritDoc}
404- * @param characterStream {@inheritDoc}
405- */
406- @Override
407- public void setCharacterStream(Reader characterStream){
408- this.characterStream = characterStream;
409- }
410-
411- /**
412- * {@inheritDoc}
413- * @return {@inheritDoc}
414- */
415- @Override
416- public String getEncoding(){
417- return this.encoding;
418- }
419-
420- /**
421- * {@inheritDoc}
422- * @param encoding {@inheritDoc}
423- */
424- @Override
425- public void setEncoding(String encoding){
426- this.encoding = encoding;
427- return;
428- }
429-
430- /**
431- * {@inheritDoc}
432- * @return {@inheritDoc}
433- */
434- @Override
435- public String getPublicId(){
436- return this.publicId;
437- }
438-
439- /**
440- * {@inheritDoc}
441- * @param publicId {@inheritDoc}
442- */
443- @Override
444- public void setPublicId(String publicId){
445- this.publicId = publicId;
446- return;
447- }
448-
449- /**
450- * {@inheritDoc}
451- * @return {@inheritDoc}
452- */
453- @Override
454- public String getStringData(){
455- return this.stringData;
456- }
457-
458- /**
459- * {@inheritDoc}
460- * @param stringData {@inheritDoc}
461- */
462- @Override
463- public void setStringData(String stringData){
464- this.stringData = stringData;
465- return;
466- }
467-
468- /**
469- * {@inheritDoc}
470- * @return {@inheritDoc}
471- */
472- @Override
473- public String getSystemId(){
474- return this.systemId;
475- }
476-
477- /**
478- * {@inheritDoc}
479- * @param systemId {@inheritDoc}
480- */
481- @Override
482- public void setSystemId(String systemId){
483- this.systemId = systemId;
484- return;
485- }
486-
487- }
488-
489274 }
--- a/src/test/java/jp/sfjp/mikutoga/xml/DomUtilsTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
1-/*
2-@see https://www.w3.org/TR/xmlschema-2/
3- */
4-
5-package jp.sfjp.mikutoga.xml;
6-
7-import javax.xml.parsers.DocumentBuilder;
8-import javax.xml.parsers.DocumentBuilderFactory;
9-import javax.xml.parsers.ParserConfigurationException;
10-import org.junit.After;
11-import org.junit.AfterClass;
12-import org.junit.Before;
13-import org.junit.BeforeClass;
14-import org.junit.Test;
15-import static org.junit.Assert.*;
16-import org.w3c.dom.Document;
17-import org.w3c.dom.Element;
18-
19-/**
20- *
21- */
22-public class DomUtilsTest {
23-
24- private static final DocumentBuilderFactory FACTORY =
25- DocumentBuilderFactory.newInstance();
26- private static final DocumentBuilder BUILDER;
27-
28- private static final String TESTELEM = "testelem";
29- private static final String TESTATTR = "testattr";
30-
31- private static Element getTestAttredElem(String attrVal){
32- Document doc = BUILDER.newDocument();
33- Element elem = doc.createElement(TESTELEM);
34- elem.setAttribute(TESTATTR, attrVal);
35- return elem;
36- }
37-
38- static{
39- try{
40- BUILDER = FACTORY.newDocumentBuilder();
41- }catch(ParserConfigurationException e){
42- throw new ExceptionInInitializerError(e);
43- }
44- }
45-
46- public DomUtilsTest() {
47- }
48-
49- @BeforeClass
50- public static void setUpClass() throws ParserConfigurationException{
51- }
52-
53- @AfterClass
54- public static void tearDownClass() {
55- }
56-
57- @Before
58- public void setUp() {
59- }
60-
61- @After
62- public void tearDown() {
63- }
64-
65- /**
66- * Test of getBooleanAttr method, of class DomUtils.
67- */
68- @Test
69- public void testGetBooleanAttr() throws Exception {
70- System.out.println("getBooleanAttr");
71-
72- boolean result;
73- Element elem;
74-
75- elem = getTestAttredElem("true");
76- result = DomUtils.getBooleanAttr(elem, TESTATTR);
77- assertTrue(result);
78-
79- elem = getTestAttredElem("false");
80- result = DomUtils.getBooleanAttr(elem, TESTATTR);
81- assertFalse(result);
82-
83- elem = getTestAttredElem("0");
84- result = DomUtils.getBooleanAttr(elem, TESTATTR);
85- assertFalse(result);
86-
87- elem = getTestAttredElem("1");
88- result = DomUtils.getBooleanAttr(elem, TESTATTR);
89- assertTrue(result);
90-
91- elem = getTestAttredElem("\n\rtrue\u0020\t");
92- result = DomUtils.getBooleanAttr(elem, TESTATTR);
93- assertTrue(result);
94-
95- elem = getTestAttredElem("?");
96- try{
97- DomUtils.getBooleanAttr(elem, TESTATTR);
98- fail();
99- }catch(TogaXmlException e){
100- assert true;
101- }
102-
103- return;
104- }
105-
106- /**
107- * Test of getIntegerAttr method, of class DomUtils.
108- */
109- @Test
110- public void testGetIntegerAttr() throws TogaXmlException {
111- System.out.println("getIntegerAttr");
112-
113- int result;
114- Element elem;
115-
116- elem = getTestAttredElem("0");
117- result = DomUtils.getIntegerAttr(elem, TESTATTR);
118- assertEquals(0, result);
119-
120- elem = getTestAttredElem("1");
121- result = DomUtils.getIntegerAttr(elem, TESTATTR);
122- assertEquals(1, result);
123-
124- elem = getTestAttredElem("-1");
125- result = DomUtils.getIntegerAttr(elem, TESTATTR);
126- assertEquals(-1, result);
127-
128- elem = getTestAttredElem("999");
129- result = DomUtils.getIntegerAttr(elem, TESTATTR);
130- assertEquals(999, result);
131-
132- elem = getTestAttredElem("-9999");
133- result = DomUtils.getIntegerAttr(elem, TESTATTR);
134- assertEquals(-9999, result);
135-
136- elem = getTestAttredElem("\n\r999\u0020\t");
137- result = DomUtils.getIntegerAttr(elem, TESTATTR);
138- assertEquals(999, result);
139-
140- elem = getTestAttredElem("?");
141- try{
142- result = DomUtils.getIntegerAttr(elem, TESTATTR);
143- fail();
144- }catch(TogaXmlException e){
145- assert true;
146- }
147-
148- return;
149- }
150-
151- /**
152- * Test of getFloatAttr method, of class DomUtils.
153- */
154- @Test
155- public void testGetFloatAttr() throws TogaXmlException {
156- System.out.println("getFloatAttr");
157-
158- float result;
159- Element elem;
160-
161- elem = getTestAttredElem("0.0");
162- result = DomUtils.getFloatAttr(elem, TESTATTR);
163- assertEquals(0.0f, result, 0.0f);
164-
165- elem = getTestAttredElem("-0.0");
166- result = DomUtils.getFloatAttr(elem, TESTATTR);
167- assertEquals(0.0f, result, 0.0f);
168- assertEquals("-0.0", Float.toString(result));
169-
170- elem = getTestAttredElem("-123.456");
171- result = DomUtils.getFloatAttr(elem, TESTATTR);
172- assertEquals(-123.456f, result, 0.0f);
173-
174- elem = getTestAttredElem("654.321");
175- result = DomUtils.getFloatAttr(elem, TESTATTR);
176- assertEquals(654.321f, result, 0.0f);
177-
178- elem = getTestAttredElem("2.3E4");
179- result = DomUtils.getFloatAttr(elem, TESTATTR);
180- assertEquals(23000.0f, result, 0.0f);
181-
182- elem = getTestAttredElem("INF");
183- result = DomUtils.getFloatAttr(elem, TESTATTR);
184- assertEquals(Float.POSITIVE_INFINITY, result, 0.0f);
185-
186- elem = getTestAttredElem("+INF");
187- try{
188- DomUtils.getFloatAttr(elem, TESTATTR);
189- fail();
190- }catch(TogaXmlException e){
191- assert true;
192- }
193-
194- elem = getTestAttredElem("NaN");
195- result = DomUtils.getFloatAttr(elem, TESTATTR);
196- assertTrue(Float.isNaN(result));
197-
198- elem = getTestAttredElem("\n\r123.456\u0020\t");
199- result = DomUtils.getFloatAttr(elem, TESTATTR);
200- assertEquals(123.456f, result, 0.0f);
201-
202- elem = getTestAttredElem("?");
203- try{
204- DomUtils.getFloatAttr(elem, TESTATTR);
205- fail();
206- }catch(TogaXmlException e){
207- assert true;
208- }
209-
210- return;
211- }
212-
213-}
Show on old repository browser