diff --git a/plugins/org.jkiss.dbeaver.core/plugin.xml b/plugins/org.jkiss.dbeaver.core/plugin.xml
index 39be91eea176a6d1f027247c43a6ffdbea94099f..f127a3031201a90149ceb4d19b18c8d6ea331cc4 100644
--- a/plugins/org.jkiss.dbeaver.core/plugin.xml
+++ b/plugins/org.jkiss.dbeaver.core/plugin.xml
@@ -3302,8 +3302,9 @@
-
-
+
+
+
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageSQLFormat.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageSQLFormat.java
index d2aa671a8c8ba085041a92cd652eb3493110c619..2f72f2b345c6c8ce94b69b5cba86e345cd1c6c94 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageSQLFormat.java
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageSQLFormat.java
@@ -38,7 +38,7 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPIdentifierCase;
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
-import org.jkiss.dbeaver.model.sql.format.external.SQLExternalFormatter;
+import org.jkiss.dbeaver.model.sql.format.external.SQLFormatterExternal;
import org.jkiss.dbeaver.registry.sql.SQLFormatterConfigurationRegistry;
import org.jkiss.dbeaver.registry.sql.SQLFormatterDescriptor;
import org.jkiss.dbeaver.ui.UIUtils;
@@ -159,6 +159,7 @@ public class PrefPageSQLFormat extends TargetPrefPage
@Override
public void widgetSelected(SelectionEvent e) {
showFormatterSettings();
+ performApply();
}
});
formatterSelector.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
@@ -193,13 +194,13 @@ public class PrefPageSQLFormat extends TargetPrefPage
externalCmdText,
new TextContentAdapter(),
new SimpleContentProposalProvider(new String[] {
- GeneralUtils.variablePattern(SQLExternalFormatter.VAR_FILE)
+ GeneralUtils.variablePattern(SQLFormatterExternal.VAR_FILE)
}));
- UIUtils.setContentProposalToolTip(externalCmdText, CoreMessages.pref_page_sql_format_label_external_set_content_tool_tip, SQLExternalFormatter.VAR_FILE);
+ UIUtils.setContentProposalToolTip(externalCmdText, CoreMessages.pref_page_sql_format_label_external_set_content_tool_tip, SQLFormatterExternal.VAR_FILE);
externalUseFile = UIUtils.createLabelCheckbox(externalGroup,
CoreMessages.pref_page_sql_format_label_external_use_temp_file,
- CoreMessages.pref_page_sql_format_label_external_use_temp_file_tip + GeneralUtils.variablePattern(SQLExternalFormatter.VAR_FILE),
+ CoreMessages.pref_page_sql_format_label_external_use_temp_file_tip + GeneralUtils.variablePattern(SQLFormatterExternal.VAR_FILE),
false);
externalTimeout = UIUtils.createLabelSpinner(externalGroup,
CoreMessages.pref_page_sql_format_label_external_exec_timeout,
@@ -344,7 +345,7 @@ public class PrefPageSQLFormat extends TargetPrefPage
private void showFormatterSettings() {
SQLFormatterDescriptor selFormatter = formatters.get(formatterSelector.getSelectionIndex());
- boolean isExternal = selFormatter.getId().equalsIgnoreCase(SQLExternalFormatter.FORMATTER_ID);
+ boolean isExternal = selFormatter.getId().equalsIgnoreCase(SQLFormatterExternal.FORMATTER_ID);
defaultGroup.setVisible(!isExternal);
externalGroup.setVisible(isExternal);
((GridData)defaultGroup.getLayoutData()).exclude = isExternal;
diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/ModelPreferences.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/ModelPreferences.java
index fe8e40382ae87d718e57aa8f33bbf7720e3aadfa..1a51c89ea79c67397494e658683fd1171e87c837 100644
--- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/ModelPreferences.java
+++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/ModelPreferences.java
@@ -26,7 +26,7 @@ import org.jkiss.dbeaver.model.impl.preferences.BundlePreferenceStore;
import org.jkiss.dbeaver.model.qm.QMConstants;
import org.jkiss.dbeaver.model.qm.QMObjectType;
import org.jkiss.dbeaver.model.sql.SQLConstants;
-import org.jkiss.dbeaver.model.sql.format.tokenized.SQLTokenizedFormatter;
+import org.jkiss.dbeaver.model.sql.format.tokenized.SQLFormatterTokenized;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.PrefUtils;
import org.osgi.framework.Bundle;
@@ -160,7 +160,7 @@ public final class ModelPreferences
PrefUtils.setDefaultPreferenceValue(store, SQL_NAMED_PARAMETERS_PREFIX, String.valueOf(SQLConstants.DEFAULT_PARAMETER_PREFIX));
PrefUtils.setDefaultPreferenceValue(store, SQL_CONTROL_COMMAND_PREFIX, String.valueOf(SQLConstants.DEFAULT_CONTROL_COMMAND_PREFIX));
- PrefUtils.setDefaultPreferenceValue(store, SQL_FORMAT_FORMATTER, SQLTokenizedFormatter.FORMATTER_ID);
+ PrefUtils.setDefaultPreferenceValue(store, SQL_FORMAT_FORMATTER, SQLFormatterTokenized.FORMATTER_ID);
PrefUtils.setDefaultPreferenceValue(store, SQL_FORMAT_KEYWORD_CASE, "");
PrefUtils.setDefaultPreferenceValue(store, SQL_FORMAT_EXTERNAL_CMD, "");
PrefUtils.setDefaultPreferenceValue(store, SQL_FORMAT_EXTERNAL_FILE, false);
diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/external/SQLExternalFormatter.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/external/SQLFormatterExternal.java
similarity index 95%
rename from plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/external/SQLExternalFormatter.java
rename to plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/external/SQLFormatterExternal.java
index ba38e6bd9bfbd487124158eb9e4ea829e5bb06b9..11fe0a45f4338159b6deeaf23bcc695ef42a087e 100644
--- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/external/SQLExternalFormatter.java
+++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/external/SQLFormatterExternal.java
@@ -37,11 +37,11 @@ import java.util.List;
/**
* External SQL formatter
*/
-public class SQLExternalFormatter implements SQLFormatter {
+public class SQLFormatterExternal implements SQLFormatter {
public static final String FORMATTER_ID = "EXTERNAL";
- private static final Log log = Log.getLog(SQLExternalFormatter.class);
+ private static final Log log = Log.getLog(SQLFormatterExternal.class);
public static final String VAR_FILE = "file";
@Override
@@ -50,6 +50,11 @@ public class SQLExternalFormatter implements SQLFormatter {
final String command = store.getString(ModelPreferences.SQL_FORMAT_EXTERNAL_CMD);
int timeout = store.getInt(ModelPreferences.SQL_FORMAT_EXTERNAL_TIMEOUT);
boolean useFile = store.getBoolean(ModelPreferences.SQL_FORMAT_EXTERNAL_FILE);
+ if (CommonUtils.isEmpty(command)) {
+ // Nothing to format
+ return source;
+ }
+
try {
final FormatJob formatJob = new FormatJob(configuration, command, source, useFile);
formatJob.schedule();
@@ -79,7 +84,7 @@ public class SQLExternalFormatter implements SQLFormatter {
public boolean finished;
public FormatJob(SQLFormatterConfiguration configuration, String command, String source, boolean useFile) {
- super("External format: " + command);
+ super("External format - " + command);
this.command = command;
this.configuration = configuration;
this.source = source;
diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLFormatterCompact.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLFormatterCompact.java
new file mode 100644
index 0000000000000000000000000000000000000000..bee1fc593fa45bec6f1c870608243e90222ff202
--- /dev/null
+++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLFormatterCompact.java
@@ -0,0 +1,28 @@
+/*
+ * DBeaver - Universal Database Manager
+ * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jkiss.dbeaver.model.sql.format.tokenized;
+
+/**
+ * SQL formatter
+ */
+public class SQLFormatterCompact extends SQLFormatterTokenized {
+
+ public SQLFormatterCompact() {
+ setCompact(true);
+ }
+}
\ No newline at end of file
diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLTokenizedFormatter.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLFormatterTokenized.java
similarity index 96%
rename from plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLTokenizedFormatter.java
rename to plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLFormatterTokenized.java
index 2d34aa9e4d6a6f6215a3f78e57e1cd0c7ed000d8..81a8cf1f93a0a499bbcbd358510b37829e372e7a 100644
--- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLTokenizedFormatter.java
+++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/sql/format/tokenized/SQLFormatterTokenized.java
@@ -1,451 +1,466 @@
-/*
- * DBeaver - Universal Database Manager
- * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jkiss.dbeaver.model.sql.format.tokenized;
-
-import org.jkiss.dbeaver.model.DBPIdentifierCase;
-import org.jkiss.dbeaver.model.sql.SQLUtils;
-import org.jkiss.dbeaver.model.sql.format.SQLFormatter;
-import org.jkiss.dbeaver.model.sql.format.SQLFormatterConfiguration;
-import org.jkiss.dbeaver.utils.GeneralUtils;
-import org.jkiss.utils.ArrayUtils;
-import org.jkiss.utils.CommonUtils;
-import org.jkiss.utils.Pair;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * SQL formatter
- */
-public class SQLTokenizedFormatter implements SQLFormatter {
-
- public static final String FORMATTER_ID = "DEFAULT";
-
- private static final String[] JOIN_BEGIN = { "LEFT", "RIGHT", "INNER", "OUTER", "JOIN" };
-
- private SQLFormatterConfiguration formatterCfg;
- private List functionBracket = new ArrayList<>();
- private List statementDelimiters = new ArrayList<>(2);
- private String delimiterRedefiner;
-
- @Override
- public String format(final String argSql, SQLFormatterConfiguration configuration)
- {
- formatterCfg = configuration;
-
- for (String delim : formatterCfg.getSyntaxManager().getStatementDelimiters()) {
- statementDelimiters.add(delim.toUpperCase(Locale.ENGLISH));
- }
- delimiterRedefiner = formatterCfg.getSyntaxManager().getDialect().getScriptDelimiterRedefiner();
- if (delimiterRedefiner != null) {
- delimiterRedefiner = delimiterRedefiner.toUpperCase(Locale.ENGLISH);
- }
- SQLTokensParser fParser = new SQLTokensParser(formatterCfg);
-
- functionBracket.clear();
-
- boolean isSqlEndsWithNewLine = false;
- if (argSql.endsWith("\n")) { //$NON-NLS-1$
- isSqlEndsWithNewLine = true;
- }
-
- List list = fParser.parse(argSql);
- list = format(list);
-
- StringBuilder after = new StringBuilder(argSql.length() + 20);
- for (FormatterToken token : list) {
- after.append(token.getString());
- }
-
- if (isSqlEndsWithNewLine) {
- after.append(GeneralUtils.getDefaultLineSeparator());
- }
-
- return after.toString();
- }
-
- private List format(final List argList) {
- if (argList.isEmpty()) {
- return argList;
- }
-
- FormatterToken token = argList.get(0);
- if (token.getType() == TokenType.SPACE) {
- argList.remove(0);
- if (argList.isEmpty()) {
- return argList;
- }
- }
-
- token = argList.get(argList.size() - 1);
- if (token.getType() == TokenType.SPACE) {
- argList.remove(argList.size() - 1);
- if (argList.isEmpty()) {
- return argList;
- }
- }
-
- final DBPIdentifierCase keywordCase = formatterCfg.getKeywordCase();
- for (int index = 0; index < argList.size(); index++) {
- token = argList.get(index);
- if (token.getType() == TokenType.KEYWORD) {
- token.setString(keywordCase.transform(token.getString()));
- }
- }
-
- // Remove extra tokens (spaces, etc)
- for (int index = argList.size() - 1; index >= 1; index--) {
- token = argList.get(index);
- FormatterToken prevToken = argList.get(index - 1);
- if (token.getType() == TokenType.SPACE && (prevToken.getType() == TokenType.SYMBOL || prevToken.getType() == TokenType.COMMENT)) {
- argList.remove(index);
- } else if ((token.getType() == TokenType.SYMBOL || token.getType() == TokenType.COMMENT) && prevToken.getType() == TokenType.SPACE) {
- argList.remove(index - 1);
- } else if (token.getType() == TokenType.SPACE) {
- token.setString(" "); //$NON-NLS-1$
- }
- }
-
- for (int index = 0; index < argList.size() - 2; index++) {
- FormatterToken t0 = argList.get(index);
- FormatterToken t1 = argList.get(index + 1);
- FormatterToken t2 = argList.get(index + 2);
-
- String tokenString = t0.getString().toUpperCase(Locale.ENGLISH);
- String token2String = t2.getString().toUpperCase(Locale.ENGLISH);;
- // Concatenate tokens
- if (t0.getType() == TokenType.KEYWORD && t1.getType() == TokenType.SPACE && t2.getType() == TokenType.KEYWORD) {
- if (((tokenString.equals("ORDER") || tokenString.equals("GROUP") || tokenString.equals("CONNECT")) && token2String.equals("BY")) ||
- ((tokenString.equals("START")) && token2String.equals("WITH")))
- {
- t0.setString(t0.getString() + " " + t2.getString());
- argList.remove(index + 1);
- argList.remove(index + 1);
- }
- }
-
- // Oracle style joins
- if (tokenString.equals("(") && t1.getString().equals("+") && token2String.equals(")")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- t0.setString("(+)"); //$NON-NLS-1$
- argList.remove(index + 1);
- argList.remove(index + 1);
- }
- }
-
- int indent = 0;
- final List bracketIndent = new ArrayList<>();
- FormatterToken prev = new FormatterToken(TokenType.SPACE, " "); //$NON-NLS-1$
- boolean encounterBetween = false;
- for (int index = 0; index < argList.size(); index++) {
- token = argList.get(index);
- String tokenString = token.getString().toUpperCase(Locale.ENGLISH);
- if (token.getType() == TokenType.SYMBOL) {
- if (tokenString.equals("(")) { //$NON-NLS-1$
- functionBracket.add(formatterCfg.isFunction(prev.getString()) ? Boolean.TRUE : Boolean.FALSE);
- bracketIndent.add(indent);
- indent++;
- index += insertReturnAndIndent(argList, index + 1, indent);
- } else if (tokenString.equals(")") && !bracketIndent.isEmpty() && !functionBracket.isEmpty()) { //$NON-NLS-1$
- indent = bracketIndent.remove(bracketIndent.size() - 1);
- index += insertReturnAndIndent(argList, index, indent);
- functionBracket.remove(functionBracket.size() - 1);
- } else if (tokenString.equals(",")) { //$NON-NLS-1$
- index += insertReturnAndIndent(argList, index + 1, indent);
- } else if (statementDelimiters.contains(tokenString)) { //$NON-NLS-1$
- indent = 0;
- index += insertReturnAndIndent(argList, index, indent);
- }
- } else if (token.getType() == TokenType.KEYWORD) {
- switch (tokenString) {
- case "DELETE":
- case "SELECT":
- case "UPDATE": //$NON-NLS-1$
- case "INSERT":
- case "INTO":
- case "CREATE":
- case "DROP":
- case "TRUNCATE":
- case "TABLE":
- case "CASE": //$NON-NLS-1$
- indent++;
- index += insertReturnAndIndent(argList, index + 1, indent);
- break;
- case "FROM":
- case "WHERE":
- case "SET":
- case "START WITH":
- case "CONNECT BY":
- case "ORDER BY":
- case "GROUP BY":
- case "HAVING": //$NON-NLS-1$
- index += insertReturnAndIndent(argList, index, indent - 1);
- index += insertReturnAndIndent(argList, index + 1, indent);
- break;
- case "LEFT":
- case "RIGHT":
- case "INNER":
- case "OUTER":
- case "JOIN":
- if (isJoinStart(argList, index)) {
- index += insertReturnAndIndent(argList, index, indent - 1);
- }
- if (tokenString.equals("JOIN")) {
- //index += insertReturnAndIndent(argList, index + 1, indent);
- }
- break;
- case "VALUES": //$NON-NLS-1$
- indent--;
- index += insertReturnAndIndent(argList, index, indent);
- break;
- case "END": //$NON-NLS-1$
- indent--;
- index += insertReturnAndIndent(argList, index, indent);
- break;
- case "OR":
- case "WHEN":
- case "ELSE": //$NON-NLS-1$
- index += insertReturnAndIndent(argList, index, indent);
- break;
- case "ON":
- //indent++;
- index += insertReturnAndIndent(argList, index + 1, indent);
- break;
- case "USING": //$NON-NLS-1$ //$NON-NLS-2$
- index += insertReturnAndIndent(argList, index, indent + 1);
- break;
- case "TOP": //$NON-NLS-1$ //$NON-NLS-2$
- // SQL Server specific
- index += insertReturnAndIndent(argList, index, indent);
- if (argList.size() < index + 3) {
- index += insertReturnAndIndent(argList, index + 3, indent);
- }
- break;
- case "UNION":
- case "INTERSECT":
- case "EXCEPT": //$NON-NLS-1$
- indent -= 2;
- index += insertReturnAndIndent(argList, index, indent);
- //index += insertReturnAndIndent(argList, index + 1, indent);
- indent++;
- break;
- case "BETWEEN": //$NON-NLS-1$
- encounterBetween = true;
- break;
- case "AND": //$NON-NLS-1$
- if (!encounterBetween) {
- index += insertReturnAndIndent(argList, index, indent);
- }
- encounterBetween = false;
- break;
- }
- } else if (token.getType() == TokenType.COMMENT) {
- boolean isComment = false;
- String[] slComments = formatterCfg.getSyntaxManager().getDialect().getSingleLineComments();
- if (slComments != null) {
- for (String slc : slComments) {
- if (token.getString().startsWith(slc)) {
- isComment = true;
- break;
- }
- }
- }
- if (!isComment) {
- Pair mlComments = formatterCfg.getSyntaxManager().getDialect().getMultiLineComments();
- if (mlComments != null) {
- if (token.getString().startsWith(mlComments.getFirst())) {
- index += insertReturnAndIndent(argList, index + 1, indent);
- }
- }
- }
- } else if (token.getType() == TokenType.COMMAND) {
- indent = 0;
- if (index > 0) {
- index += insertReturnAndIndent(argList, index, 0);
- }
- index += insertReturnAndIndent(argList, index + 1, 0);
- if (!CommonUtils.isEmpty(delimiterRedefiner) && token.getString().startsWith(delimiterRedefiner)) {
- final String command = token.getString().trim().toUpperCase(Locale.ENGLISH);
- final int divPos = command.lastIndexOf(' ');
- if (divPos > 0) {
- String delimiter = command.substring(divPos).trim();
- if (!CommonUtils.isEmpty(delimiter)) {
- statementDelimiters.clear();
- statementDelimiters.add(delimiter);
- }
- }
- }
- } else {
- if (statementDelimiters.contains(tokenString)) {
- indent = 0;
- index += insertReturnAndIndent(argList, index + 1, indent);
- }
- }
- prev = token;
- }
-
- for (int index = argList.size() - 1; index >= 4; index--) {
- if (index >= argList.size()) {
- continue;
- }
-
- FormatterToken t0 = argList.get(index);
- FormatterToken t1 = argList.get(index - 1);
- FormatterToken t2 = argList.get(index - 2);
- FormatterToken t3 = argList.get(index - 3);
- FormatterToken t4 = argList.get(index - 4);
-
- if (t4.getString().equals("(") //$NON-NLS-1$
- && t3.getString().trim().isEmpty()
- && t1.getString().trim().isEmpty()
- && t0.getString().equalsIgnoreCase(")")) //$NON-NLS-1$
- {
- t4.setString(t4.getString() + t2.getString() + t0.getString());
- argList.remove(index);
- argList.remove(index - 1);
- argList.remove(index - 2);
- argList.remove(index - 3);
- }
- }
-
- for (int index = 1; index < argList.size(); index++) {
- prev = argList.get(index - 1);
- token = argList.get(index);
-
- if (prev.getType() != TokenType.SPACE &&
- token.getType() != TokenType.SPACE &&
- !token.getString().startsWith("("))
- {
- if (token.getString().equals(",") || statementDelimiters.contains(token.getString())) { //$NON-NLS-1$
- continue;
- }
- if (formatterCfg.isFunction(prev.getString())
- && token.getString().equals("(")) { //$NON-NLS-1$
- continue;
- }
- if (token.getType() == TokenType.VALUE && prev.getType() == TokenType.NAME) {
- // Do not add space between name and value [JDBC:MSSQL]
- continue;
- }
- if (token.getType() == TokenType.SYMBOL && isEmbeddedToken(token) ||
- prev.getType() == TokenType.SYMBOL && isEmbeddedToken(prev))
- {
- // Do not insert spaces around colons
- continue;
- }
- if (token.getType() == TokenType.SYMBOL && prev.getType() == TokenType.SYMBOL) {
- // Do not add space between symbols
- continue;
- }
- argList.add(index, new FormatterToken(TokenType.SPACE, " ")); //$NON-NLS-1$
- }
- }
-
- return argList;
- }
-
- private static boolean isEmbeddedToken(FormatterToken token) {
- return ":".equals(token.getString()) || ".".equals(token.getString());
- }
-
- private boolean isJoinStart(List argList, int index) {
- // Keyword sequence must start from LEFT, RIGHT, INNER, OUTER or JOIN and must end with JOIN
- // And we must be in the beginning of sequence
-
- // check current token
- if (!ArrayUtils.contains(JOIN_BEGIN, argList.get(index).getString())) {
- return false;
- }
- // check previous token
- for (int i = index - 1; i >= 0; i--) {
- FormatterToken token = argList.get(i);
- if (token.getType() == TokenType.SPACE) {
- continue;
- }
- if (ArrayUtils.contains(JOIN_BEGIN, token.getString())) {
- // It is not the begin of sequence
- return false;
- } else {
- break;
- }
- }
- // check last token
- for (int i = index; i < argList.size(); i++) {
- FormatterToken token = argList.get(i);
- if (token.getType() == TokenType.SPACE) {
- continue;
- }
- if (token.getString().equals("JOIN")) {
- return true;
- }
- if (!ArrayUtils.contains(JOIN_BEGIN, token.getString())) {
- // It is not the begin of sequence
- return false;
- }
- }
- return false;
- }
-
- private int insertReturnAndIndent(final List argList, final int argIndex, final int argIndent)
- {
- if (functionBracket.contains(Boolean.TRUE))
- return 0;
- try {
- String s = GeneralUtils.getDefaultLineSeparator();
- if (argIndex > 0) {
- final FormatterToken prevToken = argList.get(argIndex - 1);
- if (prevToken.getType() == TokenType.COMMENT &&
- SQLUtils.isCommentLine(formatterCfg.getSyntaxManager().getDialect(), prevToken.getString()))
- {
- s = ""; //$NON-NLS-1$
- }
- }
- for (int index = 0; index < argIndent; index++) {
- s += formatterCfg.getIndentString();
- }
-
- FormatterToken token = argList.get(argIndex);
- if (token.getType() == TokenType.SPACE) {
- token.setString(s);
- return 0;
- }
- boolean isDelimiter = statementDelimiters.contains(token.getString().toUpperCase());
-
- if (!isDelimiter) {
- token = argList.get(argIndex - 1);
- if (token.getType() == TokenType.SPACE) {
- token.setString(s);
- return 0;
- }
- }
-
- if (isDelimiter) {
- if (argList.size() > argIndex + 1) {
- argList.add(argIndex + 1, new FormatterToken(TokenType.SPACE, s + s));
- }
- } else {
- argList.add(argIndex, new FormatterToken(TokenType.SPACE, s));
- }
- return 1;
- } catch (IndexOutOfBoundsException e) {
- e.printStackTrace();
- return 0;
- }
- }
-
+/*
+ * DBeaver - Universal Database Manager
+ * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jkiss.dbeaver.model.sql.format.tokenized;
+
+import org.jkiss.dbeaver.model.DBPIdentifierCase;
+import org.jkiss.dbeaver.model.sql.SQLUtils;
+import org.jkiss.dbeaver.model.sql.format.SQLFormatter;
+import org.jkiss.dbeaver.model.sql.format.SQLFormatterConfiguration;
+import org.jkiss.dbeaver.utils.GeneralUtils;
+import org.jkiss.utils.ArrayUtils;
+import org.jkiss.utils.CommonUtils;
+import org.jkiss.utils.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * SQL formatter
+ */
+public class SQLFormatterTokenized implements SQLFormatter {
+
+ public static final String FORMATTER_ID = "DEFAULT";
+
+ private static final String[] JOIN_BEGIN = { "LEFT", "RIGHT", "INNER", "OUTER", "JOIN" };
+
+ private SQLFormatterConfiguration formatterCfg;
+ private List functionBracket = new ArrayList<>();
+ private List statementDelimiters = new ArrayList<>(2);
+ private String delimiterRedefiner;
+ private boolean isCompact;
+
+ @Override
+ public String format(final String argSql, SQLFormatterConfiguration configuration)
+ {
+ formatterCfg = configuration;
+
+ for (String delim : formatterCfg.getSyntaxManager().getStatementDelimiters()) {
+ statementDelimiters.add(delim.toUpperCase(Locale.ENGLISH));
+ }
+ delimiterRedefiner = formatterCfg.getSyntaxManager().getDialect().getScriptDelimiterRedefiner();
+ if (delimiterRedefiner != null) {
+ delimiterRedefiner = delimiterRedefiner.toUpperCase(Locale.ENGLISH);
+ }
+ SQLTokensParser fParser = new SQLTokensParser(formatterCfg);
+
+ functionBracket.clear();
+
+ boolean isSqlEndsWithNewLine = false;
+ if (argSql.endsWith("\n")) { //$NON-NLS-1$
+ isSqlEndsWithNewLine = true;
+ }
+
+ List list = fParser.parse(argSql);
+ list = format(list);
+
+ StringBuilder after = new StringBuilder(argSql.length() + 20);
+ for (FormatterToken token : list) {
+ after.append(token.getString());
+ }
+
+ if (isSqlEndsWithNewLine) {
+ after.append(GeneralUtils.getDefaultLineSeparator());
+ }
+
+ return after.toString();
+ }
+
+ public boolean isCompact() {
+ return isCompact;
+ }
+
+ public void setCompact(boolean compact) {
+ isCompact = compact;
+ }
+
+ private List format(final List argList) {
+ if (argList.isEmpty()) {
+ return argList;
+ }
+
+ FormatterToken token = argList.get(0);
+ if (token.getType() == TokenType.SPACE) {
+ argList.remove(0);
+ if (argList.isEmpty()) {
+ return argList;
+ }
+ }
+
+ token = argList.get(argList.size() - 1);
+ if (token.getType() == TokenType.SPACE) {
+ argList.remove(argList.size() - 1);
+ if (argList.isEmpty()) {
+ return argList;
+ }
+ }
+
+ final DBPIdentifierCase keywordCase = formatterCfg.getKeywordCase();
+ for (int index = 0; index < argList.size(); index++) {
+ token = argList.get(index);
+ if (token.getType() == TokenType.KEYWORD) {
+ token.setString(keywordCase.transform(token.getString()));
+ }
+ }
+
+ // Remove extra tokens (spaces, etc)
+ for (int index = argList.size() - 1; index >= 1; index--) {
+ token = argList.get(index);
+ FormatterToken prevToken = argList.get(index - 1);
+ if (token.getType() == TokenType.SPACE && (prevToken.getType() == TokenType.SYMBOL || prevToken.getType() == TokenType.COMMENT)) {
+ argList.remove(index);
+ } else if ((token.getType() == TokenType.SYMBOL || token.getType() == TokenType.COMMENT) && prevToken.getType() == TokenType.SPACE) {
+ argList.remove(index - 1);
+ } else if (token.getType() == TokenType.SPACE) {
+ token.setString(" "); //$NON-NLS-1$
+ }
+ }
+
+ for (int index = 0; index < argList.size() - 2; index++) {
+ FormatterToken t0 = argList.get(index);
+ FormatterToken t1 = argList.get(index + 1);
+ FormatterToken t2 = argList.get(index + 2);
+
+ String tokenString = t0.getString().toUpperCase(Locale.ENGLISH);
+ String token2String = t2.getString().toUpperCase(Locale.ENGLISH);;
+ // Concatenate tokens
+ if (t0.getType() == TokenType.KEYWORD && t1.getType() == TokenType.SPACE && t2.getType() == TokenType.KEYWORD) {
+ if (((tokenString.equals("ORDER") || tokenString.equals("GROUP") || tokenString.equals("CONNECT")) && token2String.equals("BY")) ||
+ ((tokenString.equals("START")) && token2String.equals("WITH")))
+ {
+ t0.setString(t0.getString() + " " + t2.getString());
+ argList.remove(index + 1);
+ argList.remove(index + 1);
+ }
+ }
+
+ // Oracle style joins
+ if (tokenString.equals("(") && t1.getString().equals("+") && token2String.equals(")")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ t0.setString("(+)"); //$NON-NLS-1$
+ argList.remove(index + 1);
+ argList.remove(index + 1);
+ }
+ }
+
+ int indent = 0;
+ final List bracketIndent = new ArrayList<>();
+ FormatterToken prev = new FormatterToken(TokenType.SPACE, " "); //$NON-NLS-1$
+ boolean encounterBetween = false;
+ for (int index = 0; index < argList.size(); index++) {
+ token = argList.get(index);
+ String tokenString = token.getString().toUpperCase(Locale.ENGLISH);
+ if (token.getType() == TokenType.SYMBOL) {
+ if (tokenString.equals("(")) { //$NON-NLS-1$
+ functionBracket.add(formatterCfg.isFunction(prev.getString()) ? Boolean.TRUE : Boolean.FALSE);
+ bracketIndent.add(indent);
+ indent++;
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ } else if (tokenString.equals(")") && !bracketIndent.isEmpty() && !functionBracket.isEmpty()) { //$NON-NLS-1$
+ indent = bracketIndent.remove(bracketIndent.size() - 1);
+ index += insertReturnAndIndent(argList, index, indent);
+ functionBracket.remove(functionBracket.size() - 1);
+ } else if (tokenString.equals(",")) { //$NON-NLS-1$
+ if (!isCompact) {
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ }
+ } else if (statementDelimiters.contains(tokenString)) { //$NON-NLS-1$
+ indent = 0;
+ index += insertReturnAndIndent(argList, index, indent);
+ }
+ } else if (token.getType() == TokenType.KEYWORD) {
+ switch (tokenString) {
+ case "DELETE":
+ case "SELECT":
+ case "UPDATE": //$NON-NLS-1$
+ case "INSERT":
+ case "INTO":
+ case "CREATE":
+ case "DROP":
+ case "TRUNCATE":
+ case "TABLE":
+ case "CASE": //$NON-NLS-1$
+ if (!isCompact) {
+ indent++;
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ }
+ break;
+ case "FROM":
+ case "WHERE":
+ case "SET":
+ case "START WITH":
+ case "CONNECT BY":
+ case "ORDER BY":
+ case "GROUP BY":
+ case "HAVING": //$NON-NLS-1$
+ index += insertReturnAndIndent(argList, index, indent - 1);
+ if (!isCompact) {
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ }
+ break;
+ case "LEFT":
+ case "RIGHT":
+ case "INNER":
+ case "OUTER":
+ case "JOIN":
+ if (isJoinStart(argList, index)) {
+ index += insertReturnAndIndent(argList, index, indent - 1);
+ }
+ if (tokenString.equals("JOIN")) {
+ //index += insertReturnAndIndent(argList, index + 1, indent);
+ }
+ break;
+ case "VALUES": //$NON-NLS-1$
+ indent--;
+ index += insertReturnAndIndent(argList, index, indent);
+ break;
+ case "END": //$NON-NLS-1$
+ indent--;
+ index += insertReturnAndIndent(argList, index, indent);
+ break;
+ case "OR":
+ case "WHEN":
+ case "ELSE": //$NON-NLS-1$
+ index += insertReturnAndIndent(argList, index, indent);
+ break;
+ case "ON":
+ //indent++;
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ break;
+ case "USING": //$NON-NLS-1$ //$NON-NLS-2$
+ index += insertReturnAndIndent(argList, index, indent + 1);
+ break;
+ case "TOP": //$NON-NLS-1$ //$NON-NLS-2$
+ // SQL Server specific
+ index += insertReturnAndIndent(argList, index, indent);
+ if (argList.size() < index + 3) {
+ index += insertReturnAndIndent(argList, index + 3, indent);
+ }
+ break;
+ case "UNION":
+ case "INTERSECT":
+ case "EXCEPT": //$NON-NLS-1$
+ indent -= 2;
+ index += insertReturnAndIndent(argList, index, indent);
+ //index += insertReturnAndIndent(argList, index + 1, indent);
+ indent++;
+ break;
+ case "BETWEEN": //$NON-NLS-1$
+ encounterBetween = true;
+ break;
+ case "AND": //$NON-NLS-1$
+ if (!encounterBetween) {
+ index += insertReturnAndIndent(argList, index, indent);
+ }
+ encounterBetween = false;
+ break;
+ }
+ } else if (token.getType() == TokenType.COMMENT) {
+ boolean isComment = false;
+ String[] slComments = formatterCfg.getSyntaxManager().getDialect().getSingleLineComments();
+ if (slComments != null) {
+ for (String slc : slComments) {
+ if (token.getString().startsWith(slc)) {
+ isComment = true;
+ break;
+ }
+ }
+ }
+ if (!isComment) {
+ Pair mlComments = formatterCfg.getSyntaxManager().getDialect().getMultiLineComments();
+ if (mlComments != null) {
+ if (token.getString().startsWith(mlComments.getFirst())) {
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ }
+ }
+ }
+ } else if (token.getType() == TokenType.COMMAND) {
+ indent = 0;
+ if (index > 0) {
+ index += insertReturnAndIndent(argList, index, 0);
+ }
+ index += insertReturnAndIndent(argList, index + 1, 0);
+ if (!CommonUtils.isEmpty(delimiterRedefiner) && token.getString().startsWith(delimiterRedefiner)) {
+ final String command = token.getString().trim().toUpperCase(Locale.ENGLISH);
+ final int divPos = command.lastIndexOf(' ');
+ if (divPos > 0) {
+ String delimiter = command.substring(divPos).trim();
+ if (!CommonUtils.isEmpty(delimiter)) {
+ statementDelimiters.clear();
+ statementDelimiters.add(delimiter);
+ }
+ }
+ }
+ } else {
+ if (statementDelimiters.contains(tokenString)) {
+ indent = 0;
+ index += insertReturnAndIndent(argList, index + 1, indent);
+ }
+ }
+ prev = token;
+ }
+
+ for (int index = argList.size() - 1; index >= 4; index--) {
+ if (index >= argList.size()) {
+ continue;
+ }
+
+ FormatterToken t0 = argList.get(index);
+ FormatterToken t1 = argList.get(index - 1);
+ FormatterToken t2 = argList.get(index - 2);
+ FormatterToken t3 = argList.get(index - 3);
+ FormatterToken t4 = argList.get(index - 4);
+
+ if (t4.getString().equals("(") //$NON-NLS-1$
+ && t3.getString().trim().isEmpty()
+ && t1.getString().trim().isEmpty()
+ && t0.getString().equalsIgnoreCase(")")) //$NON-NLS-1$
+ {
+ t4.setString(t4.getString() + t2.getString() + t0.getString());
+ argList.remove(index);
+ argList.remove(index - 1);
+ argList.remove(index - 2);
+ argList.remove(index - 3);
+ }
+ }
+
+ for (int index = 1; index < argList.size(); index++) {
+ prev = argList.get(index - 1);
+ token = argList.get(index);
+
+ if (prev.getType() != TokenType.SPACE &&
+ token.getType() != TokenType.SPACE &&
+ !token.getString().startsWith("("))
+ {
+ if (token.getString().equals(",") || statementDelimiters.contains(token.getString())) { //$NON-NLS-1$
+ continue;
+ }
+ if (formatterCfg.isFunction(prev.getString())
+ && token.getString().equals("(")) { //$NON-NLS-1$
+ continue;
+ }
+ if (token.getType() == TokenType.VALUE && prev.getType() == TokenType.NAME) {
+ // Do not add space between name and value [JDBC:MSSQL]
+ continue;
+ }
+ if (token.getType() == TokenType.SYMBOL && isEmbeddedToken(token) ||
+ prev.getType() == TokenType.SYMBOL && isEmbeddedToken(prev))
+ {
+ // Do not insert spaces around colons
+ continue;
+ }
+ if (token.getType() == TokenType.SYMBOL && prev.getType() == TokenType.SYMBOL) {
+ // Do not add space between symbols
+ continue;
+ }
+ argList.add(index, new FormatterToken(TokenType.SPACE, " ")); //$NON-NLS-1$
+ }
+ }
+
+ return argList;
+ }
+
+ private static boolean isEmbeddedToken(FormatterToken token) {
+ return ":".equals(token.getString()) || ".".equals(token.getString());
+ }
+
+ private boolean isJoinStart(List argList, int index) {
+ // Keyword sequence must start from LEFT, RIGHT, INNER, OUTER or JOIN and must end with JOIN
+ // And we must be in the beginning of sequence
+
+ // check current token
+ if (!ArrayUtils.contains(JOIN_BEGIN, argList.get(index).getString())) {
+ return false;
+ }
+ // check previous token
+ for (int i = index - 1; i >= 0; i--) {
+ FormatterToken token = argList.get(i);
+ if (token.getType() == TokenType.SPACE) {
+ continue;
+ }
+ if (ArrayUtils.contains(JOIN_BEGIN, token.getString())) {
+ // It is not the begin of sequence
+ return false;
+ } else {
+ break;
+ }
+ }
+ // check last token
+ for (int i = index; i < argList.size(); i++) {
+ FormatterToken token = argList.get(i);
+ if (token.getType() == TokenType.SPACE) {
+ continue;
+ }
+ if (token.getString().equals("JOIN")) {
+ return true;
+ }
+ if (!ArrayUtils.contains(JOIN_BEGIN, token.getString())) {
+ // It is not the begin of sequence
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private int insertReturnAndIndent(final List argList, final int argIndex, final int argIndent)
+ {
+ if (functionBracket.contains(Boolean.TRUE))
+ return 0;
+ try {
+ String s = GeneralUtils.getDefaultLineSeparator();
+ if (argIndex > 0) {
+ final FormatterToken prevToken = argList.get(argIndex - 1);
+ if (prevToken.getType() == TokenType.COMMENT &&
+ SQLUtils.isCommentLine(formatterCfg.getSyntaxManager().getDialect(), prevToken.getString()))
+ {
+ s = ""; //$NON-NLS-1$
+ }
+ }
+ for (int index = 0; index < argIndent; index++) {
+ s += formatterCfg.getIndentString();
+ }
+
+ FormatterToken token = argList.get(argIndex);
+ if (token.getType() == TokenType.SPACE) {
+ token.setString(s);
+ return 0;
+ }
+ boolean isDelimiter = statementDelimiters.contains(token.getString().toUpperCase());
+
+ if (!isDelimiter) {
+ token = argList.get(argIndex - 1);
+ if (token.getType() == TokenType.SPACE) {
+ token.setString(s);
+ return 0;
+ }
+ }
+
+ if (isDelimiter) {
+ if (argList.size() > argIndex + 1) {
+ argList.add(argIndex + 1, new FormatterToken(TokenType.SPACE, s + s));
+ }
+ } else {
+ argList.add(argIndex, new FormatterToken(TokenType.SPACE, s));
+ }
+ return 1;
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
}
\ No newline at end of file