/* * Copyright 2000-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 com.intellij.psi.impl.java.stubs; import com.intellij.lang.ASTNode; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; import com.intellij.psi.JavaTokenType; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiNameHelper; import com.intellij.psi.impl.cache.RecordUtil; import com.intellij.psi.impl.java.stubs.impl.PsiClassStubImpl; import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys; import com.intellij.psi.impl.source.PsiAnonymousClassImpl; import com.intellij.psi.impl.source.PsiClassImpl; import com.intellij.psi.impl.source.PsiEnumConstantInitializerImpl; import com.intellij.psi.impl.source.tree.JavaDocElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.impl.source.tree.LightTreeUtil; import com.intellij.psi.impl.source.tree.java.AnonymousClassElement; import com.intellij.psi.impl.source.tree.java.EnumConstantInitializerElement; 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.psi.tree.IElementType; import com.intellij.util.io.StringRef; import org.jetbrains.annotations.NotNull; import java.io.IOException; /** * @author max */ public abstract class JavaClassElementType extends JavaStubElementType<PsiClassStub, PsiClass> { public JavaClassElementType(@NotNull String id) { super(id); } @Override public PsiClass createPsi(@NotNull final PsiClassStub stub) { return getPsiFactory(stub).createClass(stub); } @Override public PsiClass createPsi(@NotNull final ASTNode node) { if (node instanceof EnumConstantInitializerElement) { return new PsiEnumConstantInitializerImpl(node); } else if (node instanceof AnonymousClassElement) { return new PsiAnonymousClassImpl(node); } return new PsiClassImpl(node); } @Override public PsiClassStub createStub(final LighterAST tree, final LighterASTNode node, final StubElement parentStub) { boolean isDeprecatedByComment = false; boolean isInterface = false; boolean isEnum = false; boolean isEnumConst = false; boolean isAnonymous = false; boolean isAnnotation = false; boolean isInQualifiedNew = false; boolean hasDeprecatedAnnotation = false; String qualifiedName = null; String name = null; String baseRef = null; if (node.getTokenType() == JavaElementType.ANONYMOUS_CLASS) { isAnonymous = true; } else if (node.getTokenType() == JavaElementType.ENUM_CONSTANT_INITIALIZER) { isAnonymous = isEnumConst = true; baseRef = ((PsiClassStub)parentStub.getParentStub()).getName(); } for (final LighterASTNode child : tree.getChildren(node)) { final IElementType type = child.getTokenType(); if (type == JavaDocElementType.DOC_COMMENT) { isDeprecatedByComment = RecordUtil.isDeprecatedByDocComment(tree, child); } else if (type == JavaElementType.MODIFIER_LIST) { hasDeprecatedAnnotation = RecordUtil.isDeprecatedByAnnotation(tree, child); } else if (type == JavaTokenType.AT) { isAnnotation = true; } else if (type == JavaTokenType.INTERFACE_KEYWORD) { isInterface = true; } else if (type == JavaTokenType.ENUM_KEYWORD) { isEnum = true; } else if (!isAnonymous && type == JavaTokenType.IDENTIFIER) { name = RecordUtil.intern(tree.getCharTable(), child); } else if (isAnonymous && !isEnumConst && type == JavaElementType.JAVA_CODE_REFERENCE) { baseRef = LightTreeUtil.toFilteredString(tree, child, null); } } if (name != null) { if (parentStub instanceof PsiJavaFileStub) { final String pkg = ((PsiJavaFileStub)parentStub).getPackageName(); if (!pkg.isEmpty()) qualifiedName = pkg + '.' + name; else qualifiedName = name; } else if (parentStub instanceof PsiClassStub) { final String parentFqn = ((PsiClassStub)parentStub).getQualifiedName(); qualifiedName = parentFqn != null ? parentFqn + '.' + name : null; } } if (isAnonymous) { final LighterASTNode parent = tree.getParent(node); if (parent != null && parent.getTokenType() == JavaElementType.NEW_EXPRESSION) { isInQualifiedNew = (LightTreeUtil.firstChildOfType(tree, parent, JavaTokenType.DOT) != null); } } final byte flags = PsiClassStubImpl.packFlags(isDeprecatedByComment, isInterface, isEnum, isEnumConst, isAnonymous, isAnnotation, isInQualifiedNew, hasDeprecatedAnnotation); final JavaClassElementType type = typeForClass(isAnonymous, isEnumConst); return new PsiClassStubImpl(type, parentStub, qualifiedName, name, baseRef, flags); } public static JavaClassElementType typeForClass(final boolean anonymous, final boolean enumConst) { return enumConst ? JavaStubElementTypes.ENUM_CONSTANT_INITIALIZER : anonymous ? JavaStubElementTypes.ANONYMOUS_CLASS : JavaStubElementTypes.CLASS; } @Override public void serialize(@NotNull PsiClassStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeByte(((PsiClassStubImpl)stub).getFlags()); if (!stub.isAnonymous()) { dataStream.writeName(stub.getName()); dataStream.writeName(stub.getQualifiedName()); dataStream.writeName(stub.getSourceFileName()); } else { dataStream.writeName(stub.getBaseClassReferenceText()); } } @NotNull @Override public PsiClassStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { byte flags = dataStream.readByte(); boolean isAnonymous = PsiClassStubImpl.isAnonymous(flags); boolean isEnumConst = PsiClassStubImpl.isEnumConstInitializer(flags); JavaClassElementType type = typeForClass(isAnonymous, isEnumConst); if (!isAnonymous) { StringRef name = dataStream.readName(); StringRef qname = dataStream.readName(); StringRef sourceFileName = dataStream.readName(); PsiClassStubImpl classStub = new PsiClassStubImpl(type, parentStub, StringRef.toString(qname), StringRef.toString(name), null, flags); classStub.setSourceFileName(StringRef.toString(sourceFileName)); return classStub; } else { StringRef baseRef = dataStream.readName(); return new PsiClassStubImpl(type, parentStub, null, null, StringRef.toString(baseRef), flags); } } @Override public void indexStub(@NotNull PsiClassStub stub, @NotNull IndexSink sink) { boolean isAnonymous = stub.isAnonymous(); if (isAnonymous) { String baseRef = stub.getBaseClassReferenceText(); if (baseRef != null) { sink.occurrence(JavaStubIndexKeys.ANONYMOUS_BASEREF, PsiNameHelper.getShortClassName(baseRef)); } } else { final String shortName = stub.getName(); if (shortName != null) { sink.occurrence(JavaStubIndexKeys.CLASS_SHORT_NAMES, shortName); } final String fqn = stub.getQualifiedName(); if (fqn != null) { sink.occurrence(JavaStubIndexKeys.CLASS_FQN, fqn.hashCode()); } } } @Override public String getId(@NotNull final PsiClassStub stub) { final String name = stub.getName(); return name != null ? name : super.getId(stub); } }