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.hayabusa.db;
017
018import org.opengion.fukurou.system.HybsConst ;                  // 6.1.0.0 (2014/12/26)
019import org.opengion.fukurou.util.ErrorMessage;
020import org.opengion.fukurou.util.StringUtil;
021import org.opengion.fukurou.model.NativeType;
022
023/**
024 * 一般的な半角文字列を扱う為の、カラム属性を定義します。
025 *
026 * 半角文字列とは、「 c < 0x20 || c > 0x7e 以外」でのみ
027 * 構成された文字列のことです。
028 *
029 * タイプチェックとして、以下の条件を判定します。
030 * ・文字列長は、Byte換算での文字数との比較
031 * ・半角文字列チェック「 c < 0x20 || c > 0x7e 以外」エラー
032 * ・文字パラメータの 正規表現チェック
033 * ・クロスサイトスクリプティングチェック
034 *
035 * @og.group データ属性
036 *
037 * @version  4.0
038 * @author   Kazuhiko Hasegawa
039 * @since    JDK5.0,
040 */
041public abstract class AbstractDBType implements DBType {
042        private final String defValue ;         // データのデフォルト値
043
044        /** システムの改行コードを設定します。*/
045        protected static final String CR                 = HybsConst.CR;                        // 6.1.0.0 (2014/12/26) refactoring
046        /** StringBilderなどの初期値を設定します。   {@value} */
047        protected static final int BUFFER_MIDDLE = HybsConst.BUFFER_MIDDLE;     // 6.1.0.0 (2014/12/26) refactoring
048
049        /**
050         * デフォルトコンストラクター
051         *
052         * @og.rev 4.0.0.0 (2005/01/31) type 廃止
053         */
054        public AbstractDBType() {
055                this( "" );             // データのデフォルト値
056        }
057
058        /**
059         * コンストラクター
060         *
061         * 各サブクラスのタイプ値とデフォルト値を設定して、オブジェクトを構築します。
062         *
063         * @og.rev 4.0.0.0 (2005/01/31) type 廃止
064         *
065         * @param  defValue データのデフォルト値
066         */
067        public AbstractDBType( final String defValue ) {
068                this.defValue = defValue;
069        }
070
071        /**
072         * NATIVEの型の識別コードを返します。
073         *
074         * @og.rev 3.5.4.7 (2004/02/06) 新規作成
075         * @og.rev 4.1.1.2 (2008/02/28) Enum型(fukurou.model.NativeType)に変更
076         *
077         * @return  NATIVEの型の識別コード(DBType で規定)
078         * @og.rtnNotNull
079         * @see org.opengion.fukurou.model.NativeType
080         */
081        public NativeType getNativeType() {
082                return NativeType.STRING;
083        }
084
085        /**
086         * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
087         *
088         * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
089         * なお、エラーチェックは行われません。
090         * 実行前に、必ず valueCheck( String value ,int len ) を行う必要があります。
091         *
092         * @og.rev 3.5.4.5 (2004/01/23) エンコード指定に変更します。
093         *
094         * @param   value    FILL埋めする文字列
095         * @param   sizeX    整数部分の文字列の長さ
096         * @param   sizeY    小数部分の文字列の長さ
097         * @param   encode   固定長で変換する文字エンコード
098         *
099         * @return  FILL埋めした新しい文字列
100         * @og.rtnNotNull
101         */
102        public String valueFill( final String value ,final int sizeX ,final int sizeY,final String encode ) {
103                final int len = (sizeY == 0) ? sizeX : sizeX + sizeY + 1;
104
105                return StringUtil.stringFill( value,len,encode );
106        }
107
108        /**
109         * そのDBTypeの,デフォルトの値(物理的初期設定値)を返します。
110         *
111         * 一般に、文字列の場合は,ゼロストリング""  数字の場合は "0" です。
112         *
113         * @return  物理的初期設定値
114         */
115        public String getDefault() {
116                return defValue;
117        }
118
119        /**
120         * String引数の文字列を+1した文字列を返します。
121         *
122         * これは、英字の場合(A,B,C など)は、B,C,D のように,最終桁の文字コードを
123         * +1 します。
124         * 文字列が数字タイプの場合は, 数字に変換して、+1 します。
125         * 最終桁が、"9","z","Z" および、その全角文字の場合、"0","a","Z" および、その全角文字に
126         * 変換後、ひとつ上の桁で、同様の +1 操作を行います。
127         * 最も上位の桁が、これらの繰り上がり桁の場合は、すべての桁が初期化された状態に戻ります。
128         * 例:123 ⇒ 124 , ABC ⇒ ABD , 789 ⇒ 790 , XYZ ⇒ XXZ ,
129         *     ABC123 ⇒ ABC124 , AB99 ⇒ AC00 , 12ZZ99 ⇒ 13AA00 , ZZZZ ⇒ AAAA
130         * 引数が null の場合と、ゼロ文字列("")の場合は,物理的初期設定値(String getDefault())
131         * の値を返します。
132         *
133         * @og.rev 4.0.0.0 (2005/01/31)  Addの方法を変更(汎用的なAdd)
134         *
135         * @param   value  String引数
136         *
137         * @return  引数の文字列を+1した文字列。
138         */
139        public String valueAdd( final String value ) {
140                if( value == null || value.isEmpty() ) { return getDefault(); }
141
142                char[] chs = value.toCharArray() ;
143
144                for( int i=chs.length-1; i>=0; i-- ) {
145                        boolean over = true;
146                        switch( chs[i] ) {
147                                case '9'  : chs[i] = '0' ; break;
148                                case 'z'  : chs[i] = 'a' ; break;
149                                case 'Z'  : chs[i] = 'A' ; break;
150                                case '9' : chs[i] = '0'; break;
151                                case 'z' : chs[i] = 'a'; break;
152                                case 'Z' : chs[i] = 'A'; break;
153                                default   : chs[i]++; over=false; break;
154                        }
155                        if( !over ) { break; }          // キャリーオーバーしていなければ、終了
156                }
157
158                return new String( chs );
159        }
160
161        /**
162         * String引数の文字列に、第2引数に指定の文字列(数字、日付等)を加算して返します。
163         *
164         * これは、valueAdd( String ) と本質的には同じ動きをしますが、任意の文字列を加算する
165         * ため、主として、数字系や日付系の DBType にのみ実装します。
166         * 実装がない場合は、UnsupportedOperationException を throw します。
167         * 
168         * 第2引数 が、null の場合は、+1 する valueAdd( String )が呼ばれます。
169         * これは、将来的には、valueAdd( String ) を無くすことを意味します。
170         *
171         * @og.rev 5.6.0.3 (2012/01/24) ADD に、引数の値を加算する機能を追加します。
172         *
173         * @param   value  String引数
174         * @param   add    加算する文字列(null の場合は、従来と同じ、+1 します。)
175         *
176         * @return  引数の文字列第2引数に指定の文字列(数字、日付等)を加算した文字列。
177         * @throws UnsupportedOperationException 実装が存在しない場合
178         */
179        public String valueAdd( final String value,final String add ) {
180                if( add == null || add.isEmpty()  ) { return valueAdd( value ); }
181
182                final String errMsg = "このクラスでは、引数付の任意の加算は実装されていません。"
183                                                        + getClass().getName() + " Action=[ADD]"
184                                                        + " oldValue=[" + value + "] newValue=[" + add + "]" ;
185                throw new UnsupportedOperationException( errMsg );
186        }
187
188        /**
189         * エディターで編集されたデータを登録する場合に、データそのものを変換して、実登録データを作成します。
190         *
191         * 例えば,大文字のみのフィールドなら、大文字化します。
192         * 実登録データの作成は、DBType オブジェクトを利用しますので,
193         * これと CellEditor とがアンマッチの場合は、うまくデータ変換
194         * されない可能性がありますので、注意願います。
195         *
196         * @og.rev 3.3.3.0 (2003/07/09) 前後のスペースを取り除いておく。
197         * @og.rev 3.3.3.1 (2003/07/18) 後ろスペースを取り除く。(StringUtil#rTrim)
198         *
199         * @param       value   (一般に編集データとして登録されたデータ)
200         *
201         * @return  修正後の文字列(一般にデータベースに登録するデータ)
202         */
203        public String valueSet( final String value ) {
204                return StringUtil.rTrim( value );
205        }
206
207        /**
208         * action で指定されたコマンドを実行して、値の変換を行います。
209         *
210         * oldValue(旧データ)は、元のDBTableModelに設定されていた値です。通常は、
211         * この値を使用してカラム毎に変換を行います。newValue(新データ)は、引数で
212         * 指定された新しい値です。この値には、パラメータを指定して変換方法を
213         * 制御することも可能です。
214         * 指定のアクションがカラムで処理できない場合は、エラーになります。
215         *
216         * @param   action アクションコマンド
217         * @param   oldValue 入力データ(旧データ)
218         * @param   newValue 入力データ(新データ)
219         *
220         * @return      実行後のデータ
221         */
222        public String valueAction( final String action,final String oldValue,final String newValue ) {
223                final String errMsg = "このクラスでは、このアクションは実装されていません。"
224                                                        + getClass().getName() + " Action=[" + action + "]"
225                                                        + " oldValue=[" + oldValue + "] newValue=[" + newValue + "]" ;
226                throw new UnsupportedOperationException( errMsg );
227        }
228
229        /**
230         * データが登録可能かどうか[true/false]をチェックします。
231         *
232         * データがエラーの場合は、そのエラー内容を返します。
233         *
234         * @og.rev 2.1.1.1 (2002/11/15) HTMLタグチェックのメソッドの共有化。
235         * @og.rev 3.0.1.3 (2003/03/11) DBTypeCheckUtilクラスを利用するように修正
236         * @og.rev 3.6.0.0 (2004/09/22) dbType パラメータを引数に追加
237         * @og.rev 5.2.2.0 (2010/11/01) 厳密にチェック(isStrict=true)するフラグを追加
238         *
239         * @param   key   キー
240         * @param   value 値
241         * @param   sizeX 整数部分の文字列の長さ
242         * @param   sizeY 小数部分の文字列の長さ
243         * @param   typeParam  dbType パラメータ
244         * @param   isStrict     厳密にチェックするかどうか [true:する/false:標準的]
245         *
246         * @return  エラー内容
247         */
248        public ErrorMessage valueCheck( final String key ,final String value ,
249                                                                        final int sizeX ,final int sizeY ,final String typeParam ,final boolean isStrict) {
250
251                ErrorMessage msg = new ErrorMessage();
252                if( value == null || value.isEmpty() ) { return msg; }
253
254                final int len = (sizeY == 0) ? sizeX : sizeX + sizeY + 1;
255                if( len < value.length() ) {
256                        // 文字列の長さが指定の長さよりも長いです。
257                        msg.addMessage( 0,ErrorMessage.NG,"ERR0006",key,value,
258                                                                        String.valueOf( value.length() ),String.valueOf( len ) );
259                }
260
261                final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
262                boolean isError = false;
263                for( int i=0; i<value.length(); i++ ) {
264                        final char ch = value.charAt( i );
265                        if( ch < 0x20 || ch > 0x7e ) {
266                                val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
267                                isError = true;
268                        }
269                        else {
270                                val.append( ch );
271                        }
272                }
273                if( isError ) {
274                        // 指定の文字以外の文字が使われています。
275                        msg.addMessage( 0,ErrorMessage.NG,"ERR0009", key,val.toString() );
276                }
277
278                // 3.6.0.0 (2004/09/22) dbType パラメータを使用したマッチチェック
279                final String check = DBTypeCheckUtil.matcheCheck( value,typeParam );
280                if( check != null ) {
281                        // 指定の文字以外の文字が使われています。
282                        msg.addMessage( 0,ErrorMessage.NG,"ERR0009", key,check );
283                }
284
285                // クロスサイトスクリプティング対策:'<', '>' は登録させない。
286                msg = xssCheck( key ,value, msg );
287                return msg;
288        }
289
290        /**
291         * HTMLタグかどうかをチェックします。
292         *
293         * クロスサイトスクリプティング対策として、'&lt;', '&gt;' は登録させない。
294         *
295         * @og.rev 2.1.1.1 (2002/11/15) HTMLタグチェックのメソッドの共有化。
296         * @og.rev 6.2.0.0 (2015/02/27) ERR0010 の引数が、変更されているので、修正します。
297         *
298         * @param   key         タグのキー
299         * @param   value       対象の値
300         * @param   msg         ErrorMessageオブジェクト
301         *
302         * @return  エラー内容(エラーを追加した、ErrorMessageオブジェクト)
303         */
304        protected ErrorMessage xssCheck( final String key ,final String value, final ErrorMessage msg ) {
305                final StringBuilder val = new StringBuilder( BUFFER_MIDDLE );
306                boolean isError = false;
307                for( int i=0; i<value.length(); i++ ) {
308                        final char ch = value.charAt( i );
309                        if( ch == '<' || ch == '>' ) {
310                                val.append( "<span class=\"NG\">" ).append( ch ).append( "</span>" );
311                                isError = true;
312                        }
313                        else {
314                                val.append( ch );
315                        }
316                }
317                if( isError ) {
318                        // 6.2.0.0 (2015/02/27) ERR0010:HTMLタグは登録できません。key={0} value={1}({2}) char={3}
319                        // {2} が何を想定していたのか不明ですが、エラー発生元のクラス名を設定しておきます。
320                        final String clsName = this.getClass().getSimpleName();
321
322                        // HTMLタグは登録できません。
323                        msg.addMessage( 0,ErrorMessage.NG,"ERR0010", key,val.toString(),clsName,"<,>" );
324                }
325                return msg;
326        }
327}