/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.idea.stubindex;
import com.intellij.psi.stubs.IndexSink;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubInputStream;
import com.intellij.psi.stubs.StubOutputStream;
import com.intellij.util.io.StringRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.stubs.*;
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
import org.jetbrains.kotlin.psi.stubs.elements.StubIndexService;
import org.jetbrains.kotlin.util.TypeIndexUtilKt;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
public class IdeStubIndexService extends StubIndexService {
@Override
public void indexFile(KotlinFileStub stub, IndexSink sink) {
FqName packageFqName = stub.getPackageFqName();
sink.occurrence(KotlinExactPackagesIndex.getInstance().getKey(), packageFqName.asString());
if (stub.isScript()) return;
FqName facadeFqName = ((KotlinFileStubForIde) stub).getFacadeFqName();
if (facadeFqName != null) {
sink.occurrence(KotlinFileFacadeFqNameIndex.INSTANCE.getKey(), facadeFqName.asString());
sink.occurrence(KotlinFileFacadeShortNameIndex.INSTANCE.getKey(), facadeFqName.shortName().asString());
sink.occurrence(KotlinFileFacadeClassByPackageIndex.INSTANCE.getKey(), packageFqName.asString());
}
FqName partFqName = ((KotlinFileStubForIde) stub).getPartFqName();
if (partFqName != null) {
sink.occurrence(KotlinFilePartClassIndex.INSTANCE.getKey(), partFqName.asString());
}
List<StringRef> partNames = ((KotlinFileStubForIde) stub).getFacadePartSimpleNames();
if (partNames != null) {
for (StringRef partName : partNames) {
String partSimpleName = StringRef.toString(partName);
if (partSimpleName == null) {
continue;
}
FqName multifileClassPartFqName = packageFqName.child(Name.identifier(partSimpleName));
sink.occurrence(KotlinMultifileClassPartIndex.INSTANCE.getKey(), multifileClassPartFqName.asString());
}
}
}
@Override
public void indexClass(KotlinClassStub stub, IndexSink sink) {
String name = stub.getName();
if (name != null) {
sink.occurrence(KotlinClassShortNameIndex.getInstance().getKey(), name);
}
FqName fqName = stub.getFqName();
if (fqName != null) {
sink.occurrence(KotlinFullClassNameIndex.getInstance().getKey(), fqName.asString());
if (stub.isTopLevel()) {
sink.occurrence(KotlinTopLevelClassByPackageIndex.getInstance().getKey(), fqName.parent().asString());
}
}
if (stub.isInterface()) {
sink.occurrence(KotlinClassShortNameIndex.getInstance().getKey(), JvmAbi.DEFAULT_IMPLS_CLASS_NAME);
}
indexSuperNames(stub, sink);
}
@Override
public void indexObject(KotlinObjectStub stub, IndexSink sink) {
String name = stub.getName();
if (name != null) {
sink.occurrence(KotlinClassShortNameIndex.getInstance().getKey(), name);
}
FqName fqName = stub.getFqName();
if (fqName != null) {
sink.occurrence(KotlinFullClassNameIndex.getInstance().getKey(), fqName.asString());
if (stub.isTopLevel()) {
sink.occurrence(KotlinTopLevelClassByPackageIndex.getInstance().getKey(), fqName.parent().asString());
}
}
indexSuperNames(stub, sink);
}
private static void indexSuperNames(KotlinClassOrObjectStub<? extends KtClassOrObject> stub, IndexSink sink) {
for (String superName : stub.getSuperNames()) {
sink.occurrence(KotlinSuperClassIndex.getInstance().getKey(), superName);
}
if (!(stub instanceof KotlinClassStub)) {
return;
}
KotlinModifierListStub modifierListStub = getModifierListStub(stub);
if (modifierListStub == null) return;
if (modifierListStub.hasModifier(KtTokens.ENUM_KEYWORD)) {
sink.occurrence(KotlinSuperClassIndex.getInstance().getKey(), Enum.class.getSimpleName());
}
if (modifierListStub.hasModifier(KtTokens.ANNOTATION_KEYWORD)) {
sink.occurrence(KotlinSuperClassIndex.getInstance().getKey(), Annotation.class.getSimpleName());
}
}
@Nullable
private static KotlinModifierListStub getModifierListStub(@NotNull KotlinClassOrObjectStub<? extends KtClassOrObject> stub) {
StubElement<?> childStub = stub.findChildStubByType(KtStubElementTypes.MODIFIER_LIST);
if (!(childStub instanceof KotlinModifierListStub)) {
return null;
}
return (KotlinModifierListStub) childStub;
}
@Override
public void indexFunction(KotlinFunctionStub stub, IndexSink sink) {
String name = stub.getName();
if (name != null) {
sink.occurrence(KotlinFunctionShortNameIndex.getInstance().getKey(), name);
if (TypeIndexUtilKt.isProbablyNothing(stub.getPsi().getTypeReference())) {
sink.occurrence(KotlinProbablyNothingFunctionShortNameIndex.getInstance().getKey(), name);
}
}
if (stub.isTopLevel()) {
// can have special fq name in case of syntactically incorrect function with no name
FqName fqName = stub.getFqName();
if (fqName != null) {
sink.occurrence(KotlinTopLevelFunctionFqnNameIndex.getInstance().getKey(), fqName.asString());
sink.occurrence(KotlinTopLevelFunctionByPackageIndex.getInstance().getKey(), fqName.parent().asString());
IndexUtilsKt.indexTopLevelExtension(stub, sink);
}
}
IndexUtilsKt.indexInternals(stub, sink);
}
@Override
public void indexTypeAlias(KotlinTypeAliasStub stub, IndexSink sink) {
String name = stub.getName();
if (name != null) {
sink.occurrence(KotlinTypeAliasShortNameIndex.getInstance().getKey(), name);
}
IndexUtilsKt.indexTypeAliasExpansion(stub, sink);
if (stub.isTopLevel()) {
FqName fqName = stub.getFqName();
if (fqName != null) {
sink.occurrence(KotlinTopLevelTypeAliasFqNameIndex.getInstance().getKey(), fqName.asString());
sink.occurrence(KotlinTopLevelTypeAliasByPackageIndex.getInstance().getKey(), fqName.parent().asString());
}
}
}
@Override
public void indexProperty(KotlinPropertyStub stub, IndexSink sink) {
String name = stub.getName();
if (name != null) {
sink.occurrence(KotlinPropertyShortNameIndex.getInstance().getKey(), name);
if (TypeIndexUtilKt.isProbablyNothing(stub.getPsi().getTypeReference())) {
sink.occurrence(KotlinProbablyNothingPropertyShortNameIndex.getInstance().getKey(), name);
}
}
if (stub.isTopLevel()) {
FqName fqName = stub.getFqName();
// can have special fq name in case of syntactically incorrect property with no name
if (fqName != null) {
sink.occurrence(KotlinTopLevelPropertyFqnNameIndex.getInstance().getKey(), fqName.asString());
sink.occurrence(KotlinTopLevelPropertyByPackageIndex.getInstance().getKey(), fqName.parent().asString());
IndexUtilsKt.indexTopLevelExtension(stub, sink);
}
}
IndexUtilsKt.indexInternals(stub, sink);
}
@Override
public void indexParameter(@NotNull KotlinParameterStub stub, @NotNull IndexSink sink) {
String name = stub.getName();
if (name != null && stub.hasValOrVar()) {
sink.occurrence(KotlinPropertyShortNameIndex.getInstance().getKey(), name);
}
}
@Override
public void indexAnnotation(KotlinAnnotationEntryStub stub, IndexSink sink) {
sink.occurrence(KotlinAnnotationsIndex.getInstance().getKey(), stub.getShortName());
KotlinFileStub fileStub = getContainingFileStub(stub);
if (fileStub != null) {
List<KotlinImportDirectiveStub> aliasImportStubs = fileStub.findImportsByAlias(stub.getShortName());
for (KotlinImportDirectiveStub importStub : aliasImportStubs) {
sink.occurrence(KotlinAnnotationsIndex.getInstance().getKey(), importStub.getImportedFqName().shortName().asString());
}
}
}
private static KotlinFileStub getContainingFileStub(StubElement stub) {
StubElement parent = stub.getParentStub();
while (parent != null) {
if (parent instanceof KotlinFileStub) {
return (KotlinFileStub) parent;
}
parent = parent.getParentStub();
}
return null;
}
@Override
public void indexScript(@NotNull KotlinScriptStub stub, @NotNull IndexSink sink) {
sink.occurrence(KotlinScriptFqnIndex.getInstance().getKey(), stub.getFqName().asString());
}
@NotNull
@Override
public KotlinFileStub createFileStub(@NotNull KtFile file) {
StringRef packageFqName = StringRef.fromString(file.getPackageFqNameByTree().asString());
boolean isScript = file.isScriptByTree();
if (PackagePartClassUtils.fileHasTopLevelCallables(file)) {
JvmFileClassInfo fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file);
StringRef facadeSimpleName = StringRef.fromString(fileClassInfo.getFacadeClassFqName().shortName().asString());
StringRef partSimpleName = StringRef.fromString(fileClassInfo.getFileClassFqName().shortName().asString());
return new KotlinFileStubForIde(file, packageFqName, isScript, facadeSimpleName, partSimpleName, null);
}
return new KotlinFileStubForIde(file, packageFqName, isScript, null, null, null);
}
@Override
public void serializeFileStub(
@NotNull KotlinFileStub stub, @NotNull StubOutputStream dataStream
) throws IOException {
KotlinFileStubForIde fileStub = (KotlinFileStubForIde) stub;
dataStream.writeName(fileStub.getPackageFqName().asString());
dataStream.writeBoolean(fileStub.isScript());
dataStream.writeName(StringRef.toString(fileStub.getFacadeSimpleName()));
dataStream.writeName(StringRef.toString(fileStub.getPartSimpleName()));
List<StringRef> facadePartNames = fileStub.getFacadePartSimpleNames();
if (facadePartNames == null) {
dataStream.writeInt(0);
}
else {
dataStream.writeInt(facadePartNames.size());
for (StringRef partName : facadePartNames) {
dataStream.writeName(StringRef.toString(partName));
}
}
}
@NotNull
@Override
public KotlinFileStub deserializeFileStub(@NotNull StubInputStream dataStream) throws IOException {
StringRef packageFqNameAsString = dataStream.readName();
boolean isScript = dataStream.readBoolean();
StringRef facadeSimpleName = dataStream.readName();
StringRef partSimpleName = dataStream.readName();
int numPartNames = dataStream.readInt();
List<StringRef> facadePartNames = new ArrayList<StringRef>();
for (int i = 0; i < numPartNames; ++i) {
StringRef partNameRef = dataStream.readName();
facadePartNames.add(partNameRef);
}
return new KotlinFileStubForIde(null, packageFqNameAsString, isScript, facadeSimpleName, partSimpleName, facadePartNames);
}
}