From 3cd6a0c7c7dfc34b6de6549cbb4f020fdb2e0ffb Mon Sep 17 00:00:00 2001 From: Serge Rider Date: Sat, 28 Dec 2019 15:54:41 +0300 Subject: [PATCH] #7494 SQL rule manager refactoring Former-commit-id: dd99ce3f1265240193aae7eada4f8d2d9b8b5fce --- .../model/sql/parser/SQLRuleManager.java | 258 ++++++++++++++++++ .../sql/parser/rules/LineCommentRule.java | 113 ++++++++ .../sql/parser/rules/NestedMultiLineRule.java | 143 ++++++++++ .../sql/parser/rules/SQLCommandRule.java | 48 ++++ .../sql/parser/rules/SQLDelimiterRule.java | 120 ++++++++ .../sql/parser/rules/SQLParameterRule.java | 93 +++++++ .../sql/parser/rules/SQLVariableRule.java | 80 ++++++ .../model/sql/parser/rules/SQLWordRule.java | 122 +++++++++ .../model/text/parser/TPRuleBasedScanner.java | 195 +++++++++++++ .../model/text/parser/TPTokenScanner.java | 52 ++++ .../dbeaver/ui/editors/sql/SQLEditorBase.java | 8 +- .../SQLEditorSourceViewerConfiguration.java | 2 +- .../sql/convert/ISQLTextConverter.java | 4 +- .../sql/convert/impl/HTMLSQLConverter.java | 4 +- .../convert/impl/SourceCodeSQLConverter.java | 4 +- .../convert/impl/UnformattedSQLConverter.java | 4 +- ...QLRuleManager.java => SQLRuleScanner.java} | 50 +--- .../sql/syntax/rules/SQLDelimiterSetRule.java | 6 +- .../sql/syntax/rules/SQLFullLineRule.java | 7 +- 19 files changed, 1246 insertions(+), 67 deletions(-) create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/SQLRuleManager.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/LineCommentRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/NestedMultiLineRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLCommandRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLDelimiterRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLParameterRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLVariableRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLWordRule.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPRuleBasedScanner.java create mode 100644 plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPTokenScanner.java rename plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/{SQLRuleManager.java => SQLRuleScanner.java} (90%) diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/SQLRuleManager.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/SQLRuleManager.java new file mode 100644 index 0000000000..e46f4a1e7f --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/SQLRuleManager.java @@ -0,0 +1,258 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser; + +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.rules.*; +import org.eclipse.swt.SWT; +import org.eclipse.ui.IEditorInput; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.model.DBPDataSource; +import org.jkiss.dbeaver.model.DBPDataSourceContainer; +import org.jkiss.dbeaver.model.sql.SQLConstants; +import org.jkiss.dbeaver.model.sql.SQLDialect; +import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; +import org.jkiss.dbeaver.model.sql.parser.rules.SQLDelimiterRule; +import org.jkiss.dbeaver.model.sql.parser.rules.SQLWordRule; +import org.jkiss.dbeaver.model.sql.registry.SQLCommandHandlerDescriptor; +import org.jkiss.dbeaver.model.sql.registry.SQLCommandsRegistry; +import org.jkiss.dbeaver.model.text.parser.TPRule; +import org.jkiss.dbeaver.runtime.sql.SQLRuleProvider; +import org.jkiss.dbeaver.ui.editors.text.TextWhiteSpaceDetector; +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; + +/** + * SQLRuleManager. + * + * Contains information about some concrete datasource underlying database syntax. + * Support runtime change of datasource (reloads syntax information) + */ +public class SQLRuleManager { + + @NotNull + private TPRule[] allRules = new TPRule[0]; + @NotNull + private SQLSyntaxManager syntaxManager; + private boolean evalMode; + + public SQLRuleManager(@NotNull SQLSyntaxManager syntaxManager) { + this.syntaxManager = syntaxManager; + } + + @NotNull + public TPRule[] getAllRules() { + return allRules; + } + + public boolean isEvalMode() { + return evalMode; + } + + public void startEval() { + this.evalMode = true; + } + + public void endEval() { + this.evalMode = false; + { + for (TPRule rule : allRules) { + if (rule instanceof SQLDelimiterRule) { + ((SQLDelimiterRule) rule).changeDelimiter(null); + } + } + } + } + + public void dispose() { + } + + public void refreshRules(@Nullable DBPDataSource dataSource, @Nullable IEditorInput editorInput, boolean minimalRules) { + SQLDialect dialect = syntaxManager.getDialect(); + SQLRuleProvider ruleProvider = GeneralUtils.adapt(dialect, SQLRuleProvider.class); + DBPDataSourceContainer dataSourceContainer = dataSource == null ? null : dataSource.getContainer(); + + final IToken keywordToken = new Token( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_KEYWORD), null, keywordStyle)); + final IToken typeToken = new Token( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_DATATYPE), null, keywordStyle)); + final IToken stringToken = new Token( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_STRING), null, SWT.NORMAL)); + final IToken quotedToken = new Token( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_DATATYPE), null, SWT.NORMAL)); + final IToken numberToken = new Token( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_NUMBER), null, SWT.NORMAL)); + final IToken commentToken = new SQLCommentToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_COMMENT), null, SWT.NORMAL)); + final SQLDelimiterToken delimiterToken = new SQLDelimiterToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_DELIMITER, SWT.COLOR_RED), null, SWT.NORMAL)); + final SQLParameterToken parameterToken = new SQLParameterToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_PARAMETER, SWT.COLOR_DARK_BLUE), null, keywordStyle)); + final SQLVariableToken variableToken = new SQLVariableToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_PARAMETER, SWT.COLOR_DARK_BLUE), null, keywordStyle)); + final IToken otherToken = new Token( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_TEXT), null, SWT.NORMAL)); + final SQLBlockHeaderToken blockHeaderToken = new SQLBlockHeaderToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_KEYWORD), null, keywordStyle)); + final SQLBlockBeginToken blockBeginToken = new SQLBlockBeginToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_KEYWORD), null, keywordStyle)); + final SQLBlockEndToken blockEndToken = new SQLBlockEndToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_KEYWORD), null, keywordStyle)); + + setDefaultReturnToken(otherToken); + List rules = new ArrayList<>(); + + // Add rule for single-line comments. + for (String lineComment : dialect.getSingleLineComments()) { + if (lineComment.startsWith("^")) { + rules.add(new LineCommentRule(lineComment, commentToken)); //$NON-NLS-1$ + } else { + rules.add(new EndOfLineRule(lineComment, commentToken)); //$NON-NLS-1$ + } + } + + if (ruleProvider != null) { + ruleProvider.extendRules(dataSourceContainer, rules, SQLRuleProvider.RulePosition.CONTROL); + } + + if (!minimalRules) { + final SQLControlToken controlToken = new SQLControlToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_COMMAND), null, keywordStyle)); + + String commandPrefix = syntaxManager.getControlCommandPrefix(); + + // Control rules + for (SQLCommandHandlerDescriptor controlCommand : SQLCommandsRegistry.getInstance().getCommandHandlers()) { + rules.add(new SQLCommandRule(commandPrefix, controlCommand, controlToken)); //$NON-NLS-1$ + } + } + { + if (!minimalRules && syntaxManager.isVariablesEnabled()) { + // Variable rule + rules.add(new SQLVariableRule(parameterToken)); + } + } + + if (!minimalRules) { + // Add rules for delimited identifiers and string literals. + char escapeChar = syntaxManager.getEscapeChar(); + String[][] identifierQuoteStrings = syntaxManager.getIdentifierQuoteStrings(); + String[][] stringQuoteStrings = syntaxManager.getStringQuoteStrings(); + + boolean hasDoubleQuoteRule = false; + if (!ArrayUtils.isEmpty(identifierQuoteStrings)) { + for (String[] quotes : identifierQuoteStrings) { + rules.add(new SingleLineRule(quotes[0], quotes[1], quotedToken, escapeChar)); + if (quotes[1].equals(SQLConstants.STR_QUOTE_DOUBLE) && quotes[0].equals(quotes[1])) { + hasDoubleQuoteRule = true; + } + } + } + if (!ArrayUtils.isEmpty(stringQuoteStrings)) { + for (String[] quotes : stringQuoteStrings) { + rules.add(new MultiLineRule(quotes[0], quotes[1], stringToken, escapeChar)); + } + } + if (!hasDoubleQuoteRule) { + rules.add(new MultiLineRule(SQLConstants.STR_QUOTE_DOUBLE, SQLConstants.STR_QUOTE_DOUBLE, quotedToken, escapeChar)); + } + } + if (ruleProvider != null) { + ruleProvider.extendRules(dataSourceContainer, rules, SQLRuleProvider.RulePosition.QUOTES); + } + + // Add rules for multi-line comments + Pair multiLineComments = dialect.getMultiLineComments(); + if (multiLineComments != null) { + rules.add(new MultiLineRule(multiLineComments.getFirst(), multiLineComments.getSecond(), commentToken, (char) 0, true)); + } + + if (!minimalRules) { + // Add generic whitespace rule. + rules.add(new WhitespaceRule(new TextWhiteSpaceDetector())); + + // Add numeric rule + rules.add(new NumberRule(numberToken)); + } + + SQLDelimiterRule delimRule = new SQLDelimiterRule(syntaxManager.getStatementDelimiters(), delimiterToken); + rules.add(delimRule); + + { + // Delimiter redefine + String delimRedefine = dialect.getScriptDelimiterRedefiner(); + if (!CommonUtils.isEmpty(delimRedefine)) { + final SQLSetDelimiterToken setDelimiterToken = new SQLSetDelimiterToken( + new TextAttribute(getColor(SQLConstants.CONFIG_COLOR_COMMAND), null, keywordStyle)); + + rules.add(new SQLDelimiterSetRule(delimRedefine, setDelimiterToken, delimRule)); + } + } + + if (ruleProvider != null) { + ruleProvider.extendRules(dataSourceContainer, rules, SQLRuleProvider.RulePosition.KEYWORDS); + } + + if (!minimalRules) { + + // Add word rule for keywords, types, and constants. + SQLWordRule wordRule = new SQLWordRule(delimRule, otherToken); + for (String reservedWord : dialect.getReservedWords()) { + wordRule.addWord(reservedWord, keywordToken); + } + if (dataSource != null) { + for (String function : dialect.getFunctions(dataSource)) { + wordRule.addWord(function, typeToken); + } + for (String type : dialect.getDataTypes(dataSource)) { + wordRule.addWord(type, typeToken); + } + } + final String[] blockHeaderStrings = dialect.getBlockHeaderStrings(); + if (!ArrayUtils.isEmpty(blockHeaderStrings)) { + for (String bhs : blockHeaderStrings) { + wordRule.addWord(bhs, blockHeaderToken); + } + } + String[][] blockBounds = dialect.getBlockBoundStrings(); + if (blockBounds != null) { + for (String[] block : blockBounds) { + if (block.length != 2) { + continue; + } + wordRule.addWord(block[0], blockBeginToken); + wordRule.addWord(block[1], blockEndToken); + } + } + rules.add(wordRule); + + // Parameter rule + for (String npPrefix : syntaxManager.getNamedParameterPrefixes()) { + rules.add(new SQLParameterRule(syntaxManager, parameterToken, npPrefix)); + } + } + + allRules = rules.toArray(new TPRule[0]); + } + +} diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/LineCommentRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/LineCommentRule.java new file mode 100644 index 0000000000..80cd71849c --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/LineCommentRule.java @@ -0,0 +1,113 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.EndOfLineRule; +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.Token; + +/** + * The same as end-of-line rule but matches word in case-insensitive fashion + + * needs whitespace after last letter or digit + */ +public class LineCommentRule extends EndOfLineRule +{ + public LineCommentRule(String startSequence, IToken token) { + super(startSequence, token, (char) 0); + } + + public LineCommentRule(String startSequence, IToken token, char escapeCharacter) { + super(startSequence, token, escapeCharacter); + } + + public LineCommentRule(String startSequence, IToken token, char escapeCharacter, boolean escapeContinuesLine) { + super(startSequence, token, escapeCharacter, escapeContinuesLine); + } + + protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) { + + if (resume) { + + if (endSequenceDetected(scanner)) + return fToken; + + } else { +/* + // Check we are at the line beginning + for (;;) { + scanner.unread(); + int c = scanner.read(); + } + +*/ + int c= scanner.read(); + if (Character.toUpperCase(c) == Character.toUpperCase(fStartSequence[0])) { + if (sequenceDetected(scanner, fStartSequence, false)) { + if (endSequenceDetected(scanner)) + return fToken; + } + } + } + + scanner.unread(); + return Token.UNDEFINED; + } + + @Override + public IToken evaluate(ICharacterScanner scanner, boolean resume) { + if (fColumn == UNDEFINED) + return doEvaluate(scanner, resume); + + int c= scanner.read(); + scanner.unread(); + if (Character.toUpperCase(c) == Character.toUpperCase(fStartSequence[0])) + return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED); + return Token.UNDEFINED; + } + + @Override + protected boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed) { + for (int i= 1; i < sequence.length; i++) { + int c= scanner.read(); + if (c == ICharacterScanner.EOF && eofAllowed) { + return true; + } else if (Character.toUpperCase(c) != Character.toUpperCase(sequence[i])) { + // Non-matching character detected, rewind the scanner back to the start. + // Do not unread the first character. + scanner.unread(); + for (int j= i-1; j > 0; j--) + scanner.unread(); + return false; + } + } + + if (Character.isLetterOrDigit(sequence[sequence.length - 1])) { + // Check for trailing whitespace + int lastChar = scanner.read(); + scanner.unread(); + if (lastChar != ICharacterScanner.EOF) { + if (!Character.isWhitespace((char) lastChar)) { + return false; + } + } + } + + return true; + } + +} \ No newline at end of file diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/NestedMultiLineRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/NestedMultiLineRule.java new file mode 100644 index 0000000000..cdc13e9776 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/NestedMultiLineRule.java @@ -0,0 +1,143 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.MultiLineRule; +import org.eclipse.jface.text.rules.Token; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLPartitionScanner; + +public class NestedMultiLineRule extends MultiLineRule +{ + protected int _commentNestingDepth = 0; + + public NestedMultiLineRule(String startSequence, String endSequence, IToken token) + { + super(startSequence, endSequence, token); + } + + public NestedMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter) + { + super(startSequence, endSequence, token, escapeCharacter); + } + + public NestedMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter, + boolean breaksOnEOF) + { + super(startSequence, endSequence, token, escapeCharacter, breaksOnEOF); + } + + @Override + protected boolean endSequenceDetected(ICharacterScanner scanner) + { + int c; + //char[][] delimiters = scanner.getLegalLineDelimiters(); + //boolean previousWasEscapeCharacter = false; + while ((c = scanner.read()) != ICharacterScanner.EOF) + { + if (c == fEscapeCharacter) + { + // Skip the escaped character. + scanner.read(); + } + else if (fEndSequence.length > 0 && c == fEndSequence[0]) + { + // Check if the specified end sequence has been found. + if (sequenceDetected(scanner, fEndSequence, true)) + { + _commentNestingDepth--; + } + if (_commentNestingDepth <= 0) + { + return true; + } + } + else if (fStartSequence.length > 0 && c == fStartSequence[0]) + { + // Check if the nested start sequence has been found. + if (sequenceDetected(scanner, fStartSequence, false)) + { + _commentNestingDepth++; + } + } + //previousWasEscapeCharacter = (c == fEscapeCharacter); + } + if (fBreaksOnEOF) + { + return true; + } + scanner.unread(); + return false; + } + + @Override + protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) + { + if (resume) + { + _commentNestingDepth = 0; + if (scanner instanceof SQLPartitionScanner) + { + String scanned = ((SQLPartitionScanner) scanner).getScannedPartitionString(); + if (scanned != null && scanned.length() > 0) + { + String startSequence = new String(fStartSequence); + int index = 0; + while ((index = scanned.indexOf(startSequence, index)) >= 0) + { + index++; + _commentNestingDepth++; + } + //must be aware of the closing sequences + String endSequence = new String(fEndSequence); + index = 0; + while ((index = scanned.indexOf(endSequence, index)) >= 0) + { + index++; + _commentNestingDepth--; + } + } + } + if (endSequenceDetected(scanner)) + { + return fToken; + } + + } + else + { + + int c = scanner.read(); + if (c == fStartSequence[0]) + { + if (sequenceDetected(scanner, fStartSequence, false)) + { + _commentNestingDepth = 1; + if (endSequenceDetected(scanner)) + { + return fToken; + } + } + } + } + + scanner.unread(); + return Token.UNDEFINED; + + } +} \ No newline at end of file diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLCommandRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLCommandRule.java new file mode 100644 index 0000000000..4b78e5c796 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLCommandRule.java @@ -0,0 +1,48 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.EndOfLineRule; +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.jkiss.dbeaver.model.sql.registry.SQLCommandHandlerDescriptor; +import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLControlToken; + +public class SQLCommandRule extends EndOfLineRule { + public SQLCommandRule(String commandPrefix, SQLCommandHandlerDescriptor controlCommand, SQLControlToken controlToken) { + super(commandPrefix + controlCommand.getId() + ' ', controlToken); + } + + protected boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed) { + for (int i= 1; i < sequence.length; i++) { + int c= scanner.read(); + char seqChar = sequence[i]; + boolean validChar = (seqChar == ' ' && Character.isWhitespace(c)) || + Character.toUpperCase(c) == Character.toUpperCase(seqChar); + if (!validChar) { + // Non-matching character detected, rewind the scanner back to the start. + // Do not unread the first character. + scanner.unread(); + for (int j= i-1; j > 0; j--) + scanner.unread(); + return false; + } + } + + return true; + } + +} diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLDelimiterRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLDelimiterRule.java new file mode 100644 index 0000000000..d92377a7f0 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLDelimiterRule.java @@ -0,0 +1,120 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.jkiss.dbeaver.model.text.parser.TPCharacterScanner; +import org.jkiss.dbeaver.model.text.parser.TPRule; +import org.jkiss.dbeaver.model.text.parser.TPToken; +import org.jkiss.dbeaver.model.text.parser.TPTokenAbstract; +import org.jkiss.utils.CommonUtils; + +import java.util.Locale; + +/** +* DelimiterRule +*/ +public class SQLDelimiterRule implements TPRule { + private final TPToken token; + private char[][] delimiters, origDelimiters; + private char[] buffer, origBuffer; + + public SQLDelimiterRule(String[] delimiters, TPToken token) { + this.token = token; + this.origDelimiters = this.delimiters = new char[delimiters.length][]; + int index = 0, maxLength = 0; + for (String delim : delimiters) { + this.delimiters[index] = delim.toCharArray(); + for (int i = 0; i < this.delimiters[index].length; i++) { + this.delimiters[index][i] = Character.toUpperCase(this.delimiters[index][i]); + } + maxLength = Math.max(maxLength, this.delimiters[index].length); + index++; + } + this.origBuffer = this.buffer = new char[maxLength]; + } + + public char[][] getDelimiters() { + return delimiters; + } + + @Override + public TPToken evaluate(TPCharacterScanner scanner) { + for (int i = 0; ; i++) { + int c = scanner.read(); + boolean matches = false; + if (c != ICharacterScanner.EOF) { + c = Character.toUpperCase(c); + for (int k = 0; k < delimiters.length; k++) { + if (i < delimiters[k].length && delimiters[k][i] == c) { + buffer[i] = (char)c; + if (i == delimiters[k].length - 1 && equalsBegin(delimiters[k])) { + // Matched. Check next character + if (Character.isLetterOrDigit(c)) { + int cn = scanner.read(); + scanner.unread(); + if (Character.isLetterOrDigit(cn)) { + matches = false; + continue; + } + } + return token; + } + matches = true; + break; + } + } + } + if (!matches) { + for (int k = 0; k <= i; k++) { + scanner.unread(); + } + return TPTokenAbstract.UNDEFINED; + } + } + } + + private boolean equalsBegin(char[] delimiter) { + for (int i = 0; i < delimiter.length; i++) { + if (buffer[i] != delimiter[i]) { + return false; + } + } + return true; + } + + public void changeDelimiter(String newDelimiter) { + if (CommonUtils.isEmpty(newDelimiter)) { + this.delimiters = this.origDelimiters; + this.buffer = this.origBuffer; + } else { + for (char[] delim : delimiters) { + String delimStr = String.valueOf(delim); + if (newDelimiter.equals(delimStr)) { + return; + } + if (newDelimiter.endsWith(delimStr)) { + // New delimiter ends with old delimiter (as command terminator). Remove it. + newDelimiter = newDelimiter.substring(0, newDelimiter.length() - delimStr.length()).trim(); + } + } + this.delimiters = new char[1][]; + this.delimiters[0] = newDelimiter.toUpperCase(Locale.ENGLISH).toCharArray(); + this.buffer = new char[newDelimiter.length()]; + } + } +} diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLParameterRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLParameterRule.java new file mode 100644 index 0000000000..aeea6d18c5 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLParameterRule.java @@ -0,0 +1,93 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; +import org.jkiss.dbeaver.model.text.parser.TPCharacterScanner; +import org.jkiss.dbeaver.model.text.parser.TPRule; +import org.jkiss.dbeaver.model.text.parser.TPToken; +import org.jkiss.dbeaver.model.text.parser.TPTokenAbstract; +import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLParameterToken; + +/** +* SQL parameter rule +*/ +public class SQLParameterRule implements TPRule { + private final SQLSyntaxManager syntaxManager; + private final SQLParameterToken parameterToken; + private final StringBuilder buffer; + private final char anonymousParameterMark; + private final String namedParameterPrefix; + + public SQLParameterRule(SQLSyntaxManager syntaxManager, SQLParameterToken parameterToken, String prefix) { + this.syntaxManager = syntaxManager; + this.parameterToken = parameterToken; + this.buffer = new StringBuilder(); + this.anonymousParameterMark = syntaxManager.getAnonymousParameterMark(); + this.namedParameterPrefix = prefix; + } + + @Override + public TPToken evaluate(TPCharacterScanner scanner) + { + scanner.unread(); + int prevChar = scanner.read(); + if (Character.isJavaIdentifierPart(prevChar) || + prevChar == namedParameterPrefix.charAt(0) || prevChar == anonymousParameterMark || prevChar == '\\' || prevChar == '/') + { + return TPTokenAbstract.UNDEFINED; + } + int c = scanner.read(); + if (c != ICharacterScanner.EOF && (c == anonymousParameterMark || c == namedParameterPrefix.charAt(0))) { + buffer.setLength(0); + do { + buffer.append((char) c); + c = scanner.read(); + } while (c != ICharacterScanner.EOF && Character.isJavaIdentifierPart(c)); + scanner.unread(); + + // Check for parameters + if (syntaxManager.isAnonymousParametersEnabled()) { + if (buffer.length() == 1 && buffer.charAt(0) == anonymousParameterMark) { + return parameterToken; + } + } + if (syntaxManager.isParametersEnabled()) { + if (buffer.charAt(0) == namedParameterPrefix.charAt(0) && buffer.length() > 1) { + boolean validChars = true; + for (int i = 1; i < buffer.length(); i++) { + if (!Character.isJavaIdentifierPart(buffer.charAt(i))) { + validChars = false; + break; + } + } + if (validChars) { + return parameterToken; + } + } + } + + for (int i = buffer.length() - 1; i >= 0; i--) { + scanner.unread(); + } + } else { + scanner.unread(); + } + return TPTokenAbstract.UNDEFINED; + } +} diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLVariableRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLVariableRule.java new file mode 100644 index 0000000000..85f133362a --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLVariableRule.java @@ -0,0 +1,80 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.jkiss.dbeaver.model.sql.SQLQueryParameter; +import org.jkiss.dbeaver.model.text.parser.TPCharacterScanner; +import org.jkiss.dbeaver.model.text.parser.TPRule; +import org.jkiss.dbeaver.model.text.parser.TPToken; +import org.jkiss.dbeaver.model.text.parser.TPTokenAbstract; + +/** +* SQL variable rule. +* ${varName} +*/ +public class SQLVariableRule implements TPRule { + + private final TPToken parameterToken; + + public SQLVariableRule(TPToken parameterToken) { + this.parameterToken = parameterToken; + } + + @Override + public TPToken evaluate(TPCharacterScanner scanner) + { + int c = scanner.read(); + if (c == '$') { + int prefixLength = 0; + c = scanner.read(); + if (SQLQueryParameter.supportsJasperSyntax()) { + if (c == 'P') { + c = scanner.read(); + prefixLength++; + if (c == '!') { + c = scanner.read(); + prefixLength++; + } + } + } + if (c == '{') { + int varLength = 0; + for (;;) { + c = scanner.read(); + if (c == '}' || Character.isWhitespace(c) || c == ICharacterScanner.EOF) { + break; + } + varLength++; + } + if (varLength > 0 && c == '}') { + return parameterToken; + } + scanner.unread(); + + for (int i = varLength - 1 + prefixLength; i >= 0; i--) { + scanner.unread(); + } + } + scanner.unread(); + } + scanner.unread(); + + return TPTokenAbstract.UNDEFINED; + } + +} diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLWordRule.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLWordRule.java new file mode 100644 index 0000000000..1616e8ba3d --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/parser/rules/SQLWordRule.java @@ -0,0 +1,122 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.parser.rules; + +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.jkiss.dbeaver.model.text.parser.TPCharacterScanner; +import org.jkiss.dbeaver.model.text.parser.TPRule; +import org.jkiss.dbeaver.model.text.parser.TPToken; +import org.jkiss.dbeaver.model.text.parser.TPTokenAbstract; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Smart word detector + */ +public class SQLWordRule implements TPRule { + + private SQLDelimiterRule delimRule; + private TPToken defaultToken; + private Map fWords = new HashMap<>(); + private StringBuilder fBuffer = new StringBuilder(); + private char[][] delimiters; + + public SQLWordRule(SQLDelimiterRule delimRule, TPToken defaultToken) { + this.delimRule = delimRule; + this.defaultToken = defaultToken; + } + + public boolean hasWord(String word) { + return fWords.containsKey(word.toLowerCase()); + } + + public void addWord(String word, TPToken token) { + fWords.put(word.toLowerCase(), token); + } + + @Override + public TPToken evaluate(TPCharacterScanner scanner) { + int c = scanner.read(); + if (c != ICharacterScanner.EOF && Character.isUnicodeIdentifierStart(c)) { + fBuffer.setLength(0); + delimiters = delimRule.getDelimiters(); + do { + fBuffer.append((char) c); + c = scanner.read(); + } while (c != TPCharacterScanner.EOF && isWordPart((char) c, scanner)); + scanner.unread(); + + String buffer = fBuffer.toString().toLowerCase(); + TPToken token = fWords.get(buffer); + + if (token != null) + return token; + + if (defaultToken.isUndefined()) + unreadBuffer(scanner); + + return defaultToken; + } + + scanner.unread(); + return TPTokenAbstract.UNDEFINED; + } + + private boolean isWordPart(char c, TPCharacterScanner scanner) { + if (!Character.isUnicodeIdentifierPart(c) && c != '$') { + return false; + } + // Check for delimiter + for (char[] wordDelimiter : delimiters) { + if (!Character.isLetter(c) && c == wordDelimiter[0]) { + if (wordDelimiter.length == 1) { + return false; + } + int charsRead = 0; + boolean matches = true; + for (int i = 1; i < wordDelimiter.length; i++) { + int c2 = scanner.read(); + charsRead++; + if (c2 == ICharacterScanner.EOF) { + break; + } + if (c2 != wordDelimiter[i]) { + matches = false; + break; + } + } + for (int i = 0; i < charsRead; i++) { + scanner.unread(); + } + if (matches) { + return false; + } + } + } + + return true; + } + + private void unreadBuffer(TPCharacterScanner scanner) { + for (int i = fBuffer.length() - 1; i >= 0; i--) { + scanner.unread(); + } + } + +} diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPRuleBasedScanner.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPRuleBasedScanner.java new file mode 100644 index 0000000000..084fb1c500 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPRuleBasedScanner.java @@ -0,0 +1,195 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.text.parser; + + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; + + +/** + * Rule based text scanner + */ +public class TPRuleBasedScanner implements TPCharacterScanner, TPTokenScanner { + + /** The list of rules of this scanner */ + private TPRule[] fRules; + /** The token to be returned by default if no rule fires */ + private TPToken fDefaultReturnToken; + /** The document to be scanned */ + private IDocument fDocument; + /** The cached legal line delimiters of the document */ + private char[][] fDelimiters; + /** The offset of the next character to be read */ + private int fOffset; + /** The end offset of the range to be scanned */ + private int fRangeEnd; + /** The offset of the last read token */ + private int fTokenOffset; + /** The cached column of the current scanner position */ + private int fColumn; + /** Internal setting for the un-initialized column cache. */ + private static final int UNDEFINED= -1; + + /** + * Creates a new rule based scanner which does not have any rule. + */ + public TPRuleBasedScanner() { + } + + /** + * Configures the scanner with the given sequence of rules. + * + * @param rules the sequence of rules controlling this scanner + */ + public void setRules(TPRule[] rules) { + if (rules != null) { + fRules= new TPRule[rules.length]; + System.arraycopy(rules, 0, fRules, 0, rules.length); + } else + fRules= null; + } + + /** + * Configures the scanner's default return token. This is the token + * which is returned when none of the rules fired and EOF has not been + * reached. + * + * @param defaultReturnToken the default return token + * @since 2.0 + */ + public void setDefaultReturnToken(TPToken defaultReturnToken) { + Assert.isNotNull(defaultReturnToken.getData()); + fDefaultReturnToken= defaultReturnToken; + } + + @Override + public void setRange(final IDocument document, int offset, int length) { + Assert.isLegal(document != null); + final int documentLength= document.getLength(); + checkRange(offset, length, documentLength); + + fDocument= document; + fOffset= offset; + fColumn= UNDEFINED; + fRangeEnd= offset + length; + + String[] delimiters= fDocument.getLegalLineDelimiters(); + fDelimiters= new char[delimiters.length][]; + for (int i= 0; i < delimiters.length; i++) + fDelimiters[i]= delimiters[i].toCharArray(); + + if (fDefaultReturnToken == null) + fDefaultReturnToken= TPTokenAbstract.UNDEFINED; + } + + /** + * Checks that the given range is valid. + * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=69292 + * + * @param offset the offset of the document range to scan + * @param length the length of the document range to scan + * @param documentLength the document's length + * @since 3.3 + */ + private void checkRange(int offset, int length, int documentLength) { + Assert.isLegal(offset > -1); + Assert.isLegal(length > -1); + Assert.isLegal(offset + length <= documentLength); + } + + @Override + public int getTokenOffset() { + return fTokenOffset; + } + + @Override + public int getTokenLength() { + if (fOffset < fRangeEnd) + return fOffset - getTokenOffset(); + return fRangeEnd - getTokenOffset(); + } + + + @Override + public int getColumn() { + if (fColumn == UNDEFINED) { + try { + int line= fDocument.getLineOfOffset(fOffset); + int start= fDocument.getLineOffset(line); + + fColumn= fOffset - start; + + } catch (BadLocationException ex) { + } + } + return fColumn; + } + + @Override + public char[][] getLegalLineDelimiters() { + return fDelimiters; + } + + @Override + public TPToken nextToken() { + + fTokenOffset= fOffset; + fColumn= UNDEFINED; + + if (fRules != null) { + for (TPRule fRule : fRules) { + TPToken token= (fRule.evaluate(this)); + if (!token.isUndefined()) + return token; + } + } + + if (read() == EOF) + return TPTokenAbstract.EOF; + return fDefaultReturnToken; + } + + @Override + public int read() { + + try { + + if (fOffset < fRangeEnd) { + try { + return fDocument.getChar(fOffset); + } catch (BadLocationException e) { + } + } + + return EOF; + + } finally { + ++ fOffset; + fColumn= UNDEFINED; + } + } + + @Override + public void unread() { + --fOffset; + fColumn= UNDEFINED; + } +} + + diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPTokenScanner.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPTokenScanner.java new file mode 100644 index 0000000000..d34aada389 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/text/parser/TPTokenScanner.java @@ -0,0 +1,52 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2019 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.text.parser; + +import org.eclipse.jface.text.IDocument; + +/** + * Token scanner + */ +public interface TPTokenScanner { + + /** + * Configures the scanner by providing access to the document range that should + * be scanned. + */ + void setRange(IDocument document, int offset, int length); + + /** + * Returns the next token in the document. + * + * @return the next token in the document + */ + TPToken nextToken(); + + /** + * Returns the offset of the last token read by this scanner. + * + * @return the offset of the last token read by this scanner + */ + int getTokenOffset(); + + /** + * Returns the length of the last token read by this scanner. + * + * @return the length of the last token read by this scanner + */ + int getTokenLength(); +} diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorBase.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorBase.java index 91803bebb1..78c81b1ae3 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorBase.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorBase.java @@ -66,7 +66,7 @@ import org.jkiss.dbeaver.ui.editors.sql.preferences.*; import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLCharacterPairMatcher; import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLEditorCompletionContext; import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLPartitionScanner; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleScanner; import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLControlToken; import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLToken; import org.jkiss.dbeaver.ui.editors.sql.templates.SQLTemplatesPage; @@ -107,7 +107,7 @@ public abstract class SQLEditorBase extends BaseTextEditor implements DBPContext @NotNull private final SQLSyntaxManager syntaxManager; @NotNull - private final SQLRuleManager ruleManager; + private final SQLRuleScanner ruleManager; private ProjectionSupport projectionSupport; private ProjectionAnnotationModel annotationModel; @@ -132,7 +132,7 @@ public abstract class SQLEditorBase extends BaseTextEditor implements DBPContext public SQLEditorBase() { super(); syntaxManager = new SQLSyntaxManager(); - ruleManager = new SQLRuleManager(syntaxManager); + ruleManager = new SQLRuleScanner(syntaxManager); themeListener = new IPropertyChangeListener() { long lastUpdateTime = 0; @@ -267,7 +267,7 @@ public abstract class SQLEditorBase extends BaseTextEditor implements DBPContext } @NotNull - public SQLRuleManager getRuleManager() { + public SQLRuleScanner getRuleManager() { return ruleManager; } diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorSourceViewerConfiguration.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorSourceViewerConfiguration.java index 0fa16d700b..5fa848ed45 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorSourceViewerConfiguration.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditorSourceViewerConfiguration.java @@ -63,7 +63,7 @@ public class SQLEditorSourceViewerConfiguration extends TextSourceViewerConfigur * The editor with which this configuration is associated. */ private SQLEditorBase editor; - private SQLRuleManager ruleManager; + private SQLRuleScanner ruleManager; private IContentAssistProcessor completionProcessor; private IHyperlinkDetector hyperlinkDetector; diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/ISQLTextConverter.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/ISQLTextConverter.java index 2191e49556..240f278582 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/ISQLTextConverter.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/ISQLTextConverter.java @@ -21,7 +21,7 @@ import org.eclipse.jface.text.IDocument; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleScanner; import java.util.Map; @@ -34,7 +34,7 @@ public interface ISQLTextConverter { String convertText( @NotNull SQLDialect dialect, @NotNull SQLSyntaxManager syntaxManager, - @NotNull SQLRuleManager ruleManager, + @NotNull SQLRuleScanner ruleManager, @NotNull IDocument document, int startPos, int length, diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/HTMLSQLConverter.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/HTMLSQLConverter.java index a1a420af3d..7112ca627d 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/HTMLSQLConverter.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/HTMLSQLConverter.java @@ -28,7 +28,7 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; import org.jkiss.dbeaver.ui.editors.sql.convert.ISQLTextConverter; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleScanner; import java.util.Map; @@ -43,7 +43,7 @@ public class HTMLSQLConverter implements ISQLTextConverter { public String convertText( @NotNull SQLDialect dialect, @NotNull SQLSyntaxManager syntaxManager, - @NotNull SQLRuleManager ruleManager, + @NotNull SQLRuleScanner ruleManager, @NotNull IDocument document, int startPos, int length, diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/SourceCodeSQLConverter.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/SourceCodeSQLConverter.java index 89588855e8..bac32795bf 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/SourceCodeSQLConverter.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/SourceCodeSQLConverter.java @@ -24,7 +24,7 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; import org.jkiss.dbeaver.ui.editors.sql.convert.ISQLTextConverter; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleScanner; import org.jkiss.utils.CommonUtils; import java.util.Map; @@ -45,7 +45,7 @@ public abstract class SourceCodeSQLConverter implements ISQLTextConverter { public String convertText( @NotNull SQLDialect dialect, @NotNull SQLSyntaxManager syntaxManager, - @NotNull SQLRuleManager ruleManager, + @NotNull SQLRuleScanner ruleManager, @NotNull IDocument document, int startPos, int length, diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/UnformattedSQLConverter.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/UnformattedSQLConverter.java index 67471346ea..f4c54c38b7 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/UnformattedSQLConverter.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/convert/impl/UnformattedSQLConverter.java @@ -25,7 +25,7 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; import org.jkiss.dbeaver.ui.editors.sql.convert.ISQLTextConverter; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleScanner; import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLCommentToken; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.Pair; @@ -43,7 +43,7 @@ public class UnformattedSQLConverter implements ISQLTextConverter { public String convertText( @NotNull SQLDialect dialect, @NotNull SQLSyntaxManager syntaxManager, - @NotNull SQLRuleManager ruleManager, + @NotNull SQLRuleScanner ruleManager, @NotNull IDocument document, int startPos, int length, diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/SQLRuleManager.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/SQLRuleScanner.java similarity index 90% rename from plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/SQLRuleManager.java rename to plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/SQLRuleScanner.java index cf822a7cbf..12ed437788 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/SQLRuleManager.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/SQLRuleScanner.java @@ -33,7 +33,6 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.sql.SQLConstants; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; -import org.jkiss.dbeaver.model.sql.parser.SQLWordDetector; import org.jkiss.dbeaver.model.sql.registry.SQLCommandHandlerDescriptor; import org.jkiss.dbeaver.model.sql.registry.SQLCommandsRegistry; import org.jkiss.dbeaver.model.text.parser.TPCharacterScanner; @@ -57,7 +56,7 @@ import java.util.*; * Contains information about some concrete datasource underlying database syntax. * Support runtime change of datasource (reloads syntax information) */ -public class SQLRuleManager extends RuleBasedScanner implements TPCharacterScanner { +public class SQLRuleScanner extends RuleBasedScanner implements TPCharacterScanner { @NotNull private final IThemeManager themeManager; @@ -70,7 +69,7 @@ public class SQLRuleManager extends RuleBasedScanner implements TPCharacterScann private boolean evalMode; - public SQLRuleManager(@NotNull SQLSyntaxManager syntaxManager) { + public SQLRuleScanner(@NotNull SQLSyntaxManager syntaxManager) { this.syntaxManager = syntaxManager; this.themeManager = PlatformUI.getWorkbench().getThemeManager(); } @@ -317,49 +316,4 @@ public class SQLRuleManager extends RuleBasedScanner implements TPCharacterScann return color; } - private static IWordDetector getWordOrSymbolDetector(String word) { - if (Character.isLetterOrDigit(word.charAt(0))) { - return new WordDetectorAdapter(new SQLWordDetector()); - } else { - // Default delim rule - return new SymbolSequenceDetector(word); - } - } - - private static class WordDetectorAdapter implements IWordDetector { - private final SQLWordDetector wordDetector; - - private WordDetectorAdapter(SQLWordDetector wordDetector) { - this.wordDetector = wordDetector; - } - - @Override - public boolean isWordStart(char c) { - return wordDetector.isWordStart(c); - } - - @Override - public boolean isWordPart(char c) { - return wordDetector.isWordPart(c); - } - } - - private static class SymbolSequenceDetector implements IWordDetector { - private final String delimiter; - - public SymbolSequenceDetector(String delimiter) { - this.delimiter = delimiter; - } - - @Override - public boolean isWordStart(char c) { - return delimiter.charAt(0) == c; - } - - @Override - public boolean isWordPart(char c) { - return delimiter.indexOf(c) != -1; - } - } - } diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLDelimiterSetRule.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLDelimiterSetRule.java index e5c2502ccc..d6d2468f8f 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLDelimiterSetRule.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLDelimiterSetRule.java @@ -20,11 +20,11 @@ import org.eclipse.jface.text.rules.ICharacterScanner; import org.eclipse.jface.text.rules.IRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.Token; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; +import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleScanner; import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLSetDelimiterToken; /** -* Delimiter redefien rule +* Delimiter redefine rule */ public class SQLDelimiterSetRule implements IRule { @@ -86,7 +86,7 @@ public class SQLDelimiterSetRule implements IRule { } scanner.unread(); } - if (scanner instanceof SQLRuleManager && ((SQLRuleManager) scanner).isEvalMode()) { + if (scanner instanceof SQLRuleScanner && ((SQLRuleScanner) scanner).isEvalMode()) { final String newDelimiter = delimLength <= 0 ? delimBuffer.toString().trim() : delimBuffer.substring(0, delimLength).trim(); delimiterRule.changeDelimiter(newDelimiter); diff --git a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLFullLineRule.java b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLFullLineRule.java index 6f41bb1eeb..c198af4ff6 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLFullLineRule.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.sql/src/org/jkiss/dbeaver/ui/editors/sql/syntax/rules/SQLFullLineRule.java @@ -16,9 +16,10 @@ */ package org.jkiss.dbeaver.ui.editors.sql.syntax.rules; -import org.eclipse.jface.text.rules.*; -import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLRuleManager; -import org.jkiss.dbeaver.ui.editors.sql.syntax.tokens.SQLSetDelimiterToken; +import org.eclipse.jface.text.rules.EndOfLineRule; +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.Token; /** * Rule which starts in the beginning of line -- GitLab