All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.plugins.groovy.lang.resolve.DeclarationCacheKey Maven / Gradle / Ivy

Go to download

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;
    }
  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy