001 002package gudusoft.gsqlparser.pp.stmtformatter; 003 004import gudusoft.gsqlparser.EDbVendor; 005import gudusoft.gsqlparser.TCustomSqlStatement; 006import gudusoft.gsqlparser.ETokenType; 007import gudusoft.gsqlparser.TGSqlParser; 008import gudusoft.gsqlparser.TSourceToken; 009import gudusoft.gsqlparser.TSourceTokenList; 010import gudusoft.gsqlparser.TStatementList; 011import gudusoft.gsqlparser.pp.mediator.MediatorFactory; 012import gudusoft.gsqlparser.pp.mediator.type.NameCaseMediator; 013import gudusoft.gsqlparser.pp.processor.type.comm.CapitalisationProcessor; 014import gudusoft.gsqlparser.pp.output.OutputConfig; 015import gudusoft.gsqlparser.pp.output.OutputConfigFactory; 016import gudusoft.gsqlparser.pp.para.GFmtOpt; 017import gudusoft.gsqlparser.pp.para.styleenums.TCompactMode; 018import gudusoft.gsqlparser.pp.print.IPrinter; 019import gudusoft.gsqlparser.pp.print.PrinterFactory; 020import gudusoft.gsqlparser.pp.processor.ProcessorFactory; 021import gudusoft.gsqlparser.pp.stmtformatter.builder.AbstractStmtFormatterBuilder; 022import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.AllStmtsFormatterBuilder; 023import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.AlterTableStmtFormatterBuilder; 024import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.CommonStmtFormatterBuilder; 025import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.CompactModeStmtFormatterBuilder; 026import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.CreateTableStmtFormatterBuilder; 027import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.CreateViewStmtFormatterBuilder; 028import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.DeclareStmtFormatterBuilder; 029import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.DeleteStmtFormatterBuilder; 030import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.ExecuteStmtFormatterBuilder; 031import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.GoStmtFormatterBuilder; 032import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.IfStmtFormatterBuilder; 033import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.InsertStmtFormatterBuilder; 034import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.MergeStmtFormatterBuilder; 035import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.PlsqlBlockStmtFormatterBuilder; 036import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.PlsqlIfStmtFormatterBuilder; 037import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.PlsqlStmtFormatterBuilder; 038import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.ReturnStmtFormatterBuilder; 039import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.SelectStmtFormatterBuilder; 040import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.SetStmtFormatterBuilder; 041import gudusoft.gsqlparser.pp.stmtformatter.builder.comm.UpdateStmtFormatterBuilder; 042import gudusoft.gsqlparser.pp.stmtformatter.type.AbstractStmtFormatter; 043import gudusoft.gsqlparser.pp.stmtformatter.type.comm.AllStmtsFormatter; 044import gudusoft.gsqlparser.pp.stmtformatter.type.comm.AlterTableStmtFormatter; 045import gudusoft.gsqlparser.pp.stmtformatter.type.comm.CommonStmtFormatter; 046import gudusoft.gsqlparser.pp.stmtformatter.type.comm.CompactModeStmtFormatter; 047import gudusoft.gsqlparser.pp.stmtformatter.type.comm.CreateTableStmtFormatter; 048import gudusoft.gsqlparser.pp.stmtformatter.type.comm.CreateViewStmtFormatter; 049import gudusoft.gsqlparser.pp.stmtformatter.type.comm.DeclareStmtFormatter; 050import gudusoft.gsqlparser.pp.stmtformatter.type.comm.DeleteStmtFormatter; 051import gudusoft.gsqlparser.pp.stmtformatter.type.comm.ExecuteStmtFormatter; 052import gudusoft.gsqlparser.pp.stmtformatter.type.comm.GoStmtFormatter; 053import gudusoft.gsqlparser.pp.stmtformatter.type.comm.IfStmtFormatter; 054import gudusoft.gsqlparser.pp.stmtformatter.type.comm.InsertStmtFormatter; 055import gudusoft.gsqlparser.pp.stmtformatter.type.comm.MergeStmtFormatter; 056import gudusoft.gsqlparser.pp.stmtformatter.type.comm.PlsqlBlockStmtFormatter; 057import gudusoft.gsqlparser.pp.stmtformatter.type.comm.PlsqlIfStmtFormatter; 058import gudusoft.gsqlparser.pp.stmtformatter.type.comm.PlsqlStmtFormatter; 059import gudusoft.gsqlparser.pp.stmtformatter.type.comm.ReturnStmtFormatter; 060import gudusoft.gsqlparser.pp.stmtformatter.type.comm.SelectStmtFormatter; 061import gudusoft.gsqlparser.pp.stmtformatter.type.comm.SetStmtFormatter; 062import gudusoft.gsqlparser.pp.stmtformatter.type.comm.UpdateStmtFormatter; 063import gudusoft.gsqlparser.pp.utils.SourceTokenOperator; 064import gudusoft.gsqlparser.stmt.TAlterTableStatement; 065import gudusoft.gsqlparser.stmt.TCommonBlock; 066import gudusoft.gsqlparser.stmt.TCreateTableSqlStatement; 067import gudusoft.gsqlparser.stmt.TCreateViewSqlStatement; 068import gudusoft.gsqlparser.stmt.TDeleteSqlStatement; 069import gudusoft.gsqlparser.stmt.TIfStmt; 070import gudusoft.gsqlparser.stmt.TInsertSqlStatement; 071import gudusoft.gsqlparser.stmt.TMergeSqlStatement; 072import gudusoft.gsqlparser.stmt.TSelectSqlStatement; 073import gudusoft.gsqlparser.stmt.TStoredProcedureSqlStatement; 074import gudusoft.gsqlparser.stmt.TUpdateSqlStatement; 075import gudusoft.gsqlparser.stmt.mssql.TMssqlBlock; 076import gudusoft.gsqlparser.stmt.mssql.TMssqlDeclare; 077import gudusoft.gsqlparser.stmt.mssql.TMssqlExecute; 078import gudusoft.gsqlparser.stmt.mssql.TMssqlGo; 079import gudusoft.gsqlparser.stmt.mssql.TMssqlIfElse; 080import gudusoft.gsqlparser.stmt.mssql.TMssqlReturn; 081import gudusoft.gsqlparser.stmt.mssql.TMssqlSet; 082import gudusoft.gsqlparser.pp2.Pp2FormatOptions; 083import gudusoft.gsqlparser.pp2.Pp2FormatResult; 084import gudusoft.gsqlparser.pp2.engine.Pp2Engine; 085 086import java.io.ByteArrayOutputStream; 087import java.io.OutputStream; 088import java.util.ArrayList; 089import java.util.List; 090import java.util.Map; 091import java.util.concurrent.ConcurrentHashMap; 092 093public class FormatterFactory 094{ 095 096 /** 097 * all formatters 098 */ 099 private volatile static Map<String, AbstractStmtFormatter> formatter = new ConcurrentHashMap<String, AbstractStmtFormatter>( ); 100 101 private static Object object = new Object( ); 102 103 /** Set by {@link #setOutputConfig} only; see {@link #outputConfigFor}. */ 104 private static volatile OutputConfig outputConfig; 105 106 /** 107 * The output config a call prints with: the one set with {@link #setOutputConfig}, else one made from 108 * the call's own option and dialect. The call's own config is never kept for later calls: kept, the 109 * first HTML call made every later call in the JVM print HTML, whatever its option said. 110 */ 111 static OutputConfig outputConfigFor( GFmtOpt option, EDbVendor dbVendor ) 112 { 113 OutputConfig set = outputConfig; 114 return set != null ? set : OutputConfigFactory.getOutputConfig( option, dbVendor ); 115 } 116 117 /** 118 * create select statment formatter 119 * 120 * @param opt 121 * options 122 * @return instance 123 */ 124 public static SelectStmtFormatter createSelectStmtFormatter( GFmtOpt opt ) 125 { 126 return createFormatter( opt, 127 SelectStmtFormatter.class, 128 new IFormatterBuilderCreator<SelectStmtFormatter>( ) { 129 130 public AbstractStmtFormatterBuilder<SelectStmtFormatter> create( ) 131 { 132 return new SelectStmtFormatterBuilder( ); 133 } 134 } ); 135 } 136 137 public static InsertStmtFormatter createInsertStmtFormatter( GFmtOpt opt ) 138 { 139 return createFormatter( opt, 140 InsertStmtFormatter.class, 141 new IFormatterBuilderCreator<InsertStmtFormatter>( ) { 142 143 public AbstractStmtFormatterBuilder<InsertStmtFormatter> create( ) 144 { 145 return new InsertStmtFormatterBuilder( ); 146 } 147 } ); 148 } 149 150 public static DeleteStmtFormatter createDeleteStmtFormatter( GFmtOpt opt ) 151 { 152 return createFormatter( opt, 153 DeleteStmtFormatter.class, 154 new IFormatterBuilderCreator<DeleteStmtFormatter>( ) { 155 156 public AbstractStmtFormatterBuilder<DeleteStmtFormatter> create( ) 157 { 158 return new DeleteStmtFormatterBuilder( ); 159 } 160 } ); 161 } 162 163 public static UpdateStmtFormatter createUpdateStmtFormatter( GFmtOpt opt ) 164 { 165 return createFormatter( opt, 166 UpdateStmtFormatter.class, 167 new IFormatterBuilderCreator<UpdateStmtFormatter>( ) { 168 169 public AbstractStmtFormatterBuilder<UpdateStmtFormatter> create( ) 170 { 171 return new UpdateStmtFormatterBuilder( ); 172 } 173 } ); 174 } 175 176 public static CreateTableStmtFormatter createCreateTableStmtFormatter( 177 GFmtOpt opt ) 178 { 179 return createFormatter( opt, 180 CreateTableStmtFormatter.class, 181 new IFormatterBuilderCreator<CreateTableStmtFormatter>( ) { 182 183 public AbstractStmtFormatterBuilder<CreateTableStmtFormatter> create( ) 184 { 185 return new CreateTableStmtFormatterBuilder( ); 186 } 187 } ); 188 } 189 190 public static DeclareStmtFormatter createDeclareStmtFormatter( GFmtOpt opt ) 191 { 192 return createFormatter( opt, 193 DeclareStmtFormatter.class, 194 new IFormatterBuilderCreator<DeclareStmtFormatter>( ) { 195 196 public AbstractStmtFormatterBuilder<DeclareStmtFormatter> create( ) 197 { 198 return new DeclareStmtFormatterBuilder( ); 199 } 200 } ); 201 } 202 203 public static ExecuteStmtFormatter createExecuteStmtFormatter( GFmtOpt opt ) 204 { 205 return createFormatter( opt, 206 ExecuteStmtFormatter.class, 207 new IFormatterBuilderCreator<ExecuteStmtFormatter>( ) { 208 209 public AbstractStmtFormatterBuilder<ExecuteStmtFormatter> create( ) 210 { 211 return new ExecuteStmtFormatterBuilder( ); 212 } 213 } ); 214 } 215 216 public static SetStmtFormatter createSetStmtFormatter( GFmtOpt opt ) 217 { 218 return createFormatter( opt, 219 SetStmtFormatter.class, 220 new IFormatterBuilderCreator<SetStmtFormatter>( ) { 221 222 public AbstractStmtFormatterBuilder<SetStmtFormatter> create( ) 223 { 224 return new SetStmtFormatterBuilder( ); 225 } 226 } ); 227 } 228 229 public static IfStmtFormatter createIfStmtFormatter( GFmtOpt opt ) 230 { 231 return createFormatter( opt, 232 IfStmtFormatter.class, 233 new IFormatterBuilderCreator<IfStmtFormatter>( ) { 234 235 public AbstractStmtFormatterBuilder<IfStmtFormatter> create( ) 236 { 237 return new IfStmtFormatterBuilder( ); 238 } 239 } ); 240 } 241 242 public static PlsqlIfStmtFormatter createPlsqlIfStmtFormatter( GFmtOpt opt ) 243 { 244 return createFormatter( opt, 245 PlsqlIfStmtFormatter.class, 246 new IFormatterBuilderCreator<PlsqlIfStmtFormatter>( ) { 247 248 public AbstractStmtFormatterBuilder<PlsqlIfStmtFormatter> create( ) 249 { 250 return new PlsqlIfStmtFormatterBuilder( ); 251 } 252 } ); 253 } 254 255 public static PlsqlStmtFormatter createPlsqlStmtFormatter( GFmtOpt opt ) 256 { 257 return createFormatter( opt, 258 PlsqlStmtFormatter.class, 259 new IFormatterBuilderCreator<PlsqlStmtFormatter>( ) { 260 261 public AbstractStmtFormatterBuilder<PlsqlStmtFormatter> create( ) 262 { 263 return new PlsqlStmtFormatterBuilder( ); 264 } 265 } ); 266 } 267 268 public static GoStmtFormatter createGoStmtFormatter( GFmtOpt opt ) 269 { 270 return createFormatter( opt, 271 GoStmtFormatter.class, 272 new IFormatterBuilderCreator<GoStmtFormatter>( ) { 273 274 public AbstractStmtFormatterBuilder<GoStmtFormatter> create( ) 275 { 276 return new GoStmtFormatterBuilder( ); 277 } 278 } ); 279 } 280 281 public static CompactModeStmtFormatter createCompactModeStmtFormatter( 282 GFmtOpt opt ) 283 { 284 return createFormatter( opt, 285 CompactModeStmtFormatter.class, 286 new IFormatterBuilderCreator<CompactModeStmtFormatter>( ) { 287 288 public AbstractStmtFormatterBuilder<CompactModeStmtFormatter> create( ) 289 { 290 return new CompactModeStmtFormatterBuilder( ); 291 } 292 } ); 293 } 294 295 public static ReturnStmtFormatter createReturnStmtFormatter( GFmtOpt opt ) 296 { 297 return createFormatter( opt, 298 ReturnStmtFormatter.class, 299 new IFormatterBuilderCreator<ReturnStmtFormatter>( ) { 300 301 public AbstractStmtFormatterBuilder<ReturnStmtFormatter> create( ) 302 { 303 return new ReturnStmtFormatterBuilder( ); 304 } 305 } ); 306 } 307 308 public static CreateViewStmtFormatter createCreateViewStmtFormatter( 309 GFmtOpt opt ) 310 { 311 return createFormatter( opt, 312 CreateViewStmtFormatter.class, 313 new IFormatterBuilderCreator<CreateViewStmtFormatter>( ) { 314 315 public AbstractStmtFormatterBuilder<CreateViewStmtFormatter> create( ) 316 { 317 return new CreateViewStmtFormatterBuilder( ); 318 } 319 } ); 320 } 321 322 public static gudusoft.gsqlparser.pp.stmtformatter.type.comm.CreateMaterializedViewStmtFormatter createCreateMaterializedViewStmtFormatter( 323 GFmtOpt opt ) 324 { 325 return createFormatter( opt, 326 gudusoft.gsqlparser.pp.stmtformatter.type.comm.CreateMaterializedViewStmtFormatter.class, 327 new IFormatterBuilderCreator<gudusoft.gsqlparser.pp.stmtformatter.type.comm.CreateMaterializedViewStmtFormatter>( ) { 328 329 public AbstractStmtFormatterBuilder<gudusoft.gsqlparser.pp.stmtformatter.type.comm.CreateMaterializedViewStmtFormatter> create( ) 330 { 331 return new gudusoft.gsqlparser.pp.stmtformatter.builder.comm.CreateMaterializedViewStmtFormatterBuilder( ); 332 } 333 } ); 334 } 335 336 public static AlterTableStmtFormatter createAlterTableStatement( GFmtOpt opt ) 337 { 338 return createFormatter( opt, 339 AlterTableStmtFormatter.class, 340 new IFormatterBuilderCreator<AlterTableStmtFormatter>( ) { 341 342 public AbstractStmtFormatterBuilder<AlterTableStmtFormatter> create( ) 343 { 344 return new AlterTableStmtFormatterBuilder( ); 345 } 346 } ); 347 } 348 349 /** 350 * the callback interface to achieve the formatter builder 351 * 352 * @author zhoujun 353 * 354 */ 355 public interface IFormatterBuilderCreator<E extends AbstractStmtFormatter> 356 { 357 358 AbstractStmtFormatterBuilder<E> create( ); 359 } 360 361 /** 362 * create the formatter 363 * 364 * @param <E> 365 * @param opt 366 * @param clazz 367 * @param creater 368 * @return 369 */ 370 public static <E extends AbstractStmtFormatter> E createFormatter( 371 GFmtOpt opt, Class<E> clazz, IFormatterBuilderCreator<E> creater ) 372 { 373 String id = opt.sessionId + clazz.getName( ); 374 if ( !formatter.containsKey( id ) ) 375 { 376 synchronized ( FormatterFactory.class ) 377 { 378 if ( !formatter.containsKey( id ) ) 379 { 380 AbstractStmtFormatterBuilder builder = creater.create( ); 381 builder.setOption( opt ); 382 formatter.put( id, builder.build( ) ); 383 } 384 } 385 } 386 return (E) formatter.get( id ); 387 } 388 389 public static void processStatement( GFmtOpt option, TCustomSqlStatement sql ) 390 { 391 if ( option.compactMode == TCompactMode.Cpmugly ) 392 { 393 FormatterFactory.createCompactModeStmtFormatter( option ) 394 .format( sql ); 395 return; 396 } 397 398 if ( isNotNeedFormat( sql ) ) 399 { 400 return; 401 } 402 if ( sql instanceof TSelectSqlStatement ) 403 { 404 FormatterFactory.createSelectStmtFormatter( option ) 405 .format( (TSelectSqlStatement) sql ); 406 } 407 else if ( sql instanceof TInsertSqlStatement ) 408 { 409 FormatterFactory.createInsertStmtFormatter( option ) 410 .format( (TInsertSqlStatement) sql ); 411 } 412 else if ( sql instanceof TDeleteSqlStatement ) 413 { 414 FormatterFactory.createDeleteStmtFormatter( option ) 415 .format( (TDeleteSqlStatement) sql ); 416 } 417 else if ( sql instanceof TUpdateSqlStatement ) 418 { 419 FormatterFactory.createUpdateStmtFormatter( option ) 420 .format( (TUpdateSqlStatement) sql ); 421 } 422 else if ( sql instanceof TCreateTableSqlStatement ) 423 { 424 FormatterFactory.createCreateTableStmtFormatter( option ) 425 .format( (TCreateTableSqlStatement) sql ); 426 } 427 else if ( sql instanceof TMssqlDeclare ) 428 { 429 FormatterFactory.createDeclareStmtFormatter( option ) 430 .format( (TMssqlDeclare) sql ); 431 } 432 else if ( sql instanceof TMssqlExecute ) 433 { 434 FormatterFactory.createExecuteStmtFormatter( option ) 435 .format( (TMssqlExecute) sql ); 436 } 437 else if ( sql instanceof TStoredProcedureSqlStatement ) 438 { 439 FormatterFactory.createPlsqlStmtFormatter( option ) 440 .format( (TStoredProcedureSqlStatement) sql ); 441 } 442 else if ( sql instanceof TMssqlSet ) 443 { 444 FormatterFactory.createSetStmtFormatter( option ) 445 .format( (TMssqlSet) sql ); 446 } 447 else if ( sql instanceof TMssqlIfElse ) 448 { 449 FormatterFactory.createIfStmtFormatter( option ) 450 .format( (TMssqlIfElse) sql ); 451 } 452 else if ( sql instanceof TMssqlGo ) 453 { 454 FormatterFactory.createGoStmtFormatter( option ) 455 .format( (TMssqlGo) sql ); 456 } 457 else if ( sql instanceof TMssqlReturn ) 458 { 459 FormatterFactory.createReturnStmtFormatter( option ) 460 .format( (TMssqlReturn) sql ); 461 } 462 else if ( sql instanceof TCreateViewSqlStatement ) 463 { 464 FormatterFactory.createCreateViewStmtFormatter( option ) 465 .format( (TCreateViewSqlStatement) sql ); 466 } 467 else if ( sql instanceof gudusoft.gsqlparser.stmt.TCreateMaterializedSqlStatement ) 468 { 469 FormatterFactory.createCreateMaterializedViewStmtFormatter( option ) 470 .format( (gudusoft.gsqlparser.stmt.TCreateMaterializedSqlStatement) sql ); 471 } 472 else if ( sql instanceof TAlterTableStatement ) 473 { 474 FormatterFactory.createAlterTableStatement( option ) 475 .format( (TAlterTableStatement) sql ); 476 } 477 else if ( sql instanceof TMergeSqlStatement ) 478 { 479 FormatterFactory.createMergeSqlStatement( option ) 480 .format( (TMergeSqlStatement) sql ); 481 } 482 else if ( sql instanceof TIfStmt ) 483 { 484 FormatterFactory.createPlsqlIfStmtFormatter( option ) 485 .format( (TIfStmt) sql ); 486 } 487 else 488 { 489 FormatterFactory.createCommonStatement( option ).format( sql ); 490 } 491 } 492 493 private static AbstractStmtFormatter<TCommonBlock> createPlsqlBlockStmtFormatter( 494 GFmtOpt opt ) 495 { 496 return createFormatter( opt, 497 PlsqlBlockStmtFormatter.class, 498 new IFormatterBuilderCreator<PlsqlBlockStmtFormatter>( ) { 499 500 public AbstractStmtFormatterBuilder<PlsqlBlockStmtFormatter> create( ) 501 { 502 return new PlsqlBlockStmtFormatterBuilder( ); 503 } 504 } ); 505 } 506 507 private static MergeStmtFormatter createMergeSqlStatement( GFmtOpt opt ) 508 { 509 return createFormatter( opt, 510 MergeStmtFormatter.class, 511 new IFormatterBuilderCreator<MergeStmtFormatter>( ) { 512 513 public AbstractStmtFormatterBuilder<MergeStmtFormatter> create( ) 514 { 515 return new MergeStmtFormatterBuilder( ); 516 } 517 } ); 518 } 519 520 private static CommonStmtFormatter createCommonStatement( GFmtOpt opt ) 521 { 522 return createFormatter( opt, 523 CommonStmtFormatter.class, 524 new IFormatterBuilderCreator<CommonStmtFormatter>( ) { 525 526 public AbstractStmtFormatterBuilder<CommonStmtFormatter> create( ) 527 { 528 return new CommonStmtFormatterBuilder( ); 529 } 530 } ); 531 } 532 533 public static boolean isNotNeedFormat( TCustomSqlStatement sql ) 534 { 535 if ( sql == null ) 536 { 537 // the null statement needn't formatting. 538 return true; 539 } 540 if ( sql.getStartToken( ) != null ) 541 { 542 TSourceToken t = sql.getStartToken( ); 543 return isNotNeedFormat( t ); 544 } 545 return false; 546 } 547 548 public static boolean isNotNeedFormat( TSourceToken t ) 549 { 550 if ( t == null ) 551 { 552 return false; 553 } 554 TSourceTokenList bts = t.getTokensBefore( ); 555 if ( bts.size( ) > 0 ) 556 { 557 for ( int i = 0; i < bts.size( ); i++ ) 558 { 559 if ( SourceTokenOperator.createNoFormatFlagToken( ) 560 .equals( bts.get( i ) ) ) 561 { 562 return true; 563 } 564 } 565 } 566 return false; 567 } 568 569 public static void processBlockStmt( GFmtOpt opt, TMssqlBlock stmt, 570 TSourceToken ifToken ) 571 { 572 TSourceToken startToken = stmt.getStartToken( ); 573 TSourceToken endToken = stmt.getEndToken( ); 574 if ( startToken == null || endToken == null ) 575 { 576 return; 577 } 578 int curBeginIndent = SourceTokenOperator.curColumnNumberVT( ifToken ); 579 if ( opt.beStyleBlockLeftBEOnNewline ) 580 { 581 curBeginIndent += opt.beStyleBlockLeftBEIndentSize; 582 // process the begin token 583 SourceTokenOperator.addBefore( opt, 584 startToken, 585 SourceTokenOperator.createReturnSourceToken( ) ); 586 SourceTokenOperator.addBefore( opt, 587 startToken, 588 SourceTokenOperator.createWhitespaceSourceToken( curBeginIndent ) ); 589 } 590 // process the end token 591 int curEndIndent = SourceTokenOperator.curColumnNumberVT( ifToken ) 592 + ( opt.beStyleBlockLeftBEOnNewline ? opt.beStyleBlockLeftBEIndentSize 593 : opt.beStyleBlockRightBEIndentSize ); 594 SourceTokenOperator.addBefore( opt, 595 endToken, 596 SourceTokenOperator.createReturnSourceToken( ) ); 597 SourceTokenOperator.addBefore( opt, 598 endToken, 599 SourceTokenOperator.createWhitespaceSourceToken( curEndIndent ) ); 600 601 TStatementList list = stmt.getBodyStatements( ); 602 603 // first, process the indent for the first statement 604 if ( list != null && list.size( ) > 0 ) 605 { 606 int curBlockIndent = curEndIndent + opt.beStyleBlockIndentSize; 607 TCustomSqlStatement bstmt = list.get( 0 ); 608 TSourceToken bstartToken = bstmt.getStartToken( ); 609 if ( bstartToken != null ) 610 { 611 SourceTokenOperator.addBefore( opt, 612 bstartToken, 613 SourceTokenOperator.createReturnSourceToken( ) ); 614 SourceTokenOperator.addBefore( opt, 615 bstartToken, 616 SourceTokenOperator.createWhitespaceSourceToken( curBlockIndent ) ); 617 } 618 } 619 // second, process all the empty lines between the statements 620 AllStmtsFormatter.processEmptyLine( list, opt, true ); 621 622 // third, format the statements in the begin-end block 623 if ( list != null && list.size( ) > 0 ) 624 { 625 // int curBlockIndent = curEndIndent + opt.beStyleBlockIndentSize; 626 for ( int i = 0; i < list.size( ); i++ ) 627 { 628 TCustomSqlStatement bstmt = list.get( i ); 629 // TSourceToken bstartToken = bstmt.getStartToken(); 630 // if (bstartToken != null) { 631 // SourceTokenOperator.addBefore(opt, bstartToken, 632 // SourceTokenOperator.createReturnSourceToken()); 633 // if (i != 0 && opt.insertBlankLineInBatchSqls) { 634 // TCustomSqlStatement preBstmt = list.get(i - 1); 635 // TSourceToken preBEndToken = preBstmt.getEndToken(); 636 // if (preBEndToken != null) { 637 // SourceTokenOperator.addAfter(opt, preBstmt.getEndToken(), 638 // SourceTokenOperator.createReturnSourceToken()); 639 // } 640 // } 641 // SourceTokenOperator.addBefore(opt, bstartToken, 642 // SourceTokenOperator.createWhitespaceSourceToken(curBlockIndent)); 643 // 644 // } 645 FormatterFactory.processStatement( opt, bstmt ); 646 647 } 648 } 649 650 } 651 652 /** 653 * A statement that starts on the line of the previous statement's 654 * terminator ({@code SELECT x FROM t; SELECT y ...}) gets a line break 655 * first. Every column the processors compute for it is measured from the 656 * last line break, so without one the whole statement was laid out as if 657 * it started at the column after the terminator. 658 */ 659 private static void startOnNewLine( GFmtOpt opt, TCustomSqlStatement prev, TCustomSqlStatement sql ) 660 { 661 TSourceToken start = sql.getStartToken( ); 662 TSourceToken prevEnd = prev.getEndToken( ); 663 if ( start == null || prevEnd == null || start.container == null 664 || start.container != prevEnd.container || prevEnd.posinlist >= start.posinlist 665 || start.lineNo <= 0 || prevEnd.lineNo <= 0 ) 666 { 667 return; 668 } 669 // the original line numbers: the whitespace pre-pass has already rewritten the tokens between 670 if ( start.lineNo != prevEnd.lineNo ) 671 { 672 return; 673 } 674 TSourceTokenList before = start.getTokensBefore( ); 675 for ( int i = 0; before != null && i < before.size( ); i++ ) 676 { 677 if ( before.get( i ) == SourceTokenOperator.createNoFormatFlagToken( ) ) 678 { 679 return; // a no-format zone is reproduced verbatim 680 } 681 } 682 SourceTokenOperator.removeWhitespaceAndReturnFromEnd( opt, start.container, start.posinlist ); 683 SourceTokenOperator.addBefore( opt, start, SourceTokenOperator.createStatementBreakToken( ) ); 684 } 685 686 public static String pp( TGSqlParser parser, GFmtOpt option ) 687 { 688 try 689 { 690 return ppSession( parser, option ); 691 } 692 finally 693 { 694 clearAllObject( option.sessionId ); 695 } 696 } 697 698 private static String ppSession( TGSqlParser parser, GFmtOpt option ) 699 { 700 // Table, column and alias names with a case of their own, decided on 701 // the parse the caller made (a comment-removal re-parse runs with 702 // default resolver settings). 703 NameCaseMediator names = NameCaseMediator.isUsedBy( option ) 704 ? MediatorFactory.getMediator( NameCaseMediator.class, option.sessionId ) 705 : null; 706 if ( names != null ) 707 { 708 names.load( parser, option ); 709 } 710 if (option.removeComment) { 711 parser = SqlFormatter.preHandleRemoveComment(parser); 712 if ( names != null ) 713 { 714 names.reload( parser, option ); 715 } 716 } 717 OutputStream o = new ByteArrayOutputStream( ); 718 IPrinter printer = PrinterFactory.createTextPrinter( o ); 719 720 FormatterFactory.createAllStmtsFormatter( option ) 721 .beforeFormat( parser.sqlstatements ); 722 723 for ( int i = 0; i < parser.sqlstatements.size( ); i++ ) 724 { 725 TCustomSqlStatement sql = parser.sqlstatements.get( i ); 726 if ( i > 0 ) 727 { 728 startOnNewLine( option, parser.sqlstatements.get( i - 1 ), sql ); 729 } 730 processStatement( option, sql ); 731 } 732 CapitalisationProcessor.caseKeywordsBetweenStatements( parser.sqlstatements, option ); 733 if ( parser.sqlstatements != null && parser.sqlstatements.size( ) > 0 ) 734 { 735 FormatterFactory.createAllStmtsFormatter( option ) 736 .doFormat( parser.sqlstatements ); 737 FormatterFactory.createAllStmtsFormatter( option ) 738 .afterFormat( parser.sqlstatements ); 739 740 printer.setOutputConfig( outputConfigFor( option, parser.getDbVendor( ) ) ); 741 printer.print( parser.sqlstatements.get( 0 ).getStartToken( ).container ); 742 } 743 744 return o.toString( ); 745 746 } 747 748 /** 749 * Fault-tolerant formatter entry point (pp2). 750 * 751 * <h4>On parseable SQL, pp2 <em>is</em> pp</h4> 752 * 753 * <p>This is the first thing to know about pp2: it is not a second, 754 * competing layout engine, and it has no layout style of its own for SQL 755 * that parses. When the whole of {@code parser.sqltext} parses, pp2 calls 756 * {@link #pp(TGSqlParser, GFmtOpt)} on the whole document with these same 757 * options and returns <b>pp's own output, byte for byte</b>. Changing a 758 * working {@code pp()} call to {@code pp2()} therefore does not change how 759 * already-formattable SQL comes out; it only changes what happens to SQL 760 * that {@code pp()} could not format at all. 761 * 762 * <p>Precisely, each call walks this ladder and stops at the first rung 763 * that succeeds: 764 * 765 * <ol> 766 * <li><b>Whole-document pp.</b> pp2 parses the whole text itself. If that 767 * parse succeeds, it runs {@code FormatterFactory.pp} over the whole 768 * document and checks the output with a token-equivalence guard (same 769 * solid and comment tokens, same order, ignoring whitespace and 770 * keyword case). If the guard passes, that text <i>is</i> the answer: 771 * status {@link gudusoft.gsqlparser.pp2.FormatStatus#OK OK}, every 772 * region reported as 773 * {@link gudusoft.gsqlparser.pp2.RendererId#GUARDED_AST GUARDED_AST}. 774 * This rung is governed by 775 * {@link Pp2FormatOptions#wholeDocumentFastPath} (default 776 * {@code true}); set it to {@code false} to force the regional 777 * pipeline below.</li> 778 * <li><b>Per-region pp.</b> Otherwise — the document did not parse, or pp 779 * threw, or pp's output failed the guard — pp2 re-tokenizes the text, 780 * splits it into statement regions, and parses each region on its own. 781 * Every region that parses is <i>still</i> formatted by 782 * {@code FormatterFactory.pp}, behind the same guard. One broken 783 * statement therefore costs pp-quality layout for that statement only, 784 * not for the whole script.</li> 785 * <li><b>pp2's own renderers.</b> A region that does not parse, or whose 786 * pp output failed the guard, is laid out by pp2's lexical-island 787 * renderer, and failing that by its conservative token renderer. 788 * Neither consults an AST, so neither can fail on malformed input; the 789 * trade-off is plainer layout. Such a region is reported as 790 * {@link gudusoft.gsqlparser.pp2.RendererId#LEXICAL_ISLAND LEXICAL_ISLAND} 791 * or 792 * {@link gudusoft.gsqlparser.pp2.RendererId#CONSERVATIVE CONSERVATIVE}, 793 * and lifts the overall status to 794 * {@link gudusoft.gsqlparser.pp2.FormatStatus#OK_WITH_RECOVERY OK_WITH_RECOVERY}.</li> 795 * </ol> 796 * 797 * <p>{@link Pp2FormatResult#getRegions()} tells you which rung ran where, 798 * and {@link Pp2FormatResult#getDiagnostics()} carries the per-region 799 * parser errors. A result whose regions are all {@code GUARDED_AST} is pp 800 * output; anything else marks the part of the input pp could not reach. 801 * 802 * <h4>What pp2 gives you over {@link #pp(TGSqlParser, GFmtOpt)}</h4> 803 * 804 * <ul> 805 * <li>{@code pp()} requires the caller to have run {@code parser.parse()} 806 * and got {@code 0} back. pp2 does not: it parses internally and 807 * ignores whatever parse state the caller left behind, so a 808 * {@code TGSqlParser} carrying only {@code sqltext} and a vendor is 809 * enough.</li> 810 * <li>{@code pp()} on malformed SQL yields nothing usable. pp2 always 811 * returns text and never throws for non-null inputs.</li> 812 * <li>pp2 preserves every solid input token and every comment, in input 813 * order, on every path — including the fallback renderers. Nothing is 814 * dropped, reordered, or rewritten.</li> 815 * </ul> 816 * 817 * <p>This is the single additive pp2 bridge (plan §4.1/S17). It does not 818 * change the behavior of {@link #pp(TGSqlParser, GFmtOpt)} or any other 819 * existing method. The heavy lifting lives in 820 * {@code gudusoft.gsqlparser.pp2.engine.Pp2Engine}. 821 * 822 * @param parser carries the SQL text ({@code parser.sqltext}) and dialect 823 * ({@code parser.getDbVendor()}); need not be parsed first 824 * @param opts pp2 formatting options; must not be null 825 * @return a structured {@link Pp2FormatResult} with per-region renderer 826 * choice, status, and diagnostics; never null 827 * @throws NullPointerException if {@code parser} or {@code opts} is null 828 */ 829 public static Pp2FormatResult pp2( TGSqlParser parser, Pp2FormatOptions opts ) 830 { 831 if ( parser == null ) throw new NullPointerException( "parser" ); 832 if ( opts == null ) throw new NullPointerException( "opts" ); 833 String sql = parser.sqltext == null ? "" : parser.sqltext; 834 return new Pp2Engine( ).format( sql, parser.getDbVendor( ), opts ); 835 } 836 837 /** 838 * Convenience overload mirroring the {@link #pp(TGSqlParser, GFmtOpt)} 839 * signature: returns the formatted text directly, so it is a drop-in 840 * replacement for a {@code pp()} call. 841 * 842 * <p>The caller's {@link GFmtOpt} is copied into a {@link Pp2FormatOptions} 843 * via {@link Pp2FormatOptions#from(GFmtOpt)} so casing / comma / indent 844 * options carry over. Byte-parity with {@code pp()} on parseable input is 845 * not an approximation of pp's style — on that input pp2 <b>calls 846 * {@code pp()} and returns its output unchanged</b>; see 847 * {@link #pp2(TGSqlParser, Pp2FormatOptions)} for the full ladder. 848 * 849 * <pre>{@code 850 * TGSqlParser parser = new TGSqlParser(EDbVendor.dbvoracle); 851 * parser.sqltext = sql; // no parse() call needed 852 * String formatted = FormatterFactory.pp2(parser, GFmtOptFactory.newInstance()); 853 * }</pre> 854 * 855 * <p>This overload discards the per-region detail. Call 856 * {@link #pp2(TGSqlParser, Pp2FormatOptions)} instead when you need to know 857 * whether any part of the input fell back to pp2's own renderers (i.e. was 858 * not formatted by pp). 859 * 860 * @param parser carries the SQL text and dialect; need not be parsed first 861 * @param opt existing formatter options; must not be null 862 * @return the formatted SQL text; never null 863 * @throws NullPointerException if {@code parser} or {@code opt} is null 864 */ 865 public static String pp2( TGSqlParser parser, GFmtOpt opt ) 866 { 867 if ( parser == null ) throw new NullPointerException( "parser" ); 868 if ( opt == null ) throw new NullPointerException( "opt" ); 869 return pp2( parser, Pp2FormatOptions.from( opt ) ).getText( ); 870 } 871 872 /** 873 * The output config set with {@link #setOutputConfig}, or {@code null}. Formatting never sets it. 874 * @return the output config every call prints with, or {@code null} when each call follows its own 875 * {@code GFmtOpt.outputFmt} 876 */ 877 public static OutputConfig getOutputConfig( ) 878 { 879 return outputConfig; 880 } 881 882 /** 883 * Makes every later {@link #pp(TGSqlParser, GFmtOpt)} and {@link SqlFormatter#format} call, in every 884 * thread, print with this config whatever its option's {@code outputFmt} says, until it is set back to 885 * {@code null} (the default), after which each call prints as its own option says again. Customise the 886 * config before setting it, not while calls may be printing with it. 887 * @param outputConfig the config to print every call with, for example an 888 * {@code HtmlOutputConfig} with custom colors; {@code null} to let each call follow its option 889 */ 890 public static void setOutputConfig( OutputConfig outputConfig ) 891 { 892 FormatterFactory.outputConfig = outputConfig; 893 } 894 895 public static AllStmtsFormatter createAllStmtsFormatter( GFmtOpt option ) 896 { 897 return AllStmtsFormatterBuilder.create( option ); 898 } 899 900 public static void clearAllObject( String sessionId ) 901 { 902 FormatterFactory.clear( sessionId ); 903 ProcessorFactory.clear( sessionId ); 904 MediatorFactory.clear( sessionId ); 905 } 906 907 public static void clear( String sessionId ) 908 { 909 synchronized ( object ) 910 { 911 List<String> removedKeys = new ArrayList<String>( ); 912 for ( String key : formatter.keySet( ) ) 913 { 914 if ( key.startsWith( sessionId ) ) 915 { 916 removedKeys.add( key ); 917 } 918 } 919 for ( String key : removedKeys ) 920 { 921 formatter.remove( key ); 922 } 923 } 924 } 925}