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.InputSource;
019 import org.xml.sax.SAXException;
020 import org.xml.sax.Attributes;
021 import org.xml.sax.helpers.DefaultHandler;
022
023 import javax.xml.parsers.SAXParserFactory;
024 import javax.xml.parsers.SAXParser;
025 import javax.xml.parsers.ParserConfigurationException;
026
027 import java.io.Reader;
028 import java.io.IOException;
029 import java.util.Map;
030
031 /**
032 * こã?クラスは、拡張オラクル XDK形式ã?XMLファイルをå?ç�?�™るハンドラですã?
033 * オラクルXDK形式ã?XMLとは、下記ã?ような ROWSET をトãƒ??とする ROW の
034 * é›?�¾りで?‘レコードを表しã?各ROWには、カラãƒ?��をキーとするXMLになってã�?�¾すã?
035 *
036 * <ROWSET>
037 * <ROW num="1">
038 * <カラãƒ?>値1</カラãƒ?>
039 * ?¥?¥?¥
040 * <カラãƒ?>値n</カラãƒ?>
041 * </ROW>
042 * ?¥?¥?¥
043 * <ROW num="n">
044 * ?¥?¥?¥
045 * </ROW>
046 * <ROWSET>
047 *
048 * こã?形式であれば、XDK(Oracle XML Developer's Kit)を利用すればã€?�ž常に簡単に
049 * ãƒ??タベã?スとXMLファイルとの交換が可能ですã?
050 * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" >
051 * XDK(Oracle XML Developer's Kit)</a>
052 *
053 * 拡張XDK形式とは、ROW 以外に、SQL処ç�?”¨タグ(EXEC_SQL)を持つ XML ファイルですã?
054 * またã?登録するãƒ??ブル(table)ã‚?ROWSETタグの属æ?æƒ??として付与することができますã?
055 * (大æ–?—小文字に注æ„?
056 * これは、オラクルXDKで処ç�?�™るå?合ã?無視されますã?で、同様に扱ã�?�“とがå?来ますã?
057 * こã?、EXEC_SQL は、それそれã? XMLãƒ??タをデータベã?スに登録する際にã€?
058 * SQL処ç�?‚’自動的に流す為の、SQLæ–?‚’記載しますã?
059 * こã?処ç�??、イベント毎に実行される為、その配置é ??重要ですã?
060 * こã?タグは、è¤?•°記述することもå?来ますが、BODY部にはã€?¼‘つのSQLæ–??み記述しますã?
061 *
062 * <ROWSET tableName="XX" >
063 * <EXEC_SQL> æœ??に記載して、å?期å?ç�?ãƒ??タクリアç?を実行させるã€?
064 * delete from GEXX where YYYYY
065 * </EXEC_SQL>
066 * <MERGE_SQL> こã?SQLæ–?�§ UPDATEして、結果が0件ならINSERTを行いますã?
067 * update GEXX set AA=[AA] , BB=[BB] where CC=[CC]
068 * </MERGE_SQL>
069 * <ROW num="1">
070 * <カラãƒ?>値1</カラãƒ?>
071 * ?¥?¥?¥
072 * <カラãƒ?>値n</カラãƒ?>
073 * </ROW>
074 * ?¥?¥?¥
075 * <ROW num="n">
076 * ?¥?¥?¥
077 * </ROW>
078 * <EXEC_SQL> æœ?¾Œに記載してã€??目の設å®?整合æ?登録)を行うã€?
079 * update GEXX set AA='XX' , BB='YY' where CC='ZZ'
080 * </EXEC_SQL>
081 * <ROWSET>
082 *
083 * DefaultHandler クラスを拡張してã�?‚‹為ã€??常の処ç�?�¨同様に、使用できますã?
084 *
085 * InputSource input = new InputSource( reader );
086 * HybsXMLHandler hndler = new HybsXMLHandler();
087 *
088 * SAXParserFactory f = SAXParserFactory.newInstance();
089 * SAXParser parser = f.newSAXParser();
090 * parser.parse( input,hndler );
091 *
092 * またã?上記ã?処ç�?��のもã?を簡略化したメソãƒ?ƒ‰:parse( Reader ) を持ってã�?‚‹ためã€?
093 * 通常そã?メソãƒ?ƒ‰を使用しますã?
094 *
095 * HybsXMLHandler には、TagElementListener をセãƒ?ƒˆすることができますã?
096 * これは、ROW 毎に å†?ƒ¨æƒ??ã‚?TagElement オブジェクト化しã?action( TagElement )
097 * が呼び出されますã?こã? Listener を介してã€?¼‘レコードずつ処ç�?�™ることã�?
098 * 可能ですã?
099 *
100 * @version 4.0
101 * @author Kazuhiko Hasegawa
102 * @since JDK5.0,
103 */
104 public class HybsXMLHandler extends DefaultHandler {
105 /** シスãƒ?ƒ 依存ã?改行記号をセãƒ?ƒˆしますã? */
106 private static final String CR = System.getProperty("line.separator");
107
108 /** こã?ハンドラのトップタグå�? {@value} */
109 public static final String ROWSET = "ROWSET";
110 /** こã?ハンドラで取り扱える ROWSETタグの属æ? */
111 public static final String ROWSET_TABLE = "tableName";
112
113 /** こã?ハンドラで取り扱えるタグå�? {@value} */
114 public static final String ROW = "ROW";
115 /** こã?ハンドラで取り扱える ROWタグの属æ? {@value} */
116 public static final String ROW_NUM = "num";
117 /** こã?ハンドラで取り扱えるタグå�? {@value} */
118 public static final String EXEC_SQL = "EXEC_SQL";
119 /** こã?ハンドラで取り扱えるタグå�? {@value} */
120 public static final String MERGE_SQL = "MERGE_SQL";
121
122 private Map<String,String> defaultMap = null;
123 private TagElementListener listener = null;
124 private TagElement element = null;
125 private String key = null;
126 private StringBuilder body = null;
127 private boolean bodyIn = false;
128 private int level = 0;
129
130 /**
131 * パã?ス処ç�?‚’行いますã?
132 * 通常のパã?ス処ç�??簡易メソãƒ?ƒ‰になってã�?�¾すã?
133 *
134 * @param reader パã?ス処ç�?”¨のReaderオブジェクãƒ?
135 */
136 public void parse( final Reader reader ) {
137 try {
138 SAXParserFactory fact = SAXParserFactory.newInstance();
139 SAXParser parser = fact.newSAXParser();
140
141 InputSource input = new InputSource( reader );
142
143 try {
144 parser.parse( input,this );
145 }
146 catch( SAXException ex ) {
147 if( ! "END".equals( ex.getMessage() ) ) {
148 String errMsg = "XMLパã?スエラー key=" + key + CR
149 + "element=" + element + CR
150 + ex.getMessage() ;
151 if( body != null ) {
152 errMsg = errMsg + CR + body.toString();
153 }
154 throw new RuntimeException( errMsg,ex );
155 }
156 }
157 }
158 catch( ParserConfigurationException ex1 ) {
159 String errMsg = "SAXParser のコンフィグレーションが構築できませんã€?
160 + "key=" + key + CR + ex1.getMessage();
161 throw new RuntimeException( errMsg,ex1 );
162 }
163 catch( SAXException ex2 ) {
164 String errMsg = "SAXParser が構築できませんã€?
165 + "key=" + key + CR + ex2.getMessage();
166 throw new RuntimeException( errMsg,ex2 );
167 }
168 catch( IOException ex3 ) {
169 String errMsg = "InputSource の読み取り時にエラーが発生しましたã€?
170 + "key=" + key + CR + ex3.getMessage();
171 throw new RuntimeException( errMsg,ex3 );
172 }
173 }
174
175 /**
176 * å†?ƒ¨に TagElementListener を登録しますã?
177 * これはã€?lt;ROW> タグの endElement 処ç�?¯Žに呼び出されますã?
178 * つまりã?行データを取得é?度、TagElement オブジェクトを作æ?しã?
179 * こã? TagElementListener の action( TagElement ) メソãƒ?ƒ‰を呼び出しますã?
180 * 何もセãƒ?ƒˆしなã�??またã?、null がセãƒ?ƒˆされたå?合ã?、何もしませんã€?
181 *
182 * @param listener TagElementListenerオブジェクãƒ?
183 */
184 public void setTagElementListener( final TagElementListener listener ) {
185 this.listener = listener;
186 }
187
188 /**
189 * TagElement オブジェクトを作æ?する時ã? 初期カラãƒ?値を設定しますã?
190 * TagElements オブジェクトã?、XMLファイルより作æ?する為ã€??目(カラãƒ?ã‚?
191 * XMLファイルのROW属æ?に持ってã�?‚‹é ?›®と値で作æ?されますã?
192 * こã?カラãƒ?��をã?外部から初期設定することが可能ですã?
193 * そã?場合ã?ここで登録したカラãƒ??(Mapに、LinkedHashMap を使用した場å�?
194 * が保持されますã?またã?ROW属æ?に存在しなã�?‚«ラãƒ?�Œあれば、å?とともに
195 * 初期値として設定しておくことが可能ですã?
196 * なおã?ここでのMapは、直接設定してã�?�¾すã?で、ご注意くã�?�•ã�??
197 *
198 * @param map 初期カラãƒ??ãƒ??
199 */
200 public void setDefaultMap( final Map<String,String> map ) {
201 defaultMap = map;
202 }
203
204 /**
205 * 要ç´??のæ–?—データの通知を受け取りますã?
206 * インタフェース ContentHandler å†?? characters メソãƒ?ƒ‰をオーバã?ライドしてã�?�¾すã?
207 * å�?–‡字データチャンクに対して特殊なアクション (ノã?ドまたã?バッファへのãƒ??タの追åŠ??
208 * ãƒ??タのファイルへの出力など) を実行することができますã?
209 *
210 * @param buffer æ–?—データ配å?
211 * @param start 配å?å†??開始位置
212 * @param length 配å?から読み取られるæ–?—数
213 * @see org.xml.sax.helpers.DefaultHandler#characters(char[] , int , int )
214 */
215 @Override
216 public void characters( final char[] buffer, final int start, final int length ) throws SAXException {
217 if( ! ROW.equals( key ) && ! ROWSET.equals( key ) && length > 0 ) {
218 body.append( buffer,start,length );
219 bodyIn = true;
220 }
221 }
222
223 /**
224 * 要ç´??開始é?知を受け取りますã?
225 * インタフェース ContentHandler å†?? startElement メソãƒ?ƒ‰をオーバã?ライドしてã�?�¾すã?
226 * パã?サは XML æ–?›¸å†??å�?¦�ç´??前でこã?メソãƒ?ƒ‰を呼び出しますã?
227 * å�?startElement イベントには対応すã‚?endElement イベントがありますã?
228 * これは、要ç´?�Œ空である場合も変わりません。対応すã‚?endElement イベントã?前にã€?
229 * 要ç´??コンãƒ?ƒ³ãƒ??部がé?番に報告されますã?
230 * ここでは、タグがレベル?“以上ã?場合ã?、上位タグのå†?®¹として取り扱ã�?�¾すã?よってã€?
231 * タグに名前空間が定義されてã�?‚‹場合ã?そã?属æ?は削除しますã?
232 *
233 * @param namespace 名前空é–??µ?²?©
234 * @param localName 前置修飾子を含まなã�?ƒーカル名ã?名前空間å?ç�?�Œ行われなã�??合ã?空æ–?—å?
235 * @param qname 前置修飾子を持つ修飾名ã?修飾名を使用できなã�??合ã?空æ–?—å?
236 * @param attributes 要ç´?�«付加された属æ?。属æ?が存在しなã�??合ã?空の Attributesオブジェクãƒ?
237 * @see org.xml.sax.helpers.DefaultHandler#startElement(String , String , String , Attributes )
238 */
239 @Override
240 public void startElement(final String namespace, final String localName,
241 final String qname, final Attributes attributes) throws SAXException {
242 if( ROWSET.equals( qname ) ) {
243 if( listener != null ) {
244 element = new TagElement( ROWSET,defaultMap );
245 element.put( ROWSET_TABLE,attributes.getValue( ROWSET_TABLE ) );
246 listener.actionInit( element );
247 }
248 element = null;
249 }
250 else if( ROW.equals( qname ) ) {
251 element = new TagElement( ROW,defaultMap );
252 String num = attributes.getValue( ROW_NUM );
253 element.setRowNo( num );
254 }
255 else if( EXEC_SQL.equals( qname ) ) {
256 element = new TagElement( EXEC_SQL );
257 }
258 else if( MERGE_SQL.equals( qname ) ) {
259 element = new TagElement( MERGE_SQL );
260 }
261
262 if( level <= 2 ) {
263 key = qname;
264 body = new StringBuilder();
265 }
266 else {
267 // レベル??以上ã?タグは上位タグのå†?®¹として扱ã�?�¾すã?
268 body.append( "<" ).append( qname );
269 int len = attributes.getLength();
270 for( int i=0; i<len; i++ ) {
271 // 名前空間ã?宣è¨??、削除しておきますã?あくまでãƒ??タとして取り扱ã�?‚ºですã?
272 String attr = attributes.getQName(i);
273 if( ! attr.startsWith( "xmlns:" ) ) {
274 body.append( " " );
275 body.append( attr ).append( "=\"" );
276 body.append( attributes.getValue(i) ).append( "\"" );
277 }
278 }
279 body.append( ">" );
280 }
281
282 bodyIn = false; // 入れ子状のタグのBODY部の有無
283 level ++ ;
284 }
285
286 /**
287 * 要ç´??終äº??知を受け取りますã?
288 * インタフェース ContentHandler å†?? endElement メソãƒ?ƒ‰をオーバã?ライドしてã�?�¾すã?
289 * SAX パã?サは、XML æ–?›¸å†??å�?¦�ç´??終わりにこã?メソãƒ?ƒ‰を呼び出しますã?
290 * å�?endElement イベントには対応すã‚?startElement イベントがありますã?
291 * これは、要ç´?�Œ空である場合も変わりませんã€?
292 *
293 * @param namespace 名前空é–?URI
294 * @param localName 前置修飾子を含まなã�?ƒーカル名ã?名前空間å?ç�?�Œ行われなã�??合ã?空æ–?—å?
295 * @param qname 前置修飾子を持つ XML 1.0 修飾名ã?修飾名を使用できなã�??合ã?空æ–?—å?
296 * @see org.xml.sax.helpers.DefaultHandler#endElement(String , String , String )
297 */
298 @Override
299 public void endElement(final String namespace, final String localName, final String qname) throws SAXException {
300 level -- ;
301 if( ROW.equals( qname ) ) {
302 if( listener != null ) {
303 listener.actionRow( element );
304 }
305 element = null;
306 }
307 else if( EXEC_SQL.equals( qname ) ) {
308 element.setBody( body.toString().trim() );
309 if( listener != null ) {
310 listener.actionExecSQL( element );
311 }
312 element = null;
313 }
314 else if( MERGE_SQL.equals( qname ) ) {
315 element.setBody( body.toString().trim() );
316 if( listener != null ) {
317 listener.actionMergeSQL( element );
318 }
319 element = null;
320 }
321 else if( level <= 2 ) {
322 if( element != null ) {
323 element.put( key , body.toString().trim() );
324 }
325 }
326 else {
327 if( bodyIn ) {
328 body.append( "</" ).append( qname ).append( ">" );
329 }
330 else {
331 body.insert( body.length()-1, " /" ); // タグのæœ?¾Œを " />" とするã€?
332 }
333 }
334 }
335 }