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 org.xml.sax.Attributes; 019 020/** 021 * エレメントをあらわす、OGElement クラスを定義します。 022 * 023 * エレメントは、OGNode クラスを継承し、名称、属性、ノードリストを持つオブジェクトです。 024 * 通常で言うところの、タグになります。 025 * 属性は、OGAttributes クラスで管理します。ノードリスト に関する操作は、OGNodeクラスの実装です。 026 * 027 * OGNode は、enum OGNodeType で区別される状態を持っています。 028 * OGNodeType は、それぞれ、再設定が可能です。 029 * 例えば、既存のエレメントやノードに対して、コメントタイプ(Comment)を指定すると、 030 * ファイル等への出力時にコメントとして出力されます。 031 * 032 * @og.rev 5.1.8.0 (2010/07/01) 新規作成 033 * 034 * @version 5.0 035 * @author Kazuhiko Hasegawa 036 * @since JDK6.0, 037 */ 038public class OGElement extends OGNode { 039 040 private final String qName ; // このタグの名前(nameSpace も含むエレメントの名前) 041 private OGAttributes attri = null; // 属性オブジェクト 042 043 // 階層に応じたスペースの設定 044 private static final int PARA_LEN = 8; 045 private static final String PARA_CHAR = "\t"; 046 private static final String[] PARA = new String[PARA_LEN]; 047 static { 048 PARA[0] = CR; 049 StringBuilder buf = new StringBuilder(); 050 buf.append( CR ); 051 for( int i=1; i<PARA_LEN; i++ ) { 052 buf.append( PARA_CHAR ); 053 PARA[i] = buf.toString(); 054 } 055 } 056 057 /** 058 * ノード名を指定してのトコンストラクター 059 * 060 * ノード名のみ指定するため、属性と、ノードリストが空のエレメントを構築します。 061 * 062 * @param qName ノード名 063 */ 064 public OGElement( final String qName ) { 065 this( qName,null ); 066 } 067 068 /** 069 * ノード名、属性タブ、属性リストを指定してのトコンストラクター 070 * 071 * 注意 属性値の正規化は必ず行われます。 072 * 属性値に含まれるCR(復帰), LF(改行), TAB(タブ)は、 半角スペースに置き換えられます。 073 * XMLの規定では、属性の並び順は保障されませんが、SAXのAttributesは、XMLに記述された順番で 074 * 取得できていますので、このクラスでの属性リストも、記述順での並び順になります。 075 * 076 * @og.rev 5.2.1.0 (2010/10/01) タグ属性の改行処理を、Set からString[] に変更。 077 * @og.rev 5.6.1.2 (2013/02/22) CR_SET を配列から文字列に変更 078 * 079 * @param qName ノード名 080 * @param atts 属性リスト 081 */ 082 public OGElement( final String qName , final Attributes atts ) { 083 super(); 084 setNodeType( OGNodeType.Element ); 085 086 if( qName == null ) { 087 String errMsg = "エレメントには、ノード名は必須です。"; 088 throw new RuntimeException( errMsg ); 089 } 090 091 this.qName = qName; 092 this.attri = new OGAttributes( atts ) ; 093 } 094 095 /** 096 * ノード名を返します。 097 * 098 * @return ノード名 099 */ 100 public String getTagName() { 101 return qName; 102 } 103 104 /** 105 * 属性オブジェクトを返します。 106 * 107 * これは、org.xml.sax.Attributes ではなく、OGAttributes オブジェクトを返します。 108 * 内部オブジェクトそのものを返しますので、この OGAttributes の変更は、この 109 * エレメントが持つ内部属性も変更されます。 110 * 111 * @return 属性オブジェクト 112 */ 113 public OGAttributes getOGAttributes() { 114 return attri; 115 } 116 117 /** 118 * 属性オブジェクトをセットします。 119 * 120 * 属性オブジェクトのセットは、このメソッドからのみできるようにします。 121 * 内部オブジェクトそのものにセットしますので、異なる OGAttributes をセットしたい場合は、 122 * 外部で、コピーしてからセットしてください。 123 * 124 * @og.rev 5.6.1.2 (2013/02/22) 新規追加 125 * 126 * @param attri 属性オブジェクト(org.opengion.fukurou.xml.OGAttributes) 127 */ 128 public void setOGAttributes( final OGAttributes attri ) { 129 this.attri = attri; 130 } 131 132 /** 133 * 属性リストから、id属性の、属性値を取得します。 134 * 135 * id属性 は、内部的にキャッシュしており、すぐに取り出せます。 136 * タグを特定する場合、一般属性のキーと値で選別するのではなく、 137 * id属性を付与して選別するようにすれば、高速に見つけることが可能になります。 138 * 139 * @og.rev 5.1.9.0 (2010/08/01) 新規追加 140 * 141 * @return id属性値 142 */ 143 public String getId() { 144 return (attri != null) ? attri.getId() : null ; 145 } 146 147 /** 148 * 属性リストから、指定の属性キーの、属性値を取得します。 149 * 150 * この処理は、属性リストをすべてスキャンして、キーにマッチする 151 * 属性オブジェクトを見つけ、そこから、属性値を取り出すので、 152 * パフォーマンスに問題があります。 153 * 基本的には、アドレス指定で、属性値を取り出すようにしてください。 154 * 155 * @og.rev 5.6.1.2 (2013/02/22) 新規追加 156 * 157 * @param key 属性キー 158 * 159 * @return 属性値 160 */ 161 public String getVal( final String key ) { 162 return (attri != null) ? attri.getVal( key ) : null ; 163 } 164 165 /** 166 * 属性リストに、属性(キー、値のセット)を設定します。 167 * 168 * 属性リストの一番最後に、属性(キー、値のセット)を設定します。 169 * 170 * @param key 属性リストのキー 171 * @param val 属性リストの値 172 */ 173 public void addAttr( final String key , final String val ) { 174 if( attri == null ) { attri = new OGAttributes() ; } 175 attri.add( key,val ) ; 176 } 177 178 /** 179 * 自分自身の状態が、指定の条件に合致しているかどうか、判定します。 180 * 181 * 合致している場合は、true を、合致していない場合は、false を返します。 182 * 183 * 指定の属性が null の場合は、すべてに合致すると判断します。 184 * 例えば、kye のみ指定すると、その属性名を持っているエレメントすべてで 185 * true が返されます。 186 * 実行速度を考えると、ノード名は指定すべきです。 187 * 188 * @param name ノード名 null の場合は、すべての ノード名 に合致 189 * @param key 属性名 null の場合は、すべての 属性名 に合致 190 * @param val 属性値 null の場合は、すべての 属性値 に合致 191 * 192 * @return 条件がこのエレメントに合致した場合 true 193 */ 194 public boolean match( final String name , final String key , final String val ) { 195 // name が存在するが、不一致の場合は、false 196 if( name != null && ! name.equals( qName ) ) { return false; } 197 198 // attri が null なのに、key か val が、null でない場合は合致しないので、false と判断 199 if( attri == null && ( key != null || val != null ) ) { return false; } 200 201 // キーが存在し、値も存在する場合は、その値の合致と同じ結果となる。 202 if( key != null ) { 203 if( val != null ) { return val.equals( attri.getVal( key ) ); } // 値があれば、比較する。 204 else { return attri.getAdrs( key ) >= 0 ; } // 値がなければ、存在チェック 205 } 206 207 // 値が存在する場合は、その値が含まれるかチェックし、あれば、true, なければ false 208 if( val != null ) { 209 boolean flag = false; 210 int len = attri.size(); 211 for( int i=0; i<len; i++ ) { 212 if( val.equals( attri.getVal(i) ) ) { flag = true; break; } 213 } 214 return flag; 215 } 216 217 // 上記の条件以外は、すべてが null なので、true 218 return true; 219 } 220 221 /** 222 * 段落文字列を返します。 223 * 224 * 段落文字列は、階層を表す文字列です。 225 * 通常は TAB ですが、XMLの階層が、PARA_LEN を超えても、段落を増やしません。 226 * 段落の最初の文字は、改行です。 227 * 228 * @og.rev 5.6.1.2 (2013/02/22) 内部テキストがない場合のタグの終了時にスペースは入れない。 229 * @og.rev 5.6.4.4 (2013/05/31) PARA_LEN を超えても、段落を増やしません。 230 * 231 * @param cnt 階層(-1:なし。 232 * @return 段落文字列 233 * @see OGNodeType 234 */ 235 private String getPara( final int cnt ) { 236 if( cnt < 0 ) { return ""; } 237 if( cnt < PARA_LEN ) { return PARA[cnt]; } 238 else { return PARA[PARA_LEN-1]; } // 5.6.4.4 (2013/05/31) PARA_LEN を超えても、段落を増やしません。 239 } 240 241 /** 242 * オブジェクトの文字列表現を返します。 243 * 244 * 文字列は、OGNodeType により異なります。 245 * Comment ノードの場合は、コメント記号を、Cdata ノードの場合は、CDATA を 246 * つけて出力します。 247 * 248 * @og.rev 5.6.1.2 (2013/02/22) 内部テキストがない場合のタグの終了時にスペースは入れない。 249 * @og.rev 5.6.4.4 (2013/05/31) 改行3つを改行2つに置換します。 250 * 251 * @param cnt Nodeの階層(-1:なし、0:改行のみ、1:改行+" "・・・・) 252 * @return このオブジェクトの文字列表現 253 * @see OGNode#toString() 254 */ 255 @Override 256 public String getText( final int cnt ) { 257 StringBuilder buf = new StringBuilder(); 258 259 buf.append( getPara(cnt) ); 260 buf.append( "<" ).append( qName ); 261 262 buf.append( attri.getText( getPara(cnt+1) ) ); 263 264 String text = super.getText(cnt+1); 265 266 if( text.trim().isEmpty() ) { 267 buf.append( "/>" ); // 5.6.1.2 (2013/02/22) タグの終了時にスペースは入れない。 268 } 269 else { 270 buf.append( ">" ).append( text ); 271 buf.append( getPara(cnt) ); 272 buf.append( "</" ).append( qName ).append( ">" ); 273 // buf.append( CR ); 274 } 275 String rtn = buf.toString(); 276 277 switch( getNodeType() ) { 278 case Comment: rtn = "<!-- " + rtn + " -->"; break; 279 case Cdata: rtn = "<![CDATA[ " + rtn + " ]]>"; break; 280 // case Text: 281 // case List: 282 default: break; 283 } 284 285 return rtn.replaceAll( CR+CR+CR , CR+CR ) ; // 改行3つを改行2つに置換します。 286 } 287}