| /* |
| * 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.injected.editor.DocumentWindow; |
| import com.intellij.lang.injection.InjectedLanguageManager; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.RangeMarker; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.ProperTextRange; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.tree.MarkersHolderFileViewProvider; |
| import com.intellij.reference.SoftReference; |
| import com.intellij.util.containers.UnsafeWeakList; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.annotations.TestOnly; |
| |
| import java.lang.ref.Reference; |
| import java.util.List; |
| |
| public class SmartPointerManagerImpl extends SmartPointerManager { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl"); |
| |
| private static final Key<List<SmartPointerEx>> SMART_POINTERS_IN_PSI_FILE_KEY = Key.create("SMART_POINTERS_IN_PSI_FILE_KEY"); |
| private static final Key<Boolean> BELTS_ARE_FASTEN_KEY = Key.create("BELTS_ARE_FASTEN_KEY"); |
| |
| private final Project myProject; |
| private final Object lock = new Object(); |
| |
| public SmartPointerManagerImpl(Project project) { |
| myProject = project; |
| } |
| |
| public void fastenBelts(@NotNull PsiFile file, int offset, @Nullable RangeMarker[] cachedRangeMarkers) { |
| synchronized (lock) { |
| if (areBeltsFastened(file)) return; |
| |
| file.putUserData(BELTS_ARE_FASTEN_KEY, Boolean.TRUE); |
| |
| List<SmartPointerEx> pointers = getPointers(file); |
| if (pointers == null) return; |
| PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject()); |
| |
| for (SmartPointerEx pointer : pointers) { |
| if (pointer != null) { |
| pointer.fastenBelt(offset, cachedRangeMarkers); |
| } |
| } |
| |
| for (DocumentWindow injectedDoc : InjectedLanguageManager.getInstance(myProject).getCachedInjectedDocuments(file)) { |
| PsiFile injectedFile = psiDocumentManager.getPsiFile(injectedDoc); |
| if (injectedFile == null) continue; |
| RangeMarker[] cachedMarkers = getCachedRangeMarkerToInjectedFragment(injectedFile); |
| fastenBelts(injectedFile, 0, cachedMarkers); |
| } |
| } |
| } |
| |
| @NotNull |
| private static RangeMarker[] getCachedRangeMarkerToInjectedFragment(@NotNull PsiFile injectedFile) { |
| MarkersHolderFileViewProvider provider = (MarkersHolderFileViewProvider)injectedFile.getViewProvider(); |
| return provider.getCachedMarkers(); |
| } |
| |
| public void unfastenBelts(@NotNull PsiFile file, int offset) { |
| synchronized (lock) { |
| PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject()); |
| file.putUserData(BELTS_ARE_FASTEN_KEY, null); |
| |
| List<SmartPointerEx> pointers = getPointers(file); |
| if (pointers == null) return; |
| |
| for (SmartPointerEx pointer : pointers) { |
| if (pointer != null) { |
| pointer.unfastenBelt(offset); |
| } |
| } |
| |
| for (DocumentWindow injectedDoc : InjectedLanguageManager.getInstance(myProject).getCachedInjectedDocuments(file)) { |
| PsiFile injectedFile = psiDocumentManager.getPsiFile(injectedDoc); |
| if (injectedFile == null) continue; |
| unfastenBelts(injectedFile, 0); |
| } |
| } |
| } |
| |
| private static final Key<Reference<SmartPointerEx>> CACHED_SMART_POINTER_KEY = Key.create("CACHED_SMART_POINTER_KEY"); |
| @Override |
| @NotNull |
| public <E extends PsiElement> SmartPsiElementPointer<E> createSmartPsiElementPointer(@NotNull E element) { |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| PsiFile containingFile = element.getContainingFile(); |
| return createSmartPsiElementPointer(element, containingFile); |
| } |
| @Override |
| @NotNull |
| public <E extends PsiElement> SmartPsiElementPointer<E> createSmartPsiElementPointer(@NotNull E element, PsiFile containingFile) { |
| if (containingFile != null && !containingFile.isValid() || containingFile == null && !element.isValid()) { |
| LOG.error("Invalid element:" + element); |
| } |
| SmartPointerEx<E> pointer = getCachedPointer(element); |
| if (pointer != null) { |
| containingFile = containingFile == null ? element.getContainingFile() : containingFile; |
| if (containingFile != null && areBeltsFastened(containingFile)) { |
| pointer.fastenBelt(0, null); |
| } |
| } |
| else { |
| pointer = new SmartPsiElementPointerImpl<E>(myProject, element, containingFile); |
| initPointer(pointer, containingFile); |
| element.putUserData(CACHED_SMART_POINTER_KEY, new SoftReference<SmartPointerEx>(pointer)); |
| } |
| if (pointer instanceof SmartPsiElementPointerImpl) { |
| synchronized (lock) { |
| ((SmartPsiElementPointerImpl)pointer).incrementAndGetReferenceCount(1); |
| } |
| } |
| return pointer; |
| |
| } |
| |
| private static <E extends PsiElement> SmartPointerEx<E> getCachedPointer(@NotNull E element) { |
| Reference<SmartPointerEx> data = element.getUserData(CACHED_SMART_POINTER_KEY); |
| SmartPointerEx cachedPointer = SoftReference.dereference(data); |
| if (cachedPointer != null) { |
| PsiElement cachedElement = cachedPointer.getCachedElement(); |
| if (cachedElement != null && cachedElement != element) { |
| return null; |
| } |
| } |
| return cachedPointer; |
| } |
| |
| @Override |
| @NotNull |
| public SmartPsiFileRange createSmartPsiFileRangePointer(@NotNull PsiFile file, @NotNull TextRange range) { |
| if (!file.isValid()) { |
| LOG.error("Invalid element:" + file); |
| } |
| SmartPsiFileRangePointerImpl pointer = new SmartPsiFileRangePointerImpl(file, ProperTextRange.create(range)); |
| initPointer(pointer, file); |
| |
| return pointer; |
| } |
| |
| private <E extends PsiElement> void initPointer(@NotNull SmartPointerEx<E> pointer, PsiFile containingFile) { |
| if (containingFile == null) return; |
| synchronized (lock) { |
| List<SmartPointerEx> pointers = getPointers(containingFile); |
| if (pointers == null) { |
| pointers = new UnsafeWeakList<SmartPointerEx>(); // we synchronise access anyway |
| containingFile.putUserData(SMART_POINTERS_IN_PSI_FILE_KEY, pointers); |
| } |
| pointers.add(pointer); |
| |
| if (areBeltsFastened(containingFile)) { |
| pointer.fastenBelt(0, null); |
| } |
| } |
| } |
| |
| @Override |
| public boolean removePointer(@NotNull SmartPsiElementPointer pointer) { |
| synchronized (lock) { |
| if (pointer instanceof SmartPsiElementPointerImpl) { |
| int refCount = ((SmartPsiElementPointerImpl)pointer).incrementAndGetReferenceCount(-1); |
| if (refCount == 0) { |
| PsiElement element = ((SmartPointerEx)pointer).getCachedElement(); |
| if (element != null) { |
| element.putUserData(CACHED_SMART_POINTER_KEY, null); |
| } |
| PsiFile containingFile = pointer.getContainingFile(); |
| if (containingFile == null) return false; |
| List<SmartPointerEx> pointers = getPointers(containingFile); |
| if (pointers == null) return false; |
| SmartPointerElementInfo info = ((SmartPsiElementPointerImpl)pointer).getElementInfo(); |
| info.cleanup(); |
| return pointers.remove(pointer); |
| } |
| } |
| } |
| return false; |
| } |
| |
| private static List<SmartPointerEx> getPointers(@NotNull PsiFile containingFile) { |
| return containingFile.getUserData(SMART_POINTERS_IN_PSI_FILE_KEY); |
| } |
| |
| @TestOnly |
| public int getPointersNumber(@NotNull PsiFile containingFile) { |
| synchronized (lock) { |
| List<SmartPointerEx> pointers = getPointers(containingFile); |
| return pointers == null ? 0 : ((UnsafeWeakList)pointers).toStrongList().size(); |
| } |
| } |
| |
| private static boolean areBeltsFastened(@NotNull PsiFile file) { |
| return file.getUserData(BELTS_ARE_FASTEN_KEY) == Boolean.TRUE; |
| } |
| |
| |
| @Override |
| public boolean pointToTheSameElement(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2) { |
| return SmartPsiElementPointerImpl.pointsToTheSameElementAs(pointer1, pointer2); |
| } |
| } |