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

manticore-projects / jsqlformatter / #4

31 Mar 2024 05:03AM CUT coverage: 70.428% (+0.2%) from 70.212%
#4

push

github

manticore-projects
style: CheckStyle exception

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

1810 of 2570 relevant lines covered (70.43%)

0.7 hits per line

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

74.68
/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 hu.webarticum.treeprinter.SimpleTreeNode;
24
import hu.webarticum.treeprinter.printer.listing.ListingTreePrinter;
25
import net.sf.jsqlparser.expression.Alias;
26
import net.sf.jsqlparser.expression.AllValue;
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.Parenthesis;
47
import net.sf.jsqlparser.expression.RowConstructor;
48
import net.sf.jsqlparser.expression.SignedExpression;
49
import net.sf.jsqlparser.expression.StringValue;
50
import net.sf.jsqlparser.expression.StructType;
51
import net.sf.jsqlparser.expression.TimeKeyExpression;
52
import net.sf.jsqlparser.expression.WhenClause;
53
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
54
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
55
import net.sf.jsqlparser.expression.operators.relational.Between;
56
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
57
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
58
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
59
import net.sf.jsqlparser.expression.operators.relational.InExpression;
60
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
61
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
62
import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList;
63
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
64
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
65
import net.sf.jsqlparser.schema.Column;
66
import net.sf.jsqlparser.schema.Table;
67
import net.sf.jsqlparser.statement.OutputClause;
68
import net.sf.jsqlparser.statement.ReferentialAction;
69
import net.sf.jsqlparser.statement.Statement;
70
import net.sf.jsqlparser.statement.Statements;
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
import org.apache.commons.cli.CommandLine;
122
import org.apache.commons.cli.CommandLineParser;
123
import org.apache.commons.cli.DefaultParser;
124
import org.apache.commons.cli.HelpFormatter;
125
import org.apache.commons.cli.Option;
126
import org.apache.commons.cli.OptionGroup;
127
import org.apache.commons.cli.Options;
128
import org.apache.commons.cli.ParseException;
129
import org.apache.commons.io.IOUtils;
130
import org.apache.commons.lang3.StringUtils;
131
import org.apache.commons.lang3.reflect.FieldUtils;
132
import org.graalvm.nativeimage.IsolateThread;
133
import org.graalvm.nativeimage.c.function.CEntryPoint;
134
import org.graalvm.nativeimage.c.type.CCharPointer;
135
import org.graalvm.nativeimage.c.type.CTypeConversion;
136
import org.jsoup.Jsoup;
137
import org.jsoup.nodes.Document;
138
import org.jsoup.nodes.Element;
139
import org.jsoup.parser.Parser;
140
import org.jsoup.select.Elements;
141

142
import javax.swing.tree.TreeNode;
143
import java.io.BufferedReader;
144
import java.io.ByteArrayInputStream;
145
import java.io.ByteArrayOutputStream;
146
import java.io.File;
147
import java.io.FileInputStream;
148
import java.io.IOException;
149
import java.io.ObjectInputStream;
150
import java.io.ObjectOutput;
151
import java.io.ObjectOutputStream;
152
import java.io.StringReader;
153
import java.lang.reflect.Field;
154
import java.nio.charset.Charset;
155
import java.nio.file.Path;
156
import java.nio.file.Paths;
157
import java.util.ArrayList;
158
import java.util.Arrays;
159
import java.util.Base64;
160
import java.util.Collection;
161
import java.util.Collections;
162
import java.util.Enumeration;
163
import java.util.List;
164
import java.util.Objects;
165
import java.util.logging.Level;
166
import java.util.logging.Logger;
167
import java.util.regex.Matcher;
168
import java.util.regex.Pattern;
169

170
/**
171
 * A powerful Java SQL Formatter based on the JSQLParser.
172
 *
173
 * @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
174
 * @version 0.1
175
 */
176
@SuppressWarnings({"PMD.CyclomaticComplexity"})
177
public class JSQLFormatter {
×
178

179
  public static final Pattern SQUARED_BRACKET_QUOTATION_PATTERN = Pattern.compile(
1✔
180
      "(((?!\\[\\d+])\\[.*]\\.\\.?)|(\\.\\[\\w+( +\\w+)*])|((?!\\s\\[\\d+])\\s\\[\\w+( +\\w+)*]))");
181
  private static final Logger LOGGER = Logger.getLogger(JSQLFormatter.class.getName());
1✔
182
  private static final AnsiFormat ANSI_FORMAT_LINE_NUMBER =
1✔
183
      new AnsiFormat(Attribute.BRIGHT_BLACK_BACK(), Attribute.DESATURATED());
1✔
184
  private static final AnsiFormat ANSI_FORMAT_KEYWORD =
1✔
185
      new AnsiFormat(Attribute.BLUE_TEXT(), Attribute.BOLD());
1✔
186
  private static final AnsiFormat ANSI_FORMAT_HINT = new AnsiFormat(Attribute.BRIGHT_BLUE_TEXT());
1✔
187
  private static final AnsiFormat ANSI_FORMAT_OPERATOR = new AnsiFormat(Attribute.BLUE_TEXT());
1✔
188
  private static final AnsiFormat ANSI_FORMAT_PARAMETER =
1✔
189
      new AnsiFormat(Attribute.YELLOW_TEXT(), Attribute.DESATURATED());
1✔
190
  private static final AnsiFormat ANSI_FORMAT_ALIAS =
1✔
191
      new AnsiFormat(Attribute.RED_TEXT(), Attribute.BOLD(), Attribute.DESATURATED());
1✔
192
  private static final AnsiFormat ANSI_FORMAT_FUNCTION =
1✔
193
      new AnsiFormat(Attribute.BRIGHT_RED_TEXT());
1✔
194
  private static final AnsiFormat ANSI_FORMAT_TYPE =
1✔
195
      new AnsiFormat(Attribute.YELLOW_TEXT(), Attribute.DESATURATED());
1✔
196
  private static SquaredBracketQuotation squaredBracketQuotation = SquaredBracketQuotation.AUTO;
1✔
197
  private static Separation separation = Separation.BEFORE;
1✔
198
  private static Spelling keywordSpelling = Spelling.UPPER;
1✔
199
  private static Spelling functionSpelling = Spelling.CAMEL;
1✔
200
  private static Spelling objectSpelling = Spelling.LOWER;
1✔
201
  private static OutputFormat outputFormat = OutputFormat.PLAIN;
1✔
202
  private static ShowLineNumbers showLineNumbers = ShowLineNumbers.NO;
1✔
203

204
  private static BackSlashQuoting backSlashQuoting = BackSlashQuoting.NO;
1✔
205
  private static int indentWidth = 4;
1✔
206
  private static String indentString = "    ";
1✔
207
  private static int lineCount = 0;
1✔
208

209
  public static SquaredBracketQuotation getSquaredBracketQuotation() {
210
    return squaredBracketQuotation;
×
211
  }
212

213
  public static void setSquaredBracketQuotation(SquaredBracketQuotation squaredBracketQuotation) {
214
    JSQLFormatter.squaredBracketQuotation = squaredBracketQuotation;
×
215
  }
×
216

217
  public static BackSlashQuoting getBackSlashQuoting() {
218
    return backSlashQuoting;
×
219
  }
220

221
  public static void setBackSlashQuoting(BackSlashQuoting backSlashQuoting) {
222
    JSQLFormatter.backSlashQuoting = backSlashQuoting;
×
223
  }
×
224

225
  public static Separation getSeparation() {
226
    return separation;
×
227
  }
228

229
  public static void setSeparation(Separation separation) {
230
    JSQLFormatter.separation = separation;
×
231
  }
×
232

233
  public static Spelling getKeywordSpelling() {
234
    return keywordSpelling;
×
235
  }
236

237
  public static void setKeywordSpelling(Spelling keywordSpelling) {
238
    JSQLFormatter.keywordSpelling = keywordSpelling;
×
239
  }
×
240

241
  public static Spelling getFunctionSpelling() {
242
    return functionSpelling;
×
243
  }
244

245
  public static void setFunctionSpelling(Spelling functionSpelling) {
246
    JSQLFormatter.functionSpelling = functionSpelling;
×
247
  }
×
248

249
  public static Spelling getObjectSpelling() {
250
    return objectSpelling;
×
251
  }
252

253
  public static void setObjectSpelling(Spelling objectSpelling) {
254
    JSQLFormatter.objectSpelling = objectSpelling;
×
255
  }
×
256

257
  public static OutputFormat getOutputFormat() {
258
    return outputFormat;
×
259
  }
260

261
  public static void setOutputFormat(OutputFormat outputFormat) {
262
    JSQLFormatter.outputFormat = outputFormat;
×
263
  }
×
264

265
  public static int getIndentWidth() {
266
    return indentWidth;
1✔
267
  }
268

269
  public static void setIndentWidth(int indentWidth) {
270
    JSQLFormatter.indentWidth = indentWidth;
×
271

272
    char[] chars = new char[indentWidth];
×
273
    Arrays.fill(chars, ' ');
×
274

275
    JSQLFormatter.indentString = new String(chars);
×
276
  }
×
277

278
  public static String getIndentString() {
279
    return indentString;
×
280
  }
281

282
  public static void setIndentString(String indentString) {
283
    JSQLFormatter.indentString = indentString;
×
284
  }
×
285

286
  private static void appendDecodeExpressionsList(ExpressionList<?> parameters, BreakLine breakLine,
287
      StringBuilder builder, int indent) {
288
    int subIndent = breakLine.equals(BreakLine.NEVER) ? indent : getSubIndent(builder, false);
1✔
289

290
    int i = 0;
1✔
291
    for (Expression expression : parameters) {
1✔
292
      switch (breakLine) {
1✔
293
        case AS_NEEDED:
294
          BreakLine bl = i == 0 || (i - 1) % 2 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
295
          appendExpression(expression, null, builder, subIndent, i, parameters.size(), true, bl);
1✔
296
          break;
1✔
297

298
        default:
299
          appendExpression(expression, null, builder, subIndent, i, parameters.size(), true,
×
300
              breakLine);
301
      }
302
      i++;
1✔
303
    }
1✔
304
  }
1✔
305

306
  static String toCamelCase(String s) {
307
    StringBuilder camelCaseString = new StringBuilder();
1✔
308

309
    String[] nameParts = s.split("_");
1✔
310
    int i = 0;
1✔
311
    for (String part : nameParts) {
1✔
312
      if (i > 0) {
1✔
313
        camelCaseString.append("_");
1✔
314
      }
315
      camelCaseString.append(part.substring(0, 1).toUpperCase())
1✔
316
          .append(part.substring(1).toLowerCase());
1✔
317
      i++;
1✔
318
    }
319
    return camelCaseString.toString();
1✔
320
  }
321

322
  private static StringBuilder appendKeyWord(StringBuilder builder, OutputFormat format,
323
      String keyword, String before, String after) {
324

325
    String s;
326
    switch (keywordSpelling) {
1✔
327
      case UPPER:
328
        s = keyword.toUpperCase();
1✔
329
        break;
1✔
330
      case LOWER:
331
        s = keyword.toLowerCase();
1✔
332
        break;
1✔
333
      case CAMEL:
334
        s = toCamelCase(keyword);
×
335
        break;
×
336
      default:
337
        s = keyword;
×
338
    }
339

340
    switch (format) {
1✔
341
      case ANSI:
342
        builder.append(before).append(ANSI_FORMAT_KEYWORD.format(s)).append(after);
1✔
343
        break;
1✔
344
      case HTML:
345
        builder.append(before).append("<span style=\"color:blue; font-style:bold;\">").append(s)
1✔
346
            .append("</span>").append(after);
1✔
347
        break;
1✔
348
      default:
349
        builder.append(before).append(s).append(after);
1✔
350
        break;
351
    }
352
    return builder;
1✔
353
  }
354

355
  private static StringBuilder appendNormalizingTrailingWhiteSpace(StringBuilder builder,
356
      String s) {
357
    if (builder.length() > 0) {
1✔
358
      int pos = builder.length() - 1;
1✔
359
      char lastChar = builder.charAt(pos);
1✔
360
      if (lastChar == ' ') {
1✔
361
        while (lastChar == ' ' && pos > 0) {
1✔
362
          pos--;
1✔
363
          lastChar = builder.charAt(pos);
1✔
364
        }
365
        builder.setLength(pos + 1);
1✔
366
      }
367
    }
368
    builder.append(s);
1✔
369
    return builder;
1✔
370
  }
371

372
  private static StringBuilder appendNormalizedLineBreak(StringBuilder builder) {
373
    switch (showLineNumbers) {
1✔
374
      case YES:
375
        lineCount++;
1✔
376

377
        String lineCountStr = "0000" + lineCount;
1✔
378
        lineCountStr = lineCountStr.substring(lineCountStr.length() - 5);
1✔
379
        lineCountStr += " | ";
1✔
380

381
        StringBuilder fillerStr = new StringBuilder();
1✔
382
        int adjust = getIndentWidth() - lineCountStr.length() % getIndentWidth();
1✔
383
        for (int i = 0; i < adjust; i++) {
1✔
384
          fillerStr.append(" ");
1✔
385
        }
386

387

388
        switch (outputFormat) {
1✔
389
          case ANSI:
390
            return appendNormalizingTrailingWhiteSpace(builder, Ansi.RESET
1✔
391
                + ANSI_FORMAT_LINE_NUMBER.format("\n" + lineCountStr) + Ansi.RESET + fillerStr);
1✔
392
          case HTML:
393
            return builder.append("\n")
1✔
394
                .append("<span style=\"color:light-grey; font-size:8pt; font-style:normal;\">")
1✔
395
                .append(lineCountStr).append("</span>").append(fillerStr);
1✔
396
          default:
397
            return appendNormalizingTrailingWhiteSpace(builder, "\n" + lineCountStr + fillerStr);
1✔
398
        }
399
      default:
400
        return appendNormalizingTrailingWhiteSpace(builder, "\n");
1✔
401
    }
402
  }
403

404
  private static StringBuilder appendHint(StringBuilder builder, OutputFormat format, String hint,
405
      String before, String after) {
406

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

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

437
  private static StringBuilder appendOperator(StringBuilder builder, OutputFormat format,
438
      String operator, String before, String after) {
439

440
    String s;
441
    switch (keywordSpelling) {
1✔
442
      case UPPER:
443
        s = operator.toUpperCase();
1✔
444
        break;
1✔
445
      case LOWER:
446
        s = operator.toLowerCase();
1✔
447
        break;
1✔
448
      case CAMEL:
449
        s = toCamelCase(operator);
×
450
        break;
×
451
      default:
452
        s = operator;
×
453
    }
454

455
    switch (format) {
1✔
456
      case ANSI:
457
        builder.append(before).append(ANSI_FORMAT_OPERATOR.format(s)).append(after);
1✔
458
        break;
1✔
459
      case HTML:
460
        builder.append(before).append("<span style=\"color:blue; font-style:normal;\">").append(s)
1✔
461
            .append("</span>").append(after);
1✔
462
        break;
1✔
463
      default:
464
        builder.append(before).append(s).append(after);
1✔
465
        break;
466
    }
467
    return builder;
1✔
468
  }
469

470
  private static StringBuilder appendValue(StringBuilder builder, OutputFormat format, String value,
471
      String before, String after) {
472
    switch (format) {
1✔
473
      case ANSI:
474
        builder.append(before).append(ANSI_FORMAT_PARAMETER.format(value)).append(after);
1✔
475
        break;
1✔
476
      case HTML:
477
        builder.append(before).append("<span style=\"color:yellow; font-style:normal;\">")
1✔
478
            .append(value).append("</span>").append(after);
1✔
479
        break;
1✔
480
      default:
481
        builder.append(before).append(value).append(after);
1✔
482
        break;
483
    }
484
    return builder;
1✔
485
  }
486

487
  private static StringBuilder appendAlias(StringBuilder builder, OutputFormat format, String alias,
488
      String before, String after) {
489

490
    String s;
491
    if (alias.trim().startsWith("\"") || alias.trim().startsWith("[")) {
1✔
492
      s = alias;
1✔
493
    } else {
494
      switch (objectSpelling) {
1✔
495
        case UPPER:
496
          s = alias.toUpperCase();
1✔
497
          break;
1✔
498
        case LOWER:
499
          s = alias.toLowerCase();
1✔
500
          break;
1✔
501
        case CAMEL:
502
          s = toCamelCase(alias);
×
503
          break;
×
504
        default:
505
          s = alias;
×
506
      }
507
    }
508

509
    switch (format) {
1✔
510
      case ANSI:
511
        builder.append(before).append(ANSI_FORMAT_ALIAS.format(s)).append(after);
×
512
        break;
×
513
      case HTML:
514
        builder.append(before).append("<span style=\"color:red; font-style:bold;\">").append(s)
×
515
            .append("</span>").append(after);
×
516
        break;
×
517
      default:
518
        builder.append(before).append(s).append(after);
1✔
519
        break;
520
    }
521
    return builder;
1✔
522
  }
523

524
  private static StringBuilder appendObjectName(StringBuilder builder, OutputFormat format,
525
      String objectName, String before, String after) {
526

527
    StringBuilder nameBuilder = new StringBuilder();
1✔
528

529
    int j = 0;
1✔
530
    String[] parts = objectName.contains(".") ? objectName.split("\\.") : new String[] {objectName};
1✔
531
    for (String w : parts) {
1✔
532
      if (j > 0) {
1✔
533
        nameBuilder.append(".");
1✔
534
      }
535
      if (w.trim().startsWith("\"") || w.trim().startsWith("[")) {
1✔
536
        nameBuilder.append(w);
1✔
537
      } else {
538
        switch (objectSpelling) {
1✔
539
          case UPPER:
540
            nameBuilder.append(w.toUpperCase());
1✔
541
            break;
1✔
542
          case LOWER:
543
            nameBuilder.append(w.toLowerCase());
1✔
544
            break;
1✔
545
          case CAMEL:
546
            nameBuilder.append(toCamelCase(w));
×
547
            break;
548
        }
549
      }
550
      j++;
1✔
551
    }
552

553
    switch (format) {
1✔
554
      default:
555
        builder.append(before).append(nameBuilder).append(after);
1✔
556
        break;
557
    }
558
    return builder;
1✔
559
  }
560

561
  private static StringBuilder appendFunction(StringBuilder builder, OutputFormat format,
562
      String function, String before, String after) {
563

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

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

594
  private static StringBuilder appendType(StringBuilder builder, OutputFormat format, String type,
595
      String before, String after) {
596

597
    String s;
598
    switch (keywordSpelling) {
1✔
599
      case UPPER:
600
        s = type.toUpperCase();
1✔
601
        break;
1✔
602
      case LOWER:
603
        s = type.toLowerCase();
1✔
604
        break;
1✔
605
      case CAMEL:
606
        s = toCamelCase(type);
×
607
        break;
×
608
      default:
609
        s = type;
×
610
    }
611

612
    switch (format) {
1✔
613
      case ANSI:
614
        builder.append(before).append(ANSI_FORMAT_TYPE.format(s)).append(after);
×
615
        break;
×
616
      case HTML:
617
        builder.append(before).append("<span style=\"color:yellow; font-style:normal;\">").append(s)
×
618
            .append("</span>").append(after);
×
619
        break;
×
620
      default:
621
        builder.append(before).append(s).append(after);
1✔
622
        break;
623
    }
624
    return builder;
1✔
625
  }
626

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

631
    return lastLine.length();
1✔
632
  }
633

634
  private static int getSubIndent(StringBuilder builder, boolean moveToTab) {
635
    int lastLineLength = getLastLineLength(builder);
1✔
636

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

639
    for (int i = lastLineLength; moveToTab && i < subIndent * indentWidth; i++) {
1✔
640
      builder.append(" ");
1✔
641
    }
642

643
    return subIndent;
1✔
644
  }
645

646
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
647
  private static void appendDelete(StringBuilder builder, Delete delete, int indent) {
648
    List<WithItem> withItems = delete.getWithItemsList();
1✔
649
    if (withItems != null && !withItems.isEmpty()) {
1✔
650
      int i = 0;
1✔
651
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
652

653
      for (WithItem withItem : withItems) {
1✔
654
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
655
        i++;
1✔
656
      }
1✔
657

658
      appendNormalizedLineBreak(builder);
1✔
659
    }
660
    appendKeyWord(builder, outputFormat, "DELETE", "", " ");
1✔
661

662
    OracleHint oracleHint = delete.getOracleHint();
1✔
663
    if (oracleHint != null) {
1✔
664
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
665
    }
666

667
    List<Table> tables = delete.getTables();
1✔
668

669
    if (tables != null && !tables.isEmpty()) {
1✔
670
      int j = 0;
1✔
671
      for (Table table : tables) {
1✔
672
        switch (separation) {
1✔
673
          case AFTER:
674
            appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "",
×
675
                j < tables.size() - 1 ? ", " : " ");
×
676
            break;
×
677
          case BEFORE:
678
          default:
679
            appendObjectName(builder, outputFormat, table.getFullyQualifiedName(),
1✔
680
                j > 0 ? ", " : "", " ");
1✔
681
            break;
682
        }
683

684
        j++;
1✔
685
      }
1✔
686
    }
687

688
    appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
689

690
    Table table = delete.getTable();
1✔
691
    Alias alias = table.getAlias();
1✔
692

693
    appendTable(table, alias, builder);
1✔
694

695
    List<Join> joins = delete.getJoins();
1✔
696
    appendJoins(joins, builder, indent);
1✔
697

698
    Expression whereExpression = delete.getWhere();
1✔
699
    appendWhere(whereExpression, builder, indent);
1✔
700

701
    List<OrderByElement> orderByElements = delete.getOrderByElements();
1✔
702
    appendOrderByElements(orderByElements, builder, indent);
1✔
703

704
    Limit limit = delete.getLimit();
1✔
705
    if (limit != null) {
1✔
706
      appendNormalizedLineBreak(builder);
×
707
      for (int j = 0; j < indent; j++) {
×
708
        builder.append(indentString);
×
709
      }
710

711
      appendKeyWord(builder, outputFormat, "LIMIT", "", "");
×
712

713
      Expression rowCount = limit.getRowCount();
×
714
      if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
×
715
        // no offset allowed
716
        appendKeyWord(builder, outputFormat, "NULL", " ", "");
×
717
      } else {
718
        if (null != limit.getOffset()) {
×
719
          appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false, BreakLine.NEVER);
×
720
          builder.append(", ");
×
721
        }
722
        if (null != limit.getRowCount()) {
×
723
          appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
×
724
              BreakLine.NEVER);
725
        }
726
      }
727
    }
728
  }
1✔
729

730
  public static File getAbsoluteFile(String filename) {
731
    String homePath = new File(System.getProperty("user.home")).toURI().getPath();
×
732

733
    String _filename = filename.replaceFirst("~", Matcher.quoteReplacement(homePath))
×
734
        .replaceFirst("\\$\\{user.home}", Matcher.quoteReplacement(homePath));
×
735

736
    File f = new File(_filename);
×
737
    if (!f.isAbsolute()) {
×
738
      Path basePath = Paths.get("").toAbsolutePath();
×
739

740
      Path resolvedPath = basePath.resolve(filename);
×
741
      Path absolutePath = resolvedPath.normalize();
×
742
      f = absolutePath.toFile();
×
743
    }
744
    return f;
×
745
  }
746

747
  public static String getAbsoluteFileName(String filename) {
748
    return getAbsoluteFile(filename).getAbsolutePath();
×
749
  }
750

751
  /**
752
   * @param args The Command Line Parameters.
753
   */
754
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
755
  public static void main(String[] args) throws Exception {
756
    Options options = new Options();
×
757

758
    options.addOption("i", "inputFile", true, "The input SQL file or folder.");
×
759
    options.addOption("o", "outputFile", true, "The out SQL file for the formatted statements.");
×
760

761
    OptionGroup formatOptions = new OptionGroup();
×
762
    formatOptions.addOption(Option.builder("f").longOpt(FormattingOption.OUTPUT_FORMAT.toString())
×
763
        .hasArg().desc("The output-format.\n[PLAIN* ANSI HTML RTF]").build());
×
764
    formatOptions.addOption(
×
765
        Option.builder(null).longOpt("ansi").desc("Output ANSI annotated text.").build());
×
766
    formatOptions.addOption(
×
767
        Option.builder(null).longOpt("html").desc("Output HTML annotated text.").build());
×
768
    options.addOptionGroup(formatOptions);
×
769

770
    OptionGroup indentOptions = new OptionGroup();
×
771
    indentOptions.addOption(Option.builder("t").longOpt(FormattingOption.INDENT_WIDTH.toString())
×
772
        .hasArg().desc("The indent width.\n[2 4* 8]").build());
×
773
    indentOptions.addOption(Option.builder("2").desc("Indent with 2 characters.").build());
×
774
    indentOptions.addOption(Option.builder("8").desc("Indent with 8 characters.").build());
×
775
    options.addOptionGroup(indentOptions);
×
776

777
    options.addOption(Option.builder(null).longOpt(FormattingOption.KEYWORD_SPELLING.toString())
×
778
        .hasArg().desc("Keyword spelling.\n[UPPER*, LOWER, CAMEL, KEEP]").build());
×
779

780
    options.addOption(Option.builder(null).longOpt(FormattingOption.FUNCTION_SPELLING.toString())
×
781
        .hasArg().desc("Function name spelling.\n[UPPER, LOWER, CAMEL*, KEEP]").build());
×
782

783
    options.addOption(Option.builder(null).longOpt(FormattingOption.OBJECT_SPELLING.toString())
×
784
        .hasArg().desc("Object name spelling.\n[UPPER, LOWER*, CAMEL, KEEP]").build());
×
785

786
    options.addOption(Option.builder(null).longOpt(FormattingOption.SEPARATION.toString()).hasArg()
×
787
        .desc("Position of the field separator.\n[BEFORE*, AFTER]").build());
×
788

789
    options.addOption(Option.builder(null)
×
790
        .longOpt(FormattingOption.SQUARE_BRACKET_QUOTATION.toString()).hasArg()
×
791
        .desc("Interpret Square Brackets as Quotes instead of Arrays.\n[AUTO*, YES, NO]").build());
×
792

793
    options.addOption(Option.builder(null).longOpt(FormattingOption.SHOW_LINE_NUMBERS.toString())
×
794
        .hasArg().desc("Show Line Numbers.\n[YES, NO*]").build());
×
795

796
    options.addOption(Option.builder(null).longOpt(FormattingOption.BACKSLASH_QUOTING.toString())
×
797
        .hasArg().desc("Allow Back Slash '\\' for escaping.\n[YES, NO*]").build());
×
798

799
    // create the parser
800
    CommandLineParser parser = new DefaultParser();
×
801
    try {
802
      // parse the command line arguments
803
      CommandLine line = parser.parse(options, args);
×
804

805
      ArrayList<String> formatterOptions = new ArrayList<>();
×
806

807
      if (line.hasOption("ansi")) {
×
808
        FormattingOption.OUTPUT_FORMAT.addFormatterOption(OutputFormat.ANSI.toString(),
×
809
            formatterOptions);
810
      }
811

812
      if (line.hasOption("html")) {
×
813
        FormattingOption.OUTPUT_FORMAT.addFormatterOption(OutputFormat.HTML.toString(),
×
814
            formatterOptions);
815
      }
816

817
      if (line.hasOption("2")) {
×
818
        FormattingOption.INDENT_WIDTH.addFormatterOption("2", formatterOptions);
×
819
      }
820

821
      if (line.hasOption("8")) {
×
822
        FormattingOption.INDENT_WIDTH.addFormatterOption("4", formatterOptions);
×
823
      }
824

825
      FormattingOption.INDENT_WIDTH.addFormatterOption(line, formatterOptions);
×
826
      FormattingOption.KEYWORD_SPELLING.addFormatterOption(line, formatterOptions);
×
827
      FormattingOption.FUNCTION_SPELLING.addFormatterOption(line, formatterOptions);
×
828
      FormattingOption.OBJECT_SPELLING.addFormatterOption(line, formatterOptions);
×
829
      FormattingOption.SEPARATION.addFormatterOption(line, formatterOptions);
×
830
      FormattingOption.SQUARE_BRACKET_QUOTATION.addFormatterOption(line, formatterOptions);
×
831
      FormattingOption.BACKSLASH_QUOTING.addFormatterOption(line, formatterOptions);
×
832
      FormattingOption.SHOW_LINE_NUMBERS.addFormatterOption(line, formatterOptions);
×
833

834
      if (line.hasOption("help") || line.getOptions().length == 0 && line.getArgs().length == 0) {
×
835
        HelpFormatter formatter = new HelpFormatter();
×
836
        formatter.setOptionComparator(null);
×
837

838
        String startupCommand =
839
            System.getProperty("java.vm.name").equalsIgnoreCase("Substrate VM") ? "./JSQLFormatter"
×
840
                : "java -jar JSQLFormatter.jar";
×
841

842
        formatter.printHelp(startupCommand, options, true);
×
843
        return;
×
844
      }
845

846
      File inputFile = null;
×
847
      if (line.hasOption("inputFile")) {
×
848
        inputFile = getAbsoluteFile(line.getOptionValue("inputFile"));
×
849

850
        if (!inputFile.canRead()) {
×
851
          throw new Exception(
×
852
              "Can't read the specified INPUT-FILE " + inputFile.getCanonicalPath());
×
853
        }
854

855
        try (FileInputStream inputStream = new FileInputStream(inputFile)) {
×
856
          String sqlStr = IOUtils.toString(inputStream, Charset.defaultCharset());
×
857
          System.out.println("\n-- FROM " + inputFile.getName() + "\n"
×
858
              + format(sqlStr, formatterOptions.toArray(new String[formatterOptions.size()])));
×
859
        } catch (Exception ex) {
×
860
          throw new Exception("Error when reading from INPUT FILE " + inputFile.getAbsolutePath(),
×
861
              ex);
862
        }
×
863
      }
864

865
      List<String> argsList = line.getArgList();
×
866
      if (argsList.isEmpty() && !line.hasOption("input-file")) {
×
867
        throw new Exception("No SQL statements provided for formatting.");
×
868
      } else {
869
        for (String s : argsList) {
×
870
          try {
871
            System.out.println("\n-- FROM ARGUMENT LIST\n"
×
872
                + format(s, formatterOptions.toArray(new String[formatterOptions.size()])));
×
873
          } catch (Exception ex) {
×
874
            LOGGER.log(Level.WARNING, "Failed to format statement\n" + s, ex);
×
875
          }
×
876
        }
×
877
      }
878

879
    } catch (ParseException ex) {
×
880
      LOGGER.log(Level.FINE, "Parsing failed.  Reason: " + ex.getMessage(), ex);
×
881

882
      HelpFormatter formatter = new HelpFormatter();
×
883
      formatter.setOptionComparator(null);
×
884
      formatter.printHelp("java -jar H2MigrationTool.jar", options, true);
×
885

886
      throw new Exception("Could not parse the Command Line Arguments.", ex);
×
887
    }
×
888
  }
×
889

890
  /**
891
   * Format a list of SQL Statements.
892
   *
893
   * <p>
894
   * SELECT, INSERT, UPDATE and MERGE statements are supported.
895
   *
896
   * @param thread The
897
   * @param sql The SQL Statements to beautify.
898
   * @param options The Formatting Options (List of "key = value" pairs).
899
   * @return The beautifully formatted SQL Statements, semi-colon separated.
900
   */
901
  @CEntryPoint(name = "format")
902
  public static CCharPointer format(IsolateThread thread, CCharPointer sql, CCharPointer options) {
903
    String sqlStr = CTypeConversion.toJavaString(sql);
×
904

905
    String[] optionStr = CTypeConversion.toJavaString(options).split(",");
×
906
    try {
907
      sqlStr = format(sqlStr, optionStr);
×
908
    } catch (Exception ex) {
×
909
      System.out.println(ex.getMessage());
×
910
    }
×
911

912
    try (CTypeConversion.CCharPointerHolder holder = CTypeConversion.toCString(sqlStr)) {
×
913
      final CCharPointer result = holder.get();
×
914
      return result;
×
915
    }
916
  }
917

918
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
919
  public static ArrayList<Exception> verify(String sqlStr, String... options) {
920
    ArrayList<Exception> exceptions = new ArrayList<>();
×
921

922
    applyFormattingOptions(options);
×
923

924
    Pattern SEMICOLON_PATTERN = Pattern.compile(";|$");
×
925
    Matcher m = SEMICOLON_PATTERN.matcher(sqlStr);
×
926
    ArrayList<Integer> semicolons = new ArrayList<>();
×
927

928
    while (m.find()) {
×
929
      semicolons.add(m.start());
×
930
    }
931

932
    m = CommentMap.COMMENT_PATTERN.matcher(sqlStr);
×
933
    while (m.find()) {
×
934
      int start = m.start();
×
935
      int end = m.end();
×
936

937
      int n = semicolons.size();
×
938
      for (int i = n - 1; i >= 0; i--) {
×
939
        int pos = semicolons.get(i);
×
940
        if (start <= pos && pos < end) {
×
941
          semicolons.remove(i);
×
942
        }
943
      }
944
    }
×
945

946
    int pos = 0;
×
947
    int length = sqlStr.length();
×
948
    int n = semicolons.size();
×
949
    for (int i = 0; i < n; i++) {
×
950
      int semicolonPos = semicolons.get(i);
×
951

952
      if (semicolonPos > pos) {
×
953
        String statementSql = sqlStr.substring(pos, Integer.min(semicolonPos + 1, length));
×
954
        pos = semicolonPos + 1;
×
955

956
        // we are at the end and find only remaining whitespace
957
        if (statementSql.trim().isEmpty()) {
×
958
          break;
×
959
        }
960

961
        boolean useSquareBracketQuotation;
962
        switch (squaredBracketQuotation) {
×
963
          case YES:
964
            useSquareBracketQuotation = true;
×
965
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
966
                useSquareBracketQuotation);
×
967
            break;
×
968
          case NO:
969
            useSquareBracketQuotation = false;
×
970
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
971
                useSquareBracketQuotation);
×
972
            break;
×
973
          case AUTO:
974
          default:
975
            useSquareBracketQuotation =
×
976
                SQUARED_BRACKET_QUOTATION_PATTERN.matcher(statementSql).find();
×
977
            LOGGER.log(Level.FINE, "Square Bracket Quotation auto-detected as {0}.",
×
978
                useSquareBracketQuotation);
×
979
        }
980
        try {
981
          CCJSqlParserUtil.parse(statementSql,
×
982
              parser -> parser.withSquareBracketQuotation(useSquareBracketQuotation));
×
983

984
        } catch (Exception ex1) {
×
985
          exceptions.add(new Exception("Cannot parse the Statement:\n" + statementSql, ex1));
×
986
        }
×
987
      } else {
988
        break;
989
      }
990
    }
991
    return exceptions;
×
992
  }
993

994
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
995
  public static String format(String sqlStr, String... options) throws Exception {
996
    applyFormattingOptions(options);
1✔
997

998
    StringBuilder builder = new StringBuilder();
1✔
999

1000
    int indent = 0;
1✔
1001
    lineCount = 0;
1✔
1002

1003
    // Pattern SEMICOLON_PATTERN =
1004
    // Pattern.compile("((?:(?:'[^']*+')|(?:\"[^\"]*+\")|(?:--.*)|(?:\\/\\*[^\\*\\/]*+\\*\\/)|[^;])*+);");
1005
    Pattern SEMICOLON_PATTERN = Pattern.compile(";|$|\\n\\n\\n");
1✔
1006
    Matcher m = SEMICOLON_PATTERN.matcher(sqlStr);
1✔
1007
    ArrayList<Integer> semicolons = new ArrayList<>();
1✔
1008

1009
    while (m.find()) {
1✔
1010
      semicolons.add(m.start());
1✔
1011
    }
1012

1013
    m = CommentMap.COMMENT_PATTERN.matcher(sqlStr);
1✔
1014
    while (m.find()) {
1✔
1015
      int start = m.start();
1✔
1016
      int end = m.end();
1✔
1017

1018
      int n = semicolons.size();
1✔
1019
      for (int i = n - 1; i >= 0; i--) {
1✔
1020
        int pos = semicolons.get(i);
1✔
1021
        if (start <= pos && pos < end) {
1✔
1022
          semicolons.remove(i);
1✔
1023
        }
1024
      }
1025
    }
1✔
1026

1027
    int pos = 0;
1✔
1028
    int length = sqlStr.length();
1✔
1029
    int n = semicolons.size();
1✔
1030
    for (int i = 0; i < n; i++) {
1✔
1031
      int semicolonPos = semicolons.get(i);
1✔
1032

1033
      if (semicolonPos > pos) {
1✔
1034
        String statementSql = sqlStr.substring(pos, Integer.min(semicolonPos + 1, length));
1✔
1035
        pos = semicolonPos + 1;
1✔
1036

1037
        // we are at the end and find only remaining whitespace
1038
        // or comments
1039
        if (statementSql.trim().isEmpty() || i == n - 1 && statementSql.trim().startsWith("--")
1✔
1040
            || i == n - 1 && statementSql.trim().startsWith("/*")) {
1✔
1041
          break;
×
1042
        }
1043

1044
        StringBuilder statementBuilder = new StringBuilder();
1✔
1045

1046
        boolean useSquareBracketQuotation;
1047
        switch (squaredBracketQuotation) {
1✔
1048
          case YES:
1049
            useSquareBracketQuotation = true;
×
1050
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
1051
                useSquareBracketQuotation);
×
1052
            break;
×
1053
          case NO:
1054
            useSquareBracketQuotation = false;
×
1055
            LOGGER.log(Level.FINE, "Square Bracket Quotation set as {0}.",
×
1056
                useSquareBracketQuotation);
×
1057
            break;
×
1058
          case AUTO:
1059
          default:
1060
            useSquareBracketQuotation =
1✔
1061
                SQUARED_BRACKET_QUOTATION_PATTERN.matcher(statementSql).find();
1✔
1062
            LOGGER.log(Level.FINE, "Square Bracket Quotation auto-detected as {0}.",
1✔
1063
                useSquareBracketQuotation);
1✔
1064
        }
1065

1066
        boolean useBackSlashQuoting;
1067
        if (Objects.requireNonNull(backSlashQuoting) == BackSlashQuoting.YES) {
1✔
1068
          useBackSlashQuoting = true;
×
1069
          LOGGER.log(Level.FINE, "Back Slash Quoting set as {0}.", true);
×
1070
        } else {
1071
          useBackSlashQuoting = false;
1✔
1072
          LOGGER.log(Level.FINE, "Back Slash Quoting set as {0}.", false);
1✔
1073
        }
1074

1075
        CommentMap commentMap = new CommentMap(statementSql);
1✔
1076

1077
        Pattern DIRECTIVE_PATTERN = Pattern.compile("@JSQLFormatter\\s?\\((.*)\\)");
1✔
1078
        for (Comment comment : commentMap.values()) {
1✔
1079
          Matcher m1 = DIRECTIVE_PATTERN.matcher(comment.text);
1✔
1080
          if (m1.find()) {
1✔
1081
            String[] keyValuePairs = m1.group(1).split(",");
1✔
1082
            applyFormattingOptions(keyValuePairs);
1✔
1083
          }
1084
        }
1✔
1085

1086
        try {
1087
          Statement statement = CCJSqlParserUtil.parse(statementSql.replaceAll("\\n\\n+", "\n"),
1✔
1088
              parser -> parser.withSquareBracketQuotation(useSquareBracketQuotation)
1✔
1089
                  .withBackslashEscapeCharacter(useBackSlashQuoting).withTimeOut(60000));
1✔
1090

1091
          if (statement instanceof Select) {
1✔
1092
            Select select = (Select) statement;
1✔
1093
            appendSelect(select, statementBuilder, indent, true, false);
1✔
1094

1095
          } else if (statement instanceof Update) {
1✔
1096
            Update update = (Update) statement;
1✔
1097
            appendUpdate(statementBuilder, update, indent);
1✔
1098

1099
          } else if (statement instanceof Insert) {
1✔
1100
            Insert insert = (Insert) statement;
1✔
1101
            appendInsert(statementBuilder, insert, indent);
1✔
1102

1103
          } else if (statement instanceof Merge) {
1✔
1104
            Merge merge = (Merge) statement;
1✔
1105
            appendMerge(statementBuilder, merge, indent);
1✔
1106

1107
          } else if (statement instanceof Delete) {
1✔
1108
            Delete delete = (Delete) statement;
1✔
1109
            appendDelete(statementBuilder, delete, indent);
1✔
1110

1111
          } else if (statement instanceof Truncate) {
1✔
1112
            Truncate truncate = (Truncate) statement;
1✔
1113
            appendTruncate(statementBuilder, truncate);
1✔
1114

1115
          } else if (statement instanceof CreateTable) {
1✔
1116
            CreateTable createTable = (CreateTable) statement;
1✔
1117
            appendCreateTable(statementBuilder, createTable, indent);
1✔
1118

1119
          } else if (statement instanceof CreateIndex) {
1✔
1120
            CreateIndex createIndex = (CreateIndex) statement;
1✔
1121
            appendCreateIndex(statementBuilder, createIndex, indent);
1✔
1122

1123
          } else if (statement instanceof CreateView) {
1✔
1124
            CreateView createView = (CreateView) statement;
1✔
1125
            appendCreateView(statementBuilder, createView, indent);
1✔
1126

1127
          } else if (statement instanceof Alter) {
1✔
1128
            Alter alter = (Alter) statement;
1✔
1129
            appendAlter(statementBuilder, alter, indent);
1✔
1130

1131
          } else if (statement != null) {
1✔
1132
            try {
1133
              statementBuilder.append("\n").append(statement);
1✔
1134
            } catch (Exception ex) {
×
1135
              throw new UnsupportedOperationException(
×
1136
                  "The " + statement.getClass().getName() + " Statement is not supported yet.");
×
1137
            }
1✔
1138
          }
1139
          appendNormalizedLineBreak(statementBuilder).append(";\n");
1✔
1140

1141
          builder.append(commentMap.isEmpty() ? statementBuilder
1✔
1142
              : commentMap.insertComments(statementBuilder, outputFormat));
1✔
1143

1144
        } catch (Exception ex1) {
×
1145
          if (statementSql.trim().length() <= commentMap.getLength()) {
×
1146
            LOGGER.info("Found only comments, but no SQL code.");
×
1147
            builder.append(statementSql);
×
1148
          } else {
1149

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

1152
            builder.append("-- failed to format start\n").append(statementSql)
×
1153
                .append("\n-- failed to format end\n").append("\n");
×
1154
          }
1155
        }
1✔
1156
      } else {
1157
        break;
1158
      }
1159
    }
1160

1161
    if (outputFormat == OutputFormat.HTML) {
1✔
1162
      builder = new StringBuilder().append("<html>\n").append("<head>\n")
1✔
1163
          .append("<title>SQL Statement's Java Object Tree</title>\n").append("</head>\n")
1✔
1164
          .append("<body>\n").append("<pre style=\"font-size:-2;background-color:#EFEFEF;\">\n")
1✔
1165
          .append(builder).append("\n</pre>\n").append("</body>\n").append("</html>");
1✔
1166
    }
1167

1168
    return builder.toString().trim();
1✔
1169
  }
1170

1171
  public static StringBuilder formatToJava(String sqlStr, int indent, String... options)
1172
      throws Exception {
1173
    String formatted = format(sqlStr, options);
×
1174
    StringReader stringReader = new StringReader(formatted);
×
1175
    BufferedReader bufferedReader = new BufferedReader(stringReader);
×
1176
    String line;
1177
    StringBuilder builder = new StringBuilder();
×
1178
    int i = 0;
×
1179
    while ((line = bufferedReader.readLine()) != null) {
×
1180
      if (i > 0) {
×
1181
        for (int j = 0; j < indent - 2; j++) {
×
1182
          builder.append(" ");
×
1183
        }
1184
        builder.append("+ ");
×
1185
      } else {
1186
        for (int j = 0; j < indent; j++) {
×
1187
          builder.append(" ");
×
1188
        }
1189
      }
1190
      builder.append("\"").append(line).append("\"\n");
×
1191
      i++;
×
1192
    }
1193
    return builder;
×
1194
  }
1195

1196
  public static ArrayList<JavaObjectNode> getAstNodes(String sqlStr, String... options)
1197
      throws Exception {
1198
    ArrayList<JavaObjectNode> nodes = new ArrayList<>();
1✔
1199

1200
    Statements statements = CCJSqlParserUtil.parseStatements(sqlStr);
1✔
1201
    for (Statement statement : statements) {
1✔
1202
      JavaObjectNode node = new JavaObjectNode(null, "Statements", statement);
1✔
1203
      nodes.add(node);
1✔
1204
    }
1✔
1205
    return nodes;
1✔
1206
  }
1207

1208
  public static SimpleTreeNode translateNode(TreeNode node) {
1209
    SimpleTreeNode simpleTreeNode = new SimpleTreeNode(node.toString());
1✔
1210
    Enumeration<? extends TreeNode> children = node.children();
1✔
1211
    while (children.hasMoreElements()) {
1✔
1212
      simpleTreeNode.addChild(translateNode(children.nextElement()));
1✔
1213
    }
1214

1215
    return simpleTreeNode;
1✔
1216
  }
1217

1218
  public static String encodeObject(Object object) throws IOException {
1219
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
1✔
1220
    ObjectOutput objectOutput = new ObjectOutputStream(byteArrayOutputStream);
1✔
1221
    objectOutput.writeObject(object);
1✔
1222
    objectOutput.flush();
1✔
1223
    objectOutput.close();
1✔
1224
    byteArrayOutputStream.flush();
1✔
1225

1226
    return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
1✔
1227
  }
1228

1229
  public static String formatToTree(String sqlStr, String... options) throws Exception {
1230
    applyFormattingOptions(options);
1✔
1231

1232
    // The Java TreeNode Structure
1233
    JSQLFormatter.JavaObjectNode[] nodes =
1✔
1234
        JSQLFormatter.getAstNodes(sqlStr).toArray(new JSQLFormatter.JavaObjectNode[0]);
1✔
1235

1236
    SimpleTreeNode rootNode = new SimpleTreeNode("SQL Text");
1✔
1237
    for (JSQLFormatter.JavaObjectNode node : nodes) {
1✔
1238
      rootNode.addChild(translateNode(node));
1✔
1239
    }
1240

1241
    return new ListingTreePrinter().stringify(rootNode);
1✔
1242
  }
1243

1244
  private static StringBuilder appendToXML(StringBuilder builder, JavaObjectNode node, int indent)
1245
      throws IOException {
1246

1247
    if (node.isLeaf()) {
1✔
1248
      builder.append(StringUtils.leftPad("", indent * 4)).append("<")
1✔
1249
          .append(node.object.getClass().getSimpleName()).append(" type='")
1✔
1250
          .append(node.object.getClass().getSimpleName()).append("'").append(" class='")
1✔
1251
          .append(node.object.getClass().getName()).append("'").append(" object='")
1✔
1252
          .append(encodeObject(node.object)).append("'").append(">").append(node.object)
1✔
1253
          .append("</").append(node.object.getClass().getSimpleName()).append(">\n");
1✔
1254
      // } else if (node.object instanceof net.sf.jsqlparser.schema.Column
1255
      // || node.object instanceof net.sf.jsqlparser.schema.Table
1256
      // || node.object instanceof net.sf.jsqlparser.schema.Database
1257
      // || node.object instanceof net.sf.jsqlparser.schema.Sequence
1258
      // || node.object instanceof net.sf.jsqlparser.schema.Server
1259
      // || node.object instanceof net.sf.jsqlparser.schema.Synonym) {
1260
      // return formatClassName(object);
1261
      // } else if (node.object instanceof Collection) {
1262
      // return formatCollection((Collection) object);
1263
    } else {
1264
      builder.append(StringUtils.leftPad("", indent * 4)).append("<").append(node.fieldName)
1✔
1265
          .append(" type='").append(node.object.getClass().getSimpleName()).append("'")
1✔
1266
          .append(" class='").append(node.object.getClass().getName()).append("'")
1✔
1267
          .append(" object='").append(encodeObject(node.object)).append("'").append(">\n");
1✔
1268

1269
      Enumeration<? extends TreeNode> children = node.children();
1✔
1270
      while (children.hasMoreElements()) {
1✔
1271
        appendToXML(builder, (JavaObjectNode) children.nextElement(), indent + 1);
1✔
1272
      }
1273

1274
      builder.append(StringUtils.leftPad("", indent * 4)).append("</").append(node.fieldName)
1✔
1275
          .append(">\n");
1✔
1276

1277
    }
1278
    return builder;
1✔
1279
  }
1280

1281
  public static String formatToXML(String sqlStr, String... options) throws Exception {
1282
    applyFormattingOptions(options);
1✔
1283

1284
    StringBuilder builder = new StringBuilder();
1✔
1285
    JSQLFormatter.JavaObjectNode[] nodes =
1✔
1286
        JSQLFormatter.getAstNodes(sqlStr).toArray(new JSQLFormatter.JavaObjectNode[0]);
1✔
1287

1288
    for (JSQLFormatter.JavaObjectNode node : nodes) {
1✔
1289
      appendToXML(builder, node, 0);
1✔
1290
    }
1291

1292
    return builder.toString();
1✔
1293
  }
1294

1295
  public static <T> Collection<T> extract(String sql, Class<T> clazz, String xpath)
1296
      throws Exception {
1297
    ArrayList<T> objects = new ArrayList<>();
1✔
1298

1299
    String xmlStr = formatToXML(sql);
1✔
1300
    Document doc = Jsoup.parse(xmlStr, "", Parser.xmlParser());
1✔
1301
    Elements elements = doc.selectXpath(xpath);
1✔
1302
    for (Element element : elements) {
1✔
1303
      String className = element.attr("class");
1✔
1304
      String attrStr = element.attr("object");
1✔
1305

1306
      if (clazz.getName().equals(className)) {
1✔
1307
        byte[] bytes = Base64.getDecoder().decode(attrStr);
1✔
1308
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
1✔
1309
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
1✔
1310
        Object o = objectInputStream.readObject();
1✔
1311
        objectInputStream.close();
1✔
1312

1313
        try {
1314
          objects.add((T) o);
1✔
1315
        } catch (Exception ex) {
×
1316
          // @ todo: this should be ignored as we test for equal class names already
1317
          LOGGER.log(Level.WARNING,
×
1318
              "Failed to translate a " + o.getClass().getName() + " into a " + clazz.getName());
×
1319
        }
1✔
1320
      }
1321
    }
1✔
1322
    return objects;
1✔
1323
  }
1324

1325
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1326
  public static void applyFormattingOptions(String[] options) {
1327
    // set the formatting options
1328
    if (options != null) {
1✔
1329
      for (String s : options) {
1✔
1330
        String[] o = s.split("=");
1✔
1331
        if (o.length == 2) {
1✔
1332
          LOGGER.log(Level.FINE, "Found Formatting Option {0} = {1}", o);
1✔
1333

1334
          String key = o[0].trim();
1✔
1335
          String value = o[1].trim();
1✔
1336

1337
          if (key.equalsIgnoreCase(FormattingOption.OUTPUT_FORMAT.toString())) {
1✔
1338
            try {
1339
              outputFormat = OutputFormat.valueOf(value.toUpperCase());
1✔
1340
            } catch (Exception ex) {
×
1341
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1342
            }
1✔
1343

1344
          } else if (key.equalsIgnoreCase(FormattingOption.KEYWORD_SPELLING.toString())) {
1✔
1345
            try {
1346
              keywordSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1347
            } catch (Exception ex) {
×
1348
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1349
            }
1✔
1350

1351
          } else if (key.equalsIgnoreCase(FormattingOption.FUNCTION_SPELLING.toString())) {
1✔
1352
            try {
1353
              functionSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1354
            } catch (Exception ex) {
×
1355
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1356
            }
1✔
1357

1358
          } else if (key.equalsIgnoreCase(FormattingOption.OBJECT_SPELLING.toString())) {
1✔
1359
            try {
1360
              objectSpelling = Spelling.valueOf(value.toUpperCase());
1✔
1361
            } catch (Exception ex) {
×
1362
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1363
            }
1✔
1364

1365
          } else if (key.equalsIgnoreCase(FormattingOption.SEPARATION.toString())) {
1✔
1366
            try {
1367
              separation = Separation.valueOf(value.toUpperCase());
1✔
1368
            } catch (Exception ex) {
×
1369
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1370
            }
1✔
1371

1372
          } else if (key.equalsIgnoreCase(FormattingOption.SQUARE_BRACKET_QUOTATION.toString())) {
1✔
1373
            try {
1374
              squaredBracketQuotation = SquaredBracketQuotation.valueOf(value.toUpperCase());
×
1375
            } catch (Exception ex) {
×
1376
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1377
            }
×
1378

1379
          } else if (key.equalsIgnoreCase(FormattingOption.BACKSLASH_QUOTING.toString())) {
1✔
1380
            try {
1381
              backSlashQuoting = BackSlashQuoting.valueOf(value.toUpperCase());
×
1382
            } catch (Exception ex) {
×
1383
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1384
            }
×
1385

1386
          } else if (key.equalsIgnoreCase(FormattingOption.SHOW_LINE_NUMBERS.toString())) {
1✔
1387
            try {
1388
              showLineNumbers = ShowLineNumbers.valueOf(value.toUpperCase());
1✔
1389
            } catch (Exception ex) {
×
1390
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1391
            }
1✔
1392

1393
          } else if (key.equalsIgnoreCase(FormattingOption.INDENT_WIDTH.toString())) {
1✔
1394
            try {
1395
              indentWidth = Integer.parseInt(value);
1✔
1396

1397
              char[] chars = new char[indentWidth];
1✔
1398
              Arrays.fill(chars, ' ');
1✔
1399

1400
              indentString = new String(chars);
1✔
1401
            } catch (Exception ex) {
×
1402
              LOGGER.log(Level.WARNING, "Formatting Option {0} does not support {1} ", o);
×
1403
            }
1✔
1404
          } else {
1405
            LOGGER.log(Level.WARNING, "Unknown Formatting Option {0} = {1} ", o);
×
1406
          }
1407

1408
        } else {
1✔
1409
          LOGGER.log(Level.WARNING, "Invalid Formatting Option {0}", s);
×
1410
        }
1411
      }
1412
    }
1413
  }
1✔
1414

1415
  private static void appendMerge(StringBuilder builder, Merge merge, int indent) {
1416

1417
    List<WithItem> withItems = merge.getWithItemsList();
1✔
1418
    if (withItems != null && !withItems.isEmpty()) {
1✔
1419
      int i = 0;
1✔
1420
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1421

1422
      for (WithItem withItem : withItems) {
1✔
1423
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1424
        i++;
1✔
1425
      }
1✔
1426

1427
      appendNormalizedLineBreak(builder);
1✔
1428
    }
1429

1430
    appendKeyWord(builder, outputFormat, "MERGE", "", " ");
1✔
1431
    OracleHint oracleHint = merge.getOracleHint();
1✔
1432
    if (oracleHint != null) {
1✔
1433
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
1434
    }
1435

1436
    appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1437

1438
    Table table = merge.getTable();
1✔
1439
    Alias alias = table.getAlias();
1✔
1440

1441
    appendTable(table, alias, builder);
1✔
1442

1443
    appendNormalizedLineBreak(builder);
1✔
1444
    for (int j = 0; j < indent + 1; j++) {
1✔
1445
      builder.append(indentString);
1✔
1446
    }
1447
    appendKeyWord(builder, outputFormat, "USING", "", " ");
1✔
1448

1449
    FromItem fromItem = merge.getFromItem();
1✔
1450
    appendFromItem(fromItem, builder, indent, 0, 1);
1✔
1451

1452
    Expression onExpression = merge.getOnCondition();
1✔
1453
    if (onExpression != null) {
1✔
1454
      appendNormalizedLineBreak(builder);
1✔
1455
      for (int j = 0; j < indent + 2; j++) {
1✔
1456
        builder.append(indentString);
1✔
1457
      }
1458
      appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
1459
      appendExpression(onExpression, null, builder, indent, 0, 1, false, BreakLine.AS_NEEDED);
1✔
1460

1461
      appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1462
    }
1463

1464
    MergeInsert insert = merge.getMergeInsert();
1✔
1465
    MergeUpdate update = merge.getMergeUpdate();
1✔
1466
    if (merge.isInsertFirst()) {
1✔
1467
      appendMergeInsert(insert, builder, indent, 0);
1✔
1468
      appendMergeUpdate(update, builder, indent);
1✔
1469
    } else {
1470
      appendMergeUpdate(update, builder, indent);
1✔
1471
      appendMergeInsert(insert, builder, indent, 0);
1✔
1472
    }
1473

1474
    appendOutputClaus(merge.getOutputClause(), builder, indent);
1✔
1475
  }
1✔
1476

1477
  private static void appendOutputClaus(OutputClause outputClause, StringBuilder builder,
1478
      int indent) {
1479
    if (outputClause != null) {
1✔
1480
      appendNormalizedLineBreak(builder);
1✔
1481
      for (int j = 0; j < indent; j++) {
1✔
1482
        builder.append(indentString);
×
1483
      }
1484
      appendKeyWord(builder, outputFormat, "OUTPUT", "", " ");
1✔
1485
      int i = 0;
1✔
1486
      int subIndent = getSubIndent(builder, outputClause.getSelectItemList().size() > 3);
1✔
1487
      appendSelectItemList(outputClause.getSelectItemList(), builder, subIndent, i,
1✔
1488
          BreakLine.AS_NEEDED, indent);
1489

1490
      appendNormalizedLineBreak(builder);
1✔
1491
      for (int j = 0; j < indent + 1; j++) {
1✔
1492
        builder.append(indentString);
1✔
1493
      }
1494
      appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1495

1496
      if (outputClause.getOutputTable() != null) {
1✔
1497
        Table table = outputClause.getOutputTable();
1✔
1498
        appendTable(table, table.getAlias(), builder);
1✔
1499

1500
        appendStringList(outputClause.getColumnList(), builder, indent + 1, true,
1✔
1501
            BreakLine.AS_NEEDED);
1502
      } else if (outputClause.getTableVariable() != null) {
1✔
1503
        appendObjectName(builder, outputFormat, outputClause.getTableVariable().toString(), " ",
×
1504
            "");
1505
      }
1506
    }
1507
  }
1✔
1508

1509
  public static void appendMergeUpdate(MergeUpdate update, StringBuilder builder, int indent) {
1510
    if (update != null) {
1✔
1511
      appendNormalizedLineBreak(builder);
1✔
1512

1513
      for (int j = 0; j < indent; j++) {
1✔
1514
        builder.append(indentString);
×
1515
      }
1516
      appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
1517
      appendKeyWord(builder, outputFormat, "MATCHED", "", " ");
1✔
1518
      appendKeyWord(builder, outputFormat, "THEN", "", "\n");
1✔
1519

1520
      for (int j = 0; j < indent + 1; j++) {
1✔
1521
        builder.append(indentString);
1✔
1522
      }
1523
      appendKeyWord(builder, outputFormat, "UPDATE", "", " ");
1✔
1524
      appendKeyWord(builder, outputFormat, "SET", "", " ");
1✔
1525

1526
      int subIndent = getSubIndent(builder, true);
1✔
1527

1528
      appendUpdateSets(builder, update.getUpdateSets(), subIndent);
1✔
1529

1530
      Expression whereCondition = update.getWhereCondition();
1✔
1531
      if (whereCondition != null) {
1✔
1532
        appendNormalizedLineBreak(builder);
1✔
1533
        for (int j = 0; j < indent + 1; j++) {
1✔
1534
          builder.append(indentString);
1✔
1535
        }
1536
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1537

1538
        subIndent = getSubIndent(builder, true);
1✔
1539

1540
        appendExpression(whereCondition, null, builder, subIndent, 0, 1, false,
1✔
1541
            BreakLine.AFTER_FIRST);
1542
      }
1543

1544
      Expression deleteWhereCondition = update.getDeleteWhereCondition();
1✔
1545
      if (deleteWhereCondition != null) {
1✔
1546
        appendNormalizedLineBreak(builder);
1✔
1547
        for (int j = 0; j < indent + 1; j++) {
1✔
1548
          builder.append(indentString);
1✔
1549
        }
1550
        appendKeyWord(builder, outputFormat, "DELETE", "", " ");
1✔
1551
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1552

1553
        subIndent = getSubIndent(builder, true);
1✔
1554

1555
        appendExpression(deleteWhereCondition, null, builder, subIndent, 0, 1, false,
1✔
1556
            BreakLine.AFTER_FIRST);
1557
      }
1558
    }
1559
  }
1✔
1560

1561
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
1562
  public static void appendMergeInsert(MergeInsert insert, StringBuilder builder, int indent,
1563
      int i) {
1564
    if (insert != null) {
1✔
1565
      appendNormalizedLineBreak(builder);
1✔
1566
      for (int j = 0; j < indent; j++) {
1✔
1567
        builder.append(indentString);
×
1568
      }
1569
      appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
1570
      appendKeyWord(builder, outputFormat, "NOT", "", " ");
1✔
1571
      appendKeyWord(builder, outputFormat, "MATCHED", "", " ");
1✔
1572
      appendKeyWord(builder, outputFormat, "THEN", "", "\n");
1✔
1573

1574
      for (int j = 0; j < indent + 1; j++) {
1✔
1575
        builder.append(indentString);
1✔
1576
      }
1577
      appendKeyWord(builder, outputFormat, "INSERT", "", " ");
1✔
1578

1579
      List<Column> columns = insert.getColumns();
1✔
1580
      List<Expression> expressions = insert.getValues();
1✔
1581

1582
      int k = i;
1✔
1583
      if (columns != null && !columns.isEmpty()) {
1✔
1584
        builder.append("( ");
1✔
1585
        int subIndent = getSubIndent(builder, false);
1✔
1586

1587
        for (Column column : columns) {
1✔
1588
          appendExpression(column, null, builder, subIndent, k++, columns.size(), true,
1✔
1589
              BreakLine.AFTER_FIRST);
1590
        }
1✔
1591
        appendNormalizingTrailingWhiteSpace(builder, " )\n");
1✔
1592
      }
1593

1594
      if (columns != null && !columns.isEmpty()) {
1✔
1595
        for (int j = 0; j < indent + 1; j++) {
1✔
1596
          builder.append(indentString);
1✔
1597
        }
1598
      }
1599
      appendKeyWord(builder, outputFormat, "VALUES", "", " ( ");
1✔
1600

1601
      int subIndent = getSubIndent(builder, false);
1✔
1602
      if (expressions != null) {
1✔
1603
        int j = 0;
1✔
1604
        for (Expression expression : expressions) {
1✔
1605
          appendExpression(expression, null, builder, subIndent, j++, expressions.size(), true,
1✔
1606
              BreakLine.AFTER_FIRST);
1607
        }
1✔
1608
      }
1609
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
1610

1611
      Expression whereCondition = insert.getWhereCondition();
1✔
1612
      if (whereCondition != null) {
1✔
1613
        appendNormalizedLineBreak(builder);
1✔
1614
        for (int j = 0; j < indent + 1; j++) {
1✔
1615
          builder.append(indentString);
1✔
1616
        }
1617
        appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
1618

1619
        subIndent = getSubIndent(builder, true);
1✔
1620

1621
        appendExpression(whereCondition, null, builder, subIndent, 0, 1, false,
1✔
1622
            BreakLine.AFTER_FIRST);
1623
      }
1624
    }
1625
  }
1✔
1626

1627
  private static void appendInsert(StringBuilder builder, Insert insert, int indent) {
1628
    List<WithItem> withItems = insert.getWithItemsList();
1✔
1629
    if (withItems != null && !withItems.isEmpty()) {
1✔
1630
      int i = 0;
1✔
1631
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1632

1633
      for (WithItem withItem : withItems) {
1✔
1634
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1635
        i++;
1✔
1636
      }
1✔
1637

1638
      appendNormalizedLineBreak(builder);
1✔
1639
    }
1640

1641
    appendKeyWord(builder, outputFormat, "INSERT", "", " ");
1✔
1642
    OracleHint oracleHint = insert.getOracleHint();
1✔
1643
    if (oracleHint != null) {
1✔
1644
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
×
1645
    }
1646
    appendKeyWord(builder, outputFormat, "INTO", "", " ");
1✔
1647

1648
    Table table = insert.getTable();
1✔
1649
    Alias alias = table.getAlias();
1✔
1650

1651
    appendTable(table, alias, builder);
1✔
1652

1653
    List<Column> columns = insert.getColumns();
1✔
1654
    if (columns != null) {
1✔
1655
      int i = 0;
1✔
1656
      builder.append(" (");
1✔
1657
      for (Column column : columns) {
1✔
1658
        appendExpression(column, null, builder, indent + 1, i, columns.size(), true,
1✔
1659
            BreakLine.ALWAYS);
1660
        i++;
1✔
1661
      }
1✔
1662
      appendNormalizingTrailingWhiteSpace(builder, " ) ");
1✔
1663
    }
1664
    appendNormalizedLineBreak(builder);
1✔
1665
    Select select = insert.getSelect();
1✔
1666
    appendSelect(select, builder, indent, false, false);
1✔
1667
  }
1✔
1668

1669
  private static void appendUpdate(StringBuilder builder, Update update, int indent) {
1670
    List<WithItem> withItems = update.getWithItemsList();
1✔
1671
    if (withItems != null && !withItems.isEmpty()) {
1✔
1672
      int i = 0;
1✔
1673
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1674

1675
      for (WithItem withItem : withItems) {
1✔
1676
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1677
        i++;
1✔
1678
      }
1✔
1679

1680
      appendNormalizedLineBreak(builder);
1✔
1681
    }
1682

1683
    appendKeyWord(builder, outputFormat, "UPDATE", "", " ");
1✔
1684

1685
    OracleHint oracleHint = update.getOracleHint();
1✔
1686
    if (oracleHint != null) {
1✔
1687
      appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
×
1688
    }
1689

1690
    Table table = update.getTable();
1✔
1691
    Alias alias = table.getAlias();
1✔
1692

1693
    appendTable(table, alias, builder);
1✔
1694

1695
    if (update.getStartJoins() != null) {
1✔
1696
      appendJoins(update.getStartJoins(), builder, indent);
1✔
1697
    }
1698

1699
    appendNormalizedLineBreak(builder);
1✔
1700
    for (int j = 0; j < indent; j++) {
1✔
1701
      builder.append(indentString);
×
1702
    }
1703
    appendKeyWord(builder, outputFormat, "SET", "", " ");
1✔
1704

1705
    final int subIndent = getSubIndent(builder, true);
1✔
1706
    appendUpdateSets(builder, update.getUpdateSets(), subIndent);
1✔
1707

1708
    if (update.getFromItem() != null) {
1✔
1709
      appendNormalizedLineBreak(builder);
1✔
1710
      for (int j = 0; j < indent; j++) {
1✔
1711
        builder.append(indentString);
×
1712
      }
1713
      appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
1714
      appendFromItem(update.getFromItem(), builder, indent, 0, 1);
1✔
1715
    }
1716

1717
    List<Join> joins = update.getJoins();
1✔
1718
    appendJoins(joins, builder, indent);
1✔
1719

1720
    Expression whereExpression = update.getWhere();
1✔
1721
    appendWhere(whereExpression, builder, indent);
1✔
1722

1723
    List<OrderByElement> orderByElements = update.getOrderByElements();
1✔
1724
    appendOrderByElements(orderByElements, builder, indent);
1✔
1725
  }
1✔
1726

1727
  private static void appendUpdateSets(StringBuilder builder, List<UpdateSet> updateSets,
1728
      int subIndent) {
1729
    int i = 0;
1✔
1730
    int n = updateSets.size();
1✔
1731
    for (UpdateSet updateSet : updateSets) {
1✔
1732
      if (i > 0) {
1✔
1733
        appendNormalizedLineBreak(builder);
1✔
1734
        for (int j = 0; j < subIndent; j++) {
1✔
1735
          builder.append(indentString);
1✔
1736
        }
1737
      }
1738

1739
      switch (separation) {
1✔
1740
        case BEFORE:
1741
          builder.append(i > 0 ? ", " : "");
1✔
1742
      }
1743

1744
      appendExpressionList(updateSet.getColumns(), builder, subIndent, BreakLine.AFTER_FIRST);
1✔
1745
      appendNormalizingTrailingWhiteSpace(builder, " = ");
1✔
1746
      appendExpressionList(updateSet.getValues(), builder, subIndent, BreakLine.AFTER_FIRST);
1✔
1747

1748
      switch (separation) {
1✔
1749
        case AFTER:
1750
          appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? ", " : "");
1✔
1751
          break;
1752
      }
1753

1754
      i++;
1✔
1755
    }
1✔
1756
  }
1✔
1757

1758
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
1759
  private static void appendSelect(Select select, StringBuilder builder, int indent,
1760
      boolean breakLineBefore, boolean indentFirstLine) {
1761

1762
    List<WithItem> withItems = select.getWithItemsList();
1✔
1763
    if (withItems != null && !withItems.isEmpty()) {
1✔
1764
      int i = 0;
1✔
1765
      if (breakLineBefore) {
1✔
1766
        appendNormalizedLineBreak(builder);
1✔
1767
        for (int j = 0; indentFirstLine && j < indent; j++) {
1✔
1768
          builder.append(indentString);
1✔
1769
        }
1770
      }
1771
      appendKeyWord(builder, outputFormat, "WITH", "", " ");
1✔
1772

1773
      for (WithItem withItem : withItems) {
1✔
1774
        appendWithItem(withItem, builder, indent, i, withItems.size());
1✔
1775
        i++;
1✔
1776
      }
1✔
1777
    }
1778

1779
    // All Known Implementing Classes: PlainSelect, SetOperationList,
1780
    // ValuesStatement, WithItem
1781
    if (select instanceof PlainSelect) {
1✔
1782
      PlainSelect plainSelect = (PlainSelect) select;
1✔
1783

1784
      int i = 0;
1✔
1785
      if (breakLineBefore || withItems != null && !withItems.isEmpty()) {
1✔
1786
        appendNormalizedLineBreak(builder);
1✔
1787
        for (int j = 0; indentFirstLine && j < indent; j++) {
1✔
1788
          builder.append(indentString);
1✔
1789
        }
1790
      }
1791

1792
      appendKeyWord(builder, outputFormat, "SELECT", "", " ");
1✔
1793

1794
      OracleHint oracleHint = plainSelect.getOracleHint();
1✔
1795
      if (oracleHint != null) {
1✔
1796
        appendHint(builder, outputFormat, oracleHint.toString(), "", " ");
1✔
1797
      }
1798

1799
      Top top = plainSelect.getTop();
1✔
1800
      if (top !=null) {
1✔
1801
        appendKeyWord(builder, outputFormat, "TOP", "", top.hasParenthesis() ? "( " : " ");
1✔
1802
        appendExpression(top.getExpression(), null, builder, 0, 0, 0, false, BreakLine.AS_NEEDED);
1✔
1803
        if (top.hasParenthesis()) {
1✔
1804
          builder.append(" )");
×
1805
        }
1806
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1807

1808
        if (top.isPercentage()) {
1✔
1809
          appendKeyWord(builder, outputFormat, "PERCENT", "", " ");
×
1810
        }
1811

1812
        if (top.isWithTies()) {
1✔
1813
          appendKeyWord(builder, outputFormat, "WITH TIES", "", " ");
×
1814
        }
1815
      }
1816

1817
      Distinct distinct = plainSelect.getDistinct();
1✔
1818
      if (distinct != null) {
1✔
1819
        if (distinct.isUseUnique()) {
1✔
1820
          // @todo: implement Use Unique Distinct clause
1821
          throw new UnsupportedOperationException("Unique DISTINCT not supported yet.");
×
1822
        }
1823

1824
        if (distinct.getOnSelectItems() != null && !distinct.getOnSelectItems().isEmpty()) {
1✔
1825
          // @todo: implement Use Unique Distinct clause
1826
          throw new UnsupportedOperationException(
×
1827
              "DISTINCT on select items are not supported yet.");
1828
        }
1829
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
1✔
1830
      }
1831

1832
      int subIndent = oracleHint != null || distinct != null || top!=null
1✔
1833
                      ? indent + 1
1✔
1834
                      : getSubIndent(builder, plainSelect.getSelectItems().size() > 1);
1✔
1835
      BreakLine bl =
1836
          oracleHint != null || distinct != null || top!=null
1✔
1837
          ? BreakLine.ALWAYS
1✔
1838
          : BreakLine.AFTER_FIRST;
1✔
1839

1840
      List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
1✔
1841
      appendSelectItemList(selectItems, builder, subIndent, i, bl, indent);
1✔
1842

1843
      FromItem fromItem = plainSelect.getFromItem();
1✔
1844
      if (fromItem != null) {
1✔
1845
        appendNormalizedLineBreak(builder);
1✔
1846
        for (int j = 0; j < indent; j++) {
1✔
1847
          builder.append(indentString);
1✔
1848
        }
1849
        appendKeyWord(builder, outputFormat, "FROM", "", " ");
1✔
1850
        appendFromItem(fromItem, builder, indent, i, 1);
1✔
1851

1852
        List<Join> joins = plainSelect.getJoins();
1✔
1853
        appendJoins(joins, builder, indent);
1✔
1854
      }
1855

1856
      Expression whereExpression = plainSelect.getWhere();
1✔
1857
      appendWhere(whereExpression, builder, indent);
1✔
1858

1859
      GroupByElement groupByElement = plainSelect.getGroupBy();
1✔
1860
      appendGroupByElement(groupByElement, builder, indent);
1✔
1861

1862
      Expression havingExpression = plainSelect.getHaving();
1✔
1863
      appendHavingExpression(havingExpression, builder, indent);
1✔
1864

1865
      List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
1✔
1866
      appendOrderByElements(orderByElements, builder, indent);
1✔
1867

1868
      Limit limit = plainSelect.getLimit();
1✔
1869
      if (limit != null) {
1✔
1870
        appendNormalizedLineBreak(builder);
1✔
1871
        for (int j = 0; j < indent; j++) {
1✔
1872
          builder.append(indentString);
1✔
1873
        }
1874
        appendKeyWord(builder, outputFormat, "LIMIT", "", " ");
1✔
1875

1876
        Expression rowCount = limit.getRowCount();
1✔
1877
        if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
1✔
1878
          // no offset allowed
1879
          appendKeyWord(builder, outputFormat, "NULL", "", " ");
×
1880
        } else {
1881
          if (null != limit.getOffset()) {
1✔
1882
            appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false,
1✔
1883
                BreakLine.NEVER);
1884
            builder.append(", ");
1✔
1885
          }
1886
          if (null != limit.getRowCount()) {
1✔
1887
            appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
1✔
1888
                BreakLine.NEVER);
1889
          }
1890
        }
1891
      }
1892

1893
      Offset offset = plainSelect.getOffset();
1✔
1894
      if (offset != null) {
1✔
1895
        appendNormalizedLineBreak(builder);
1✔
1896
        for (int j = 0; j < indent; j++) {
1✔
1897
          builder.append(indentString);
×
1898
        }
1899
        appendKeyWord(builder, outputFormat, "OFFSET", "", " ");
1✔
1900

1901
        Expression offsetExpression = offset.getOffset();
1✔
1902
        appendExpression(offsetExpression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1903

1904
        String offsetParam = offset.getOffsetParam();
1✔
1905
        if (offsetParam != null) {
1✔
1906
          appendString(offsetParam, builder, indent, 0, 1, false, BreakLine.NEVER);
×
1907
        }
1908
      }
1909

1910
      Fetch fetch = plainSelect.getFetch();
1✔
1911
      if (fetch != null) {
1✔
1912
        appendNormalizedLineBreak(builder);
1✔
1913
        for (int j = 0; j < indent; j++) {
1✔
1914
          builder.append(indentString);
×
1915
        }
1916
        appendKeyWord(builder, outputFormat, "FETCH", "", "");
1✔
1917

1918
        if (fetch.isFetchParamFirst()) {
1✔
1919
          appendKeyWord(builder, outputFormat, "FIRST", " ", "");
1✔
1920
        } else {
1921
          appendKeyWord(builder, outputFormat, "NEXT", " ", "");
1✔
1922
        }
1923

1924
        Expression expression = fetch.getExpression();
1✔
1925
        if (expression != null) {
1✔
1926
          appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
1927
          appendExpression(expression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1928
        }
1929

1930
        for (String s : fetch.getFetchParameters()) {
1✔
1931
          appendKeyWord(builder, outputFormat, s, " ", "");
1✔
1932
        }
1✔
1933

1934
      }
1935

1936
    } else if (select instanceof SetOperationList) {
1✔
1937
      SetOperationList setOperationList = (SetOperationList) select;
1✔
1938

1939
      List<SetOperation> setOperations = setOperationList.getOperations();
1✔
1940

1941
      int k = 0;
1✔
1942

1943
      List<Select> selects = setOperationList.getSelects();
1✔
1944
      if (selects != null && !selects.isEmpty()) {
1✔
1945
        for (Select selectBody1 : selects) {
1✔
1946
          if (k > 0 && setOperations != null && setOperations.size() >= k) {
1✔
1947
            SetOperation setOperation = setOperations.get(k - 1);
1✔
1948
            appendSetOperation(setOperation, builder, indent);
1✔
1949
          }
1950
          int subIndent = indent;
1✔
1951
          appendSelect(selectBody1, builder, subIndent, k > 0 || breakLineBefore,
1✔
1952
              k > 0 || breakLineBefore || indentFirstLine);
1953
          k++;
1✔
1954
        }
1✔
1955
      }
1956

1957
      List<OrderByElement> orderByElements = setOperationList.getOrderByElements();
1✔
1958
      appendOrderByElements(orderByElements, builder, indent);
1✔
1959

1960
      Limit limit = setOperationList.getLimit();
1✔
1961
      if (limit != null) {
1✔
1962
        appendNormalizedLineBreak(builder);
1✔
1963
        for (int j = 0; j < indent; j++) {
1✔
1964
          builder.append(indentString);
×
1965
        }
1966
        appendKeyWord(builder, outputFormat, "LIMIT", "", " ");
1✔
1967

1968
        Expression rowCount = limit.getRowCount();
1✔
1969
        if (rowCount instanceof AllValue || rowCount instanceof NullValue) {
1✔
1970
          // no offset allowed
1971
          appendKeyWord(builder, outputFormat, "NULL", "", " ");
×
1972
        } else {
1973
          if (null != limit.getOffset()) {
1✔
1974
            appendExpression(limit.getOffset(), null, builder, indent, 0, 1, false,
×
1975
                BreakLine.NEVER);
1976
            builder.append(", ");
×
1977
          }
1978
          if (null != limit.getRowCount()) {
1✔
1979
            appendExpression(limit.getRowCount(), null, builder, indent, 0, 1, false,
1✔
1980
                BreakLine.NEVER);
1981
          }
1982
        }
1983
      }
1984

1985
      Offset offset = setOperationList.getOffset();
1✔
1986
      if (offset != null) {
1✔
1987
        appendNormalizedLineBreak(builder);
1✔
1988
        for (int j = 0; j < indent; j++) {
1✔
1989
          builder.append(indentString);
×
1990
        }
1991
        appendKeyWord(builder, outputFormat, "OFFSET", "", " ");
1✔
1992

1993
        Expression offsetExpression = offset.getOffset();
1✔
1994
        appendExpression(offsetExpression, null, builder, indent, 0, 1, false, BreakLine.NEVER);
1✔
1995

1996
        String offsetParam = offset.getOffsetParam();
1✔
1997
        if (offsetParam != null) {
1✔
1998
          appendString(offsetParam, builder, indent, 0, 1, false, BreakLine.NEVER);
×
1999
        }
2000
      }
2001

2002
    } else if (select instanceof Values) {
1✔
2003
      Values values = (Values) select;
1✔
2004

2005
      if (breakLineBefore) {
1✔
2006
        appendNormalizedLineBreak(builder);
×
2007
        for (int j = 0; indentFirstLine && j < indent; j++) {
×
2008
          builder.append(indentString);
×
2009
        }
2010
      }
2011

2012
      appendKeyWord(builder, outputFormat, "VALUES", "", " ");
1✔
2013
      appendExpressionList(values.getExpressions(), builder, indent, BreakLine.AS_NEEDED);
1✔
2014

2015
    } else if (select instanceof ParenthesedSelect) {
1✔
2016
      ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
1✔
2017
      builder.append("( ");
1✔
2018
      int subIndent = breakLineBefore ? indent + 1
1✔
2019
          : getSubIndent(builder, !(parenthesedSelect.getSelect() instanceof Values));
1✔
2020

2021
      appendSelect(parenthesedSelect.getSelect(), builder, subIndent, breakLineBefore,
1✔
2022
          indentFirstLine);
2023
      builder.append(" )");
1✔
2024
    }
2025
  }
1✔
2026

2027
  public static void appendSelectItemList(List<SelectItem<?>> selectItems, StringBuilder builder,
2028
      int subIndent, int i, BreakLine bl, int indent) throws UnsupportedOperationException {
2029
    int j = i;
1✔
2030
    for (SelectItem<?> selectItem : selectItems) {
1✔
2031
      Alias alias = selectItem.getAlias();
1✔
2032
      Expression expression = selectItem.getExpression();
1✔
2033

2034
      appendExpression(expression, alias, builder, subIndent, j++, selectItems.size(), true, bl);
1✔
2035
    }
1✔
2036
  }
1✔
2037

2038
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2039
  private static void appendOrderByElements(List<OrderByElement> orderByElements,
2040
      StringBuilder builder, int indent) {
2041
    if (orderByElements != null) {
1✔
2042
      int i = 0;
1✔
2043
      appendNormalizedLineBreak(builder);
1✔
2044
      for (int j = 0; j < indent; j++) {
1✔
2045
        builder.append(indentString);
1✔
2046
      }
2047
      appendKeyWord(builder, outputFormat, "ORDER BY", "", " ");
1✔
2048

2049
      int subIndent = getSubIndent(builder, orderByElements.size() > 1);
1✔
2050

2051
      for (OrderByElement orderByElement : orderByElements) {
1✔
2052
        Expression expression = orderByElement.getExpression();
1✔
2053
        appendExpression(expression, null, builder, subIndent, i, orderByElements.size(), true,
1✔
2054
            BreakLine.AFTER_FIRST);
2055

2056
        if (orderByElement.isAscDescPresent()) {
1✔
2057
          builder.append(" ");
1✔
2058

2059
          if (orderByElement.isAsc()) {
1✔
2060
            appendKeyWord(builder, outputFormat, "ASC", "", " ");
1✔
2061
          } else {
2062
            appendKeyWord(builder, outputFormat, "DESC", "", " ");
1✔
2063
          }
2064
        }
2065

2066
        NullOrdering nullOrdering = orderByElement.getNullOrdering();
1✔
2067
        if (NullOrdering.NULLS_FIRST.equals(nullOrdering)) {
1✔
2068
          appendKeyWord(builder, outputFormat, "NULLS FIRST",
1✔
2069
              orderByElement.isAscDescPresent() ? "" : " ", " ");
1✔
2070
        }
2071

2072
        if (NullOrdering.NULLS_LAST.equals(nullOrdering)) {
1✔
2073
          appendKeyWord(builder, outputFormat, "NULLS LAST",
×
2074
              orderByElement.isAscDescPresent() ? "" : " ", " ");
×
2075
        }
2076

2077
        i++;
1✔
2078
      }
1✔
2079
    }
2080
  }
1✔
2081

2082
  private static void appendHavingExpression(Expression havingExpression, StringBuilder builder,
2083
      int indent) {
2084
    if (havingExpression != null) {
1✔
2085
      appendNormalizedLineBreak(builder);
1✔
2086
      for (int j = 0; j < indent; j++) {
1✔
2087
        builder.append(indentString);
×
2088
      }
2089
      appendKeyWord(builder, outputFormat, "HAVING", "", " ");
1✔
2090
      appendExpression(havingExpression, null, builder, indent, 0, 1, false, BreakLine.AFTER_FIRST);
1✔
2091
    }
2092
  }
1✔
2093

2094
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2095
  private static void appendGroupByElement(GroupByElement groupByElement, StringBuilder builder,
2096
      int indent) throws UnsupportedOperationException {
2097
    int i = 0;
1✔
2098
    if (groupByElement != null) {
1✔
2099
      appendNormalizedLineBreak(builder);
1✔
2100
      for (int j = 0; j < indent; j++) {
1✔
2101
        builder.append(indentString);
1✔
2102
      }
2103
      appendKeyWord(builder, outputFormat, "GROUP BY", "", " ");
1✔
2104

2105
      List<?> groupingSets = groupByElement.getGroupingSets();
1✔
2106
      ExpressionList<?> groupByExpressions = groupByElement.getGroupByExpressionList();
1✔
2107

2108
      if (groupingSets != null && !groupingSets.isEmpty()) {
1✔
2109
        throw new UnsupportedOperationException("Grouping Sets are not supported yet.");
×
2110
      }
2111

2112
      if (groupByExpressions != null && !groupByExpressions.isEmpty()) {
1✔
2113
        BreakLine breakLine = BreakLine.AFTER_FIRST;
1✔
2114
        int size = groupByExpressions.size();
1✔
2115
        int subIndent = getSubIndent(builder, size > 1);
1✔
2116

2117
        for (Expression expression : groupByExpressions) {
1✔
2118
          switch (breakLine) {
1✔
2119
            case AS_NEEDED:
2120
              BreakLine bl =
2121
                  size == 4 || size >= 5 && i % 3 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
×
2122
              appendExpression(expression, null, builder, subIndent, i, size, true, bl);
×
2123
              break;
×
2124

2125
            default:
2126
              appendExpression(expression, null, builder, subIndent, i, size, true, breakLine);
1✔
2127
          }
2128
          i++;
1✔
2129
        }
1✔
2130
      }
2131
    }
2132
  }
1✔
2133

2134
  private static void appendWhere(Expression whereExpression, StringBuilder builder, int indent) {
2135
    if (whereExpression != null) {
1✔
2136
      appendNormalizedLineBreak(builder);
1✔
2137
      for (int j = 0; j < indent; j++) {
1✔
2138
        builder.append(indentString);
1✔
2139
      }
2140
      appendKeyWord(builder, outputFormat, "WHERE", "", " ");
1✔
2141
      appendExpression(whereExpression, null, builder, indent, 0, 1, false, BreakLine.AFTER_FIRST);
1✔
2142
    }
2143
  }
1✔
2144

2145
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2146
  private static void appendJoins(List<Join> joins, StringBuilder builder, int indent) {
2147
    if (joins != null) {
1✔
2148
      for (Join join : joins) {
1✔
2149

2150
        if (join.isSimple()) {
1✔
2151
          switch (separation) {
1✔
2152
            case AFTER:
2153
              builder.append(",");
1✔
2154
          }
2155
        }
2156
        appendNormalizedLineBreak(builder);
1✔
2157

2158
        if (join.isSimple()) {
1✔
2159
          for (int j = 0; j <= indent; j++) {
1✔
2160
            builder.append(indentString);
1✔
2161
          }
2162

2163
          switch (separation) {
1✔
2164
            case BEFORE:
2165
              builder.append(", ");
1✔
2166
          }
2167

2168
        } else {
2169
          for (int j = 0; j <= indent; j++) {
1✔
2170
            builder.append(indentString);
1✔
2171
          }
2172

2173
          if (join.isInner()) {
1✔
2174
            appendKeyWord(builder, outputFormat, "INNER", "", " ");
1✔
2175
          }
2176
          if (join.isLeft()) {
1✔
2177
            appendKeyWord(builder, outputFormat, "LEFT", "", " ");
1✔
2178
          }
2179
          if (join.isRight()) {
1✔
2180
            appendKeyWord(builder, outputFormat, "RIGHT", "", " ");
×
2181
          }
2182
          if (join.isNatural()) {
1✔
2183
            appendKeyWord(builder, outputFormat, "NATURAL", "", " ");
×
2184
          }
2185
          if (join.isCross()) {
1✔
2186
            appendKeyWord(builder, outputFormat, "CROSS", "", " ");
×
2187
          }
2188
          if (join.isOuter()) {
1✔
2189
            appendKeyWord(builder, outputFormat, "OUTER", "", " ");
1✔
2190
          }
2191
          if (join.isFull()) {
1✔
2192
            appendKeyWord(builder, outputFormat, "FULL", "", " ");
×
2193
          }
2194

2195
          appendKeyWord(builder, outputFormat, "JOIN", "", " ");
1✔
2196
        }
2197

2198
        FromItem rightFromItem = join.getRightItem();
1✔
2199
        appendFromItem(rightFromItem, builder, indent, 0, 1);
1✔
2200

2201
        for (Expression onExpression : join.getOnExpressions()) {
1✔
2202
          if (onExpression != null) {
1✔
2203
            appendNormalizedLineBreak(builder);
1✔
2204
            for (int j = 0; j <= indent + 1; j++) {
1✔
2205
              builder.append(indentString);
1✔
2206
            }
2207
            appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
2208

2209
            appendExpression(onExpression, null, builder, indent + 2, 0, 1, false,
1✔
2210
                BreakLine.AFTER_FIRST);
2211
          }
2212
        }
1✔
2213

2214
        List<Column> usingColumns = join.getUsingColumns();
1✔
2215
        if (usingColumns != null && !usingColumns.isEmpty()) {
1✔
2216
          appendNormalizedLineBreak(builder);
×
2217
          for (int j = 0; j <= indent + 2; j++) {
×
2218
            builder.append(indentString);
×
2219
          }
2220
          appendKeyWord(builder, outputFormat, "USING", "", " ( ");
×
2221
          int k = 0;
×
2222
          for (Column column : usingColumns) {
×
2223
            appendExpression(column, null, builder, indent + 3, k, usingColumns.size(), true,
×
2224
                BreakLine.AFTER_FIRST);
2225
            k++;
×
2226
          }
×
2227
          appendNormalizingTrailingWhiteSpace(builder, " )");
×
2228
        }
2229
      }
1✔
2230
    }
2231
  }
1✔
2232

2233
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2234
  private static void appendWithItem(WithItem withItem, StringBuilder builder, int indent, int i,
2235
      int n) {
2236

2237
    if (i > 0) {
1✔
2238
      appendNormalizedLineBreak(builder);
1✔
2239
      for (int j = 0; j <= indent; j++) {
1✔
2240
        builder.append(indentString);
1✔
2241
      }
2242
    }
2243

2244
    switch (separation) {
1✔
2245
      case BEFORE:
2246
        appendAlias(builder, outputFormat, withItem.getAlias().getName(), i > 0 ? ", " : "", " ");
1✔
2247
        break;
1✔
2248
      default:
2249
        appendAlias(builder, outputFormat, withItem.getAlias().getName(), "", " ");
1✔
2250
    }
2251

2252
    List<SelectItem<?>> selectItems = withItem.getWithItemList();
1✔
2253
    if (selectItems != null && !selectItems.isEmpty()) {
1✔
2254
      builder.append("( ");
1✔
2255

2256
      int subIndent = getSubIndent(builder, selectItems.size() > 2);
1✔
2257
      BreakLine bl = selectItems.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
2258

2259
      appendSelectItemList(selectItems, builder, subIndent, i, bl, indent);
1✔
2260
      builder.append(" ) ");
1✔
2261

2262
      appendNormalizedLineBreak(builder);
1✔
2263
      for (int j = 0; j < indent + 1; j++) {
1✔
2264
        builder.append(indentString);
1✔
2265
      }
2266
      appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2267
      appendSelect(withItem.getSelect(), builder, indent + 1, false, false);
1✔
2268

2269
    } else {
1✔
2270
      appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2271
      appendSelect(withItem.getSelect(), builder, indent + 1, true, true);
1✔
2272
    }
2273

2274
    switch (separation) {
1✔
2275
      case AFTER:
2276
        appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? "," : "");
1✔
2277
        break;
1✔
2278
      case BEFORE:
2279
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2280
        break;
2281
    }
2282
  }
1✔
2283

2284
  private static void appendRowConstructor(StringBuilder builder, int indent,
2285
      RowConstructor<?> rowConstructor) {
2286

2287
    if (rowConstructor.getName() != null) {
×
2288
      appendAlias(builder, outputFormat, rowConstructor.getName(), "", "");
×
2289
    }
2290

2291
    appendExpressionList(rowConstructor, builder, indent, BreakLine.AS_NEEDED);
×
2292
  }
×
2293

2294
  private static void appendStringList(Collection<String> strings, StringBuilder builder,
2295
      int indent, boolean commaSeparated, BreakLine breakLine) {
2296
    int i = 0;
1✔
2297
    if (strings != null) {
1✔
2298
      for (String s : strings) {
1✔
2299
        appendString(s, builder, indent, i, strings.size(), commaSeparated, breakLine);
1✔
2300
        i++;
1✔
2301
      }
1✔
2302
    }
2303
  }
1✔
2304

2305
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2306
  private static void appendString(String s, StringBuilder builder, int indent, int i, int n,
2307
      boolean commaSeparated, BreakLine breakLine) {
2308

2309
    if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2310
      appendNormalizedLineBreak(builder);
1✔
2311
      for (int j = 0; j < indent; j++) {
1✔
2312
        builder.append(indentString);
1✔
2313
      }
2314
    }
2315

2316
    switch (separation) {
1✔
2317
      case AFTER:
2318
        appendObjectName(builder, outputFormat, s, "", "");
1✔
2319
        appendNormalizingTrailingWhiteSpace(builder, commaSeparated && i < n - 1 ? ", " : "");
1✔
2320
        break;
1✔
2321
      case BEFORE:
2322
        builder.append(commaSeparated && i > 0 ? ", " : "");
1✔
2323
        appendObjectName(builder, outputFormat, s, "", "");
1✔
2324
    }
2325
  }
1✔
2326

2327
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
2328
  private static void appendExpression(Expression expression, Alias alias, StringBuilder builder,
2329
      int indent, final int i, final int n, boolean commaSeparated, BreakLine breakLine) {
2330

2331
    if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2332
      appendNormalizedLineBreak(builder);
1✔
2333
      for (int j = 0; j < indent; j++) {
1✔
2334
        builder.append(indentString);
1✔
2335
      }
2336
    }
2337

2338
    switch (separation) {
1✔
2339
      case BEFORE:
2340
        builder.append(commaSeparated && i > 0 ? ", " : "");
1✔
2341
    }
2342

2343
    if (expression instanceof Column) {
1✔
2344
      Column column = (Column) expression;
1✔
2345
      appendObjectName(builder, outputFormat, column.getFullyQualifiedName(), "", "");
1✔
2346

2347
    } else if (expression instanceof AndExpression) {
1✔
2348
      AndExpression andExpression = (AndExpression) expression;
1✔
2349
      appendExpression(andExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2350
          BreakLine.AFTER_FIRST);
2351

2352
      appendNormalizedLineBreak(builder);
1✔
2353
      for (int j = 0; j <= indent; j++) {
1✔
2354
        builder.append(indentString);
1✔
2355
      }
2356
      appendOperator(builder, outputFormat, "AND", "", " ");
1✔
2357

2358
      appendExpression(andExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2359
          BreakLine.AFTER_FIRST);
2360

2361
    } else if (expression instanceof OrExpression) {
1✔
2362
      OrExpression orExpression = (OrExpression) expression;
1✔
2363
      appendExpression(orExpression.getLeftExpression(), null, builder, indent, i, n, false,
1✔
2364
          BreakLine.AFTER_FIRST);
2365

2366
      appendNormalizedLineBreak(builder);
1✔
2367
      for (int j = 0; j <= indent; j++) {
1✔
2368
        builder.append(indentString);
1✔
2369
      }
2370
      appendOperator(builder, outputFormat, "OR", "", " ");
1✔
2371

2372
      appendExpression(orExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2373
          BreakLine.AFTER_FIRST);
2374

2375
    } else if (expression instanceof EqualsTo) {
1✔
2376
      EqualsTo equalsTo = (EqualsTo) expression;
1✔
2377

2378
      if (equalsTo.getOraclePriorPosition() == EqualsTo.ORACLE_PRIOR_START) {
1✔
2379
        appendOperator(builder, outputFormat, "PRIOR", "", " ");
×
2380
      }
2381

2382
      appendExpression(equalsTo.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2383
          BreakLine.AS_NEEDED);
2384

2385
      if (equalsTo.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_RIGHT) {
1✔
2386
        appendOperator(builder, outputFormat, "(+)", "", " ");
1✔
2387
      }
2388

2389
      appendOperator(builder, outputFormat, "=", " ", " ");
1✔
2390

2391
      if (equalsTo.getOraclePriorPosition() == EqualsTo.ORACLE_PRIOR_END) {
1✔
2392
        appendOperator(builder, outputFormat, "PRIOR", "", " ");
×
2393
      }
2394

2395
      appendExpression(equalsTo.getRightExpression(), alias, builder, indent + 1, i, n, false,
1✔
2396
          BreakLine.AFTER_FIRST);
2397

2398
      if (equalsTo.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_LEFT) {
1✔
2399
        appendOperator(builder, outputFormat, "(+)", "", " ");
1✔
2400
      }
2401

2402
    } else if (expression instanceof Parenthesis) {
1✔
2403
      Parenthesis parenthesis = (Parenthesis) expression;
1✔
2404
      int subIndent = getSubIndent(builder, false);
1✔
2405
      builder.append("( ");
1✔
2406
      appendExpression(parenthesis.getExpression(), null, builder, subIndent, i, n, false,
1✔
2407
          BreakLine.NEVER);
2408
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2409

2410
    } else if (expression instanceof CaseExpression) {
1✔
2411
      int subIndent = getSubIndent(builder, false);
1✔
2412

2413
      CaseExpression caseExpression = (CaseExpression) expression;
1✔
2414
      appendKeyWord(builder, outputFormat, "CASE", "", " ");
1✔
2415
      if (caseExpression.getSwitchExpression()!=null) {
1✔
2416
        appendExpression( caseExpression.getSwitchExpression(), null, builder, indent + 1, i, n, false, BreakLine.NEVER);
1✔
2417
      }
2418

2419
      List<WhenClause> whenClauses = caseExpression.getWhenClauses();
1✔
2420
      for (WhenClause whenClause : whenClauses) {
1✔
2421
        appendNormalizedLineBreak(builder);
1✔
2422
        for (int j = 0; j < subIndent ; j++) {
1✔
2423
          builder.append(indentString);
1✔
2424
        }
2425
        appendKeyWord(builder, outputFormat, "WHEN", "", " ");
1✔
2426
        appendExpression(whenClause.getWhenExpression(), null, builder, subIndent + 1, 0, 1, false,
1✔
2427
            BreakLine.AFTER_FIRST);
2428

2429
        appendNormalizedLineBreak(builder);
1✔
2430
        for (int j = 0; j < subIndent + 1; j++) {
1✔
2431
          builder.append(indentString);
1✔
2432
        }
2433
        appendKeyWord(builder, outputFormat, "THEN", "", " ");
1✔
2434
        appendExpression(whenClause.getThenExpression(), null, builder, subIndent + 1, 0, 1, false,
1✔
2435
            BreakLine.AFTER_FIRST);
2436
      }
1✔
2437

2438
      Expression elseExpression = caseExpression.getElseExpression();
1✔
2439
      if (elseExpression != null) {
1✔
2440
        appendNormalizedLineBreak(builder);
1✔
2441
        for (int j = 0; j < subIndent; j++) {
1✔
2442
          builder.append(indentString);
1✔
2443
        }
2444
        appendKeyWord(builder, outputFormat, "ELSE", "", " ");
1✔
2445
        appendExpression(elseExpression, null, builder, subIndent + 1, 0, 1, false,
1✔
2446
            BreakLine.AFTER_FIRST);
2447
      }
2448

2449
      appendNormalizedLineBreak(builder);
1✔
2450
      for (int j = 0; j < subIndent; j++) {
1✔
2451
        builder.append(indentString);
1✔
2452
      }
2453
      appendKeyWord(builder, outputFormat, "END", "", "");
1✔
2454

2455
    } else if (expression instanceof StringValue) {
1✔
2456
      StringValue stringValue = (StringValue) expression;
1✔
2457
      appendValue(builder, outputFormat, stringValue.toString(), "", "");
1✔
2458

2459
    } else if (expression instanceof LongValue) {
1✔
2460
      LongValue longValue = (LongValue) expression;
1✔
2461
      appendValue(builder, outputFormat, longValue.toString(), "", "");
1✔
2462

2463
    } else if (expression instanceof DateValue) {
1✔
2464
      DateValue dateValue = (DateValue) expression;
1✔
2465
      appendValue(builder, outputFormat, dateValue.toString(), "", "");
1✔
2466

2467
    } else if (expression instanceof DoubleValue) {
1✔
2468
      DoubleValue doubleValue = (DoubleValue) expression;
1✔
2469
      appendValue(builder, outputFormat, doubleValue.toString(), "", "");
1✔
2470

2471
    } else if (expression instanceof NotExpression) {
1✔
2472
      NotExpression notExpression = (NotExpression) expression;
1✔
2473
      if (notExpression.isExclamationMark()) {
1✔
2474
        appendOperator(builder, outputFormat, "!", "", "");
×
2475
      } else {
2476
        appendOperator(builder, outputFormat, "NOT", "", " ");
1✔
2477
      }
2478

2479
      appendExpression(notExpression.getExpression(), null, builder, indent + 1, i, n, false,
1✔
2480
          BreakLine.AFTER_FIRST);
2481

2482
    } else if (expression instanceof LikeExpression) {
1✔
2483
      LikeExpression likeExpression = (LikeExpression) expression;
1✔
2484

2485
      appendExpression(likeExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2486
          BreakLine.AFTER_FIRST);
2487

2488
      if (likeExpression.isNot()) {
1✔
2489
        appendOperator(builder, outputFormat, "NOT", " ", "");
1✔
2490
      }
2491

2492
      appendOperator(builder, outputFormat, "LIKE", " ", " ");
1✔
2493

2494
      appendExpression(likeExpression.getRightExpression(), null, builder, indent + 1, i, n, false,
1✔
2495
          BreakLine.AFTER_FIRST);
2496

2497
      Expression escapeExpression = likeExpression.getEscape();
1✔
2498
      if (escapeExpression != null) {
1✔
2499
        appendOperator(builder, outputFormat, "ESCAPE", " ", " ");
×
2500
        appendExpression(escapeExpression, null, builder, indent + 1, i, n, false,
×
2501
            BreakLine.AS_NEEDED);
2502
      }
2503

2504
    } else if (expression instanceof NextValExpression) {
1✔
2505
      NextValExpression nextValExpression = (NextValExpression) expression;
×
2506
      if (nextValExpression.isUsingNextValueFor()) {
×
2507
        appendOperator(builder, outputFormat, "NEXT VALUE FOR", "", " ");
×
2508
      } else {
2509
        appendOperator(builder, outputFormat, "NEXTVAL FOR", "", " ");
×
2510
      }
2511

2512
      int j = 0;
×
2513
      for (String name : nextValExpression.getNameList()) {
×
2514
        if (j > 0) {
×
2515
          builder.append(".");
×
2516
        }
2517
        appendObjectName(builder, outputFormat, name, "", "");
×
2518
        j++;
×
2519
      }
×
2520

2521
    } else if (expression instanceof ExistsExpression) {
1✔
2522
      ExistsExpression existsExpression = (ExistsExpression) expression;
1✔
2523
      if (existsExpression.isNot()) {
1✔
2524
        appendOperator(builder, outputFormat, "NOT EXISTS", "", "");
×
2525
      } else {
2526
        appendOperator(builder, outputFormat, "EXISTS", "", " ");
1✔
2527
      }
2528

2529
      appendExpression(existsExpression.getRightExpression(), null, builder, indent + 1, i, n,
1✔
2530
          false, BreakLine.AFTER_FIRST);
2531

2532
    } else if (expression instanceof ExtractExpression) {
1✔
2533
      ExtractExpression extractExpression = (ExtractExpression) expression;
1✔
2534

2535
      // "EXTRACT(" + name + " FROM " + expression + ')';
2536

2537
      appendKeyWord(builder, outputFormat, "EXTRACT", "", "( ");
1✔
2538
      appendValue(builder, outputFormat, extractExpression.getName(), "", "");
1✔
2539
      appendKeyWord(builder, outputFormat, "FROM", " ", " ");
1✔
2540

2541
      appendExpression(extractExpression.getExpression(), null, builder, indent + 1, i, n, false,
1✔
2542
          BreakLine.AFTER_FIRST);
2543
      appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2544

2545
    } else if (expression instanceof JdbcNamedParameter) {
1✔
2546
      JdbcNamedParameter jdbcNamedParameter = (JdbcNamedParameter) expression;
1✔
2547
      appendValue(builder, outputFormat, jdbcNamedParameter.toString(), "", "");
1✔
2548

2549
    } else if (expression instanceof JdbcParameter) {
1✔
2550
      JdbcParameter jdbcParameter = (JdbcParameter) expression;
1✔
2551
      appendValue(builder, outputFormat, jdbcParameter.toString(), "", "");
1✔
2552

2553
    } else if (expression instanceof IsNullExpression) {
1✔
2554
      IsNullExpression isNullExpression = (IsNullExpression) expression;
1✔
2555
      appendExpression(isNullExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2556
          BreakLine.AFTER_FIRST);
2557

2558
      if (isNullExpression.isUseNotNull()) {
1✔
2559
        appendOperator(builder, outputFormat, "NOTNULL", " ", "");
×
2560
      } else if (isNullExpression.isUseIsNull()) {
1✔
2561
        if (isNullExpression.isNot()) {
×
2562
          appendOperator(builder, outputFormat, "NOT ISNULL", " ", "");
×
2563
        } else {
2564
          appendOperator(builder, outputFormat, "ISNULL", " ", "");
×
2565
        }
2566
      } else {
2567
        if (isNullExpression.isNot()) {
1✔
2568
          appendOperator(builder, outputFormat, "IS NOT NULL", " ", "");
1✔
2569
        } else {
2570
          appendOperator(builder, outputFormat, "IS NULL", " ", "");
1✔
2571
        }
2572
      }
2573

2574
    } else if (expression instanceof NullValue) {
1✔
2575
      NullValue nullValue = (NullValue) expression;
1✔
2576
      appendKeyWord(builder, outputFormat, nullValue.toString(), "", "");
1✔
2577

2578
    } else if (expression instanceof TimeKeyExpression) {
1✔
2579
      TimeKeyExpression timeKeyExpression = (TimeKeyExpression) expression;
1✔
2580
      appendValue(builder, outputFormat, timeKeyExpression.toString(), "", "");
1✔
2581

2582
    } else if (expression instanceof InExpression) {
1✔
2583
      InExpression inExpression = (InExpression) expression;
1✔
2584
      Expression leftExpression = inExpression.getLeftExpression();
1✔
2585
      boolean useNot = inExpression.isNot();
1✔
2586

2587
      Expression rightExpression = inExpression.getRightExpression();
1✔
2588

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

2591
      if (inExpression.isGlobal()) {
1✔
2592
        appendKeyWord(builder, outputFormat, "GLOBAL", " ", "");
×
2593
      }
2594

2595
      if (useNot) {
1✔
2596
        appendOperator(builder, outputFormat, "NOT IN", " ", " ");
×
2597
      } else {
2598
        appendOperator(builder, outputFormat, "IN", " ", " ");
1✔
2599
      }
2600

2601
      appendExpression(rightExpression, null, builder, indent, i, n, false, BreakLine.AS_NEEDED);
1✔
2602
    } else if (expression instanceof Function) {
1✔
2603
      Function function = (Function) expression;
1✔
2604

2605
      String name = function.getName();
1✔
2606
      ExpressionList<?> parameters = function.getParameters();
1✔
2607
      NamedExpressionList<?> namedParameters = function.getNamedParameters();
1✔
2608
      boolean distinct = function.isDistinct();
1✔
2609
      boolean allColumns = function.isAllColumns();
1✔
2610
      boolean escaped = function.isEscaped();
1✔
2611
      KeepExpression keep = function.getKeep();
1✔
2612
      Object attribute = function.getAttribute();
1✔
2613

2614
      if (escaped) {
1✔
2615
        appendFunction(builder, outputFormat, "fn", " {", " ");
×
2616
      }
2617

2618
      appendFunction(builder, outputFormat, name, "", "");
1✔
2619

2620
      if (parameters != null || namedParameters != null) {
1✔
2621
        if (parameters != null) {
1✔
2622
          if (distinct) {
1✔
2623
            appendKeyWord(builder, outputFormat, "DISTINCT", "( ", " ");
1✔
2624
          } else if (allColumns) {
1✔
2625
            appendKeyWord(builder, outputFormat, "ALL", "( ", " ");
×
2626
          } else {
2627
            builder.append("( ");
1✔
2628
          }
2629

2630
          if (name.equalsIgnoreCase("Decode")) {
1✔
2631
            appendDecodeExpressionsList(parameters, BreakLine.AS_NEEDED, builder, indent);
1✔
2632
          } else {
2633
            appendExpressionList(parameters, builder, indent, BreakLine.AS_NEEDED);
1✔
2634
          }
2635

2636
          appendNormalizingTrailingWhiteSpace(builder, " )");
1✔
2637
        } else {
2638
          // @todo: implement this properly and add a test case
2639
          builder.append(namedParameters);
1✔
2640
        }
2641
      } else if (allColumns) {
1✔
2642
        builder.append("( * )");
×
2643
      } else {
2644
        builder.append("()");
1✔
2645
      }
2646

2647
      if (attribute != null) {
1✔
2648
        builder.append(".").append(attribute);
×
2649
      }
2650

2651
      if (keep != null) {
1✔
2652
        builder.append(" ").append(keep);
×
2653
      }
2654

2655
      if (escaped) {
1✔
2656
        builder.append("} ");
×
2657
      }
2658

2659
    } else if (expression instanceof SignedExpression) {
1✔
2660
      SignedExpression signedExpression = (SignedExpression) expression;
1✔
2661
      appendOperator(builder, outputFormat, String.valueOf(signedExpression.getSign()), "", " ");
1✔
2662

2663
      appendExpression(signedExpression.getExpression(), null, builder, indent, i, n, false,
1✔
2664
          BreakLine.NEVER);
2665

2666
    } else if (expression instanceof Select) {
1✔
2667
      Select select = (Select) expression;
1✔
2668
      appendSelect(select, builder, indent, false, false);
1✔
2669

2670
    } else if (expression instanceof RowConstructor) {
1✔
2671
      RowConstructor<?> rowConstructor = (RowConstructor<?>) expression;
×
2672
      appendRowConstructor(builder, indent, rowConstructor);
×
2673

2674
    } else if (expression instanceof MySQLGroupConcat) {
1✔
2675
      MySQLGroupConcat mySQLGroupConcat = (MySQLGroupConcat) expression;
1✔
2676
      appendFunction(builder, outputFormat, "GROUP_CONCAT", "", "( ");
1✔
2677

2678
      int subIndent = getSubIndent(builder, true);
1✔
2679

2680
      if (mySQLGroupConcat.isDistinct()) {
1✔
2681
        appendKeyWord(builder, outputFormat, "DISTINCT", "", " ");
1✔
2682
      }
2683
      appendExpressionsList(mySQLGroupConcat.getExpressionList(), builder, subIndent,
1✔
2684
          BreakLine.AS_NEEDED);
2685
      List<OrderByElement> orderByElements = mySQLGroupConcat.getOrderByElements();
1✔
2686
      appendOrderByElements(orderByElements, builder, subIndent);
1✔
2687

2688
      String separator = mySQLGroupConcat.getSeparator();
1✔
2689
      if (separator != null) {
1✔
2690
        appendNormalizedLineBreak(builder);
1✔
2691
        for (int j = 0; j < subIndent; j++) {
1✔
2692
          builder.append(indentString);
1✔
2693
        }
2694
        appendKeyWord(builder, outputFormat, "SEPARATOR", "", " " + separator);
1✔
2695
      }
2696
      builder.append(" )");
1✔
2697

2698
      // Abstract Class, call last and let the specific implementations catch first
2699
    } else if (expression instanceof BinaryExpression) {
1✔
2700
      BinaryExpression binaryExpression = (BinaryExpression) expression;
1✔
2701
      appendExpression(binaryExpression.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2702
          BreakLine.NEVER);
2703

2704
      if ((i > 0 || breakLine.equals(BreakLine.ALWAYS)) && !breakLine.equals(BreakLine.NEVER)) {
1✔
2705
        appendNormalizedLineBreak(builder);
1✔
2706
        for (int j = 0; j <= indent + 1; j++) {
1✔
2707
          builder.append(indentString);
1✔
2708
        }
2709
      }
2710
      appendOperator(builder, outputFormat, binaryExpression.getStringExpression(), " ", " ");
1✔
2711

2712
      appendExpression(binaryExpression.getRightExpression(), null, builder, indent + 1, i, n,
1✔
2713
          false, BreakLine.NEVER);
2714

2715
    } else if (expression instanceof ExpressionList) {
1✔
2716
      ExpressionList<?> expressions = (ExpressionList<?>) expression;
1✔
2717
      appendExpressionList(expressions, builder, indent, BreakLine.AS_NEEDED);
1✔
2718
    } else if (expression instanceof AllTableColumns) {
1✔
2719
      AllTableColumns allTableColumns = (AllTableColumns) expression;
1✔
2720
      appendObjectName(builder, outputFormat, allTableColumns.getTable().getFullyQualifiedName(),
1✔
2721
          "", ".*");
2722

2723
      if (allTableColumns.getExceptColumns() != null
1✔
2724
          && !allTableColumns.getExceptColumns().isEmpty()) {
×
2725
        appendKeyWord(builder, outputFormat, "EXCEPT", " ", "( ");
×
2726
        appendExpressionsList(allTableColumns.getExceptColumns(), builder, indent,
×
2727
            BreakLine.AS_NEEDED);
2728
        builder.append(" )");
×
2729
      }
2730

2731
      if (allTableColumns.getReplaceExpressions() != null
1✔
2732
          && !allTableColumns.getReplaceExpressions().isEmpty()) {
×
2733
        appendKeyWord(builder, outputFormat, "REPLACE", " ", "( ");
×
2734
        int subIndent = getSubIndent(builder, allTableColumns.getReplaceExpressions().size() > 3);
×
2735
        appendSelectItemList(allTableColumns.getReplaceExpressions(), builder, subIndent, i,
×
2736
            BreakLine.AS_NEEDED, indent);
2737
        builder.append(" )");
×
2738
      }
2739
    } else if (expression instanceof AllColumns) {
1✔
2740
      AllColumns allColumns = (AllColumns) expression;
1✔
2741
      appendObjectName(builder, outputFormat, "*", "", "");
1✔
2742

2743
      if (allColumns.getExceptColumns() != null && !allColumns.getExceptColumns().isEmpty()) {
1✔
2744
        appendKeyWord(builder, outputFormat, "EXCEPT", " ", "( ");
×
2745
        appendExpressionsList(allColumns.getExceptColumns(), builder, indent, BreakLine.AS_NEEDED);
×
2746
        builder.append(" )");
×
2747
      }
2748

2749
      if (allColumns.getReplaceExpressions() != null
1✔
2750
          && !allColumns.getReplaceExpressions().isEmpty()) {
×
2751
        appendKeyWord(builder, outputFormat, "REPLACE", " ", "( ");
×
2752
        int subIndent = getSubIndent(builder, allColumns.getReplaceExpressions().size() > 3);
×
2753
        appendSelectItemList(allColumns.getReplaceExpressions(), builder, subIndent, i,
×
2754
            BreakLine.AS_NEEDED, indent);
2755
        builder.append(" )");
×
2756
      }
2757

2758
    } else if (expression instanceof IntervalExpression) {
1✔
2759
      IntervalExpression intervalExpression = (IntervalExpression) expression;
1✔
2760
      if (intervalExpression.isUsingIntervalKeyword()) {
1✔
2761
        appendKeyWord(builder, outputFormat, "INTERVAL", "", " ");
1✔
2762
      }
2763
      if (intervalExpression.getExpression() != null) {
1✔
2764
        appendExpression(intervalExpression.getExpression(), null, builder, indent, i, n, false,
×
2765
            breakLine);
2766
      } else {
2767
        appendValue(builder, outputFormat, intervalExpression.getParameter(), "", "");
1✔
2768
      }
2769
      if (intervalExpression.getIntervalType() != null) {
1✔
2770
        appendKeyWord(builder, outputFormat, intervalExpression.getIntervalType(), " ", "");
1✔
2771
      }
2772
    } else if (expression instanceof CastExpression) {
1✔
2773
      CastExpression castExpression = (CastExpression) expression;
1✔
2774

2775
      if (castExpression.isUseCastKeyword()) {
1✔
2776
        if (castExpression.getColumnDefinitions().size() > 1) {
1✔
2777
          appendFunction(builder, outputFormat, castExpression.keyword, " ", "( ");
×
2778
          appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, true,
×
2779
              BreakLine.AS_NEEDED);
2780
          appendKeyWord(builder, outputFormat, "AS ROW ( ", " ", " ");
×
2781
          int j = 0;
×
2782
          for (ColumnDefinition columnDefinition : castExpression.getColumnDefinitions()) {
×
2783
            if (j++ > 0) {
×
2784
              builder.append(", ");
×
2785
            }
2786
            appendKeyWord(builder, outputFormat, columnDefinition.toString(), "", "");
×
2787
          }
×
2788
        } else {
×
2789
          appendFunction(builder, outputFormat, castExpression.keyword, " ", "( ");
1✔
2790
          appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, true,
1✔
2791
              BreakLine.AS_NEEDED);
2792
          appendKeyWord(builder, outputFormat, "AS " + castExpression.getColDataType().toString(),
1✔
2793
              " ", " )");
2794
        }
2795
      } else {
2796
        appendExpression(castExpression.getLeftExpression(), null, builder, indent, i, n, true,
1✔
2797
            BreakLine.AS_NEEDED);
2798
        appendKeyWord(builder, outputFormat, castExpression.getColDataType().toString(), "::", "");
1✔
2799
      }
2800
    } else if (expression instanceof Between) {
1✔
2801
      Between between = (Between) expression;
1✔
2802
      appendExpression(between.getLeftExpression(), null, builder, indent + 1, i, n, false,
1✔
2803
          BreakLine.NEVER);
2804

2805
      int subIndent = getSubIndent(builder, false);
1✔
2806
      appendKeyWord(builder, outputFormat, "BETWEEN", between.isNot() ? " NOT " : " ", " ");
1✔
2807

2808
      appendExpression(between.getBetweenExpressionStart(), null, builder, indent + 1, i, n, false,
1✔
2809
          BreakLine.NEVER);
2810

2811
      appendNormalizedLineBreak(builder);
1✔
2812
      for (int j = 0; j <= subIndent; j++) {
1✔
2813
        builder.append(indentString);
1✔
2814
      }
2815
      appendKeyWord(builder, outputFormat, "AND", " ", " ");
1✔
2816

2817
      appendExpression(between.getBetweenExpressionEnd(), null, builder, indent + 1, i, n, false,
1✔
2818
          BreakLine.NEVER);
2819
    } else if (expression instanceof ArrayConstructor) {
1✔
2820
      ArrayConstructor arrayConstructor = (ArrayConstructor) expression;
1✔
2821
      if (arrayConstructor.isArrayKeyword()) {
1✔
2822
        appendKeyWord(builder, outputFormat, "ARRAY", " ", "");
×
2823
      }
2824

2825
      boolean multiline = false;
1✔
2826
      for (Expression p:arrayConstructor.getExpressions()) {
1✔
2827
        if (
1✔
2828
                p instanceof ArrayConstructor
2829
                        || p instanceof ExpressionList
2830
                        || p instanceof Select
2831
                        || p instanceof StructType
2832
        ) {
2833
          multiline = true;
1✔
2834
          break;
1✔
2835
        }
2836
      }
×
2837

2838
      int subIndent = getSubIndent(builder, true);
1✔
2839
      builder.append("[ ");
1✔
2840
      if (multiline) {
1✔
2841
        appendExpressionList(arrayConstructor.getExpressions(), builder, subIndent, BreakLine.ALWAYS);
1✔
2842
        appendNormalizedLineBreak(builder);
1✔
2843
        for (int j = 0; j < subIndent; j++) {
1✔
2844
          builder.append(indentString);
1✔
2845
        }
2846
      } else {
2847
        appendExpressionList(arrayConstructor.getExpressions(), builder, subIndent, BreakLine.NEVER);
×
2848
      }
2849
      builder.append("]");
1✔
2850
    } else {
1✔
2851
      LOGGER
1✔
2852
          .warning("Unhandled expression: " + expression.getClass().getName() + " = " + expression);
1✔
2853
      builder.append(expression);
1✔
2854
    }
2855

2856
    if (alias != null) {
1✔
2857
      appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2858
      if (alias.isUseAs()) {
1✔
2859
        appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2860
      }
2861
      appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2862
    }
2863

2864
    switch (separation) {
1✔
2865
      case AFTER:
2866
        appendNormalizingTrailingWhiteSpace(builder, commaSeparated && i < n - 1 ? ", " : "");
1✔
2867
        break;
2868
    }
2869
  }
1✔
2870

2871
  private static void appendExpressionList(ExpressionList<?> expressionList, StringBuilder builder,
2872
      int indent, BreakLine breakLine) {
2873
    if (expressionList instanceof ParenthesedExpressionList) {
1✔
2874
      builder.append("( ");
1✔
2875
    }
2876
    appendExpressionsList(expressionList, builder, indent, breakLine);
1✔
2877
    if (expressionList instanceof ParenthesedExpressionList) {
1✔
2878
      builder.append(" )");
1✔
2879
    }
2880
  }
1✔
2881

2882
  private static void appendExpressionsList(List<? extends Expression> expressions,
2883
      StringBuilder builder, int indent, BreakLine breakLine) {
2884
    int size = expressions.size();
1✔
2885
    int subIndent =
2886
        breakLine.equals(BreakLine.NEVER) || breakLine.equals(BreakLine.AS_NEEDED) && size <= 3
1✔
2887
            || size == 1 ? indent : getSubIndent(builder, true);
1✔
2888

2889
    int i = 0;
1✔
2890
    for (Expression expression : expressions) {
1✔
2891
      switch (breakLine) {
1✔
2892
        case AS_NEEDED:
2893
          BreakLine bl =
2894
              size == 4 || size >= 5 && i % 3 == 0 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
2895
          appendExpression(expression, null, builder, subIndent, i, expressions.size(), true, bl);
1✔
2896
          break;
1✔
2897

2898
        default:
2899
          appendExpression(expression, null, builder, subIndent, i, expressions.size(), true,
1✔
2900
              breakLine);
2901
      }
2902
      i++;
1✔
2903
    }
1✔
2904
  }
1✔
2905

2906
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2907
  private static void appendFromItem(FromItem fromItem, StringBuilder builder, int indent, int i,
2908
      int n) {
2909

2910
    if (fromItem != null) {
1✔
2911
      if (i > 0) {
1✔
2912
        appendNormalizedLineBreak(builder);
×
2913
        for (int j = 0; j <= indent; j++) {
×
2914
          builder.append(indentString);
×
2915
        }
2916
      }
2917

2918
      switch (separation) {
1✔
2919
        case BEFORE:
2920
          builder.append(i > 0 ? ", " : "");
1✔
2921
      }
2922

2923
      Alias alias = fromItem.getAlias();
1✔
2924

2925
      // All Known Implementing Classes: LateralSubSelect, ParenthesisFromItem,
2926
      // SpecialSubSelect, SubJoin, SubSelect, Table, TableFunction, ValuesList
2927
      if (fromItem instanceof Table) {
1✔
2928
        Table table = (Table) fromItem;
1✔
2929
        appendTable(table, alias, builder);
1✔
2930
      } else if (fromItem instanceof Select) {
1✔
2931
        Select select = (Select) fromItem;
1✔
2932
        appendSelect(select, builder, indent, false, true);
1✔
2933

2934
        if (alias != null) {
1✔
2935
          appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2936
          if (alias.isUseAs()) {
1✔
2937
            appendKeyWord(builder, outputFormat, "AS", "", " ");
×
2938
          }
2939

2940
          appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2941
        }
2942
      } else if (fromItem instanceof ParenthesedFromItem) {
1✔
2943
        ParenthesedFromItem parenthesedFromItem = (ParenthesedFromItem) fromItem;
1✔
2944
        builder.append("( ");
1✔
2945
        int subIndent = getSubIndent(builder, true);
1✔
2946

2947
        appendFromItem(parenthesedFromItem.getFromItem(), builder, indent, i, n);
1✔
2948
        List<Join> joins = parenthesedFromItem.getJoins();
1✔
2949
        appendJoins(joins, builder, subIndent);
1✔
2950
        builder.append(" )");
1✔
2951

2952
        if (alias != null) {
1✔
2953
          appendNormalizingTrailingWhiteSpace(builder, " ");
×
2954
          if (alias.isUseAs()) {
×
2955
            appendKeyWord(builder, outputFormat, "AS", "", " ");
×
2956
          }
2957

2958
          appendAlias(builder, outputFormat, alias.getName(), "", " ");
×
2959
        }
2960
      } else {
1✔
2961
        LOGGER.log(Level.WARNING, "FROM Item not covered: " + fromItem.getClass().getName());
×
2962
      }
2963

2964
      switch (separation) {
1✔
2965
        case AFTER:
2966
          appendNormalizingTrailingWhiteSpace(builder, i < n - 1 ? ", " : "");
1✔
2967
          break;
2968
      }
2969
    }
2970
  }
1✔
2971

2972
  private static void appendTable(Table table, Alias alias, StringBuilder builder) {
2973

2974
    if (table != null) {
1✔
2975
      appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
2976
      if (alias != null) {
1✔
2977
        appendNormalizingTrailingWhiteSpace(builder, " ");
1✔
2978
        if (alias.isUseAs()) {
1✔
2979
          appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
2980
        }
2981

2982
        appendAlias(builder, outputFormat, alias.getName(), "", " ");
1✔
2983
      }
2984
    }
2985
  }
1✔
2986

2987
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
2988
  private static void appendSetOperation(SetOperation setOperation, StringBuilder builder,
2989
      int indent) {
2990

2991
    // Direct Known Subclasses:
2992
    // ExceptOp, IntersectOp, MinusOp, UnionOp
2993
    if (setOperation instanceof UnionOp) {
1✔
2994
      UnionOp unionOp = (UnionOp) setOperation;
1✔
2995

2996
      appendNormalizedLineBreak(builder);
1✔
2997
      for (int j = 0; j < indent; j++) {
1✔
2998
        builder.append(indentString);
1✔
2999
      }
3000
      appendOperator(builder, outputFormat, "UNION", "", " ");
1✔
3001

3002
      if (unionOp.isAll()) {
1✔
3003
        appendOperator(builder, outputFormat, "ALL", "", " ");
1✔
3004
      }
3005
    } else if (setOperation instanceof MinusOp) {
1✔
3006
      appendNormalizedLineBreak(builder);
1✔
3007
      for (int j = 0; j < indent; j++) {
1✔
3008
        builder.append(indentString);
1✔
3009
      }
3010
      appendOperator(builder, outputFormat, "MINUS", "", " ");
1✔
3011
    } else if (setOperation instanceof IntersectOp) {
×
3012
      appendNormalizedLineBreak(builder);
×
3013
      for (int j = 0; j < indent; j++) {
×
3014
        builder.append(indentString);
×
3015
      }
3016
      appendOperator(builder, outputFormat, "INTERSECT", "", " ");
×
3017

3018
    } else if (setOperation instanceof ExceptOp) {
×
3019
      appendNormalizedLineBreak(builder);
×
3020
      for (int j = 0; j < indent; j++) {
×
3021
        builder.append(indentString);
×
3022
      }
3023
      appendOperator(builder, outputFormat, "EXCEPT", "", " ");
×
3024
    } else if (setOperation != null) {
×
3025
      throw new UnsupportedOperationException(
×
3026
          setOperation.getClass().getName() + " is not supported yet.");
×
3027
    }
3028
  }
1✔
3029

3030
  private static void appendTruncate(StringBuilder builder, Truncate truncate) {
3031
    Table table = truncate.getTable();
1✔
3032
    boolean cascade = truncate.getCascade();
1✔
3033

3034
    appendKeyWord(builder, outputFormat, "TRUNCATE TABLE", "", " ")
1✔
3035
        .append(table.getFullyQualifiedName());
1✔
3036
    if (cascade) {
1✔
3037
      appendOperator(builder, outputFormat, "CASCADE", " ", "");
1✔
3038
    }
3039
  }
1✔
3040

3041
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
3042
  private static void appendCreateTable(StringBuilder builder, CreateTable createTable,
3043
      int indent) {
3044

3045
    int i = 0;
1✔
3046
    appendNormalizedLineBreak(builder);
1✔
3047

3048
    List<String> createOptionsString = createTable.getCreateOptionsStrings();
1✔
3049
    String createOps = createOptionsString != null && !createOptionsString.isEmpty()
1✔
3050
        ? PlainSelect.getStringList(createOptionsString, false, false)
×
3051
        : null;
1✔
3052

3053
    boolean unlogged = createTable.isUnlogged();
1✔
3054
    boolean ifNotExists = createTable.isIfNotExists();
1✔
3055

3056
    Table table = createTable.getTable();
1✔
3057
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3058

3059
    if (unlogged) {
1✔
3060
      appendHint(builder, outputFormat, "UNLOGGED", "", " ");
×
3061
    }
3062

3063
    if (createOps != null) {
1✔
3064
      appendHint(builder, outputFormat, createOps, "", " ");
×
3065
    }
3066

3067
    appendKeyWord(builder, outputFormat, "TABLE", "", " ");
1✔
3068

3069
    if (ifNotExists) {
1✔
3070
      appendHint(builder, outputFormat, "IF NOT EXISTS", "", " ");
×
3071
    }
3072

3073
    appendAlias(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3074

3075
    List<ColumnDefinition> columnDefinitions = createTable.getColumnDefinitions();
1✔
3076
    List<Index> indexes = createTable.getIndexes();
1✔
3077

3078
    if (columnDefinitions != null && !columnDefinitions.isEmpty()) {
1✔
3079
      builder.append(" (");
1✔
3080

3081
      int colWidth = 0;
1✔
3082
      int typeWidth = 0;
1✔
3083

3084
      for (ColumnDefinition columnDefinition : columnDefinitions) {
1✔
3085
        String columnName = columnDefinition.getColumnName();
1✔
3086
        // @todo: please get rid of that Replace workaround
3087
        String colDataType = columnDefinition.getColDataType().toString().replace(", ", ",");
1✔
3088

3089
        if (colWidth < columnName.length()) {
1✔
3090
          colWidth = columnName.length();
1✔
3091
        }
3092

3093
        if (typeWidth < colDataType.length()) {
1✔
3094
          typeWidth = colDataType.length();
1✔
3095
        }
3096
      }
1✔
3097

3098
      // int typeIndex = (((indent +1)* indentString.length() + colWidth + 1) /
3099
      // indentString.length()) * ("
3100
      // ".length() + 1);
3101

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

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

3106
      for (ColumnDefinition columnDefinition : columnDefinitions) {
1✔
3107
        appendNormalizedLineBreak(builder);
1✔
3108
        for (int j = 0; j <= indent; j++) {
1✔
3109
          builder.append(indentString);
1✔
3110
        }
3111

3112
        String columnName = columnDefinition.getColumnName();
1✔
3113
        ColDataType colDataType = columnDefinition.getColDataType();
1✔
3114
        List<String> columnSpecs = columnDefinition.getColumnSpecs();
1✔
3115

3116
        switch (separation) {
1✔
3117
          case BEFORE:
3118
            builder.append(i > 0 ? ", " : "");
1✔
3119
        }
3120

3121
        appendObjectName(builder, outputFormat, columnName, "", "");
1✔
3122

3123
        int lastLineLength = getLastLineLength(builder);
1✔
3124

3125
        for (int j = lastLineLength; j <= typeIndex * indentWidth; j++) {
1✔
3126
          builder.append(" ");
1✔
3127
        }
3128
        // @todo: please get rid of that Replace workaround
3129
        appendType(builder, outputFormat, colDataType.toString().replace(", ", ","), "", "");
1✔
3130

3131
        lastLineLength = getLastLineLength(builder);
1✔
3132

3133
        if (columnSpecs != null && !columnSpecs.isEmpty()) {
1✔
3134
          for (int j = lastLineLength; j <= specIndex * indentWidth; j++) {
1✔
3135
            builder.append(" ");
1✔
3136
          }
3137
          appendType(builder, outputFormat, PlainSelect.getStringList(columnSpecs, false, false),
1✔
3138
              "", "");
3139
        }
3140

3141
        switch (separation) {
1✔
3142
          case AFTER:
3143
            appendNormalizingTrailingWhiteSpace(builder,
1✔
3144
                i < columnDefinitions.size() + indexes.size() - 1 ? ", " : "");
1✔
3145
            break;
3146
        }
3147

3148
        i++;
1✔
3149
      }
1✔
3150

3151
      // Direct Known Subclasses:
3152
      // ExcludeConstraint, NamedConstraint
3153

3154
      // Direct Known Subclasses:
3155
      // CheckConstraint, ForeignKeyIndex
3156

3157
      if (indexes != null && !indexes.isEmpty()) {
1✔
3158
        for (Index index : indexes) {
1✔
3159
          appendNormalizedLineBreak(builder);
1✔
3160
          for (int j = 0; j <= indent; j++) {
1✔
3161
            builder.append(indentString);
1✔
3162
          }
3163

3164
          switch (separation) {
1✔
3165
            case BEFORE:
3166
              builder.append(i > 0 ? ", " : "");
1✔
3167
          }
3168

3169
          if (index instanceof ForeignKeyIndex) {
1✔
3170
            ForeignKeyIndex foreignKeyIndex = (ForeignKeyIndex) index;
1✔
3171

3172
            String type = foreignKeyIndex.getType();
1✔
3173
            String name = foreignKeyIndex.getName();
1✔
3174
            List<String> columnsNames = foreignKeyIndex.getColumnsNames();
1✔
3175
            List<String> idxSpec = foreignKeyIndex.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

3190
            appendKeyWord(builder, outputFormat, type, "", " ");
1✔
3191

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

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

3203
            Table foreignTable = foreignKeyIndex.getTable();
1✔
3204
            List<String> referencedColumnNames = foreignKeyIndex.getReferencedColumnNames();
1✔
3205

3206
            appendNormalizedLineBreak(builder);
1✔
3207
            for (int j = 0; j <= indent + 1; j++) {
1✔
3208
              builder.append(indentString);
1✔
3209
            }
3210
            appendKeyWord(builder, outputFormat, "REFERENCES", "", " ");
1✔
3211
            appendObjectName(builder, outputFormat, foreignTable.getFullyQualifiedName(), "", " ");
1✔
3212

3213
            builder.append("( ");
1✔
3214
            subIndent = getSubIndent(builder, referencedColumnNames.size() > 2);
1✔
3215
            bl = referencedColumnNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3216
            appendStringList(referencedColumnNames, builder, subIndent, true, bl);
1✔
3217
            builder.append(" )");
1✔
3218

3219
            ReferentialAction updateAction =
1✔
3220
                foreignKeyIndex.getReferentialAction(ReferentialAction.Type.UPDATE);
1✔
3221
            if (updateAction != null) {
1✔
3222
              appendNormalizedLineBreak(builder);
×
3223
              for (int j = 0; j <= indent + 2; j++) {
×
3224
                builder.append(indentString);
×
3225
              }
3226
              builder.append(updateAction);
×
3227
            }
3228

3229
            ReferentialAction deleteAction =
1✔
3230
                foreignKeyIndex.getReferentialAction(ReferentialAction.Type.DELETE);
1✔
3231
            if (deleteAction != null) {
1✔
3232
              appendNormalizedLineBreak(builder);
×
3233
              for (int j = 0; j <= indent + 2; j++) {
×
3234
                builder.append(indentString);
×
3235
              }
3236
              builder.append(deleteAction);
×
3237
            }
3238

3239
          } else if (index instanceof CheckConstraint) {
1✔
3240
            CheckConstraint checkConstraint = (CheckConstraint) index;
×
3241

3242
            String contraintName = checkConstraint.getName();
×
3243
            Expression expression = checkConstraint.getExpression();
×
3244

3245
            appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
×
3246
            appendAlias(builder, outputFormat, contraintName, "", "");
×
3247

3248
            appendNormalizedLineBreak(builder);
×
3249
            for (int j = 0; j <= indent + 1; j++) {
×
3250
              builder.append(indentString);
×
3251
            }
3252

3253
            builder.append(" CHECK (").append(expression).append(")");
×
3254

3255
          } else if (index instanceof NamedConstraint) {
1✔
3256
            NamedConstraint namedConstraint = (NamedConstraint) index;
1✔
3257

3258
            String type = namedConstraint.getType();
1✔
3259
            String name = namedConstraint.getName();
1✔
3260
            List<String> columnsNames = namedConstraint.getColumnsNames();
1✔
3261
            List<String> idxSpec = namedConstraint.getIndexSpec();
1✔
3262
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
1✔
3263

3264
            // @todo: beautify the expression
3265
            // @todo: add a test case
3266
            if (name != null) {
1✔
3267
              appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
1✔
3268
              appendAlias(builder, outputFormat, name, "", "");
1✔
3269
              appendNormalizedLineBreak(builder);
1✔
3270
            }
3271

3272
            for (int j = 0; name != null && j <= indent + 1; j++) {
1✔
3273
              builder.append(indentString);
1✔
3274
            }
3275
            appendKeyWord(builder, outputFormat, type, "", " ");
1✔
3276

3277
            builder.append("( ");
1✔
3278
            int subIndent = getSubIndent(builder, columnsNames.size() > 2);
1✔
3279
            BreakLine bl = columnsNames.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3280
            appendStringList(columnsNames, builder, subIndent, true, bl);
1✔
3281
            builder.append(" )");
1✔
3282

3283
            // @todo: add a test case for this
3284
            if (idxSpec != null && !idxSpecText.isEmpty()) {
1✔
3285
              appendHint(builder, outputFormat, idxSpecText, " ", "");
×
3286
            }
3287

3288
          } else if (index instanceof ExcludeConstraint) {
1✔
3289
            ExcludeConstraint excludeConstraint = (ExcludeConstraint) index;
×
3290
            Expression expression = excludeConstraint.getExpression();
×
3291

3292
            // @todo: beautify the expression
3293
            // @todo: add a test case
3294

3295
            builder.append("EXCLUDE WHERE ");
×
3296
            builder.append("(");
×
3297
            builder.append(expression);
×
3298
            builder.append(")");
×
3299

3300
          } else if (index != null) {
×
3301
            String type = index.getType();
×
3302
            String name = index.getName();
×
3303
            List<Index.ColumnParams> columnParams = index.getColumns();
×
3304
            List<String> idxSpec = index.getIndexSpec();
×
3305
            String idxSpecText = PlainSelect.getStringList(idxSpec, false, false);
×
3306

3307
            builder.append(type);
×
3308

3309
            appendKeyWord(builder, outputFormat, type, "", " ");
×
3310
            if (name != null) {
×
3311
              appendAlias(builder, outputFormat, name, "", "");
×
3312
            }
3313

3314
            builder.append(" ").append(PlainSelect.getStringList(columnParams, true, true))
×
3315
                .append(!idxSpecText.isEmpty() ? " " + idxSpecText : "");
×
3316
          }
3317

3318
          switch (separation) {
1✔
3319
            case AFTER:
3320
              appendNormalizingTrailingWhiteSpace(builder,
1✔
3321
                  i < columnDefinitions.size() + indexes.size() - 1 ? ", " : "");
1✔
3322
              break;
3323
          }
3324

3325
          i++;
1✔
3326
        }
1✔
3327
      }
3328
      appendNormalizedLineBreak(builder).append(")");
1✔
3329
    }
3330
    List<String> tableOptionsStrings = createTable.getTableOptionsStrings();
1✔
3331
    String options = PlainSelect.getStringList(tableOptionsStrings, false, false);
1✔
3332
    if (options != null && !options.isEmpty()) {
1✔
3333
      appendHint(builder, outputFormat, options, " ", "");
1✔
3334
    }
3335

3336
    RowMovement rowMovement = createTable.getRowMovement();
1✔
3337
    if (rowMovement != null) {
1✔
3338
      // @todo: beautify this part
3339
      // @todo: provide test cases
3340
      builder.append(" ").append(rowMovement.getMode()).append(" ROW MOVEMENT");
×
3341
    }
3342

3343
    Select select = createTable.getSelect();
1✔
3344
    if (select != null) {
1✔
3345

3346
      appendNormalizedLineBreak(builder);
1✔
3347
      for (int j = 0; j <= indent; j++) {
1✔
3348
        builder.append(indentString);
1✔
3349
      }
3350
      builder.append("AS ");
1✔
3351
      appendSelect(select, builder, indent, false, false);
1✔
3352
    }
3353

3354
    Table likeTable = createTable.getLikeTable();
1✔
3355
    if (likeTable != null) {
1✔
3356

3357
      builder.append(" AS ");
×
3358

3359
      Alias alias = likeTable.getAlias();
×
3360

3361
      appendTable(likeTable, alias, builder);
×
3362
    }
3363
  }
1✔
3364

3365
  @SuppressWarnings({"PMD.CyclomaticComplexity"})
3366
  private static void appendCreateIndex(StringBuilder builder, CreateIndex createIndex,
3367
      int indent) {
3368

3369
    Index index = createIndex.getIndex();
1✔
3370
    Table table = createIndex.getTable();
1✔
3371

3372
    List<String> tailParameters = createIndex.getTailParameters();
1✔
3373
    List<Index.ColumnParams> columnsParameters = index.getColumns();
1✔
3374

3375
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3376

3377
    if (index.getType() != null) {
1✔
3378
      appendKeyWord(builder, outputFormat, index.getType(), "", " ");
1✔
3379
    }
3380

3381
    appendKeyWord(builder, outputFormat, "INDEX", "", " ");
1✔
3382
    if (createIndex.isUsingIfNotExists()) {
1✔
3383
      appendKeyWord(builder, outputFormat, "IF NOT EXISTS", "", " ");
×
3384
    }
3385
    appendAlias(builder, outputFormat, index.getName(), "", "");
1✔
3386

3387
    appendNormalizedLineBreak(builder);
1✔
3388
    for (int j = 0; j <= indent; j++) {
1✔
3389
      builder.append(indentString);
1✔
3390
    }
3391
    appendKeyWord(builder, outputFormat, "ON", "", " ");
1✔
3392
    appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3393

3394
    if (index.getUsing() != null) {
1✔
3395
      appendKeyWord(builder, outputFormat, "USING", "  ", " ");
×
3396
      builder.append(index.getUsing());
×
3397
    }
3398

3399
    if (index.getColumnsNames() != null) {
1✔
3400
      builder.append("( ");
1✔
3401

3402
      int subIndent = getSubIndent(builder, columnsParameters.size() > 2);
1✔
3403
      BreakLine bl = columnsParameters.size() > 2 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3404

3405
      int i = 0;
1✔
3406
      for (Index.ColumnParams param : columnsParameters) {
1✔
3407
        appendString(param.getColumnName(), builder, subIndent, i, columnsParameters.size(), true,
1✔
3408
            bl);
3409
        i++;
1✔
3410
      }
1✔
3411

3412
      builder.append(" )");
1✔
3413

3414
      if (tailParameters != null) {
1✔
3415
        builder.append(" ");
1✔
3416
        for (String param : tailParameters) {
1✔
3417
          appendHint(builder, outputFormat, param, "", " ");
1✔
3418
        }
1✔
3419
      }
3420
    }
3421
  }
1✔
3422

3423
  private static void appendCreateView(StringBuilder builder, CreateView createView, int indent) {
3424
    boolean isOrReplace = createView.isOrReplace();
1✔
3425
    ForceOption force = createView.getForce();
1✔
3426
    TemporaryOption temp = createView.getTemporary();
1✔
3427
    boolean isMaterialized = createView.isMaterialized();
1✔
3428

3429
    Table view = createView.getView();
1✔
3430

3431
    ExpressionList<Column> columnNames = createView.getColumnNames();
1✔
3432
    Select select = createView.getSelect();
1✔
3433
    boolean isWithReadOnly = createView.isWithReadOnly();
1✔
3434

3435
    appendNormalizedLineBreak(builder);
1✔
3436

3437
    appendKeyWord(builder, outputFormat, "CREATE", "", " ");
1✔
3438
    if (isOrReplace) {
1✔
3439
      appendKeyWord(builder, outputFormat, "OR REPLACE", "", " ");
1✔
3440
    }
3441
    switch (force) {
1✔
3442
      case FORCE:
3443
        appendKeyWord(builder, outputFormat, "FORCE", "", " ");
×
3444
        break;
×
3445
      case NO_FORCE:
3446
        appendKeyWord(builder, outputFormat, "NO FORCE", "", " ");
×
3447
        break;
3448
    }
3449

3450
    if (temp != TemporaryOption.NONE) {
1✔
3451
      builder.append(temp.name()).append(" ");
×
3452
    }
3453

3454
    if (isMaterialized) {
1✔
3455
      appendKeyWord(builder, outputFormat, "MATERIALIZED", "", " ");
×
3456
    }
3457
    appendKeyWord(builder, outputFormat, "VIEW", "", " ");
1✔
3458
    appendAlias(builder, outputFormat, view.getFullyQualifiedName(), "", "");
1✔
3459
    if (columnNames != null) {
1✔
3460
      builder.append(PlainSelect.getStringList(columnNames, true, true));
×
3461
    }
3462

3463
    appendNormalizedLineBreak(builder);
1✔
3464
    for (int j = 0; j <= indent; j++) {
1✔
3465
      builder.append(indentString);
1✔
3466
    }
3467
    appendKeyWord(builder, outputFormat, "AS", "", " ");
1✔
3468
    appendSelect(select, builder, indent + 2, false, false);
1✔
3469

3470
    if (isWithReadOnly) {
1✔
3471
      builder.append(" WITH READ ONLY");
×
3472
      appendHint(builder, outputFormat, "WITH READ ONLY", " ", "");
×
3473
    }
3474
  }
1✔
3475

3476
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
3477
  private static void appendAlter(StringBuilder builder, Alter alter, int indent) {
3478
    boolean useOnly = alter.isUseOnly();
1✔
3479
    Table table = alter.getTable();
1✔
3480
    List<AlterExpression> alterExpressions = alter.getAlterExpressions();
1✔
3481

3482
    appendKeyWord(builder, outputFormat, "ALTER TABLE", "", " ");
1✔
3483
    if (useOnly) {
1✔
3484
      appendKeyWord(builder, outputFormat, "ONLY", "", " ");
×
3485
    }
3486
    appendObjectName(builder, outputFormat, table.getFullyQualifiedName(), "", "");
1✔
3487
    int i = 0;
1✔
3488

3489
    if (alterExpressions != null) {
1✔
3490
      for (AlterExpression alterExpression : alterExpressions) {
1✔
3491
        if (i > 0) {
1✔
3492
          appendNormalizedLineBreak(builder);
×
3493
          for (int j = 0; j <= indent; j++) {
×
3494
            builder.append(indentString);
×
3495
          }
3496
        }
3497

3498
        switch (separation) {
1✔
3499
          case BEFORE:
3500
            builder.append(i > 0 ? ", " : "");
1✔
3501
        }
3502

3503
        AlterOperation operation = alterExpression.getOperation();
1✔
3504
        String commentText = alterExpression.getCommentText();
1✔
3505
        String columnName = alterExpression.getColumnName();
1✔
3506
        String columnOldName = alterExpression.getColumnOldName();
1✔
3507

3508
        List<AlterExpression.ColumnDataType> colDataTypeList = alterExpression.getColDataTypeList();
1✔
3509
        String optionalSpecifier = alterExpression.getOptionalSpecifier();
1✔
3510

3511
        List<AlterExpression.ColumnDropNotNull> columnDropNotNullList =
1✔
3512
            alterExpression.getColumnDropNotNullList();
1✔
3513

3514
        String constraintName = alterExpression.getConstraintName();
1✔
3515
        boolean constraintIfExists = alterExpression.isUsingIfExists();
1✔
3516

3517
        List<String> pkColumns = alterExpression.getPkColumns();
1✔
3518
        List<String> ukColumns = alterExpression.getUkColumns();
1✔
3519
        String ukName = alterExpression.getUkName();
1✔
3520
        boolean uk = alterExpression.getUk();
1✔
3521

3522
        List<String> fkColumns = alterExpression.getFkColumns();
1✔
3523
        String fkSourceTable = alterExpression.getFkSourceTable();
1✔
3524
        List<String> fkSourceColumns = alterExpression.getFkSourceColumns();
1✔
3525

3526
        ReferentialAction deleteAction =
1✔
3527
            alterExpression.getReferentialAction(ReferentialAction.Type.DELETE);
1✔
3528
        ReferentialAction updateAction =
1✔
3529
            alterExpression.getReferentialAction(ReferentialAction.Type.UPDATE);
1✔
3530

3531
        Index index = alterExpression.getIndex();
1✔
3532

3533
        List<ConstraintState> constraints = alterExpression.getConstraints();
1✔
3534
        boolean useEqual = alterExpression.getUseEqual();
1✔
3535

3536
        List<String> parameters = alterExpression.getParameters();
1✔
3537

3538
        appendNormalizedLineBreak(builder);
1✔
3539

3540
        for (int j = 0; j <= indent; j++) {
1✔
3541
          builder.append(indentString);
1✔
3542
        }
3543
        if (operation == AlterOperation.RENAME_TABLE) {
1✔
3544
          appendKeyWord(builder, outputFormat, "RENAME TO", "", " ");
1✔
3545
          appendObjectName(builder, outputFormat, alterExpression.getNewTableName(), "", "");
1✔
3546
          break;
1✔
3547
        } else {
3548
          appendKeyWord(builder, outputFormat, operation.name(), "", " ");
1✔
3549
        }
3550

3551
        if (commentText != null) {
1✔
3552
          if (columnName != null) {
×
3553
            appendKeyWord(builder, outputFormat, "COMMENT", " ", " ");
×
3554
          }
3555
          builder.append(commentText);
×
3556
        } else if (columnName != null) {
1✔
3557
          if (alterExpression.hasColumn()) {
1✔
3558
            appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
1✔
3559
          }
3560
          if (operation == AlterOperation.RENAME) {
1✔
3561
            appendObjectName(builder, outputFormat, columnOldName, "", "");
1✔
3562
            appendKeyWord(builder, outputFormat, "TO", " ", " ");
1✔
3563
          }
3564
          appendObjectName(builder, outputFormat, columnName, "", "");
1✔
3565

3566
        } else if (operation == AlterOperation.DROP && !alterExpression.hasColumn()
1✔
3567
            && alterExpression.getPkColumns() != null) {
1✔
3568
          // Oracle supports dropping multiple columns
3569
          // we use the PKColumns List in this case instead of the Column
3570

3571
          List<String> columns = alterExpression.getPkColumns();
1✔
3572

3573
          builder.append("(");
1✔
3574

3575
          int subIndent = getSubIndent(builder, columns.size() > 3);
1✔
3576
          BreakLine bl = columns.size() > 3 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3577

3578
          appendStringList(alterExpression.getPkColumns(), builder, subIndent, true, bl);
1✔
3579
          builder.append(" )");
1✔
3580

3581
        } else if (colDataTypeList != null) {
1✔
3582

3583
          int colWidth = 0;
1✔
3584
          int typeWidth = 0;
1✔
3585

3586
          BreakLine breakLine =
3587
              colDataTypeList.size() > 1 ? BreakLine.AFTER_FIRST : BreakLine.NEVER;
1✔
3588

3589
          if (operation == AlterOperation.CHANGE) {
1✔
3590
            if (optionalSpecifier != null) {
×
3591
              builder.append(optionalSpecifier).append(" ");
×
3592
            }
3593
            appendObjectName(builder, outputFormat, columnOldName, "", " ");
×
3594
          } else if (colDataTypeList.size() > 1) {
1✔
3595

3596
            for (ColumnDefinition columnDefinition : colDataTypeList) {
1✔
3597
              String columnName1 = columnDefinition.getColumnName();
1✔
3598
              // @todo: please get rid of that Replace workaround
3599
              String colDataType = columnDefinition.getColDataType().toString().replace(", ", ",");
1✔
3600

3601
              if (colWidth < columnName1.length()) {
1✔
3602
                colWidth = columnName1.length();
1✔
3603
              }
3604

3605
              if (typeWidth < colDataType.length()) {
1✔
3606
                typeWidth = colDataType.length();
1✔
3607
              }
3608
            }
1✔
3609

3610
            builder.append("( ");
1✔
3611

3612
          } else {
3613
            if (alterExpression.hasColumn()) {
1✔
3614
              appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
1✔
3615
            }
3616
          }
3617

3618
          int subIndent = getSubIndent(builder, colDataTypeList.size() > 1);
1✔
3619
          int typeIndex = subIndent + (colWidth / indentString.length()) + 1;
1✔
3620
          int specIndex = indent + typeIndex + (typeWidth / indentString.length()) + 1;
1✔
3621

3622
          for (ColumnDefinition columnDefinition : colDataTypeList) {
1✔
3623
            if (i > 0 || breakLine.equals(BreakLine.ALWAYS)) {
1✔
3624
              if (!breakLine.equals(BreakLine.NEVER)) {
1✔
3625
                appendNormalizedLineBreak(builder);
1✔
3626
                for (int j = 0; j < subIndent; j++) {
1✔
3627
                  builder.append(indentString);
1✔
3628
                }
3629
              }
3630
              builder.append(", ");
1✔
3631
            }
3632

3633
            String columnName1 = columnDefinition.getColumnName();
1✔
3634
            ColDataType colDataType = columnDefinition.getColDataType();
1✔
3635
            List<String> columnSpecs = columnDefinition.getColumnSpecs();
1✔
3636

3637
            appendObjectName(builder, outputFormat, columnName1, "", " ");
1✔
3638

3639
            int lastLineLength = getLastLineLength(builder);
1✔
3640

3641
            for (int j = lastLineLength; j <= typeIndex * indentWidth; j++) {
1✔
3642
              builder.append(" ");
1✔
3643
            }
3644
            // @todo: please get rid of that Replace workaround
3645
            appendType(builder, outputFormat, colDataType.toString().replace(", ", ","), "", "");
1✔
3646

3647
            lastLineLength = getLastLineLength(builder);
1✔
3648

3649
            if (columnSpecs != null && !columnSpecs.isEmpty()) {
1✔
3650
              if (colDataTypeList.size() > 1) {
1✔
3651
                for (int j = lastLineLength; j <= specIndex * indentWidth; j++) {
1✔
3652
                  builder.append(" ");
1✔
3653
                }
3654
              } else {
3655
                builder.append(" ");
1✔
3656
              }
3657

3658
              appendType(builder, outputFormat,
1✔
3659
                  PlainSelect.getStringList(columnSpecs, false, false), "", "");
1✔
3660
            }
3661
            i++;
1✔
3662
          }
1✔
3663

3664
          if (colDataTypeList.size() > 1) {
1✔
3665
            builder.append(")");
1✔
3666
          }
3667
        } else if (columnDropNotNullList != null) {
1✔
3668
          if (operation == AlterOperation.CHANGE) {
×
3669
            if (optionalSpecifier != null) {
×
3670
              builder.append(optionalSpecifier).append(" ");
×
3671
            }
3672
            appendObjectName(builder, outputFormat, columnOldName, "", " ");
×
3673
          } else if (columnDropNotNullList.size() > 1) {
×
3674
            builder.append("(");
×
3675
          } else {
3676
            if (alterExpression.hasColumn()) {
×
3677
              appendKeyWord(builder, outputFormat, "COLUMN", "", " ");
×
3678
            }
3679
          }
3680
          builder.append(PlainSelect.getStringList(columnDropNotNullList));
×
3681
          if (columnDropNotNullList.size() > 1) {
×
3682
            builder.append(")");
×
3683
          }
3684
        } else if (constraintName != null) {
1✔
3685
          appendKeyWord(builder, outputFormat, "CONSTRAINT", "", " ");
×
3686

3687
          if (constraintIfExists) {
×
3688
            appendKeyWord(builder, outputFormat, "IF EXISTS", "", " ");
×
3689
          }
3690
          appendObjectName(builder, outputFormat, constraintName, "", "");
×
3691
        } else if (pkColumns != null) {
1✔
3692
          appendKeyWord(builder, outputFormat, "PRIMARY KEY", "", " (");
×
3693

3694
          builder.append(PlainSelect.getStringList(pkColumns)).append(")");
×
3695
        } else if (ukColumns != null) {
1✔
3696
          appendKeyWord(builder, outputFormat, "UNIQUE", "", "");
×
3697
          if (ukName != null) {
×
3698
            if (uk) {
×
3699
              appendKeyWord(builder, outputFormat, "KEY", " ", " ");
×
3700
            } else {
3701
              appendKeyWord(builder, outputFormat, "INDEX", " ", " ");
×
3702
            }
3703
            appendObjectName(builder, outputFormat, ukName, "", "");
×
3704
          }
3705
          builder.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")");
×
3706
        } else if (fkColumns != null) {
1✔
3707
          appendKeyWord(builder, outputFormat, "FOREIGN KEY", "", " (");
1✔
3708
          builder.append(PlainSelect.getStringList(fkColumns)).append(")");
1✔
3709

3710
          appendNormalizedLineBreak(builder);
1✔
3711
          for (int j = 0; j <= indent + 1; j++) {
1✔
3712
            builder.append(indentString);
1✔
3713
          }
3714

3715
          appendKeyWord(builder, outputFormat, "REFERENCES", "", " ");
1✔
3716

3717
          builder.append(fkSourceTable).append(" (")
1✔
3718
              .append(PlainSelect.getStringList(fkSourceColumns)).append(")");
1✔
3719
          // referentialActions.forEach(b::append);
3720
          if (updateAction != null) {
1✔
3721
            builder.append(updateAction);
×
3722
          }
3723

3724
          if (deleteAction != null) {
1✔
3725
            builder.append(deleteAction);
×
3726
          }
3727
        } else if (index != null) {
×
3728
          builder.append(index);
×
3729
        }
3730
        if (constraints != null && !constraints.isEmpty()) {
1✔
3731
          builder.append(" ").append(PlainSelect.getStringList(constraints, false, false));
×
3732
        }
3733
        if (useEqual) {
1✔
3734
          builder.append("=");
×
3735
        }
3736
        if (parameters != null && !parameters.isEmpty()) {
1✔
3737
          builder.append(" ").append(PlainSelect.getStringList(parameters, false, false));
×
3738
        }
3739

3740
        switch (separation) {
1✔
3741
          case AFTER:
3742
            appendNormalizingTrailingWhiteSpace(builder,
×
3743
                i < alterExpressions.size() - 1 ? ", " : "");
×
3744
            break;
3745
        }
3746

3747
        i++;
1✔
3748
      }
1✔
3749
    }
3750
  }
1✔
3751

3752
  public enum OutputFormat {
1✔
3753
    PLAIN, ANSI, HTML, RTF, XSLFO
1✔
3754
  }
3755

3756
  public enum Spelling {
1✔
3757
    UPPER, LOWER, CAMEL, KEEP
1✔
3758
  }
3759

3760
  public enum Separation {
1✔
3761
    BEFORE, AFTER
1✔
3762
  }
3763

3764
  public enum BreakLine {
1✔
3765
    NEVER // keep all arguments on one line
1✔
3766
    , AS_NEEDED // only when more than 3 arguments
1✔
3767
    , AFTER_FIRST // break all after the first argument
1✔
3768
    , ALWAYS // break all arguments to a new line
1✔
3769
  }
3770

3771
  public enum SquaredBracketQuotation {
1✔
3772
    AUTO, YES, NO
1✔
3773
  }
3774

3775
  public enum ShowLineNumbers {
1✔
3776
    YES, NO
1✔
3777
  }
3778

3779
  public enum BackSlashQuoting {
1✔
3780
    YES, NO
1✔
3781
  }
3782

3783
  public enum FormattingOption {
1✔
3784
    SQUARE_BRACKET_QUOTATION("squareBracketQuotation"), BACKSLASH_QUOTING(
1✔
3785
        "backSlashQuoting"), OUTPUT_FORMAT("outputFormat"), KEYWORD_SPELLING(
1✔
3786
            "keywordSpelling"), FUNCTION_SPELLING("functionSpelling"), OBJECT_SPELLING(
1✔
3787
                "objectSpelling"), SEPARATION("separation"), INDENT_WIDTH(
1✔
3788
                    "indentWidth"), SHOW_LINE_NUMBERS("showLineNumbers");
1✔
3789

3790
    private final String optionName;
3791

3792
    FormattingOption(String optionName) {
1✔
3793
      this.optionName = optionName;
1✔
3794
    }
1✔
3795

3796
    @Override
3797
    public String toString() {
3798
      return optionName;
1✔
3799
    }
3800

3801
    public void addFormatterOption(CommandLine line, ArrayList<String> formatterOptions) {
3802
      if (line.hasOption(optionName)) {
×
3803
        formatterOptions.add(optionName + "=" + line.getOptionValue(optionName));
×
3804
      }
3805
    }
×
3806

3807
    public void addFormatterOption(String value, ArrayList<String> formatterOptions) {
3808
      formatterOptions.add(optionName + "=" + value);
×
3809
    }
×
3810
  }
3811

3812
  public static class JavaObjectNode implements TreeNode {
3813
    private final TreeNode parent;
3814
    private final ArrayList<TreeNode> children = new ArrayList<>();
1✔
3815
    public String fieldName;
3816
    public Object object;
3817

3818
    public JavaObjectNode(TreeNode parent, String fieldName, Object object) {
1✔
3819
      this.parent = parent;
1✔
3820
      this.fieldName = fieldName;
1✔
3821
      this.object = object;
1✔
3822
      addChildren();
1✔
3823
    }
1✔
3824

3825
    @SuppressWarnings({"PMD.CyclomaticComplexity"})
3826
    private void addChildren() {
3827
      ArrayList<Field> fields = new ArrayList<>(FieldUtils.getAllFieldsList(object.getClass()));
1✔
3828

3829
      for (Field field : fields) {
1✔
3830
        try {
3831
          // System.out.println(object.getClass().getName() + " : " + field);
3832
          Object child = FieldUtils.readField(field, this.object, true);
1✔
3833
          if (!(object instanceof Column)) {
1✔
3834
            if (child.getClass().getName().startsWith("net.sf.jsqlparser")
1✔
3835
                && !child.getClass().getName().startsWith("net.sf.jsqlparser.parser")
1✔
3836
                && !child.getClass().isEnum()) {
1✔
3837
              JavaObjectNode childNode = new JavaObjectNode(this, field.getName(), child);
1✔
3838
              children.add(childNode);
1✔
3839
            } else if (child instanceof Collection) {
1✔
3840
              Collection<?> collection = (Collection<?>) child;
1✔
3841
              if (!collection.isEmpty()
1✔
3842
                  && collection.toArray()[0].getClass().getName().startsWith("net.sf.jsqlparser")) {
1✔
3843
                for (Object element : collection) {
1✔
3844
                  if (element.getClass().getName().startsWith("net.sf.jsqlparser")) {
1✔
3845
                    JavaObjectNode subChildNode =
1✔
3846
                        new JavaObjectNode(this, field.getName(), element);
1✔
3847
                    this.children.add(subChildNode);
1✔
3848
                  }
3849
                }
1✔
3850
              }
3851
            }
3852
          }
3853
        } catch (Exception ex) {
1✔
3854
          LOGGER.log(Level.FINE, "failed to process field " + field.getName(), ex);
1✔
3855
        }
1✔
3856
      }
1✔
3857
    }
1✔
3858

3859
    @Override
3860
    public TreeNode getChildAt(int childIndex) {
3861
      return children.get(childIndex);
×
3862
    }
3863

3864
    @Override
3865
    public int getChildCount() {
3866
      return children.size();
×
3867
    }
3868

3869
    @Override
3870
    public TreeNode getParent() {
3871
      return parent;
×
3872
    }
3873

3874
    @Override
3875
    public int getIndex(TreeNode node) {
3876
      return children.indexOf(node);
×
3877
    }
3878

3879
    @Override
3880
    public boolean getAllowsChildren() {
3881
      return true;
×
3882
    }
3883

3884
    @Override
3885
    public boolean isLeaf() {
3886
      return children.isEmpty();
1✔
3887
    }
3888

3889
    @Override
3890
    public Enumeration<? extends TreeNode> children() {
3891
      return Collections.enumeration(children);
1✔
3892
    }
3893

3894

3895
    private String formatClassName(Object o) {
3896
      if (outputFormat.equals(OutputFormat.HTML)) {
1✔
3897
        return "<html><font color='gray'>" + o.getClass().getSimpleName() + ":</font> <em>" + o
×
3898
            + "</em></html>";
3899
      } else if (outputFormat.equals(OutputFormat.ANSI)) {
1✔
3900
        return ANSI_FORMAT_KEYWORD.format(o.getClass().getSimpleName()) + ": "
1✔
3901
            + ANSI_FORMAT_PARAMETER.format(o.toString());
1✔
3902
      } else {
3903
        return o.getClass().getSimpleName() + ": " + o;
1✔
3904
      }
3905
    }
3906

3907
    private String formatFieldClassName(Object o) {
3908
      if (outputFormat.equals(OutputFormat.HTML)) {
1✔
3909
        return "<html><font color='gray'>" + fieldName + ":</font> <em>"
×
3910
            + o.getClass().getCanonicalName() + "</em></html>";
×
3911
      } else if (outputFormat.equals(OutputFormat.ANSI)) {
1✔
3912
        return ANSI_FORMAT_KEYWORD.format(fieldName) + ": " + ANSI_FORMAT_PARAMETER
1✔
3913
            .format(o.getClass().getCanonicalName().replace("net.sf.jsqlparser.", ""));
1✔
3914
      } else {
3915
        return fieldName + ": "
1✔
3916
            + object.getClass().getCanonicalName().replace("net.sf.jsqlparser.", "");
1✔
3917
      }
3918
    }
3919

3920
    private String formatCollection(Collection<?> collection) {
3921
      if (outputFormat.equals(OutputFormat.HTML)) {
×
3922
        return "<html><font color='gray'>" + fieldName + " -></font> Collection&lt;"
×
3923
            + collection.toArray()[0].getClass().getSimpleName() + "&gt;</html>";
×
3924
      } else if (outputFormat.equals(OutputFormat.ANSI)) {
×
3925
        return ANSI_FORMAT_KEYWORD.format(fieldName) + " -> Collection<"
×
3926
            + ANSI_FORMAT_PARAMETER.format(collection.toArray()[0].getClass().getSimpleName())
×
3927
            + ">";
3928
      } else {
3929
        return object.getClass().getSimpleName() + ": " + object;
×
3930
      }
3931
    }
3932

3933
    @Override
3934
    public String toString() {
3935
      if (object instanceof Column || object instanceof Table
1✔
3936
          || object instanceof net.sf.jsqlparser.schema.Database
3937
          || object instanceof net.sf.jsqlparser.schema.Sequence
3938
          || object instanceof net.sf.jsqlparser.schema.Server
3939
          || object instanceof net.sf.jsqlparser.schema.Synonym) {
3940
        return formatClassName(object);
1✔
3941
      } else if (object instanceof Collection) {
1✔
3942
        return formatCollection((Collection<?>) object);
×
3943
      } else if (isLeaf()) {
1✔
3944
        return formatClassName(object);
1✔
3945
      } else {
3946
        return formatFieldClassName(object);
1✔
3947
      }
3948
    }
3949
  }
3950
}
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