blob: 9e755ad5a61530b6792db61d0c23625783be0a42 [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.psi.impl.smartPointers;
import com.intellij.lang.LanguageUtil;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.FreeThreadedFileViewProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx<E> {
private Reference<E> myElement;
private final SmartPointerElementInfo myElementInfo;
private final Class<? extends PsiElement> myElementClass;
private byte myReferenceCount;
public SmartPsiElementPointerImpl(@NotNull Project project, @NotNull E element, @Nullable PsiFile containingFile) {
this(element, createElementInfo(project, element, containingFile), element.getClass());
}
public SmartPsiElementPointerImpl(@NotNull E element,
@NotNull SmartPointerElementInfo elementInfo,
@NotNull Class<? extends PsiElement> elementClass) {
ApplicationManager.getApplication().assertReadAccessAllowed();
cacheElement(element);
myElementClass = elementClass;
myElementInfo = elementInfo;
}
public boolean equals(Object obj) {
return obj instanceof SmartPsiElementPointer && pointsToTheSameElementAs(this, (SmartPsiElementPointer)obj);
}
public int hashCode() {
return myElementInfo.elementHashCode();
}
@Override
@NotNull
public Project getProject() {
return myElementInfo.getProject();
}
@Override
@Nullable
public E getElement() {
E element = getCachedElement();
if (element != null && !element.isValid()) {
element = null;
}
if (element == null) {
//noinspection unchecked
element = (E)myElementInfo.restoreElement();
if (element != null && (!element.getClass().equals(myElementClass) || !element.isValid())) {
element = null;
}
cacheElement(element);
}
return element;
}
private void cacheElement(E element) {
myElement = element == null ? null : new SoftReference<E>(element);
}
@Override
public E getCachedElement() {
return com.intellij.reference.SoftReference.dereference(myElement);
}
@Override
public PsiFile getContainingFile() {
PsiFile file = getElementInfo().restoreFile();
if (file != null) {
return file;
}
final Document doc = myElementInfo.getDocumentToSynchronize();
if (doc == null) {
final E resolved = getElement();
return resolved == null ? null : resolved.getContainingFile();
}
return PsiDocumentManager.getInstance(getProject()).getPsiFile(doc);
}
@Override
public VirtualFile getVirtualFile() {
return myElementInfo.getVirtualFile();
}
@Override
public Segment getRange() {
return myElementInfo.getRange();
}
@NotNull
static <E extends PsiElement> SmartPointerElementInfo createElementInfo(@NotNull Project project, @NotNull E element, PsiFile containingFile) {
if (element instanceof PsiCompiledElement || containingFile == null || !containingFile.isPhysical() || !element.isPhysical()) {
if (element instanceof StubBasedPsiElement && element instanceof PsiCompiledElement) {
if (element instanceof PsiFile) {
return new FileElementInfo((PsiFile)element);
}
PsiAnchor.StubIndexReference stubReference = PsiAnchor.createStubReference(element, containingFile);
if (stubReference != null) {
return new ClsElementInfo(stubReference);
}
}
return new HardElementInfo(project, element);
}
if (element instanceof PsiDirectory) {
return new DirElementInfo((PsiDirectory)element);
}
for(SmartPointerElementInfoFactory factory: Extensions.getExtensions(SmartPointerElementInfoFactory.EP_NAME)) {
final SmartPointerElementInfo result = factory.createElementInfo(element);
if (result != null) return result;
}
FileViewProvider viewProvider = containingFile.getViewProvider();
if (viewProvider instanceof FreeThreadedFileViewProvider) {
PsiElement hostContext = InjectedLanguageManager.getInstance(containingFile.getProject()).getInjectionHost(containingFile);
if (hostContext != null) return new InjectedSelfElementInfo(project, element, element.getTextRange(), containingFile, hostContext);
}
if (element instanceof PsiFile) {
return new FileElementInfo((PsiFile)element);
}
TextRange elementRange = element.getTextRange();
if (elementRange == null) {
return new HardElementInfo(project, element);
}
ProperTextRange proper = ProperTextRange.create(elementRange);
return new SelfElementInfo(project, proper, element.getClass(), containingFile, LanguageUtil.getRootLanguage(element));
}
@Override
public void unfastenBelt(int offset) {
myElementInfo.unfastenBelt(offset);
}
@Override
public void fastenBelt(int offset, @Nullable RangeMarker[] cachedRangeMarkers) {
myElementInfo.fastenBelt(offset, cachedRangeMarkers);
}
@NotNull
public SmartPointerElementInfo getElementInfo() {
return myElementInfo;
}
protected static boolean pointsToTheSameElementAs(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2) {
if (pointer1 == pointer2) return true;
if (pointer1 instanceof SmartPsiElementPointerImpl && pointer2 instanceof SmartPsiElementPointerImpl) {
SmartPsiElementPointerImpl impl1 = (SmartPsiElementPointerImpl)pointer1;
SmartPsiElementPointerImpl impl2 = (SmartPsiElementPointerImpl)pointer2;
SmartPointerElementInfo elementInfo1 = impl1.getElementInfo();
SmartPointerElementInfo elementInfo2 = impl2.getElementInfo();
if (!elementInfo1.pointsToTheSameElementAs(elementInfo2)) return false;
PsiElement cachedElement1 = impl1.getCachedElement();
PsiElement cachedElement2 = impl2.getCachedElement();
return cachedElement1 == null || cachedElement2 == null || Comparing.equal(cachedElement1, cachedElement2);
}
return Comparing.equal(pointer1.getElement(), pointer2.getElement());
}
int incrementAndGetReferenceCount(int delta) {
if (myReferenceCount == Byte.MAX_VALUE) return Byte.MAX_VALUE; // saturated
return myReferenceCount += delta;
}
}