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