提交 3cd6a0c7 编写于 作者: S Serge Rider

#7494 SQL rule manager refactoring


Former-commit-id: dd99ce3f
上级 f61119e0
/*
* 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<TPRule> 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<String, String> 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]);
}
}
/*
* 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
/*
* 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
/*
* 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;
}
}
/*
* 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()];
}
}
}
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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<String, TPToken> 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();
}
}
}
/*
* 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;
}
}
/*
* 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();
}
......@@ -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;
}
......
......@@ -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;
......
......@@ -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,
......
......@@ -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,
......
......@@ -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,
......
......@@ -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,
......
......@@ -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;
}
}
}
......@@ -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);
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册