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.opengion.fukurou.util.Closer ; 019import org.opengion.fukurou.util.FileUtil ; 020 021import java.io.PrintWriter ; 022import java.io.BufferedWriter ; 023import java.io.OutputStreamWriter ; 024import java.io.FileOutputStream ; 025import java.io.IOException ; 026import java.io.File; 027import java.io.StringReader ; 028import java.io.FileNotFoundException ; 029import java.io.UnsupportedEncodingException; 030import java.util.Stack; 031import java.util.List; 032import java.util.ArrayList; 033import java.util.Map; 034import java.util.HashMap; 035 036import org.xml.sax.Attributes; 037import org.xml.sax.ext.DefaultHandler2; 038import org.xml.sax.InputSource ; 039import org.xml.sax.SAXException; 040import org.xml.sax.SAXParseException; 041import javax.xml.parsers.SAXParserFactory; 042import javax.xml.parsers.SAXParser; 043import javax.xml.parsers.ParserConfigurationException; 044 045/** 046 * JSP/XMLファイルを読み取って、OGNode/OGElement オブジェクトを取得する、パーサークラスです。 047 * 048 * 自分自身が、DefaultHandler2 を拡張していますので、パーサー本体になります。 049 * javax.xml.parsers および、org.w3c.dom の簡易処理を行います。 050 * read で、トップレベルの OGNode を読み込み、write で、ファイルに書き出します。 051 * 通常の W3C 系の オブジェクトを利用しないのは、属性の並び順を保障するためです。 052 * ただし、属性のタブ、改行は失われます。 053 * また、属性値に含まれるCR(復帰), LF(改行), TAB(タブ)は、 半角スペースに置き換えられます。 054 * これは、SAXParser 側での XML の仕様の関係で、属性は、正規化されるためです。 055 * 056 * @og.rev 5.1.8.0 (2010/07/01) 新規作成 057 * @og.rev 5.1.9.0 (2010/08/01) static メソッドを廃止。通常のオブジェクトクラスとして扱います。 058 * 059 * @version 5.0 060 * @author Kazuhiko Hasegawa 061 * @since JDK6.0, 062 */ 063public class JspSaxParser extends DefaultHandler2 { 064 public static final String CR = System.getProperty("line.separator"); 065 066 private final List<JspParserFilter> filters = new ArrayList<JspParserFilter>(); // 5.1.9.0 (2010/08/01) 067 private SAXParser parser = null; 068 069 // 以下、パース時に使用する変数。(パース毎に初期化する。) 070 private Map<String,OGElement> idMap = null; // 5.1.9.0 (2010/08/01) 071 private Stack<OGNode> stack = null; 072 073 private OGNode ele = null; // 現時点のエレメントノード 074 private String attTab = ""; // tagBefore の一次TEMP 075 private boolean inCDATA = false; // CDATA エレメントの中かどうかの判定 076 private boolean inEntity = false; // Entity の中かどうかの判定 077// private File file = null; // 処理実行中のファイル名 078 private String filename = null; // 処理実行中のファイル名 079 080 /** 081 * XMLファイルを読み込み、OGDocument を返します。 082 * 083 * 内部的には、SAXParserFactory から、SAXParser を構築し、Property に、 084 * http://xml.org/sax/properties/lexical-handler を設定しています。 085 * コメントノードを処理するためです。 086 * 087 * @og.rev 5.1.9.0 (2010/08/01) static からノーマルに変更 088 * 089 * @param aFile XMLファイル 090 * 091 * @return ファイルから読み取って構築したOGDocumentオブジェクト 092 */ 093 public OGDocument read( final File aFile ) { 094 095// JspSaxParser sxp = new JspSaxParser(); 096// sxp.setFile( aFile ); 097 filename = aFile.getAbsolutePath() ; 098 099 try { 100 if( parser == null ) { 101 // SAXパーサーファクトリを生成 102 SAXParserFactory spfactory = SAXParserFactory.newInstance(); 103 104 // SAXパーサーを生成 105 parser = spfactory.newSAXParser(); 106 107 parser.setProperty("http://xml.org/sax/properties/lexical-handler", this); // LexicalHandler として 108 } 109 // XMLファイルを指定されたハンドラーで処理します 110 parser.parse( aFile, this ); 111 112 } catch ( ParserConfigurationException ex ) { 113 String errMsg = "重大な構成エラーが発生しました。" 114 + CR + "\t" + ex.getMessage() 115 + CR + "\t" + aFile ; 116 throw new RuntimeException( errMsg,ex ); 117 // 5.1.9.0 (2010/08/01) 廃止 118 // } catch ( SAXNotRecognizedException ex ) { 119 // String errMsg = "XMLReader は、認識されない機能またはプロパティー識別子を検出しました。" 120 // + CR + "\t" + ex.getMessage() 121 // + CR + "\t" + aFile ; 122 // if( ex2 != null ) { errMsg = errMsg + CR + "\t" + ex2.getMessage(); } 123 // throw new RuntimeException( errMsg,ex ); 124 // } catch ( SAXNotSupportedException ex ) { 125 // String errMsg = "XMLReader は、要求された操作 (状態または値の設定) を実行できませんでした。" 126 // + CR + "\t" + ex.getMessage() 127 // + CR + "\t" + aFile ; 128 // if( ex2 != null ) { errMsg = errMsg + CR + "\t" + ex2.getMessage(); } 129 // throw new RuntimeException( errMsg,ex ); 130 } catch ( SAXException ex ) { 131 String errMsg = "SAX の一般的なエラーが発生しました。" 132 + CR + "\t" + ex.getMessage() 133 + CR + "\t" + aFile ; 134 Exception ex2 = ex.getException(); 135 if( ex2 != null ) { errMsg = errMsg + CR + "\t" + ex2.getMessage(); } 136 throw new RuntimeException( errMsg,ex ); 137 } catch ( IOException ex ) { 138 String errMsg = "ファイル読取時にエラーが発生しました。" 139 + CR + "\t" + ex.getMessage() 140 + CR + "\t" + aFile ; 141 throw new RuntimeException( errMsg,ex ); 142 // 5.1.9.0 (2010/08/01) 廃止 143 // } catch( RuntimeException ex ) { 144 // String errMsg = "実行時エラーが発生しました。" 145 // + CR + "\t" + ex.getMessage() 146 // + CR + "\t" + aFile ; 147 // throw new RuntimeException( errMsg,ex ); 148 } 149 150 return getDocument() ; 151 } 152 153 /** 154 * XML形式で表現された、文字列(String) から、OGDocument を構築します。 155 * 156 * 処理的には、#read( File ) と同じで、取り出す元が、文字列というだけです。 157 * XMLファイルからの読み込みと異なり、通常は、Element を表現した文字列が作成されますが、 158 * 返されるのは、OGDocument オブジェクトです。 159 * 160 * @og.rev 5.1.9.0 (2010/08/01) static からノーマルに変更 161 * 162 * @param str XML形式で表現された文字列 163 * 164 * @return ファイルから読み取って構築した OGDocumentオブジェクト 165 */ 166 public OGDocument string2Node( final String str ) { 167 168// JspSaxParser sxp = new JspSaxParser(); 169 filename = null ; 170 171 try { 172 if( parser == null ) { 173 // SAXパーサーファクトリを生成 174 SAXParserFactory spfactory = SAXParserFactory.newInstance(); 175 // SAXパーサーを生成 176 parser = spfactory.newSAXParser(); 177 178 parser.setProperty("http://xml.org/sax/properties/lexical-handler", this); // LexicalHandler として 179 } 180 181 // XMLファイルを指定されたデフォルトハンドラーで処理します 182 InputSource source = new InputSource( new StringReader( str ) ); 183 parser.parse( source, this ); 184 185 } catch ( ParserConfigurationException ex ) { 186 String errMsg = "重大な構成エラーが発生しました。" 187 + CR + ex.getMessage(); 188 throw new RuntimeException( errMsg,ex ); 189 // 5.1.9.0 (2010/08/01) 廃止 190 // } catch ( SAXNotRecognizedException ex ) { 191 // String errMsg = "XMLReader は、認識されない機能またはプロパティー識別子を検出しました。" 192 // + CR + ex.getMessage(); 193 // Exception ex2 = ex.getException(); 194 // if( ex2 != null ) { errMsg = errMsg + CR + "\t" + ex2.getMessage(); } 195 // throw new RuntimeException( errMsg,ex ); 196 } catch ( SAXException ex ) { 197 String errMsg = "SAX の一般的なエラーが発生しました。" 198 + CR + ex.getMessage(); 199 Exception ex2 = ex.getException(); 200 if( ex2 != null ) { errMsg = errMsg + CR + "\t" + ex2.getMessage(); } 201 throw new RuntimeException( errMsg,ex ); 202 } catch ( IOException ex ) { 203 String errMsg = "ストリームオブジェクト作成時にエラーが発生しました。" 204 + CR + ex.getMessage(); 205 throw new RuntimeException( errMsg,ex ); 206 // 5.1.9.0 (2010/08/01) 廃止 207 // } catch( RuntimeException ex ) { 208 // String errMsg = "実行時エラーが発生しました。" 209 // + CR + ex.getMessage(); 210 // throw new RuntimeException( errMsg,ex ); 211 } 212 213 return getDocument() ; 214 } 215 216 /** 217 * OGDocument を所定のファイルに、XML形式で書き出します。 218 * 219 * ここでは、UTF-8 文字コードでの書き出しです。 220 * 221 * @og.rev 5.1.9.0 (2010/08/01) static からノーマルに変更 222 * 223 * @param aFile 書き出すファイル 224 * @param node 書き出す OGDocument 225 */ 226// public void write( final File aFile, final OGDocument node ) { 227// write( aFile,node,"UTF-8" ); 228// } 229 230 /** 231 * OGDocument を所定のファイルに、XML形式で書き出します。 232 * 233 * @param aFile 書き出すファイル 234 * @param node 書き出す OGDocument 235 */ 236 public void write( final File aFile, final OGDocument node ) { 237 PrintWriter out = null; 238 String encode = node.getEncode(); 239 try { 240 out = new PrintWriter( new BufferedWriter( new OutputStreamWriter( new FileOutputStream(aFile),encode ))); 241// out.println( "<?xml version=\"1.0\" encoding=\"" + encode + "\"?>" ); 242 out.println( node.toString() ); 243 } catch ( FileNotFoundException ex ) { 244 String errMsg = "指定されたパス名で示されるファイルが存在しませんでした。" 245 + CR + "\t" + ex.getMessage() 246 + CR + "\t" + aFile ; 247 throw new RuntimeException( errMsg,ex ); 248 } catch ( UnsupportedEncodingException ex ) { 249 String errMsg = "文字のエンコーディング(" + encode + ")がサポートされていません。" 250 + CR + "\t" + ex.getMessage() 251 + CR + "\t" + aFile ; 252 throw new RuntimeException( errMsg,ex ); 253 // 5.1.9.0 (2010/08/01) 廃止 254 // } catch( RuntimeException ex ) { 255 // String errMsg = "実行時エラーが発生しました。" 256 // + CR + "\t" + ex.getMessage() 257 // + CR + "\t" + aFile ; 258 // throw new RuntimeException( errMsg,ex ); 259 } 260 finally { 261 Closer.ioClose( out ); 262 } 263 } 264 265 /** 266 * ディレクトリの再帰処理でパース処理を行います。 267 * 268 * @og.rev 5.1.9.0 (2010/08/01) static からノーマルに変更 269 * 270 * @param fromFile 読み取りもとのファイル/フォルダ 271 * @param toFile 書き込み先のファイル/フォルダ 272 */ 273 public void copyDirectry( final File fromFile, final File toFile ) { 274 // コピー元がファイルの場合はコピーして、終了する。 275 if( fromFile.exists() && fromFile.isFile() ) { 276 boolean isOK = false; 277 String name = fromFile.getName(); 278 if( name.endsWith( ".jsp" ) || name.endsWith( ".xml" ) ) { 279 try { 280 OGDocument doc = read( fromFile ); 281 if( doc != null && !filters.isEmpty() ) { 282 for( JspParserFilter filter: filters ) { 283 doc = filter.filter( doc ); 284 if( doc == null ) { break; } // エラー、または処理の中止 285 } 286 } 287 if( doc != null ) { 288 write( toFile,doc ); 289 isOK = true; 290 } 291 } 292 catch( RuntimeException ex ) { 293 // ex.printStackTrace(); 294 System.out.println( ex.getMessage() ); 295 } 296 } 297 298 // JSPやXMLでない、パースエラー、書き出しエラーなど正常終了できなかった場合は、バイナリコピー 299 if( !isOK ) { 300 FileUtil.copy( fromFile,toFile,true ); 301 } 302 return ; 303 } 304 305 // コピー先ディレクトリが存在しなければ、作成する 306 if( !toFile.exists() ) { 307 if( !toFile.mkdirs() ) { 308 System.err.println( toFile + " の ディレクトリ作成に失敗しました。" ); 309 return ; 310 } 311 } 312 313 // ディレクトリ内のファイルをすべて取得する 314 File[] files = fromFile.listFiles(); 315 316 // ディレクトリ内のファイルに対しコピー処理を行う 317 for( int i = 0; i<files.length; i++ ){ 318 copyDirectry( files[i], new File( toFile, files[i].getName()) ); 319 } 320 } 321 322 /** 323 * copyDirectry 処理で、OGDocument をフィルター処理するオブジェクトを登録します。 324 * 325 * 内部リストへフィルターを追加します。 326 * フィルター処理は、追加された順に行われます。 327 * 内部リストへの追加はできますが、削除はできません。 328 * 329 * @og.rev 5.1.9.0 (2010/08/01) 新規追加 330 * 331 * @param filter フィルターオブジェクト 332 */ 333 public void addFilter( final JspParserFilter filter ) { 334 filters.add( filter ); 335 } 336 337 /** 338 * サンプルプログラムです。 339 * 340 * 引数の IN がファイルの場合は、OUTもファイルとして扱います。 341 * IN がフォルダの場合は、階層にしたがって、再帰的に処理を行い、OUT に出力します。 342 * フォルダ階層をパースしている最中に、XMLとして処理できない、処理中にエラーが発生した 343 * などの場合は、バイナリコピーを行います。 344 * 345 * "Usage: JspSaxParser <inFile|inDir> <outFile|outDir> [<JspParserFilter1> ・・・ ]" 346 * 347 * @param args コマンド引数配列 348 * @throws Exception なんらかのエラーが発生した場合 349 */ 350 public static void main( final String[] args ) throws Exception { 351 if( args.length < 2 ) { 352 System.out.println( "Usage: JspSaxParser <inFile|inDir> <outFile|outDir> [<JspParserFilter1> ・・・ ]" ); 353 } 354 355 File in = new File( args[0] ); 356 File out = new File( args[1] ); 357 358 JspSaxParser jsp = new JspSaxParser(); 359 360 if( args.length >= 3 ) { 361 for( int i=2; i<args.length; i++ ) { 362 JspParserFilter filter = (JspParserFilter)Class.forName( args[i] ).newInstance(); 363 jsp.addFilter( filter ); 364 } 365 } 366 367 jsp.copyDirectry( in,out ); 368 } 369 370 /** 371 * 処理中のファイルオブジェクトを設定します。 372 * 373 * これは、エラー、ワーニング時のファイル名を出力するために利用しています。 374 * 375 * @og.rev 5.1.9.0 (2010/08/01) 廃止 376 * 377 * @param file 処理中のファイルオブジェクト 378 */ 379// public void setFile( final File file ) { 380// this.file = file; 381// } 382 383 // ********************************************************************************************** // 384 // ** ** // 385 // ** ここから下は、DefaultHandler2 の実装になります。 ** // 386 // ** ** // 387 // ********************************************************************************************** // 388 389 /** 390 * 文書の開始通知を受け取ります。 391 * 392 * インタフェース ContentHandler 内の startDocument 393 * 394 * @see org.xml.sax.helpers.DefaultHandler#startDocument() 395 * @see org.xml.sax.ContentHandler#startDocument() 396 */ 397 @Override 398 public void startDocument() { 399 stack = new Stack<OGNode>(); 400 ele = new OGDocument(); 401 ((OGDocument)ele).setFilename( filename ); 402 403 idMap = new HashMap<String,OGElement>(); // 5.1.9.0 (2010/08/01) 追加 404 405 attTab = ""; // tagBefore の一次TEMP 406 inCDATA = false; // CDATA エレメントの中かどうかの判定 407 inEntity = false; // Entity の中かどうかの判定 408 } 409 410 /** 411 * 要素の開始通知を受け取ります。 412 * 413 * インタフェース ContentHandler 内の startElement 414 * 415 * @param uri 名前空間 URI。要素が名前空間 URI を持たない場合、または名前空間処理が実行されない場合は null 416 * @param localName 前置修飾子を含まないローカル名。名前空間処理が行われない場合は空文字列 417 * @param qName 接頭辞を持つ修飾名。修飾名を使用できない場合は空文字列 418 * @param attributes 要素に付加された属性。属性が存在しない場合、空の Attributesオブジェクト 419 * 420 * @see org.xml.sax.helpers.DefaultHandler#startElement(String,String,String,Attributes) 421 * @see org.xml.sax.ContentHandler#startElement(String,String,String,Attributes) 422 */ 423 @Override 424 public void startElement( final String uri, final String localName, final String qName, final Attributes attributes ) { 425 426// OGElement newEle = new OGElement( qName,attTab,attributes,-1 ); 427 OGElement newEle = new OGElement( qName,attributes ); 428 String id = newEle.getId(); 429 if( id != null ) { idMap.put( id,newEle ); } // 5.1.9.0 (2010/08/01) idをMapにキャッシュ 430 431 ele.addNode( newEle ); 432 stack.push( ele ); 433 ele = newEle ; 434 } 435 436 /** 437 * 要素内の文字データの通知を受け取ります。 438 * 439 * エンティティー内かどうかを判断する、inEntity フラグが true の間は、 440 * 何も処理しません。 441 * 442 * インタフェース ContentHandler 内の characters 443 * 444 * @param cbuf 文字データ配列 445 * @param off 文字配列内の開始位置 446 * @param len 文字配列から使用される文字数 447 * 448 * @see org.xml.sax.helpers.DefaultHandler#characters(char[],int,int) 449 * @see org.xml.sax.ContentHandler#characters(char[],int,int) 450 */ 451 @Override 452 public void characters( final char[] cbuf, final int off, final int len ) { 453 if( inEntity ) { return ; } // < ⇒ < に変換されるので、エンティティ内では、なにも処理しない。 454 455 String text = toText( cbuf,off,len ); 456 if( inCDATA ) { 457 ele.addNode( text ); 458 return ; 459 } 460 461 OGNode node = new OGNode( text ); 462 ele.addNode( node ); 463 464 // '\r'(CR:復帰)+ '\n'(LF:改行)の可能性があるが、 '\n'(LF:改行)が、より後ろにあるので、これで判定。 465 int lastIdx = text.lastIndexOf( '\n' ); 466 if( lastIdx >= 0 ) { 467 attTab = text.substring( lastIdx+1 ); // 改行から、最後までの部分文字列 468 } 469 else { 470 attTab = text; // 改行がないので、すべて 471 } 472 } 473 474 /** 475 * CDATA セクションの開始を報告します。 476 * 477 * CDATA セクションのコンテンツは、正規の characters イベントを介して報告されます。 478 * このイベントは境界の報告だけに使用されます。 479 * 480 * インタフェース LexicalHandler 内の startCDATA 481 * 482 * @see org.xml.sax.ext.DefaultHandler2#startCDATA() 483 * @see org.xml.sax.ext.LexicalHandler#startCDATA() 484 */ 485 @Override 486 public void startCDATA() { 487 OGNode node = new OGNode(); 488 node.setNodeType( OGNodeType.Cdata ); 489 490 ele.addNode( node ); 491 stack.push( ele ); 492 ele = node ; 493 inCDATA = true; 494 } 495 496 /** 497 * CDATA セクションの終わりを報告します。 498 * 499 * インタフェース LexicalHandler 内の endCDATA 500 * 501 * @see org.xml.sax.ext.DefaultHandler2#endCDATA() 502 * @see org.xml.sax.ext.LexicalHandler#endCDATA() 503 */ 504 @Override 505 public void endCDATA() { 506 ele = stack.pop(); 507 inCDATA = false; 508 } 509 510 /** 511 * DTD 宣言がある場合、その開始を報告します。 512 * 513 * start/endDTD イベントは、ContentHandler の 514 * start/endDocument イベント内の最初の startElement イベントの前に出現します。 515 * 516 * インタフェース LexicalHandler 内の startDTD 517 * 518 * @param name 文書型名 519 * @param publicId 宣言された外部 DTD サブセットの公開識別子。 宣言されていない場合は null 520 * @param systemId 宣言された外部 DTD サブセットのシステム識別子。 宣言されていない場合は null。 521 * ドキュメントのベース URI に対しては解決されないことに 注意すること 522 * @see org.xml.sax.ext.DefaultHandler2#startDTD( String , String , String ) 523 * @see org.xml.sax.ext.LexicalHandler#startDTD( String , String , String ) 524 */ 525 @Override 526 public void startDTD( final String name, final String publicId, final String systemId ) { 527 StringBuilder buf = new StringBuilder(); 528 buf.append( "<!DOCTYPE " ).append( name ); 529 if( publicId != null ) { buf.append( " PUBLIC \"" ).append( publicId ).append( "\"" ); } 530 if( systemId != null ) { buf.append( "\"" ).append( systemId).append( "\"" ); } 531 532 OGNode node = new OGNode( buf.toString() ); 533 node.setNodeType( OGNodeType.DTD ); 534 ele.addNode( node ); 535 } 536 537 /** 538 * DTD 宣言の終わりを報告します。 539 * 540 * このメソッドは、DOCTYPE 宣言の終わりを報告するメソッドです。 541 * ここでは、何もしません。 542 * 543 * インタフェース LexicalHandler 内の endDTD 544 * 545 * @see org.xml.sax.ext.DefaultHandler2#endDTD() 546 * @see org.xml.sax.ext.LexicalHandler#endDTD() 547 */ 548 @Override 549 public void endDTD() { 550 // ここでは何もしません。 551 } 552 553 /** 554 * 内部および外部の XML エンティティーの一部の開始を報告します。 555 * 556 * インタフェース LexicalHandler の記述: 557 * 558 * ※ ここでは、&lt; などの文字列が、lt という名のエンティティーで 559 * 報告されるため、元の&付きの文字列に復元しています。 560 * エンティティー内かどうかを判断する、inEntity フラグを true にセットします。 561 * inEntity=true の間は、#characters(char[],int,int) は、何も処理しません。 562 * 563 * @param name エンティティーの名前 564 * @see org.xml.sax.ext.LexicalHandler#startEntity(String) 565 */ 566 @Override 567 public void startEntity( final String name ) { 568 String text = "&" + name + ";" ; 569 OGNode node = new OGNode( text ); 570 ele.addNode( node ); 571 inEntity = true; 572 } 573 574 /** 575 * エンティティーの終わりを報告します。 576 * 577 * インタフェース LexicalHandler の記述: 578 * 579 * ※ ここでは、inEntity=false を設定するだけです。 580 * 581 * @param name エンティティーの名前 582 * @see org.xml.sax.ext.LexicalHandler#endEntity(String) 583 */ 584 @Override 585 public void endEntity( final String name ) { 586 inEntity = false; 587 } 588 589 /** 590 * 要素コンテンツに含まれる無視できる空白文字の通知を受け取ります。 591 * 592 * インタフェース ContentHandler 内の ignorableWhitespace 593 * 594 * @param cbuf 文字データ配列(空白文字) 595 * @param off 文字配列内の開始位置 596 * @param len 文字配列から使用される文字数 597 * 598 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[],int,int) 599 */ 600 @Override 601 public void ignorableWhitespace( final char[] cbuf, final int off, final int len ) { 602 String text = toText( cbuf,off,len ); 603 OGNode node = new OGNode( text ); 604 ele.addNode( node ); 605 } 606 607 /** 608 * 文書内の任意の位置にある XML コメントを報告します。 609 * 610 * インタフェース LexicalHandler の記述: 611 * 612 * @param cbuf 文字データ配列(コメント文字) 613 * @param off 配列内の開始位置 614 * @param len 配列から読み取られる文字数 615 * 616 * @see org.xml.sax.helpers.DefaultHandler#characters(char[],int,int) 617 */ 618 @Override 619 public void comment( final char[] cbuf, final int off, final int len ) { 620 String text = toText( cbuf,off,len ); 621 OGNode node = new OGNode( text ); 622 node.setNodeType( OGNodeType.Comment ); 623 ele.addNode( node ); 624 } 625 626 /** 627 * 要素の終了通知を受け取ります。 628 * 629 * @param uri 名前空間 URI。要素が名前空間 URI を持たない場合、または名前空間処理が実行されない場合は null 630 * @param localName 前置修飾子を含まないローカル名。名前空間処理が行われない場合は空文字列 631 * @param qName 接頭辞を持つ修飾名。修飾名を使用できない場合は空文字列 632 * 633 * @see org.xml.sax.helpers.DefaultHandler#endElement(String,String,String) 634 * @see org.xml.sax.ContentHandler#endElement(String,String,String) 635 */ 636 @Override 637 public void endElement( final String uri, final String localName, final String qName ) { 638 ele = stack.pop(); 639 } 640 641 /** 642 * パーサー警告の通知を受け取ります。 643 * 644 * インタフェース org.xml.sax.ErrorHandler 内の warning 645 * 646 * ここでは、パーサー警告の内容を標準エラーに表示します。 647 * 648 * @param ex 例外として符号化された警告情報 649 * @see org.xml.sax.ErrorHandler#warning(SAXParseException) 650 */ 651 @Override 652 public void warning( final SAXParseException ex ) { 653 String errMsg = ex.getMessage() + ":" + ex.getPublicId() 654 + CR + "\t" + filename + " (" + ex.getLineNumber() + ")"; 655 System.err.println( "WARNING:" + errMsg ); 656 } 657 658 /** 659 * 文字配列から、文字列を作成します。(改行コードの統一) 660 * 661 * 処理的には、new String( cbuf,off,len ) ですが、XMLでリード 662 * されたファイルは、改行コードが、'\r'(CR:復帰)+ '\n'(LF:改行)ではなく、 663 * '\n'(LF:改行) のみに処理されます。(されるようです。規定不明) 664 * そこで、実行環境の改行コード(System.getProperty("line.separator"))と 665 * 置き換えます。 666 * 667 * @param cbuf 文字データ配列 668 * @param off 配列内の開始位置 669 * @param len 配列から読み取られる文字数 670 * 671 * @return 最終的な、Stringオブジェクト 672 */ 673 private String toText( final char[] cbuf, final int off, final int len ) { 674 String text = new String( cbuf,off,len ); 675 return text.replaceAll( "\n", CR ); 676 } 677 678 /** 679 * OGDocument を取得します。 680 * 681 * @return 最終的な、OGNodeオブジェクトに相当します 682 */ 683 private OGDocument getDocument() { 684 OGDocument doc = null; 685 if( ele != null && ele.getNodeType() == OGNodeType.Document ) { 686 doc = (OGDocument)ele; 687 doc.setIdMap( idMap ); 688 } 689 return doc; 690 } 691}