org.jetbrains.plugins.groovy.lang.resolve.DeclarationCacheKey Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-psi Show documentation
Show all versions of groovy-psi Show documentation
A packaging of the IntelliJ Community Edition groovy-psi library.
This is release number 1 of trunk branch 142.
The newest version!
/*
* Copyright 2000-2014 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.plugins.groovy.lang.resolve;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.PairProcessor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
import org.jetbrains.plugins.groovy.lang.resolve.processors.GrScopeProcessorWithHints;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
/**
* @author peter
*/
class DeclarationCacheKey {
private static final CachedValueProvider>> VALUE_PROVIDER =
new CachedValueProvider>>() {
@Nullable
@Override
public Result>> compute() {
ConcurrentMap> map = ContainerUtil.newConcurrentMap();
return Result.create(map, PsiModificationTracker.MODIFICATION_COUNT);
}
};
@Nullable private final String name;
@NotNull private final EnumSet kinds;
private final boolean nonCode;
@NotNull private final PsiElement place;
DeclarationCacheKey(@Nullable String name, ClassHint hint, boolean nonCode, @NotNull PsiElement place) {
this.name = name;
this.kinds = getResolveKinds(hint);
this.nonCode = nonCode;
this.place = place;
}
private static EnumSet getResolveKinds(ClassHint hint) {
EnumSet set = EnumSet.noneOf(ClassHint.ResolveKind.class);
for (ClassHint.ResolveKind kind : ClassHint.ResolveKind.values()) {
if (hint.shouldProcess(kind)) {
set.add(kind);
}
}
return set;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DeclarationCacheKey)) {
return false;
}
DeclarationCacheKey key = (DeclarationCacheKey)o;
if (nonCode != key.nonCode) {
return false;
}
if (!kinds.equals(key.kinds)) {
return false;
}
if (name != null ? !name.equals(key.name) : key.name != null) {
return false;
}
if (place != key.place) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + kinds.hashCode();
result = 31 * result + (nonCode ? 1 : 0);
result = 31* result + place.hashCode();
return result;
}
@Override
public String toString() {
return "DeclarationCacheKey{" +
"name='" + name + '\'' +
", kinds=" + kinds +
", nonCode=" + nonCode +
", place=" + place.toString() +
'}';
}
private List collectDeclarations(final PsiElement place) {
final List result = ContainerUtil.newSmartList();
PsiTreeUtil.treeWalkUp(place, null, new PairProcessor() {
@Override
public boolean process(PsiElement scope, PsiElement lastParent) {
result.add(collectScopeDeclarations(scope, lastParent));
if (nonCode && scope instanceof GrClosableBlock) return false; //closures tree walk up themselves if non code declarataions are acepted
return true;
}
});
return result;
}
private DeclarationHolder collectScopeDeclarations(PsiElement scope, PsiElement lastParent) {
MyCollectProcessor plainCollector = new MyCollectProcessor();
MyCollectProcessor nonCodeCollector = new MyCollectProcessor();
ResolveUtil.doProcessDeclarations(place, lastParent, scope, plainCollector, nonCode ? nonCodeCollector : null, ResolveState.initial());
return new DeclarationHolder(scope, plainCollector.declarations, nonCodeCollector.declarations);
}
private List getAllDeclarations(PsiElement place) {
ConcurrentMap> cache = CachedValuesManager.getCachedValue(place, VALUE_PROVIDER);
List declarations = cache.get(this);
if (declarations == null) {
declarations = collectDeclarations(place);
cache.putIfAbsent(this, declarations);
}
return declarations;
}
boolean processCachedDeclarations(PsiElement place, PsiScopeProcessor processor) {
for (DeclarationHolder holder : getAllDeclarations(place)) {
ProgressManager.checkCanceled();
if (!holder.processCachedDeclarations(processor)) {
return false;
}
}
return true;
}
private static class DeclarationHolder {
final PsiElement scope;
final List> plainDeclarations;
final List> nonCodeDeclarations;
private DeclarationHolder(PsiElement scope,
List> plainDeclarations,
List> nonCodeDeclarations) {
this.scope = scope;
this.plainDeclarations = plainDeclarations.isEmpty() ? null : plainDeclarations;
this.nonCodeDeclarations = nonCodeDeclarations.isEmpty() ? null : nonCodeDeclarations;
}
boolean processCachedDeclarations(PsiScopeProcessor processor) {
PsiScopeProcessor realProcessor = ResolveUtil.substituteProcessor(processor, scope);
if (plainDeclarations != null) {
for (Pair pair : plainDeclarations) {
if (!realProcessor.execute(pair.first, pair.second)) {
return false;
}
}
}
if (nonCodeDeclarations != null) {
for (Pair pair : nonCodeDeclarations) {
if (!processor.execute(pair.first, pair.second)) {
return false;
}
}
}
ResolveUtil.issueLevelChangeEvents(processor, scope);
return true;
}
@Override
public String toString() {
return "[scope=" + scope.toString() + ", plain=" + plainDeclarations.size() + ", nonCode=" + nonCodeDeclarations.size();
}
}
private class MyCollectProcessor extends GrScopeProcessorWithHints {
final List> declarations = ContainerUtil.newSmartList();
public MyCollectProcessor() {
super(DeclarationCacheKey.this.name, DeclarationCacheKey.this.kinds);
}
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
declarations.add(Pair.create(element, state));
return true;
}
}
}