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.util;
017
018import java.util.Set;
019import java.util.Map;
020import java.util.HashMap;
021import java.util.Arrays;
022import java.util.Locale;
023
024/**
025 * Attributes.java は、String 型キーにString型値を Map するクラスです。
026 *
027 * HTMLのPOST/GET等の受け渡しや、String型の引数が多い場合に効果があります。
028 * 特に、getAttributes( String[] param ) による属性リスト作成は、
029 * HTMLタグの属性定義を行う上で,非常に便利に利用できます。
030 *
031 * この実装は同期化されません。
032 *
033 * @version  4.0
034 * @author   Kazuhiko Hasegawa
035 * @since    JDK5.0,
036 */
037public final class Attributes {
038        private final Map<String,String> attri ;
039
040        /**
041         * デフォルトコンストラクター
042         *
043         */
044        public Attributes() {
045                attri = new HashMap<String,String>();
046        }
047
048        /**
049         * Attributesオブジェクト を与えて新しく作成するコンストラクター
050         *
051         * @param att Attributesオブジェクト
052         */
053        public Attributes( final Attributes att ) {
054                attri = new HashMap<String,String>( att.attri );
055        }
056
057        /**
058         * マップからマッピングをすべて削除します 。
059         *
060         */
061        public void clear()  {
062                attri.clear() ;
063        }
064
065        /**
066         * マップが指定のキーをマップする値を返します。
067         * マップがこのキーのマッピングを保持していない場合は null
068         * を返します。戻り値の null は、マップがキーのマッピングを
069         * 保持していないことを示すとはかぎりません。つまり、マップが
070         * 明示的にキーを null にマップすることもあります。
071         *
072         * @param    key 関連付けられた値が返されるキー(大文字小文字は同値)
073         *
074         * @return   マップが、指定されたキーにマッピングしている値。
075         *           このキーに対するマッピングがマップにない場合は null
076         */
077        public String get( final String key ) {
078                return attri.get( key.toLowerCase( Locale.JAPAN ) ) ;
079        }
080
081        /**
082         * 指定された値と指定されたキーをこのマップに関連付けます
083         * 指定されたキーに、null を関連付けることはできません。
084         * (もちろん、"":ゼロストリング は登録できます。)
085         * なぜなら、getAttribute( String[] keys ) 等で値が null の
086         * キーは、取得できない為です。
087         * また、すでに何らかの値がセットされている所に、null をセットした
088         * 場合は、前の値をなにも変更しません。
089         * 通常、値をクリアしたい場合は、 remove( String key ) を利用してください。
090         *
091         * @param    key 指定される値が関連付けられるキー(大文字小文字は同値)
092         * @param    value 指定されるキーに関連付けられる値
093         */
094        public void set( final String key,final String value ) {
095                if( value != null ) {
096                        attri.put( key.toLowerCase( Locale.JAPAN ),value ) ;
097                }
098        }
099
100        /**
101         * 指定された値と指定されたキーをこのマップに関連付けます
102         * set( String key,String value ) との違いは、value が null
103         * の場合に、def を代わりにセットすることです。
104         * ただし、value が null で、def も null の場合は、
105         * なにもセットされません。
106         *
107         * @param    key 指定される値が関連付けられるキー(大文字小文字は同値)
108         * @param    value 指定されるキーに関連付けられる値
109         * @param    def value が null の場合にキーに関連付けられる値
110         */
111        public void set( final String key,final String value,final String def ) {
112                if( value != null )    { attri.put( key.toLowerCase( Locale.JAPAN ),value ) ; }
113                else if( def != null ) { attri.put( key.toLowerCase( Locale.JAPAN ),def )   ; }
114        }
115
116        /**
117         * 指定された値と指定されたキーをこのマップに追加します
118         *
119         * マップ自身のキーは、ユニークである為、既存の値に対して、
120         * 新しく値を追加します。
121         * 追加する方法は、値の文字列の結合です。このメソッドでは、
122         * デフォルトのスペースで結合します。
123         *
124         * 値が null または、すでにそのキーに同一の値が関連付けられている場合は、
125         * 何もしません。
126         *
127         * @param    key   指定される値が関連付けられるキー(大文字小文字は同値)
128         * @param    value 指定されるキーの値に、追加される値
129         */
130        public void add( final String key,final String value ) {
131                add( key,value," " ) ;
132        }
133
134        /**
135         * 指定された値と指定されたキーをこのマップに追加します
136         *
137         * マップ自身のキーは、ユニークである為、既存の値に対して、
138         * 新しく値を追加します。
139         * 追加する方法は、値の文字列の結合です。このメソッドでは、
140         * 引数 sepa で文字列を結合します。
141         *
142         * 値が null または、sepa が null または、すでにそのキーに
143         * 同一の値が関連付けられている場合は、何もしません。
144         *
145         * @param    key   指定される値が関連付けられるキー(大文字小文字は同値)
146         * @param    value 指定されるキーの値に、追加される値
147         * @param    sepa  値を連結するときの文字列
148         */
149        public void add( final String key,final String value,final String sepa ) {
150                if( value != null && sepa != null ) {
151                        String lkey = key.toLowerCase( Locale.JAPAN );
152                        String temp = attri.get( lkey );
153                        if( temp != null ) {
154                                temp = temp.trim();
155                                if( temp.indexOf( value ) < 0 ||                     // 存在しない または、
156                                        ( ! temp.equals( value ) &&                             // 一致しない
157                                          ! temp.startsWith( value + sepa ) &&  // 先頭にない
158                                          ! temp.endsWith( sepa + value ) &&    // 最終にない
159                                          temp.indexOf( sepa + value + sepa ) < 0 ) ) {      // 途中にない
160                                                if( temp.endsWith( sepa ) ) {
161                                                        attri.put( lkey,temp + value );
162                                                }
163                                                else {
164                                                        attri.put( lkey,temp + sepa + value );
165                                                }
166                                }
167                        }
168                        else {
169                                attri.put( lkey,value );
170                        }
171                }
172        }
173
174        /**
175         * Attributes 属性を、既存の属性に追加します。
176         * すでに同一キーの属性が存在している場合は,上書きで
177         * 置き換えます。
178         * 引数 att が null の場合は,何もしません。
179         *
180         * @param att Attributes属性
181         */
182        public void addAttributes( final Attributes att ) {
183                if( att != null ) {
184                        String[] keys = att.getKeyArray();
185                        for( int i=0; i<keys.length; i++ ) {
186                                set( keys[i],att.get( keys[i] ) );
187                        }
188                }
189        }
190
191        /**
192         * このキーにマッピングがある場合に、そのマッピングをマップから削除します
193         *
194         * @param    key マッピングがマップから削除されるキー(大文字小文字は同値)
195         *
196         * @return   このキーにマッピングがある場合に、そのマッピングをマップから削除します
197         *           指定されたキーに関連した以前の値。key にマッピングがなかった場合は null。
198         */
199        public String remove( final String key ) {
200                return attri.remove( key.toLowerCase( Locale.JAPAN ) );
201        }
202
203        /**
204         * マップ内のキーと値のマッピングの数を返します。
205         *
206         * @return   インタフェース Map 内の size
207         *
208         */
209        public int size() {
210                return attri.size() ;
211        }
212
213        /**
214         * マップに含まれているキーの配列を返します。
215         * ここでは、キーの配列はソートして返します。
216         *
217         * @return   マップに含まれているキーの配列
218         *
219         */
220        public String[] getKeyArray() {
221                Set<String> keyset = attri.keySet();
222                String[] rtn = keyset.toArray( new String[keyset.size()] ) ;
223                Arrays.sort( rtn );
224                return rtn ;
225        }
226
227        /**
228         * マップに含まれているキーと属性のペアを タグの属性リストの形式で返します。
229         * key1="value1" key2="value2" key3="value3" .... の形式で、value が null の
230         * 場合は,key そのもののペアを出力しません。
231         * value が空文字列 "" の場合は,key="" で出力します。
232         *
233         * 引数には,key として出力したい値を配列文字列で渡します。
234         * これは、拡張性に乏しい(すべて出せば,属性項目の追加に対応できる。)
235         * 方法ですが、タグ毎に異なる属性のみを管理するには,厳格に出力
236         * タグ属性を定義したいという思いから,導入しました。
237         *
238         * @param    keys 指定 key の配列文字列(大文字小文字は同値)
239         *
240         * @return   キーと属性のペアをタグの属性リストの形式で返します
241         *
242         */
243        public String getAttribute( final String[] keys ) {
244                StringBuilder buf = new StringBuilder( 200 );
245
246                for( int i=0; i<keys.length; i++ ) {
247                        String value = get( keys[i].toLowerCase( Locale.JAPAN ) );
248                        if( value != null ) {
249                                buf.append( keys[i] ).append("=");
250                                buf.append("\"").append( value ).append("\" ");
251                        }
252                }
253                return buf.toString();
254        }
255
256        /**
257         * マップに含まれているキーと属性のペアを タグの属性リストの形式ですべて返します。
258         * なお、value が null の場合は,key そのもののペアを出力しません。
259         * value が空文字列 "" の場合は,key="" で出力します。
260         *
261         * @return   キーと属性のペアをタグの属性リストの形式で返します
262         *
263         */
264        public String getAttribute() {
265                return getAttribute( getKeyArray() );
266        }
267
268        /**
269         * このオブジェクトの文字列表現を返します。
270         * 基本的にデバッグ目的に使用します。
271         *
272         * @return      オブジェクトの文字列表現
273         */
274        @Override
275        public String toString() {
276                return( getAttribute() );
277        }
278}