OSDN Git Service

XML名前空間対応
authorOlyutorskii <olyutorskii@users.osdn.me>
Tue, 23 Aug 2011 15:57:20 +0000 (00:57 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Tue, 23 Aug 2011 15:57:20 +0000 (00:57 +0900)
src/main/java/jp/sourceforge/mikutoga/xml/DomNsUtils.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/xml/SiblingElemIterator.java [new file with mode: 0644]

diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/DomNsUtils.java b/src/main/java/jp/sourceforge/mikutoga/xml/DomNsUtils.java
new file mode 100644 (file)
index 0000000..8e338b8
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * XML DOM utilities with namespace
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.xml;
+
+import java.text.MessageFormat;
+import java.util.Iterator;
+import javax.xml.bind.DatatypeConverter;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * DOMユーティリティ(名前空間対応)。
+ * <p>各種名前空間引数にnullが渡された場合、全ての名前空間にマッチする。
+ * <p>各種ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。
+ * <p>ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。
+ */
+public final class DomNsUtils {
+
+    private static final String ERRMSG_NOELEM =
+            "Elem:[{0}] was not found in Elem:[{1}]";
+    private static final String ERRMSG_NOATTR =
+            "Attr:[{0}] was not found in Elem:[{1}]";
+    private static final String ERRMSG_INVATTR =
+            "Invalid attribute form Attr[{0}] Value[{1}]";
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private DomNsUtils(){
+        assert false;
+        throw new AssertionError();
+    }
+
+
+    /**
+     * 名前空間とローカル名が一致するノードか判定する。
+     * @param node ノード
+     * @param nsuri 名前空間URI
+     * @param localName ローカル名。
+     * @return ノードの名前空間およびローカル名が一致したらtrue
+     */
+    public static boolean hasNsLocalNameNode(Node node,
+                                             String nsuri,
+                                             String localName ){
+        String nodeLocalName = node.getLocalName();
+        String nodeNsUri     = node.getNamespaceURI();
+
+        boolean needLocalCmp =
+                localName != null;
+        boolean needUriCmp =
+                nsuri != null && nodeNsUri != null;
+
+        if(needLocalCmp && ! localName.equals(nodeLocalName) ) return false;
+        if(needUriCmp   && ! 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 最初の直下子要素。見つからなければnull。
+     */
+    public static Element pickFirstChild(Node parent,
+                                         String nsuri,
+                                         String localName ){
+        Node node = parent.getFirstChild();
+        while(node != null){
+            if(hasNsLocalNameElem(node, nsuri, localName)){
+                break;
+            }
+            node = node.getNextSibling();
+        }
+        return (Element) node;
+    }
+
+    /**
+     * 指定された名前空間とローカル名に合致する最初の直下子要素を返す。
+     * <p>見つからなければ例外を投げる。
+     * @param parent 親要素
+     * @param nsuri 名前空間URI
+     * @param localName ローカル名
+     * @return 最初の直下子要素。
+     * @throws TogaXmlException 1つも見つからなかった
+     */
+    public static Element getFirstChild(Element parent,
+                                        String nsuri,
+                                        String localName )
+            throws TogaXmlException{
+        Element elem = pickFirstChild(parent, nsuri, localName);
+
+        if(elem == null){
+            String message = MessageFormat.format(ERRMSG_NOELEM,
+                                                  localName,
+                                                  parent.getLocalName() );
+            throw new TogaXmlException(message);
+        }
+
+        return elem;
+    }
+
+    /**
+     * 指定された名前の子要素のforeachを返す。
+     * @param parent 親要素
+     * @param nsuri 名前空間URI
+     * @param localName 子要素名
+     * @return 子要素のforeach
+     */
+    public static Iterable<Element> getEachChild(final Element parent,
+                                                 final String nsuri,
+                                                 final String localName ){
+        Iterable<Element> result = new Iterable<Element>(){
+            @Override
+            public Iterator<Element> iterator(){
+                return new SiblingElemIterator(parent, nsuri, localName);
+            }
+        };
+        return result;
+    }
+
+    /**
+     * 要素に属性が存在するか判定する。
+     * @param elem 要素
+     * @param nsuri 名前空間URI
+     * @param localName ローカル名
+     * @return 存在するならtrue
+     */
+    public static boolean hasAttrNS(Element elem,
+                                    String nsuri,
+                                    String localName ){
+        return elem.hasAttributeNS(nsuri, localName);
+    }
+
+    /**
+     * 要素からxsd:string型属性値を読み取る。
+     * @param elem 要素
+     * @param nsuri 名前空間URI
+     * @param localName 属性名
+     * @return 文字列
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    public static String getStringAttrNS(Element elem,
+                                         String nsuri,
+                                         String localName )
+            throws TogaXmlException{
+        if( ! hasAttrNS(elem, nsuri, localName) ){
+            String message = MessageFormat.format(ERRMSG_NOATTR,
+                                                  localName,
+                                                  elem.getLocalName() );
+            throw new TogaXmlException(message);
+        }
+
+        String result;
+        try{
+            result = elem.getAttributeNS(nsuri, localName);
+        }catch(DOMException e){
+            assert false;
+            throw new AssertionError(e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 要素からxsd:boolean型属性値を読み取る。
+     * @param elem 要素
+     * @param nsuri 名前空間URI
+     * @param localName 属性名
+     * @return 真ならtrue
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    public static boolean getBooleanAttrNS(Element elem,
+                                           String nsuri,
+                                           String localName )
+            throws TogaXmlException{
+        String value = getStringAttrNS(elem, nsuri, localName);
+
+        boolean result;
+        try{
+            result = DatatypeConverter.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 = DatatypeConverter.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 = DatatypeConverter.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/sourceforge/mikutoga/xml/SiblingElemIterator.java b/src/main/java/jp/sourceforge/mikutoga/xml/SiblingElemIterator.java
new file mode 100644 (file)
index 0000000..8337298
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * sibling element iterator on DOM tree
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.xml;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * 兄弟要素間用Iterator。
+ * <p>同じ親と名前空間とローカル名を持つ要素同士を「兄弟要素」とする。
+ * <p>ノードの持つ名前空間がnullの場合、全ての名前空間引数にマッチする。
+ * <p>削除操作は未サポート。
+ */
+public class SiblingElemIterator implements Iterator<Element> {
+
+    private Element next;
+    private final String nsuri;
+    private final String localName;
+
+
+    /**
+     * コンストラクタ。
+     * @param first 最初の兄弟要素。nullだと一度もiterateしない。
+     */
+    public SiblingElemIterator(Element first){
+        super();
+
+        this.next = first;
+
+        if(this.next == null){
+            this.nsuri     = null;
+            this.localName = null;
+        }else{
+            this.nsuri     = this.next.getNamespaceURI();
+            this.localName = this.next.getLocalName();
+        }
+
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     * <p>名前空間引数にnullが渡された場合、全ての名前空間にマッチする。
+     * <p>ローカル名引数にnullが渡された場合、全てのローカル名にマッチする。
+     * @param parent 親要素
+     * @param nsuri 子要素の名前空間URI
+     * @param localName 子要素のローカル名
+     */
+    public SiblingElemIterator(Element parent,
+                               String nsuri,
+                               String localName ){
+        super();
+
+        this.next = DomNsUtils.pickFirstChild(parent, nsuri, localName);
+
+        if(this.next == null){
+            this.nsuri     = null;
+            this.localName = null;
+        }else{
+            this.nsuri     = nsuri;
+            this.localName = localName;
+        }
+
+        return;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public boolean hasNext(){
+        if(this.next != null) return true;
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     * @throws NoSuchElementException {@inheritDoc}
+     */
+    @Override
+    public Element next() throws NoSuchElementException {
+        if(this.next == null) throw new NoSuchElementException();
+
+        Element result = this.next;
+
+        Node sibNode = result;
+        do{
+            sibNode = sibNode.getNextSibling();
+            if(sibNode == null) break;
+        }while( ! matchElemName(sibNode) );
+        this.next = (Element) sibNode;
+
+        return result;
+    }
+
+    /**
+     * 兄弟要素にふさわしい名前を持つか判定する。
+     * @param node 判定対象
+     * @return 兄弟にふさわしい名前を持つならtrue
+     */
+    private boolean matchElemName(Node node){
+        return DomNsUtils.hasNsLocalNameElem(node,
+                                             this.nsuri, this.localName );
+    }
+
+    /**
+     * {@inheritDoc}
+     * ※削除不可。
+     * @throws UnsupportedOperationException 削除を試みたので失敗した
+     */
+    @Override
+    public void remove() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+}