001    /*
002     * Copyright (c) 2009 The openGion Project.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013     * either express or implied. See the License for the specific language
014     * governing permissions and limitations under the License.
015     */
016    package org.opengion.fukurou.xml;
017    
018    import java.util.List;
019    import java.util.ArrayList;
020    
021    /**
022     * ノã?ドã?基底クラスとなるã?OGNode クラスを定義しますã?
023     *
024     * OGElement、OGDocument は、この、OGNode クラスを継承しますã?
025     * ただしã?OGAttributes は、独立してã�?‚‹ため、このクラスは継承してã�?�¾せんã€?
026     *
027     * æœ?‚‚ä¸?ˆ¬çš?�ªノã?ドã?、テキストノードでありã€?
028     *
029     * OGNode は、enum OGNodeType で区別される状態を持ってã�?�¾すã?
030     * そã?å†??OGElement と OGDocument は、サブクラスになってã�?�¾すã?
031     * OGNodeType は、それぞれã?再設定が可能ですã?
032     * 例えば、既存ã?エレメントやノã?ドに対して、コメントタイãƒ?Comment)を指定するとã€?
033     * ファイル等への出力時にコメントとして出力されますã?
034     *
035     * ã€??Listã€??ã€?å†?ƒ¨に、OGNode の ArrayList を持つ
036     * ã€??Textã€??ã€?å†?ƒ¨は、文字å?の BODY 部åˆ?‚’持つ
037     * ã€??Comment ã€?å†?ƒ¨は、文字å?であるがã?toString() 時には、コメント記号を前後に出力するã?
038     * ã€??Cdata ã€??:å†?ƒ¨は、TextNodeのArrayList を持つ、toString() 時には、Cdataを前後に出力するã?
039     * ã€??Element ã€?タグ名ã?属æ?、OGNode の ArrayList の入れ子状態をもつ
040     * ã€??Documentã€?トップã?Element として、read/write するときに使用。構é?は、唯ä¸?? OGElement を持つ List タイãƒ?
041     *
042     * @og.rev 5.1.8.0 (2010/07/01) 新規作æ?
043     * @og.rev 5.6.1.2 (2013/02/22) 構想からã‚?‚Š直ã�?
044     *
045     * @version  5.0
046     * @author   Kazuhiko Hasegawa
047     * @since    JDK6.0,
048     */
049    public class OGNode {
050            public static final String CR  = System.getProperty("line.separator");
051    //      public static final String TAB = "\t" ;
052    
053            private final List<OGNode> nodes = new ArrayList<OGNode>();         // ノã?ドリスãƒ?
054            private final String    text;                                                                   // ãƒ?‚­ストノード用のæ–?­—å?ノã?ドå?
055            private OGNodeType              nodeType ;                                                              // List,Text,Comment,Cdata,Element,Document
056            private OGNode                  parentNode = null;                                              // 自身の親ノã?ãƒ?ただしã?æœ?µ‚セãƒ?ƒˆされたノーãƒ?
057    
058            /**
059             * ãƒ?ƒ•ォルトコンストラクター
060             *
061             * ここでは、NodeType は、List に設定されますã?
062             */
063            public OGNode() {
064                    this.text  = null;
065                    nodeType   = OGNodeType.List;
066            }
067    
068            /**
069             * ãƒ?‚­ストノードを構築するためã?コンストラクター
070             *
071             * ãƒ?‚­ストノードã?、簡易的に、å?部には、ノードリストではなく文字å?を持ってã�?�¾すã?
072             *
073             * @og.rev 5.6.1.2 (2013/02/22) å†?ƒ¨ãƒ?‚­ストがなã�??合ã?タグの終äº?™‚にスペã?スは入れなã�??
074             *
075             * ここでは、NodeType は、Text に設定されますã?
076             * ただしã?引数のãƒ?‚­ストが null のNodeType は、List に設定されますã?
077             *
078             * @param       txt     ãƒ?‚­ストノードã?設定å?
079             */
080            public OGNode( final String txt ) {
081                    text = txt ;
082                    if( text != null )      { nodeType = OGNodeType.Text; }
083                    else                            { nodeType = OGNodeType.List; }
084            }
085    
086            /**
087             * ãƒ?‚­ストノードをノã?ドリストに追åŠ?�—ますã?
088             *
089             * å†?ƒ¨çš?�«ãƒ?‚­ストノードを構築して、リストに追åŠ?�—てã�?�¾すã?
090             * 戻りå?は、StringBuilder#append(String) の様にã€??結登録できるように
091             * 自åˆ??身を返してã�?�¾すã?
092             * ãƒ?‚­ストノードに、この処ç�?‚’行うと、エラーになりますã?
093             * ä¸?—¦、テキストノードとして作æ?したノã?ドには、ノードを追åŠ?�§きませんã€?
094             *
095             * @param       txt     ãƒ?‚­ストノードã?設定å?
096             *
097             * @return      自åˆ??身(this)のノã?ãƒ?
098             */
099            public OGNode addNode( final String txt ) {
100                    if( txt != null ) {
101                            if( nodeType == OGNodeType.Text ) {
102                                    // ãƒ?‚­ストノードにノã?ドã?追åŠ?�§きませんã€?
103                                    String errMsg = "ä¸?—¦、テキストノードとして作æ?したノã?ドには、ノードを追åŠ?�§きませんã€?;
104                                    throw new RuntimeException( errMsg );
105                            }
106    
107                            OGNode node = new OGNode( txt );
108                            node.parentNode = this;
109                            nodes.add( node );
110                    }
111                    return this;
112            }
113    
114            /**
115             * ノã?ドをノã?ドリストに追åŠ?�—ますã?
116             *
117             * 追åŠ?�™るノードã?親として、è?åˆ??身を登録しますã?
118             * なおã?同じオブジェクトを、è¤?•°の親に追åŠ?�™るå?å�?ノã?ドリストには追åŠ?�¯能)はã€?
119             * 親ノã?ドã?、最後に登録されたノードã?みが設定されますã?
120             * ãƒ?‚­ストノードに、この処ç�?‚’行うと、エラーになりますã?
121             * ä¸?—¦、テキストノードとして作æ?したノã?ドには、ノードを追åŠ?�§きませんã€?
122             *
123             * @param       node    ノã?ãƒ?
124             *
125             * @return      自åˆ??身(this)のノã?ãƒ?
126             */
127            public OGNode addNode( final OGNode node ) {
128                    if( node != null ) {
129                            if( nodeType == OGNodeType.Text ) {
130                                    // ãƒ?‚­ストノードにノã?ドã?追åŠ?�§きませんã€?
131                                    String errMsg = "ä¸?—¦、テキストノードとして作æ?したノã?ドには、ノードを追åŠ?�§きませんã€?;
132                                    throw new RuntimeException( errMsg );
133                            }
134    
135                            node.parentNode = this;
136                            nodes.add( node );
137                    }
138                    return this;
139            }
140    
141            /**
142             * ノã?ドリストに追åŠ?�•れてã�?‚‹、ノードã?個数を返しますã?
143             *
144             * @return      ノã?ドリストã?数
145             */
146            public int nodeSize() {
147                    return nodes.size();
148            }
149    
150            /**
151             * ノã?ドリストに追åŠ?�•れてã�?‚‹、ノードを返しますã?
152             *
153             * ノã?ドã?æŒ?®šにはã€??列番号を使用しますã?
154             * ノã?ドã?個数は、事前に、nodeSize() で調べて置ã�?�¦くださいã€?
155             * 当然、テキストノードã?場合ã?、nodeSize()==0 なのでã€?
156             * こã?メソãƒ?ƒ‰では取得できませんã€?
157             *
158             * @param       adrs    ノã?ドリストã?位置
159             *
160             * @return      æŒ?®šã?配å?番号のノã?ãƒ?
161             */
162            public OGNode getNode( final int adrs ) {
163                    return nodes.get(adrs);
164            }
165    
166            /**
167             * ノã?ドリストに、ノードをセãƒ?ƒˆしますã?
168             *
169             * ノã?ドリストã?æŒ?®šã?アドレスに、ノードをセãƒ?ƒˆしますã?
170             * これは、追åŠ?�§はなく置換えになりますã?
171             * ノã?ドã?æŒ?®šにはã€??列番号を使用しますã?
172             * ノã?ドã?個数は、事前に、nodeSize() で調べて置ã�?�¦くださいã€?
173             *
174             * @param       adrs    ノã?ドリストã?位置
175             * @param       node    セãƒ?ƒˆするノã?ãƒ?
176             */
177            public void setNode( final int adrs , final OGNode node ) {
178                    nodes.set(adrs,node);
179            }
180    
181            /**
182             * 自身にセãƒ?ƒˆされてã�?‚‹、親ノã?ドを返しますã?
183             *
184             * 親ノã?ドã?、è?身のオブジェクトに、ä¸?�¤しか設定できませんã€?
185             * これは、オブジェクトとして、同ä¸?ƒŽードを、è¤?•°の親ノã?ドに
186             * 追åŠ?�—たå?å�?これは、ノードリストへの追åŠ?�ªので可能)æœ?¾Œに追åŠ?�—ã�?
187             * 親ノã?ドã?み、保持してã�?‚‹ことになりますã?
188             * XML を構築するときã?、同ä¸??ノã?ドであってもã?毎回、作æ?しなおさなã�?�¨ã€?
189             * 親ノã?ドを見つけて、何かを行う場合には、おかしな動きをすることになりますã?
190             * なおã?ノã?ドオブジェクトè?体が、親ノã?ドから削除されてもã?自身の
191             * 親ノã?ド情報は保持し続けてã�?�¾すã?
192             * ある Element から削除したノã?ドを別のElementに追åŠ?�™ると、その時点でã€?
193             * 親ノã?ドも更新されますã?
194             *
195             * @return      親ノã?ãƒ?
196             */
197            public OGNode getParentNode() {
198                    return parentNode;
199            }
200    
201            /**
202             * 自身にセãƒ?ƒˆされてã�?‚‹、親ノã?ドã?階層数を返しますã?
203             *
204             * 自身のオブジェクトに設定されてã�?‚‹親ノã?ドをé ?•ªにさかのぼってã€?
205             * 何階層あるか返しますã?
206             * これは、getText(int) の引数に使えますã?
207             * 親ノã?ドがひとつもなã�??合ã?つまりè?身が最上位ã?場合ã?ã€? が返されますã?
208             *
209             * @return      自身の階層
210             */
211            public int getParentCount() {
212                    int para = 0;
213                    OGNode node = getParentNode();
214                    while( node != null ) {
215                            para++ ;
216                            node = node.getParentNode();
217                    }
218                    return para;
219            }
220    
221            /**
222             * ノã?ドリストからã?æŒ?®šã?配å?番号の、ノードを削除しますã?
223             *
224             * ノã?ドã?æŒ?®šにはã€??列番号を使用しますã?
225             * ノã?ドã?個数は、事前に、nodeSize() で調べて置ã�?�¦くださいã€?
226             *
227             * @param       adrs    ノã?ドリストã?位置
228             *
229             * @return      削除されたノーãƒ?
230             */
231            public OGNode removeNode( final int adrs ) {
232                    return nodes.remove(adrs);
233            }
234    
235            /**
236             * ノã?ドリストからã?すべてのノã?ドを削除しますã?
237             *
238             * これは、ノードリストをクリアしますã?
239             *
240             */
241            public void clearNode() {
242                    nodes.clear();
243            }
244    
245            /**
246             * ノã?ドリストからã?æŒ?®šã?ノã?ãƒ?orgNode)を新しいノã?ãƒ?newNode)に置き換えますã?
247             *
248             * ノã?ドã?、それぞれã?ノã?ドが作æ?されたé?番で、ユニã?クな番号を持ってã�?�¾すã?
249             * そã?番号をå?に、ノードを探しå?して、置き換えますã?
250             * 通常の、XMLパã?スから作æ?されたノードã?、すべてä¸?„�にユニã?ク番号が振られますがã€?
251             * 新しくつったノードをè¤?•°のノã?ドと置き換える場合ã?置き換えられた後ã?ノã?ドã?ã€?
252             * オブジェクトそのもã?がã?同ä¸?�«なるためã?注意がå¿?¦�ですã?
253             *
254             * @param       orgNode 置換å?のオリジナルノã?ãƒ?
255             * @param       newNode 置換する新しいノã?ãƒ?
256             */
257            public void changeNode( final OGNode orgNode , final OGNode newNode ) {
258                    int size = nodes.size();
259                    for( int i=0; i<size; i++ ) {
260                            OGNode node = nodes.get(i);
261    //                      if( node.nodeNo == orgNode.nodeNo ) {
262                            if( node.equals( orgNode ) ) {          // Object.equals なので、オブジェクトそのもã?のä¸??判å®?
263                                    nodes.set( i,newNode );
264                            }
265                            else {
266                                    node.changeNode( orgNode,newNode );
267                            }
268                    }
269            }
270    
271            /**
272             * ノã?ドリストからã?直ä¸?メンバã?)のエレメントã?みをリストにして返しますã?
273             *
274             * ノã?ドリストã?第ä¸?ƒ¬ベルで、エレメントã?みを返しますã?
275             * 通常は、あるエレメントを、getElementList( String ) 等で検索した後ã?そã?子要ç´?‚’
276             * 取り出すå?合に使用しますã?
277             * 該当するエレメントが、なにも存在しなã�??合ã?、空のリストオブジェクトが返されますã?
278             *
279             * @return      直ä¸?メンバã?)のエレメントã?リスãƒ?
280             */
281            public List<OGElement> getChildElementList() {
282                    List<OGElement> eles = new ArrayList<OGElement>();
283    
284                    for( OGNode node : nodes ) {
285                            if( node.nodeType == OGNodeType.Element ) {
286                                    eles.add( (OGElement)node );
287                            }
288                    }
289    
290                    return eles;
291            }
292    
293            /**
294             * ノã?ドリストからã?下位ã?階層に存在するすべてのエレメントをリストにして返しますã?
295             *
296             * エレメントã?、名前をæŒ?®šして検索しますã?
297             * 該当するエレメントが、なにも存在しなã�??合ã?、空のリストオブジェクトが返されますã?
298             *
299             * @param       qName   エレメントã?名前
300             *
301             * @return      下位ã?階層に存在するすべてのエレメントã?リスãƒ?
302             */
303            public List<OGElement> getElementList( final String qName ) {
304                    List<OGElement> eles = new ArrayList<OGElement>();
305    
306                    if( qName != null ) {
307                            for( OGNode node : nodes ) {
308                                    if( node.nodeType == OGNodeType.Element ) {
309                                            OGElement ele = (OGElement)node;
310                                            if( qName.equals( ele.getTagName() ) ) {
311                                                    eles.add( ele );
312                                            }
313                                            eles.addAll( ele.getElementList( qName ) );
314                                    }
315                            }
316                    }
317    
318                    return eles;
319            }
320    
321            /**
322             * ノã?ドタイプを設定しますã?
323             *
324             * ノã?ドタイプとは、List , Text , Comment , Cdata , Element , Document などの
325             * ノã?ドã?種別を表ã�?enum タイプですã?
326             * 基本çš?�«は、オブジェクトã?取得時に、ファクトリメソãƒ?ƒ‰経由であれば、è?動的に設å®?
327             * されてã�?�¾すã?
328             * ここでは、可変設定できますã?
329             * 例えば、既存ã?エレメントやノã?ドに対して、コメントタイãƒ?Comment)を指定するとã€?
330             * ファイル等への出力時にコメントとして出力されますã?
331             * null を指定すると、なにもå?ç�?�•れませんã€?
332             *
333             * @param       type    enumのOGNodeType
334             * @see OGNodeType
335             */
336            public void setNodeType( final OGNodeType type ) {
337                    if( type != null ) {
338                            if( type != OGNodeType.Text && nodeType == OGNodeType.Text ) {
339                                    OGNode node = new OGNode( text );
340                                    node.parentNode = this;
341                                    nodes.add( node );
342                            }
343    
344                            nodeType = type ;
345                    }
346            }
347    
348            /**
349             * ノã?ドタイプを取得しますã?
350             *
351             * ノã?ドタイプとは、List , Text , Comment , Cdata , Element , Document などの
352             * ノã?ドã?種別を表ã�?enum タイプですã?
353             * 基本çš?�«は、オブジェクトã?取得時に、ファクトリメソãƒ?ƒ‰経由であれば、è?動的に設å®?
354             * されてã�?�¾すã?
355             *
356             * @return      ノã?ドタイãƒ?
357             * @see OGNodeType
358             */
359            public OGNodeType getNodeType() {
360                    return nodeType;
361            }
362    
363            /**
364             * ノã?ドリストã?æ–?­—å?を返しますã?
365             *
366             * これは、タグでè¨?�†ところのBODY部に書かれた文字å?に相当しますã?
367             * 該当する文字å?がã?存在しなã�??合ã?、空のæ–?­—å?(ゼロストリング)が返されますã?
368             *
369             * @param       cnt             Nodeの階層
370             * @return      ノã?ドリストã?æ–?­—å?(BODY部に書かれた文字å?)
371             */
372            public String getText( final int cnt ) {
373                    StringBuilder buf = new StringBuilder();
374    
375                    if( nodeType == OGNodeType.Text ) {
376                            buf.append( text );
377                    }
378                    else {
379                            for( OGNode node : nodes ) {
380                                    buf.append( node.getText( cnt ) );
381                            }
382                    }
383    
384                    String rtn = buf.toString();
385                    switch( nodeType ) {
386                            case Comment:   rtn = "<!-- "      + rtn + " -->"; break;
387                            case Cdata:             rtn = "<![CDATA[ " + rtn + " ]]>"; break;
388    //                      case Document:
389    //                      case Text:
390    //                      case DTD:
391    //                      case List:
392                            default:                break;
393                    }
394    
395                    return rtn ;
396            }
397    
398            /**
399             * オブジェクトã?æ–?­—å?表現を返しますã?
400             *
401             * æ–?­—å?は、OGNodeType により異なりますã?
402             * Comment ノã?ドã?場合ã?、コメント記号をã?Cdata ノã?ドã?場合ã?、CDATA ã‚?
403             * つけて出力しますã?
404             *
405             * @return      こã?オブジェクトã?æ–?­—å?表現
406             * @see Object#toString()
407             */
408            @Override
409            public String toString() {
410                    return getText( -10 );
411            }
412    }