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}