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("<"); break;
524 case '>' : rtn.append(">"); break;
525 // case '"' : rtn.append("""); break;
526 // case '\'' : rtn.append("'"); break;
527 case '&' : rtn.append("&"); break;
528 default : rtn.append(ch);
529 }
530 }
531 return( rtn.toString() );
532 }
533 }