OSDN Git Service

Appendable対応
[mikutoga/TogaGem.git] / src / main / java / jp / sfjp / mikutoga / xml / DomUtils.java
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 javax.xml.bind.DatatypeConverter;
15 import org.w3c.dom.Element;
16 import org.w3c.dom.Node;
17
18 /**
19  * DOMユーティリティ。
20  */
21 public final class DomUtils {
22
23     // 構文解析バグ回避。
24     private static final char BS_CHAR = (char) 0x005c;
25
26     /**
27      * 隠しコンストラクタ。
28      */
29     private DomUtils(){
30         super();
31         assert false;
32         throw new AssertionError();
33     }
34
35     /**
36      * 要素からxsd:string型属性値を読み取る。
37      * @param elem 要素
38      * @param attrName 属性名
39      * @return 文字列
40      * @throws TogaXmlException 属性値が見つからなかった。
41      */
42     public static String getStringAttr(Element elem, String attrName)
43             throws TogaXmlException{
44         if( ! elem.hasAttribute(attrName) ){
45             String message = "Attr:[" + attrName + "] "
46                     + "was not found in "
47                     + "Elem:[" + elem.getTagName()+"]";
48             throw new TogaXmlException(message);
49         }
50
51         String result;
52         try{
53             result = elem.getAttribute(attrName);
54         }catch(IllegalArgumentException e){
55             String message = "Invalid attribute form [" + attrName + "]";
56             throw new TogaXmlException(message, e);
57         }
58
59         return result;
60     }
61
62     /**
63      * 要素からxsd:boolean型属性値を読み取る。
64      * @param elem 要素
65      * @param attrName 属性名
66      * @return 真ならtrue
67      * @throws TogaXmlException 属性値が見つからなかった。
68      */
69     public static boolean getBooleanAttr(Element elem, String attrName)
70             throws TogaXmlException{
71         String value = getStringAttr(elem, attrName);
72
73         boolean result;
74         try{
75             result = DatatypeConverter.parseBoolean(value);
76         }catch(IllegalArgumentException e){
77             String message =
78                     "Invalid boolean attribute form "
79                     + "[" + attrName + "][" + value + "]";
80             throw new TogaXmlException(message, e);
81         }
82
83         return result;
84     }
85
86     /**
87      * 要素からxsd:integer型属性値を読み取る。
88      * @param elem 要素
89      * @param attrName 属性名
90      * @return int値
91      * @throws TogaXmlException 属性値が見つからなかった。
92      */
93     public static int getIntegerAttr(Element elem, String attrName)
94             throws TogaXmlException{
95         String value = getStringAttr(elem, attrName);
96
97         int result;
98         try{
99             result = DatatypeConverter.parseInt(value);
100         }catch(IllegalArgumentException e){
101             String message =
102                     "Invalid integer attribute form "
103                     + "[" + attrName + "][" + value + "]";
104             throw new TogaXmlException(message, e);
105         }
106
107         return result;
108     }
109
110     /**
111      * 要素からxsd:float型属性値を読み取る。
112      * @param elem 要素
113      * @param attrName 属性名
114      * @return float値
115      * @throws TogaXmlException 属性値が見つからなかった。
116      */
117     public static float getFloatAttr(Element elem, String attrName)
118             throws TogaXmlException{
119         String value = getStringAttr(elem, attrName);
120
121         float result;
122         try{
123             result = DatatypeConverter.parseFloat(value);
124         }catch(IllegalArgumentException e){
125             String message =
126                     "Invalid float attribute form "
127                     + "[" + attrName + "][" + value + "]";
128             throw new TogaXmlException(message, e);
129         }
130
131         return result;
132     }
133
134     /**
135      * 要素から日本語Windows用ファイル名を属性値として読み取る。
136      * 念のため文字U+00A5は文字U-005Cに変換される。
137      * @param elem 要素
138      * @param attrName 属性名
139      * @return ファイル名
140      * @throws TogaXmlException 属性値が見つからなかった。
141      */
142     public static String getSjisFileNameAttr(Element elem, String attrName)
143             throws TogaXmlException{
144         String result;
145         try{
146             result = getStringAttr(elem, attrName);
147         }catch(IllegalArgumentException e){
148             String message =
149                     "Invalid winfile attribute form "
150                     + "[" + attrName + "]";
151             throw new TogaXmlException(message, e);
152         }
153
154         result = result.replace("" + '\u00a5', "" + BS_CHAR);
155
156         return result;
157     }
158
159     /**
160      * 指定された名前の子要素を1つだけ返す。
161      * @param parent 親要素
162      * @param tagName 子要素名
163      * @return 子要素
164      * @throws TogaXmlException 1つも見つからなかった
165      */
166     public static Element getChild(Element parent, String tagName)
167             throws TogaXmlException{
168         Element result = null;
169
170         for(Node node = parent.getFirstChild();
171             node != null;
172             node = node.getNextSibling() ){
173
174             if(node.getNodeType() != Node.ELEMENT_NODE) continue;
175             Element elem = (Element) node;
176
177             String elemTagName = elem.getTagName();
178             if( tagName.equals(elemTagName) ){
179                 result = elem;
180                 break;
181             }
182         }
183
184         if(result == null){
185             String message =
186                     "Elem:[" + tagName + "] was not found in "
187                     +"Elem:[" + parent.getTagName() + "]";
188             throw new TogaXmlException(message);
189         }
190
191         return result;
192     }
193
194     /**
195      * 親要素が指定された名前の子要素を持つか判定する。
196      * @param parent 親要素
197      * @param tagName 子要素名
198      * @return 指定名の子要素が存在すればtrue
199      */
200     public static boolean hasChild(Element parent, String tagName){
201         for(Node node = parent.getFirstChild();
202             node != null;
203             node = node.getNextSibling() ){
204
205             if(node.getNodeType() != Node.ELEMENT_NODE) continue;
206             Element elem = (Element) node;
207
208             String elemTagName = elem.getTagName();
209             if( tagName.equals(elemTagName) ) return true;
210         }
211
212         return false;
213     }
214
215     /**
216      * 指定された名前の子要素のリストを返す。
217      * @param parent 親要素
218      * @param childTag 子要素名
219      * @return 子要素のリスト
220      */
221     public static List<Element> getChildList(Element parent,
222                                                String childTag){
223         List<Element> result = new LinkedList<Element>();
224
225         for(Node node = parent.getFirstChild();
226             node != null;
227             node = node.getNextSibling() ){
228
229             if(node.getNodeType() != Node.ELEMENT_NODE) continue;
230             Element elem = (Element) node;
231
232             String tagName = elem.getTagName();
233             if( ! childTag.equals(tagName) ) continue;
234
235             result.add(elem);
236         }
237
238         return result;
239     }
240
241     /**
242      * 指定された名前の子要素の列挙子を返す。
243      * @param parent 親要素
244      * @param childTag 子要素名
245      * @return 子要素の列挙子
246      */
247     public static Iterator<Element> getChildIterator(Element parent,
248                                                        String childTag){
249         Element firstElem;
250         try{
251             firstElem = getChild(parent, childTag);
252         }catch(TogaXmlException e){
253             firstElem = null;
254         }
255
256         Iterator<Element> result = new ElemIterator(firstElem);
257
258         return result;
259     }
260
261     /**
262      * 指定された名前の子要素のforeachを返す。
263      * @param parent 親要素
264      * @param childTag 子要素名
265      * @return 子要素のforeach
266      */
267     public static Iterable<Element> getEachChild(Element parent,
268                                                    String childTag){
269         final Iterator<Element> iterator = getChildIterator(parent, childTag);
270         Iterable<Element> result = new Iterable<Element>(){
271             @Override
272             public Iterator<Element> iterator(){
273                 return iterator;
274             }
275         };
276         return result;
277     }
278
279     /**
280      * 要素の次の要素を返す。
281      * @param elem 要素
282      * @return 次の要素。なければnull
283      */
284     public static Element nextElement(Element elem){
285         Node nextNode = elem;
286         for(;;){
287             nextNode = nextNode.getNextSibling();
288             if(nextNode == null) break;
289             if(nextNode.getNodeType() == Node.ELEMENT_NODE){
290                 break;
291             }
292         }
293
294         return (Element) nextNode;
295     }
296
297     /**
298      * 同じ要素名を持つ次の要素を返す。
299      * @param elem 要素
300      * @return 次の要素。なければnull
301      */
302     public static Element nextNamedElement(Element elem){
303         String tagName = elem.getTagName();
304         Element nextElem = elem;
305         for(;;){
306             nextElem = nextElement(nextElem);
307             if(nextElem == null) break;
308             if(tagName.equals(nextElem.getTagName())) break;
309         }
310
311         return nextElem;
312     }
313
314     /**
315      * 同じ親要素と同じ要素名を持つ兄弟要素を列挙する列挙子。
316      */
317     private static final class ElemIterator implements Iterator<Element> {
318         private Element next;
319
320         /**
321          * コンストラクタ。
322          * @param elem 最初の要素。nullを指定すれば空列挙子となる。
323          */
324         ElemIterator(Element elem){
325             super();
326             this.next = elem;
327         }
328
329         /**
330          * {@inheritDoc}
331          * @return {@inheritDoc}
332          */
333         @Override
334         public boolean hasNext(){
335             if(this.next == null) return false;
336             return true;
337         }
338
339         /**
340          * {@inheritDoc}
341          * @return {@inheritDoc}
342          * @throws NoSuchElementException {@inheritDoc}
343          */
344         @Override
345         public Element next() throws NoSuchElementException{
346             if(this.next == null) throw new NoSuchElementException();
347             Element result = this.next;
348             this.next = nextNamedElement(this.next);
349             return result;
350         }
351
352         /**
353          * {@inheritDoc}
354          * ※ 未サポート。
355          */
356         @Override
357         public void remove(){
358             throw new UnsupportedOperationException();
359         }
360
361     }
362
363 }