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