blob: 1255a7fec9e314888bd73d5ae23823fae7a91a35 [file] [log] [blame]
/*
* 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.dupLocator.index;
import com.intellij.dupLocator.DuplicatesProfile;
import com.intellij.dupLocator.DuplocateVisitor;
import com.intellij.dupLocator.DuplocatorState;
import com.intellij.dupLocator.treeHash.FragmentsCollector;
import com.intellij.dupLocator.util.PsiFragment;
import com.intellij.lang.Language;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.SystemProperties;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.io.EnumeratorIntegerDescriptor;
import com.intellij.util.io.KeyDescriptor;
import gnu.trove.THashMap;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
/**
* @by Maxim.Mossienko on 12/11/13.
*/
public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayList> implements PsiDependentIndex {
static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.duplicates.online.calculation",
isEnabledByDefault());
private static boolean isEnabledByDefault() {
Application application = ApplicationManager.getApplication();
return application.isInternal() && !application.isUnitTestMode();
}
@NonNls public static final ID<Integer, TIntArrayList> NAME = ID.create("DuplicatesIndex");
private static final int myBaseVersion = 10;
private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() {
@Override
public boolean acceptInput(@NotNull final VirtualFile file) {
return ourEnabled && findDuplicatesProfile(file.getFileType()) != null;
}
};
private final DataExternalizer<TIntArrayList> myValueExternalizer = new DataExternalizer<TIntArrayList>() {
@Override
public void save(@NotNull DataOutput out, TIntArrayList list) throws IOException {
if (list.size() == 1) DataInputOutputUtil.writeINT(out, list.getQuick(0));
else {
DataInputOutputUtil.writeINT(out, -list.size());
int prev = 0;
for (int i = 0, len = list.size(); i < len; ++i) {
int value = list.getQuick(i);
DataInputOutputUtil.writeINT(out, value - prev);
prev = value;
}
}
}
@Override
public TIntArrayList read(@NotNull DataInput in) throws IOException {
int capacityOrValue = DataInputOutputUtil.readINT(in);
if (capacityOrValue >= 0) {
TIntArrayList list = new TIntArrayList(1);
list.add(capacityOrValue);
return list;
}
capacityOrValue = -capacityOrValue;
TIntArrayList list = new TIntArrayList(capacityOrValue);
int prev = 0;
while(capacityOrValue-- > 0) {
int value = DataInputOutputUtil.readINT(in) + prev;
list.add(value);
prev = value;
}
return list;
}
};
private final DataIndexer<Integer, TIntArrayList, FileContent> myIndexer = new DataIndexer<Integer, TIntArrayList, FileContent>() {
@Override
@NotNull
public Map<Integer, TIntArrayList> map(@NotNull final FileContent inputData) {
FileType type = inputData.getFileType();
DuplicatesProfile profile = findDuplicatesProfile(type);
if (profile == null) return Collections.emptyMap();
MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage());
DuplocateVisitor visitor = profile.createVisitor(collector, true);
visitor.visitNode(((FileContentImpl)inputData).getPsiFileAccountingForUnsavedDocument());
return collector.getMap();
}
};
@Nullable
public static DuplicatesProfile findDuplicatesProfile(FileType fileType) {
if (!(fileType instanceof LanguageFileType)) return null;
Language language = ((LanguageFileType)fileType).getLanguage();
DuplicatesProfile profile = DuplicatesProfile.findProfileForLanguage(language);
return profile != null && profile.supportIndex() ? profile : null;
}
@Override
public int getVersion() {
return myBaseVersion + (ourEnabled ? 0xFF : 0);
}
@Override
public boolean dependsOnFileContent() {
return true;
}
@NotNull
@Override
public ID<Integer,TIntArrayList> getName() {
return NAME;
}
@NotNull
@Override
public DataIndexer<Integer, TIntArrayList, FileContent> getIndexer() {
return myIndexer;
}
@NotNull
@Override
public DataExternalizer<TIntArrayList> getValueExternalizer() {
return myValueExternalizer;
}
@NotNull
@Override
public KeyDescriptor<Integer> getKeyDescriptor() {
return EnumeratorIntegerDescriptor.INSTANCE;
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return myInputFilter;
}
//private static final TracingData myTracingData = new TracingData();
private static final TracingData myTracingData = null;
private static class MyFragmentsCollector implements FragmentsCollector {
private final THashMap<Integer, TIntArrayList> myMap = new THashMap<Integer, TIntArrayList>();
private final DuplicatesProfile myProfile;
private final DuplocatorState myDuplocatorState;
public MyFragmentsCollector(DuplicatesProfile profile, Language language) {
myProfile = profile;
myDuplocatorState = profile.getDuplocatorState(language);
}
@Override
public void add(int hash, int cost, @Nullable PsiFragment frag) {
if (!isIndexedFragment(frag, cost, myProfile, myDuplocatorState)) {
return;
}
if (myTracingData != null) myTracingData.record(hash, cost, frag);
TIntArrayList list = myMap.get(hash);
if (list == null) { myMap.put(hash, list = new TIntArrayList()); }
list.add(frag.getStartOffset());
}
public THashMap<Integer,TIntArrayList> getMap() {
return myMap;
}
}
static boolean isIndexedFragment(@Nullable PsiFragment frag, int cost, DuplicatesProfile profile, DuplocatorState duplocatorState) {
if(frag == null) return false;
return profile.shouldPutInIndex(frag, cost, duplocatorState);
}
@TestOnly
public static boolean setEnabled(boolean value) {
boolean old = ourEnabled;
ourEnabled = value;
return old;
}
@Override
public boolean hasSnapshotMapping() {
return true;
}
}