| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * 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 com.intellij.psi.impl.cache.impl; |
| |
| import com.intellij.lexer.DelegateLexer; |
| import com.intellij.lexer.Lexer; |
| import com.intellij.psi.impl.cache.impl.id.IdTableBuilding; |
| import com.intellij.psi.search.IndexPattern; |
| import com.intellij.psi.search.UsageSearchContext; |
| import com.intellij.util.text.CharArrayUtil; |
| import gnu.trove.TIntArrayList; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public abstract class BaseFilterLexer extends DelegateLexer implements IdTableBuilding.ScanWordProcessor { |
| private final OccurrenceConsumer myOccurrenceConsumer; |
| |
| private int myTodoScannedBound = 0; |
| private int myOccurenceMask; |
| private TodoScanningState myTodoScanningState; |
| private CharSequence myCachedBufferSequence; |
| private char[] myCachedArraySequence; |
| |
| protected BaseFilterLexer(Lexer originalLexer, OccurrenceConsumer occurrenceConsumer) { |
| super(originalLexer); |
| myOccurrenceConsumer = occurrenceConsumer; |
| } |
| |
| protected final void advanceTodoItemCountsInToken() { |
| if (!myOccurrenceConsumer.isNeedToDo()) return; |
| int start = getTokenStart(); |
| int end = getTokenEnd(); |
| start = Math.max(start, myTodoScannedBound); |
| if (start >= end) return; // this prevents scanning of the same comment twice |
| |
| CharSequence input = myCachedBufferSequence.subSequence(start, end); |
| myTodoScanningState = advanceTodoItemsCount(input, myOccurrenceConsumer, myTodoScanningState); |
| |
| myTodoScannedBound = end; |
| } |
| |
| public static class TodoScanningState { |
| final IndexPattern[] myPatterns; |
| final Matcher[] myMatchers; |
| TIntArrayList myOccurences; |
| |
| public TodoScanningState(IndexPattern[] patterns, Matcher[] matchers) { |
| myPatterns = patterns; |
| myMatchers = matchers; |
| myOccurences = new TIntArrayList(1); |
| } |
| } |
| |
| public static TodoScanningState advanceTodoItemsCount(final CharSequence input, final OccurrenceConsumer consumer, TodoScanningState todoScanningState) { |
| if (todoScanningState == null) { |
| IndexPattern[] patterns = IndexPatternUtil.getIndexPatterns(); |
| |
| Matcher[] matchers = new Matcher[patterns.length]; |
| todoScanningState = new TodoScanningState(patterns, matchers); |
| |
| for (int i = 0; i < patterns.length; ++i) { |
| Pattern pattern = patterns[i].getOptimizedIndexingPattern(); |
| |
| if (pattern != null) { |
| matchers[i] = pattern.matcher(""); |
| } |
| } |
| } else { |
| todoScanningState.myOccurences.resetQuick(); |
| } |
| |
| for (int i = todoScanningState.myMatchers.length - 1; i >= 0; --i) { |
| Matcher matcher = todoScanningState.myMatchers[i]; |
| if (matcher == null) continue; |
| matcher.reset(input); |
| |
| while (matcher.find()) { |
| int start = matcher.start(); |
| if (start != matcher.end() && todoScanningState.myOccurences.indexOf(start) == -1) { |
| consumer.incTodoOccurrence(todoScanningState.myPatterns[i]); |
| todoScanningState.myOccurences.add(start); |
| } |
| } |
| } |
| |
| return todoScanningState; |
| } |
| |
| @Override |
| public final void run(CharSequence chars, @Nullable char[] charsArray, int start, int end) { |
| myOccurrenceConsumer.addOccurrence(chars, charsArray, start, end, myOccurenceMask); |
| } |
| |
| protected final void addOccurrenceInToken(final int occurrenceMask) { |
| myOccurrenceConsumer.addOccurrence(myCachedBufferSequence, myCachedArraySequence, getTokenStart(), getTokenEnd(), occurrenceMask); |
| } |
| |
| protected final void scanWordsInToken(final int occurrenceMask, boolean mayHaveFileRefs, final boolean mayHaveEscapes) { |
| myOccurenceMask = occurrenceMask; |
| final int start = getTokenStart(); |
| final int end = getTokenEnd(); |
| IdTableBuilding.scanWords(this, myCachedBufferSequence, myCachedArraySequence, start, end, mayHaveEscapes); |
| |
| if (mayHaveFileRefs) { |
| processPossibleComplexFileName(myCachedBufferSequence, myCachedArraySequence, start, end); |
| } |
| } |
| |
| private void processPossibleComplexFileName(CharSequence chars, char[] cachedArraySequence, int startOffset, int endOffset) { |
| int offset = findCharsWithinRange(chars, startOffset, endOffset, "/\\"); |
| offset = Math.min(offset, endOffset); |
| int start = startOffset; |
| |
| while(start < endOffset) { |
| if (start != offset) { |
| myOccurrenceConsumer.addOccurrence(chars, cachedArraySequence, start, offset, UsageSearchContext.IN_FOREIGN_LANGUAGES); |
| } |
| start = offset + 1; |
| offset = Math.min(endOffset, findCharsWithinRange(chars, start, endOffset, "/\\")); |
| } |
| } |
| |
| private static int findCharsWithinRange(final CharSequence chars, int startOffset, int endOffset, String charsToFind) { |
| while(startOffset < endOffset) { |
| if (charsToFind.indexOf(chars.charAt(startOffset)) != -1) { |
| return startOffset; |
| } |
| ++startOffset; |
| } |
| |
| return startOffset; |
| } |
| |
| @Override |
| public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) { |
| super.start(buffer, startOffset, endOffset, initialState); |
| myCachedBufferSequence = getBufferSequence(); |
| myCachedArraySequence = CharArrayUtil.fromSequenceWithoutCopying(myCachedBufferSequence); |
| } |
| } |