• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

manticore-projects / jsqlformatter / #11

01 Jul 2024 12:23AM UTC coverage: 77.783% (-0.08%) from 77.867%
#11

push

github

manticore-projects
Release 5.0

1768 of 2273 relevant lines covered (77.78%)

0.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.89
/src/main/java/com/manticore/jsqlformatter/JSQLFormatter.java
1
/**
2
 * Manticore Projects JSQLFormatter is a SQL Beautifying and Formatting Software.
3
 * Copyright (C) 2024 Andreas Reichel <andreas@manticore-projects.com>
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Affero General Public License as published
7
 * by the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Affero General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Affero General Public License
16
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 */
18
package com.manticore.jsqlformatter;
19

20
import com.diogonunes.jcolor.Ansi;
21
import com.diogonunes.jcolor.AnsiFormat;
22
import com.diogonunes.jcolor.Attribute;
23
import net.sf.jsqlparser.expression.Alias;
24
import net.sf.jsqlparser.expression.AllValue;
25
import net.sf.jsqlparser.expression.AnalyticExpression;
26
import net.sf.jsqlparser.expression.AnalyticType;
27
import net.sf.jsqlparser.expression.ArrayConstructor;
28
import net.sf.jsqlparser.expression.BinaryExpression;
29
import net.sf.jsqlparser.expression.CaseExpression;
30
import net.sf.jsqlparser.expression.CastExpression;
31
import net.sf.jsqlparser.expression.DateValue;
32
import net.sf.jsqlparser.expression.DoubleValue;
33
import net.sf.jsqlparser.expression.Expression;
34
import net.sf.jsqlparser.expression.ExtractExpression;
35
import net.sf.jsqlparser.expression.Function;
36
import net.sf.jsqlparser.expression.IntervalExpression;
37
import net.sf.jsqlparser.expression.JdbcNamedParameter;
38
import net.sf.jsqlparser.expression.JdbcParameter;
39
import net.sf.jsqlparser.expression.KeepExpression;
40
import net.sf.jsqlparser.expression.LongValue;
41
import net.sf.jsqlparser.expression.MySQLGroupConcat;
42
import net.sf.jsqlparser.expression.NextValExpression;
43
import net.sf.jsqlparser.expression.NotExpression;
44
import net.sf.jsqlparser.expression.NullValue;
45
import net.sf.jsqlparser.expression.OracleHint;
46
import net.sf.jsqlparser.expression.RowConstructor;
47
import net.sf.jsqlparser.expression.SignedExpression;
48
import net.sf.jsqlparser.expression.StringValue;
49
import net.sf.jsqlparser.expression.StructType;
50
import net.sf.jsqlparser.expression.TimeKeyExpression;
51
import net.sf.jsqlparser.expression.TranscodingFunction;
52
import net.sf.jsqlparser.expression.WhenClause;
53
import net.sf.jsqlparser.expression.WindowDefinition;
54
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
55
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
56
import net.sf.jsqlparser.expression.operators.relational.Between;
57
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
58
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
59
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
60
import net.sf.jsqlparser.expression.operators.relational.InExpression;
61
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
62
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
63
import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList;
64
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
65
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
66
import net.sf.jsqlparser.schema.Column;
67
import net.sf.jsqlparser.schema.Table;
68
import net.sf.jsqlparser.statement.OutputClause;
69
import net.sf.jsqlparser.statement.ReferentialAction;
70
import net.sf.jsqlparser.statement.Statement;
71
import net.sf.jsqlparser.statement.alter.Alter;
72
import net.sf.jsqlparser.statement.alter.AlterExpression;
73
import net.sf.jsqlparser.statement.alter.AlterOperation;
74
import net.sf.jsqlparser.statement.alter.ConstraintState;
75
import net.sf.jsqlparser.statement.create.index.CreateIndex;
76
import net.sf.jsqlparser.statement.create.table.CheckConstraint;
77
import net.sf.jsqlparser.statement.create.table.ColDataType;
78
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
79
import net.sf.jsqlparser.statement.create.table.CreateTable;
80
import net.sf.jsqlparser.statement.create.table.ExcludeConstraint;
81
import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex;
82
import net.sf.jsqlparser.statement.create.table.Index;
83
import net.sf.jsqlparser.statement.create.table.NamedConstraint;
84
import net.sf.jsqlparser.statement.create.table.RowMovement;
85
import net.sf.jsqlparser.statement.create.view.CreateView;
86
import net.sf.jsqlparser.statement.create.view.ForceOption;
87
import net.sf.jsqlparser.statement.create.view.TemporaryOption;
88
import net.sf.jsqlparser.statement.delete.Delete;
89
import net.sf.jsqlparser.statement.insert.Insert;
90
import net.sf.jsqlparser.statement.merge.Merge;
91
import net.sf.jsqlparser.statement.merge.MergeInsert;
92
import net.sf.jsqlparser.statement.merge.MergeUpdate;
93
import net.sf.jsqlparser.statement.select.AllColumns;
94
import net.sf.jsqlparser.statement.select.AllTableColumns;
95
import net.sf.jsqlparser.statement.select.Distinct;
96
import net.sf.jsqlparser.statement.select.ExceptOp;
97
import net.sf.jsqlparser.statement.select.Fetch;
98
import net.sf.jsqlparser.statement.select.FromItem;
99
import net.sf.jsqlparser.statement.select.GroupByElement;
100
import net.sf.jsqlparser.statement.select.IntersectOp;
101
import net.sf.jsqlparser.statement.select.Join;
102
import net.sf.jsqlparser.statement.select.Limit;
103
import net.sf.jsqlparser.statement.select.MinusOp;
104
import net.sf.jsqlparser.statement.select.Offset;
105
import net.sf.jsqlparser.statement.select.OrderByElement;
106
import net.sf.jsqlparser.statement.select.OrderByElement.NullOrdering;
107
import net.sf.jsqlparser.statement.select.ParenthesedFromItem;
108
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
109
import net.sf.jsqlparser.statement.select.PlainSelect;
110
import net.sf.jsqlparser.statement.select.Select;
111
import net.sf.jsqlparser.statement.select.SelectItem;
112
import net.sf.jsqlparser.statement.select.SetOperation;
113
import net.sf.jsqlparser.statement.select.SetOperationList;
114
import net.sf.jsqlparser.statement.select.Top;
115
import net.sf.jsqlparser.statement.select.UnionOp;
116
import net.sf.jsqlparser.statement.select.Values;
117
import net.sf.jsqlparser.statement.select.WithItem;
118
import net.sf.jsqlparser.statement.truncate.Truncate;
119
import net.sf.jsqlparser.statement.update.Update;
120
import net.sf.jsqlparser.statement.update.UpdateSet;
121

122
import java.io.BufferedReader;
123
import java.io.File;
124
import java.io.StringReader;
125
import java.nio.file.Path;
126
import java.nio.file.Paths;
127
import java.util.ArrayList;
128
import java.util.Arrays;
129
import java.util.Collection;
130
import java.util.List;
131
import java.util.Objects;
132
import java.util.logging.Level;
133
import java.util.logging.Logger;
134
import java.util.regex.Matcher;
135
import java.util.regex.Pattern;
136
import java.util.stream.Collectors;
137

138
/**
139
 * A powerful Java SQL Formatter based on the JSQLParser.
140
 *
141
 * @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
142
 * @version 0.1
143
 */
144
@SuppressWarnings({"PMD.CyclomaticComplexity"})
145
public class JSQLFormatter {
×
146

147
  public static final Pattern SQUARED_BRACKET_QUOTATION_PATTERN = Pattern.compile(
1✔
148
      "(((?!\\[\\d+])\\[.*]\\.\\.?)|(\\.\\[\\w+( +\\w+)*])|((?!\\s\\[\\d+])\\s\\[\\w+( +\\w+)*]))");
149

150
  private static final Logger LOGGER = Logger.getLogger(JSQLFormatter.class.getName());
1✔
151
  public static final AnsiFormat ANSI_FORMAT_LINE_NUMBER =
1✔
152
      new AnsiFormat(Attribute.BRIGHT_BLACK_BACK(), Attribute.DESATURATED());
1✔
153
  public static final AnsiFormat ANSI_FORMAT_KEYWORD =
1✔
154
      new AnsiFormat(Attribute.BLUE_TEXT(), Attribute.BOLD());
1✔
155
  public static final AnsiFormat ANSI_FORMAT_HINT = new AnsiFormat(Attribute.BRIGHT_BLUE_TEXT());
1✔
156
  public static final AnsiFormat ANSI_FORMAT_OPERATOR = new AnsiFormat(Attribute.BLUE_TEXT());
1✔
157
  public static final AnsiFormat ANSI_FORMAT_PARAMETER =
1✔
158
      new AnsiFormat(Attribute.YELLOW_TEXT(), Attribute.DESATURATED());
1✔
159
  public static final AnsiFormat ANSI_FORMAT_ALIAS =
1✔
160
      new AnsiFormat(Attribute.RED_TEXT(), Attribute.BOLD(), Attribute.DESATURATED());
1✔
161
  public static final AnsiFormat ANSI_FORMAT_FUNCTION = new AnsiFormat(Attribute.BRIGHT_RED_TEXT());
1✔
162
  public static final AnsiFormat ANSI_FORMAT_TYPE =
1✔
163
      new AnsiFormat(Attribute.YELLOW_TEXT(), Attribute.DESATURATED());
1✔
164

165
  private static SquaredBracketQuotation squaredBracketQuotation = SquaredBracketQuotation.AUTO;
1✔
166
  private static Separation separation = Separation.BEFORE;
1✔
167
  private static Spelling keywordSpelling = Spelling.UPPER;
1✔
168
  private static Spelling functionSpelling = Spelling.CAMEL;
1✔
169
  private static Spelling objectSpelling = Spelling.LOWER;
1✔
170
  private static OutputFormat outputFormat = OutputFormat.PLAIN;
1✔
171
  private static ShowLineNumbers showLineNumbers = ShowLineNumbers.NO;
1✔
172
  private static StatementTerminator statementTerminator = StatementTerminator.SEMICOLON;
1✔
173

174
  private static BackSlashQuoting backSlashQuoting = BackSlashQuoting.NO;
1✔
175
  private static int indentWidth = 4;
1✔
176
  private static String indentString = "    ";
1✔
177
  private static int lineCount = 0;
1✔
178

179
  public static SquaredBracketQuotation getSquaredBracketQuotation() {
180
    return squaredBracketQuotation;
×
181
  }
182

183
  public static void setSquaredBracketQuotation(SquaredBracketQuotation squaredBracketQuotation) {
184
    JSQLFormatter.squaredBracketQuotation = squaredBracketQuotation;
×
185
  }
×
186

187
  public static BackSlashQuoting getBackSlashQuoting() {
188
    return backSlashQuoting;
×
189
  }
190

191
  public static void setBackSlashQuoting(BackSlashQuoting backSlashQuoting) {
192
    JSQLFormatter.backSlashQuoting = backSlashQuoting;
×
193
  }
×
194

195
  public static StatementTerminator getStatementTerminator() {
196
    return statementTerminator;
×
197
  }
198

199
  public static void setStatementTerminator(StatementTerminator statementTerminator) {
200
    JSQLFormatter.statementTerminator = statementTerminator;
×
201
  }
×
202

203
  public static Separation getSeparation() {
204
    return separation;
×
205
  }
206

207
  public static void setSeparation(Separation separation) {
208
    JSQLFormatter.separation = separation;
×
209
  }
×
210

211
  public static Spelling getKeywordSpelling() {
212
    return keywordSpelling;
×
213
  }
214

215
  public static void setKeywordSpelling(Spelling keywordSpelling) {
216
    JSQLFormatter.keywordSpelling = keywordSpelling;
×
217
  }
×
218

219
  public static Spelling getFunctionSpelling() {
220
    return functionSpelling;
×
221
  }
222

223
  public static void setFunctionSpelling(Spelling functionSpelling) {
224
    JSQLFormatter.functionSpelling = functionSpelling;
×
225
  }
×
226

227
  public static Spelling getObjectSpelling() {
228
    return objectSpelling;
×
229
  }
230

231
  public static void setObjectSpelling(Spelling objectSpelling) {
232
    JSQLFormatter.objectSpelling = objectSpelling;
×
233
  }
×
234

235
  public static OutputFormat getOutputFormat() {
236
    return outputFormat;
×
237
  }
238

239
  public static void setOutputFormat(OutputFormat outputFormat) {
240
    JSQLFormatter.outputFormat = outputFormat;
×
241
  }
×
242

243
  public static int getIndentWidth() {
244
    return indentWidth;
1✔
245
  }
246

247
  public static void setIndentWidth(int indentWidth) {
248
    JSQLFormatter.indentWidth = indentWidth;
×
249

250
    char[] chars = new char[indentWidth];
×
251
    Arrays.fill(chars, ' ');
×
252

253
    JSQLFormatter.indentString = new String(chars);
×
254
  }
×
255

256
  public static String getIndentString() {
257
    return indentString;
×
258
  }
259

260
  public static void setIndentString(String indentString) {
261
    JSQLFormatter.indentString = indentString;
×
262
  }
×
263

264
  private static void appendDecodeExpressionsList(ExpressionList<?> parameters, BreakLine breakLine,
265
      StringBuilder builder, int indent) {
266
    int subIndent = breakLine.equals(BreakLine.NEVER) ? indent : getSubIndent(builder, false);
1✔
267

268
    int i = 0;
1✔
269
    for (Expression expression : parameters) {
1✔
270
      switch (breakLine) {
1✔
271
        case AS_NEEDED:
272
          BreakLine bl = i == 0 || (i - 1) % 2 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
273
          appendExpression(expression, null, builder, subIndent, i, parameters.size(), true, bl);
1✔
274
          break;
1✔
275

276
        default:
277
          appendExpression(expression, null, builder, subIndent, i, parameters.size(), true,
×
278
              breakLine);
279
      }
280
      i++;
1✔
281
    }
1✔
282
  }
1✔
283

284
  static String toCamelCase(String s) {
285
    StringBuilder camelCaseString = new StringBuilder();
1✔
286

287
    String[] nameParts = s.split("_");
1✔
288
    int i = 0;
1✔
289
    for (String part : nameParts) {
1✔
290
      if (i > 0) {
1✔
291
        camelCaseString.append("_");
1✔
292
      }
293
      camelCaseString.append(part.substring(0, 1).toUpperCase())
1✔
294
          .append(part.substring(1).toLowerCase());
1✔
295
      i++;
1✔
296
    }
297
    return camelCaseString.toString();
1✔
298
  }
299

300
  private static StringBuilder appendKeyWord(StringBuilder builder, OutputFormat format,
301
      String keyword, String before, String after) {
302

303
    String s;
304
    switch (keywordSpelling) {
1✔
305
      case UPPER:
306
        s = keyword.toUpperCase();
1✔
307
        break;
1✔
308
      case LOWER:
309
        s = keyword.toLowerCase();
1✔
310
        break;
1✔
311
      case CAMEL:
312
        s = toCamelCase(keyword);
×
313
        break;
×
314
      default:
315
        s = keyword;
×
316
    }
317

318
    switch (format) {
1✔
319
      case ANSI:
320
        builder.append(before).append(ANSI_FORMAT_KEYWORD.format(s)).append(after);
1✔
321
        break;
1✔
322
      case HTML:
323
        builder.append(before).append("<span style=\"color:blue; font-style:bold;\">").append(s)
1✔
324
            .append("</span>").append(after);
1✔
325
        break;
1✔
326
      default:
327
        builder.append(before).append(s).append(after);
1✔
328
        break;
329
    }
330
    return builder;
1✔
331
  }
332

333
  private static StringBuilder appendNormalizingTrailingWhiteSpace(StringBuilder builder,
334
      String s) {
335
    if (builder.length() > 0) {
1✔
336
      int pos = builder.length() - 1;
1✔
337
      char lastChar = builder.charAt(pos);
1✔
338
      if (lastChar == ' ') {
1✔
339
        while (lastChar == ' ' && pos > 0) {
1✔
340
          pos--;
1✔
341
          lastChar = builder.charAt(pos);
1✔
342
        }
343
        builder.setLength(pos + 1);
1✔
344
      }
345
    }
346
    builder.append(s);
1✔
347
    return builder;
1✔
348
  }
349

350
  private static StringBuilder appendNormalizedLineBreak(StringBuilder builder) {
351
    switch (showLineNumbers) {
1✔
352
      case YES:
353
        lineCount++;
1✔
354

355
        String lineCountStr = "0000" + lineCount;
1✔
356
        lineCountStr = lineCountStr.substring(lineCountStr.length() - 5);
1✔
357
        lineCountStr += " | ";
1✔
358

359
        StringBuilder fillerStr = new StringBuilder();
1✔
360
        int adjust = getIndentWidth() - lineCountStr.length() % getIndentWidth();
1✔
361
        for (int i = 0; i < adjust; i++) {
1✔
362
          fillerStr.append(" ");
1✔
363
        }
364

365

366
        switch (outputFormat) {
1✔
367
          case ANSI:
368
            return appendNormalizingTrailingWhiteSpace(builder, Ansi.RESET
1✔
369
                + ANSI_FORMAT_LINE_NUMBER.format("\n" + lineCountStr) + Ansi.RESET + fillerStr);
1✔
370
          case HTML:
371
            return builder.append("\n")
1✔
372
                .append("<span style=\"color:light-grey; font-size:8pt; font-style:normal;\">")
1✔
373
                .append(lineCountStr).append("</span>").append(fillerStr);
1✔
374
          default:
375
            return appendNormalizingTrailingWhiteSpace(builder, "\n" + lineCountStr + fillerStr);
1✔
376
        }
377
      default:
378
        return appendNormalizingTrailingWhiteSpace(builder, "\n");
1✔
379
    }
380
  }
381

382
  private static StringBuilder appendHint(StringBuilder builder, OutputFormat format, String hint,
383
      String before, String after) {
384

385
    String s;
386
    switch (keywordSpelling) {
1✔
387
      case UPPER:
388
        s = hint.toUpperCase();
1✔
389
        break;
1✔
390
      case LOWER:
391
        s = hint.toLowerCase();
1✔
392
        break;
1✔
393
      case CAMEL:
394
        s = toCamelCase(hint);
×
395
        break;
×
396
      default:
397
        s = hint;
×
398
    }
399

400
    switch (format) {
1✔
401
      case ANSI:
402
        builder.append(before).append(ANSI_FORMAT_HINT.format(s)).append(after);
×
403
        break;
×
404
      case HTML:
405
        builder.append(before).append("<span style=\"color:light-blue; font-style:thin\">")
×
406
            .append(s).append("</span>").append(after);
×
407
        break;
×
408
      default:
409
        builder.append(before).append(s).append(after);
1✔
410
        break;
411
    }
412
    return builder;
1✔
413
  }
414

415
  private static StringBuilder appendOperator(StringBuilder builder, OutputFormat format,
416
      String operator, String before, String after) {
417

418
    String s;
419
    switch (keywordSpelling) {
1✔
420
      case UPPER:
421
        s = operator.toUpperCase();
1✔
422
        break;
1✔
423
      case LOWER:
424
        s = operator.toLowerCase();
1✔
425
        break;
1✔
426
      case CAMEL:
427
        s = toCamelCase(operator);
×
428
        break;
×
429
      default:
430
        s = operator;
×
431
    }
432

433
    switch (format) {
1✔
434
      case ANSI:
435
        builder.append(before).append(ANSI_FORMAT_OPERATOR.format(s)).append(after);
1✔
436
        break;
1✔
437
      case HTML:
438
        builder.append(before).append("<span style=\"color:blue; font-style:normal;\">").append(s)
1✔
439
            .append("</span>").append(after);
1✔
440
        break;
1✔
441
      default:
442
        builder.append(before).append(s).append(after);
1✔
443
        break;
444
    }
445
    return builder;
1✔
446
  }
447

448
  private static StringBuilder appendValue(StringBuilder builder, OutputFormat format, String value,
449
      String before, String after) {
450
    switch (format) {
1✔
451
      case ANSI:
452
        builder.append(before).append(ANSI_FORMAT_PARAMETER.format(value)).append(after);
1✔
453
        break;
1✔
454
      case HTML:
455
        builder.append(before).append("<span style=\"color:yellow; font-style:normal;\">")
1✔
456
            .append(value).append("</span>").append(after);
1✔
457
        break;
1✔
458
      default:
459
        builder.append(before).append(value).append(after);
1✔
460
        break;
461
    }
462
    return builder;
1✔
463
  }
464

465
  private static StringBuilder appendAlias(StringBuilder builder, OutputFormat format, String alias,
466
      String before, String after) {
467

468
    String s;
469
    if (alias.trim().startsWith("\"") || alias.trim().startsWith("[")) {
1✔
470
      s = alias;
1✔
471
    } else {
472
      switch (objectSpelling) {
1✔
473
        case UPPER:
474
          s = alias.toUpperCase();
1✔
475
          break;
1✔
476
        case LOWER:
477
          s = alias.toLowerCase();
1✔
478
          break;
1✔
479
        case CAMEL:
480
          s = toCamelCase(alias);
×
481
          break;
×
482
        default:
483
          s = alias;
×
484
      }
485
    }
486

487
    switch (format) {
1✔
488
      case ANSI:
489
        builder.append(before).append(ANSI_FORMAT_ALIAS.format(s)).append(after);
×
490
        break;
×
491
      case HTML:
492
        builder.append(before).append("<span style=\"color:red; font-style:bold;\">").append(s)
×
493
            .append("</span>").append(after);
×
494
        break;
×
495
      default:
496
        builder.append(before).append(s).append(after);
1✔
497
        break;
498
    }
499
    return builder;
1✔
500
  }
501

502
  private static StringBuilder appendObjectName(StringBuilder builder, OutputFormat format,
503
      String objectName, String before, String after) {
504

505
    StringBuilder nameBuilder = new StringBuilder();
1✔
506

507
    int j = 0;
1✔
508
    String[] parts = objectName.contains(".") ? objectName.split("\\.") : new String[] {objectName};
1✔
509
    for (String w : parts) {
1✔
510
      if (j > 0) {
1✔
511
        nameBuilder.append(".");
1✔
512
      }
513
      if (w.trim().startsWith("\"") || w.trim().startsWith("[")) {
1✔
514
        nameBuilder.append(w);
1✔
515
      } else {
516
        switch (objectSpelling) {
1✔
517
          case UPPER:
518
            nameBuilder.append(w.toUpperCase());
1✔
519
            break;
1✔
520
          case LOWER:
521
            nameBuilder.append(w.toLowerCase());
1✔
522
            break;
1✔
523
          case CAMEL:
524
            nameBuilder.append(toCamelCase(w));
×
525
            break;
526
        }
527
      }
528
      j++;
1✔
529
    }
530

531
    switch (format) {
1✔
532
      default:
533
        builder.append(before).append(nameBuilder).append(after);
1✔
534
        break;
535
    }
536
    return builder;
1✔
537
  }
538

539
  private static StringBuilder appendFunction(StringBuilder builder, OutputFormat format,
540
      String function, String before, String after) {
541

542
    String s;
543
    switch (functionSpelling) {
1✔
544
      case UPPER:
545
        s = function.toUpperCase();
×
546
        break;
×
547
      case LOWER:
548
        s = function.toLowerCase();
×
549
        break;
×
550
      case CAMEL:
551
        s = toCamelCase(function);
1✔
552
        break;
1✔
553
      default:
554
        s = function;
1✔
555
    }
556

557
    switch (format) {
1✔
558
      case ANSI:
559
        builder.append(before).append(ANSI_FORMAT_FUNCTION.format(s)).append(after);
×
560
        break;
×
561
      case HTML:
562
        builder.append(before).append("<span style=\"color:light-red; font-style:italic;\">")
×
563
            .append(s).append("</span>").append(after);
×
564
        break;
×
565
      default:
566
        builder.append(before).append(s).append(after);
1✔
567
        break;
568
    }
569
    return builder;
1✔
570
  }
571

572
  private static StringBuilder appendType(StringBuilder builder, OutputFormat format, String type,
573
      String before, String after) {
574

575
    String s;
576
    switch (keywordSpelling) {
1✔
577
      case UPPER:
578
        s = type.toUpperCase();
1✔
579
        break;
1✔
580
      case LOWER:
581
        s = type.toLowerCase();
1✔
582
        break;
1✔
583
      case CAMEL:
584
        s = toCamelCase(type);
×
585
        break;
×
586
      default:
587
        s = type;
×
588
    }
589

590
    switch (format) {
1✔
591
      case ANSI:
592
        builder.append(before).append(ANSI_FORMAT_TYPE.format(s)).append(after);
×
593
        break;
×
594
      case HTML:
595
        builder.append(before).append("<span style=\"color:yellow; font-style:normal;\">").append(s)
×
596
            .append("</span>").append(after);
×
597
        break;
×
598
      default:
599
        builder.append(before).append(s).append(after);
1✔
600
        break;
601
    }
602
    return builder;
1✔
603
  }
604

605
  private static int getLastLineLength(StringBuilder builder) {
606
    String lastLine = builder.substring(builder.lastIndexOf("\n") + 1);
1✔
607
    lastLine = lastLine.replaceAll("\u001B\\[[;\\d]*[ -/]*[@-~]", "");
1✔
608

609
    return lastLine.length();
1✔
610
  }
611

612
  private static int getSubIndent(StringBuilder builder, boolean moveToTab) {
613
    int lastLineLength = getLastLineLength(builder);
1✔
614

615
    int subIndent = lastLineLength / indentWidth + (lastLineLength % indentWidth > 0 ? 1 : 0);
1✔
616

617
    for (int i = lastLineLength; moveToTab && i < subIndent * indentWidth; i++) {
1✔
618
      builder.append(" ");
1✔
619
    }
620

621
    return subIndent;
1✔
622
  }
623

624
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
625
  private static void appendDelete(StringBuilder builder, Delete delete, int indent) {
626
    List<WithItem> withItems = delete.getWithItemsList();
1✔
627
    if (withItems != null && !withItems.isEmpty()) {
1✔
628
      int i = 0;
1✔
629
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
630

631
      for (WithItem withItem : withItems) {
1✔
632
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
633
        i++;
1✔
634
      }
1✔
635

636
      appendNormalizedLineBreak(builder);
1✔
637
    }
638
    appendKeyWord(builder, outputFormat, "DELETE", "", " ");
1✔
639

640
    OracleHint oracleHint = delete.getOracleHint();
1✔
641
    if (oracleHint != null) {
1✔
642
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
643
    }
644

645
    List<Table> tables = delete.getTables();
1✔
646

647
    if (tables != null && !tables.isEmpty()) {
1✔
648
      int j = 0;
1✔
649
      for (Table table : tables) {
1✔
650
        switch (separation) {
1✔
651
          case AFTER:
652
            appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "",
×
653
                j < tables.size() - 1 ? ", " : " ");
×
654
            break;
×
655
          case BEFORE:
656
          default:
657
            appendObjectName(builder, outputFormat, table.getFullyQualifiedName(),
1✔
658
                j > 0 ? ", " : "", " ");
1✔
659
            break;
660
        }
661

662
        j++;
1✔
663
      }
1✔
664
    }
665

666
    appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
667

668
    Table table = delete.getTable();
1✔
669
    Alias alias = table.getAlias();
1✔
670

671
    appendTable(table, alias, builder);
1✔
672

673
    List<Join> joins = delete.getJoins();
1✔
674
    appendJoins(joins, builder, indent);
1✔
675

676
    Expression whereExpression = delete.getWhere();
1✔
677
    appendWhere(whereExpression, builder, indent);
1✔
678

679
    List<OrderByElement> orderByElements = delete.getOrderByElements();
1✔
680
    appendOrderByElements(orderByElements, builder, indent);
1✔
681

682
    Limit limit = delete.getLimit();
1✔
683
    if (limit != null) {
1✔
684
      appendNormalizedLineBreak(builder);
×
685
      for (int j = 0; j < indent; j++) {
×
686
        builder.append(indentString);
×
687
      }
688

689
      appendKeyWord(builder, outputFormat, "LIMIT", "", "");
×
690

691
      Expression rowCount = limit.getRowCount();
×
692
      if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
×
693
        // no offset allowed
694
        appendKeyWord(builder, outputFormat, "NULL", " ", "");
×
695
      } else {
696
        if (null != limit.getOffset()) {
×
697
          appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false, BreakLine.NEVER);
×
698
          builder.append(", ");
×
699
        }
700
        if (null != limit.getRowCount()) {
×
701
          appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
×
702
              BreakLine.NEVER);
703
        }
704
      }
705
    }
706
  }
1✔
707

708
  public static File getAbsoluteFile(String filename) {
709
    String homePath = new File(System.getProperty("user.home")).toURI().getPath();
×
710

711
    String _filename = filename.replaceFirst("~", Matcher.quoteReplacement(homePath))
×
712
        .replaceFirst("\\$\\{user.home}", Matcher.quoteReplacement(homePath));
×
713

714
    File f = new File(_filename);
×
715
    if (!f.isAbsolute()) {
×
716
      Path basePath = Paths.get("").toAbsolutePath();
×
717

718
      Path resolvedPath = basePath.resolve(filename);
×
719
      Path absolutePath = resolvedPath.normalize();
×
720
      f = absolutePath.toFile();
×
721
    }
722
    return f;
×
723
  }
724

725
  public static String getAbsoluteFileName(String filename) {
726
    return getAbsoluteFile(filename).getAbsolutePath();
×
727
  }
728

729
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
730
  public static ArrayList<Exception> verify(String sqlStr, String... options) {
731
    ArrayList<Exception> exceptions = new ArrayList<>();
×
732

733
    applyFormattingOptions(options);
×
734

735
    Pattern SEMICOLON_PATTERN = Pattern.compile(";|$");
×
736
    Matcher m = SEMICOLON_PATTERN.matcher(sqlStr);
×
737
    ArrayList<Integer> semicolons = new ArrayList<>();
×
738

739
    while (m.find()) {
×
740
      semicolons.add(m.start());
×
741
    }
742

743
    m = CommentMap.COMMENT_PATTERN.matcher(sqlStr);
×
744
    while (m.find()) {
×
745
      int start = m.start();
×
746
      int end = m.end();
×
747

748
      int n = semicolons.size();
×
749
      for (int i = n - 1; i >= 0; i--) {
×
750
        int pos = semicolons.get(i);
×
751
        if (start <= pos && pos < end) {
×
752
          semicolons.remove(i);
×
753
        }
754
      }
755
    }
×
756

757
    int pos = 0;
×
758
    int length = sqlStr.length();
×
759
    int n = semicolons.size();
×
760
    for (int i = 0; i < n; i++) {
×
761
      int semicolonPos = semicolons.get(i);
×
762

763
      if (semicolonPos > pos) {
×
764
        String statementSql = sqlStr.substring(pos, Integer.min(semicolonPos + 1, length));
×
765
        pos = semicolonPos + 1;
×
766

767
        // we are at the end and find only remaining whitespace
768
        if (statementSql.trim().isEmpty()) {
×
769
          break;
×
770
        }
771

772
        boolean useSquareBracketQuotation;
773
        switch (squaredBracketQuotation) {
×
774
          case YES:
775
            useSquareBracketQuotation = true;
×
776
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
777
                useSquareBracketQuotation);
×
778
            break;
×
779
          case NO:
780
            useSquareBracketQuotation = false;
×
781
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
782
                useSquareBracketQuotation);
×
783
            break;
×
784
          case AUTO:
785
          default:
786
            useSquareBracketQuotation =
×
787
                SQUARED_BRACKET_QUOTATION_PATTERN.matcher(statementSql).find();
×
788
            LOGGER.log(Level.FINE, "Square Bracket Quotation auto-detected as {0}.",
×
789
                useSquareBracketQuotation);
×
790
        }
791
        try {
792
          CCJSqlParserUtil.parse(statementSql,
×
793
              parser -> parser.withSquareBracketQuotation(useSquareBracketQuotation));
×
794

795
        } catch (Exception ex1) {
×
796
          exceptions.add(new Exception("Cannot parse the Statement:\n" + statementSql, ex1));
×
797
        }
×
798
      } else {
799
        break;
800
      }
801
    }
802
    return exceptions;
×
803
  }
804

805
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
806
  public static String format(String sqlStr, String... options) throws Exception {
807
    applyFormattingOptions(options);
1✔
808

809
    StringBuilder builder = new StringBuilder();
1✔
810

811
    int indent = 0;
1✔
812
    lineCount = 0;
1✔
813

814
    // Pattern SEMICOLON_PATTERN =
815
    // Pattern.compile("((?:(?:'[^']*+')|(?:\"[^\"]*+\")|(?:--.*)|(?:\\/\\*[^\\*\\/]*+\\*\\/)|[^;])*+);");
816
    Pattern SEMICOLON_PATTERN = Pattern.compile(";|$|\\n\\n\\n");
1✔
817
    Matcher m = SEMICOLON_PATTERN.matcher(sqlStr);
1✔
818
    ArrayList<Integer> semicolons = new ArrayList<>();
1✔
819

820
    while (m.find()) {
1✔
821
      semicolons.add(m.start());
1✔
822
    }
823

824
    m = CommentMap.COMMENT_PATTERN.matcher(sqlStr);
1✔
825
    while (m.find()) {
1✔
826
      int start = m.start();
1✔
827
      int end = m.end();
1✔
828

829
      int n = semicolons.size();
1✔
830
      for (int i = n - 1; i >= 0; i--) {
1✔
831
        int pos = semicolons.get(i);
1✔
832
        if (start <= pos && pos < end) {
1✔
833
          semicolons.remove(i);
1✔
834
        }
835
      }
836
    }
1✔
837

838
    int pos = 0;
1✔
839
    int length = sqlStr.length();
1✔
840
    int n = semicolons.size();
1✔
841
    for (int i = 0; i < n; i++) {
1✔
842
      int semicolonPos = semicolons.get(i);
1✔
843

844
      if (semicolonPos > pos) {
1✔
845
        String statementSql = sqlStr.substring(pos, Integer.min(semicolonPos + 1, length));
1✔
846
        pos = semicolonPos + 1;
1✔
847

848
        // we are at the end and find only remaining whitespace
849
        // or comments
850
        if (statementSql.trim().isEmpty() || i == n - 1 && statementSql.trim().startsWith("--")
1✔
851
            || i == n - 1 && statementSql.trim().startsWith("/*")) {
1✔
852
          break;
×
853
        }
854

855
        StringBuilder statementBuilder = new StringBuilder();
1✔
856

857
        boolean useSquareBracketQuotation;
858
        switch (squaredBracketQuotation) {
1✔
859
          case YES:
860
            useSquareBracketQuotation = true;
×
861
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
862
                useSquareBracketQuotation);
×
863
            break;
×
864
          case NO:
865
            useSquareBracketQuotation = false;
×
866
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
867
                useSquareBracketQuotation);
×
868
            break;
×
869
          case AUTO:
870
          default:
871
            useSquareBracketQuotation =
1✔
872
                SQUARED_BRACKET_QUOTATION_PATTERN.matcher(statementSql).find();
1✔
873
            LOGGER.log(Level.FINE, "Square Bracket Quotation auto-detected as {0}.",
1✔
874
                useSquareBracketQuotation);
1✔
875
        }
876

877
        boolean useBackSlashQuoting;
878
        if (Objects.requireNonNull(backSlashQuoting) == BackSlashQuoting.YES) {
1✔
879
          useBackSlashQuoting = true;
×
880
          LOGGER.log(Level.FINE, "Back Slash Quoting set as {0}.", true);
×
881
        } else {
882
          useBackSlashQuoting = false;
1✔
883
          LOGGER.log(Level.FINE, "Back Slash Quoting set as {0}.", false);
1✔
884
        }
885

886
        CommentMap commentMap = new CommentMap(statementSql);
1✔
887

888
        Pattern DIRECTIVE_PATTERN = Pattern.compile("@JSQLFormatter\\s?\\((.*)\\)");
1✔
889
        for (Comment comment : commentMap.values()) {
1✔
890
          Matcher m1 = DIRECTIVE_PATTERN.matcher(comment.text);
1✔
891
          if (m1.find()) {
1✔
892
            String[] keyValuePairs = m1.group(1).split(",");
1✔
893
            applyFormattingOptions(keyValuePairs);
1✔
894
          }
895
        }
1✔
896

897
        try {
898
          Statement statement =
1✔
899
              CCJSqlParserUtil.parse(CCJSqlParserUtil.sanitizeSingleSql(statementSql),
1✔
900
                  parser -> parser.withSquareBracketQuotation(useSquareBracketQuotation)
1✔
901
                      .withBackslashEscapeCharacter(useBackSlashQuoting).withTimeOut(60000)
1✔
902
                      .withUnsupportedStatements(false));
1✔
903

904
          if (statement instanceof Select) {
1✔
905
            Select select = (Select) statement;
1✔
906
            appendSelect(select, statementBuilder, indent, true, false);
1✔
907

908
          } else if (statement instanceof Update) {
1✔
909
            Update update = (Update) statement;
1✔
910
            appendUpdate(statementBuilder, update, indent);
1✔
911

912
          } else if (statement instanceof Insert) {
1✔
913
            Insert insert = (Insert) statement;
1✔
914
            appendInsert(statementBuilder, insert, indent);
1✔
915

916
          } else if (statement instanceof Merge) {
1✔
917
            Merge merge = (Merge) statement;
1✔
918
            appendMerge(statementBuilder, merge, indent);
1✔
919

920
          } else if (statement instanceof Delete) {
1✔
921
            Delete delete = (Delete) statement;
1✔
922
            appendDelete(statementBuilder, delete, indent);
1✔
923

924
          } else if (statement instanceof Truncate) {
1✔
925
            Truncate truncate = (Truncate) statement;
1✔
926
            appendTruncate(statementBuilder, truncate);
1✔
927

928
          } else if (statement instanceof CreateTable) {
1✔
929
            CreateTable createTable = (CreateTable) statement;
1✔
930
            appendCreateTable(statementBuilder, createTable, indent);
1✔
931

932
          } else if (statement instanceof CreateIndex) {
1✔
933
            CreateIndex createIndex = (CreateIndex) statement;
1✔
934
            appendCreateIndex(statementBuilder, createIndex, indent);
1✔
935

936
          } else if (statement instanceof CreateView) {
1✔
937
            CreateView createView = (CreateView) statement;
1✔
938
            appendCreateView(statementBuilder, createView, indent);
1✔
939

940
          } else if (statement instanceof Alter) {
1✔
941
            Alter alter = (Alter) statement;
1✔
942
            appendAlter(statementBuilder, alter, indent);
1✔
943

944
          } else if (statement != null) {
1✔
945
            try {
946
              statementBuilder.append("\n").append(statement);
1✔
947
            } catch (Exception ex) {
×
948
              throw new UnsupportedOperationException(
×
949
                  "The " + statement.getClass().getName() + " Statement is not supported yet.");
×
950
            }
1✔
951
          }
952

953
          switch (statementTerminator) {
1✔
954
            case SEMICOLON:
955
              appendNormalizedLineBreak(statementBuilder).append(";\n");
1✔
956
              break;
1✔
957
            case NONE:
958
              appendNormalizedLineBreak(statementBuilder).append("\n\n");
1✔
959
              break;
1✔
960
            case GO:
961
              appendNormalizedLineBreak(statementBuilder).append("GO\n");
×
962
              break;
×
963
            case BACKSLASH:
964
              appendNormalizedLineBreak(statementBuilder).append("\\\n");
×
965
              break;
966
          }
967

968
          builder.append(commentMap.isEmpty() ? statementBuilder
1✔
969
              : commentMap.insertComments(statementBuilder, outputFormat));
1✔
970

971
        } catch (Exception ex1) {
×
972
          if (statementSql.trim().length() <= commentMap.getLength()) {
×
973
            LOGGER.info("Found only comments, but no SQL code.");
×
974
            builder.append(statementSql);
×
975
          } else {
976

977
            LOGGER.log(Level.WARNING, "Failed for format statement between \n" + statementSql, ex1);
×
978

979
            builder.append("-- failed to format start\n").append(statementSql)
×
980
                .append("\n-- failed to format end\n").append("\n");
×
981
          }
982
        }
1✔
983
      } else {
984
        break;
985
      }
986
    }
987

988
    if (outputFormat == OutputFormat.HTML) {
1✔
989
      builder = new StringBuilder().append("<html>\n").append("<head>\n")
1✔
990
          .append("<title>SQL Statement's Java Object Tree</title>\n").append("</head>\n")
1✔
991
          .append("<body>\n").append("<pre style=\"font-size:-2;background-color:#EFEFEF;\">\n")
1✔
992
          .append(builder).append("\n</pre>\n").append("</body>\n").append("</html>");
1✔
993
    }
994

995
    return builder.toString().trim();
1✔
996
  }
997

998
  public static StringBuilder formatToJava(String sqlStr, int indent, String... options)
999
      throws Exception {
1000
    String formatted = format(sqlStr, options);
×
1001
    StringReader stringReader = new StringReader(formatted);
×
1002
    BufferedReader bufferedReader = new BufferedReader(stringReader);
×
1003
    String line;
1004
    StringBuilder builder = new StringBuilder();
×
1005
    int i = 0;
×
1006
    while ((line = bufferedReader.readLine()) != null) {
×
1007
      if (i > 0) {
×
1008
        for (int j = 0; j < indent - 2; j++) {
×
1009
          builder.append(" ");
×
1010
        }
1011
        builder.append("+ ");
×
1012
      } else {
1013
        for (int j = 0; j < indent; j++) {
×
1014
          builder.append(" ");
×
1015
        }
1016
      }
1017
      builder.append("\"").append(line).append("\"\n");
×
1018
      i++;
×
1019
    }
1020
    return builder;
×
1021
  }
1022

1023
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1024
  public static void applyFormattingOptions(String[] options) {
1025
    // set the formatting options
1026
    if (options != null) {
1✔
1027
      for (String s : options) {
1✔
1028
        String[] o = s.split("=");
1✔
1029
        if (o.length == 2) {
1✔
1030
          LOGGER.log(Level.FINE, "Found Formatting Option {0} = {1}", o);
1✔
1031

1032
          String key = o[0].trim();
1✔
1033
          String value = o[1].trim();
1✔
1034

1035
          if (key.equalsIgnoreCase(FormattingOption.OUTPUT_FORMAT.toString())) {
1✔
1036
            try {
1037
              outputFormat = OutputFormat.valueOf(value.toUpperCase());
1✔
1038
            } catch (Exception ex) {
×
1039
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1040
            }
1✔
1041

1042
          } else if (key.equalsIgnoreCase(FormattingOption.KEYWORD_SPELLING.toString())) {
1✔
1043
            try {
1044
              keywordSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1045
            } catch (Exception ex) {
×
1046
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1047
            }
1✔
1048

1049
          } else if (key.equalsIgnoreCase(FormattingOption.FUNCTION_SPELLING.toString())) {
1✔
1050
            try {
1051
              functionSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1052
            } catch (Exception ex) {
×
1053
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1054
            }
1✔
1055

1056
          } else if (key.equalsIgnoreCase(FormattingOption.OBJECT_SPELLING.toString())) {
1✔
1057
            try {
1058
              objectSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1059
            } catch (Exception ex) {
×
1060
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1061
            }
1✔
1062

1063
          } else if (key.equalsIgnoreCase(FormattingOption.SEPARATION.toString())) {
1✔
1064
            try {
1065
              separation = Separation.valueOf(value.toUpperCase());
1✔
1066
            } catch (Exception ex) {
×
1067
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1068
            }
1✔
1069

1070
          } else if (key.equalsIgnoreCase(FormattingOption.SQUARE_BRACKET_QUOTATION.toString())) {
1✔
1071
            try {
1072
              squaredBracketQuotation = SquaredBracketQuotation.valueOf(value.toUpperCase());
×
1073
            } catch (Exception ex) {
×
1074
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1075
            }
×
1076

1077
          } else if (key.equalsIgnoreCase(FormattingOption.BACKSLASH_QUOTING.toString())) {
1✔
1078
            try {
1079
              backSlashQuoting = BackSlashQuoting.valueOf(value.toUpperCase());
×
1080
            } catch (Exception ex) {
×
1081
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1082
            }
×
1083

1084
          } else if (key.equalsIgnoreCase(FormattingOption.SHOW_LINE_NUMBERS.toString())) {
1✔
1085
            try {
1086
              showLineNumbers = ShowLineNumbers.valueOf(value.toUpperCase());
1✔
1087
            } catch (Exception ex) {
×
1088
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1089
            }
1✔
1090

1091
          } else if (key.equalsIgnoreCase(FormattingOption.INDENT_WIDTH.toString())) {
1✔
1092
            try {
1093
              indentWidth = Integer.parseInt(value);
1✔
1094

1095
              char[] chars = new char[indentWidth];
1✔
1096
              Arrays.fill(chars, ' ');
1✔
1097

1098
              indentString = new String(chars);
1✔
1099
            } catch (Exception ex) {
×
1100
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1101
            }
1✔
1102
          } else if (key.equalsIgnoreCase(FormattingOption.STATEMENT_TERMINATOR.toString())) {
1✔
1103
            try {
1104
              statementTerminator = StatementTerminator.valueOf(value.toUpperCase());
1✔
1105
            } catch (Exception ex) {
×
1106
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1107
            }
1✔
1108

1109
          } else {
1110
            LOGGER.log(Level.WARNING, "Unknown Formatting Option {0} = {1} ", o);
×
1111
          }
1112

1113
        } else {
1✔
1114
          LOGGER.log(Level.WARNING, "Invalid Formatting Option {0}", s);
×
1115
        }
1116
      }
1117
    }
1118
  }
1✔
1119

1120
  private static void appendMerge(StringBuilder builder, Merge merge, int indent) {
1121

1122
    List<WithItem> withItems = merge.getWithItemsList();
1✔
1123
    if (withItems != null && !withItems.isEmpty()) {
1✔
1124
      int i = 0;
1✔
1125
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1126

1127
      for (WithItem withItem : withItems) {
1✔
1128
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1129
        i++;
1✔
1130
      }
1✔
1131

1132
      appendNormalizedLineBreak(builder);
1✔
1133
    }
1134

1135
    appendKeyWord(builder, outputFormat, "MERGE", "", " ");
1✔
1136
    OracleHint oracleHint = merge.getOracleHint();
1✔
1137
    if (oracleHint != null) {
1✔
1138
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
1139
    }
1140

1141
    appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1142

1143
    Table table = merge.getTable();
1✔
1144
    Alias alias = table.getAlias();
1✔
1145

1146
    appendTable(table, alias, builder);
1✔
1147

1148
    appendNormalizedLineBreak(builder);
1✔
1149
    for (int j = 0; j < indent + 1; j++) {
1✔
1150
      builder.append(indentString);
1✔
1151
    }
1152
    appendKeyWord(builder, outputFormat, "USING", "", " ");
1✔
1153

1154
    FromItem fromItem = merge.getFromItem();
1✔
1155
    appendFromItem(fromItem, builder, indent, 0, 1);
1✔
1156

1157
    Expression onExpression = merge.getOnCondition();
1✔
1158
    if (onExpression != null) {
1✔
1159
      appendNormalizedLineBreak(builder);
1✔
1160
      for (int j = 0; j < indent + 2; j++) {
1✔
1161
        builder.append(indentString);
1✔
1162
      }
1163
      appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
1164
      appendExpression(onExpression, null, builder, indent + 2, 0, 1, false, BreakLine.AS_NEEDED);
1✔
1165

1166
      appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1167
    }
1168

1169
    MergeInsert insert = merge.getMergeInsert();
1✔
1170
    MergeUpdate update = merge.getMergeUpdate();
1✔
1171
    if (merge.isInsertFirst()) {
1✔
1172
      appendMergeInsert(insert, builder, indent, 0);
1✔
1173
      appendMergeUpdate(update, builder, indent);
1✔
1174
    } else {
1175
      appendMergeUpdate(update, builder, indent);
1✔
1176
      appendMergeInsert(insert, builder, indent, 0);
1✔
1177
    }
1178

1179
    appendOutputClaus(merge.getOutputClause(), builder, indent);
1✔
1180
  }
1✔
1181

1182
  private static void appendOutputClaus(OutputClause outputClause, StringBuilder builder,
1183
      int indent) {
1184
    if (outputClause != null) {
1✔
1185
      appendNormalizedLineBreak(builder);
1✔
1186
      for (int j = 0; j < indent; j++) {
1✔
1187
        builder.append(indentString);
×
1188
      }
1189
      appendKeyWord(builder, outputFormat, "OUTPUT", "", " ");
1✔
1190
      int i = 0;
1✔
1191
      int subIndent = getSubIndent(builder, outputClause.getSelectItemList().size() > 3);
1✔
1192
      appendSelectItemList(outputClause.getSelectItemList(), builder, subIndent, i,
1✔
1193
          BreakLine.AS_NEEDED, indent);
1194

1195
      appendNormalizedLineBreak(builder);
1✔
1196
      for (int j = 0; j < indent + 1; j++) {
1✔
1197
        builder.append(indentString);
1✔
1198
      }
1199
      appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1200

1201
      if (outputClause.getOutputTable() != null) {
1✔
1202
        Table table = outputClause.getOutputTable();
1✔
1203
        appendTable(table, table.getAlias(), builder);
1✔
1204

1205
        appendStringList(outputClause.getColumnList(), builder, indent + 1, true,
1✔
1206
            BreakLine.AS_NEEDED);
1207
      } else if (outputClause.getTableVariable() != null) {
1✔
1208
        appendObjectName(builder, outputFormat, outputClause.getTableVariable().toString(), " ",
×
1209
            "");
1210
      }
1211
    }
1212
  }
1✔
1213

1214
  public static void appendMergeUpdate(MergeUpdate update, StringBuilder builder, int indent) {
1215
    if (update != null) {
1✔
1216
      appendNormalizedLineBreak(builder);
1✔
1217

1218
      for (int j = 0; j < indent; j++) {
1✔
1219
        builder.append(indentString);
×
1220
      }
1221
      appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
1222
      appendKeyWord(builder, outputFormat, "MATCHED", "", " ");
1✔
1223
      appendKeyWord(builder, outputFormat, "THEN", "", "\n");
1✔
1224

1225
      for (int j = 0; j < indent + 1; j++) {
1✔
1226
        builder.append(indentString);
1✔
1227
      }
1228
      appendKeyWord(builder, outputFormat, "UPDATE", "", " ");
1✔
1229
      appendKeyWord(builder, outputFormat, "SET", "", " ");
1✔
1230

1231
      int subIndent = getSubIndent(builder, true);
1✔
1232

1233
      appendUpdateSets(builder, update.getUpdateSets(), subIndent);
1✔
1234

1235
      Expression whereCondition = update.getWhereCondition();
1✔
1236
      if (whereCondition != null) {
1✔
1237
        appendNormalizedLineBreak(builder);
1✔
1238
        for (int j = 0; j < indent + 1; j++) {
1✔
1239
          builder.append(indentString);
1✔
1240
        }
1241
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1242

1243
        subIndent = getSubIndent(builder, true);
1✔
1244

1245
        appendExpression(whereCondition, null, builder, subIndent, 0, 1, false,
1✔
1246
            BreakLine.AFTER_FIRST);
1247
      }
1248

1249
      Expression deleteWhereCondition = update.getDeleteWhereCondition();
1✔
1250
      if (deleteWhereCondition != null) {
1✔
1251
        appendNormalizedLineBreak(builder);
1✔
1252
        for (int j = 0; j < indent + 1; j++) {
1✔
1253
          builder.append(indentString);
1✔
1254
        }
1255
        appendKeyWord(builder, outputFormat, "DELETE", "", " ");
1✔
1256
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1257

1258
        subIndent = getSubIndent(builder, true);
1✔
1259

1260
        appendExpression(deleteWhereCondition, null, builder, subIndent, 0, 1, false,
1✔
1261
            BreakLine.AFTER_FIRST);
1262
      }
1263
    }
1264
  }
1✔
1265

1266
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1267
  public static void appendMergeInsert(MergeInsert insert, StringBuilder builder, int indent,
1268
      int i) {
1269
    if (insert != null) {
1✔
1270
      appendNormalizedLineBreak(builder);
1✔
1271
      for (int j = 0; j < indent; j++) {
1✔
1272
        builder.append(indentString);
×
1273
      }
1274
      appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
1275
      appendKeyWord(builder, outputFormat, "NOT", "", " ");
1✔
1276
      appendKeyWord(builder, outputFormat, "MATCHED", "", " ");
1✔
1277
      appendKeyWord(builder, outputFormat, "THEN", "", "\n");
1✔
1278

1279
      for (int j = 0; j < indent + 1; j++) {
1✔
1280
        builder.append(indentString);
1✔
1281
      }
1282
      appendKeyWord(builder, outputFormat, "INSERT", "", " ");
1✔
1283

1284
      List<Column> columns = insert.getColumns();
1✔
1285
      List<Expression> expressions = insert.getValues();
1✔
1286

1287
      int k = i;
1✔
1288
      if (columns != null && !columns.isEmpty()) {
1✔
1289
        builder.append("( ");
1✔
1290
        int subIndent = getSubIndent(builder, false);
1✔
1291

1292
        for (Column column : columns) {
1✔
1293
          appendExpression(column, null, builder, subIndent, k++, columns.size(), true,
1✔
1294
              BreakLine.AFTER_FIRST);
1295
        }
1✔
1296
        appendNormalizingTrailingWhiteSpace(builder, " )\n");
1✔
1297
      }
1298

1299
      if (columns != null && !columns.isEmpty()) {
1✔
1300
        for (int j = 0; j < indent + 1; j++) {
1✔
1301
          builder.append(indentString);
1✔
1302
        }
1303
      }
1304
      appendKeyWord(builder, outputFormat, "VALUES", "", " ( ");
1✔
1305

1306
      int subIndent = getSubIndent(builder, false);
1✔
1307
      if (expressions != null) {
1✔
1308
        int j = 0;
1✔
1309
        for (Expression expression : expressions) {
1✔
1310
          appendExpression(expression, null, builder, subIndent, j++, expressions.size(), true,
1✔
1311
              BreakLine.AFTER_FIRST);
1312
        }
1✔
1313
      }
1314
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
1315

1316
      Expression whereCondition = insert.getWhereCondition();
1✔
1317
      if (whereCondition != null) {
1✔
1318
        appendNormalizedLineBreak(builder);
1✔
1319
        for (int j = 0; j < indent + 1; j++) {
1✔
1320
          builder.append(indentString);
1✔
1321
        }
1322
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1323

1324
        subIndent = getSubIndent(builder, true);
1✔
1325

1326
        appendExpression(whereCondition, null, builder, subIndent, 0, 1, false,
1✔
1327
            BreakLine.AFTER_FIRST);
1328
      }
1329
    }
1330
  }
1✔
1331

1332
  private static void appendInsert(StringBuilder builder, Insert insert, int indent) {
1333
    List<WithItem> withItems = insert.getWithItemsList();
1✔
1334
    if (withItems != null && !withItems.isEmpty()) {
1✔
1335
      int i = 0;
1✔
1336
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1337

1338
      for (WithItem withItem : withItems) {
1✔
1339
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1340
        i++;
1✔
1341
      }
1✔
1342

1343
      appendNormalizedLineBreak(builder);
1✔
1344
    }
1345

1346
    appendKeyWord(builder, outputFormat, "INSERT", "", " ");
1✔
1347
    OracleHint oracleHint = insert.getOracleHint();
1✔
1348
    if (oracleHint != null) {
1✔
1349
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
×
1350
    }
1351
    appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1352

1353
    Table table = insert.getTable();
1✔
1354
    Alias alias = table.getAlias();
1✔
1355

1356
    appendTable(table, alias, builder);
1✔
1357

1358
    List<Column> columns = insert.getColumns();
1✔
1359
    if (columns != null) {
1✔
1360
      int i = 0;
1✔
1361
      builder.append(" (");
1✔
1362
      for (Column column : columns) {
1✔
1363
        appendExpression(column, null, builder, indent + 1, i, columns.size(), true,
1✔
1364
            BreakLine.ALWAYS);
1365
        i++;
1✔
1366
      }
1✔
1367
      appendNormalizingTrailingWhiteSpace(builder, " ) ");
1✔
1368
    }
1369
    appendNormalizedLineBreak(builder);
1✔
1370
    Select select = insert.getSelect();
1✔
1371
    appendSelect(select, builder, indent, false, false);
1✔
1372
  }
1✔
1373

1374
  private static void appendUpdate(StringBuilder builder, Update update, int indent) {
1375
    List<WithItem> withItems = update.getWithItemsList();
1✔
1376
    if (withItems != null && !withItems.isEmpty()) {
1✔
1377
      int i = 0;
1✔
1378
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1379

1380
      for (WithItem withItem : withItems) {
1✔
1381
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1382
        i++;
1✔
1383
      }
1✔
1384

1385
      appendNormalizedLineBreak(builder);
1✔
1386
    }
1387

1388
    appendKeyWord(builder, outputFormat, "UPDATE", "", " ");
1✔
1389

1390
    OracleHint oracleHint = update.getOracleHint();
1✔
1391
    if (oracleHint != null) {
1✔
1392
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
×
1393
    }
1394

1395
    Table table = update.getTable();
1✔
1396
    Alias alias = table.getAlias();
1✔
1397

1398
    appendTable(table, alias, builder);
1✔
1399

1400
    if (update.getStartJoins() != null) {
1✔
1401
      appendJoins(update.getStartJoins(), builder, indent);
1✔
1402
    }
1403

1404
    appendNormalizedLineBreak(builder);
1✔
1405
    for (int j = 0; j < indent; j++) {
1✔
1406
      builder.append(indentString);
×
1407
    }
1408
    appendKeyWord(builder, outputFormat, "SET", "", " ");
1✔
1409

1410
    final int subIndent = getSubIndent(builder, true);
1✔
1411
    appendUpdateSets(builder, update.getUpdateSets(), subIndent);
1✔
1412

1413
    if (update.getFromItem() != null) {
1✔
1414
      appendNormalizedLineBreak(builder);
1✔
1415
      for (int j = 0; j < indent; j++) {
1✔
1416
        builder.append(indentString);
×
1417
      }
1418
      appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
1419
      appendFromItem(update.getFromItem(), builder, indent, 0, 1);
1✔
1420
    }
1421

1422
    List<Join> joins = update.getJoins();
1✔
1423
    appendJoins(joins, builder, indent);
1✔
1424

1425
    Expression whereExpression = update.getWhere();
1✔
1426
    appendWhere(whereExpression, builder, indent);
1✔
1427

1428
    List<OrderByElement> orderByElements = update.getOrderByElements();
1✔
1429
    appendOrderByElements(orderByElements, builder, indent);
1✔
1430
  }
1✔
1431

1432
  private static void appendUpdateSets(StringBuilder builder, List<UpdateSet> updateSets,
1433
      int subIndent) {
1434
    int i = 0;
1✔
1435
    int n = updateSets.size();
1✔
1436
    for (UpdateSet updateSet : updateSets) {
1✔
1437
      if (i > 0) {
1✔
1438
        appendNormalizedLineBreak(builder);
1✔
1439
        for (int j = 0; j < subIndent; j++) {
1✔
1440
          builder.append(indentString);
1✔
1441
        }
1442
      }
1443

1444
      switch (separation) {
1✔
1445
        case BEFORE:
1446
          builder.append(i > 0 ? ", " : "");
1✔
1447
      }
1448

1449
      appendExpressionList(updateSet.getColumns(), builder, subIndent, BreakLine.AFTER_FIRST);
1✔
1450
      appendNormalizingTrailingWhiteSpace(builder, " = ");
1✔
1451
      appendExpressionList(updateSet.getValues(), builder, subIndent, BreakLine.AFTER_FIRST);
1✔
1452

1453
      switch (separation) {
1✔
1454
        case AFTER:
1455
          appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? ", " : "");
1✔
1456
          break;
1457
      }
1458

1459
      i++;
1✔
1460
    }
1✔
1461
  }
1✔
1462

1463
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
1464
  private static void appendSelect(Select select, StringBuilder builder, int indent,
1465
      boolean breakLineBefore, boolean indentFirstLine) {
1466

1467
    List<WithItem> withItems = select.getWithItemsList();
1✔
1468
    if (withItems != null && !withItems.isEmpty()) {
1✔
1469
      int i = 0;
1✔
1470
      if (breakLineBefore) {
1✔
1471
        appendNormalizedLineBreak(builder);
1✔
1472
        for (int j = 0; indentFirstLine && j < indent; j++) {
1✔
1473
          builder.append(indentString);
1✔
1474
        }
1475
      }
1476
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1477

1478
      for (WithItem withItem : withItems) {
1✔
1479
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1480
        i++;
1✔
1481
      }
1✔
1482
    }
1483

1484
    // All Known Implementing Classes: PlainSelect, SetOperationList,
1485
    // ValuesStatement, WithItem
1486
    if (select instanceof PlainSelect) {
1✔
1487
      PlainSelect plainSelect = (PlainSelect) select;
1✔
1488

1489
      int i = 0;
1✔
1490
      if (breakLineBefore || withItems != null && !withItems.isEmpty()) {
1✔
1491
        appendNormalizedLineBreak(builder);
1✔
1492
        for (int j = 0; indentFirstLine && j < indent; j++) {
1✔
1493
          builder.append(indentString);
1✔
1494
        }
1495
      }
1496

1497
      appendKeyWord(builder, outputFormat, "SELECT", "", " ");
1✔
1498

1499
      OracleHint oracleHint = plainSelect.getOracleHint();
1✔
1500
      if (oracleHint != null) {
1✔
1501
        appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
1502
      }
1503

1504
      Top top = plainSelect.getTop();
1✔
1505
      if (top != null) {
1✔
1506
        appendKeyWord(builder, outputFormat, "TOP", "", top.hasParenthesis() ? "( " : " ");
1✔
1507
        appendExpression(top.getExpression(), null, builder, 0, 0, 0, false, BreakLine.AS_NEEDED);
1✔
1508
        if (top.hasParenthesis()) {
1✔
1509
          builder.append(" )");
×
1510
        }
1511
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1512

1513
        if (top.isPercentage()) {
1✔
1514
          appendKeyWord(builder, outputFormat, "PERCENT", "", " ");
×
1515
        }
1516

1517
        if (top.isWithTies()) {
1✔
1518
          appendKeyWord(builder, outputFormat, "WITH TIES", "", " ");
×
1519
        }
1520
      }
1521

1522
      Distinct distinct = plainSelect.getDistinct();
1✔
1523
      if (distinct != null) {
1✔
1524
        if (distinct.isUseUnique()) {
1✔
1525
          // @todo: implement Use Unique Distinct clause
1526
          throw new UnsupportedOperationException("Unique DISTINCT not supported yet.");
×
1527
        }
1528

1529
        if (distinct.getOnSelectItems() != null && !distinct.getOnSelectItems().isEmpty()) {
1✔
1530
          // @todo: implement Use Unique Distinct clause
1531
          throw new UnsupportedOperationException(
×
1532
              "DISTINCT on select items are not supported yet.");
1533
        }
1534
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
1✔
1535
      }
1536

1537
      int subIndent = oracleHint != null || distinct != null || top != null ? indent + 1
1✔
1538
          : getSubIndent(builder, plainSelect.getSelectItems().size() > 1);
1✔
1539
      BreakLine bl = oracleHint != null || distinct != null || top != null ? BreakLine.ALWAYS
1✔
1540
          : BreakLine.AFTER_FIRST;
1✔
1541

1542
      List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
1✔
1543
      appendSelectItemList(selectItems, builder, subIndent, i, bl, indent);
1✔
1544

1545
      FromItem fromItem = plainSelect.getFromItem();
1✔
1546
      if (fromItem != null) {
1✔
1547
        appendNormalizedLineBreak(builder);
1✔
1548
        for (int j = 0; j < indent; j++) {
1✔
1549
          builder.append(indentString);
1✔
1550
        }
1551
        appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
1552
        appendFromItem(fromItem, builder, indent, i, 1);
1✔
1553

1554
        List<Join> joins = plainSelect.getJoins();
1✔
1555
        appendJoins(joins, builder, indent);
1✔
1556
      }
1557

1558
      Expression whereExpression = plainSelect.getWhere();
1✔
1559
      appendWhere(whereExpression, builder, indent);
1✔
1560

1561
      GroupByElement groupByElement = plainSelect.getGroupBy();
1✔
1562
      appendGroupByElement(groupByElement, builder, indent);
1✔
1563

1564
      Expression havingExpression = plainSelect.getHaving();
1✔
1565
      appendHavingExpression(havingExpression, builder, indent);
1✔
1566

1567
      // @todo: write-out Qualify
1568
      if (plainSelect.getQualify() != null) {
1✔
1569
        builder.append(" QUALIFY ");
×
1570
        builder.append(plainSelect.getQualify());
×
1571
      }
1572

1573
      // @todo: write-out Windows
1574
      if (plainSelect.getWindowDefinitions() != null) {
1✔
1575
        appendNormalizedLineBreak(builder);
1✔
1576
        for (int j = 0; j < indent; j++) {
1✔
1577
          builder.append(indentString);
1✔
1578
        }
1579
        builder.append("WINDOW ");
1✔
1580
        int k = 0;
1✔
1581
        for (WindowDefinition w : plainSelect.getWindowDefinitions()) {
1✔
1582
          appendNormalizedLineBreak(builder);
1✔
1583
          for (int j = 0; j < indent + 1; j++) {
1✔
1584
            builder.append(indentString);
1✔
1585
          }
1586
          if (k++ > 0) {
1✔
1587
            builder.append(", ");
×
1588
          }
1589
          if (w.getWindowName() != null) {
1✔
1590
            appendObjectName(builder, outputFormat, w.getWindowName(), "", " ");
1✔
1591
            appendKeyWord(builder, outputFormat, "AS", "", " (");
1✔
1592
          } else {
1593
            builder.append("(");
×
1594
          }
1595

1596
          appendNormalizedLineBreak(builder);
1✔
1597
          for (int j = 0; j < indent + 2; j++) {
1✔
1598
            builder.append(indentString);
1✔
1599
          }
1600
          w.getPartitionBy().toStringPartitionBy(builder);
1✔
1601

1602
          appendNormalizedLineBreak(builder);
1✔
1603
          for (int j = 0; j < indent + 2; j++) {
1✔
1604
            builder.append(indentString);
1✔
1605
          }
1606
          w.getOrderBy().toStringOrderByElements(builder);
1✔
1607

1608
          if (w.getWindowElement() != null) {
1✔
1609
            if (w.getOrderBy().getOrderByElements() != null) {
1✔
1610
              appendNormalizedLineBreak(builder);
1✔
1611
              for (int j = 0; j < indent + 2; j++) {
1✔
1612
                builder.append(indentString);
1✔
1613
              }
1614
            }
1615
            appendKeyWord(builder, outputFormat, w.getWindowElement().toString(), "", "");
1✔
1616
          }
1617

1618
          builder.append(" )");
1✔
1619
        }
1✔
1620
      }
1621

1622
      // @todo: write-out FOR MODE
1623
      if (plainSelect.getForMode() != null) {
1✔
1624
        builder.append(" FOR ");
×
1625
        builder.append(plainSelect.getForMode().getValue());
×
1626

1627
        if (plainSelect.getForUpdateTable() != null) {
×
1628
          builder.append(" OF ").append(plainSelect.getForUpdateTable());
×
1629
        }
1630
        if (plainSelect.getWait() != null) {
×
1631
          // wait's toString will do the formatting for us
1632
          builder.append(plainSelect.getWait());
×
1633
        }
1634
        if (plainSelect.isNoWait()) {
×
1635
          builder.append(" NOWAIT");
×
1636
        } else if (plainSelect.isSkipLocked()) {
×
1637
          builder.append(" SKIP LOCKED");
×
1638
        }
1639
      }
1640

1641
      // @todo: write-out FOR CLAUSE
1642
      if (plainSelect.getForClause() != null) {
1✔
1643
        plainSelect.getForClause().appendTo(builder);
×
1644
      }
1645

1646
      // @todo: write-out EMIT CHANGES
1647
      if (plainSelect.isEmitChanges()) {
1✔
1648
        builder.append(" EMIT CHANGES");
×
1649
      }
1650

1651
      // @todo: write-out LIMIT BY
1652
      if (plainSelect.getLimitBy() != null) {
1✔
1653
        builder.append(plainSelect.getLimitBy());
×
1654
      }
1655

1656
      List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
1✔
1657
      appendOrderByElements(orderByElements, builder, indent);
1✔
1658

1659
      Limit limit = plainSelect.getLimit();
1✔
1660
      if (limit != null) {
1✔
1661
        appendNormalizedLineBreak(builder);
1✔
1662
        for (int j = 0; j < indent; j++) {
1✔
1663
          builder.append(indentString);
1✔
1664
        }
1665
        appendKeyWord(builder, outputFormat, "LIMIT", "", " ");
1✔
1666

1667
        Expression rowCount = limit.getRowCount();
1✔
1668
        if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
1✔
1669
          // no offset allowed
1670
          appendKeyWord(builder, outputFormat, "NULL", "", " ");
×
1671
        } else {
1672
          if (null != limit.getOffset()) {
1✔
1673
            appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false,
1✔
1674
                BreakLine.NEVER);
1675
            builder.append(", ");
1✔
1676
          }
1677
          if (null != limit.getRowCount()) {
1✔
1678
            appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
1✔
1679
                BreakLine.NEVER);
1680
          }
1681
        }
1682
      }
1683

1684
      Offset offset = plainSelect.getOffset();
1✔
1685
      if (offset != null) {
1✔
1686
        appendNormalizedLineBreak(builder);
1✔
1687
        for (int j = 0; j < indent; j++) {
1✔
1688
          builder.append(indentString);
×
1689
        }
1690
        appendKeyWord(builder, outputFormat, "OFFSET", "", " ");
1✔
1691

1692
        Expression offsetExpression = offset.getOffset();
1✔
1693
        appendExpression(offsetExpression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1694

1695
        String offsetParam = offset.getOffsetParam();
1✔
1696
        if (offsetParam != null) {
1✔
1697
          appendString(offsetParam, builder, indent, 0, 1, false, BreakLine.NEVER);
×
1698
        }
1699
      }
1700

1701
      Fetch fetch = plainSelect.getFetch();
1✔
1702
      if (fetch != null) {
1✔
1703
        appendNormalizedLineBreak(builder);
1✔
1704
        for (int j = 0; j < indent; j++) {
1✔
1705
          builder.append(indentString);
×
1706
        }
1707
        appendKeyWord(builder, outputFormat, "FETCH", "", "");
1✔
1708

1709
        if (fetch.isFetchParamFirst()) {
1✔
1710
          appendKeyWord(builder, outputFormat, "FIRST", " ", "");
1✔
1711
        } else {
1712
          appendKeyWord(builder, outputFormat, "NEXT", " ", "");
1✔
1713
        }
1714

1715
        Expression expression = fetch.getExpression();
1✔
1716
        if (expression != null) {
1✔
1717
          appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1718
          appendExpression(expression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1719
        }
1720

1721
        for (String s : fetch.getFetchParameters()) {
1✔
1722
          appendKeyWord(builder, outputFormat, s, " ", "");
1✔
1723
        }
1✔
1724

1725
        // @todo: write-out ISOLATION
1726
        if (plainSelect.getIsolation() != null) {
1✔
1727
          builder.append(plainSelect.getIsolation());
×
1728
        }
1729

1730
        // @todo: write-out Optimizer For
1731
        if (plainSelect.getOptimizeFor() != null) {
1✔
1732
          builder.append(plainSelect.getOptimizeFor());
×
1733
        }
1734

1735
        // @todo: write-out FOR XML PATH
1736
        if (plainSelect.getForXmlPath() != null) {
1✔
1737
          builder.append(" FOR XML PATH(").append(plainSelect.getForXmlPath()).append(")");
×
1738
        }
1739

1740
        // @todo: write-out INTO TEMP Table
1741
        if (plainSelect.getIntoTempTable() != null) {
1✔
1742
          builder.append(" INTO TEMP ").append(plainSelect.getIntoTempTable());
×
1743
        }
1744

1745
        // @todo: write-out WITH NO LOG
1746
        if (plainSelect.isUseWithNoLog()) {
1✔
1747
          builder.append(" WITH NO LOG");
×
1748
        }
1749
      }
1750

1751
    } else if (select instanceof SetOperationList) {
1✔
1752
      SetOperationList setOperationList = (SetOperationList) select;
1✔
1753

1754
      List<SetOperation> setOperations = setOperationList.getOperations();
1✔
1755

1756
      int k = 0;
1✔
1757

1758
      List<Select> selects = setOperationList.getSelects();
1✔
1759
      if (selects != null && !selects.isEmpty()) {
1✔
1760
        for (Select selectBody1 : selects) {
1✔
1761
          if (k > 0 && setOperations != null && setOperations.size() >= k) {
1✔
1762
            SetOperation setOperation = setOperations.get(k - 1);
1✔
1763
            appendSetOperation(setOperation, builder, indent);
1✔
1764
          }
1765
          int subIndent = indent;
1✔
1766
          appendSelect(selectBody1, builder, subIndent, k > 0 || breakLineBefore,
1✔
1767
              k > 0 || breakLineBefore || indentFirstLine);
1768
          k++;
1✔
1769
        }
1✔
1770
      }
1771

1772
      List<OrderByElement> orderByElements = setOperationList.getOrderByElements();
1✔
1773
      appendOrderByElements(orderByElements, builder, indent);
1✔
1774

1775
      Limit limit = setOperationList.getLimit();
1✔
1776
      if (limit != null) {
1✔
1777
        appendNormalizedLineBreak(builder);
1✔
1778
        for (int j = 0; j < indent; j++) {
1✔
1779
          builder.append(indentString);
×
1780
        }
1781
        appendKeyWord(builder, outputFormat, "LIMIT", "", " ");
1✔
1782

1783
        Expression rowCount = limit.getRowCount();
1✔
1784
        if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
1✔
1785
          // no offset allowed
1786
          appendKeyWord(builder, outputFormat, "NULL", "", " ");
×
1787
        } else {
1788
          if (null != limit.getOffset()) {
1✔
1789
            appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false,
×
1790
                BreakLine.NEVER);
1791
            builder.append(", ");
×
1792
          }
1793
          if (null != limit.getRowCount()) {
1✔
1794
            appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
1✔
1795
                BreakLine.NEVER);
1796
          }
1797
        }
1798
      }
1799

1800
      Offset offset = setOperationList.getOffset();
1✔
1801
      if (offset != null) {
1✔
1802
        appendNormalizedLineBreak(builder);
1✔
1803
        for (int j = 0; j < indent; j++) {
1✔
1804
          builder.append(indentString);
×
1805
        }
1806
        appendKeyWord(builder, outputFormat, "OFFSET", "", " ");
1✔
1807

1808
        Expression offsetExpression = offset.getOffset();
1✔
1809
        appendExpression(offsetExpression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1810

1811
        String offsetParam = offset.getOffsetParam();
1✔
1812
        if (offsetParam != null) {
1✔
1813
          appendString(offsetParam, builder, indent, 0, 1, false, BreakLine.NEVER);
×
1814
        }
1815
      }
1816

1817
    } else if (select instanceof Values) {
1✔
1818
      Values values = (Values) select;
1✔
1819

1820
      if (breakLineBefore) {
1✔
1821
        appendNormalizedLineBreak(builder);
×
1822
        for (int j = 0; indentFirstLine && j < indent; j++) {
×
1823
          builder.append(indentString);
×
1824
        }
1825
      }
1826

1827
      appendKeyWord(builder, outputFormat, "VALUES", "", " ");
1✔
1828
      appendExpressionList(values.getExpressions(), builder, indent, BreakLine.AS_NEEDED);
1✔
1829

1830
    } else if (select instanceof ParenthesedSelect) {
1✔
1831
      ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
1✔
1832
      builder.append("( ");
1✔
1833
      int subIndent = breakLineBefore ? indent + 1
1✔
1834
          : getSubIndent(builder, !(parenthesedSelect.getSelect() instanceof Values));
1✔
1835

1836
      appendSelect(parenthesedSelect.getSelect(), builder, subIndent, breakLineBefore,
1✔
1837
          indentFirstLine);
1838
      builder.append(" )");
1✔
1839
    }
1840
  }
1✔
1841

1842
  public static void appendSelectItemList(List<SelectItem<?>> selectItems, StringBuilder builder,
1843
      int subIndent, int i, BreakLine bl, int indent) throws UnsupportedOperationException {
1844
    int j = i;
1✔
1845
    for (SelectItem<?> selectItem : selectItems) {
1✔
1846
      Alias alias = selectItem.getAlias();
1✔
1847
      Expression expression = selectItem.getExpression();
1✔
1848

1849
      appendExpression(expression, alias, builder, subIndent, j++, selectItems.size(), true, bl);
1✔
1850
    }
1✔
1851
  }
1✔
1852

1853
  public static void appendColumnSelectItemList(List<SelectItem<Column>> selectItems,
1854
      StringBuilder builder, int subIndent, int i, BreakLine bl, int indent)
1855
      throws UnsupportedOperationException {
1856
    int j = i;
×
1857
    for (SelectItem<?> selectItem : selectItems) {
×
1858
      Alias alias = selectItem.getAlias();
×
1859
      Expression expression = selectItem.getExpression();
×
1860

1861
      appendExpression(expression, alias, builder, subIndent, j++, selectItems.size(), true, bl);
×
1862
    }
×
1863
  }
×
1864

1865
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1866
  private static void appendOrderByElements(List<OrderByElement> orderByElements,
1867
      StringBuilder builder, int indent) {
1868
    if (orderByElements != null) {
1✔
1869
      int i = 0;
1✔
1870
      appendNormalizedLineBreak(builder);
1✔
1871
      for (int j = 0; j < indent; j++) {
1✔
1872
        builder.append(indentString);
1✔
1873
      }
1874
      appendKeyWord(builder, outputFormat, "ORDER BY", "", " ");
1✔
1875

1876
      int subIndent = getSubIndent(builder, orderByElements.size() > 1);
1✔
1877

1878
      for (OrderByElement orderByElement : orderByElements) {
1✔
1879
        Expression expression = orderByElement.getExpression();
1✔
1880
        appendExpression(expression, null, builder, subIndent, i, orderByElements.size(), true,
1✔
1881
            BreakLine.AFTER_FIRST);
1882

1883
        if (orderByElement.isAscDescPresent()) {
1✔
1884
          builder.append(" ");
1✔
1885

1886
          if (orderByElement.isAsc()) {
1✔
1887
            appendKeyWord(builder, outputFormat, "ASC", "", " ");
1✔
1888
          } else {
1889
            appendKeyWord(builder, outputFormat, "DESC", "", " ");
1✔
1890
          }
1891
        }
1892

1893
        NullOrdering nullOrdering = orderByElement.getNullOrdering();
1✔
1894
        if (NullOrdering.NULLS_FIRST.equals(nullOrdering)) {
1✔
1895
          appendKeyWord(builder, outputFormat, "NULLS FIRST",
1✔
1896
              orderByElement.isAscDescPresent() ? "" : " ", " ");
1✔
1897
        }
1898

1899
        if (NullOrdering.NULLS_LAST.equals(nullOrdering)) {
1✔
1900
          appendKeyWord(builder, outputFormat, "NULLS LAST",
×
1901
              orderByElement.isAscDescPresent() ? "" : " ", " ");
×
1902
        }
1903

1904
        i++;
1✔
1905
      }
1✔
1906
    }
1907
  }
1✔
1908

1909
  private static void appendHavingExpression(Expression havingExpression, StringBuilder builder,
1910
      int indent) {
1911
    if (havingExpression != null) {
1✔
1912
      appendNormalizedLineBreak(builder);
1✔
1913
      for (int j = 0; j < indent; j++) {
1✔
1914
        builder.append(indentString);
×
1915
      }
1916
      appendKeyWord(builder, outputFormat, "HAVING", "", " ");
1✔
1917
      appendExpression(havingExpression, null, builder, indent, 0, 1, false, BreakLine.AFTER_FIRST);
1✔
1918
    }
1919
  }
1✔
1920

1921
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1922
  private static void appendGroupByElement(GroupByElement groupByElement, StringBuilder builder,
1923
      int indent) throws UnsupportedOperationException {
1924
    int i = 0;
1✔
1925
    if (groupByElement != null) {
1✔
1926
      appendNormalizedLineBreak(builder);
1✔
1927
      for (int j = 0; j < indent; j++) {
1✔
1928
        builder.append(indentString);
1✔
1929
      }
1930
      appendKeyWord(builder, outputFormat, "GROUP BY", "", " ");
1✔
1931

1932
      List<?> groupingSets = groupByElement.getGroupingSets();
1✔
1933
      ExpressionList<?> groupByExpressions = groupByElement.getGroupByExpressionList();
1✔
1934

1935
      if (groupingSets != null && !groupingSets.isEmpty()) {
1✔
1936
        throw new UnsupportedOperationException("Grouping Sets are not supported yet.");
×
1937
      }
1938

1939
      if (groupByExpressions != null && !groupByExpressions.isEmpty()) {
1✔
1940
        BreakLine breakLine = BreakLine.AFTER_FIRST;
1✔
1941
        int size = groupByExpressions.size();
1✔
1942
        int subIndent = getSubIndent(builder, size > 1);
1✔
1943

1944
        for (Expression expression : groupByExpressions) {
1✔
1945
          switch (breakLine) {
1✔
1946
            case AS_NEEDED:
1947
              BreakLine bl =
1948
                  size == 4 || size >= 5 && i % 3 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
×
1949
              appendExpression(expression, null, builder, subIndent, i, size, true, bl);
×
1950
              break;
×
1951

1952
            default:
1953
              appendExpression(expression, null, builder, subIndent, i, size, true, breakLine);
1✔
1954
          }
1955
          i++;
1✔
1956
        }
1✔
1957
      }
1958
    }
1959
  }
1✔
1960

1961
  private static void appendWhere(Expression whereExpression, StringBuilder builder, int indent) {
1962
    if (whereExpression != null) {
1✔
1963
      appendNormalizedLineBreak(builder);
1✔
1964
      for (int j = 0; j < indent; j++) {
1✔
1965
        builder.append(indentString);
1✔
1966
      }
1967
      appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1968
      appendExpression(whereExpression, null, builder, indent, 0, 1, false, BreakLine.AFTER_FIRST);
1✔
1969
    }
1970
  }
1✔
1971

1972
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1973
  private static void appendJoins(List<Join> joins, StringBuilder builder, int indent) {
1974
    if (joins != null) {
1✔
1975
      for (Join join : joins) {
1✔
1976

1977
        if (join.isSimple()) {
1✔
1978
          switch (separation) {
1✔
1979
            case AFTER:
1980
              builder.append(",");
1✔
1981
          }
1982
        }
1983
        appendNormalizedLineBreak(builder);
1✔
1984

1985
        if (join.isSimple()) {
1✔
1986
          for (int j = 0; j <= indent; j++) {
1✔
1987
            builder.append(indentString);
1✔
1988
          }
1989

1990
          switch (separation) {
1✔
1991
            case BEFORE:
1992
              builder.append(", ");
1✔
1993
          }
1994

1995
        } else {
1996
          for (int j = 0; j <= indent; j++) {
1✔
1997
            builder.append(indentString);
1✔
1998
          }
1999

2000
          if (join.isInner()) {
1✔
2001
            appendKeyWord(builder, outputFormat, "INNER", "", " ");
1✔
2002
          }
2003
          if (join.isLeft()) {
1✔
2004
            appendKeyWord(builder, outputFormat, "LEFT", "", " ");
1✔
2005
          }
2006
          if (join.isRight()) {
1✔
2007
            appendKeyWord(builder, outputFormat, "RIGHT", "", " ");
×
2008
          }
2009
          if (join.isNatural()) {
1✔
2010
            appendKeyWord(builder, outputFormat, "NATURAL", "", " ");
×
2011
          }
2012
          if (join.isCross()) {
1✔
2013
            appendKeyWord(builder, outputFormat, "CROSS", "", " ");
×
2014
          }
2015
          if (join.isOuter()) {
1✔
2016
            appendKeyWord(builder, outputFormat, "OUTER", "", " ");
1✔
2017
          }
2018
          if (join.isFull()) {
1✔
2019
            appendKeyWord(builder, outputFormat, "FULL", "", " ");
×
2020
          }
2021

2022
          appendKeyWord(builder, outputFormat, "JOIN", "", " ");
1✔
2023
        }
2024

2025
        FromItem rightFromItem = join.getRightItem();
1✔
2026
        appendFromItem(rightFromItem, builder, indent, 0, 1);
1✔
2027

2028
        for (Expression onExpression : join.getOnExpressions()) {
1✔
2029
          if (onExpression != null) {
1✔
2030
            appendNormalizedLineBreak(builder);
1✔
2031
            for (int j = 0; j <= indent + 1; j++) {
1✔
2032
              builder.append(indentString);
1✔
2033
            }
2034
            appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
2035

2036
            appendExpression(onExpression, null, builder, indent + 2, 0, 1, false,
1✔
2037
                BreakLine.AFTER_FIRST);
2038
          }
2039
        }
1✔
2040

2041
        List<Column> usingColumns = join.getUsingColumns();
1✔
2042
        if (usingColumns != null && !usingColumns.isEmpty()) {
1✔
2043
          appendNormalizedLineBreak(builder);
×
2044
          for (int j = 0; j <= indent + 2; j++) {
×
2045
            builder.append(indentString);
×
2046
          }
2047
          appendKeyWord(builder, outputFormat, "USING", "", " ( ");
×
2048
          int k = 0;
×
2049
          for (Column column : usingColumns) {
×
2050
            appendExpression(column, null, builder, indent + 3, k, usingColumns.size(), true,
×
2051
                BreakLine.AFTER_FIRST);
2052
            k++;
×
2053
          }
×
2054
          appendNormalizingTrailingWhiteSpace(builder, " )");
×
2055
        }
2056
      }
1✔
2057
    }
2058
  }
1✔
2059

2060
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2061
  private static void appendWithItem(WithItem withItem, StringBuilder builder, int indent, int i,
2062
      int n) {
2063

2064
    if (i > 0) {
1✔
2065
      appendNormalizedLineBreak(builder);
1✔
2066
      for (int j = 0; j <= indent; j++) {
1✔
2067
        builder.append(indentString);
1✔
2068
      }
2069
    }
2070

2071
    switch (separation) {
1✔
2072
      case BEFORE:
2073
        appendAlias(builder, outputFormat, withItem.getAlias().getName(), i > 0 ? ", " : "", " ");
1✔
2074
        break;
1✔
2075
      default:
2076
        appendAlias(builder, outputFormat, withItem.getAlias().getName(), "", " ");
1✔
2077
    }
2078

2079
    List<SelectItem<?>> selectItems = withItem.getWithItemList();
1✔
2080
    if (selectItems != null && !selectItems.isEmpty()) {
1✔
2081
      builder.append("( ");
1✔
2082

2083
      int subIndent = getSubIndent(builder, selectItems.size() > 2);
1✔
2084
      BreakLine bl = selectItems.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
2085

2086
      appendSelectItemList(selectItems, builder, subIndent, i, bl, indent);
1✔
2087
      builder.append(" ) ");
1✔
2088

2089
      appendNormalizedLineBreak(builder);
1✔
2090
      for (int j = 0; j < indent + 1; j++) {
1✔
2091
        builder.append(indentString);
1✔
2092
      }
2093
      appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2094
      appendSelect(withItem.getSelect(), builder, indent + 1, false, false);
1✔
2095

2096
    } else {
1✔
2097
      appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2098
      appendSelect(withItem.getSelect(), builder, indent + 1, true, true);
1✔
2099
    }
2100

2101
    switch (separation) {
1✔
2102
      case AFTER:
2103
        appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? "," : "");
1✔
2104
        break;
1✔
2105
      case BEFORE:
2106
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2107
        break;
2108
    }
2109
  }
1✔
2110

2111
  private static void appendRowConstructor(StringBuilder builder, int indent,
2112
      RowConstructor<?> rowConstructor) {
2113

2114
    if (rowConstructor.getName() != null) {
×
2115
      appendAlias(builder, outputFormat, rowConstructor.getName(), "", "");
×
2116
    }
2117

2118
    appendExpressionList(rowConstructor, builder, indent, BreakLine.AS_NEEDED);
×
2119
  }
×
2120

2121
  private static void appendStringList(Collection<String> strings, StringBuilder builder,
2122
      int indent, boolean commaSeparated, BreakLine breakLine) {
2123
    int i = 0;
1✔
2124
    if (strings != null) {
1✔
2125
      for (String s : strings) {
1✔
2126
        appendString(s, builder, indent, i, strings.size(), commaSeparated, breakLine);
1✔
2127
        i++;
1✔
2128
      }
1✔
2129
    }
2130
  }
1✔
2131

2132
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2133
  private static void appendString(String s, StringBuilder builder, int indent, int i, int n,
2134
      boolean commaSeparated, BreakLine breakLine) {
2135

2136
    if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2137
      appendNormalizedLineBreak(builder);
1✔
2138
      for (int j = 0; j < indent; j++) {
1✔
2139
        builder.append(indentString);
1✔
2140
      }
2141
    }
2142

2143
    switch (separation) {
1✔
2144
      case AFTER:
2145
        appendObjectName(builder, outputFormat, s, "", "");
1✔
2146
        appendNormalizingTrailingWhiteSpace(builder, commaSeparated && i < n - 1 ? ", " : "");
1✔
2147
        break;
1✔
2148
      case BEFORE:
2149
        builder.append(commaSeparated && i > 0 ? ", " : "");
1✔
2150
        appendObjectName(builder, outputFormat, s, "", "");
1✔
2151
    }
2152
  }
1✔
2153

2154
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
2155
  private static void appendExpression(Expression expression, Alias alias, StringBuilder builder,
2156
      int indent, final int i, final int n, boolean commaSeparated, BreakLine breakLine) {
2157

2158
    if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2159
      appendNormalizedLineBreak(builder);
1✔
2160
      for (int j = 0; j < indent; j++) {
1✔
2161
        builder.append(indentString);
1✔
2162
      }
2163
    }
2164

2165
    switch (separation) {
1✔
2166
      case BEFORE:
2167
        builder.append(commaSeparated && i > 0 ? ", " : "");
1✔
2168
    }
2169

2170
    if (expression instanceof Column) {
1✔
2171
      Column column = (Column) expression;
1✔
2172
      appendObjectName(builder, outputFormat, column.getFullyQualifiedName(), "", "");
1✔
2173

2174
    } else if (expression instanceof AndExpression) {
1✔
2175
      AndExpression andExpression = (AndExpression) expression;
1✔
2176
      appendExpression(andExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2177
          BreakLine.AFTER_FIRST);
2178

2179
      appendNormalizedLineBreak(builder);
1✔
2180
      for (int j = 0; j <= indent; j++) {
1✔
2181
        builder.append(indentString);
1✔
2182
      }
2183
      appendOperator(builder, outputFormat, "AND", "", " ");
1✔
2184

2185
      appendExpression(andExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2186
          BreakLine.AFTER_FIRST);
2187

2188
    } else if (expression instanceof OrExpression) {
1✔
2189
      OrExpression orExpression = (OrExpression) expression;
1✔
2190
      appendExpression(orExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2191
          BreakLine.AFTER_FIRST);
2192

2193
      appendNormalizedLineBreak(builder);
1✔
2194
      for (int j = 0; j <= indent; j++) {
1✔
2195
        builder.append(indentString);
1✔
2196
      }
2197
      appendOperator(builder, outputFormat, "OR", "", " ");
1✔
2198

2199
      appendExpression(orExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2200
          BreakLine.AFTER_FIRST);
2201

2202
    } else if (expression instanceof EqualsTo) {
1✔
2203
      EqualsTo equalsTo = (EqualsTo) expression;
1✔
2204

2205
      if (equalsTo.getOraclePriorPosition() == EqualsTo.ORACLE_PRIOR_START) {
1✔
2206
        appendOperator(builder, outputFormat, "PRIOR", "", " ");
×
2207
      }
2208

2209
      appendExpression(equalsTo.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2210
          BreakLine.AS_NEEDED);
2211

2212
      if (equalsTo.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_RIGHT) {
1✔
2213
        appendOperator(builder, outputFormat, "(+)", "", " ");
1✔
2214
      }
2215

2216
      appendOperator(builder, outputFormat, "=", " ", " ");
1✔
2217

2218
      if (equalsTo.getOraclePriorPosition() == EqualsTo.ORACLE_PRIOR_END) {
1✔
2219
        appendOperator(builder, outputFormat, "PRIOR", "", " ");
×
2220
      }
2221

2222
      appendExpression(equalsTo.getRightExpression(), alias, builder, indent + 1, i, n, false,
1✔
2223
          BreakLine.AFTER_FIRST);
2224

2225
      if (equalsTo.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_LEFT) {
1✔
2226
        appendOperator(builder, outputFormat, "(+)", "", " ");
1✔
2227
      }
2228

2229
    } else if (expression instanceof CaseExpression) {
1✔
2230
      int subIndent = getSubIndent(builder, false);
1✔
2231

2232
      CaseExpression caseExpression = (CaseExpression) expression;
1✔
2233
      appendKeyWord(builder, outputFormat, "CASE", "", " ");
1✔
2234
      if (caseExpression.getSwitchExpression() != null) {
1✔
2235
        appendExpression(caseExpression.getSwitchExpression(), null, builder, indent + 1, i, n,
1✔
2236
            false, BreakLine.NEVER);
2237
      }
2238

2239
      List<WhenClause> whenClauses = caseExpression.getWhenClauses();
1✔
2240
      for (WhenClause whenClause : whenClauses) {
1✔
2241
        appendNormalizedLineBreak(builder);
1✔
2242
        for (int j = 0; j < subIndent; j++) {
1✔
2243
          builder.append(indentString);
1✔
2244
        }
2245
        appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
2246
        appendExpression(whenClause.getWhenExpression(), null, builder, subIndent + 1, 0, 1, false,
1✔
2247
            BreakLine.AFTER_FIRST);
2248

2249
        appendNormalizedLineBreak(builder);
1✔
2250
        for (int j = 0; j < subIndent + 1; j++) {
1✔
2251
          builder.append(indentString);
1✔
2252
        }
2253
        appendKeyWord(builder, outputFormat, "THEN", "", " ");
1✔
2254
        appendExpression(whenClause.getThenExpression(), null, builder, subIndent + 1, 0, 1, false,
1✔
2255
            BreakLine.AFTER_FIRST);
2256
      }
1✔
2257

2258
      Expression elseExpression = caseExpression.getElseExpression();
1✔
2259
      if (elseExpression != null) {
1✔
2260
        appendNormalizedLineBreak(builder);
1✔
2261
        for (int j = 0; j < subIndent; j++) {
1✔
2262
          builder.append(indentString);
1✔
2263
        }
2264
        appendKeyWord(builder, outputFormat, "ELSE", "", " ");
1✔
2265
        appendExpression(elseExpression, null, builder, subIndent + 1, 0, 1, false,
1✔
2266
            BreakLine.AFTER_FIRST);
2267
      }
2268

2269
      appendNormalizedLineBreak(builder);
1✔
2270
      for (int j = 0; j < subIndent; j++) {
1✔
2271
        builder.append(indentString);
1✔
2272
      }
2273
      appendKeyWord(builder, outputFormat, "END", "", "");
1✔
2274

2275
    } else if (expression instanceof StringValue) {
1✔
2276
      StringValue stringValue = (StringValue) expression;
1✔
2277
      appendValue(builder, outputFormat, stringValue.toString(), "", "");
1✔
2278

2279
    } else if (expression instanceof LongValue) {
1✔
2280
      LongValue longValue = (LongValue) expression;
1✔
2281
      appendValue(builder, outputFormat, longValue.toString(), "", "");
1✔
2282

2283
    } else if (expression instanceof DateValue) {
1✔
2284
      DateValue dateValue = (DateValue) expression;
1✔
2285
      appendValue(builder, outputFormat, dateValue.toString(), "", "");
1✔
2286

2287
    } else if (expression instanceof DoubleValue) {
1✔
2288
      DoubleValue doubleValue = (DoubleValue) expression;
1✔
2289
      appendValue(builder, outputFormat, doubleValue.toString(), "", "");
1✔
2290

2291
    } else if (expression instanceof NotExpression) {
1✔
2292
      NotExpression notExpression = (NotExpression) expression;
1✔
2293
      if (notExpression.isExclamationMark()) {
1✔
2294
        appendOperator(builder, outputFormat, "!", "", "");
×
2295
      } else {
2296
        appendOperator(builder, outputFormat, "NOT", "", " ");
1✔
2297
      }
2298

2299
      appendExpression(notExpression.getExpression(), null, builder, indent + 1, i, n, false,
1✔
2300
          BreakLine.AFTER_FIRST);
2301

2302
    } else if (expression instanceof LikeExpression) {
1✔
2303
      LikeExpression likeExpression = (LikeExpression) expression;
1✔
2304

2305
      appendExpression(likeExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2306
          BreakLine.AFTER_FIRST);
2307

2308
      if (likeExpression.isNot()) {
1✔
2309
        appendOperator(builder, outputFormat, "NOT", " ", "");
1✔
2310
      }
2311

2312
      appendOperator(builder, outputFormat, "LIKE", " ", " ");
1✔
2313

2314
      appendExpression(likeExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2315
          BreakLine.AFTER_FIRST);
2316

2317
      Expression escapeExpression = likeExpression.getEscape();
1✔
2318
      if (escapeExpression != null) {
1✔
2319
        appendOperator(builder, outputFormat, "ESCAPE", " ", " ");
×
2320
        appendExpression(escapeExpression, null, builder, indent + 1, i, n, false,
×
2321
            BreakLine.AS_NEEDED);
2322
      }
2323

2324
    } else if (expression instanceof NextValExpression) {
1✔
2325
      NextValExpression nextValExpression = (NextValExpression) expression;
×
2326
      if (nextValExpression.isUsingNextValueFor()) {
×
2327
        appendOperator(builder, outputFormat, "NEXT VALUE FOR", "", " ");
×
2328
      } else {
2329
        appendOperator(builder, outputFormat, "NEXTVAL FOR", "", " ");
×
2330
      }
2331

2332
      int j = 0;
×
2333
      for (String name : nextValExpression.getNameList()) {
×
2334
        if (j > 0) {
×
2335
          builder.append(".");
×
2336
        }
2337
        appendObjectName(builder, outputFormat, name, "", "");
×
2338
        j++;
×
2339
      }
×
2340

2341
    } else if (expression instanceof ExistsExpression) {
1✔
2342
      ExistsExpression existsExpression = (ExistsExpression) expression;
1✔
2343
      if (existsExpression.isNot()) {
1✔
2344
        appendOperator(builder, outputFormat, "NOT EXISTS", "", "");
×
2345
      } else {
2346
        appendOperator(builder, outputFormat, "EXISTS", "", " ");
1✔
2347
      }
2348

2349
      appendExpression(existsExpression.getRightExpression(), null, builder, indent + 1, i, n,
1✔
2350
          false, BreakLine.AFTER_FIRST);
2351

2352
    } else if (expression instanceof ExtractExpression) {
1✔
2353
      ExtractExpression extractExpression = (ExtractExpression) expression;
1✔
2354

2355
      // "EXTRACT(" + name + " FROM " + expression + ')';
2356

2357
      appendKeyWord(builder, outputFormat, "EXTRACT", "", "( ");
1✔
2358
      appendValue(builder, outputFormat, extractExpression.getName(), "", "");
1✔
2359
      appendKeyWord(builder, outputFormat, "FROM", " ", " ");
1✔
2360

2361
      appendExpression(extractExpression.getExpression(), null, builder, indent + 1, i, n, false,
1✔
2362
          BreakLine.AFTER_FIRST);
2363
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2364

2365
    } else if (expression instanceof JdbcNamedParameter) {
1✔
2366
      JdbcNamedParameter jdbcNamedParameter = (JdbcNamedParameter) expression;
1✔
2367
      appendValue(builder, outputFormat, jdbcNamedParameter.toString(), "", "");
1✔
2368

2369
    } else if (expression instanceof JdbcParameter) {
1✔
2370
      JdbcParameter jdbcParameter = (JdbcParameter) expression;
1✔
2371
      appendValue(builder, outputFormat, jdbcParameter.toString(), "", "");
1✔
2372

2373
    } else if (expression instanceof IsNullExpression) {
1✔
2374
      IsNullExpression isNullExpression = (IsNullExpression) expression;
1✔
2375
      appendExpression(isNullExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2376
          BreakLine.AFTER_FIRST);
2377

2378
      if (isNullExpression.isUseNotNull()) {
1✔
2379
        appendOperator(builder, outputFormat, "NOTNULL", " ", "");
×
2380
      } else if (isNullExpression.isUseIsNull()) {
1✔
2381
        if (isNullExpression.isNot()) {
×
2382
          appendOperator(builder, outputFormat, "NOT ISNULL", " ", "");
×
2383
        } else {
2384
          appendOperator(builder, outputFormat, "ISNULL", " ", "");
×
2385
        }
2386
      } else {
2387
        if (isNullExpression.isNot()) {
1✔
2388
          appendOperator(builder, outputFormat, "IS NOT NULL", " ", "");
1✔
2389
        } else {
2390
          appendOperator(builder, outputFormat, "IS NULL", " ", "");
1✔
2391
        }
2392
      }
2393

2394
    } else if (expression instanceof NullValue) {
1✔
2395
      NullValue nullValue = (NullValue) expression;
1✔
2396
      appendKeyWord(builder, outputFormat, nullValue.toString(), "", "");
1✔
2397

2398
    } else if (expression instanceof TimeKeyExpression) {
1✔
2399
      TimeKeyExpression timeKeyExpression = (TimeKeyExpression) expression;
1✔
2400
      appendValue(builder, outputFormat, timeKeyExpression.toString(), "", "");
1✔
2401

2402
    } else if (expression instanceof InExpression) {
1✔
2403
      InExpression inExpression = (InExpression) expression;
1✔
2404
      Expression leftExpression = inExpression.getLeftExpression();
1✔
2405
      boolean useNot = inExpression.isNot();
1✔
2406

2407
      Expression rightExpression = inExpression.getRightExpression();
1✔
2408

2409
      appendExpression(leftExpression, null, builder, indent, i, n, false, BreakLine.AS_NEEDED);
1✔
2410

2411
      if (inExpression.isGlobal()) {
1✔
2412
        appendKeyWord(builder, outputFormat, "GLOBAL", " ", "");
×
2413
      }
2414

2415
      if (useNot) {
1✔
2416
        appendOperator(builder, outputFormat, "NOT IN", " ", " ");
×
2417
      } else {
2418
        appendOperator(builder, outputFormat, "IN", " ", " ");
1✔
2419
      }
2420

2421
      appendExpression(rightExpression, null, builder, indent, i, n, false, BreakLine.AS_NEEDED);
1✔
2422
    } else if (expression instanceof Function) {
1✔
2423
      Function function = (Function) expression;
1✔
2424

2425
      String name = function.getName();
1✔
2426
      ExpressionList<?> parameters = function.getParameters();
1✔
2427
      NamedExpressionList<?> namedParameters = function.getNamedParameters();
1✔
2428
      boolean distinct = function.isDistinct();
1✔
2429
      boolean allColumns = function.isAllColumns();
1✔
2430
      boolean escaped = function.isEscaped();
1✔
2431
      KeepExpression keep = function.getKeep();
1✔
2432
      Object attribute = function.getAttribute();
1✔
2433

2434
      if (escaped) {
1✔
2435
        appendFunction(builder, outputFormat, "fn", " {", " ");
×
2436
      }
2437

2438
      appendFunction(builder, outputFormat, name, "", "");
1✔
2439

2440
      if (parameters != null || namedParameters != null) {
1✔
2441
        if (parameters != null) {
1✔
2442
          if (distinct) {
1✔
2443
            appendKeyWord(builder, outputFormat, "DISTINCT", "( ", " ");
1✔
2444
          } else if (allColumns) {
1✔
2445
            appendKeyWord(builder, outputFormat, "ALL", "( ", " ");
×
2446
          } else {
2447
            builder.append("( ");
1✔
2448
          }
2449

2450
          if (name.equalsIgnoreCase("Decode")) {
1✔
2451
            appendDecodeExpressionsList(parameters, BreakLine.AS_NEEDED, builder, indent);
1✔
2452
          } else {
2453
            appendExpressionList(parameters, builder, indent, BreakLine.AS_NEEDED);
1✔
2454
          }
2455

2456
          appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2457
        } else {
2458
          // @todo: implement this properly and add a test case
2459
          builder.append(namedParameters);
1✔
2460
        }
2461
      } else if (allColumns) {
1✔
2462
        builder.append("( * )");
×
2463
      } else {
2464
        builder.append("()");
1✔
2465
      }
2466

2467
      if (attribute != null) {
1✔
2468
        builder.append(".").append(attribute);
×
2469
      }
2470

2471
      if (keep != null) {
1✔
2472
        builder.append(" ").append(keep);
×
2473
      }
2474

2475
      if (escaped) {
1✔
2476
        builder.append("} ");
×
2477
      }
2478

2479
    } else if (expression instanceof SignedExpression) {
1✔
2480
      SignedExpression signedExpression = (SignedExpression) expression;
1✔
2481
      appendOperator(builder, outputFormat, String.valueOf(signedExpression.getSign()), "", " ");
1✔
2482

2483
      appendExpression(signedExpression.getExpression(), null, builder, indent, i, n, false,
1✔
2484
          BreakLine.NEVER);
2485

2486
    } else if (expression instanceof Select) {
1✔
2487
      Select select = (Select) expression;
1✔
2488
      appendSelect(select, builder, indent, false, false);
1✔
2489

2490
    } else if (expression instanceof RowConstructor) {
1✔
2491
      RowConstructor<?> rowConstructor = (RowConstructor<?>) expression;
×
2492
      appendRowConstructor(builder, indent, rowConstructor);
×
2493

2494
    } else if (expression instanceof MySQLGroupConcat) {
1✔
2495
      MySQLGroupConcat mySQLGroupConcat = (MySQLGroupConcat) expression;
1✔
2496
      appendFunction(builder, outputFormat, "GROUP_CONCAT", "", "( ");
1✔
2497

2498
      int subIndent = getSubIndent(builder, true);
1✔
2499

2500
      if (mySQLGroupConcat.isDistinct()) {
1✔
2501
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
1✔
2502
      }
2503
      appendExpressionsList(mySQLGroupConcat.getExpressionList(), builder, subIndent,
1✔
2504
          BreakLine.AS_NEEDED);
2505
      List<OrderByElement> orderByElements = mySQLGroupConcat.getOrderByElements();
1✔
2506
      appendOrderByElements(orderByElements, builder, subIndent);
1✔
2507

2508
      String separator = mySQLGroupConcat.getSeparator();
1✔
2509
      if (separator != null) {
1✔
2510
        appendNormalizedLineBreak(builder);
1✔
2511
        for (int j = 0; j < subIndent; j++) {
1✔
2512
          builder.append(indentString);
1✔
2513
        }
2514
        appendKeyWord(builder, outputFormat, "SEPARATOR", "", " " + separator);
1✔
2515
      }
2516
      builder.append(" )");
1✔
2517

2518
      // Abstract Class, call last and let the specific implementations catch first
2519
    } else if (expression instanceof BinaryExpression) {
1✔
2520
      BinaryExpression binaryExpression = (BinaryExpression) expression;
1✔
2521
      appendExpression(binaryExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2522
          BreakLine.NEVER);
2523

2524
      if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2525
        appendNormalizedLineBreak(builder);
1✔
2526
        for (int j = 0; j <= indent; j++) {
1✔
2527
          builder.append(indentString);
1✔
2528
        }
2529
      }
2530
      appendOperator(builder, outputFormat, binaryExpression.getStringExpression(), " ", " ");
1✔
2531

2532
      appendExpression(binaryExpression.getRightExpression(), null, builder, indent, i, n, false,
1✔
2533
          BreakLine.NEVER);
2534

2535
    } else if (expression instanceof ExpressionList) {
1✔
2536
      ExpressionList<?> expressions = (ExpressionList<?>) expression;
1✔
2537
      appendExpressionList(expressions, builder, indent, BreakLine.AS_NEEDED);
1✔
2538
    } else if (expression instanceof AllTableColumns) {
1✔
2539
      AllTableColumns allTableColumns = (AllTableColumns) expression;
1✔
2540
      appendObjectName(builder, outputFormat, allTableColumns.getTable().getFullyQualifiedName(),
1✔
2541
          "", ".*");
2542

2543
      if (allTableColumns.getExceptColumns() != null
1✔
2544
          && !allTableColumns.getExceptColumns().isEmpty()) {
1✔
2545
        appendKeyWord(builder, outputFormat, allTableColumns.getExceptKeyword(), " ", "( ");
1✔
2546
        appendExpressionsList(allTableColumns.getExceptColumns(), builder, indent,
1✔
2547
            BreakLine.AS_NEEDED);
2548
        builder.append(" )");
1✔
2549
      }
2550

2551
      if (allTableColumns.getReplaceExpressions() != null
1✔
2552
          && !allTableColumns.getReplaceExpressions().isEmpty()) {
×
2553
        appendKeyWord(builder, outputFormat, "REPLACE", " ", "( ");
×
2554
        int subIndent = getSubIndent(builder, allTableColumns.getReplaceExpressions().size() > 3);
×
2555
        appendColumnSelectItemList(allTableColumns.getReplaceExpressions(), builder, subIndent, i,
×
2556
            BreakLine.AS_NEEDED, indent);
2557
        builder.append(" )");
×
2558
      }
2559
    } else if (expression instanceof AllColumns) {
1✔
2560
      AllColumns allColumns = (AllColumns) expression;
1✔
2561
      appendObjectName(builder, outputFormat, "*", "", "");
1✔
2562

2563
      if (allColumns.getExceptColumns() != null && !allColumns.getExceptColumns().isEmpty()) {
1✔
2564
        appendKeyWord(builder, outputFormat, allColumns.getExceptKeyword(), " ", "( ");
1✔
2565
        appendExpressionsList(allColumns.getExceptColumns(), builder, indent, BreakLine.AS_NEEDED);
1✔
2566
        builder.append(" )");
1✔
2567
      }
2568

2569
      if (allColumns.getReplaceExpressions() != null
1✔
2570
          && !allColumns.getReplaceExpressions().isEmpty()) {
×
2571
        appendKeyWord(builder, outputFormat, "REPLACE", " ", "( ");
×
2572
        int subIndent = getSubIndent(builder, allColumns.getReplaceExpressions().size() > 3);
×
2573
        appendColumnSelectItemList(allColumns.getReplaceExpressions(), builder, subIndent, i,
×
2574
            BreakLine.AS_NEEDED, indent);
2575
        builder.append(" )");
×
2576
      }
2577

2578
    } else if (expression instanceof IntervalExpression) {
1✔
2579
      IntervalExpression intervalExpression = (IntervalExpression) expression;
1✔
2580
      if (intervalExpression.isUsingIntervalKeyword()) {
1✔
2581
        appendKeyWord(builder, outputFormat, "INTERVAL", "", " ");
1✔
2582
      }
2583
      if (intervalExpression.getExpression() != null) {
1✔
2584
        appendExpression(intervalExpression.getExpression(), null, builder, indent, i, n, false,
×
2585
            breakLine);
2586
      } else {
2587
        appendValue(builder, outputFormat, intervalExpression.getParameter(), "", "");
1✔
2588
      }
2589
      if (intervalExpression.getIntervalType() != null) {
1✔
2590
        appendKeyWord(builder, outputFormat, intervalExpression.getIntervalType(), " ", "");
1✔
2591
      }
2592
    } else if (expression instanceof CastExpression) {
1✔
2593
      CastExpression castExpression = (CastExpression) expression;
1✔
2594

2595
      if (castExpression.isImplicitCast()) {
1✔
2596
        appendKeyWord(builder, outputFormat, castExpression.getColDataType().toString(), "", " ");
1✔
2597
        appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2598
            BreakLine.NEVER);
2599
      } else if (castExpression.isUseCastKeyword()) {
1✔
2600
        if (castExpression.getColumnDefinitions().size() > 1) {
1✔
2601
          appendFunction(builder, outputFormat, castExpression.keyword, " ", "( ");
×
2602
          appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, false,
×
2603
              BreakLine.NEVER);
2604
          appendKeyWord(builder, outputFormat, "AS ROW ( ", " ", " ");
×
2605
          int j = 0;
×
2606
          for (ColumnDefinition columnDefinition : castExpression.getColumnDefinitions()) {
×
2607
            if (j++ > 0) {
×
2608
              builder.append(", ");
×
2609
            }
2610
            appendKeyWord(builder, outputFormat, columnDefinition.toString(), "", "");
×
2611
          }
×
2612
        } else {
×
2613
          appendFunction(builder, outputFormat, castExpression.keyword, " ", "( ");
1✔
2614
          appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2615
              BreakLine.NEVER);
2616
          appendKeyWord(builder, outputFormat, "AS " + castExpression.getColDataType().toString(),
1✔
2617
              " ", " )");
2618
        }
2619
      } else {
2620
        appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, true,
1✔
2621
            BreakLine.AS_NEEDED);
2622
        appendKeyWord(builder, outputFormat, castExpression.getColDataType().toString(), "::", "");
1✔
2623
      }
2624
    } else if (expression instanceof Between) {
1✔
2625
      Between between = (Between) expression;
1✔
2626
      appendExpression(between.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2627
          BreakLine.NEVER);
2628

2629
      int subIndent = getSubIndent(builder, false);
1✔
2630
      appendKeyWord(builder, outputFormat, "BETWEEN", between.isNot() ? " NOT " : " ", " ");
1✔
2631

2632
      appendExpression(between.getBetweenExpressionStart(), null, builder, indent + 1, i, n, false,
1✔
2633
          BreakLine.NEVER);
2634

2635
      appendNormalizedLineBreak(builder);
1✔
2636
      for (int j = 0; j <= subIndent; j++) {
1✔
2637
        builder.append(indentString);
1✔
2638
      }
2639
      appendKeyWord(builder, outputFormat, "AND", " ", " ");
1✔
2640

2641
      appendExpression(between.getBetweenExpressionEnd(), null, builder, indent + 1, i, n, false,
1✔
2642
          BreakLine.NEVER);
2643
    } else if (expression instanceof ArrayConstructor) {
1✔
2644
      ArrayConstructor arrayConstructor = (ArrayConstructor) expression;
1✔
2645
      if (arrayConstructor.isArrayKeyword()) {
1✔
2646
        appendKeyWord(builder, outputFormat, "ARRAY", " ", "");
×
2647
      }
2648

2649
      boolean multiline = false;
1✔
2650
      for (Expression p : arrayConstructor.getExpressions()) {
1✔
2651
        if (p instanceof ArrayConstructor || p instanceof ExpressionList || p instanceof Select
1✔
2652
            || p instanceof StructType) {
2653
          multiline = true;
1✔
2654
          break;
1✔
2655
        }
2656
      }
×
2657

2658
      int subIndent = getSubIndent(builder, true);
1✔
2659
      builder.append("[ ");
1✔
2660
      if (multiline) {
1✔
2661
        appendExpressionList(arrayConstructor.getExpressions(), builder, subIndent,
1✔
2662
            BreakLine.ALWAYS);
2663
        appendNormalizedLineBreak(builder);
1✔
2664
        for (int j = 0; j < subIndent; j++) {
1✔
2665
          builder.append(indentString);
1✔
2666
        }
2667
      } else {
2668
        appendExpressionList(arrayConstructor.getExpressions(), builder, subIndent,
×
2669
            BreakLine.NEVER);
2670
      }
2671
      builder.append("]");
1✔
2672
    } else if (expression instanceof TranscodingFunction) {
1✔
2673
      TranscodingFunction transcodingFunction = (TranscodingFunction) expression;
1✔
2674
      appendFunction(builder, outputFormat, "Convert", "", "(");
1✔
2675
      if (transcodingFunction.isTranscodeStyle()) {
1✔
2676
        appendExpression(transcodingFunction.getExpression(), null, builder, indent, 0, 1, false,
×
2677
            BreakLine.AS_NEEDED);
2678
        appendKeyWord(builder, outputFormat, "USING", " ", "");
×
2679
        appendKeyWord(builder, outputFormat, transcodingFunction.getTranscodingName(), " ", " )");
×
2680
      } else {
2681
        appendKeyWord(builder, outputFormat, transcodingFunction.getColDataType().toString(), " ",
1✔
2682
            ", ");
2683
        appendExpression(transcodingFunction.getExpression(), null, builder, indent, 0, 1, false,
1✔
2684
            BreakLine.AS_NEEDED);
2685

2686
        String transcodingName = transcodingFunction.getTranscodingName();
1✔
2687
        if (transcodingName != null && !transcodingName.isEmpty()) {
1✔
2688
          appendKeyWord(builder, outputFormat, transcodingName, ", ", " )");
×
2689
        } else {
2690
          builder.append(" )");
1✔
2691
        }
2692
      }
2693
    } else if (expression instanceof AnalyticExpression) {
1✔
2694
      AnalyticExpression analyticExpression = (AnalyticExpression) expression;
1✔
2695

2696
      int subIndent = getSubIndent(builder, false);
1✔
2697
      appendFunction(builder, outputFormat, analyticExpression.getName(), "", "( ");
1✔
2698
      if (analyticExpression.isDistinct()) {
1✔
2699
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
×
2700
      }
2701

2702
      Expression expr = analyticExpression.getExpression();
1✔
2703
      if (expr != null) {
1✔
2704
        appendExpression(expr, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
2705
        if (analyticExpression.getOffset() != null) {
1✔
2706
          builder.append(", ");
1✔
2707
          appendExpression(analyticExpression.getOffset(), null, builder, indent, 0, 1, true,
1✔
2708
              BreakLine.NEVER);
2709
          if (analyticExpression.getDefaultValue() != null) {
1✔
2710
            builder.append(", ");
×
2711
            appendExpression(analyticExpression.getDefaultValue(), null, builder, indent, 0, 1,
×
2712
                true, BreakLine.NEVER);
2713
          }
2714
        }
2715
      } else if (analyticExpression.isAllColumns()) {
×
2716
        builder.append("*");
×
2717
      }
2718

2719
      if (analyticExpression.getHavingClause() != null) {
1✔
2720
        analyticExpression.getHavingClause().appendTo(builder);
×
2721
      }
2722

2723
      if (analyticExpression.getNullHandling() != null) {
1✔
2724
        switch (analyticExpression.getNullHandling()) {
1✔
2725
          case IGNORE_NULLS:
2726
            builder.append(" IGNORE NULLS");
1✔
2727
            break;
1✔
2728
          case RESPECT_NULLS:
2729
            builder.append(" RESPECT NULLS");
×
2730
        }
2731
      }
2732

2733
      if (analyticExpression.getFuncOrderBy() != null) {
1✔
2734
        builder.append(" ORDER BY ");
1✔
2735
        builder.append(analyticExpression.getFuncOrderBy().stream().map(OrderByElement::toString)
1✔
2736
            .collect(Collectors.joining(", ")));
1✔
2737
      }
2738

2739
      if (analyticExpression.getLimit() != null) {
1✔
2740
        builder.append(analyticExpression.getLimit());
×
2741
      }
2742
      builder.append(" ) ");
1✔
2743

2744
      if (analyticExpression.getKeep() != null) {
1✔
2745
        appendNormalizedLineBreak(builder);
×
2746
        for (int j = 0; j < subIndent + 1; j++) {
×
2747
          builder.append(indentString);
×
2748
        }
2749
        builder.append(analyticExpression.getKeep()).append(" ");
×
2750
      }
2751

2752
      if (analyticExpression.getFilterExpression() != null) {
1✔
2753
        appendNormalizedLineBreak(builder);
1✔
2754
        for (int j = 0; j < subIndent + 1; j++) {
1✔
2755
          builder.append(indentString);
1✔
2756
        }
2757
        builder.append("FILTER ( WHERE ");
1✔
2758
        builder.append(analyticExpression.getFilterExpression());
1✔
2759
        builder.append(" )");
1✔
2760
        if (analyticExpression.getType() != AnalyticType.FILTER_ONLY) {
1✔
2761
          builder.append(" ");
×
2762
        }
2763
      }
2764

2765
      if (analyticExpression.isIgnoreNullsOutside()) {
1✔
2766
        builder.append("IGNORE NULLS ");
×
2767
      }
2768

2769
      switch (analyticExpression.getType()) {
1✔
2770
        case FILTER_ONLY:
2771
          return;
1✔
2772
        case WITHIN_GROUP:
2773
          appendNormalizedLineBreak(builder);
1✔
2774
          for (int j = 0; j < subIndent + 1; j++) {
1✔
2775
            builder.append(indentString);
1✔
2776
          }
2777
          builder.append("WITHIN GROUP");
1✔
2778
          break;
1✔
2779
        case WITHIN_GROUP_OVER:
2780
          appendNormalizedLineBreak(builder);
×
2781
          for (int j = 0; j < subIndent + 1; j++) {
×
2782
            builder.append(indentString);
×
2783
          }
2784
          builder.append("WITHIN GROUP ( ");
×
2785
          analyticExpression.getWindowDefinition().getOrderBy().toStringOrderByElements(builder);
×
2786
          builder.append(" ) OVER ( ");
×
2787
          analyticExpression.getWindowDefinition().getPartitionBy().toStringPartitionBy(builder);
×
2788
          builder.append(" )");
×
2789
          break;
×
2790
        default:
2791
          appendNormalizedLineBreak(builder);
1✔
2792
          for (int j = 0; j < subIndent + 1; j++) {
1✔
2793
            builder.append(indentString);
1✔
2794
          }
2795
          builder.append("OVER");
1✔
2796
      }
2797

2798
      if (analyticExpression.getWindowName() != null) {
1✔
2799
        builder.append(" ").append(analyticExpression.getWindowName());
1✔
2800
      } else if (analyticExpression.getType() != AnalyticType.WITHIN_GROUP_OVER) {
1✔
2801
        builder.append(" ");
1✔
2802
        builder.append(analyticExpression.getWindowDefinition());
1✔
2803
      }
2804
    } else {
1✔
2805
      LOGGER
1✔
2806
          .warning("Unhandled expression: " + expression.getClass().getName() + " = " + expression);
1✔
2807
      builder.append(expression);
1✔
2808
    }
2809

2810
    if (alias != null) {
1✔
2811
      appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2812
      if (alias.isUseAs()) {
1✔
2813
        appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2814
      }
2815
      appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2816
    }
2817

2818
    switch (separation) {
1✔
2819
      case AFTER:
2820
        appendNormalizingTrailingWhiteSpace(builder, commaSeparated && i < n - 1 ? ", " : "");
1✔
2821
        break;
2822
    }
2823
  }
1✔
2824

2825
  private static void appendExpressionList(ExpressionList<?> expressionList, StringBuilder builder,
2826
      int indent, BreakLine breakLine) {
2827
    int subIndent = indent;
1✔
2828
    if (expressionList instanceof ParenthesedExpressionList) {
1✔
2829
      builder.append("( ");
1✔
2830
      subIndent++;
1✔
2831
    }
2832
    appendExpressionsList(expressionList, builder, subIndent, breakLine);
1✔
2833
    if (expressionList instanceof ParenthesedExpressionList) {
1✔
2834
      builder.append(" )");
1✔
2835
    }
2836
  }
1✔
2837

2838
  private static void appendExpressionsList(List<? extends Expression> expressions,
2839
      StringBuilder builder, int indent, BreakLine breakLine) {
2840
    int size = expressions.size();
1✔
2841
    int subIndent =
2842
        breakLine.equals(BreakLine.NEVER) || breakLine.equals(BreakLine.AS_NEEDED) && size <= 3
1✔
2843
            || size == 1 ? indent : getSubIndent(builder, true);
1✔
2844

2845
    int i = 0;
1✔
2846
    for (Expression expression : expressions) {
1✔
2847
      switch (breakLine) {
1✔
2848
        case AS_NEEDED:
2849
          BreakLine bl =
2850
              size == 4 || size >= 5 && i % 3 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
2851
          appendExpression(expression, null, builder, subIndent, i, expressions.size(), true, bl);
1✔
2852
          break;
1✔
2853

2854
        default:
2855
          appendExpression(expression, null, builder, subIndent, i, expressions.size(), true,
1✔
2856
              breakLine);
2857
      }
2858
      i++;
1✔
2859
    }
1✔
2860
  }
1✔
2861

2862
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2863
  private static void appendFromItem(FromItem fromItem, StringBuilder builder, int indent, int i,
2864
      int n) {
2865

2866
    if (fromItem != null) {
1✔
2867
      if (i > 0) {
1✔
2868
        appendNormalizedLineBreak(builder);
×
2869
        for (int j = 0; j <= indent; j++) {
×
2870
          builder.append(indentString);
×
2871
        }
2872
      }
2873

2874
      switch (separation) {
1✔
2875
        case BEFORE:
2876
          builder.append(i > 0 ? ", " : "");
1✔
2877
      }
2878

2879
      Alias alias = fromItem.getAlias();
1✔
2880

2881
      // All Known Implementing Classes: LateralSubSelect, ParenthesisFromItem,
2882
      // SpecialSubSelect, SubJoin, SubSelect, Table, TableFunction, ValuesList
2883
      if (fromItem instanceof Table) {
1✔
2884
        Table table = (Table) fromItem;
1✔
2885
        appendTable(table, alias, builder);
1✔
2886
      } else if (fromItem instanceof Select) {
1✔
2887
        Select select = (Select) fromItem;
1✔
2888
        appendSelect(select, builder, indent, false, true);
1✔
2889

2890
        if (alias != null) {
1✔
2891
          appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2892
          if (alias.isUseAs()) {
1✔
2893
            appendKeyWord(builder, outputFormat, "AS", "", " ");
×
2894
          }
2895

2896
          appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2897
        }
2898
      } else if (fromItem instanceof ParenthesedFromItem) {
1✔
2899
        ParenthesedFromItem parenthesedFromItem = (ParenthesedFromItem) fromItem;
1✔
2900
        builder.append("( ");
1✔
2901
        int subIndent = getSubIndent(builder, true);
1✔
2902

2903
        appendFromItem(parenthesedFromItem.getFromItem(), builder, indent, i, n);
1✔
2904
        List<Join> joins = parenthesedFromItem.getJoins();
1✔
2905
        appendJoins(joins, builder, subIndent);
1✔
2906
        builder.append(" )");
1✔
2907

2908
        if (alias != null) {
1✔
2909
          appendNormalizingTrailingWhiteSpace(builder, " ");
×
2910
          if (alias.isUseAs()) {
×
2911
            appendKeyWord(builder, outputFormat, "AS", "", " ");
×
2912
          }
2913

2914
          appendAlias(builder, outputFormat, alias.getName(), "", " ");
×
2915
        }
2916
      } else {
1✔
2917
        LOGGER.log(Level.WARNING, "FROM Item not covered: " + fromItem.getClass().getName());
×
2918
      }
2919

2920
      switch (separation) {
1✔
2921
        case AFTER:
2922
          appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? ", " : "");
1✔
2923
          break;
2924
      }
2925
    }
2926
  }
1✔
2927

2928
  private static void appendTable(Table table, Alias alias, StringBuilder builder) {
2929

2930
    if (table != null) {
1✔
2931
      appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
2932
      if (alias != null) {
1✔
2933
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2934
        if (alias.isUseAs()) {
1✔
2935
          appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2936
        }
2937

2938
        appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2939
      }
2940
    }
2941
  }
1✔
2942

2943
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2944
  private static void appendSetOperation(SetOperation setOperation, StringBuilder builder,
2945
      int indent) {
2946

2947
    // Direct Known Subclasses:
2948
    // ExceptOp, IntersectOp, MinusOp, UnionOp
2949
    if (setOperation instanceof UnionOp) {
1✔
2950
      UnionOp unionOp = (UnionOp) setOperation;
1✔
2951

2952
      appendNormalizedLineBreak(builder);
1✔
2953
      for (int j = 0; j < indent; j++) {
1✔
2954
        builder.append(indentString);
1✔
2955
      }
2956
      appendOperator(builder, outputFormat, "UNION", "", " ");
1✔
2957

2958
      if (unionOp.isAll()) {
1✔
2959
        appendOperator(builder, outputFormat, "ALL", "", " ");
1✔
2960
      }
2961
    } else if (setOperation instanceof MinusOp) {
1✔
2962
      appendNormalizedLineBreak(builder);
1✔
2963
      for (int j = 0; j < indent; j++) {
1✔
2964
        builder.append(indentString);
1✔
2965
      }
2966
      appendOperator(builder, outputFormat, "MINUS", "", " ");
1✔
2967
    } else if (setOperation instanceof IntersectOp) {
×
2968
      appendNormalizedLineBreak(builder);
×
2969
      for (int j = 0; j < indent; j++) {
×
2970
        builder.append(indentString);
×
2971
      }
2972
      appendOperator(builder, outputFormat, "INTERSECT", "", " ");
×
2973

2974
    } else if (setOperation instanceof ExceptOp) {
×
2975
      appendNormalizedLineBreak(builder);
×
2976
      for (int j = 0; j < indent; j++) {
×
2977
        builder.append(indentString);
×
2978
      }
2979
      appendOperator(builder, outputFormat, "EXCEPT", "", " ");
×
2980
    } else if (setOperation != null) {
×
2981
      throw new UnsupportedOperationException(
×
2982
          setOperation.getClass().getName() + " is not supported yet.");
×
2983
    }
2984
  }
1✔
2985

2986
  private static void appendTruncate(StringBuilder builder, Truncate truncate) {
2987
    Table table = truncate.getTable();
1✔
2988
    boolean cascade = truncate.getCascade();
1✔
2989

2990
    appendKeyWord(builder, outputFormat, "TRUNCATE TABLE", "", " ")
1✔
2991
        .append(table.getFullyQualifiedName());
1✔
2992
    if (cascade) {
1✔
2993
      appendOperator(builder, outputFormat, "CASCADE", " ", "");
1✔
2994
    }
2995
  }
1✔
2996

2997
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
2998
  private static void appendCreateTable(StringBuilder builder, CreateTable createTable,
2999
      int indent) {
3000

3001
    int i = 0;
1✔
3002
    appendNormalizedLineBreak(builder);
1✔
3003

3004
    List<String> createOptionsString = createTable.getCreateOptionsStrings();
1✔
3005
    String createOps = createOptionsString != null && !createOptionsString.isEmpty()
1✔
3006
        ? PlainSelect.getStringList(createOptionsString, false, false)
×
3007
        : null;
1✔
3008

3009
    boolean unlogged = createTable.isUnlogged();
1✔
3010
    boolean ifNotExists = createTable.isIfNotExists();
1✔
3011

3012
    Table table = createTable.getTable();
1✔
3013
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3014

3015
    if (unlogged) {
1✔
3016
      appendHint(builder, outputFormat, "UNLOGGED", "", " ");
×
3017
    }
3018

3019
    if (createOps != null) {
1✔
3020
      appendHint(builder, outputFormat, createOps, "", " ");
×
3021
    }
3022

3023
    appendKeyWord(builder, outputFormat, "TABLE", "", " ");
1✔
3024

3025
    if (ifNotExists) {
1✔
3026
      appendHint(builder, outputFormat, "IF NOT EXISTS", "", " ");
×
3027
    }
3028

3029
    appendAlias(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3030

3031
    List<ColumnDefinition> columnDefinitions = createTable.getColumnDefinitions();
1✔
3032
    List<Index> indexes = createTable.getIndexes();
1✔
3033

3034
    if (columnDefinitions != null && !columnDefinitions.isEmpty()) {
1✔
3035
      builder.append(" (");
1✔
3036

3037
      int colWidth = 0;
1✔
3038
      int typeWidth = 0;
1✔
3039

3040
      for (ColumnDefinition columnDefinition : columnDefinitions) {
1✔
3041
        String columnName = columnDefinition.getColumnName();
1✔
3042
        // @todo: please get rid of that Replace workaround
3043
        String colDataType = columnDefinition.getColDataType().toString().replace(", ", ",");
1✔
3044

3045
        if (colWidth < columnName.length()) {
1✔
3046
          colWidth = columnName.length();
1✔
3047
        }
3048

3049
        if (typeWidth < colDataType.length()) {
1✔
3050
          typeWidth = colDataType.length();
1✔
3051
        }
3052
      }
1✔
3053

3054
      // int typeIndex = (((indent +1)* indentString.length() + colWidth + 1) /
3055
      // indentString.length()) * ("
3056
      // ".length() + 1);
3057

3058
      int typeIndex = indent + (colWidth / indentString.length()) + 3;
1✔
3059

3060
      int specIndex = indent + typeIndex + (typeWidth / indentString.length()) + 1;
1✔
3061

3062
      for (ColumnDefinition columnDefinition : columnDefinitions) {
1✔
3063
        appendNormalizedLineBreak(builder);
1✔
3064
        for (int j = 0; j <= indent; j++) {
1✔
3065
          builder.append(indentString);
1✔
3066
        }
3067

3068
        String columnName = columnDefinition.getColumnName();
1✔
3069
        ColDataType colDataType = columnDefinition.getColDataType();
1✔
3070
        List<String> columnSpecs = columnDefinition.getColumnSpecs();
1✔
3071

3072
        switch (separation) {
1✔
3073
          case BEFORE:
3074
            builder.append(i > 0 ? ", " : "");
1✔
3075
        }
3076

3077
        appendObjectName(builder, outputFormat, columnName, "", "");
1✔
3078

3079
        int lastLineLength = getLastLineLength(builder);
1✔
3080

3081
        for (int j = lastLineLength; j <= typeIndex * indentWidth; j++) {
1✔
3082
          builder.append(" ");
1✔
3083
        }
3084
        // @todo: please get rid of that Replace workaround
3085
        appendType(builder, outputFormat, colDataType.toString().replace(", ", ","), "", "");
1✔
3086

3087
        lastLineLength = getLastLineLength(builder);
1✔
3088

3089
        if (columnSpecs != null && !columnSpecs.isEmpty()) {
1✔
3090
          for (int j = lastLineLength; j <= specIndex * indentWidth; j++) {
1✔
3091
            builder.append(" ");
1✔
3092
          }
3093
          appendType(builder, outputFormat, PlainSelect.getStringList(columnSpecs, false, false),
1✔
3094
              "", "");
3095
        }
3096

3097
        switch (separation) {
1✔
3098
          case AFTER:
3099
            appendNormalizingTrailingWhiteSpace(builder,
1✔
3100
                i < columnDefinitions.size() + indexes.size() - 1 ? ", " : "");
1✔
3101
            break;
3102
        }
3103

3104
        i++;
1✔
3105
      }
1✔
3106

3107
      // Direct Known Subclasses:
3108
      // ExcludeConstraint, NamedConstraint
3109

3110
      // Direct Known Subclasses:
3111
      // CheckConstraint, ForeignKeyIndex
3112

3113
      if (indexes != null && !indexes.isEmpty()) {
1✔
3114
        for (Index index : indexes) {
1✔
3115
          appendNormalizedLineBreak(builder);
1✔
3116
          for (int j = 0; j <= indent; j++) {
1✔
3117
            builder.append(indentString);
1✔
3118
          }
3119

3120
          switch (separation) {
1✔
3121
            case BEFORE:
3122
              builder.append(i > 0 ? ", " : "");
1✔
3123
          }
3124

3125
          if (index instanceof ForeignKeyIndex) {
1✔
3126
            ForeignKeyIndex foreignKeyIndex = (ForeignKeyIndex) index;
1✔
3127

3128
            String type = foreignKeyIndex.getType();
1✔
3129
            String name = foreignKeyIndex.getName();
1✔
3130
            List<String> columnsNames = foreignKeyIndex.getColumnsNames();
1✔
3131
            List<String> idxSpec = foreignKeyIndex.getIndexSpec();
1✔
3132
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
1✔
3133

3134
            // @todo: beautify the expression
3135
            // @todo: add a test case
3136
            if (name != null) {
1✔
3137
              appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
1✔
3138
              appendAlias(builder, outputFormat, name, "", "");
1✔
3139
              appendNormalizedLineBreak(builder);
1✔
3140
            }
3141

3142
            for (int j = 0; name != null && j <= indent + 1; j++) {
1✔
3143
              builder.append(indentString);
1✔
3144
            }
3145

3146
            appendKeyWord(builder, outputFormat, type, "", " ");
1✔
3147

3148
            builder.append("( ");
1✔
3149
            int subIndent = getSubIndent(builder, columnsNames.size() > 2);
1✔
3150
            BreakLine bl = columnsNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3151
            appendStringList(columnsNames, builder, subIndent, true, bl);
1✔
3152
            builder.append(" )");
1✔
3153

3154
            // @todo: add a test case for this
3155
            if (idxSpec != null && !idxSpecText.isEmpty()) {
1✔
3156
              appendHint(builder, outputFormat, idxSpecText, " ", "");
×
3157
            }
3158

3159
            Table foreignTable = foreignKeyIndex.getTable();
1✔
3160
            List<String> referencedColumnNames = foreignKeyIndex.getReferencedColumnNames();
1✔
3161

3162
            appendNormalizedLineBreak(builder);
1✔
3163
            for (int j = 0; j <= indent + 1; j++) {
1✔
3164
              builder.append(indentString);
1✔
3165
            }
3166
            appendKeyWord(builder, outputFormat, "REFERENCES", "", " ");
1✔
3167
            appendObjectName(builder, outputFormat, foreignTable.getFullyQualifiedName(), "", " ");
1✔
3168

3169
            builder.append("( ");
1✔
3170
            subIndent = getSubIndent(builder, referencedColumnNames.size() > 2);
1✔
3171
            bl = referencedColumnNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3172
            appendStringList(referencedColumnNames, builder, subIndent, true, bl);
1✔
3173
            builder.append(" )");
1✔
3174

3175
            ReferentialAction updateAction =
1✔
3176
                foreignKeyIndex.getReferentialAction(ReferentialAction.Type.UPDATE);
1✔
3177
            if (updateAction != null) {
1✔
3178
              appendNormalizedLineBreak(builder);
×
3179
              for (int j = 0; j <= indent + 2; j++) {
×
3180
                builder.append(indentString);
×
3181
              }
3182
              builder.append(updateAction);
×
3183
            }
3184

3185
            ReferentialAction deleteAction =
1✔
3186
                foreignKeyIndex.getReferentialAction(ReferentialAction.Type.DELETE);
1✔
3187
            if (deleteAction != null) {
1✔
3188
              appendNormalizedLineBreak(builder);
×
3189
              for (int j = 0; j <= indent + 2; j++) {
×
3190
                builder.append(indentString);
×
3191
              }
3192
              builder.append(deleteAction);
×
3193
            }
3194

3195
          } else if (index instanceof CheckConstraint) {
1✔
3196
            CheckConstraint checkConstraint = (CheckConstraint) index;
×
3197

3198
            String contraintName = checkConstraint.getName();
×
3199
            Expression expression = checkConstraint.getExpression();
×
3200

3201
            appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
×
3202
            appendAlias(builder, outputFormat, contraintName, "", "");
×
3203

3204
            appendNormalizedLineBreak(builder);
×
3205
            for (int j = 0; j <= indent + 1; j++) {
×
3206
              builder.append(indentString);
×
3207
            }
3208

3209
            builder.append(" CHECK (").append(expression).append(")");
×
3210

3211
          } else if (index instanceof NamedConstraint) {
1✔
3212
            NamedConstraint namedConstraint = (NamedConstraint) index;
1✔
3213

3214
            String type = namedConstraint.getType();
1✔
3215
            String name = namedConstraint.getName();
1✔
3216
            List<String> columnsNames = namedConstraint.getColumnsNames();
1✔
3217
            List<String> idxSpec = namedConstraint.getIndexSpec();
1✔
3218
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
1✔
3219

3220
            // @todo: beautify the expression
3221
            // @todo: add a test case
3222
            if (name != null) {
1✔
3223
              appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
1✔
3224
              appendAlias(builder, outputFormat, name, "", "");
1✔
3225
              appendNormalizedLineBreak(builder);
1✔
3226
            }
3227

3228
            for (int j = 0; name != null && j <= indent + 1; j++) {
1✔
3229
              builder.append(indentString);
1✔
3230
            }
3231
            appendKeyWord(builder, outputFormat, type, "", " ");
1✔
3232

3233
            builder.append("( ");
1✔
3234
            int subIndent = getSubIndent(builder, columnsNames.size() > 2);
1✔
3235
            BreakLine bl = columnsNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3236
            appendStringList(columnsNames, builder, subIndent, true, bl);
1✔
3237
            builder.append(" )");
1✔
3238

3239
            // @todo: add a test case for this
3240
            if (idxSpec != null && !idxSpecText.isEmpty()) {
1✔
3241
              appendHint(builder, outputFormat, idxSpecText, " ", "");
×
3242
            }
3243

3244
          } else if (index instanceof ExcludeConstraint) {
1✔
3245
            ExcludeConstraint excludeConstraint = (ExcludeConstraint) index;
×
3246
            Expression expression = excludeConstraint.getExpression();
×
3247

3248
            // @todo: beautify the expression
3249
            // @todo: add a test case
3250

3251
            builder.append("EXCLUDE WHERE ");
×
3252
            builder.append("(");
×
3253
            builder.append(expression);
×
3254
            builder.append(")");
×
3255

3256
          } else if (index != null) {
×
3257
            String type = index.getType();
×
3258
            String name = index.getName();
×
3259
            List<Index.ColumnParams> columnParams = index.getColumns();
×
3260
            List<String> idxSpec = index.getIndexSpec();
×
3261
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
×
3262

3263
            builder.append(type);
×
3264

3265
            appendKeyWord(builder, outputFormat, type, "", " ");
×
3266
            if (name != null) {
×
3267
              appendAlias(builder, outputFormat, name, "", "");
×
3268
            }
3269

3270
            builder.append(" ").append(PlainSelect.getStringList(columnParams, true, true))
×
3271
                .append(!idxSpecText.isEmpty() ? " " + idxSpecText : "");
×
3272
          }
3273

3274
          switch (separation) {
1✔
3275
            case AFTER:
3276
              appendNormalizingTrailingWhiteSpace(builder,
1✔
3277
                  i < columnDefinitions.size() + indexes.size() - 1 ? ", " : "");
1✔
3278
              break;
3279
          }
3280

3281
          i++;
1✔
3282
        }
1✔
3283
      }
3284
      appendNormalizedLineBreak(builder).append(")");
1✔
3285
    }
3286
    List<String> tableOptionsStrings = createTable.getTableOptionsStrings();
1✔
3287
    String options = PlainSelect.getStringList(tableOptionsStrings, false, false);
1✔
3288
    if (options != null && !options.isEmpty()) {
1✔
3289
      appendHint(builder, outputFormat, options, " ", "");
1✔
3290
    }
3291

3292
    RowMovement rowMovement = createTable.getRowMovement();
1✔
3293
    if (rowMovement != null) {
1✔
3294
      // @todo: beautify this part
3295
      // @todo: provide test cases
3296
      builder.append(" ").append(rowMovement.getMode()).append(" ROW MOVEMENT");
×
3297
    }
3298

3299
    Select select = createTable.getSelect();
1✔
3300
    if (select != null) {
1✔
3301

3302
      appendNormalizedLineBreak(builder);
1✔
3303
      for (int j = 0; j <= indent; j++) {
1✔
3304
        builder.append(indentString);
1✔
3305
      }
3306
      builder.append("AS ");
1✔
3307
      appendSelect(select, builder, indent, false, false);
1✔
3308
    }
3309

3310
    Table likeTable = createTable.getLikeTable();
1✔
3311
    if (likeTable != null) {
1✔
3312

3313
      builder.append(" AS ");
×
3314

3315
      Alias alias = likeTable.getAlias();
×
3316

3317
      appendTable(likeTable, alias, builder);
×
3318
    }
3319
  }
1✔
3320

3321
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
3322
  private static void appendCreateIndex(StringBuilder builder, CreateIndex createIndex,
3323
      int indent) {
3324

3325
    Index index = createIndex.getIndex();
1✔
3326
    Table table = createIndex.getTable();
1✔
3327

3328
    List<String> tailParameters = createIndex.getTailParameters();
1✔
3329
    List<Index.ColumnParams> columnsParameters = index.getColumns();
1✔
3330

3331
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3332

3333
    if (index.getType() != null) {
1✔
3334
      appendKeyWord(builder, outputFormat, index.getType(), "", " ");
1✔
3335
    }
3336

3337
    appendKeyWord(builder, outputFormat, "INDEX", "", " ");
1✔
3338
    if (createIndex.isUsingIfNotExists()) {
1✔
3339
      appendKeyWord(builder, outputFormat, "IF NOT EXISTS", "", " ");
×
3340
    }
3341
    appendAlias(builder, outputFormat, index.getName(), "", "");
1✔
3342

3343
    appendNormalizedLineBreak(builder);
1✔
3344
    for (int j = 0; j <= indent; j++) {
1✔
3345
      builder.append(indentString);
1✔
3346
    }
3347
    appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
3348
    appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3349

3350
    if (index.getUsing() != null) {
1✔
3351
      appendKeyWord(builder, outputFormat, "USING", "  ", " ");
×
3352
      builder.append(index.getUsing());
×
3353
    }
3354

3355
    if (index.getColumnsNames() != null) {
1✔
3356
      builder.append("( ");
1✔
3357

3358
      int subIndent = getSubIndent(builder, columnsParameters.size() > 2);
1✔
3359
      BreakLine bl = columnsParameters.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3360

3361
      int i = 0;
1✔
3362
      for (Index.ColumnParams param : columnsParameters) {
1✔
3363
        appendString(param.getColumnName(), builder, subIndent, i, columnsParameters.size(), true,
1✔
3364
            bl);
3365
        i++;
1✔
3366
      }
1✔
3367

3368
      builder.append(" )");
1✔
3369

3370
      if (tailParameters != null) {
1✔
3371
        builder.append(" ");
1✔
3372
        for (String param : tailParameters) {
1✔
3373
          appendHint(builder, outputFormat, param, "", " ");
1✔
3374
        }
1✔
3375
      }
3376
    }
3377
  }
1✔
3378

3379
  private static void appendCreateView(StringBuilder builder, CreateView createView, int indent) {
3380
    boolean isOrReplace = createView.isOrReplace();
1✔
3381
    ForceOption force = createView.getForce();
1✔
3382
    TemporaryOption temp = createView.getTemporary();
1✔
3383
    boolean isMaterialized = createView.isMaterialized();
1✔
3384

3385
    Table view = createView.getView();
1✔
3386

3387
    ExpressionList<Column> columnNames = createView.getColumnNames();
1✔
3388
    Select select = createView.getSelect();
1✔
3389
    boolean isWithReadOnly = createView.isWithReadOnly();
1✔
3390

3391
    appendNormalizedLineBreak(builder);
1✔
3392

3393
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3394
    if (isOrReplace) {
1✔
3395
      appendKeyWord(builder, outputFormat, "OR REPLACE", "", " ");
1✔
3396
    }
3397
    switch (force) {
1✔
3398
      case FORCE:
3399
        appendKeyWord(builder, outputFormat, "FORCE", "", " ");
×
3400
        break;
×
3401
      case NO_FORCE:
3402
        appendKeyWord(builder, outputFormat, "NO FORCE", "", " ");
×
3403
        break;
3404
    }
3405

3406
    if (temp != TemporaryOption.NONE) {
1✔
3407
      builder.append(temp.name()).append(" ");
×
3408
    }
3409

3410
    if (isMaterialized) {
1✔
3411
      appendKeyWord(builder, outputFormat, "MATERIALIZED", "", " ");
×
3412
    }
3413
    appendKeyWord(builder, outputFormat, "VIEW", "", " ");
1✔
3414
    appendAlias(builder, outputFormat, view.getFullyQualifiedName(), "", "");
1✔
3415
    if (columnNames != null) {
1✔
3416
      builder.append(PlainSelect.getStringList(columnNames, true, true));
×
3417
    }
3418

3419
    appendNormalizedLineBreak(builder);
1✔
3420
    for (int j = 0; j <= indent; j++) {
1✔
3421
      builder.append(indentString);
1✔
3422
    }
3423
    appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
3424
    appendSelect(select, builder, indent + 2, false, false);
1✔
3425

3426
    if (isWithReadOnly) {
1✔
3427
      builder.append(" WITH READ ONLY");
×
3428
      appendHint(builder, outputFormat, "WITH READ ONLY", " ", "");
×
3429
    }
3430
  }
1✔
3431

3432
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
3433
  private static void appendAlter(StringBuilder builder, Alter alter, int indent) {
3434
    boolean useOnly = alter.isUseOnly();
1✔
3435
    Table table = alter.getTable();
1✔
3436
    List<AlterExpression> alterExpressions = alter.getAlterExpressions();
1✔
3437

3438
    appendKeyWord(builder, outputFormat, "ALTER TABLE", "", " ");
1✔
3439
    if (useOnly) {
1✔
3440
      appendKeyWord(builder, outputFormat, "ONLY", "", " ");
×
3441
    }
3442
    appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3443
    int i = 0;
1✔
3444

3445
    if (alterExpressions != null) {
1✔
3446
      for (AlterExpression alterExpression : alterExpressions) {
1✔
3447
        if (i > 0) {
1✔
3448
          appendNormalizedLineBreak(builder);
×
3449
          for (int j = 0; j <= indent; j++) {
×
3450
            builder.append(indentString);
×
3451
          }
3452
        }
3453

3454
        switch (separation) {
1✔
3455
          case BEFORE:
3456
            builder.append(i > 0 ? ", " : "");
1✔
3457
        }
3458

3459
        AlterOperation operation = alterExpression.getOperation();
1✔
3460
        String commentText = alterExpression.getCommentText();
1✔
3461
        String columnName = alterExpression.getColumnName();
1✔
3462
        String columnOldName = alterExpression.getColumnOldName();
1✔
3463

3464
        List<AlterExpression.ColumnDataType> colDataTypeList = alterExpression.getColDataTypeList();
1✔
3465
        String optionalSpecifier = alterExpression.getOptionalSpecifier();
1✔
3466

3467
        List<AlterExpression.ColumnDropNotNull> columnDropNotNullList =
1✔
3468
            alterExpression.getColumnDropNotNullList();
1✔
3469

3470
        String constraintName = alterExpression.getConstraintName();
1✔
3471
        boolean constraintIfExists = alterExpression.isUsingIfExists();
1✔
3472

3473
        List<String> pkColumns = alterExpression.getPkColumns();
1✔
3474
        List<String> ukColumns = alterExpression.getUkColumns();
1✔
3475
        String ukName = alterExpression.getUkName();
1✔
3476
        boolean uk = alterExpression.getUk();
1✔
3477

3478
        List<String> fkColumns = alterExpression.getFkColumns();
1✔
3479
        String fkSourceTable = alterExpression.getFkSourceTable();
1✔
3480
        List<String> fkSourceColumns = alterExpression.getFkSourceColumns();
1✔
3481

3482
        ReferentialAction deleteAction =
1✔
3483
            alterExpression.getReferentialAction(ReferentialAction.Type.DELETE);
1✔
3484
        ReferentialAction updateAction =
1✔
3485
            alterExpression.getReferentialAction(ReferentialAction.Type.UPDATE);
1✔
3486

3487
        Index index = alterExpression.getIndex();
1✔
3488

3489
        List<ConstraintState> constraints = alterExpression.getConstraints();
1✔
3490
        boolean useEqual = alterExpression.getUseEqual();
1✔
3491

3492
        List<String> parameters = alterExpression.getParameters();
1✔
3493

3494
        appendNormalizedLineBreak(builder);
1✔
3495

3496
        for (int j = 0; j <= indent; j++) {
1✔
3497
          builder.append(indentString);
1✔
3498
        }
3499
        if (operation == AlterOperation.RENAME_TABLE) {
1✔
3500
          appendKeyWord(builder, outputFormat, "RENAME TO", "", " ");
1✔
3501
          appendObjectName(builder, outputFormat, alterExpression.getNewTableName(), "", "");
1✔
3502
          break;
1✔
3503
        } else {
3504
          appendKeyWord(builder, outputFormat, operation.name(), "", " ");
1✔
3505
        }
3506

3507
        if (commentText != null) {
1✔
3508
          if (columnName != null) {
×
3509
            appendKeyWord(builder, outputFormat, "COMMENT", " ", " ");
×
3510
          }
3511
          builder.append(commentText);
×
3512
        } else if (columnName != null) {
1✔
3513
          if (alterExpression.hasColumn()) {
1✔
3514
            appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
1✔
3515
          }
3516
          if (operation == AlterOperation.RENAME) {
1✔
3517
            appendObjectName(builder, outputFormat, columnOldName, "", "");
1✔
3518
            appendKeyWord(builder, outputFormat, "TO", " ", " ");
1✔
3519
          }
3520
          appendObjectName(builder, outputFormat, columnName, "", "");
1✔
3521

3522
        } else if (operation == AlterOperation.DROP && !alterExpression.hasColumn()
1✔
3523
            && alterExpression.getPkColumns() != null) {
1✔
3524
          // Oracle supports dropping multiple columns
3525
          // we use the PKColumns List in this case instead of the Column
3526

3527
          List<String> columns = alterExpression.getPkColumns();
1✔
3528

3529
          builder.append("(");
1✔
3530

3531
          int subIndent = getSubIndent(builder, columns.size() > 3);
1✔
3532
          BreakLine bl = columns.size() > 3 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3533

3534
          appendStringList(alterExpression.getPkColumns(), builder, subIndent, true, bl);
1✔
3535
          builder.append(" )");
1✔
3536

3537
        } else if (colDataTypeList != null) {
1✔
3538

3539
          int colWidth = 0;
1✔
3540
          int typeWidth = 0;
1✔
3541

3542
          BreakLine breakLine =
3543
              colDataTypeList.size() > 1 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3544

3545
          if (operation == AlterOperation.CHANGE) {
1✔
3546
            if (optionalSpecifier != null) {
×
3547
              builder.append(optionalSpecifier).append(" ");
×
3548
            }
3549
            appendObjectName(builder, outputFormat, columnOldName, "", " ");
×
3550
          } else if (colDataTypeList.size() > 1) {
1✔
3551

3552
            for (ColumnDefinition columnDefinition : colDataTypeList) {
1✔
3553
              String columnName1 = columnDefinition.getColumnName();
1✔
3554
              // @todo: please get rid of that Replace workaround
3555
              String colDataType = columnDefinition.getColDataType().toString().replace(", ", ",");
1✔
3556

3557
              if (colWidth < columnName1.length()) {
1✔
3558
                colWidth = columnName1.length();
1✔
3559
              }
3560

3561
              if (typeWidth < colDataType.length()) {
1✔
3562
                typeWidth = colDataType.length();
1✔
3563
              }
3564
            }
1✔
3565

3566
            builder.append("( ");
1✔
3567

3568
          } else {
3569
            if (alterExpression.hasColumn()) {
1✔
3570
              appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
1✔
3571
            }
3572
          }
3573

3574
          int subIndent = getSubIndent(builder, colDataTypeList.size() > 1);
1✔
3575
          int typeIndex = subIndent + (colWidth / indentString.length()) + 1;
1✔
3576
          int specIndex = indent + typeIndex + (typeWidth / indentString.length()) + 1;
1✔
3577

3578
          for (ColumnDefinition columnDefinition : colDataTypeList) {
1✔
3579
            if (i > 0 || breakLine.equals(BreakLine.ALWAYS)) {
1✔
3580
              if (!breakLine.equals(BreakLine.NEVER)) {
1✔
3581
                appendNormalizedLineBreak(builder);
1✔
3582
                for (int j = 0; j < subIndent; j++) {
1✔
3583
                  builder.append(indentString);
1✔
3584
                }
3585
              }
3586
              builder.append(", ");
1✔
3587
            }
3588

3589
            String columnName1 = columnDefinition.getColumnName();
1✔
3590
            ColDataType colDataType = columnDefinition.getColDataType();
1✔
3591
            List<String> columnSpecs = columnDefinition.getColumnSpecs();
1✔
3592

3593
            appendObjectName(builder, outputFormat, columnName1, "", " ");
1✔
3594

3595
            int lastLineLength = getLastLineLength(builder);
1✔
3596

3597
            for (int j = lastLineLength; j <= typeIndex * indentWidth; j++) {
1✔
3598
              builder.append(" ");
1✔
3599
            }
3600
            // @todo: please get rid of that Replace workaround
3601
            appendType(builder, outputFormat, colDataType.toString().replace(", ", ","), "", "");
1✔
3602

3603
            lastLineLength = getLastLineLength(builder);
1✔
3604

3605
            if (columnSpecs != null && !columnSpecs.isEmpty()) {
1✔
3606
              if (colDataTypeList.size() > 1) {
1✔
3607
                for (int j = lastLineLength; j <= specIndex * indentWidth; j++) {
1✔
3608
                  builder.append(" ");
1✔
3609
                }
3610
              } else {
3611
                builder.append(" ");
1✔
3612
              }
3613

3614
              appendType(builder, outputFormat,
1✔
3615
                  PlainSelect.getStringList(columnSpecs, false, false), "", "");
1✔
3616
            }
3617
            i++;
1✔
3618
          }
1✔
3619

3620
          if (colDataTypeList.size() > 1) {
1✔
3621
            builder.append(")");
1✔
3622
          }
3623
        } else if (columnDropNotNullList != null) {
1✔
3624
          if (operation == AlterOperation.CHANGE) {
×
3625
            if (optionalSpecifier != null) {
×
3626
              builder.append(optionalSpecifier).append(" ");
×
3627
            }
3628
            appendObjectName(builder, outputFormat, columnOldName, "", " ");
×
3629
          } else if (columnDropNotNullList.size() > 1) {
×
3630
            builder.append("(");
×
3631
          } else {
3632
            if (alterExpression.hasColumn()) {
×
3633
              appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
×
3634
            }
3635
          }
3636
          builder.append(PlainSelect.getStringList(columnDropNotNullList));
×
3637
          if (columnDropNotNullList.size() > 1) {
×
3638
            builder.append(")");
×
3639
          }
3640
        } else if (constraintName != null) {
1✔
3641
          appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
×
3642

3643
          if (constraintIfExists) {
×
3644
            appendKeyWord(builder, outputFormat, "IF EXISTS", "", " ");
×
3645
          }
3646
          appendObjectName(builder, outputFormat, constraintName, "", "");
×
3647
        } else if (pkColumns != null) {
1✔
3648
          appendKeyWord(builder, outputFormat, "PRIMARY KEY", "", " (");
×
3649

3650
          builder.append(PlainSelect.getStringList(pkColumns)).append(")");
×
3651
        } else if (ukColumns != null) {
1✔
3652
          appendKeyWord(builder, outputFormat, "UNIQUE", "", "");
×
3653
          if (ukName != null) {
×
3654
            if (uk) {
×
3655
              appendKeyWord(builder, outputFormat, "KEY", " ", " ");
×
3656
            } else {
3657
              appendKeyWord(builder, outputFormat, "INDEX", " ", " ");
×
3658
            }
3659
            appendObjectName(builder, outputFormat, ukName, "", "");
×
3660
          }
3661
          builder.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")");
×
3662
        } else if (fkColumns != null) {
1✔
3663
          appendKeyWord(builder, outputFormat, "FOREIGN KEY", "", " (");
1✔
3664
          builder.append(PlainSelect.getStringList(fkColumns)).append(")");
1✔
3665

3666
          appendNormalizedLineBreak(builder);
1✔
3667
          for (int j = 0; j <= indent + 1; j++) {
1✔
3668
            builder.append(indentString);
1✔
3669
          }
3670

3671
          appendKeyWord(builder, outputFormat, "REFERENCES", "", " ");
1✔
3672

3673
          builder.append(fkSourceTable).append(" (")
1✔
3674
              .append(PlainSelect.getStringList(fkSourceColumns)).append(")");
1✔
3675
          // referentialActions.forEach(b::append);
3676
          if (updateAction != null) {
1✔
3677
            builder.append(updateAction);
×
3678
          }
3679

3680
          if (deleteAction != null) {
1✔
3681
            builder.append(deleteAction);
×
3682
          }
3683
        } else if (index != null) {
×
3684
          builder.append(index);
×
3685
        }
3686
        if (constraints != null && !constraints.isEmpty()) {
1✔
3687
          builder.append(" ").append(PlainSelect.getStringList(constraints, false, false));
×
3688
        }
3689
        if (useEqual) {
1✔
3690
          builder.append("=");
×
3691
        }
3692
        if (parameters != null && !parameters.isEmpty()) {
1✔
3693
          builder.append(" ").append(PlainSelect.getStringList(parameters, false, false));
×
3694
        }
3695

3696
        switch (separation) {
1✔
3697
          case AFTER:
3698
            appendNormalizingTrailingWhiteSpace(builder,
×
3699
                i < alterExpressions.size() - 1 ? ", " : "");
×
3700
            break;
3701
        }
3702

3703
        i++;
1✔
3704
      }
1✔
3705
    }
3706
  }
1✔
3707

3708
  public enum OutputFormat {
1✔
3709
    PLAIN, ANSI, HTML, RTF, XSLFO
1✔
3710
  }
3711

3712
  public enum Spelling {
1✔
3713
    UPPER, LOWER, CAMEL, KEEP
1✔
3714
  }
3715

3716
  public enum Separation {
1✔
3717
    BEFORE, AFTER
1✔
3718
  }
3719

3720
  public enum BreakLine {
1✔
3721
    NEVER // keep all arguments on one line
1✔
3722
    , AS_NEEDED // only when more than 3 arguments
1✔
3723
    , AFTER_FIRST // break all after the first argument
1✔
3724
    , ALWAYS // break all arguments to a new line
1✔
3725
  }
3726

3727
  public enum SquaredBracketQuotation {
1✔
3728
    AUTO, YES, NO
1✔
3729
  }
3730

3731
  public enum ShowLineNumbers {
1✔
3732
    YES, NO
1✔
3733
  }
3734

3735
  public enum BackSlashQuoting {
1✔
3736
    YES, NO
1✔
3737
  }
3738

3739
  public enum StatementTerminator {
1✔
3740
    SEMICOLON, NONE, GO, BACKSLASH
1✔
3741
  }
3742

3743
  public enum FormattingOption {
1✔
3744
    SQUARE_BRACKET_QUOTATION("squareBracketQuotation")
1✔
3745

3746
    , BACKSLASH_QUOTING("backSlashQuoting")
1✔
3747

3748
    , OUTPUT_FORMAT("outputFormat")
1✔
3749

3750
    , KEYWORD_SPELLING("keywordSpelling")
1✔
3751

3752
    , FUNCTION_SPELLING("functionSpelling")
1✔
3753

3754
    , OBJECT_SPELLING("objectSpelling")
1✔
3755

3756
    , SEPARATION("separation")
1✔
3757

3758
    , INDENT_WIDTH("indentWidth")
1✔
3759

3760
    , SHOW_LINE_NUMBERS("showLineNumbers")
1✔
3761

3762
    , STATEMENT_TERMINATOR("statementTerminator")
1✔
3763

3764
    ;
3765

3766
    public final String optionName;
3767

3768
    FormattingOption(String optionName) {
1✔
3769
      this.optionName = optionName;
1✔
3770
    }
1✔
3771

3772
    @Override
3773
    public String toString() {
3774
      return optionName;
1✔
3775
    }
3776

3777
    public void addFormatterOption(String value, ArrayList<String> formatterOptions) {
3778
      formatterOptions.add(optionName + "=" + value);
×
3779
    }
×
3780
  }
3781

3782
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc