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

manticore-projects / jsqlformatter / #5

08 May 2024 09:47AM CUT coverage: 77.807% (+7.4%) from 70.428%
#5

push

github

manticore-projects
build: update Gradle and Github actions

Signed-off-by: Andreas Reichel <andreas@manticore-projects.com>

1746 of 2244 relevant lines covered (77.81%)

0.78 hits per line

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

76.9
/src/main/java/com/manticore/jsqlformatter/JSQLFormatter.java
1
/**
2
 * Manticore Projects JSQLFormatter is a SQL Beautifying and Formatting Software.
3
 * Copyright (C) 2023 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
  private static final AnsiFormat ANSI_FORMAT_LINE_NUMBER =
1✔
152
      new AnsiFormat(Attribute.BRIGHT_BLACK_BACK(), Attribute.DESATURATED());
1✔
153
  private static final AnsiFormat ANSI_FORMAT_KEYWORD =
1✔
154
      new AnsiFormat(Attribute.BLUE_TEXT(), Attribute.BOLD());
1✔
155
  private static final AnsiFormat ANSI_FORMAT_HINT = new AnsiFormat(Attribute.BRIGHT_BLUE_TEXT());
1✔
156
  private static final AnsiFormat ANSI_FORMAT_OPERATOR = new AnsiFormat(Attribute.BLUE_TEXT());
1✔
157
  private static final AnsiFormat ANSI_FORMAT_PARAMETER =
1✔
158
      new AnsiFormat(Attribute.YELLOW_TEXT(), Attribute.DESATURATED());
1✔
159
  private static final AnsiFormat ANSI_FORMAT_ALIAS =
1✔
160
      new AnsiFormat(Attribute.RED_TEXT(), Attribute.BOLD(), Attribute.DESATURATED());
1✔
161
  private static final AnsiFormat ANSI_FORMAT_FUNCTION =
1✔
162
      new AnsiFormat(Attribute.BRIGHT_RED_TEXT());
1✔
163
  private static final AnsiFormat ANSI_FORMAT_TYPE =
1✔
164
      new AnsiFormat(Attribute.YELLOW_TEXT(), Attribute.DESATURATED());
1✔
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

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

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

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

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

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

194
  public static Separation getSeparation() {
195
    return separation;
×
196
  }
197

198
  public static void setSeparation(Separation separation) {
199
    JSQLFormatter.separation = separation;
×
200
  }
×
201

202
  public static Spelling getKeywordSpelling() {
203
    return keywordSpelling;
×
204
  }
205

206
  public static void setKeywordSpelling(Spelling keywordSpelling) {
207
    JSQLFormatter.keywordSpelling = keywordSpelling;
×
208
  }
×
209

210
  public static Spelling getFunctionSpelling() {
211
    return functionSpelling;
×
212
  }
213

214
  public static void setFunctionSpelling(Spelling functionSpelling) {
215
    JSQLFormatter.functionSpelling = functionSpelling;
×
216
  }
×
217

218
  public static Spelling getObjectSpelling() {
219
    return objectSpelling;
×
220
  }
221

222
  public static void setObjectSpelling(Spelling objectSpelling) {
223
    JSQLFormatter.objectSpelling = objectSpelling;
×
224
  }
×
225

226
  public static OutputFormat getOutputFormat() {
227
    return outputFormat;
×
228
  }
229

230
  public static void setOutputFormat(OutputFormat outputFormat) {
231
    JSQLFormatter.outputFormat = outputFormat;
×
232
  }
×
233

234
  public static int getIndentWidth() {
235
    return indentWidth;
1✔
236
  }
237

238
  public static void setIndentWidth(int indentWidth) {
239
    JSQLFormatter.indentWidth = indentWidth;
×
240

241
    char[] chars = new char[indentWidth];
×
242
    Arrays.fill(chars, ' ');
×
243

244
    JSQLFormatter.indentString = new String(chars);
×
245
  }
×
246

247
  public static String getIndentString() {
248
    return indentString;
×
249
  }
250

251
  public static void setIndentString(String indentString) {
252
    JSQLFormatter.indentString = indentString;
×
253
  }
×
254

255
  private static void appendDecodeExpressionsList(ExpressionList<?> parameters, BreakLine breakLine,
256
      StringBuilder builder, int indent) {
257
    int subIndent = breakLine.equals(BreakLine.NEVER) ? indent : getSubIndent(builder, false);
1✔
258

259
    int i = 0;
1✔
260
    for (Expression expression : parameters) {
1✔
261
      switch (breakLine) {
1✔
262
        case AS_NEEDED:
263
          BreakLine bl = i == 0 || (i - 1) % 2 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
264
          appendExpression(expression, null, builder, subIndent, i, parameters.size(), true, bl);
1✔
265
          break;
1✔
266

267
        default:
268
          appendExpression(expression, null, builder, subIndent, i, parameters.size(), true,
×
269
              breakLine);
270
      }
271
      i++;
1✔
272
    }
1✔
273
  }
1✔
274

275
  static String toCamelCase(String s) {
276
    StringBuilder camelCaseString = new StringBuilder();
1✔
277

278
    String[] nameParts = s.split("_");
1✔
279
    int i = 0;
1✔
280
    for (String part : nameParts) {
1✔
281
      if (i > 0) {
1✔
282
        camelCaseString.append("_");
1✔
283
      }
284
      camelCaseString.append(part.substring(0, 1).toUpperCase())
1✔
285
          .append(part.substring(1).toLowerCase());
1✔
286
      i++;
1✔
287
    }
288
    return camelCaseString.toString();
1✔
289
  }
290

291
  private static StringBuilder appendKeyWord(StringBuilder builder, OutputFormat format,
292
      String keyword, String before, String after) {
293

294
    String s;
295
    switch (keywordSpelling) {
1✔
296
      case UPPER:
297
        s = keyword.toUpperCase();
1✔
298
        break;
1✔
299
      case LOWER:
300
        s = keyword.toLowerCase();
1✔
301
        break;
1✔
302
      case CAMEL:
303
        s = toCamelCase(keyword);
×
304
        break;
×
305
      default:
306
        s = keyword;
×
307
    }
308

309
    switch (format) {
1✔
310
      case ANSI:
311
        builder.append(before).append(ANSI_FORMAT_KEYWORD.format(s)).append(after);
1✔
312
        break;
1✔
313
      case HTML:
314
        builder.append(before).append("<span style=\"color:blue; font-style:bold;\">").append(s)
1✔
315
            .append("</span>").append(after);
1✔
316
        break;
1✔
317
      default:
318
        builder.append(before).append(s).append(after);
1✔
319
        break;
320
    }
321
    return builder;
1✔
322
  }
323

324
  private static StringBuilder appendNormalizingTrailingWhiteSpace(StringBuilder builder,
325
      String s) {
326
    if (builder.length() > 0) {
1✔
327
      int pos = builder.length() - 1;
1✔
328
      char lastChar = builder.charAt(pos);
1✔
329
      if (lastChar == ' ') {
1✔
330
        while (lastChar == ' ' && pos > 0) {
1✔
331
          pos--;
1✔
332
          lastChar = builder.charAt(pos);
1✔
333
        }
334
        builder.setLength(pos + 1);
1✔
335
      }
336
    }
337
    builder.append(s);
1✔
338
    return builder;
1✔
339
  }
340

341
  private static StringBuilder appendNormalizedLineBreak(StringBuilder builder) {
342
    switch (showLineNumbers) {
1✔
343
      case YES:
344
        lineCount++;
1✔
345

346
        String lineCountStr = "0000" + lineCount;
1✔
347
        lineCountStr = lineCountStr.substring(lineCountStr.length() - 5);
1✔
348
        lineCountStr += " | ";
1✔
349

350
        StringBuilder fillerStr = new StringBuilder();
1✔
351
        int adjust = getIndentWidth() - lineCountStr.length() % getIndentWidth();
1✔
352
        for (int i = 0; i < adjust; i++) {
1✔
353
          fillerStr.append(" ");
1✔
354
        }
355

356

357
        switch (outputFormat) {
1✔
358
          case ANSI:
359
            return appendNormalizingTrailingWhiteSpace(builder, Ansi.RESET
1✔
360
                + ANSI_FORMAT_LINE_NUMBER.format("\n" + lineCountStr) + Ansi.RESET + fillerStr);
1✔
361
          case HTML:
362
            return builder.append("\n")
1✔
363
                .append("<span style=\"color:light-grey; font-size:8pt; font-style:normal;\">")
1✔
364
                .append(lineCountStr).append("</span>").append(fillerStr);
1✔
365
          default:
366
            return appendNormalizingTrailingWhiteSpace(builder, "\n" + lineCountStr + fillerStr);
1✔
367
        }
368
      default:
369
        return appendNormalizingTrailingWhiteSpace(builder, "\n");
1✔
370
    }
371
  }
372

373
  private static StringBuilder appendHint(StringBuilder builder, OutputFormat format, String hint,
374
      String before, String after) {
375

376
    String s;
377
    switch (keywordSpelling) {
1✔
378
      case UPPER:
379
        s = hint.toUpperCase();
1✔
380
        break;
1✔
381
      case LOWER:
382
        s = hint.toLowerCase();
1✔
383
        break;
1✔
384
      case CAMEL:
385
        s = toCamelCase(hint);
×
386
        break;
×
387
      default:
388
        s = hint;
×
389
    }
390

391
    switch (format) {
1✔
392
      case ANSI:
393
        builder.append(before).append(ANSI_FORMAT_HINT.format(s)).append(after);
×
394
        break;
×
395
      case HTML:
396
        builder.append(before).append("<span style=\"color:light-blue; font-style:thin\">")
×
397
            .append(s).append("</span>").append(after);
×
398
        break;
×
399
      default:
400
        builder.append(before).append(s).append(after);
1✔
401
        break;
402
    }
403
    return builder;
1✔
404
  }
405

406
  private static StringBuilder appendOperator(StringBuilder builder, OutputFormat format,
407
      String operator, String before, String after) {
408

409
    String s;
410
    switch (keywordSpelling) {
1✔
411
      case UPPER:
412
        s = operator.toUpperCase();
1✔
413
        break;
1✔
414
      case LOWER:
415
        s = operator.toLowerCase();
1✔
416
        break;
1✔
417
      case CAMEL:
418
        s = toCamelCase(operator);
×
419
        break;
×
420
      default:
421
        s = operator;
×
422
    }
423

424
    switch (format) {
1✔
425
      case ANSI:
426
        builder.append(before).append(ANSI_FORMAT_OPERATOR.format(s)).append(after);
1✔
427
        break;
1✔
428
      case HTML:
429
        builder.append(before).append("<span style=\"color:blue; font-style:normal;\">").append(s)
1✔
430
            .append("</span>").append(after);
1✔
431
        break;
1✔
432
      default:
433
        builder.append(before).append(s).append(after);
1✔
434
        break;
435
    }
436
    return builder;
1✔
437
  }
438

439
  private static StringBuilder appendValue(StringBuilder builder, OutputFormat format, String value,
440
      String before, String after) {
441
    switch (format) {
1✔
442
      case ANSI:
443
        builder.append(before).append(ANSI_FORMAT_PARAMETER.format(value)).append(after);
1✔
444
        break;
1✔
445
      case HTML:
446
        builder.append(before).append("<span style=\"color:yellow; font-style:normal;\">")
1✔
447
            .append(value).append("</span>").append(after);
1✔
448
        break;
1✔
449
      default:
450
        builder.append(before).append(value).append(after);
1✔
451
        break;
452
    }
453
    return builder;
1✔
454
  }
455

456
  private static StringBuilder appendAlias(StringBuilder builder, OutputFormat format, String alias,
457
      String before, String after) {
458

459
    String s;
460
    if (alias.trim().startsWith("\"") || alias.trim().startsWith("[")) {
1✔
461
      s = alias;
1✔
462
    } else {
463
      switch (objectSpelling) {
1✔
464
        case UPPER:
465
          s = alias.toUpperCase();
1✔
466
          break;
1✔
467
        case LOWER:
468
          s = alias.toLowerCase();
1✔
469
          break;
1✔
470
        case CAMEL:
471
          s = toCamelCase(alias);
×
472
          break;
×
473
        default:
474
          s = alias;
×
475
      }
476
    }
477

478
    switch (format) {
1✔
479
      case ANSI:
480
        builder.append(before).append(ANSI_FORMAT_ALIAS.format(s)).append(after);
×
481
        break;
×
482
      case HTML:
483
        builder.append(before).append("<span style=\"color:red; font-style:bold;\">").append(s)
×
484
            .append("</span>").append(after);
×
485
        break;
×
486
      default:
487
        builder.append(before).append(s).append(after);
1✔
488
        break;
489
    }
490
    return builder;
1✔
491
  }
492

493
  private static StringBuilder appendObjectName(StringBuilder builder, OutputFormat format,
494
      String objectName, String before, String after) {
495

496
    StringBuilder nameBuilder = new StringBuilder();
1✔
497

498
    int j = 0;
1✔
499
    String[] parts = objectName.contains(".") ? objectName.split("\\.") : new String[] {objectName};
1✔
500
    for (String w : parts) {
1✔
501
      if (j > 0) {
1✔
502
        nameBuilder.append(".");
1✔
503
      }
504
      if (w.trim().startsWith("\"") || w.trim().startsWith("[")) {
1✔
505
        nameBuilder.append(w);
1✔
506
      } else {
507
        switch (objectSpelling) {
1✔
508
          case UPPER:
509
            nameBuilder.append(w.toUpperCase());
1✔
510
            break;
1✔
511
          case LOWER:
512
            nameBuilder.append(w.toLowerCase());
1✔
513
            break;
1✔
514
          case CAMEL:
515
            nameBuilder.append(toCamelCase(w));
×
516
            break;
517
        }
518
      }
519
      j++;
1✔
520
    }
521

522
    switch (format) {
1✔
523
      default:
524
        builder.append(before).append(nameBuilder).append(after);
1✔
525
        break;
526
    }
527
    return builder;
1✔
528
  }
529

530
  private static StringBuilder appendFunction(StringBuilder builder, OutputFormat format,
531
      String function, String before, String after) {
532

533
    String s;
534
    switch (functionSpelling) {
1✔
535
      case UPPER:
536
        s = function.toUpperCase();
×
537
        break;
×
538
      case LOWER:
539
        s = function.toLowerCase();
×
540
        break;
×
541
      case CAMEL:
542
        s = toCamelCase(function);
1✔
543
        break;
1✔
544
      default:
545
        s = function;
1✔
546
    }
547

548
    switch (format) {
1✔
549
      case ANSI:
550
        builder.append(before).append(ANSI_FORMAT_FUNCTION.format(s)).append(after);
×
551
        break;
×
552
      case HTML:
553
        builder.append(before).append("<span style=\"color:light-red; font-style:italic;\">")
×
554
            .append(s).append("</span>").append(after);
×
555
        break;
×
556
      default:
557
        builder.append(before).append(s).append(after);
1✔
558
        break;
559
    }
560
    return builder;
1✔
561
  }
562

563
  private static StringBuilder appendType(StringBuilder builder, OutputFormat format, String type,
564
      String before, String after) {
565

566
    String s;
567
    switch (keywordSpelling) {
1✔
568
      case UPPER:
569
        s = type.toUpperCase();
1✔
570
        break;
1✔
571
      case LOWER:
572
        s = type.toLowerCase();
1✔
573
        break;
1✔
574
      case CAMEL:
575
        s = toCamelCase(type);
×
576
        break;
×
577
      default:
578
        s = type;
×
579
    }
580

581
    switch (format) {
1✔
582
      case ANSI:
583
        builder.append(before).append(ANSI_FORMAT_TYPE.format(s)).append(after);
×
584
        break;
×
585
      case HTML:
586
        builder.append(before).append("<span style=\"color:yellow; font-style:normal;\">").append(s)
×
587
            .append("</span>").append(after);
×
588
        break;
×
589
      default:
590
        builder.append(before).append(s).append(after);
1✔
591
        break;
592
    }
593
    return builder;
1✔
594
  }
595

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

600
    return lastLine.length();
1✔
601
  }
602

603
  private static int getSubIndent(StringBuilder builder, boolean moveToTab) {
604
    int lastLineLength = getLastLineLength(builder);
1✔
605

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

608
    for (int i = lastLineLength; moveToTab && i < subIndent * indentWidth; i++) {
1✔
609
      builder.append(" ");
1✔
610
    }
611

612
    return subIndent;
1✔
613
  }
614

615
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
616
  private static void appendDelete(StringBuilder builder, Delete delete, int indent) {
617
    List<WithItem> withItems = delete.getWithItemsList();
1✔
618
    if (withItems != null && !withItems.isEmpty()) {
1✔
619
      int i = 0;
1✔
620
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
621

622
      for (WithItem withItem : withItems) {
1✔
623
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
624
        i++;
1✔
625
      }
1✔
626

627
      appendNormalizedLineBreak(builder);
1✔
628
    }
629
    appendKeyWord(builder, outputFormat, "DELETE", "", " ");
1✔
630

631
    OracleHint oracleHint = delete.getOracleHint();
1✔
632
    if (oracleHint != null) {
1✔
633
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
634
    }
635

636
    List<Table> tables = delete.getTables();
1✔
637

638
    if (tables != null && !tables.isEmpty()) {
1✔
639
      int j = 0;
1✔
640
      for (Table table : tables) {
1✔
641
        switch (separation) {
1✔
642
          case AFTER:
643
            appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "",
×
644
                j < tables.size() - 1 ? ", " : " ");
×
645
            break;
×
646
          case BEFORE:
647
          default:
648
            appendObjectName(builder, outputFormat, table.getFullyQualifiedName(),
1✔
649
                j > 0 ? ", " : "", " ");
1✔
650
            break;
651
        }
652

653
        j++;
1✔
654
      }
1✔
655
    }
656

657
    appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
658

659
    Table table = delete.getTable();
1✔
660
    Alias alias = table.getAlias();
1✔
661

662
    appendTable(table, alias, builder);
1✔
663

664
    List<Join> joins = delete.getJoins();
1✔
665
    appendJoins(joins, builder, indent);
1✔
666

667
    Expression whereExpression = delete.getWhere();
1✔
668
    appendWhere(whereExpression, builder, indent);
1✔
669

670
    List<OrderByElement> orderByElements = delete.getOrderByElements();
1✔
671
    appendOrderByElements(orderByElements, builder, indent);
1✔
672

673
    Limit limit = delete.getLimit();
1✔
674
    if (limit != null) {
1✔
675
      appendNormalizedLineBreak(builder);
×
676
      for (int j = 0; j < indent; j++) {
×
677
        builder.append(indentString);
×
678
      }
679

680
      appendKeyWord(builder, outputFormat, "LIMIT", "", "");
×
681

682
      Expression rowCount = limit.getRowCount();
×
683
      if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
×
684
        // no offset allowed
685
        appendKeyWord(builder, outputFormat, "NULL", " ", "");
×
686
      } else {
687
        if (null != limit.getOffset()) {
×
688
          appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false, BreakLine.NEVER);
×
689
          builder.append(", ");
×
690
        }
691
        if (null != limit.getRowCount()) {
×
692
          appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
×
693
              BreakLine.NEVER);
694
        }
695
      }
696
    }
697
  }
1✔
698

699
  public static File getAbsoluteFile(String filename) {
700
    String homePath = new File(System.getProperty("user.home")).toURI().getPath();
×
701

702
    String _filename = filename.replaceFirst("~", Matcher.quoteReplacement(homePath))
×
703
        .replaceFirst("\\$\\{user.home}", Matcher.quoteReplacement(homePath));
×
704

705
    File f = new File(_filename);
×
706
    if (!f.isAbsolute()) {
×
707
      Path basePath = Paths.get("").toAbsolutePath();
×
708

709
      Path resolvedPath = basePath.resolve(filename);
×
710
      Path absolutePath = resolvedPath.normalize();
×
711
      f = absolutePath.toFile();
×
712
    }
713
    return f;
×
714
  }
715

716
  public static String getAbsoluteFileName(String filename) {
717
    return getAbsoluteFile(filename).getAbsolutePath();
×
718
  }
719

720
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
721
  public static ArrayList<Exception> verify(String sqlStr, String... options) {
722
    ArrayList<Exception> exceptions = new ArrayList<>();
×
723

724
    applyFormattingOptions(options);
×
725

726
    Pattern SEMICOLON_PATTERN = Pattern.compile(";|$");
×
727
    Matcher m = SEMICOLON_PATTERN.matcher(sqlStr);
×
728
    ArrayList<Integer> semicolons = new ArrayList<>();
×
729

730
    while (m.find()) {
×
731
      semicolons.add(m.start());
×
732
    }
733

734
    m = CommentMap.COMMENT_PATTERN.matcher(sqlStr);
×
735
    while (m.find()) {
×
736
      int start = m.start();
×
737
      int end = m.end();
×
738

739
      int n = semicolons.size();
×
740
      for (int i = n - 1; i >= 0; i--) {
×
741
        int pos = semicolons.get(i);
×
742
        if (start <= pos && pos < end) {
×
743
          semicolons.remove(i);
×
744
        }
745
      }
746
    }
×
747

748
    int pos = 0;
×
749
    int length = sqlStr.length();
×
750
    int n = semicolons.size();
×
751
    for (int i = 0; i < n; i++) {
×
752
      int semicolonPos = semicolons.get(i);
×
753

754
      if (semicolonPos > pos) {
×
755
        String statementSql = sqlStr.substring(pos, Integer.min(semicolonPos + 1, length));
×
756
        pos = semicolonPos + 1;
×
757

758
        // we are at the end and find only remaining whitespace
759
        if (statementSql.trim().isEmpty()) {
×
760
          break;
×
761
        }
762

763
        boolean useSquareBracketQuotation;
764
        switch (squaredBracketQuotation) {
×
765
          case YES:
766
            useSquareBracketQuotation = true;
×
767
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
768
                useSquareBracketQuotation);
×
769
            break;
×
770
          case NO:
771
            useSquareBracketQuotation = false;
×
772
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
773
                useSquareBracketQuotation);
×
774
            break;
×
775
          case AUTO:
776
          default:
777
            useSquareBracketQuotation =
×
778
                SQUARED_BRACKET_QUOTATION_PATTERN.matcher(statementSql).find();
×
779
            LOGGER.log(Level.FINE, "Square Bracket Quotation auto-detected as {0}.",
×
780
                useSquareBracketQuotation);
×
781
        }
782
        try {
783
          CCJSqlParserUtil.parse(statementSql,
×
784
              parser -> parser.withSquareBracketQuotation(useSquareBracketQuotation));
×
785

786
        } catch (Exception ex1) {
×
787
          exceptions.add(new Exception("Cannot parse the Statement:\n" + statementSql, ex1));
×
788
        }
×
789
      } else {
790
        break;
791
      }
792
    }
793
    return exceptions;
×
794
  }
795

796
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
797
  public static String format(String sqlStr, String... options) throws Exception {
798
    applyFormattingOptions(options);
1✔
799

800
    StringBuilder builder = new StringBuilder();
1✔
801

802
    int indent = 0;
1✔
803
    lineCount = 0;
1✔
804

805
    // Pattern SEMICOLON_PATTERN =
806
    // Pattern.compile("((?:(?:'[^']*+')|(?:\"[^\"]*+\")|(?:--.*)|(?:\\/\\*[^\\*\\/]*+\\*\\/)|[^;])*+);");
807
    Pattern SEMICOLON_PATTERN = Pattern.compile(";|$|\\n\\n\\n");
1✔
808
    Matcher m = SEMICOLON_PATTERN.matcher(sqlStr);
1✔
809
    ArrayList<Integer> semicolons = new ArrayList<>();
1✔
810

811
    while (m.find()) {
1✔
812
      semicolons.add(m.start());
1✔
813
    }
814

815
    m = CommentMap.COMMENT_PATTERN.matcher(sqlStr);
1✔
816
    while (m.find()) {
1✔
817
      int start = m.start();
1✔
818
      int end = m.end();
1✔
819

820
      int n = semicolons.size();
1✔
821
      for (int i = n - 1; i >= 0; i--) {
1✔
822
        int pos = semicolons.get(i);
1✔
823
        if (start <= pos && pos < end) {
1✔
824
          semicolons.remove(i);
1✔
825
        }
826
      }
827
    }
1✔
828

829
    int pos = 0;
1✔
830
    int length = sqlStr.length();
1✔
831
    int n = semicolons.size();
1✔
832
    for (int i = 0; i < n; i++) {
1✔
833
      int semicolonPos = semicolons.get(i);
1✔
834

835
      if (semicolonPos > pos) {
1✔
836
        String statementSql = sqlStr.substring(pos, Integer.min(semicolonPos + 1, length));
1✔
837
        pos = semicolonPos + 1;
1✔
838

839
        // we are at the end and find only remaining whitespace
840
        // or comments
841
        if (statementSql.trim().isEmpty() || i == n - 1 && statementSql.trim().startsWith("--")
1✔
842
            || i == n - 1 && statementSql.trim().startsWith("/*")) {
×
843
          break;
×
844
        }
845

846
        StringBuilder statementBuilder = new StringBuilder();
1✔
847

848
        boolean useSquareBracketQuotation;
849
        switch (squaredBracketQuotation) {
1✔
850
          case YES:
851
            useSquareBracketQuotation = true;
×
852
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
853
                useSquareBracketQuotation);
×
854
            break;
×
855
          case NO:
856
            useSquareBracketQuotation = false;
×
857
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
858
                useSquareBracketQuotation);
×
859
            break;
×
860
          case AUTO:
861
          default:
862
            useSquareBracketQuotation =
1✔
863
                SQUARED_BRACKET_QUOTATION_PATTERN.matcher(statementSql).find();
1✔
864
            LOGGER.log(Level.FINE, "Square Bracket Quotation auto-detected as {0}.",
1✔
865
                useSquareBracketQuotation);
1✔
866
        }
867

868
        boolean useBackSlashQuoting;
869
        if (Objects.requireNonNull(backSlashQuoting) == BackSlashQuoting.YES) {
1✔
870
          useBackSlashQuoting = true;
×
871
          LOGGER.log(Level.FINE, "Back Slash Quoting set as {0}.", true);
×
872
        } else {
873
          useBackSlashQuoting = false;
1✔
874
          LOGGER.log(Level.FINE, "Back Slash Quoting set as {0}.", false);
1✔
875
        }
876

877
        CommentMap commentMap = new CommentMap(statementSql);
1✔
878

879
        Pattern DIRECTIVE_PATTERN = Pattern.compile("@JSQLFormatter\\s?\\((.*)\\)");
1✔
880
        for (Comment comment : commentMap.values()) {
1✔
881
          Matcher m1 = DIRECTIVE_PATTERN.matcher(comment.text);
1✔
882
          if (m1.find()) {
1✔
883
            String[] keyValuePairs = m1.group(1).split(",");
1✔
884
            applyFormattingOptions(keyValuePairs);
1✔
885
          }
886
        }
1✔
887

888
        try {
889
          Statement statement =
1✔
890
              CCJSqlParserUtil.parse(CCJSqlParserUtil.sanitizeSingleSql(statementSql),
1✔
891
                  parser -> parser.withSquareBracketQuotation(useSquareBracketQuotation)
1✔
892
                      .withBackslashEscapeCharacter(useBackSlashQuoting).withTimeOut(60000)
1✔
893
                      .withUnsupportedStatements(false));
1✔
894

895
          if (statement instanceof Select) {
1✔
896
            Select select = (Select) statement;
1✔
897
            appendSelect(select, statementBuilder, indent, true, false);
1✔
898

899
          } else if (statement instanceof Update) {
1✔
900
            Update update = (Update) statement;
1✔
901
            appendUpdate(statementBuilder, update, indent);
1✔
902

903
          } else if (statement instanceof Insert) {
1✔
904
            Insert insert = (Insert) statement;
1✔
905
            appendInsert(statementBuilder, insert, indent);
1✔
906

907
          } else if (statement instanceof Merge) {
1✔
908
            Merge merge = (Merge) statement;
1✔
909
            appendMerge(statementBuilder, merge, indent);
1✔
910

911
          } else if (statement instanceof Delete) {
1✔
912
            Delete delete = (Delete) statement;
1✔
913
            appendDelete(statementBuilder, delete, indent);
1✔
914

915
          } else if (statement instanceof Truncate) {
1✔
916
            Truncate truncate = (Truncate) statement;
1✔
917
            appendTruncate(statementBuilder, truncate);
1✔
918

919
          } else if (statement instanceof CreateTable) {
1✔
920
            CreateTable createTable = (CreateTable) statement;
1✔
921
            appendCreateTable(statementBuilder, createTable, indent);
1✔
922

923
          } else if (statement instanceof CreateIndex) {
1✔
924
            CreateIndex createIndex = (CreateIndex) statement;
1✔
925
            appendCreateIndex(statementBuilder, createIndex, indent);
1✔
926

927
          } else if (statement instanceof CreateView) {
1✔
928
            CreateView createView = (CreateView) statement;
1✔
929
            appendCreateView(statementBuilder, createView, indent);
1✔
930

931
          } else if (statement instanceof Alter) {
1✔
932
            Alter alter = (Alter) statement;
1✔
933
            appendAlter(statementBuilder, alter, indent);
1✔
934

935
          } else if (statement != null) {
1✔
936
            try {
937
              statementBuilder.append("\n").append(statement);
1✔
938
            } catch (Exception ex) {
×
939
              throw new UnsupportedOperationException(
×
940
                  "The " + statement.getClass().getName() + " Statement is not supported yet.");
×
941
            }
1✔
942
          }
943
          appendNormalizedLineBreak(statementBuilder).append(";\n");
1✔
944

945
          builder.append(commentMap.isEmpty() ? statementBuilder
1✔
946
              : commentMap.insertComments(statementBuilder, outputFormat));
1✔
947

948
        } catch (Exception ex1) {
×
949
          if (statementSql.trim().length() <= commentMap.getLength()) {
×
950
            LOGGER.info("Found only comments, but no SQL code.");
×
951
            builder.append(statementSql);
×
952
          } else {
953

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

956
            builder.append("-- failed to format start\n").append(statementSql)
×
957
                .append("\n-- failed to format end\n").append("\n");
×
958
          }
959
        }
1✔
960
      } else {
961
        break;
962
      }
963
    }
964

965
    if (outputFormat == OutputFormat.HTML) {
1✔
966
      builder = new StringBuilder().append("<html>\n").append("<head>\n")
1✔
967
          .append("<title>SQL Statement's Java Object Tree</title>\n").append("</head>\n")
1✔
968
          .append("<body>\n").append("<pre style=\"font-size:-2;background-color:#EFEFEF;\">\n")
1✔
969
          .append(builder).append("\n</pre>\n").append("</body>\n").append("</html>");
1✔
970
    }
971

972
    return builder.toString().trim();
1✔
973
  }
974

975
  public static StringBuilder formatToJava(String sqlStr, int indent, String... options)
976
      throws Exception {
977
    String formatted = format(sqlStr, options);
×
978
    StringReader stringReader = new StringReader(formatted);
×
979
    BufferedReader bufferedReader = new BufferedReader(stringReader);
×
980
    String line;
981
    StringBuilder builder = new StringBuilder();
×
982
    int i = 0;
×
983
    while ((line = bufferedReader.readLine()) != null) {
×
984
      if (i > 0) {
×
985
        for (int j = 0; j < indent - 2; j++) {
×
986
          builder.append(" ");
×
987
        }
988
        builder.append("+ ");
×
989
      } else {
990
        for (int j = 0; j < indent; j++) {
×
991
          builder.append(" ");
×
992
        }
993
      }
994
      builder.append("\"").append(line).append("\"\n");
×
995
      i++;
×
996
    }
997
    return builder;
×
998
  }
999

1000
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1001
  public static void applyFormattingOptions(String[] options) {
1002
    // set the formatting options
1003
    if (options != null) {
1✔
1004
      for (String s : options) {
1✔
1005
        String[] o = s.split("=");
1✔
1006
        if (o.length == 2) {
1✔
1007
          LOGGER.log(Level.FINE, "Found Formatting Option {0} = {1}", o);
1✔
1008

1009
          String key = o[0].trim();
1✔
1010
          String value = o[1].trim();
1✔
1011

1012
          if (key.equalsIgnoreCase(FormattingOption.OUTPUT_FORMAT.toString())) {
1✔
1013
            try {
1014
              outputFormat = OutputFormat.valueOf(value.toUpperCase());
1✔
1015
            } catch (Exception ex) {
×
1016
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1017
            }
1✔
1018

1019
          } else if (key.equalsIgnoreCase(FormattingOption.KEYWORD_SPELLING.toString())) {
1✔
1020
            try {
1021
              keywordSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1022
            } catch (Exception ex) {
×
1023
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1024
            }
1✔
1025

1026
          } else if (key.equalsIgnoreCase(FormattingOption.FUNCTION_SPELLING.toString())) {
1✔
1027
            try {
1028
              functionSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1029
            } catch (Exception ex) {
×
1030
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1031
            }
1✔
1032

1033
          } else if (key.equalsIgnoreCase(FormattingOption.OBJECT_SPELLING.toString())) {
1✔
1034
            try {
1035
              objectSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1036
            } catch (Exception ex) {
×
1037
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1038
            }
1✔
1039

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

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

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

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

1068
          } else if (key.equalsIgnoreCase(FormattingOption.INDENT_WIDTH.toString())) {
1✔
1069
            try {
1070
              indentWidth = Integer.parseInt(value);
1✔
1071

1072
              char[] chars = new char[indentWidth];
1✔
1073
              Arrays.fill(chars, ' ');
1✔
1074

1075
              indentString = new String(chars);
1✔
1076
            } catch (Exception ex) {
×
1077
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1078
            }
1✔
1079
          } else {
1080
            LOGGER.log(Level.WARNING, "Unknown Formatting Option {0} = {1} ", o);
×
1081
          }
1082

1083
        } else {
1✔
1084
          LOGGER.log(Level.WARNING, "Invalid Formatting Option {0}", s);
×
1085
        }
1086
      }
1087
    }
1088
  }
1✔
1089

1090
  private static void appendMerge(StringBuilder builder, Merge merge, int indent) {
1091

1092
    List<WithItem> withItems = merge.getWithItemsList();
1✔
1093
    if (withItems != null && !withItems.isEmpty()) {
1✔
1094
      int i = 0;
1✔
1095
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1096

1097
      for (WithItem withItem : withItems) {
1✔
1098
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1099
        i++;
1✔
1100
      }
1✔
1101

1102
      appendNormalizedLineBreak(builder);
1✔
1103
    }
1104

1105
    appendKeyWord(builder, outputFormat, "MERGE", "", " ");
1✔
1106
    OracleHint oracleHint = merge.getOracleHint();
1✔
1107
    if (oracleHint != null) {
1✔
1108
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
1109
    }
1110

1111
    appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1112

1113
    Table table = merge.getTable();
1✔
1114
    Alias alias = table.getAlias();
1✔
1115

1116
    appendTable(table, alias, builder);
1✔
1117

1118
    appendNormalizedLineBreak(builder);
1✔
1119
    for (int j = 0; j < indent + 1; j++) {
1✔
1120
      builder.append(indentString);
1✔
1121
    }
1122
    appendKeyWord(builder, outputFormat, "USING", "", " ");
1✔
1123

1124
    FromItem fromItem = merge.getFromItem();
1✔
1125
    appendFromItem(fromItem, builder, indent, 0, 1);
1✔
1126

1127
    Expression onExpression = merge.getOnCondition();
1✔
1128
    if (onExpression != null) {
1✔
1129
      appendNormalizedLineBreak(builder);
1✔
1130
      for (int j = 0; j < indent + 2; j++) {
1✔
1131
        builder.append(indentString);
1✔
1132
      }
1133
      appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
1134
      appendExpression(onExpression, null, builder, indent + 2, 0, 1, false, BreakLine.AS_NEEDED);
1✔
1135

1136
      appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1137
    }
1138

1139
    MergeInsert insert = merge.getMergeInsert();
1✔
1140
    MergeUpdate update = merge.getMergeUpdate();
1✔
1141
    if (merge.isInsertFirst()) {
1✔
1142
      appendMergeInsert(insert, builder, indent, 0);
1✔
1143
      appendMergeUpdate(update, builder, indent);
1✔
1144
    } else {
1145
      appendMergeUpdate(update, builder, indent);
1✔
1146
      appendMergeInsert(insert, builder, indent, 0);
1✔
1147
    }
1148

1149
    appendOutputClaus(merge.getOutputClause(), builder, indent);
1✔
1150
  }
1✔
1151

1152
  private static void appendOutputClaus(OutputClause outputClause, StringBuilder builder,
1153
      int indent) {
1154
    if (outputClause != null) {
1✔
1155
      appendNormalizedLineBreak(builder);
1✔
1156
      for (int j = 0; j < indent; j++) {
1✔
1157
        builder.append(indentString);
×
1158
      }
1159
      appendKeyWord(builder, outputFormat, "OUTPUT", "", " ");
1✔
1160
      int i = 0;
1✔
1161
      int subIndent = getSubIndent(builder, outputClause.getSelectItemList().size() > 3);
1✔
1162
      appendSelectItemList(outputClause.getSelectItemList(), builder, subIndent, i,
1✔
1163
          BreakLine.AS_NEEDED, indent);
1164

1165
      appendNormalizedLineBreak(builder);
1✔
1166
      for (int j = 0; j < indent + 1; j++) {
1✔
1167
        builder.append(indentString);
1✔
1168
      }
1169
      appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1170

1171
      if (outputClause.getOutputTable() != null) {
1✔
1172
        Table table = outputClause.getOutputTable();
1✔
1173
        appendTable(table, table.getAlias(), builder);
1✔
1174

1175
        appendStringList(outputClause.getColumnList(), builder, indent + 1, true,
1✔
1176
            BreakLine.AS_NEEDED);
1177
      } else if (outputClause.getTableVariable() != null) {
1✔
1178
        appendObjectName(builder, outputFormat, outputClause.getTableVariable().toString(), " ",
×
1179
            "");
1180
      }
1181
    }
1182
  }
1✔
1183

1184
  public static void appendMergeUpdate(MergeUpdate update, StringBuilder builder, int indent) {
1185
    if (update != null) {
1✔
1186
      appendNormalizedLineBreak(builder);
1✔
1187

1188
      for (int j = 0; j < indent; j++) {
1✔
1189
        builder.append(indentString);
×
1190
      }
1191
      appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
1192
      appendKeyWord(builder, outputFormat, "MATCHED", "", " ");
1✔
1193
      appendKeyWord(builder, outputFormat, "THEN", "", "\n");
1✔
1194

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

1201
      int subIndent = getSubIndent(builder, true);
1✔
1202

1203
      appendUpdateSets(builder, update.getUpdateSets(), subIndent);
1✔
1204

1205
      Expression whereCondition = update.getWhereCondition();
1✔
1206
      if (whereCondition != null) {
1✔
1207
        appendNormalizedLineBreak(builder);
1✔
1208
        for (int j = 0; j < indent + 1; j++) {
1✔
1209
          builder.append(indentString);
1✔
1210
        }
1211
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1212

1213
        subIndent = getSubIndent(builder, true);
1✔
1214

1215
        appendExpression(whereCondition, null, builder, subIndent, 0, 1, false,
1✔
1216
            BreakLine.AFTER_FIRST);
1217
      }
1218

1219
      Expression deleteWhereCondition = update.getDeleteWhereCondition();
1✔
1220
      if (deleteWhereCondition != null) {
1✔
1221
        appendNormalizedLineBreak(builder);
1✔
1222
        for (int j = 0; j < indent + 1; j++) {
1✔
1223
          builder.append(indentString);
1✔
1224
        }
1225
        appendKeyWord(builder, outputFormat, "DELETE", "", " ");
1✔
1226
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1227

1228
        subIndent = getSubIndent(builder, true);
1✔
1229

1230
        appendExpression(deleteWhereCondition, null, builder, subIndent, 0, 1, false,
1✔
1231
            BreakLine.AFTER_FIRST);
1232
      }
1233
    }
1234
  }
1✔
1235

1236
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1237
  public static void appendMergeInsert(MergeInsert insert, StringBuilder builder, int indent,
1238
      int i) {
1239
    if (insert != null) {
1✔
1240
      appendNormalizedLineBreak(builder);
1✔
1241
      for (int j = 0; j < indent; j++) {
1✔
1242
        builder.append(indentString);
×
1243
      }
1244
      appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
1245
      appendKeyWord(builder, outputFormat, "NOT", "", " ");
1✔
1246
      appendKeyWord(builder, outputFormat, "MATCHED", "", " ");
1✔
1247
      appendKeyWord(builder, outputFormat, "THEN", "", "\n");
1✔
1248

1249
      for (int j = 0; j < indent + 1; j++) {
1✔
1250
        builder.append(indentString);
1✔
1251
      }
1252
      appendKeyWord(builder, outputFormat, "INSERT", "", " ");
1✔
1253

1254
      List<Column> columns = insert.getColumns();
1✔
1255
      List<Expression> expressions = insert.getValues();
1✔
1256

1257
      int k = i;
1✔
1258
      if (columns != null && !columns.isEmpty()) {
1✔
1259
        builder.append("( ");
1✔
1260
        int subIndent = getSubIndent(builder, false);
1✔
1261

1262
        for (Column column : columns) {
1✔
1263
          appendExpression(column, null, builder, subIndent, k++, columns.size(), true,
1✔
1264
              BreakLine.AFTER_FIRST);
1265
        }
1✔
1266
        appendNormalizingTrailingWhiteSpace(builder, " )\n");
1✔
1267
      }
1268

1269
      if (columns != null && !columns.isEmpty()) {
1✔
1270
        for (int j = 0; j < indent + 1; j++) {
1✔
1271
          builder.append(indentString);
1✔
1272
        }
1273
      }
1274
      appendKeyWord(builder, outputFormat, "VALUES", "", " ( ");
1✔
1275

1276
      int subIndent = getSubIndent(builder, false);
1✔
1277
      if (expressions != null) {
1✔
1278
        int j = 0;
1✔
1279
        for (Expression expression : expressions) {
1✔
1280
          appendExpression(expression, null, builder, subIndent, j++, expressions.size(), true,
1✔
1281
              BreakLine.AFTER_FIRST);
1282
        }
1✔
1283
      }
1284
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
1285

1286
      Expression whereCondition = insert.getWhereCondition();
1✔
1287
      if (whereCondition != null) {
1✔
1288
        appendNormalizedLineBreak(builder);
1✔
1289
        for (int j = 0; j < indent + 1; j++) {
1✔
1290
          builder.append(indentString);
1✔
1291
        }
1292
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1293

1294
        subIndent = getSubIndent(builder, true);
1✔
1295

1296
        appendExpression(whereCondition, null, builder, subIndent, 0, 1, false,
1✔
1297
            BreakLine.AFTER_FIRST);
1298
      }
1299
    }
1300
  }
1✔
1301

1302
  private static void appendInsert(StringBuilder builder, Insert insert, int indent) {
1303
    List<WithItem> withItems = insert.getWithItemsList();
1✔
1304
    if (withItems != null && !withItems.isEmpty()) {
1✔
1305
      int i = 0;
1✔
1306
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1307

1308
      for (WithItem withItem : withItems) {
1✔
1309
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1310
        i++;
1✔
1311
      }
1✔
1312

1313
      appendNormalizedLineBreak(builder);
1✔
1314
    }
1315

1316
    appendKeyWord(builder, outputFormat, "INSERT", "", " ");
1✔
1317
    OracleHint oracleHint = insert.getOracleHint();
1✔
1318
    if (oracleHint != null) {
1✔
1319
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
×
1320
    }
1321
    appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1322

1323
    Table table = insert.getTable();
1✔
1324
    Alias alias = table.getAlias();
1✔
1325

1326
    appendTable(table, alias, builder);
1✔
1327

1328
    List<Column> columns = insert.getColumns();
1✔
1329
    if (columns != null) {
1✔
1330
      int i = 0;
1✔
1331
      builder.append(" (");
1✔
1332
      for (Column column : columns) {
1✔
1333
        appendExpression(column, null, builder, indent + 1, i, columns.size(), true,
1✔
1334
            BreakLine.ALWAYS);
1335
        i++;
1✔
1336
      }
1✔
1337
      appendNormalizingTrailingWhiteSpace(builder, " ) ");
1✔
1338
    }
1339
    appendNormalizedLineBreak(builder);
1✔
1340
    Select select = insert.getSelect();
1✔
1341
    appendSelect(select, builder, indent, false, false);
1✔
1342
  }
1✔
1343

1344
  private static void appendUpdate(StringBuilder builder, Update update, int indent) {
1345
    List<WithItem> withItems = update.getWithItemsList();
1✔
1346
    if (withItems != null && !withItems.isEmpty()) {
1✔
1347
      int i = 0;
1✔
1348
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1349

1350
      for (WithItem withItem : withItems) {
1✔
1351
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1352
        i++;
1✔
1353
      }
1✔
1354

1355
      appendNormalizedLineBreak(builder);
1✔
1356
    }
1357

1358
    appendKeyWord(builder, outputFormat, "UPDATE", "", " ");
1✔
1359

1360
    OracleHint oracleHint = update.getOracleHint();
1✔
1361
    if (oracleHint != null) {
1✔
1362
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
×
1363
    }
1364

1365
    Table table = update.getTable();
1✔
1366
    Alias alias = table.getAlias();
1✔
1367

1368
    appendTable(table, alias, builder);
1✔
1369

1370
    if (update.getStartJoins() != null) {
1✔
1371
      appendJoins(update.getStartJoins(), builder, indent);
1✔
1372
    }
1373

1374
    appendNormalizedLineBreak(builder);
1✔
1375
    for (int j = 0; j < indent; j++) {
1✔
1376
      builder.append(indentString);
×
1377
    }
1378
    appendKeyWord(builder, outputFormat, "SET", "", " ");
1✔
1379

1380
    final int subIndent = getSubIndent(builder, true);
1✔
1381
    appendUpdateSets(builder, update.getUpdateSets(), subIndent);
1✔
1382

1383
    if (update.getFromItem() != null) {
1✔
1384
      appendNormalizedLineBreak(builder);
1✔
1385
      for (int j = 0; j < indent; j++) {
1✔
1386
        builder.append(indentString);
×
1387
      }
1388
      appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
1389
      appendFromItem(update.getFromItem(), builder, indent, 0, 1);
1✔
1390
    }
1391

1392
    List<Join> joins = update.getJoins();
1✔
1393
    appendJoins(joins, builder, indent);
1✔
1394

1395
    Expression whereExpression = update.getWhere();
1✔
1396
    appendWhere(whereExpression, builder, indent);
1✔
1397

1398
    List<OrderByElement> orderByElements = update.getOrderByElements();
1✔
1399
    appendOrderByElements(orderByElements, builder, indent);
1✔
1400
  }
1✔
1401

1402
  private static void appendUpdateSets(StringBuilder builder, List<UpdateSet> updateSets,
1403
      int subIndent) {
1404
    int i = 0;
1✔
1405
    int n = updateSets.size();
1✔
1406
    for (UpdateSet updateSet : updateSets) {
1✔
1407
      if (i > 0) {
1✔
1408
        appendNormalizedLineBreak(builder);
1✔
1409
        for (int j = 0; j < subIndent; j++) {
1✔
1410
          builder.append(indentString);
1✔
1411
        }
1412
      }
1413

1414
      switch (separation) {
1✔
1415
        case BEFORE:
1416
          builder.append(i > 0 ? ", " : "");
1✔
1417
      }
1418

1419
      appendExpressionList(updateSet.getColumns(), builder, subIndent, BreakLine.AFTER_FIRST);
1✔
1420
      appendNormalizingTrailingWhiteSpace(builder, " = ");
1✔
1421
      appendExpressionList(updateSet.getValues(), builder, subIndent, BreakLine.AFTER_FIRST);
1✔
1422

1423
      switch (separation) {
1✔
1424
        case AFTER:
1425
          appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? ", " : "");
1✔
1426
          break;
1427
      }
1428

1429
      i++;
1✔
1430
    }
1✔
1431
  }
1✔
1432

1433
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
1434
  private static void appendSelect(Select select, StringBuilder builder, int indent,
1435
      boolean breakLineBefore, boolean indentFirstLine) {
1436

1437
    List<WithItem> withItems = select.getWithItemsList();
1✔
1438
    if (withItems != null && !withItems.isEmpty()) {
1✔
1439
      int i = 0;
1✔
1440
      if (breakLineBefore) {
1✔
1441
        appendNormalizedLineBreak(builder);
1✔
1442
        for (int j = 0; indentFirstLine && j < indent; j++) {
1✔
1443
          builder.append(indentString);
1✔
1444
        }
1445
      }
1446
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1447

1448
      for (WithItem withItem : withItems) {
1✔
1449
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1450
        i++;
1✔
1451
      }
1✔
1452
    }
1453

1454
    // All Known Implementing Classes: PlainSelect, SetOperationList,
1455
    // ValuesStatement, WithItem
1456
    if (select instanceof PlainSelect) {
1✔
1457
      PlainSelect plainSelect = (PlainSelect) select;
1✔
1458

1459
      int i = 0;
1✔
1460
      if (breakLineBefore || withItems != null && !withItems.isEmpty()) {
1✔
1461
        appendNormalizedLineBreak(builder);
1✔
1462
        for (int j = 0; indentFirstLine && j < indent; j++) {
1✔
1463
          builder.append(indentString);
1✔
1464
        }
1465
      }
1466

1467
      appendKeyWord(builder, outputFormat, "SELECT", "", " ");
1✔
1468

1469
      OracleHint oracleHint = plainSelect.getOracleHint();
1✔
1470
      if (oracleHint != null) {
1✔
1471
        appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
1472
      }
1473

1474
      Top top = plainSelect.getTop();
1✔
1475
      if (top != null) {
1✔
1476
        appendKeyWord(builder, outputFormat, "TOP", "", top.hasParenthesis() ? "( " : " ");
1✔
1477
        appendExpression(top.getExpression(), null, builder, 0, 0, 0, false, BreakLine.AS_NEEDED);
1✔
1478
        if (top.hasParenthesis()) {
1✔
1479
          builder.append(" )");
×
1480
        }
1481
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1482

1483
        if (top.isPercentage()) {
1✔
1484
          appendKeyWord(builder, outputFormat, "PERCENT", "", " ");
×
1485
        }
1486

1487
        if (top.isWithTies()) {
1✔
1488
          appendKeyWord(builder, outputFormat, "WITH TIES", "", " ");
×
1489
        }
1490
      }
1491

1492
      Distinct distinct = plainSelect.getDistinct();
1✔
1493
      if (distinct != null) {
1✔
1494
        if (distinct.isUseUnique()) {
1✔
1495
          // @todo: implement Use Unique Distinct clause
1496
          throw new UnsupportedOperationException("Unique DISTINCT not supported yet.");
×
1497
        }
1498

1499
        if (distinct.getOnSelectItems() != null && !distinct.getOnSelectItems().isEmpty()) {
1✔
1500
          // @todo: implement Use Unique Distinct clause
1501
          throw new UnsupportedOperationException(
×
1502
              "DISTINCT on select items are not supported yet.");
1503
        }
1504
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
1✔
1505
      }
1506

1507
      int subIndent = oracleHint != null || distinct != null || top != null ? indent + 1
1✔
1508
          : getSubIndent(builder, plainSelect.getSelectItems().size() > 1);
1✔
1509
      BreakLine bl = oracleHint != null || distinct != null || top != null ? BreakLine.ALWAYS
1✔
1510
          : BreakLine.AFTER_FIRST;
1✔
1511

1512
      List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
1✔
1513
      appendSelectItemList(selectItems, builder, subIndent, i, bl, indent);
1✔
1514

1515
      FromItem fromItem = plainSelect.getFromItem();
1✔
1516
      if (fromItem != null) {
1✔
1517
        appendNormalizedLineBreak(builder);
1✔
1518
        for (int j = 0; j < indent; j++) {
1✔
1519
          builder.append(indentString);
1✔
1520
        }
1521
        appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
1522
        appendFromItem(fromItem, builder, indent, i, 1);
1✔
1523

1524
        List<Join> joins = plainSelect.getJoins();
1✔
1525
        appendJoins(joins, builder, indent);
1✔
1526
      }
1527

1528
      Expression whereExpression = plainSelect.getWhere();
1✔
1529
      appendWhere(whereExpression, builder, indent);
1✔
1530

1531
      GroupByElement groupByElement = plainSelect.getGroupBy();
1✔
1532
      appendGroupByElement(groupByElement, builder, indent);
1✔
1533

1534
      Expression havingExpression = plainSelect.getHaving();
1✔
1535
      appendHavingExpression(havingExpression, builder, indent);
1✔
1536

1537
      // @todo: write-out Qualify
1538
      if (plainSelect.getQualify() != null) {
1✔
1539
        builder.append(" QUALIFY ");
×
1540
        builder.append(plainSelect.getQualify());
×
1541
      }
1542

1543
      // @todo: write-out Windows
1544
      if (plainSelect.getWindowDefinitions() != null) {
1✔
1545
        appendNormalizedLineBreak(builder);
1✔
1546
        for (int j = 0; j < indent; j++) {
1✔
1547
          builder.append(indentString);
1✔
1548
        }
1549
        builder.append("WINDOW ");
1✔
1550
        int k = 0;
1✔
1551
        for (WindowDefinition w : plainSelect.getWindowDefinitions()) {
1✔
1552
          appendNormalizedLineBreak(builder);
1✔
1553
          for (int j = 0; j < indent + 1; j++) {
1✔
1554
            builder.append(indentString);
1✔
1555
          }
1556
          if (k++ > 0) {
1✔
1557
            builder.append(", ");
×
1558
          }
1559
          if (w.getWindowName() != null) {
1✔
1560
            appendObjectName(builder, outputFormat, w.getWindowName(), "", " ");
1✔
1561
            appendKeyWord(builder, outputFormat, "AS", "", " (");
1✔
1562
          } else {
1563
            builder.append("(");
×
1564
          }
1565

1566
          appendNormalizedLineBreak(builder);
1✔
1567
          for (int j = 0; j < indent + 2; j++) {
1✔
1568
            builder.append(indentString);
1✔
1569
          }
1570
          w.getPartitionBy().toStringPartitionBy(builder);
1✔
1571

1572
          appendNormalizedLineBreak(builder);
1✔
1573
          for (int j = 0; j < indent + 2; j++) {
1✔
1574
            builder.append(indentString);
1✔
1575
          }
1576
          w.getOrderBy().toStringOrderByElements(builder);
1✔
1577

1578
          if (w.getWindowElement() != null) {
1✔
1579
            if (w.getOrderBy().getOrderByElements() != null) {
1✔
1580
              appendNormalizedLineBreak(builder);
1✔
1581
              for (int j = 0; j < indent + 2; j++) {
1✔
1582
                builder.append(indentString);
1✔
1583
              }
1584
            }
1585
            appendKeyWord(builder, outputFormat, w.getWindowElement().toString(), "", "");
1✔
1586
          }
1587

1588
          builder.append(" )");
1✔
1589
        }
1✔
1590
      }
1591

1592
      // @todo: write-out FOR MODE
1593
      if (plainSelect.getForMode() != null) {
1✔
1594
        builder.append(" FOR ");
×
1595
        builder.append(plainSelect.getForMode().getValue());
×
1596

1597
        if (plainSelect.getForUpdateTable() != null) {
×
1598
          builder.append(" OF ").append(plainSelect.getForUpdateTable());
×
1599
        }
1600
        if (plainSelect.getWait() != null) {
×
1601
          // wait's toString will do the formatting for us
1602
          builder.append(plainSelect.getWait());
×
1603
        }
1604
        if (plainSelect.isNoWait()) {
×
1605
          builder.append(" NOWAIT");
×
1606
        } else if (plainSelect.isSkipLocked()) {
×
1607
          builder.append(" SKIP LOCKED");
×
1608
        }
1609
      }
1610

1611
      // @todo: write-out FOR CLAUSE
1612
      if (plainSelect.getForClause() != null) {
1✔
1613
        plainSelect.getForClause().appendTo(builder);
×
1614
      }
1615

1616
      // @todo: write-out EMIT CHANGES
1617
      if (plainSelect.isEmitChanges()) {
1✔
1618
        builder.append(" EMIT CHANGES");
×
1619
      }
1620

1621
      // @todo: write-out LIMIT BY
1622
      if (plainSelect.getLimitBy() != null) {
1✔
1623
        builder.append(plainSelect.getLimitBy());
×
1624
      }
1625

1626
      List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
1✔
1627
      appendOrderByElements(orderByElements, builder, indent);
1✔
1628

1629
      Limit limit = plainSelect.getLimit();
1✔
1630
      if (limit != null) {
1✔
1631
        appendNormalizedLineBreak(builder);
1✔
1632
        for (int j = 0; j < indent; j++) {
1✔
1633
          builder.append(indentString);
1✔
1634
        }
1635
        appendKeyWord(builder, outputFormat, "LIMIT", "", " ");
1✔
1636

1637
        Expression rowCount = limit.getRowCount();
1✔
1638
        if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
1✔
1639
          // no offset allowed
1640
          appendKeyWord(builder, outputFormat, "NULL", "", " ");
×
1641
        } else {
1642
          if (null != limit.getOffset()) {
1✔
1643
            appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false,
1✔
1644
                BreakLine.NEVER);
1645
            builder.append(", ");
1✔
1646
          }
1647
          if (null != limit.getRowCount()) {
1✔
1648
            appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
1✔
1649
                BreakLine.NEVER);
1650
          }
1651
        }
1652
      }
1653

1654
      Offset offset = plainSelect.getOffset();
1✔
1655
      if (offset != null) {
1✔
1656
        appendNormalizedLineBreak(builder);
1✔
1657
        for (int j = 0; j < indent; j++) {
1✔
1658
          builder.append(indentString);
×
1659
        }
1660
        appendKeyWord(builder, outputFormat, "OFFSET", "", " ");
1✔
1661

1662
        Expression offsetExpression = offset.getOffset();
1✔
1663
        appendExpression(offsetExpression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1664

1665
        String offsetParam = offset.getOffsetParam();
1✔
1666
        if (offsetParam != null) {
1✔
1667
          appendString(offsetParam, builder, indent, 0, 1, false, BreakLine.NEVER);
×
1668
        }
1669
      }
1670

1671
      Fetch fetch = plainSelect.getFetch();
1✔
1672
      if (fetch != null) {
1✔
1673
        appendNormalizedLineBreak(builder);
1✔
1674
        for (int j = 0; j < indent; j++) {
1✔
1675
          builder.append(indentString);
×
1676
        }
1677
        appendKeyWord(builder, outputFormat, "FETCH", "", "");
1✔
1678

1679
        if (fetch.isFetchParamFirst()) {
1✔
1680
          appendKeyWord(builder, outputFormat, "FIRST", " ", "");
1✔
1681
        } else {
1682
          appendKeyWord(builder, outputFormat, "NEXT", " ", "");
1✔
1683
        }
1684

1685
        Expression expression = fetch.getExpression();
1✔
1686
        if (expression != null) {
1✔
1687
          appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1688
          appendExpression(expression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1689
        }
1690

1691
        for (String s : fetch.getFetchParameters()) {
1✔
1692
          appendKeyWord(builder, outputFormat, s, " ", "");
1✔
1693
        }
1✔
1694

1695
        // @todo: write-out ISOLATION
1696
        if (plainSelect.getIsolation() != null) {
1✔
1697
          builder.append(plainSelect.getIsolation());
×
1698
        }
1699

1700
        // @todo: write-out Optimizer For
1701
        if (plainSelect.getOptimizeFor() != null) {
1✔
1702
          builder.append(plainSelect.getOptimizeFor());
×
1703
        }
1704

1705
        // @todo: write-out FOR XML PATH
1706
        if (plainSelect.getForXmlPath() != null) {
1✔
1707
          builder.append(" FOR XML PATH(").append(plainSelect.getForXmlPath()).append(")");
×
1708
        }
1709

1710
        // @todo: write-out INTO TEMP Table
1711
        if (plainSelect.getIntoTempTable() != null) {
1✔
1712
          builder.append(" INTO TEMP ").append(plainSelect.getIntoTempTable());
×
1713
        }
1714

1715
        // @todo: write-out WITH NO LOG
1716
        if (plainSelect.isUseWithNoLog()) {
1✔
1717
          builder.append(" WITH NO LOG");
×
1718
        }
1719
      }
1720

1721
    } else if (select instanceof SetOperationList) {
1✔
1722
      SetOperationList setOperationList = (SetOperationList) select;
1✔
1723

1724
      List<SetOperation> setOperations = setOperationList.getOperations();
1✔
1725

1726
      int k = 0;
1✔
1727

1728
      List<Select> selects = setOperationList.getSelects();
1✔
1729
      if (selects != null && !selects.isEmpty()) {
1✔
1730
        for (Select selectBody1 : selects) {
1✔
1731
          if (k > 0 && setOperations != null && setOperations.size() >= k) {
1✔
1732
            SetOperation setOperation = setOperations.get(k - 1);
1✔
1733
            appendSetOperation(setOperation, builder, indent);
1✔
1734
          }
1735
          int subIndent = indent;
1✔
1736
          appendSelect(selectBody1, builder, subIndent, k > 0 || breakLineBefore,
1✔
1737
              k > 0 || breakLineBefore || indentFirstLine);
1738
          k++;
1✔
1739
        }
1✔
1740
      }
1741

1742
      List<OrderByElement> orderByElements = setOperationList.getOrderByElements();
1✔
1743
      appendOrderByElements(orderByElements, builder, indent);
1✔
1744

1745
      Limit limit = setOperationList.getLimit();
1✔
1746
      if (limit != null) {
1✔
1747
        appendNormalizedLineBreak(builder);
1✔
1748
        for (int j = 0; j < indent; j++) {
1✔
1749
          builder.append(indentString);
×
1750
        }
1751
        appendKeyWord(builder, outputFormat, "LIMIT", "", " ");
1✔
1752

1753
        Expression rowCount = limit.getRowCount();
1✔
1754
        if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
1✔
1755
          // no offset allowed
1756
          appendKeyWord(builder, outputFormat, "NULL", "", " ");
×
1757
        } else {
1758
          if (null != limit.getOffset()) {
1✔
1759
            appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false,
×
1760
                BreakLine.NEVER);
1761
            builder.append(", ");
×
1762
          }
1763
          if (null != limit.getRowCount()) {
1✔
1764
            appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
1✔
1765
                BreakLine.NEVER);
1766
          }
1767
        }
1768
      }
1769

1770
      Offset offset = setOperationList.getOffset();
1✔
1771
      if (offset != null) {
1✔
1772
        appendNormalizedLineBreak(builder);
1✔
1773
        for (int j = 0; j < indent; j++) {
1✔
1774
          builder.append(indentString);
×
1775
        }
1776
        appendKeyWord(builder, outputFormat, "OFFSET", "", " ");
1✔
1777

1778
        Expression offsetExpression = offset.getOffset();
1✔
1779
        appendExpression(offsetExpression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1780

1781
        String offsetParam = offset.getOffsetParam();
1✔
1782
        if (offsetParam != null) {
1✔
1783
          appendString(offsetParam, builder, indent, 0, 1, false, BreakLine.NEVER);
×
1784
        }
1785
      }
1786

1787
    } else if (select instanceof Values) {
1✔
1788
      Values values = (Values) select;
1✔
1789

1790
      if (breakLineBefore) {
1✔
1791
        appendNormalizedLineBreak(builder);
×
1792
        for (int j = 0; indentFirstLine && j < indent; j++) {
×
1793
          builder.append(indentString);
×
1794
        }
1795
      }
1796

1797
      appendKeyWord(builder, outputFormat, "VALUES", "", " ");
1✔
1798
      appendExpressionList(values.getExpressions(), builder, indent, BreakLine.AS_NEEDED);
1✔
1799

1800
    } else if (select instanceof ParenthesedSelect) {
1✔
1801
      ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
1✔
1802
      builder.append("( ");
1✔
1803
      int subIndent = breakLineBefore ? indent + 1
1✔
1804
          : getSubIndent(builder, !(parenthesedSelect.getSelect() instanceof Values));
1✔
1805

1806
      appendSelect(parenthesedSelect.getSelect(), builder, subIndent, breakLineBefore,
1✔
1807
          indentFirstLine);
1808
      builder.append(" )");
1✔
1809
    }
1810
  }
1✔
1811

1812
  public static void appendSelectItemList(List<SelectItem<?>> selectItems, StringBuilder builder,
1813
      int subIndent, int i, BreakLine bl, int indent) throws UnsupportedOperationException {
1814
    int j = i;
1✔
1815
    for (SelectItem<?> selectItem : selectItems) {
1✔
1816
      Alias alias = selectItem.getAlias();
1✔
1817
      Expression expression = selectItem.getExpression();
1✔
1818

1819
      appendExpression(expression, alias, builder, subIndent, j++, selectItems.size(), true, bl);
1✔
1820
    }
1✔
1821
  }
1✔
1822

1823
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1824
  private static void appendOrderByElements(List<OrderByElement> orderByElements,
1825
      StringBuilder builder, int indent) {
1826
    if (orderByElements != null) {
1✔
1827
      int i = 0;
1✔
1828
      appendNormalizedLineBreak(builder);
1✔
1829
      for (int j = 0; j < indent; j++) {
1✔
1830
        builder.append(indentString);
1✔
1831
      }
1832
      appendKeyWord(builder, outputFormat, "ORDER BY", "", " ");
1✔
1833

1834
      int subIndent = getSubIndent(builder, orderByElements.size() > 1);
1✔
1835

1836
      for (OrderByElement orderByElement : orderByElements) {
1✔
1837
        Expression expression = orderByElement.getExpression();
1✔
1838
        appendExpression(expression, null, builder, subIndent, i, orderByElements.size(), true,
1✔
1839
            BreakLine.AFTER_FIRST);
1840

1841
        if (orderByElement.isAscDescPresent()) {
1✔
1842
          builder.append(" ");
1✔
1843

1844
          if (orderByElement.isAsc()) {
1✔
1845
            appendKeyWord(builder, outputFormat, "ASC", "", " ");
1✔
1846
          } else {
1847
            appendKeyWord(builder, outputFormat, "DESC", "", " ");
1✔
1848
          }
1849
        }
1850

1851
        NullOrdering nullOrdering = orderByElement.getNullOrdering();
1✔
1852
        if (NullOrdering.NULLS_FIRST.equals(nullOrdering)) {
1✔
1853
          appendKeyWord(builder, outputFormat, "NULLS FIRST",
1✔
1854
              orderByElement.isAscDescPresent() ? "" : " ", " ");
1✔
1855
        }
1856

1857
        if (NullOrdering.NULLS_LAST.equals(nullOrdering)) {
1✔
1858
          appendKeyWord(builder, outputFormat, "NULLS LAST",
×
1859
              orderByElement.isAscDescPresent() ? "" : " ", " ");
×
1860
        }
1861

1862
        i++;
1✔
1863
      }
1✔
1864
    }
1865
  }
1✔
1866

1867
  private static void appendHavingExpression(Expression havingExpression, StringBuilder builder,
1868
      int indent) {
1869
    if (havingExpression != null) {
1✔
1870
      appendNormalizedLineBreak(builder);
1✔
1871
      for (int j = 0; j < indent; j++) {
1✔
1872
        builder.append(indentString);
×
1873
      }
1874
      appendKeyWord(builder, outputFormat, "HAVING", "", " ");
1✔
1875
      appendExpression(havingExpression, null, builder, indent, 0, 1, false, BreakLine.AFTER_FIRST);
1✔
1876
    }
1877
  }
1✔
1878

1879
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1880
  private static void appendGroupByElement(GroupByElement groupByElement, StringBuilder builder,
1881
      int indent) throws UnsupportedOperationException {
1882
    int i = 0;
1✔
1883
    if (groupByElement != null) {
1✔
1884
      appendNormalizedLineBreak(builder);
1✔
1885
      for (int j = 0; j < indent; j++) {
1✔
1886
        builder.append(indentString);
1✔
1887
      }
1888
      appendKeyWord(builder, outputFormat, "GROUP BY", "", " ");
1✔
1889

1890
      List<?> groupingSets = groupByElement.getGroupingSets();
1✔
1891
      ExpressionList<?> groupByExpressions = groupByElement.getGroupByExpressionList();
1✔
1892

1893
      if (groupingSets != null && !groupingSets.isEmpty()) {
1✔
1894
        throw new UnsupportedOperationException("Grouping Sets are not supported yet.");
×
1895
      }
1896

1897
      if (groupByExpressions != null && !groupByExpressions.isEmpty()) {
1✔
1898
        BreakLine breakLine = BreakLine.AFTER_FIRST;
1✔
1899
        int size = groupByExpressions.size();
1✔
1900
        int subIndent = getSubIndent(builder, size > 1);
1✔
1901

1902
        for (Expression expression : groupByExpressions) {
1✔
1903
          switch (breakLine) {
1✔
1904
            case AS_NEEDED:
1905
              BreakLine bl =
1906
                  size == 4 || size >= 5 && i % 3 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
×
1907
              appendExpression(expression, null, builder, subIndent, i, size, true, bl);
×
1908
              break;
×
1909

1910
            default:
1911
              appendExpression(expression, null, builder, subIndent, i, size, true, breakLine);
1✔
1912
          }
1913
          i++;
1✔
1914
        }
1✔
1915
      }
1916
    }
1917
  }
1✔
1918

1919
  private static void appendWhere(Expression whereExpression, StringBuilder builder, int indent) {
1920
    if (whereExpression != null) {
1✔
1921
      appendNormalizedLineBreak(builder);
1✔
1922
      for (int j = 0; j < indent; j++) {
1✔
1923
        builder.append(indentString);
1✔
1924
      }
1925
      appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1926
      appendExpression(whereExpression, null, builder, indent, 0, 1, false, BreakLine.AFTER_FIRST);
1✔
1927
    }
1928
  }
1✔
1929

1930
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1931
  private static void appendJoins(List<Join> joins, StringBuilder builder, int indent) {
1932
    if (joins != null) {
1✔
1933
      for (Join join : joins) {
1✔
1934

1935
        if (join.isSimple()) {
1✔
1936
          switch (separation) {
1✔
1937
            case AFTER:
1938
              builder.append(",");
1✔
1939
          }
1940
        }
1941
        appendNormalizedLineBreak(builder);
1✔
1942

1943
        if (join.isSimple()) {
1✔
1944
          for (int j = 0; j <= indent; j++) {
1✔
1945
            builder.append(indentString);
1✔
1946
          }
1947

1948
          switch (separation) {
1✔
1949
            case BEFORE:
1950
              builder.append(", ");
1✔
1951
          }
1952

1953
        } else {
1954
          for (int j = 0; j <= indent; j++) {
1✔
1955
            builder.append(indentString);
1✔
1956
          }
1957

1958
          if (join.isInner()) {
1✔
1959
            appendKeyWord(builder, outputFormat, "INNER", "", " ");
1✔
1960
          }
1961
          if (join.isLeft()) {
1✔
1962
            appendKeyWord(builder, outputFormat, "LEFT", "", " ");
1✔
1963
          }
1964
          if (join.isRight()) {
1✔
1965
            appendKeyWord(builder, outputFormat, "RIGHT", "", " ");
×
1966
          }
1967
          if (join.isNatural()) {
1✔
1968
            appendKeyWord(builder, outputFormat, "NATURAL", "", " ");
×
1969
          }
1970
          if (join.isCross()) {
1✔
1971
            appendKeyWord(builder, outputFormat, "CROSS", "", " ");
×
1972
          }
1973
          if (join.isOuter()) {
1✔
1974
            appendKeyWord(builder, outputFormat, "OUTER", "", " ");
1✔
1975
          }
1976
          if (join.isFull()) {
1✔
1977
            appendKeyWord(builder, outputFormat, "FULL", "", " ");
×
1978
          }
1979

1980
          appendKeyWord(builder, outputFormat, "JOIN", "", " ");
1✔
1981
        }
1982

1983
        FromItem rightFromItem = join.getRightItem();
1✔
1984
        appendFromItem(rightFromItem, builder, indent, 0, 1);
1✔
1985

1986
        for (Expression onExpression : join.getOnExpressions()) {
1✔
1987
          if (onExpression != null) {
1✔
1988
            appendNormalizedLineBreak(builder);
1✔
1989
            for (int j = 0; j <= indent + 1; j++) {
1✔
1990
              builder.append(indentString);
1✔
1991
            }
1992
            appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
1993

1994
            appendExpression(onExpression, null, builder, indent + 2, 0, 1, false,
1✔
1995
                BreakLine.AFTER_FIRST);
1996
          }
1997
        }
1✔
1998

1999
        List<Column> usingColumns = join.getUsingColumns();
1✔
2000
        if (usingColumns != null && !usingColumns.isEmpty()) {
1✔
2001
          appendNormalizedLineBreak(builder);
×
2002
          for (int j = 0; j <= indent + 2; j++) {
×
2003
            builder.append(indentString);
×
2004
          }
2005
          appendKeyWord(builder, outputFormat, "USING", "", " ( ");
×
2006
          int k = 0;
×
2007
          for (Column column : usingColumns) {
×
2008
            appendExpression(column, null, builder, indent + 3, k, usingColumns.size(), true,
×
2009
                BreakLine.AFTER_FIRST);
2010
            k++;
×
2011
          }
×
2012
          appendNormalizingTrailingWhiteSpace(builder, " )");
×
2013
        }
2014
      }
1✔
2015
    }
2016
  }
1✔
2017

2018
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2019
  private static void appendWithItem(WithItem withItem, StringBuilder builder, int indent, int i,
2020
      int n) {
2021

2022
    if (i > 0) {
1✔
2023
      appendNormalizedLineBreak(builder);
1✔
2024
      for (int j = 0; j <= indent; j++) {
1✔
2025
        builder.append(indentString);
1✔
2026
      }
2027
    }
2028

2029
    switch (separation) {
1✔
2030
      case BEFORE:
2031
        appendAlias(builder, outputFormat, withItem.getAlias().getName(), i > 0 ? ", " : "", " ");
1✔
2032
        break;
1✔
2033
      default:
2034
        appendAlias(builder, outputFormat, withItem.getAlias().getName(), "", " ");
1✔
2035
    }
2036

2037
    List<SelectItem<?>> selectItems = withItem.getWithItemList();
1✔
2038
    if (selectItems != null && !selectItems.isEmpty()) {
1✔
2039
      builder.append("( ");
1✔
2040

2041
      int subIndent = getSubIndent(builder, selectItems.size() > 2);
1✔
2042
      BreakLine bl = selectItems.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
2043

2044
      appendSelectItemList(selectItems, builder, subIndent, i, bl, indent);
1✔
2045
      builder.append(" ) ");
1✔
2046

2047
      appendNormalizedLineBreak(builder);
1✔
2048
      for (int j = 0; j < indent + 1; j++) {
1✔
2049
        builder.append(indentString);
1✔
2050
      }
2051
      appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2052
      appendSelect(withItem.getSelect(), builder, indent + 1, false, false);
1✔
2053

2054
    } else {
1✔
2055
      appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2056
      appendSelect(withItem.getSelect(), builder, indent + 1, true, true);
1✔
2057
    }
2058

2059
    switch (separation) {
1✔
2060
      case AFTER:
2061
        appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? "," : "");
1✔
2062
        break;
1✔
2063
      case BEFORE:
2064
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2065
        break;
2066
    }
2067
  }
1✔
2068

2069
  private static void appendRowConstructor(StringBuilder builder, int indent,
2070
      RowConstructor<?> rowConstructor) {
2071

2072
    if (rowConstructor.getName() != null) {
×
2073
      appendAlias(builder, outputFormat, rowConstructor.getName(), "", "");
×
2074
    }
2075

2076
    appendExpressionList(rowConstructor, builder, indent, BreakLine.AS_NEEDED);
×
2077
  }
×
2078

2079
  private static void appendStringList(Collection<String> strings, StringBuilder builder,
2080
      int indent, boolean commaSeparated, BreakLine breakLine) {
2081
    int i = 0;
1✔
2082
    if (strings != null) {
1✔
2083
      for (String s : strings) {
1✔
2084
        appendString(s, builder, indent, i, strings.size(), commaSeparated, breakLine);
1✔
2085
        i++;
1✔
2086
      }
1✔
2087
    }
2088
  }
1✔
2089

2090
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2091
  private static void appendString(String s, StringBuilder builder, int indent, int i, int n,
2092
      boolean commaSeparated, BreakLine breakLine) {
2093

2094
    if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2095
      appendNormalizedLineBreak(builder);
1✔
2096
      for (int j = 0; j < indent; j++) {
1✔
2097
        builder.append(indentString);
1✔
2098
      }
2099
    }
2100

2101
    switch (separation) {
1✔
2102
      case AFTER:
2103
        appendObjectName(builder, outputFormat, s, "", "");
1✔
2104
        appendNormalizingTrailingWhiteSpace(builder, commaSeparated && i < n - 1 ? ", " : "");
1✔
2105
        break;
1✔
2106
      case BEFORE:
2107
        builder.append(commaSeparated && i > 0 ? ", " : "");
1✔
2108
        appendObjectName(builder, outputFormat, s, "", "");
1✔
2109
    }
2110
  }
1✔
2111

2112
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
2113
  private static void appendExpression(Expression expression, Alias alias, StringBuilder builder,
2114
      int indent, final int i, final int n, boolean commaSeparated, BreakLine breakLine) {
2115

2116
    if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2117
      appendNormalizedLineBreak(builder);
1✔
2118
      for (int j = 0; j < indent; j++) {
1✔
2119
        builder.append(indentString);
1✔
2120
      }
2121
    }
2122

2123
    switch (separation) {
1✔
2124
      case BEFORE:
2125
        builder.append(commaSeparated && i > 0 ? ", " : "");
1✔
2126
    }
2127

2128
    if (expression instanceof Column) {
1✔
2129
      Column column = (Column) expression;
1✔
2130
      appendObjectName(builder, outputFormat, column.getFullyQualifiedName(), "", "");
1✔
2131

2132
    } else if (expression instanceof AndExpression) {
1✔
2133
      AndExpression andExpression = (AndExpression) expression;
1✔
2134
      appendExpression(andExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2135
          BreakLine.AFTER_FIRST);
2136

2137
      appendNormalizedLineBreak(builder);
1✔
2138
      for (int j = 0; j <= indent; j++) {
1✔
2139
        builder.append(indentString);
1✔
2140
      }
2141
      appendOperator(builder, outputFormat, "AND", "", " ");
1✔
2142

2143
      appendExpression(andExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2144
          BreakLine.AFTER_FIRST);
2145

2146
    } else if (expression instanceof OrExpression) {
1✔
2147
      OrExpression orExpression = (OrExpression) expression;
1✔
2148
      appendExpression(orExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2149
          BreakLine.AFTER_FIRST);
2150

2151
      appendNormalizedLineBreak(builder);
1✔
2152
      for (int j = 0; j <= indent; j++) {
1✔
2153
        builder.append(indentString);
1✔
2154
      }
2155
      appendOperator(builder, outputFormat, "OR", "", " ");
1✔
2156

2157
      appendExpression(orExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2158
          BreakLine.AFTER_FIRST);
2159

2160
    } else if (expression instanceof EqualsTo) {
1✔
2161
      EqualsTo equalsTo = (EqualsTo) expression;
1✔
2162

2163
      if (equalsTo.getOraclePriorPosition() == EqualsTo.ORACLE_PRIOR_START) {
1✔
2164
        appendOperator(builder, outputFormat, "PRIOR", "", " ");
×
2165
      }
2166

2167
      appendExpression(equalsTo.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2168
          BreakLine.AS_NEEDED);
2169

2170
      if (equalsTo.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_RIGHT) {
1✔
2171
        appendOperator(builder, outputFormat, "(+)", "", " ");
1✔
2172
      }
2173

2174
      appendOperator(builder, outputFormat, "=", " ", " ");
1✔
2175

2176
      if (equalsTo.getOraclePriorPosition() == EqualsTo.ORACLE_PRIOR_END) {
1✔
2177
        appendOperator(builder, outputFormat, "PRIOR", "", " ");
×
2178
      }
2179

2180
      appendExpression(equalsTo.getRightExpression(), alias, builder, indent + 1, i, n, false,
1✔
2181
          BreakLine.AFTER_FIRST);
2182

2183
      if (equalsTo.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_LEFT) {
1✔
2184
        appendOperator(builder, outputFormat, "(+)", "", " ");
1✔
2185
      }
2186

2187
    } else if (expression instanceof CaseExpression) {
1✔
2188
      int subIndent = getSubIndent(builder, false);
1✔
2189

2190
      CaseExpression caseExpression = (CaseExpression) expression;
1✔
2191
      appendKeyWord(builder, outputFormat, "CASE", "", " ");
1✔
2192
      if (caseExpression.getSwitchExpression() != null) {
1✔
2193
        appendExpression(caseExpression.getSwitchExpression(), null, builder, indent + 1, i, n,
1✔
2194
            false, BreakLine.NEVER);
2195
      }
2196

2197
      List<WhenClause> whenClauses = caseExpression.getWhenClauses();
1✔
2198
      for (WhenClause whenClause : whenClauses) {
1✔
2199
        appendNormalizedLineBreak(builder);
1✔
2200
        for (int j = 0; j < subIndent; j++) {
1✔
2201
          builder.append(indentString);
1✔
2202
        }
2203
        appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
2204
        appendExpression(whenClause.getWhenExpression(), null, builder, subIndent + 1, 0, 1, false,
1✔
2205
            BreakLine.AFTER_FIRST);
2206

2207
        appendNormalizedLineBreak(builder);
1✔
2208
        for (int j = 0; j < subIndent + 1; j++) {
1✔
2209
          builder.append(indentString);
1✔
2210
        }
2211
        appendKeyWord(builder, outputFormat, "THEN", "", " ");
1✔
2212
        appendExpression(whenClause.getThenExpression(), null, builder, subIndent + 1, 0, 1, false,
1✔
2213
            BreakLine.AFTER_FIRST);
2214
      }
1✔
2215

2216
      Expression elseExpression = caseExpression.getElseExpression();
1✔
2217
      if (elseExpression != null) {
1✔
2218
        appendNormalizedLineBreak(builder);
1✔
2219
        for (int j = 0; j < subIndent; j++) {
1✔
2220
          builder.append(indentString);
1✔
2221
        }
2222
        appendKeyWord(builder, outputFormat, "ELSE", "", " ");
1✔
2223
        appendExpression(elseExpression, null, builder, subIndent + 1, 0, 1, false,
1✔
2224
            BreakLine.AFTER_FIRST);
2225
      }
2226

2227
      appendNormalizedLineBreak(builder);
1✔
2228
      for (int j = 0; j < subIndent; j++) {
1✔
2229
        builder.append(indentString);
1✔
2230
      }
2231
      appendKeyWord(builder, outputFormat, "END", "", "");
1✔
2232

2233
    } else if (expression instanceof StringValue) {
1✔
2234
      StringValue stringValue = (StringValue) expression;
1✔
2235
      appendValue(builder, outputFormat, stringValue.toString(), "", "");
1✔
2236

2237
    } else if (expression instanceof LongValue) {
1✔
2238
      LongValue longValue = (LongValue) expression;
1✔
2239
      appendValue(builder, outputFormat, longValue.toString(), "", "");
1✔
2240

2241
    } else if (expression instanceof DateValue) {
1✔
2242
      DateValue dateValue = (DateValue) expression;
1✔
2243
      appendValue(builder, outputFormat, dateValue.toString(), "", "");
1✔
2244

2245
    } else if (expression instanceof DoubleValue) {
1✔
2246
      DoubleValue doubleValue = (DoubleValue) expression;
1✔
2247
      appendValue(builder, outputFormat, doubleValue.toString(), "", "");
1✔
2248

2249
    } else if (expression instanceof NotExpression) {
1✔
2250
      NotExpression notExpression = (NotExpression) expression;
1✔
2251
      if (notExpression.isExclamationMark()) {
1✔
2252
        appendOperator(builder, outputFormat, "!", "", "");
×
2253
      } else {
2254
        appendOperator(builder, outputFormat, "NOT", "", " ");
1✔
2255
      }
2256

2257
      appendExpression(notExpression.getExpression(), null, builder, indent + 1, i, n, false,
1✔
2258
          BreakLine.AFTER_FIRST);
2259

2260
    } else if (expression instanceof LikeExpression) {
1✔
2261
      LikeExpression likeExpression = (LikeExpression) expression;
1✔
2262

2263
      appendExpression(likeExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2264
          BreakLine.AFTER_FIRST);
2265

2266
      if (likeExpression.isNot()) {
1✔
2267
        appendOperator(builder, outputFormat, "NOT", " ", "");
1✔
2268
      }
2269

2270
      appendOperator(builder, outputFormat, "LIKE", " ", " ");
1✔
2271

2272
      appendExpression(likeExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2273
          BreakLine.AFTER_FIRST);
2274

2275
      Expression escapeExpression = likeExpression.getEscape();
1✔
2276
      if (escapeExpression != null) {
1✔
2277
        appendOperator(builder, outputFormat, "ESCAPE", " ", " ");
×
2278
        appendExpression(escapeExpression, null, builder, indent + 1, i, n, false,
×
2279
            BreakLine.AS_NEEDED);
2280
      }
2281

2282
    } else if (expression instanceof NextValExpression) {
1✔
2283
      NextValExpression nextValExpression = (NextValExpression) expression;
×
2284
      if (nextValExpression.isUsingNextValueFor()) {
×
2285
        appendOperator(builder, outputFormat, "NEXT VALUE FOR", "", " ");
×
2286
      } else {
2287
        appendOperator(builder, outputFormat, "NEXTVAL FOR", "", " ");
×
2288
      }
2289

2290
      int j = 0;
×
2291
      for (String name : nextValExpression.getNameList()) {
×
2292
        if (j > 0) {
×
2293
          builder.append(".");
×
2294
        }
2295
        appendObjectName(builder, outputFormat, name, "", "");
×
2296
        j++;
×
2297
      }
×
2298

2299
    } else if (expression instanceof ExistsExpression) {
1✔
2300
      ExistsExpression existsExpression = (ExistsExpression) expression;
1✔
2301
      if (existsExpression.isNot()) {
1✔
2302
        appendOperator(builder, outputFormat, "NOT EXISTS", "", "");
×
2303
      } else {
2304
        appendOperator(builder, outputFormat, "EXISTS", "", " ");
1✔
2305
      }
2306

2307
      appendExpression(existsExpression.getRightExpression(), null, builder, indent + 1, i, n,
1✔
2308
          false, BreakLine.AFTER_FIRST);
2309

2310
    } else if (expression instanceof ExtractExpression) {
1✔
2311
      ExtractExpression extractExpression = (ExtractExpression) expression;
1✔
2312

2313
      // "EXTRACT(" + name + " FROM " + expression + ')';
2314

2315
      appendKeyWord(builder, outputFormat, "EXTRACT", "", "( ");
1✔
2316
      appendValue(builder, outputFormat, extractExpression.getName(), "", "");
1✔
2317
      appendKeyWord(builder, outputFormat, "FROM", " ", " ");
1✔
2318

2319
      appendExpression(extractExpression.getExpression(), null, builder, indent + 1, i, n, false,
1✔
2320
          BreakLine.AFTER_FIRST);
2321
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2322

2323
    } else if (expression instanceof JdbcNamedParameter) {
1✔
2324
      JdbcNamedParameter jdbcNamedParameter = (JdbcNamedParameter) expression;
1✔
2325
      appendValue(builder, outputFormat, jdbcNamedParameter.toString(), "", "");
1✔
2326

2327
    } else if (expression instanceof JdbcParameter) {
1✔
2328
      JdbcParameter jdbcParameter = (JdbcParameter) expression;
1✔
2329
      appendValue(builder, outputFormat, jdbcParameter.toString(), "", "");
1✔
2330

2331
    } else if (expression instanceof IsNullExpression) {
1✔
2332
      IsNullExpression isNullExpression = (IsNullExpression) expression;
1✔
2333
      appendExpression(isNullExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2334
          BreakLine.AFTER_FIRST);
2335

2336
      if (isNullExpression.isUseNotNull()) {
1✔
2337
        appendOperator(builder, outputFormat, "NOTNULL", " ", "");
×
2338
      } else if (isNullExpression.isUseIsNull()) {
1✔
2339
        if (isNullExpression.isNot()) {
×
2340
          appendOperator(builder, outputFormat, "NOT ISNULL", " ", "");
×
2341
        } else {
2342
          appendOperator(builder, outputFormat, "ISNULL", " ", "");
×
2343
        }
2344
      } else {
2345
        if (isNullExpression.isNot()) {
1✔
2346
          appendOperator(builder, outputFormat, "IS NOT NULL", " ", "");
1✔
2347
        } else {
2348
          appendOperator(builder, outputFormat, "IS NULL", " ", "");
1✔
2349
        }
2350
      }
2351

2352
    } else if (expression instanceof NullValue) {
1✔
2353
      NullValue nullValue = (NullValue) expression;
1✔
2354
      appendKeyWord(builder, outputFormat, nullValue.toString(), "", "");
1✔
2355

2356
    } else if (expression instanceof TimeKeyExpression) {
1✔
2357
      TimeKeyExpression timeKeyExpression = (TimeKeyExpression) expression;
1✔
2358
      appendValue(builder, outputFormat, timeKeyExpression.toString(), "", "");
1✔
2359

2360
    } else if (expression instanceof InExpression) {
1✔
2361
      InExpression inExpression = (InExpression) expression;
1✔
2362
      Expression leftExpression = inExpression.getLeftExpression();
1✔
2363
      boolean useNot = inExpression.isNot();
1✔
2364

2365
      Expression rightExpression = inExpression.getRightExpression();
1✔
2366

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

2369
      if (inExpression.isGlobal()) {
1✔
2370
        appendKeyWord(builder, outputFormat, "GLOBAL", " ", "");
×
2371
      }
2372

2373
      if (useNot) {
1✔
2374
        appendOperator(builder, outputFormat, "NOT IN", " ", " ");
×
2375
      } else {
2376
        appendOperator(builder, outputFormat, "IN", " ", " ");
1✔
2377
      }
2378

2379
      appendExpression(rightExpression, null, builder, indent, i, n, false, BreakLine.AS_NEEDED);
1✔
2380
    } else if (expression instanceof Function) {
1✔
2381
      Function function = (Function) expression;
1✔
2382

2383
      String name = function.getName();
1✔
2384
      ExpressionList<?> parameters = function.getParameters();
1✔
2385
      NamedExpressionList<?> namedParameters = function.getNamedParameters();
1✔
2386
      boolean distinct = function.isDistinct();
1✔
2387
      boolean allColumns = function.isAllColumns();
1✔
2388
      boolean escaped = function.isEscaped();
1✔
2389
      KeepExpression keep = function.getKeep();
1✔
2390
      Object attribute = function.getAttribute();
1✔
2391

2392
      if (escaped) {
1✔
2393
        appendFunction(builder, outputFormat, "fn", " {", " ");
×
2394
      }
2395

2396
      appendFunction(builder, outputFormat, name, "", "");
1✔
2397

2398
      if (parameters != null || namedParameters != null) {
1✔
2399
        if (parameters != null) {
1✔
2400
          if (distinct) {
1✔
2401
            appendKeyWord(builder, outputFormat, "DISTINCT", "( ", " ");
1✔
2402
          } else if (allColumns) {
1✔
2403
            appendKeyWord(builder, outputFormat, "ALL", "( ", " ");
×
2404
          } else {
2405
            builder.append("( ");
1✔
2406
          }
2407

2408
          if (name.equalsIgnoreCase("Decode")) {
1✔
2409
            appendDecodeExpressionsList(parameters, BreakLine.AS_NEEDED, builder, indent);
1✔
2410
          } else {
2411
            appendExpressionList(parameters, builder, indent, BreakLine.AS_NEEDED);
1✔
2412
          }
2413

2414
          appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2415
        } else {
2416
          // @todo: implement this properly and add a test case
2417
          builder.append(namedParameters);
1✔
2418
        }
2419
      } else if (allColumns) {
1✔
2420
        builder.append("( * )");
×
2421
      } else {
2422
        builder.append("()");
1✔
2423
      }
2424

2425
      if (attribute != null) {
1✔
2426
        builder.append(".").append(attribute);
×
2427
      }
2428

2429
      if (keep != null) {
1✔
2430
        builder.append(" ").append(keep);
×
2431
      }
2432

2433
      if (escaped) {
1✔
2434
        builder.append("} ");
×
2435
      }
2436

2437
    } else if (expression instanceof SignedExpression) {
1✔
2438
      SignedExpression signedExpression = (SignedExpression) expression;
1✔
2439
      appendOperator(builder, outputFormat, String.valueOf(signedExpression.getSign()), "", " ");
1✔
2440

2441
      appendExpression(signedExpression.getExpression(), null, builder, indent, i, n, false,
1✔
2442
          BreakLine.NEVER);
2443

2444
    } else if (expression instanceof Select) {
1✔
2445
      Select select = (Select) expression;
1✔
2446
      appendSelect(select, builder, indent, false, false);
1✔
2447

2448
    } else if (expression instanceof RowConstructor) {
1✔
2449
      RowConstructor<?> rowConstructor = (RowConstructor<?>) expression;
×
2450
      appendRowConstructor(builder, indent, rowConstructor);
×
2451

2452
    } else if (expression instanceof MySQLGroupConcat) {
1✔
2453
      MySQLGroupConcat mySQLGroupConcat = (MySQLGroupConcat) expression;
1✔
2454
      appendFunction(builder, outputFormat, "GROUP_CONCAT", "", "( ");
1✔
2455

2456
      int subIndent = getSubIndent(builder, true);
1✔
2457

2458
      if (mySQLGroupConcat.isDistinct()) {
1✔
2459
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
1✔
2460
      }
2461
      appendExpressionsList(mySQLGroupConcat.getExpressionList(), builder, subIndent,
1✔
2462
          BreakLine.AS_NEEDED);
2463
      List<OrderByElement> orderByElements = mySQLGroupConcat.getOrderByElements();
1✔
2464
      appendOrderByElements(orderByElements, builder, subIndent);
1✔
2465

2466
      String separator = mySQLGroupConcat.getSeparator();
1✔
2467
      if (separator != null) {
1✔
2468
        appendNormalizedLineBreak(builder);
1✔
2469
        for (int j = 0; j < subIndent; j++) {
1✔
2470
          builder.append(indentString);
1✔
2471
        }
2472
        appendKeyWord(builder, outputFormat, "SEPARATOR", "", " " + separator);
1✔
2473
      }
2474
      builder.append(" )");
1✔
2475

2476
      // Abstract Class, call last and let the specific implementations catch first
2477
    } else if (expression instanceof BinaryExpression) {
1✔
2478
      BinaryExpression binaryExpression = (BinaryExpression) expression;
1✔
2479
      appendExpression(binaryExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2480
          BreakLine.NEVER);
2481

2482
      if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2483
        appendNormalizedLineBreak(builder);
1✔
2484
        for (int j = 0; j <= indent; j++) {
1✔
2485
          builder.append(indentString);
1✔
2486
        }
2487
      }
2488
      appendOperator(builder, outputFormat, binaryExpression.getStringExpression(), " ", " ");
1✔
2489

2490
      appendExpression(binaryExpression.getRightExpression(), null, builder, indent, i, n, false,
1✔
2491
          BreakLine.NEVER);
2492

2493
    } else if (expression instanceof ExpressionList) {
1✔
2494
      ExpressionList<?> expressions = (ExpressionList<?>) expression;
1✔
2495
      appendExpressionList(expressions, builder, indent, BreakLine.AS_NEEDED);
1✔
2496
    } else if (expression instanceof AllTableColumns) {
1✔
2497
      AllTableColumns allTableColumns = (AllTableColumns) expression;
1✔
2498
      appendObjectName(builder, outputFormat, allTableColumns.getTable().getFullyQualifiedName(),
1✔
2499
          "", ".*");
2500

2501
      if (allTableColumns.getExceptColumns() != null
1✔
2502
          && !allTableColumns.getExceptColumns().isEmpty()) {
×
2503
        appendKeyWord(builder, outputFormat, "EXCEPT", " ", "( ");
×
2504
        appendExpressionsList(allTableColumns.getExceptColumns(), builder, indent,
×
2505
            BreakLine.AS_NEEDED);
2506
        builder.append(" )");
×
2507
      }
2508

2509
      if (allTableColumns.getReplaceExpressions() != null
1✔
2510
          && !allTableColumns.getReplaceExpressions().isEmpty()) {
×
2511
        appendKeyWord(builder, outputFormat, "REPLACE", " ", "( ");
×
2512
        int subIndent = getSubIndent(builder, allTableColumns.getReplaceExpressions().size() > 3);
×
2513
        appendSelectItemList(allTableColumns.getReplaceExpressions(), builder, subIndent, i,
×
2514
            BreakLine.AS_NEEDED, indent);
2515
        builder.append(" )");
×
2516
      }
2517
    } else if (expression instanceof AllColumns) {
1✔
2518
      AllColumns allColumns = (AllColumns) expression;
1✔
2519
      appendObjectName(builder, outputFormat, "*", "", "");
1✔
2520

2521
      if (allColumns.getExceptColumns() != null && !allColumns.getExceptColumns().isEmpty()) {
1✔
2522
        appendKeyWord(builder, outputFormat, "EXCEPT", " ", "( ");
×
2523
        appendExpressionsList(allColumns.getExceptColumns(), builder, indent, BreakLine.AS_NEEDED);
×
2524
        builder.append(" )");
×
2525
      }
2526

2527
      if (allColumns.getReplaceExpressions() != null
1✔
2528
          && !allColumns.getReplaceExpressions().isEmpty()) {
×
2529
        appendKeyWord(builder, outputFormat, "REPLACE", " ", "( ");
×
2530
        int subIndent = getSubIndent(builder, allColumns.getReplaceExpressions().size() > 3);
×
2531
        appendSelectItemList(allColumns.getReplaceExpressions(), builder, subIndent, i,
×
2532
            BreakLine.AS_NEEDED, indent);
2533
        builder.append(" )");
×
2534
      }
2535

2536
    } else if (expression instanceof IntervalExpression) {
1✔
2537
      IntervalExpression intervalExpression = (IntervalExpression) expression;
1✔
2538
      if (intervalExpression.isUsingIntervalKeyword()) {
1✔
2539
        appendKeyWord(builder, outputFormat, "INTERVAL", "", " ");
1✔
2540
      }
2541
      if (intervalExpression.getExpression() != null) {
1✔
2542
        appendExpression(intervalExpression.getExpression(), null, builder, indent, i, n, false,
×
2543
            breakLine);
2544
      } else {
2545
        appendValue(builder, outputFormat, intervalExpression.getParameter(), "", "");
1✔
2546
      }
2547
      if (intervalExpression.getIntervalType() != null) {
1✔
2548
        appendKeyWord(builder, outputFormat, intervalExpression.getIntervalType(), " ", "");
1✔
2549
      }
2550
    } else if (expression instanceof CastExpression) {
1✔
2551
      CastExpression castExpression = (CastExpression) expression;
1✔
2552

2553
      if (castExpression.isImplicitCast()) {
1✔
2554
        appendKeyWord(builder, outputFormat, castExpression.getColDataType().toString(), "", " ");
1✔
2555
        appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2556
            BreakLine.NEVER);
2557
      } else if (castExpression.isUseCastKeyword()) {
1✔
2558
        if (castExpression.getColumnDefinitions().size() > 1) {
1✔
2559
          appendFunction(builder, outputFormat, castExpression.keyword, " ", "( ");
×
2560
          appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, false,
×
2561
              BreakLine.NEVER);
2562
          appendKeyWord(builder, outputFormat, "AS ROW ( ", " ", " ");
×
2563
          int j = 0;
×
2564
          for (ColumnDefinition columnDefinition : castExpression.getColumnDefinitions()) {
×
2565
            if (j++ > 0) {
×
2566
              builder.append(", ");
×
2567
            }
2568
            appendKeyWord(builder, outputFormat, columnDefinition.toString(), "", "");
×
2569
          }
×
2570
        } else {
×
2571
          appendFunction(builder, outputFormat, castExpression.keyword, " ", "( ");
1✔
2572
          appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2573
              BreakLine.NEVER);
2574
          appendKeyWord(builder, outputFormat, "AS " + castExpression.getColDataType().toString(),
1✔
2575
              " ", " )");
2576
        }
2577
      } else {
2578
        appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, true,
1✔
2579
            BreakLine.AS_NEEDED);
2580
        appendKeyWord(builder, outputFormat, castExpression.getColDataType().toString(), "::", "");
1✔
2581
      }
2582
    } else if (expression instanceof Between) {
1✔
2583
      Between between = (Between) expression;
1✔
2584
      appendExpression(between.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2585
          BreakLine.NEVER);
2586

2587
      int subIndent = getSubIndent(builder, false);
1✔
2588
      appendKeyWord(builder, outputFormat, "BETWEEN", between.isNot() ? " NOT " : " ", " ");
1✔
2589

2590
      appendExpression(between.getBetweenExpressionStart(), null, builder, indent + 1, i, n, false,
1✔
2591
          BreakLine.NEVER);
2592

2593
      appendNormalizedLineBreak(builder);
1✔
2594
      for (int j = 0; j <= subIndent; j++) {
1✔
2595
        builder.append(indentString);
1✔
2596
      }
2597
      appendKeyWord(builder, outputFormat, "AND", " ", " ");
1✔
2598

2599
      appendExpression(between.getBetweenExpressionEnd(), null, builder, indent + 1, i, n, false,
1✔
2600
          BreakLine.NEVER);
2601
    } else if (expression instanceof ArrayConstructor) {
1✔
2602
      ArrayConstructor arrayConstructor = (ArrayConstructor) expression;
1✔
2603
      if (arrayConstructor.isArrayKeyword()) {
1✔
2604
        appendKeyWord(builder, outputFormat, "ARRAY", " ", "");
×
2605
      }
2606

2607
      boolean multiline = false;
1✔
2608
      for (Expression p : arrayConstructor.getExpressions()) {
1✔
2609
        if (p instanceof ArrayConstructor || p instanceof ExpressionList || p instanceof Select
1✔
2610
            || p instanceof StructType) {
2611
          multiline = true;
1✔
2612
          break;
1✔
2613
        }
2614
      }
×
2615

2616
      int subIndent = getSubIndent(builder, true);
1✔
2617
      builder.append("[ ");
1✔
2618
      if (multiline) {
1✔
2619
        appendExpressionList(arrayConstructor.getExpressions(), builder, subIndent,
1✔
2620
            BreakLine.ALWAYS);
2621
        appendNormalizedLineBreak(builder);
1✔
2622
        for (int j = 0; j < subIndent; j++) {
1✔
2623
          builder.append(indentString);
1✔
2624
        }
2625
      } else {
2626
        appendExpressionList(arrayConstructor.getExpressions(), builder, subIndent,
×
2627
            BreakLine.NEVER);
2628
      }
2629
      builder.append("]");
1✔
2630
    } else if (expression instanceof TranscodingFunction) {
1✔
2631
      TranscodingFunction transcodingFunction = (TranscodingFunction) expression;
1✔
2632
      appendFunction(builder, outputFormat, "Convert", "", "(");
1✔
2633
      if (transcodingFunction.isTranscodeStyle()) {
1✔
2634
        appendExpression(transcodingFunction.getExpression(), null, builder, indent, 0, 1, false,
×
2635
            BreakLine.AS_NEEDED);
2636
        appendKeyWord(builder, outputFormat, "USING", " ", "");
×
2637
        appendKeyWord(builder, outputFormat, transcodingFunction.getTranscodingName(), " ", " )");
×
2638
      } else {
2639
        appendKeyWord(builder, outputFormat, transcodingFunction.getColDataType().toString(), " ",
1✔
2640
            ", ");
2641
        appendExpression(transcodingFunction.getExpression(), null, builder, indent, 0, 1, false,
1✔
2642
            BreakLine.AS_NEEDED);
2643

2644
        String transcodingName = transcodingFunction.getTranscodingName();
1✔
2645
        if (transcodingName != null && !transcodingName.isEmpty()) {
1✔
2646
          appendKeyWord(builder, outputFormat, transcodingName, ", ", " )");
×
2647
        } else {
2648
          builder.append(" )");
1✔
2649
        }
2650
      }
2651
    } else if (expression instanceof AnalyticExpression) {
1✔
2652
      AnalyticExpression analyticExpression = (AnalyticExpression) expression;
1✔
2653

2654
      int subIndent = getSubIndent(builder, false);
1✔
2655
      appendFunction(builder, outputFormat, analyticExpression.getName(), "", "( ");
1✔
2656
      if (analyticExpression.isDistinct()) {
1✔
2657
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
×
2658
      }
2659

2660
      Expression expr = analyticExpression.getExpression();
1✔
2661
      if (expr != null) {
1✔
2662
        appendExpression(expr, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
2663
        if (analyticExpression.getOffset() != null) {
1✔
2664
          builder.append(", ");
1✔
2665
          appendExpression(analyticExpression.getOffset(), null, builder, indent, 0, 1, true,
1✔
2666
              BreakLine.NEVER);
2667
          if (analyticExpression.getDefaultValue() != null) {
1✔
2668
            builder.append(", ");
×
2669
            appendExpression(analyticExpression.getDefaultValue(), null, builder, indent, 0, 1,
×
2670
                true, BreakLine.NEVER);
2671
          }
2672
        }
2673
      } else if (analyticExpression.isAllColumns()) {
×
2674
        builder.append("*");
×
2675
      }
2676

2677
      if (analyticExpression.getHavingClause() != null) {
1✔
2678
        analyticExpression.getHavingClause().appendTo(builder);
×
2679
      }
2680

2681
      if (analyticExpression.getNullHandling() != null) {
1✔
2682
        switch (analyticExpression.getNullHandling()) {
1✔
2683
          case IGNORE_NULLS:
2684
            builder.append(" IGNORE NULLS");
1✔
2685
            break;
1✔
2686
          case RESPECT_NULLS:
2687
            builder.append(" RESPECT NULLS");
×
2688
        }
2689
      }
2690

2691
      if (analyticExpression.getFuncOrderBy() != null) {
1✔
2692
        builder.append(" ORDER BY ");
1✔
2693
        builder.append(analyticExpression.getFuncOrderBy().stream().map(OrderByElement::toString)
1✔
2694
            .collect(Collectors.joining(", ")));
1✔
2695
      }
2696

2697
      if (analyticExpression.getLimit() != null) {
1✔
2698
        builder.append(analyticExpression.getLimit());
×
2699
      }
2700
      builder.append(" ) ");
1✔
2701

2702
      if (analyticExpression.getKeep() != null) {
1✔
2703
        appendNormalizedLineBreak(builder);
×
2704
        for (int j = 0; j < subIndent + 1; j++) {
×
2705
          builder.append(indentString);
×
2706
        }
2707
        builder.append(analyticExpression.getKeep()).append(" ");
×
2708
      }
2709

2710
      if (analyticExpression.getFilterExpression() != null) {
1✔
2711
        appendNormalizedLineBreak(builder);
1✔
2712
        for (int j = 0; j < subIndent + 1; j++) {
1✔
2713
          builder.append(indentString);
1✔
2714
        }
2715
        builder.append("FILTER ( WHERE ");
1✔
2716
        builder.append(analyticExpression.getFilterExpression());
1✔
2717
        builder.append(" )");
1✔
2718
        if (analyticExpression.getType() != AnalyticType.FILTER_ONLY) {
1✔
2719
          builder.append(" ");
×
2720
        }
2721
      }
2722

2723
      if (analyticExpression.isIgnoreNullsOutside()) {
1✔
2724
        builder.append("IGNORE NULLS ");
×
2725
      }
2726

2727
      switch (analyticExpression.getType()) {
1✔
2728
        case FILTER_ONLY:
2729
          return;
1✔
2730
        case WITHIN_GROUP:
2731
          appendNormalizedLineBreak(builder);
1✔
2732
          for (int j = 0; j < subIndent + 1; j++) {
1✔
2733
            builder.append(indentString);
1✔
2734
          }
2735
          builder.append("WITHIN GROUP");
1✔
2736
          break;
1✔
2737
        case WITHIN_GROUP_OVER:
2738
          appendNormalizedLineBreak(builder);
×
2739
          for (int j = 0; j < subIndent + 1; j++) {
×
2740
            builder.append(indentString);
×
2741
          }
2742
          builder.append("WITHIN GROUP ( ");
×
2743
          analyticExpression.getWindowDefinition().getOrderBy().toStringOrderByElements(builder);
×
2744
          builder.append(" ) OVER ( ");
×
2745
          analyticExpression.getWindowDefinition().getPartitionBy().toStringPartitionBy(builder);
×
2746
          builder.append(" )");
×
2747
          break;
×
2748
        default:
2749
          appendNormalizedLineBreak(builder);
1✔
2750
          for (int j = 0; j < subIndent + 1; j++) {
1✔
2751
            builder.append(indentString);
1✔
2752
          }
2753
          builder.append("OVER");
1✔
2754
      }
2755

2756
      if (analyticExpression.getWindowName() != null) {
1✔
2757
        builder.append(" ").append(analyticExpression.getWindowName());
1✔
2758
      } else if (analyticExpression.getType() != AnalyticType.WITHIN_GROUP_OVER) {
1✔
2759
        builder.append(" ");
1✔
2760
        builder.append(analyticExpression.getWindowDefinition());
1✔
2761
      }
2762
    } else {
1✔
2763
      LOGGER
1✔
2764
          .warning("Unhandled expression: " + expression.getClass().getName() + " = " + expression);
1✔
2765
      builder.append(expression);
1✔
2766
    }
2767

2768
    if (alias != null) {
1✔
2769
      appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2770
      if (alias.isUseAs()) {
1✔
2771
        appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2772
      }
2773
      appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2774
    }
2775

2776
    switch (separation) {
1✔
2777
      case AFTER:
2778
        appendNormalizingTrailingWhiteSpace(builder, commaSeparated && i < n - 1 ? ", " : "");
1✔
2779
        break;
2780
    }
2781
  }
1✔
2782

2783
  private static void appendExpressionList(ExpressionList<?> expressionList, StringBuilder builder,
2784
      int indent, BreakLine breakLine) {
2785
    int subIndent = indent;
1✔
2786
    if (expressionList instanceof ParenthesedExpressionList) {
1✔
2787
      builder.append("( ");
1✔
2788
      subIndent++;
1✔
2789
    }
2790
    appendExpressionsList(expressionList, builder, subIndent, breakLine);
1✔
2791
    if (expressionList instanceof ParenthesedExpressionList) {
1✔
2792
      builder.append(" )");
1✔
2793
    }
2794
  }
1✔
2795

2796
  private static void appendExpressionsList(List<? extends Expression> expressions,
2797
      StringBuilder builder, int indent, BreakLine breakLine) {
2798
    int size = expressions.size();
1✔
2799
    int subIndent =
2800
        breakLine.equals(BreakLine.NEVER) || breakLine.equals(BreakLine.AS_NEEDED) && size <= 3
1✔
2801
            || size == 1 ? indent : getSubIndent(builder, true);
1✔
2802

2803
    int i = 0;
1✔
2804
    for (Expression expression : expressions) {
1✔
2805
      switch (breakLine) {
1✔
2806
        case AS_NEEDED:
2807
          BreakLine bl =
2808
              size == 4 || size >= 5 && i % 3 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
2809
          appendExpression(expression, null, builder, subIndent, i, expressions.size(), true, bl);
1✔
2810
          break;
1✔
2811

2812
        default:
2813
          appendExpression(expression, null, builder, subIndent, i, expressions.size(), true,
1✔
2814
              breakLine);
2815
      }
2816
      i++;
1✔
2817
    }
1✔
2818
  }
1✔
2819

2820
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2821
  private static void appendFromItem(FromItem fromItem, StringBuilder builder, int indent, int i,
2822
      int n) {
2823

2824
    if (fromItem != null) {
1✔
2825
      if (i > 0) {
1✔
2826
        appendNormalizedLineBreak(builder);
×
2827
        for (int j = 0; j <= indent; j++) {
×
2828
          builder.append(indentString);
×
2829
        }
2830
      }
2831

2832
      switch (separation) {
1✔
2833
        case BEFORE:
2834
          builder.append(i > 0 ? ", " : "");
1✔
2835
      }
2836

2837
      Alias alias = fromItem.getAlias();
1✔
2838

2839
      // All Known Implementing Classes: LateralSubSelect, ParenthesisFromItem,
2840
      // SpecialSubSelect, SubJoin, SubSelect, Table, TableFunction, ValuesList
2841
      if (fromItem instanceof Table) {
1✔
2842
        Table table = (Table) fromItem;
1✔
2843
        appendTable(table, alias, builder);
1✔
2844
      } else if (fromItem instanceof Select) {
1✔
2845
        Select select = (Select) fromItem;
1✔
2846
        appendSelect(select, builder, indent, false, true);
1✔
2847

2848
        if (alias != null) {
1✔
2849
          appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2850
          if (alias.isUseAs()) {
1✔
2851
            appendKeyWord(builder, outputFormat, "AS", "", " ");
×
2852
          }
2853

2854
          appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2855
        }
2856
      } else if (fromItem instanceof ParenthesedFromItem) {
1✔
2857
        ParenthesedFromItem parenthesedFromItem = (ParenthesedFromItem) fromItem;
1✔
2858
        builder.append("( ");
1✔
2859
        int subIndent = getSubIndent(builder, true);
1✔
2860

2861
        appendFromItem(parenthesedFromItem.getFromItem(), builder, indent, i, n);
1✔
2862
        List<Join> joins = parenthesedFromItem.getJoins();
1✔
2863
        appendJoins(joins, builder, subIndent);
1✔
2864
        builder.append(" )");
1✔
2865

2866
        if (alias != null) {
1✔
2867
          appendNormalizingTrailingWhiteSpace(builder, " ");
×
2868
          if (alias.isUseAs()) {
×
2869
            appendKeyWord(builder, outputFormat, "AS", "", " ");
×
2870
          }
2871

2872
          appendAlias(builder, outputFormat, alias.getName(), "", " ");
×
2873
        }
2874
      } else {
1✔
2875
        LOGGER.log(Level.WARNING, "FROM Item not covered: " + fromItem.getClass().getName());
×
2876
      }
2877

2878
      switch (separation) {
1✔
2879
        case AFTER:
2880
          appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? ", " : "");
1✔
2881
          break;
2882
      }
2883
    }
2884
  }
1✔
2885

2886
  private static void appendTable(Table table, Alias alias, StringBuilder builder) {
2887

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

2896
        appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2897
      }
2898
    }
2899
  }
1✔
2900

2901
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2902
  private static void appendSetOperation(SetOperation setOperation, StringBuilder builder,
2903
      int indent) {
2904

2905
    // Direct Known Subclasses:
2906
    // ExceptOp, IntersectOp, MinusOp, UnionOp
2907
    if (setOperation instanceof UnionOp) {
1✔
2908
      UnionOp unionOp = (UnionOp) setOperation;
1✔
2909

2910
      appendNormalizedLineBreak(builder);
1✔
2911
      for (int j = 0; j < indent; j++) {
1✔
2912
        builder.append(indentString);
1✔
2913
      }
2914
      appendOperator(builder, outputFormat, "UNION", "", " ");
1✔
2915

2916
      if (unionOp.isAll()) {
1✔
2917
        appendOperator(builder, outputFormat, "ALL", "", " ");
1✔
2918
      }
2919
    } else if (setOperation instanceof MinusOp) {
1✔
2920
      appendNormalizedLineBreak(builder);
1✔
2921
      for (int j = 0; j < indent; j++) {
1✔
2922
        builder.append(indentString);
1✔
2923
      }
2924
      appendOperator(builder, outputFormat, "MINUS", "", " ");
1✔
2925
    } else if (setOperation instanceof IntersectOp) {
×
2926
      appendNormalizedLineBreak(builder);
×
2927
      for (int j = 0; j < indent; j++) {
×
2928
        builder.append(indentString);
×
2929
      }
2930
      appendOperator(builder, outputFormat, "INTERSECT", "", " ");
×
2931

2932
    } else if (setOperation instanceof ExceptOp) {
×
2933
      appendNormalizedLineBreak(builder);
×
2934
      for (int j = 0; j < indent; j++) {
×
2935
        builder.append(indentString);
×
2936
      }
2937
      appendOperator(builder, outputFormat, "EXCEPT", "", " ");
×
2938
    } else if (setOperation != null) {
×
2939
      throw new UnsupportedOperationException(
×
2940
          setOperation.getClass().getName() + " is not supported yet.");
×
2941
    }
2942
  }
1✔
2943

2944
  private static void appendTruncate(StringBuilder builder, Truncate truncate) {
2945
    Table table = truncate.getTable();
1✔
2946
    boolean cascade = truncate.getCascade();
1✔
2947

2948
    appendKeyWord(builder, outputFormat, "TRUNCATE TABLE", "", " ")
1✔
2949
        .append(table.getFullyQualifiedName());
1✔
2950
    if (cascade) {
1✔
2951
      appendOperator(builder, outputFormat, "CASCADE", " ", "");
1✔
2952
    }
2953
  }
1✔
2954

2955
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
2956
  private static void appendCreateTable(StringBuilder builder, CreateTable createTable,
2957
      int indent) {
2958

2959
    int i = 0;
1✔
2960
    appendNormalizedLineBreak(builder);
1✔
2961

2962
    List<String> createOptionsString = createTable.getCreateOptionsStrings();
1✔
2963
    String createOps = createOptionsString != null && !createOptionsString.isEmpty()
1✔
2964
        ? PlainSelect.getStringList(createOptionsString, false, false)
×
2965
        : null;
1✔
2966

2967
    boolean unlogged = createTable.isUnlogged();
1✔
2968
    boolean ifNotExists = createTable.isIfNotExists();
1✔
2969

2970
    Table table = createTable.getTable();
1✔
2971
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
2972

2973
    if (unlogged) {
1✔
2974
      appendHint(builder, outputFormat, "UNLOGGED", "", " ");
×
2975
    }
2976

2977
    if (createOps != null) {
1✔
2978
      appendHint(builder, outputFormat, createOps, "", " ");
×
2979
    }
2980

2981
    appendKeyWord(builder, outputFormat, "TABLE", "", " ");
1✔
2982

2983
    if (ifNotExists) {
1✔
2984
      appendHint(builder, outputFormat, "IF NOT EXISTS", "", " ");
×
2985
    }
2986

2987
    appendAlias(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
2988

2989
    List<ColumnDefinition> columnDefinitions = createTable.getColumnDefinitions();
1✔
2990
    List<Index> indexes = createTable.getIndexes();
1✔
2991

2992
    if (columnDefinitions != null && !columnDefinitions.isEmpty()) {
1✔
2993
      builder.append(" (");
1✔
2994

2995
      int colWidth = 0;
1✔
2996
      int typeWidth = 0;
1✔
2997

2998
      for (ColumnDefinition columnDefinition : columnDefinitions) {
1✔
2999
        String columnName = columnDefinition.getColumnName();
1✔
3000
        // @todo: please get rid of that Replace workaround
3001
        String colDataType = columnDefinition.getColDataType().toString().replace(", ", ",");
1✔
3002

3003
        if (colWidth < columnName.length()) {
1✔
3004
          colWidth = columnName.length();
1✔
3005
        }
3006

3007
        if (typeWidth < colDataType.length()) {
1✔
3008
          typeWidth = colDataType.length();
1✔
3009
        }
3010
      }
1✔
3011

3012
      // int typeIndex = (((indent +1)* indentString.length() + colWidth + 1) /
3013
      // indentString.length()) * ("
3014
      // ".length() + 1);
3015

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

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

3020
      for (ColumnDefinition columnDefinition : columnDefinitions) {
1✔
3021
        appendNormalizedLineBreak(builder);
1✔
3022
        for (int j = 0; j <= indent; j++) {
1✔
3023
          builder.append(indentString);
1✔
3024
        }
3025

3026
        String columnName = columnDefinition.getColumnName();
1✔
3027
        ColDataType colDataType = columnDefinition.getColDataType();
1✔
3028
        List<String> columnSpecs = columnDefinition.getColumnSpecs();
1✔
3029

3030
        switch (separation) {
1✔
3031
          case BEFORE:
3032
            builder.append(i > 0 ? ", " : "");
1✔
3033
        }
3034

3035
        appendObjectName(builder, outputFormat, columnName, "", "");
1✔
3036

3037
        int lastLineLength = getLastLineLength(builder);
1✔
3038

3039
        for (int j = lastLineLength; j <= typeIndex * indentWidth; j++) {
1✔
3040
          builder.append(" ");
1✔
3041
        }
3042
        // @todo: please get rid of that Replace workaround
3043
        appendType(builder, outputFormat, colDataType.toString().replace(", ", ","), "", "");
1✔
3044

3045
        lastLineLength = getLastLineLength(builder);
1✔
3046

3047
        if (columnSpecs != null && !columnSpecs.isEmpty()) {
1✔
3048
          for (int j = lastLineLength; j <= specIndex * indentWidth; j++) {
1✔
3049
            builder.append(" ");
1✔
3050
          }
3051
          appendType(builder, outputFormat, PlainSelect.getStringList(columnSpecs, false, false),
1✔
3052
              "", "");
3053
        }
3054

3055
        switch (separation) {
1✔
3056
          case AFTER:
3057
            appendNormalizingTrailingWhiteSpace(builder,
1✔
3058
                i < columnDefinitions.size() + indexes.size() - 1 ? ", " : "");
1✔
3059
            break;
3060
        }
3061

3062
        i++;
1✔
3063
      }
1✔
3064

3065
      // Direct Known Subclasses:
3066
      // ExcludeConstraint, NamedConstraint
3067

3068
      // Direct Known Subclasses:
3069
      // CheckConstraint, ForeignKeyIndex
3070

3071
      if (indexes != null && !indexes.isEmpty()) {
1✔
3072
        for (Index index : indexes) {
1✔
3073
          appendNormalizedLineBreak(builder);
1✔
3074
          for (int j = 0; j <= indent; j++) {
1✔
3075
            builder.append(indentString);
1✔
3076
          }
3077

3078
          switch (separation) {
1✔
3079
            case BEFORE:
3080
              builder.append(i > 0 ? ", " : "");
1✔
3081
          }
3082

3083
          if (index instanceof ForeignKeyIndex) {
1✔
3084
            ForeignKeyIndex foreignKeyIndex = (ForeignKeyIndex) index;
1✔
3085

3086
            String type = foreignKeyIndex.getType();
1✔
3087
            String name = foreignKeyIndex.getName();
1✔
3088
            List<String> columnsNames = foreignKeyIndex.getColumnsNames();
1✔
3089
            List<String> idxSpec = foreignKeyIndex.getIndexSpec();
1✔
3090
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
1✔
3091

3092
            // @todo: beautify the expression
3093
            // @todo: add a test case
3094
            if (name != null) {
1✔
3095
              appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
1✔
3096
              appendAlias(builder, outputFormat, name, "", "");
1✔
3097
              appendNormalizedLineBreak(builder);
1✔
3098
            }
3099

3100
            for (int j = 0; name != null && j <= indent + 1; j++) {
1✔
3101
              builder.append(indentString);
1✔
3102
            }
3103

3104
            appendKeyWord(builder, outputFormat, type, "", " ");
1✔
3105

3106
            builder.append("( ");
1✔
3107
            int subIndent = getSubIndent(builder, columnsNames.size() > 2);
1✔
3108
            BreakLine bl = columnsNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3109
            appendStringList(columnsNames, builder, subIndent, true, bl);
1✔
3110
            builder.append(" )");
1✔
3111

3112
            // @todo: add a test case for this
3113
            if (idxSpec != null && !idxSpecText.isEmpty()) {
1✔
3114
              appendHint(builder, outputFormat, idxSpecText, " ", "");
×
3115
            }
3116

3117
            Table foreignTable = foreignKeyIndex.getTable();
1✔
3118
            List<String> referencedColumnNames = foreignKeyIndex.getReferencedColumnNames();
1✔
3119

3120
            appendNormalizedLineBreak(builder);
1✔
3121
            for (int j = 0; j <= indent + 1; j++) {
1✔
3122
              builder.append(indentString);
1✔
3123
            }
3124
            appendKeyWord(builder, outputFormat, "REFERENCES", "", " ");
1✔
3125
            appendObjectName(builder, outputFormat, foreignTable.getFullyQualifiedName(), "", " ");
1✔
3126

3127
            builder.append("( ");
1✔
3128
            subIndent = getSubIndent(builder, referencedColumnNames.size() > 2);
1✔
3129
            bl = referencedColumnNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3130
            appendStringList(referencedColumnNames, builder, subIndent, true, bl);
1✔
3131
            builder.append(" )");
1✔
3132

3133
            ReferentialAction updateAction =
1✔
3134
                foreignKeyIndex.getReferentialAction(ReferentialAction.Type.UPDATE);
1✔
3135
            if (updateAction != null) {
1✔
3136
              appendNormalizedLineBreak(builder);
×
3137
              for (int j = 0; j <= indent + 2; j++) {
×
3138
                builder.append(indentString);
×
3139
              }
3140
              builder.append(updateAction);
×
3141
            }
3142

3143
            ReferentialAction deleteAction =
1✔
3144
                foreignKeyIndex.getReferentialAction(ReferentialAction.Type.DELETE);
1✔
3145
            if (deleteAction != null) {
1✔
3146
              appendNormalizedLineBreak(builder);
×
3147
              for (int j = 0; j <= indent + 2; j++) {
×
3148
                builder.append(indentString);
×
3149
              }
3150
              builder.append(deleteAction);
×
3151
            }
3152

3153
          } else if (index instanceof CheckConstraint) {
1✔
3154
            CheckConstraint checkConstraint = (CheckConstraint) index;
×
3155

3156
            String contraintName = checkConstraint.getName();
×
3157
            Expression expression = checkConstraint.getExpression();
×
3158

3159
            appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
×
3160
            appendAlias(builder, outputFormat, contraintName, "", "");
×
3161

3162
            appendNormalizedLineBreak(builder);
×
3163
            for (int j = 0; j <= indent + 1; j++) {
×
3164
              builder.append(indentString);
×
3165
            }
3166

3167
            builder.append(" CHECK (").append(expression).append(")");
×
3168

3169
          } else if (index instanceof NamedConstraint) {
1✔
3170
            NamedConstraint namedConstraint = (NamedConstraint) index;
1✔
3171

3172
            String type = namedConstraint.getType();
1✔
3173
            String name = namedConstraint.getName();
1✔
3174
            List<String> columnsNames = namedConstraint.getColumnsNames();
1✔
3175
            List<String> idxSpec = namedConstraint.getIndexSpec();
1✔
3176
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
1✔
3177

3178
            // @todo: beautify the expression
3179
            // @todo: add a test case
3180
            if (name != null) {
1✔
3181
              appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
1✔
3182
              appendAlias(builder, outputFormat, name, "", "");
1✔
3183
              appendNormalizedLineBreak(builder);
1✔
3184
            }
3185

3186
            for (int j = 0; name != null && j <= indent + 1; j++) {
1✔
3187
              builder.append(indentString);
1✔
3188
            }
3189
            appendKeyWord(builder, outputFormat, type, "", " ");
1✔
3190

3191
            builder.append("( ");
1✔
3192
            int subIndent = getSubIndent(builder, columnsNames.size() > 2);
1✔
3193
            BreakLine bl = columnsNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3194
            appendStringList(columnsNames, builder, subIndent, true, bl);
1✔
3195
            builder.append(" )");
1✔
3196

3197
            // @todo: add a test case for this
3198
            if (idxSpec != null && !idxSpecText.isEmpty()) {
1✔
3199
              appendHint(builder, outputFormat, idxSpecText, " ", "");
×
3200
            }
3201

3202
          } else if (index instanceof ExcludeConstraint) {
1✔
3203
            ExcludeConstraint excludeConstraint = (ExcludeConstraint) index;
×
3204
            Expression expression = excludeConstraint.getExpression();
×
3205

3206
            // @todo: beautify the expression
3207
            // @todo: add a test case
3208

3209
            builder.append("EXCLUDE WHERE ");
×
3210
            builder.append("(");
×
3211
            builder.append(expression);
×
3212
            builder.append(")");
×
3213

3214
          } else if (index != null) {
×
3215
            String type = index.getType();
×
3216
            String name = index.getName();
×
3217
            List<Index.ColumnParams> columnParams = index.getColumns();
×
3218
            List<String> idxSpec = index.getIndexSpec();
×
3219
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
×
3220

3221
            builder.append(type);
×
3222

3223
            appendKeyWord(builder, outputFormat, type, "", " ");
×
3224
            if (name != null) {
×
3225
              appendAlias(builder, outputFormat, name, "", "");
×
3226
            }
3227

3228
            builder.append(" ").append(PlainSelect.getStringList(columnParams, true, true))
×
3229
                .append(!idxSpecText.isEmpty() ? " " + idxSpecText : "");
×
3230
          }
3231

3232
          switch (separation) {
1✔
3233
            case AFTER:
3234
              appendNormalizingTrailingWhiteSpace(builder,
1✔
3235
                  i < columnDefinitions.size() + indexes.size() - 1 ? ", " : "");
1✔
3236
              break;
3237
          }
3238

3239
          i++;
1✔
3240
        }
1✔
3241
      }
3242
      appendNormalizedLineBreak(builder).append(")");
1✔
3243
    }
3244
    List<String> tableOptionsStrings = createTable.getTableOptionsStrings();
1✔
3245
    String options = PlainSelect.getStringList(tableOptionsStrings, false, false);
1✔
3246
    if (options != null && !options.isEmpty()) {
1✔
3247
      appendHint(builder, outputFormat, options, " ", "");
1✔
3248
    }
3249

3250
    RowMovement rowMovement = createTable.getRowMovement();
1✔
3251
    if (rowMovement != null) {
1✔
3252
      // @todo: beautify this part
3253
      // @todo: provide test cases
3254
      builder.append(" ").append(rowMovement.getMode()).append(" ROW MOVEMENT");
×
3255
    }
3256

3257
    Select select = createTable.getSelect();
1✔
3258
    if (select != null) {
1✔
3259

3260
      appendNormalizedLineBreak(builder);
1✔
3261
      for (int j = 0; j <= indent; j++) {
1✔
3262
        builder.append(indentString);
1✔
3263
      }
3264
      builder.append("AS ");
1✔
3265
      appendSelect(select, builder, indent, false, false);
1✔
3266
    }
3267

3268
    Table likeTable = createTable.getLikeTable();
1✔
3269
    if (likeTable != null) {
1✔
3270

3271
      builder.append(" AS ");
×
3272

3273
      Alias alias = likeTable.getAlias();
×
3274

3275
      appendTable(likeTable, alias, builder);
×
3276
    }
3277
  }
1✔
3278

3279
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
3280
  private static void appendCreateIndex(StringBuilder builder, CreateIndex createIndex,
3281
      int indent) {
3282

3283
    Index index = createIndex.getIndex();
1✔
3284
    Table table = createIndex.getTable();
1✔
3285

3286
    List<String> tailParameters = createIndex.getTailParameters();
1✔
3287
    List<Index.ColumnParams> columnsParameters = index.getColumns();
1✔
3288

3289
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3290

3291
    if (index.getType() != null) {
1✔
3292
      appendKeyWord(builder, outputFormat, index.getType(), "", " ");
1✔
3293
    }
3294

3295
    appendKeyWord(builder, outputFormat, "INDEX", "", " ");
1✔
3296
    if (createIndex.isUsingIfNotExists()) {
1✔
3297
      appendKeyWord(builder, outputFormat, "IF NOT EXISTS", "", " ");
×
3298
    }
3299
    appendAlias(builder, outputFormat, index.getName(), "", "");
1✔
3300

3301
    appendNormalizedLineBreak(builder);
1✔
3302
    for (int j = 0; j <= indent; j++) {
1✔
3303
      builder.append(indentString);
1✔
3304
    }
3305
    appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
3306
    appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3307

3308
    if (index.getUsing() != null) {
1✔
3309
      appendKeyWord(builder, outputFormat, "USING", "  ", " ");
×
3310
      builder.append(index.getUsing());
×
3311
    }
3312

3313
    if (index.getColumnsNames() != null) {
1✔
3314
      builder.append("( ");
1✔
3315

3316
      int subIndent = getSubIndent(builder, columnsParameters.size() > 2);
1✔
3317
      BreakLine bl = columnsParameters.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3318

3319
      int i = 0;
1✔
3320
      for (Index.ColumnParams param : columnsParameters) {
1✔
3321
        appendString(param.getColumnName(), builder, subIndent, i, columnsParameters.size(), true,
1✔
3322
            bl);
3323
        i++;
1✔
3324
      }
1✔
3325

3326
      builder.append(" )");
1✔
3327

3328
      if (tailParameters != null) {
1✔
3329
        builder.append(" ");
1✔
3330
        for (String param : tailParameters) {
1✔
3331
          appendHint(builder, outputFormat, param, "", " ");
1✔
3332
        }
1✔
3333
      }
3334
    }
3335
  }
1✔
3336

3337
  private static void appendCreateView(StringBuilder builder, CreateView createView, int indent) {
3338
    boolean isOrReplace = createView.isOrReplace();
1✔
3339
    ForceOption force = createView.getForce();
1✔
3340
    TemporaryOption temp = createView.getTemporary();
1✔
3341
    boolean isMaterialized = createView.isMaterialized();
1✔
3342

3343
    Table view = createView.getView();
1✔
3344

3345
    ExpressionList<Column> columnNames = createView.getColumnNames();
1✔
3346
    Select select = createView.getSelect();
1✔
3347
    boolean isWithReadOnly = createView.isWithReadOnly();
1✔
3348

3349
    appendNormalizedLineBreak(builder);
1✔
3350

3351
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3352
    if (isOrReplace) {
1✔
3353
      appendKeyWord(builder, outputFormat, "OR REPLACE", "", " ");
1✔
3354
    }
3355
    switch (force) {
1✔
3356
      case FORCE:
3357
        appendKeyWord(builder, outputFormat, "FORCE", "", " ");
×
3358
        break;
×
3359
      case NO_FORCE:
3360
        appendKeyWord(builder, outputFormat, "NO FORCE", "", " ");
×
3361
        break;
3362
    }
3363

3364
    if (temp != TemporaryOption.NONE) {
1✔
3365
      builder.append(temp.name()).append(" ");
×
3366
    }
3367

3368
    if (isMaterialized) {
1✔
3369
      appendKeyWord(builder, outputFormat, "MATERIALIZED", "", " ");
×
3370
    }
3371
    appendKeyWord(builder, outputFormat, "VIEW", "", " ");
1✔
3372
    appendAlias(builder, outputFormat, view.getFullyQualifiedName(), "", "");
1✔
3373
    if (columnNames != null) {
1✔
3374
      builder.append(PlainSelect.getStringList(columnNames, true, true));
×
3375
    }
3376

3377
    appendNormalizedLineBreak(builder);
1✔
3378
    for (int j = 0; j <= indent; j++) {
1✔
3379
      builder.append(indentString);
1✔
3380
    }
3381
    appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
3382
    appendSelect(select, builder, indent + 2, false, false);
1✔
3383

3384
    if (isWithReadOnly) {
1✔
3385
      builder.append(" WITH READ ONLY");
×
3386
      appendHint(builder, outputFormat, "WITH READ ONLY", " ", "");
×
3387
    }
3388
  }
1✔
3389

3390
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
3391
  private static void appendAlter(StringBuilder builder, Alter alter, int indent) {
3392
    boolean useOnly = alter.isUseOnly();
1✔
3393
    Table table = alter.getTable();
1✔
3394
    List<AlterExpression> alterExpressions = alter.getAlterExpressions();
1✔
3395

3396
    appendKeyWord(builder, outputFormat, "ALTER TABLE", "", " ");
1✔
3397
    if (useOnly) {
1✔
3398
      appendKeyWord(builder, outputFormat, "ONLY", "", " ");
×
3399
    }
3400
    appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3401
    int i = 0;
1✔
3402

3403
    if (alterExpressions != null) {
1✔
3404
      for (AlterExpression alterExpression : alterExpressions) {
1✔
3405
        if (i > 0) {
1✔
3406
          appendNormalizedLineBreak(builder);
×
3407
          for (int j = 0; j <= indent; j++) {
×
3408
            builder.append(indentString);
×
3409
          }
3410
        }
3411

3412
        switch (separation) {
1✔
3413
          case BEFORE:
3414
            builder.append(i > 0 ? ", " : "");
1✔
3415
        }
3416

3417
        AlterOperation operation = alterExpression.getOperation();
1✔
3418
        String commentText = alterExpression.getCommentText();
1✔
3419
        String columnName = alterExpression.getColumnName();
1✔
3420
        String columnOldName = alterExpression.getColumnOldName();
1✔
3421

3422
        List<AlterExpression.ColumnDataType> colDataTypeList = alterExpression.getColDataTypeList();
1✔
3423
        String optionalSpecifier = alterExpression.getOptionalSpecifier();
1✔
3424

3425
        List<AlterExpression.ColumnDropNotNull> columnDropNotNullList =
1✔
3426
            alterExpression.getColumnDropNotNullList();
1✔
3427

3428
        String constraintName = alterExpression.getConstraintName();
1✔
3429
        boolean constraintIfExists = alterExpression.isUsingIfExists();
1✔
3430

3431
        List<String> pkColumns = alterExpression.getPkColumns();
1✔
3432
        List<String> ukColumns = alterExpression.getUkColumns();
1✔
3433
        String ukName = alterExpression.getUkName();
1✔
3434
        boolean uk = alterExpression.getUk();
1✔
3435

3436
        List<String> fkColumns = alterExpression.getFkColumns();
1✔
3437
        String fkSourceTable = alterExpression.getFkSourceTable();
1✔
3438
        List<String> fkSourceColumns = alterExpression.getFkSourceColumns();
1✔
3439

3440
        ReferentialAction deleteAction =
1✔
3441
            alterExpression.getReferentialAction(ReferentialAction.Type.DELETE);
1✔
3442
        ReferentialAction updateAction =
1✔
3443
            alterExpression.getReferentialAction(ReferentialAction.Type.UPDATE);
1✔
3444

3445
        Index index = alterExpression.getIndex();
1✔
3446

3447
        List<ConstraintState> constraints = alterExpression.getConstraints();
1✔
3448
        boolean useEqual = alterExpression.getUseEqual();
1✔
3449

3450
        List<String> parameters = alterExpression.getParameters();
1✔
3451

3452
        appendNormalizedLineBreak(builder);
1✔
3453

3454
        for (int j = 0; j <= indent; j++) {
1✔
3455
          builder.append(indentString);
1✔
3456
        }
3457
        if (operation == AlterOperation.RENAME_TABLE) {
1✔
3458
          appendKeyWord(builder, outputFormat, "RENAME TO", "", " ");
1✔
3459
          appendObjectName(builder, outputFormat, alterExpression.getNewTableName(), "", "");
1✔
3460
          break;
1✔
3461
        } else {
3462
          appendKeyWord(builder, outputFormat, operation.name(), "", " ");
1✔
3463
        }
3464

3465
        if (commentText != null) {
1✔
3466
          if (columnName != null) {
×
3467
            appendKeyWord(builder, outputFormat, "COMMENT", " ", " ");
×
3468
          }
3469
          builder.append(commentText);
×
3470
        } else if (columnName != null) {
1✔
3471
          if (alterExpression.hasColumn()) {
1✔
3472
            appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
1✔
3473
          }
3474
          if (operation == AlterOperation.RENAME) {
1✔
3475
            appendObjectName(builder, outputFormat, columnOldName, "", "");
1✔
3476
            appendKeyWord(builder, outputFormat, "TO", " ", " ");
1✔
3477
          }
3478
          appendObjectName(builder, outputFormat, columnName, "", "");
1✔
3479

3480
        } else if (operation == AlterOperation.DROP && !alterExpression.hasColumn()
1✔
3481
            && alterExpression.getPkColumns() != null) {
1✔
3482
          // Oracle supports dropping multiple columns
3483
          // we use the PKColumns List in this case instead of the Column
3484

3485
          List<String> columns = alterExpression.getPkColumns();
1✔
3486

3487
          builder.append("(");
1✔
3488

3489
          int subIndent = getSubIndent(builder, columns.size() > 3);
1✔
3490
          BreakLine bl = columns.size() > 3 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3491

3492
          appendStringList(alterExpression.getPkColumns(), builder, subIndent, true, bl);
1✔
3493
          builder.append(" )");
1✔
3494

3495
        } else if (colDataTypeList != null) {
1✔
3496

3497
          int colWidth = 0;
1✔
3498
          int typeWidth = 0;
1✔
3499

3500
          BreakLine breakLine =
3501
              colDataTypeList.size() > 1 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3502

3503
          if (operation == AlterOperation.CHANGE) {
1✔
3504
            if (optionalSpecifier != null) {
×
3505
              builder.append(optionalSpecifier).append(" ");
×
3506
            }
3507
            appendObjectName(builder, outputFormat, columnOldName, "", " ");
×
3508
          } else if (colDataTypeList.size() > 1) {
1✔
3509

3510
            for (ColumnDefinition columnDefinition : colDataTypeList) {
1✔
3511
              String columnName1 = columnDefinition.getColumnName();
1✔
3512
              // @todo: please get rid of that Replace workaround
3513
              String colDataType = columnDefinition.getColDataType().toString().replace(", ", ",");
1✔
3514

3515
              if (colWidth < columnName1.length()) {
1✔
3516
                colWidth = columnName1.length();
1✔
3517
              }
3518

3519
              if (typeWidth < colDataType.length()) {
1✔
3520
                typeWidth = colDataType.length();
1✔
3521
              }
3522
            }
1✔
3523

3524
            builder.append("( ");
1✔
3525

3526
          } else {
3527
            if (alterExpression.hasColumn()) {
1✔
3528
              appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
1✔
3529
            }
3530
          }
3531

3532
          int subIndent = getSubIndent(builder, colDataTypeList.size() > 1);
1✔
3533
          int typeIndex = subIndent + (colWidth / indentString.length()) + 1;
1✔
3534
          int specIndex = indent + typeIndex + (typeWidth / indentString.length()) + 1;
1✔
3535

3536
          for (ColumnDefinition columnDefinition : colDataTypeList) {
1✔
3537
            if (i > 0 || breakLine.equals(BreakLine.ALWAYS)) {
1✔
3538
              if (!breakLine.equals(BreakLine.NEVER)) {
1✔
3539
                appendNormalizedLineBreak(builder);
1✔
3540
                for (int j = 0; j < subIndent; j++) {
1✔
3541
                  builder.append(indentString);
1✔
3542
                }
3543
              }
3544
              builder.append(", ");
1✔
3545
            }
3546

3547
            String columnName1 = columnDefinition.getColumnName();
1✔
3548
            ColDataType colDataType = columnDefinition.getColDataType();
1✔
3549
            List<String> columnSpecs = columnDefinition.getColumnSpecs();
1✔
3550

3551
            appendObjectName(builder, outputFormat, columnName1, "", " ");
1✔
3552

3553
            int lastLineLength = getLastLineLength(builder);
1✔
3554

3555
            for (int j = lastLineLength; j <= typeIndex * indentWidth; j++) {
1✔
3556
              builder.append(" ");
1✔
3557
            }
3558
            // @todo: please get rid of that Replace workaround
3559
            appendType(builder, outputFormat, colDataType.toString().replace(", ", ","), "", "");
1✔
3560

3561
            lastLineLength = getLastLineLength(builder);
1✔
3562

3563
            if (columnSpecs != null && !columnSpecs.isEmpty()) {
1✔
3564
              if (colDataTypeList.size() > 1) {
1✔
3565
                for (int j = lastLineLength; j <= specIndex * indentWidth; j++) {
1✔
3566
                  builder.append(" ");
1✔
3567
                }
3568
              } else {
3569
                builder.append(" ");
1✔
3570
              }
3571

3572
              appendType(builder, outputFormat,
1✔
3573
                  PlainSelect.getStringList(columnSpecs, false, false), "", "");
1✔
3574
            }
3575
            i++;
1✔
3576
          }
1✔
3577

3578
          if (colDataTypeList.size() > 1) {
1✔
3579
            builder.append(")");
1✔
3580
          }
3581
        } else if (columnDropNotNullList != null) {
1✔
3582
          if (operation == AlterOperation.CHANGE) {
×
3583
            if (optionalSpecifier != null) {
×
3584
              builder.append(optionalSpecifier).append(" ");
×
3585
            }
3586
            appendObjectName(builder, outputFormat, columnOldName, "", " ");
×
3587
          } else if (columnDropNotNullList.size() > 1) {
×
3588
            builder.append("(");
×
3589
          } else {
3590
            if (alterExpression.hasColumn()) {
×
3591
              appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
×
3592
            }
3593
          }
3594
          builder.append(PlainSelect.getStringList(columnDropNotNullList));
×
3595
          if (columnDropNotNullList.size() > 1) {
×
3596
            builder.append(")");
×
3597
          }
3598
        } else if (constraintName != null) {
1✔
3599
          appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
×
3600

3601
          if (constraintIfExists) {
×
3602
            appendKeyWord(builder, outputFormat, "IF EXISTS", "", " ");
×
3603
          }
3604
          appendObjectName(builder, outputFormat, constraintName, "", "");
×
3605
        } else if (pkColumns != null) {
1✔
3606
          appendKeyWord(builder, outputFormat, "PRIMARY KEY", "", " (");
×
3607

3608
          builder.append(PlainSelect.getStringList(pkColumns)).append(")");
×
3609
        } else if (ukColumns != null) {
1✔
3610
          appendKeyWord(builder, outputFormat, "UNIQUE", "", "");
×
3611
          if (ukName != null) {
×
3612
            if (uk) {
×
3613
              appendKeyWord(builder, outputFormat, "KEY", " ", " ");
×
3614
            } else {
3615
              appendKeyWord(builder, outputFormat, "INDEX", " ", " ");
×
3616
            }
3617
            appendObjectName(builder, outputFormat, ukName, "", "");
×
3618
          }
3619
          builder.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")");
×
3620
        } else if (fkColumns != null) {
1✔
3621
          appendKeyWord(builder, outputFormat, "FOREIGN KEY", "", " (");
1✔
3622
          builder.append(PlainSelect.getStringList(fkColumns)).append(")");
1✔
3623

3624
          appendNormalizedLineBreak(builder);
1✔
3625
          for (int j = 0; j <= indent + 1; j++) {
1✔
3626
            builder.append(indentString);
1✔
3627
          }
3628

3629
          appendKeyWord(builder, outputFormat, "REFERENCES", "", " ");
1✔
3630

3631
          builder.append(fkSourceTable).append(" (")
1✔
3632
              .append(PlainSelect.getStringList(fkSourceColumns)).append(")");
1✔
3633
          // referentialActions.forEach(b::append);
3634
          if (updateAction != null) {
1✔
3635
            builder.append(updateAction);
×
3636
          }
3637

3638
          if (deleteAction != null) {
1✔
3639
            builder.append(deleteAction);
×
3640
          }
3641
        } else if (index != null) {
×
3642
          builder.append(index);
×
3643
        }
3644
        if (constraints != null && !constraints.isEmpty()) {
1✔
3645
          builder.append(" ").append(PlainSelect.getStringList(constraints, false, false));
×
3646
        }
3647
        if (useEqual) {
1✔
3648
          builder.append("=");
×
3649
        }
3650
        if (parameters != null && !parameters.isEmpty()) {
1✔
3651
          builder.append(" ").append(PlainSelect.getStringList(parameters, false, false));
×
3652
        }
3653

3654
        switch (separation) {
1✔
3655
          case AFTER:
3656
            appendNormalizingTrailingWhiteSpace(builder,
×
3657
                i < alterExpressions.size() - 1 ? ", " : "");
×
3658
            break;
3659
        }
3660

3661
        i++;
1✔
3662
      }
1✔
3663
    }
3664
  }
1✔
3665

3666
  public enum OutputFormat {
1✔
3667
    PLAIN, ANSI, HTML, RTF, XSLFO
1✔
3668
  }
3669

3670
  public enum Spelling {
1✔
3671
    UPPER, LOWER, CAMEL, KEEP
1✔
3672
  }
3673

3674
  public enum Separation {
1✔
3675
    BEFORE, AFTER
1✔
3676
  }
3677

3678
  public enum BreakLine {
1✔
3679
    NEVER // keep all arguments on one line
1✔
3680
    , AS_NEEDED // only when more than 3 arguments
1✔
3681
    , AFTER_FIRST // break all after the first argument
1✔
3682
    , ALWAYS // break all arguments to a new line
1✔
3683
  }
3684

3685
  public enum SquaredBracketQuotation {
1✔
3686
    AUTO, YES, NO
1✔
3687
  }
3688

3689
  public enum ShowLineNumbers {
1✔
3690
    YES, NO
1✔
3691
  }
3692

3693
  public enum BackSlashQuoting {
1✔
3694
    YES, NO
1✔
3695
  }
3696

3697
  public enum FormattingOption {
1✔
3698
    SQUARE_BRACKET_QUOTATION("squareBracketQuotation"), BACKSLASH_QUOTING(
1✔
3699
        "backSlashQuoting"), OUTPUT_FORMAT("outputFormat"), KEYWORD_SPELLING(
1✔
3700
            "keywordSpelling"), FUNCTION_SPELLING("functionSpelling"), OBJECT_SPELLING(
1✔
3701
                "objectSpelling"), SEPARATION("separation"), INDENT_WIDTH(
1✔
3702
                    "indentWidth"), SHOW_LINE_NUMBERS("showLineNumbers");
1✔
3703

3704
    private final String optionName;
3705

3706
    FormattingOption(String optionName) {
1✔
3707
      this.optionName = optionName;
1✔
3708
    }
1✔
3709

3710
    @Override
3711
    public String toString() {
3712
      return optionName;
1✔
3713
    }
3714

3715
    public void addFormatterOption(String value, ArrayList<String> formatterOptions) {
3716
      formatterOptions.add(optionName + "=" + value);
×
3717
    }
×
3718
  }
3719

3720
}
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