From: Olyutorskii Date: Sat, 29 Jun 2019 16:25:42 +0000 (+0900) Subject: Merge topic/xxe into develop X-Git-Tag: release-3.122.2^2~2 X-Git-Url: http://git.osdn.net/view?p=mikutoga%2FTogaGem.git;a=commitdiff_plain;h=ed542b5a7123483f5abaa08d326cb42b32e4b465;hp=c3c1b3569393e6133cb30491740b70f2ad727e6e Merge topic/xxe into develop --- diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 8577d10..c5293d0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -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. diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml index 5f6d0fd..3860edf 100644 --- a/config/checkstyle/checkstyle-suppressions.xml +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -6,7 +6,7 @@ + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ad3deac..0b1ceb5 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -6,7 +6,7 @@ diff --git a/src/main/java/jp/sfjp/mikutoga/typical/I18nAlias.java b/src/main/java/jp/sfjp/mikutoga/typical/I18nAlias.java index 4c4de31..5942a41 100644 --- 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; 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 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 index f36313c..0000000 --- a/src/main/java/jp/sfjp/mikutoga/xml/DomNsUtils.java +++ /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ユーティリティ(名前空間対応)。 - * - *

各種名前空間引数にnullが渡された場合、全ての名前空間にマッチする。 - * - *

各種ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。 - * - *

ノードの持つ名前空間が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; - } - - /** - * 指定された名前空間とローカル名に合致する最初の直下子要素を返す。 - * - *

見つからなければ例外を投げる。 - * - * @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 getEachChild(final Element parent, - final String nsuri, - final String localName ){ - Iterable result = new Iterable(){ - @Override - public Iterator 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 index 1861728..0000000 --- a/src/main/java/jp/sfjp/mikutoga/xml/DomUtils.java +++ /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 getChildList(Element parent, - String childTag){ - List 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 getChildIterator(Element parent, - String childTag){ - Element firstElem; - try{ - firstElem = getChild(parent, childTag); - }catch(TogaXmlException e){ - firstElem = null; - } - - Iterator result = new ElemIterator(firstElem); - - return result; - } - - /** - * 指定された名前の子要素のforeachを返す。 - * @param parent 親要素 - * @param childTag 子要素名 - * @return 子要素のforeach - */ - public static Iterable getEachChild(Element parent, - String childTag){ - final Iterator iterator = getChildIterator(parent, childTag); - Iterable result = new Iterable(){ - @Override - public Iterator 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 { - 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 index 0ccccd4..0000000 --- a/src/main/java/jp/sfjp/mikutoga/xml/LocalXmlResource.java +++ /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; - -/** - * 代用ローカルリソースの管理を行う。 - * - *

ネットワークを介したグローバルなリソースと、 - * アプリ上のローカルな代用リソースとを対応付ける。 - */ -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 index 0000000..7048e0e --- /dev/null +++ b/src/main/java/jp/sfjp/mikutoga/xml/NoopEntityResolver.java @@ -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 + * XML external entity attack (Wikipedia) + * + */ +public final class NoopEntityResolver implements EntityResolver{ + + /** Singleton resolver. */ + public static final EntityResolver NOOP_RESOLVER = + new NoopEntityResolver(); + + + /** + * Constructor. + */ + private NoopEntityResolver(){ + super(); + return; + } + + + /** + * {@inheritDoc} + * + *

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; + } + +} diff --git a/src/main/java/jp/sfjp/mikutoga/xml/SchemaUtil.java b/src/main/java/jp/sfjp/mikutoga/xml/SchemaUtil.java index bd1541e..c0f9eb2 100644 --- 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; 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. + * + *

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. + * + *

Includes some considerations for XXE vulnerabilities. + * + *

Restrict access to + * External Entity Reference & external DTDs + * in xml schema file. + * + *

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 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 & pre-compile local schema files. * - *

任意のリゾルバを指定可能 - * - * @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 index 63912bc..0000000 --- a/src/main/java/jp/sfjp/mikutoga/xml/SiblingElemIterator.java +++ /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。 - * - *

同じ親と名前空間とローカル名を持つ要素同士を「兄弟要素」とする。 - * - *

ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。 - * - *

削除操作は未サポート。 - */ -public class SiblingElemIterator implements Iterator { - - 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; - } - - /** - * コンストラクタ。 - * - *

名前空間引数にnullが渡された場合、全ての名前空間にマッチする。 - * - *

ローカル名引数に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(); - } - -} diff --git a/src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java b/src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java index 196a900..bf5ba8e 100644 --- a/src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java +++ b/src/main/java/jp/sfjp/mikutoga/xml/XmlResourceResolver.java @@ -7,82 +7,108 @@ 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 & 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 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 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 & 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 & 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 & 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変換したあとの入力ソースを返す。 + * + *

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 index f319a31..0000000 --- a/src/test/java/jp/sfjp/mikutoga/xml/DomUtilsTest.java +++ /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; - } - -}