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 org.xml.sax.Attributes;
019    import java.util.List;
020    import java.util.ArrayList;
021    
022    /**
023     * 属æ?リストをあらわす、OGAttributes クラスを定義しますã?
024     *
025     * 属æ?リストã?、キーと値のペアをã?並びé ?�§管ç�?�—てã�?‚‹リストを保持してã�?�¾すã?
026     * å†?ƒ¨çš?�«はã€?org.xml.sax.Attributes からの値の設定と、タブã?属æ?の整列を行うための
027     * 属æ?タブã?属æ?の改行ã?制御、属æ?の長さã?制御 などを行いますã?
028     *
029     * @og.rev 5.1.8.0 (2010/07/01) 新規作æ?
030     *
031     * @version  5.0
032     * @author   Kazuhiko Hasegawa
033     * @since    JDK6.0,
034     */
035    public class OGAttributes {
036            /** シスãƒ?ƒ 改行コーãƒ?**/
037            public static final String      CR     = System.getProperty("line.separator");
038    
039            /** 属æ?の個数制限ã?こã?個数で改行を行うã€?{@value} */
040            public static final int         CR_CNT = 4;
041            /** 属æ?の長さ制限ã?これ以上ã?場合ã?、改行を行うã€?{@value} */
042            public static final int         CR_LEN = 80;
043    
044            private final List<OGAtts>        attList = new ArrayList<OGAtts>();
045    
046    //      private String  attTab  = CR + "  ";            // タグの前方スペã?ス
047    //      private String  attTab  = "  ";                         // 属æ?の前方スペã?ス
048            private boolean useCR   = false;                        // 属æ?の改行å?力を行うかどã�?�‹。å?数制限が?‘と同じ
049            private int     maxValLen       = 0;                            // 属æ?の名前のæœ?¤§æ–?­—数
050            private String  id              = null;                         // 特別な属æ?。id で検索を高é?化するためã?
051    
052            /**
053             * ãƒ?ƒ•ォルトトコンストラクター
054             *
055             * 取りあえずã?属æ?オブジェクトを構築するå?合に使用しますã?
056             * 属æ?タブã?、改行+タãƒ?、属æ?リストã?、空のリストã?属æ?改行ã?、false をå?期設定しますã?
057             *
058             */
059            public OGAttributes() {
060                    // Document empty method チェãƒ?‚¯対ç­?
061            }
062    
063            /**
064             * 属æ?タブã?属æ?リストã?属æ?改行ã?有無を指定してのトコンストラクター
065             *
066             * 属æ?タブã?属æ?リストに null を指定すると、デフォルトトコンストラクターの設定と
067             * 同じ状態になりますã?
068             *
069             * 注æ„?属æ?値の正規化はå¿?�š行われますã?
070             * 属æ?値に含まれるCR(復帰), LF(改è¡?, TAB(タãƒ?はã€?半角スペã?スに置き換えられますã?
071             * XMLの規定では、属æ?の並びé ??保障されませんがã?SAXのAttributesは、XMLに記述されたé?番で
072             * 取得できてã�?�¾すã?で、このクラスでの属æ?リストも、記述é ?�§の並びé ?�«なりますã?
073             *
074             * @og.rev 5.1.9.0 (2010/08/01) id 属æ?のみ特別にキャãƒ?‚·ュしておくã€?
075             *
076             * @param attri         属æ?リスãƒ?
077             */
078    //      public OGAttributes( final String attTab , final Attributes attri , final boolean useCR ) {
079            public OGAttributes( final Attributes attri ) {
080    //              if( attTab != null ) {
081    ////                    this.attTab = CR + attTab + "  ";
082    //                      this.attTab = attTab;
083    //              }
084    //              this.useCR  = useCR;
085    
086                    int num = (attri == null)? 0 : attri.getLength();
087                    int maxLen = 0;
088                    for (int i = 0; i < num; i++) {
089    //                      OGAtts atts = new OGAtts( attri.getQName(i),attri.getValue(i) );
090                            String key = attri.getQName(i);
091                            String val = attri.getValue(i);
092                            OGAtts atts = new OGAtts( key,val );
093                            attList.add( atts );
094                            maxLen = atts.maxKeyLen( maxLen );
095    
096                            if( "id".equals( key ) ) { id = val; }          // 5.1.9.0 (2010/08/01)
097                    }
098    
099                    maxValLen = maxLen;
100            }
101    
102            /**
103             * 属æ?タブを設定しますã?
104             *
105             * 属æ?タブã?、è?身のエレメントã?書きå?し位置までのæ–?­—å?を持ってã�?�¾すã?
106             * 属æ?をã?そã?まま、タグの横に出すとã€?¼‘行に収まらなã�?‚±ースがありますã?
107             * そã?場合ã?自身の位置?‹タブを属æ?の書きå?しに使用しますã?
108             * そã?æ–?­—å?を定義してã�?�¾すã?
109             *
110             * 引数は、è?身のエレメントã?書きå?し位置ですã?
111             *
112             * @og.rev 5.1.9.0 (2010/08/01) å»?­¢
113             *
114             * å†?ƒ¨çš?�«ã€?½¢CR + attTab + タブ」 にåŠ?·¥されますã?
115             *
116             * @param       attTab  属æ?タãƒ?
117             */
118    //      public void setAttrTab( final String attTab ) {
119    //              if( attTab != null ) {
120    ////                    this.attTab = CR + attTab + "\t";
121    //                      this.attTab = attTab;
122    //              }
123    //      }
124    
125            /**
126             * 属æ?改行ã?有無を設定しますã?
127             *
128             * タグによって、属æ?が多くなったり、意味が重要な場合ã?、属æ??‘つづつに改行を
129             * 行いたいケースがありますã?
130             * 属æ?改行をtrue に設定すると、属æ?ä¸?�¤づつで、改行を行いますã?
131             *
132             * false の場合ã?、è?動的な改行å?ç�?�Œ行われますã?
133             * これは、属æ?の個数制é™?CR_CNT)をè¶?�ˆるå?合ã?、改行を行いますã?
134             *
135             * @param       useCR   属æ?改行ã?有無(true:?‘属æ?単位ã?改è¡?
136             * @see #CR_CNT
137             * @see #CR_LEN
138             */
139            public void setUseCR( final boolean useCR ) {
140                    this.useCR  = useCR;
141            }
142    
143            /**
144             * 属æ?リストã?個数を取得しますã?
145             *
146             * @return      属æ?リストã?個数
147             */
148            public int size() {
149                    return attList.size();
150            }
151    
152            /**
153             * 属æ?リストからã?æŒ?®šã?配å?番号の、属æ?キーを取得しますã?
154             *
155             * @param       adrs    配å?番号
156             *
157             * @return      属æ?キー
158             */
159            public String getKey( final int adrs ) {
160                    return attList.get(adrs).KEY ;
161            }
162    
163            /**
164             * 属æ?リストからã?æŒ?®šã?配å?番号のã€?•·さ補正が行われた属æ?キーを取得しますã?
165             *
166             * useCR=true の場合に、属æ?の改行が行われますが、そのときに、キーが縦に並びますã?
167             * そして、å?も縦に並ぶためã€?–“の ?¢?�」記号の位置をそろえて、表示しますã?
168             * 属æ?リストã?æœ?¤§長さ+ï¼?になるよã�?�«、キーのæ–?­—å?にスペã?スを埋めますã?
169             * これにより、属æ?を改行して表示してもã?値の表示位置がそろいますã?
170             *
171             * @param       adrs    配å?番号
172             *
173             * @return      属æ?リスト群の長さ補正が行われた、属æ?キー?‹空白æ–?­—å?
174             */
175    //      private String getAlignKey( final int adrs ) {
176    //              return attList.get(adrs).KEY + SPACE.substring( attList.get(adrs).LEN,maxValLen ) ;
177    //      }
178    
179            /**
180             * 属æ?リストからã?æŒ?®šã?配å?番号の、属æ?値を取得しますã?
181             *
182             * @param       adrs    配å?番号
183             *
184             * @return      属æ?値
185             */
186            public String getVal( final int adrs ) {
187                    return attList.get(adrs).VAL ;
188            }
189    
190            /**
191             * 属æ?リストからã?æŒ?®šã?属æ?キーの、属æ?値を取得しますã?
192             *
193             * こã?処ç�??、属æ?リストをすべてスキャンして、キーにマッチすã‚?
194             * 属æ?オブジェクトを見つけã?そこから、属æ?値を取りå?すã?でã€?
195             * パフォーマンスに問題がありますã?
196             * 基本çš?�«は、アドレスæŒ?®šで、属æ?値を取りå?すよã�?�«してくださいã€?
197             *
198             * @og.rev 5.1.9.0 (2010/08/01) 新規追åŠ?
199             *
200             * @param       key     属æ?キー
201             *
202             * @return      属æ?値
203             */
204            public String getVal( final String key ) {
205                    String val = null;
206    
207                    if( key != null ) {
208                            for( OGAtts atts : attList ) {
209                                    if( key.equals( atts.KEY ) ) {
210                                            val = atts.VAL;
211                                            break;
212                                    }
213                            }
214                    }
215    
216                    return val;
217            }
218    
219            /**
220             * 属æ?リストからã?æŒ?®šã?属æ?キーの、アドレスを取得しますã?
221             *
222             * どちらかとã�?�†と、キーの存在チェãƒ?‚¯に近い処ç�?‚’行いますã?
223             * こã?処ç�??、属æ?リストをすべてスキャンして、キーにマッチすã‚?
224             * 属æ?オブジェクトを見つけã?そこから、属æ?値を取りå?すã?でã€?
225             * パフォーマンスに問題がありますã?
226             *
227             * @og.rev 5.1.9.0 (2010/08/01) 新規追åŠ?
228             *
229             * @param       key     属æ?キー
230             *
231             * @return      アドレス キーが存在しなã�??合ã?ã€?1 を返すã€?
232             */
233            public int getAdrs( final String key ) {
234                    int adrs = -1;
235    
236                    if( key != null ) {
237                            for( int i=0; i<attList.size(); i++ ) {
238                                    if( key.equals( attList.get(i).KEY ) ) {
239                                            adrs = i;
240                                            break;
241                                    }
242                            }
243                    }
244    
245                    return adrs;
246            }
247    
248            /**
249             * 属æ?リストからã?id属æ?の、属æ?値を取得しますã?
250             *
251             * id属æ? は、å?部çš?�«キャãƒ?‚·ュしており、すぐに取り出せますã?
252             * タグを特定するå?合ã?ä¸?ˆ¬属æ?のキーと値で選別するのではなくã?
253             * id属æ?を付与して選別するようにすればã€?«˜é?に見つけることが可能になりますã?
254             *
255             * @og.rev 5.1.9.0 (2010/08/01) 新規追åŠ?
256             *
257             * @return      id属æ?値
258             */
259            public String getId() { return id; }
260    
261            /**
262             * 属æ?リストã?、指定ã?配å?番号に、属æ?値を設定しますã?
263             *
264             * @og.rev 5.1.9.0 (2010/08/01) id 属æ?のみ特別にキャãƒ?‚·ュしておくã€?
265             *
266             * @param       adrs    配å?番号
267             * @param       val     属æ?値
268             */
269            public void setVal( final int adrs , final String val ) {
270                    OGAtts atts = attList.remove(adrs) ;
271                    attList.add( adrs , new OGAtts( atts.KEY,val ) );
272    
273                    if( "id".equals( atts.KEY ) ) { id = val; }             // 5.1.9.0 (2010/08/01)
274            }
275    
276            /**
277             * 属æ?リストに、指定ã?キー、属æ?値を設定しますã?
278             * もし、属æ?リストに、指定ã?キーがあれã?、属æ?値を変更しますã?
279             * なければ、最後に追åŠ?�—ますã?
280             *
281             * @og.rev 5.6.1.2 (2013/02/22) 新規追åŠ?
282             *
283             * @param       key     属æ?キー
284             * @param       val     属æ?値
285             */
286            public void setVal( final String key , final String val ) {
287                    int adrs = getAdrs( key );
288                    if( adrs < 0 ) { add( key,val ); }
289                    else           { setVal( adrs,val ); }
290            }
291    
292            /**
293             * 属æ?リストに、属æ?(キー、å?のセãƒ?ƒˆ)を設定しますã?
294             *
295             * 属æ?リストã?ä¸?•ªæœ?¾Œに、属æ?(キー、å?のセãƒ?ƒˆ)を設定しますã?
296             *
297             * @og.rev 5.1.9.0 (2010/08/01) id 属æ?のみ特別にキャãƒ?‚·ュしておくã€?
298             *
299             * @param       key     属æ?リストã?キー
300             * @param       val     属æ?リストã?値
301             */
302            public void add( final String key , final String val ) {
303    
304                    OGAtts atts = new OGAtts( key,val );
305                    attList.add( atts );
306                    maxValLen = atts.maxKeyLen( maxValLen );
307    
308                    if( "id".equals( key ) ) { id = val; }          // 5.1.9.0 (2010/08/01)
309            }
310    
311            /**
312             * æŒ?®šã?アドレスの属æ?リストに、属æ?(キー、å?のセãƒ?ƒˆ)を設定しますã?
313             *
314             * æŒ?®šã?アドレスの属æ?を置き換えるのではなく追åŠ?�—ますã?
315             *
316             * @og.rev 5.1.9.0 (2010/08/01) id 属æ?のみ特別にキャãƒ?‚·ュしておくã€?
317             *
318             * @param       adrs    属æ?リストã?アドレス
319             * @param       key     属æ?リストã?キー
320             * @param       val     属æ?リストã?値
321             */
322            public void add( final int adrs , final String key , final String val ) {
323    
324                    OGAtts atts = new OGAtts( key,val );
325                    attList.add( adrs , atts );
326                    maxValLen = atts.maxKeyLen( maxValLen );
327    
328                    if( "id".equals( key ) ) { id = val; }          // 5.1.9.0 (2010/08/01)
329            }
330    
331            /**
332             * æŒ?®šã?アドレスの属æ?リストからã?属æ?æƒ??を削除しますã?
333             *
334             * æŒ?®šã?アドレスの属æ?を置き換えるのではなく追åŠ?�—ますã?
335             *
336             * @og.rev 5.1.9.0 (2010/08/01) id 属æ?のみ特別にキャãƒ?‚·ュしておくã€?
337             *
338             * @param       adrs    属æ?リストã?アドレス
339             */
340            public void remove( final int adrs ) {
341    //              attList.remove(adrs) ;
342    
343                    OGAtts atts = attList.remove(adrs) ;
344    
345                    if( "id".equals( atts.KEY ) ) { id = null; }            // 5.1.9.0 (2010/08/01)
346    
347                    // 削除したキーã�?maxValLen ã�?�£たとしてもã?再計算ã?、行いませんã€?
348                    // 再計算とは、次の長さを見つけるå¿?¦�があるので、すべての OGAtts をもã�?¸?º¦
349                    // チェãƒ?‚¯するå¿?¦�が出てくるためですã?
350            }
351    
352            /**
353             * オブジェクトã?æ–?­—å?表現を返しますã?
354             *
355             * 属æ?につã�?�¦は、並びé ??、登録é ?�Œ保障されますã?
356             *
357             * 属æ?はã€?¼“つのパターンでæ–?­—å?化を行いますã?
358             *  ・useCR=true の場å�?
359             *     こã?場合ã?、属æ?を1行ずつ改行しながら作æ?しますã?属æ?キーはã€?
360             *     æœ?¤§長?‹ï¼?でスペã?ス埋めされて、整形されますã?
361             *  ・useCR=false の場å�?
362             *     ・属æ?の個数制é™?CR_CNT)単位に、改行が行われますã?
363             *       これは、属æ?が右に多く並びすぎるã?を防ぎますã?
364             *     ・属æ?の長さ制é™?CR_LEN)単位で、改行が行われますã?
365             *       これは、たとえã?属æ?の個数が少なくてもã?æ–?­—å?として長ã�??合ã?ã€?
366             *       改行させますã?
367             *
368             * @og.rev 5.6.1.2 (2013/02/22) 改行å?ç�??修正。最後ã?属æ?処ç�??後にも改行を入れるã€?
369             * @og.rev 5.6.4.4 (2013/05/31) 改行å?ç�??修正。attTabがã?ゼロæ–?­—å?の場合ã?対応ã?
370             *
371             * @param       attTab  Nodeの階層を表す文字å?ã€?
372             * @return      こã?オブジェクトã?æ–?­—å?表現
373             * @see OGNode#toString()
374             * @see #setUseCR( boolean )
375             */
376    //      @Override
377    //      public String toString() {
378            public String getText( final String attTab ) {
379                    StringBuilder buf = new StringBuilder();
380    
381                    String crTab = (attTab.length() > 0) ? attTab : CR + "\t" ;
382    
383                    if( useCR ) {
384                            for( int i=0; i<size(); i++ ) {
385                                    OGAtts atts = attList.get(i);
386    //                              buf.append( attTab );
387                                    buf.append( crTab );
388                                    buf.append( atts.getAlignKey( maxValLen ) ).append( " = " ).append( atts.QRT_VAL );
389                            }
390                            // 5.6.1.2 (2013/02/22) 改行å?ç�??修正。最後ã?属æ?処ç�??後にも改行を入れるã€?
391                            buf.append( CR );
392                    }
393                    else {
394                            int crCnt = 0;
395                            int crLen = 0;
396                            for( int i=0; i<size(); i++ ) {
397                                    OGAtts atts = attList.get(i);
398                                    crCnt++ ;
399                                    crLen += atts.LEN;
400                                    if( i>0 && (crCnt > CR_CNT || crLen > CR_LEN) ) {
401    //                                      buf.append( attTab );
402                                            buf.append( crTab );
403                                            buf.append( atts.KEY ).append( "=" ).append( atts.QRT_VAL );
404                                            crCnt = 0;
405                                            crLen = 0;
406                                    }
407                                    else {
408                                            buf.append( " " ).append( atts.KEY ).append( "=" ).append( atts.QRT_VAL );
409                                    }
410                            }
411    //                      buf.append( " " );
412                    }
413    
414                    return buf.toString();
415            }
416    
417            /**
418             * オブジェクトã?æ–?­—å?表現を返しますã?
419             *
420             * @og.rev 5.6.1.2 (2013/02/22) 改行å?ç�??修正。最後ã?属æ?処ç�??後にも改行を入れるã€?
421             *
422             * @return      こã?オブジェクトã?æ–?­—å?表現
423             * @see OGNode#toString()
424             */
425            @Override
426            public String toString() {
427                    return getText( " " );
428            }
429    }
430    
431    /**
432     * 属æ?キーと属æ?値を管ç�?�™ã‚?クラス
433     *
434     * 属æ?自身は、属æ?キーと属æ?値のみで十å?ですが、改行å?ç�?‚„æ–?­—å?の長さ設定でã€?
435     * 予めå†?ƒ¨処ç�?‚’しておきたいため、クラス化してã�?�¾すã?
436     *
437     * å†?ƒ¨変数は、final することで定数化し、アクセスメソãƒ?ƒ‰経由ではなくã?直接å†?ƒ¨変数ã‚?
438     * 参ç?させることで、見æ?さを優先してã�?�¾すã?
439     */
440    final class OGAtts {
441            /** 属æ?の長さをそろえるための空白æ–?­—ã?æƒ?? **/
442            private static final String     SPACE  = "                    ";        // 5.1.9.0 (2010/09/01) public â‡?private へ変更
443    
444            /** 属æ?キー **/
445            public          final String  KEY ;
446            /** 属æ?値 **/
447            public          final String  VAL ;
448            private         final int     KLEN ;
449                                    final int     LEN ;
450                                    final String  QRT_VAL;
451    
452            /**
453             * 引数を指定して構築するã?コンストラクター
454             *
455             * 属æ?キーと、属æ?値 を指定して、オブジェクトを構築しますã?
456             *
457             * @param       key     属æ?キー
458             * @param       val     属æ?値
459             */
460            public OGAtts( final String key , final String val ) {
461                    KEY  = key;
462    //              VAL  = (val == null) ? "" : val ;
463                    VAL  = (val == null) ? "" : htmlFilter(val) ;
464                    KLEN = key.length();
465                    LEN  = KLEN + VAL.length();
466    
467                    QRT_VAL = ( VAL.indexOf( '"' ) >= 0 ) ? "'" + VAL + "'" : "\"" + VAL + "\"" ;
468            }
469    
470            /**
471             * キーのæ–?­—長さã?比è¼?�§、大きい数字を返しますã?
472             *
473             * 属æ?キーのæœ?¤§のæ–?­—å?長を求めるためã?引数の長さと、属æ?キーの長さを比è¼?�—てã€?
474             * 大きな値の方を返しますã?
475             * こã?処ç�?‚’、属æ?すべてに行えば、最終的にæœ?‚‚大きな値が残ることになりますã?
476             *
477             * @param       maxLen  属æ?キーのæœ?¤§長ã�?
478             *
479             * @return      属æ?リスト群の長さ補正が行われた、属æ?キー?‹空白æ–?­—å?
480             */
481            protected int maxKeyLen( final int maxLen ) {
482                    return (maxLen > KLEN) ? maxLen : KLEN ;
483            }
484    
485            /**
486             * 長さ補正が行われた属æ?キーを取得しますã?
487             *
488             * useCR=true の場合に、属æ?の改行が行われますが、そのときに、キーが縦に並びますã?
489             * そして、å?も縦に並ぶためã€?–“の ?¢?�」記号の位置をそろえて、表示しますã?
490             * 属æ?リストã?æœ?¤§長さ+ï¼?になるよã�?�«、キーのæ–?­—å?にスペã?スを埋めますã?
491             * これにより、属æ?を改行して表示してもã?値の表示位置がそろいますã?
492             *
493             * @param       maxLen  属æ?キーのæœ?¤§長ã�?
494             *
495             * @return      属æ?リスト群の長さ補正が行われた、属æ?キー?‹空白æ–?­—å?
496             */
497            protected String getAlignKey( final int maxLen ) {
498                    return KEY + SPACE.substring( KLEN,maxLen ) ;
499            }
500    
501            /**
502             * HTML上ã?エスケープ文字を変換しますã?
503             *
504             * HTMLで表示する場合にきちんとエスケープ文字に変換しておかなã�?�¨
505             * Script を実行されたりã?不要なHTMLコマンドを潜り込まされたりするためã€?
506             * セキュリãƒ?‚£ーホã?ルになる可能性があるã?で、注意してくださいã€?
507             *
508             * ※ オリジナルは、org.opengion.fukurou.util.StringUtil#htmlFilter( String )
509             * ですが、ダブルクオートã?シングルクオートã?変換処ç�?‚’省いてã�?�¾すã?
510             *
511             * @param       input HTMLエスケープ前のæ–?­—å?
512             *
513             * @return      エスケープ文字に変換後ã?æ–?­—å?
514             * @see  org.opengion.fukurou.util.StringUtil#htmlFilter( String )
515             */
516            private String htmlFilter( final String input ) {
517                    if( input == null || input.length() == 0 ) { return ""; }
518                    StringBuilder rtn = new StringBuilder();
519                    char ch;
520                    for(int i=0; i<input.length(); i++) {
521                            ch = input.charAt(i);
522                            switch( ch ) {
523                                    case '<'  : rtn.append("&lt;");   break;
524                                    case '>'  : rtn.append("&gt;");   break;
525                    //              case '"'  : rtn.append("&quot;"); break;
526                    //              case '\'' : rtn.append("&#39;");  break;
527                                    case '&'  : rtn.append("&amp;");  break;
528                                    default   : rtn.append(ch);
529                            }
530                    }
531                    return( rtn.toString() );
532            }
533    }