/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.ui.editors.sql.syntax; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.rules.*; import org.jkiss.dbeaver.model.sql.SQLConstants; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.utils.Pair; import java.util.ArrayList; import java.util.List; /** * The <code>SQLPartitionScanner</code>, a subclass of a <code>RulesBasedPartitionScanner</code>, * is responsible for dynamically computing the partitions of its * SQL document as events signal that the document has * changed. The document partitions are based on tokens that represent comments * and SQL code sections. */ public class SQLPartitionScanner extends RuleBasedPartitionScanner { public final static String SQL_PARTITIONING = "___sql_partitioning"; public final static String CONTENT_TYPE_SQL_COMMENT = "sql_comment"; public final static String CONTENT_TYPE_SQL_MULTILINE_COMMENT = "sql_multiline_comment"; public final static String CONTENT_TYPE_SQL_STRING = "sql_character"; public final static String CONTENT_TYPE_SQL_QUOTED = "sql_quoted"; public final static String[] SQL_CONTENT_TYPES = new String[]{ IDocument.DEFAULT_CONTENT_TYPE, CONTENT_TYPE_SQL_COMMENT, CONTENT_TYPE_SQL_MULTILINE_COMMENT, CONTENT_TYPE_SQL_STRING, CONTENT_TYPE_SQL_QUOTED, }; // Syntax higlight private final List<IPredicateRule> rules = new ArrayList<>(); private final IToken commentToken = new Token(CONTENT_TYPE_SQL_COMMENT); private final IToken multilineCommentToken = new Token(CONTENT_TYPE_SQL_MULTILINE_COMMENT); private final IToken sqlStringToken = new Token(CONTENT_TYPE_SQL_STRING); private final IToken sqlQuotedToken = new Token(CONTENT_TYPE_SQL_QUOTED); /** * Detector for empty comments. */ static class EmptyCommentDetector implements IWordDetector { /* * @see IWordDetector#isWordStart */ @Override public boolean isWordStart(char c) { return (c == '/'); } /* * @see IWordDetector#isWordPart */ @Override public boolean isWordPart(char c) { return (c == '*' || c == '/'); } } /** * Word rule for empty comments. */ static class EmptyCommentRule extends WordRule implements IPredicateRule { private IToken successToken; public EmptyCommentRule(IToken successToken) { super(new EmptyCommentDetector()); this.successToken = successToken; addWord("/**/", this.successToken); //$NON-NLS-1$ } @Override public IToken evaluate(ICharacterScanner scanner, boolean resume) { return evaluate(scanner); } @Override public IToken getSuccessToken() { return successToken; } } private void setupRules() { IPredicateRule[] result = new IPredicateRule[rules.size()]; rules.toArray(result); setPredicateRules(result); } private void initRules(SQLDialect dialect) { rules.add(new MultiLineRule(SQLConstants.STR_QUOTE_SINGLE, SQLConstants.STR_QUOTE_SINGLE, sqlStringToken, (char)0)); rules.add(new MultiLineRule(SQLConstants.STR_QUOTE_DOUBLE, SQLConstants.STR_QUOTE_DOUBLE, sqlQuotedToken, (char)0)); // Add special case word rule. EmptyCommentRule wordRule = new EmptyCommentRule(multilineCommentToken); rules.add(wordRule); // Add rules for multi-line comments Pair<String, String> multiLineComments = dialect.getMultiLineComments(); if (multiLineComments != null) { rules.add(new MultiLineRule(multiLineComments.getFirst(), multiLineComments.getSecond(), multilineCommentToken, (char) 0, true)); } String[] singleLineComments = dialect.getSingleLineComments(); for (String singleLineComment : singleLineComments) { // Add rule for single line comments. rules.add(new EndOfLineRule(singleLineComment, commentToken)); } } public SQLPartitionScanner(SQLDialect dialect) { initRules(dialect); setupRules(); } /** * Return the String ranging from the start of the current partition to the current scanning position. Some rules * (@see NestedMultiLineRule) need this information to calculate the comment nesting depth. * @return value */ public String getScannedPartitionString() { try { return fDocument.get(fPartitionOffset, fOffset - fPartitionOffset); } catch (Exception e) { // Do nothing } return ""; } /** * Gets the partitions of the given document as an array of * <code>ITypedRegion</code> objects. There is a distinct non-overlapping partition * for each comment line, string literal, delimited identifier, and "everything else" * (that is, SQL code other than a string literal or delimited identifier). * * @param doc the document to parse into partitions * @return an array containing the document partion regions */ public static ITypedRegion[] getDocumentRegions(IDocument doc) { ITypedRegion[] regions = null; try { regions = TextUtilities.computePartitioning(doc, SQLPartitionScanner.SQL_PARTITIONING, 0, doc.getLength(), false); } catch (BadLocationException e) { // ignore } return regions; } }