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 */
016package org.opengion.fukurou.xml;
017
018import java.util.Map;
019import java.util.LinkedHashMap;
020import java.util.List;
021import java.util.Collections;                                                                           // 6.4.3.1 (2016/02/12) refactoring
022
023import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
024import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
025
026/**
027 * このクラスは、XMLファイルのタグエレメントを表すオブジェクトです。
028 * タグとしては、コンストラクタにMapを指定すれば(defaultMap)タグのカラムを
029 * 初期設定します。この場合、Map に LinkedHashMap を指定すれば、カラムの
030 * 順番も指定順になります。
031 * ここで指定したMapの値は、put メソッドにより上書きされます。
032 * setAfterMap で指定したMapは、既存の内部情報を上書きします。キーがあれば、
033 * afterMap の値が上書きされ、キーが無ければ新規にキーが追加されます。
034 * 一般には、XMLファイルから構築された後で、XMLファイルの情報を一括して
035 * 書き換える場合などに使用します。
036 * 処理の途中にセットした場合は、それまでの値が上書きされ、それ以降の値は、
037 * put により設定された値が 優先されます。
038 * toString() により、簡易的に オラクルXDK形式のXMLファイルの
039 * 1レコード 分の情報を返します。
040 * オラクルXDK形式のXMLファイルとは、下記のような ROWSET をトップとする ROW の
041 * 集まりで1レコードを表し、各ROWには、カラム名をキーとするXMLになっています。
042 *
043 *   <ROWSET>
044 *       <ROW num="1">                         ← この部分のみ
045 *           <カラム1>値1</カラム1>      ← この部分のみ
046 *             ・・・                                   ← この部分のみ
047 *           <カラムn>値n</カラムn>      ← この部分のみ
048 *       </ROW>                                ← この部分のみ
049 *        ・・・
050 *       <ROW num="n">
051 *          ・・・
052 *       </ROW>
053 *   <ROWSET>
054 *
055 * この形式であれば、XDK(Oracle XML Developer's Kit)を利用すれば、非常に簡単に
056 * データベースとXMLファイルとの交換が可能です。
057 * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" >
058 * XDK(Oracle XML Developer's Kit)</a>
059 *
060 * @version  4.0
061 * @author   Kazuhiko Hasegawa
062 * @since    JDK5.0,
063 */
064public class TagElement {
065        private final String name ;
066        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。  */
067        private final Map<String,String> tagMap = Collections.synchronizedMap( new LinkedHashMap<>() );         // 6.4.3.1 (2016/02/12) ついでに変数名変更。
068        private String num      ;
069        private String body     ;
070
071        /**
072         * Tag名を指定して、オブジェクトを構築します。
073         *
074         * @param       name    Tag名
075         */
076        public TagElement( final String name ) {
077                this( name,null );
078        }
079
080        /**
081         * Tag名と初期化マップを指定して、オブジェクトを構築します。
082         *
083         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
084         *
085         * @param       name            Tag名
086         * @param       defaultMap      初期化マップ
087         */
088        public TagElement( final String name, final Map<String,String> defaultMap ) {
089                this.name = name;
090                // 6.4.3.1 (2016/02/12) 最初から、構築しておきます。
091                if( defaultMap != null ) { tagMap.putAll( defaultMap ); }
092        }
093
094        /**
095         * Tag名を取得します。
096         *
097         * @return      Tag名
098         */
099        public String getName() {
100                return name;
101        }
102
103        /**
104         * カラムと値のペアを登録します。
105         * すでに 内部に同一カラムが存在する場合は、値の書き換えを、カラムが
106         * 存在しない場合は、カラムの追加を行います。
107         * カラムは、追加された順番を再現してXML化できます。
108         *
109         * @og.rev 5.6.6.1 (2013/07/12) key が null や ゼロ文字列の場合は、Map に追加しません。
110         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
111         *
112         * @param       key     カラム
113         * @param       val     値
114         */
115        public void put( final String key, final String val ) {
116                if( key != null && key.length() > 0 ) {                                 // 5.6.6.1 (2013/07/12)
117                        tagMap.put( key,val );
118                }
119        }
120
121        /**
122         * すでに構築済みの カラムと値に、上書きで マップ情報を追加します。
123         * すでに 内部に同一カラムが存在する場合は、値の書き換えを、カラムが
124         * 存在しない場合は、カラムの追加を行います。
125         * カラムは、追加された順番を再現してXML化できます。
126         *
127         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
128         *
129         * @param afterMap      後設定マップ
130         */
131        public void setAfterMap( final Map<String,String> afterMap ) {
132                if( afterMap != null ) {
133                        tagMap.putAll( afterMap );
134                }
135        }
136
137        /**
138         * キーを指定して値を取得します。
139         *
140         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
141         *
142         * @param       key     カラム
143         *
144         * @return      値
145         */
146        public String get( final String key ) {
147                // 6.4.3.1 (2016/02/12) ConcurrentMap 系は、key,val ともに not null 制限です。
148                return key == null ? null : tagMap.get( key );
149        }
150
151        /**
152         * 行番号を取得します。
153         *
154         * @return      値
155         */
156        public String getRowNo() {
157                return num;
158        }
159
160        /**
161         * 行番号を設定します。
162         *
163         * @param       num     値
164         */
165        public void setRowNo( final String num ) {
166                this.num = num ;
167        }
168
169        /**
170         * BODY部の文字列を取得します。
171         *
172         * @return      値
173         */
174        public String getBody() {
175                return body;
176        }
177
178        /**
179         * BODY部の文字列を設定します。
180         *
181         * @param       body    値
182         */
183        public void setBody( final String body ) {
184                this.body = body ;
185        }
186
187        /**
188         * カラムの配列を返します。
189         *
190         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
191         *
192         * @return      カラムの配列(順序は、カラムの登録順)
193         * @og.rtnNotNull
194         */
195        public String[] getKeys() {
196                return tagMap.keySet().toArray( new String[tagMap.size()] );
197        }
198
199        /**
200         * カラム配列の順と同じ、値の配列を返します。
201         *
202         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
203         *
204         * @return      値の配列(順序は、カラムの登録順と一致しています。)
205         * @og.rtnNotNull
206         */
207        public String[] getValues() {
208                return tagMap.values().toArray( new String[tagMap.size()] );
209        }
210
211        /**
212         * 引数のカラム名のListの順番で、カラム配列の値の配列を返します。
213         *
214         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
215         *
216         * @param clms  カラム名のListオブジェクト
217         *
218         * @return      値の配列(順序は、カラムの登録順と一致しています。)
219         */
220        public String[] getValues( final List<String> clms ) {
221                final int size = clms.size();
222                final String[] vals = new String[size];
223                for( int i=0; i<size; i++ ) {
224                        vals[i] = tagMap.get( clms.get(i) );
225                }
226
227                return vals;
228        }
229
230        /**
231         * 引数のカラム名の配列の順番で、カラム配列の値の配列を返します。
232         * カラム名が、存在しない場合は、値は、null を返します。
233         *
234         * @og.rev 6.4.3.1 (2016/02/12) Collections.synchronizedMap に置き換え。ついでに変数名変更。
235         *
236         * @param       clms    カラム名の文字列配列(可変長引数)
237         *
238         * @return      値の配列(順序は、カラムの配列順と一致しています。)
239         */
240        public String[] getValues( final String... clms ) {
241                final int size = clms.length;
242                final String[] vals = new String[size];
243                for( int i=0; i<size; i++ ) {
244                        vals[i] = tagMap.get( clms[i] );
245                }
246
247                return vals;
248        }
249
250        /**
251         * 内部情報の文字列表現を返します。
252         * 1レコード分を表す ROW のXML表現を作成します。前後に、ROWSET を
253         * 追加すれば、オラクルXDK形式のXMLを作成できます。
254         *
255         * @return      内部情報の文字列表現
256         * @og.rtnNotNull
257         */
258        @Override
259        public String toString() {
260
261                // 6.0.2.5 (2014/10/31) char を append する。
262                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
263                buf.append( '<' ).append( name );
264                if( num != null ) {
265                        buf.append( " num=\"" ).append( num ).append( "\" " );
266                }
267                buf.append( '>' ).append( CR );
268
269                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
270                if( body == null ) {
271                        final String[] keys = getKeys();
272                        for( int i=0; i<keys.length; i++ ) {
273                                final String val = get( keys[i] );
274                                if( val == null ) {
275                                        buf.append( "  <" ).append( keys[i] ).append( " />" );          // XML なので、このまま。
276                                }
277                                else {
278                                        buf.append( "  <" ).append( keys[i] ).append( '>' )
279                                                .append( val )
280                                                .append( "</" ).append( keys[i] ).append( '>' );
281                                }
282                                buf.append( CR );
283                        }
284                }
285                else {
286                        buf.append( body ).append( CR );
287                }
288
289                buf.append( "</" ).append( name ).append( '>' ).append( CR );
290
291                return buf.toString();
292        }
293}