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

org.jetbrains.plugins.groovy.lang.psi.impl.toplevel.imports.GrImportStatementImpl 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.psi.impl.toplevel.imports;

import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.scope.DelegatingScopeProcessor;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrStubElementBase;
import org.jetbrains.plugins.groovy.lang.psi.stubs.GrImportStatementStub;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
import org.jetbrains.plugins.groovy.lang.resolve.processors.GrDelegatingScopeProcessorWithHints;

/**
 * @author ilyas
 */
public class GrImportStatementImpl extends GrStubElementBase implements GrImportStatement, StubBasedPsiElement {

  public GrImportStatementImpl(@NotNull ASTNode node) {
    super(node);
  }

  public GrImportStatementImpl(GrImportStatementStub stub, IStubElementType nodeType) {
    super(stub, nodeType);
  }


  @Override
  public PsiElement getParent() {
    return getParentByStub();
  }

  @Override
  public void accept(GroovyElementVisitor visitor) {
    visitor.visitImportStatement(this);
  }

  public String toString() {
    return "Import statement";
  }

  @Override
  public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
                                     @NotNull ResolveState state,
                                     @Nullable PsiElement lastParent,
                                     @NotNull PsiElement place) {
    if (isAncestor(place)) {
      return true;
    }
    if (isStatic() && lastParent instanceof GrImportStatement) return true;

    if (isOnDemand()) {
      if (!processDeclarationsForMultipleElements(processor, lastParent, place, state)) return false;
    }
    else {
      if (!processDeclarationsForSingleElement(processor, lastParent, place, state)) return false;
    }

    return true;
  }

  private boolean isAncestor(@Nullable PsiElement place) {
    while (place instanceof GrCodeReferenceElement) {
      PsiElement parent = place.getParent();
      if (parent == this) return true;
      place = parent;
    }
    return false;
  }

  private boolean processDeclarationsForSingleElement(@NotNull PsiScopeProcessor processor,
                                                      @Nullable PsiElement lastParent,
                                                      @NotNull PsiElement place,
                                                      @NotNull ResolveState state) {
    String name = getImportedName();
    if (name == null) return true;

    if (isStatic()) {
      return processSingleStaticImport(processor, state, name, lastParent, place);
    }

    NameHint nameHint = processor.getHint(NameHint.KEY);
    if (nameHint == null || name.equals(nameHint.getName(state))) {
      return processSingleClassImport(processor, state);
    }
    return true;
  }

  @Nullable
  private PsiClass resolveQualifier() {
    return CachedValuesManager.getCachedValue(this, new CachedValueProvider() {
      @Nullable
      @Override
      public Result compute() {
        GrCodeReferenceElement reference = getImportReference();
        GrCodeReferenceElement qualifier = reference == null ? null : reference.getQualifier();
        PsiElement target = qualifier == null ? null : qualifier.resolve();
        PsiClass clazz = target instanceof PsiClass ? (PsiClass)target : null;
        return Result.create(clazz, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, GrImportStatementImpl.this);
      }
    });
  }

  private boolean processSingleStaticImport(@NotNull final PsiScopeProcessor processor,
                                            @NotNull ResolveState state,
                                            @NotNull String importedName,
                                            @Nullable PsiElement lastParent,
                                            @NotNull PsiElement place) {
    final GrCodeReferenceElement ref = getImportReference();
    if (ref == null) return true;

    PsiClass clazz = resolveQualifier();
    if (clazz == null) return true;

    state = state.put(ClassHint.RESOLVE_CONTEXT, this);

    final String refName = ref.getReferenceName();

    final NameHint nameHint = processor.getHint(NameHint.KEY);
    final String hintName = nameHint == null ? null : nameHint.getName(state);

    if (hintName == null || importedName.equals(hintName)) {
      if (!clazz.processDeclarations(new GrDelegatingScopeProcessorWithHints(processor, refName, null), state, lastParent, place)) {
        return false;
      }
    }

    if (ResolveUtil.shouldProcessMethods(processor.getHint(ClassHint.KEY))) {
      if (hintName == null || importedName.equals(GroovyPropertyUtils.getPropertyNameByGetterName(hintName, true))) {
        if (!clazz.processDeclarations(new StaticGetterProcessor(refName, processor), state, lastParent, place)) {
          return false;
        }
      }

      if (hintName == null || importedName.equals(GroovyPropertyUtils.getPropertyNameBySetterName(hintName))) {
        if (!clazz.processDeclarations(new StaticSetterProcessor(refName, processor), state, lastParent, place)) {
          return false;
        }
      }
    }

    return true;
  }

  private boolean processSingleClassImport(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state) {
    if (!ResolveUtil.shouldProcessClasses(processor.getHint(ClassHint.KEY))) return true;

    GrCodeReferenceElement ref = getImportReference();
    if (ref == null) return true;

    final PsiElement resolved = ref.resolve();
    if (!(resolved instanceof PsiClass)) {
      return true;
    }

    if (!isAliasedImport() && isFromSamePackage((PsiClass)resolved)) {
      return true; //don't process classes from the same package because such import statements are ignored by compiler
    }

    return processor.execute(resolved, state.put(ClassHint.RESOLVE_CONTEXT, this));
  }

  private boolean isFromSamePackage(@NotNull PsiClass resolved) {
    final String qualifiedName = resolved.getQualifiedName();
    final String packageName = ((GroovyFile)getContainingFile()).getPackageName();
    final String assumed = packageName + '.' + resolved.getName();
    return !packageName.isEmpty() && assumed.equals(qualifiedName);
  }

  private boolean processDeclarationsForMultipleElements(@NotNull final PsiScopeProcessor processor,
                                                         @Nullable PsiElement lastParent,
                                                         @NotNull PsiElement place,
                                                         @NotNull ResolveState state) {
    GrCodeReferenceElement ref = getImportReference();
    if (ref == null) return true;

    if (isStatic()) {
      final PsiElement resolved = ref.resolve();
      if (resolved instanceof PsiClass) {
        state = state.put(ClassHint.RESOLVE_CONTEXT, this);
        final PsiClass clazz = (PsiClass)resolved;
        if (!clazz.processDeclarations(new DelegatingScopeProcessor(processor) {
          @Override
          public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
            if (element instanceof PsiMember && ((PsiMember)element).hasModifierProperty(PsiModifier.STATIC)) {
              return super.execute(element, state);
            }
            return true;
          }
        }, state, lastParent, place)) return false;
      }
    }
    else {
      if (ResolveUtil.shouldProcessClasses(processor.getHint(ClassHint.KEY))) {
        String qName = PsiUtil.getQualifiedReferenceText(ref);
        if (qName != null) {
          PsiPackage aPackage = JavaPsiFacade.getInstance(getProject()).findPackage(qName);
          if (aPackage != null && !((GroovyFile)getContainingFile()).getPackageName().equals(aPackage.getQualifiedName())) {
            state = state.put(ClassHint.RESOLVE_CONTEXT, this);
            if (!aPackage.processDeclarations(processor, state, lastParent, place)) return false;
          }
        }
      }
    }
    return true;
  }

  @Override
  public GrCodeReferenceElement getImportReference() {
    GrImportStatementStub stub = getStub();
    if (stub != null) {
      String referenceText = stub.getReferenceText();
      if (referenceText == null) {
        return null;
      }

      return GroovyPsiElementFactory.getInstance(getProject()).createCodeReferenceElementFromText(referenceText);
    }

    return (GrCodeReferenceElement)findChildByType(GroovyElementTypes.REFERENCE_ELEMENT);
  }

  @Override
  @Nullable
  public String getImportedName() {
    if (isOnDemand()) return null;

    GrImportStatementStub stub = getStub();
    if (stub != null) {
      String name = stub.getAliasName();
      if (name != null) {
        return name;
      }

      String referenceText = stub.getReferenceText();
      if (referenceText == null) return null;

      return StringUtil.getShortName(referenceText);
    }


    PsiElement aliasNameElement = getAliasNameElement();
    if (aliasNameElement != null) {
      return aliasNameElement.getText();
    }

    GrCodeReferenceElement ref = getImportReference();
    return ref == null ? null : ref.getReferenceName();
  }

  @Override
  public boolean isStatic() {
    GrImportStatementStub stub = getStub();
    if (stub != null) {
      return stub.isStatic();
    }

    return findChildByType(GroovyTokenTypes.kSTATIC) != null;
  }

  @Override
  public boolean isAliasedImport() {
    GrImportStatementStub stub = getStub();
    if (stub != null) {
      return stub.getAliasName() != null;
    }
    return getAliasNameElement() != null;
  }

  @Override
  public boolean isOnDemand() {
    GrImportStatementStub stub = getStub();
    if (stub != null) {
      return stub.isOnDemand();
    }
    return findChildByType(GroovyTokenTypes.mSTAR) != null;
  }

  @Override
  @NotNull
  public GrModifierList getAnnotationList() {
    GrImportStatementStub stub = getStub();
    if (stub != null) {
      return ObjectUtils.assertNotNull(getStubOrPsiChild(GroovyElementTypes.MODIFIERS));
    }
    return findNotNullChildByClass(GrModifierList.class);
  }

  @Nullable
  @Override
  public PsiClass resolveTargetClass() {
    final GrCodeReferenceElement ref = getImportReference();
    if (ref == null) return null;

    final PsiElement resolved;
    if (!isStatic() || isOnDemand()) {
      resolved = ref.resolve();
    }
    else {
      resolved = resolveQualifier();
    }

    return resolved instanceof PsiClass ? (PsiClass)resolved : null;
  }

  @Nullable
  @Override
  public PsiElement getAliasNameElement() {
    GrImportStatementStub stub = getStub();
    if (stub != null) {
      String alias = stub.getAliasName();
      if (alias == null) return null;

      GrImportStatement imp = GroovyPsiElementFactory.getInstance(getProject()).createImportStatementFromText("import A as " + alias);
      return imp.getAliasNameElement();
    }

    return findChildByType(GroovyTokenTypes.mIDENT);
  }

  private static class StaticSetterProcessor extends StaticAccessorProcessor {

    public StaticSetterProcessor(String refName, PsiScopeProcessor processor) {
      super(refName, processor);
    }

    @Override
    protected boolean isAccessor(@NotNull PsiMethod method) {
      return GroovyPropertyUtils.isSimplePropertySetter(method, getPropertyName());
    }
  }

  private static class StaticGetterProcessor extends StaticAccessorProcessor {

    public StaticGetterProcessor(String refName, PsiScopeProcessor processor) {
      super(refName, processor);
    }

    @Override
    protected boolean isAccessor(@NotNull PsiMethod method) {
      return GroovyPropertyUtils.isSimplePropertyGetter(method, getPropertyName());
    }
  }

  /**
   * Created by Max Medvedev on 26/03/14
   */
  private abstract static class StaticAccessorProcessor extends GrDelegatingScopeProcessorWithHints {
    private final String myPropertyName;

    public StaticAccessorProcessor(@NotNull String propertyName, @NotNull PsiScopeProcessor processor) {
      super(processor, null, RESOLVE_KINDS_METHOD);
      myPropertyName = propertyName;
    }

    @Override
    public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
      if (element instanceof PsiMethod) {
        PsiMethod method = (PsiMethod)element;
        if (method.hasModifierProperty(PsiModifier.STATIC) && isAccessor(method)) {
          return super.execute(method, state);
        }
      }

      return true;
    }

    protected abstract boolean isAccessor(@NotNull PsiMethod method);

    public String getPropertyName() {
      return myPropertyName;
    }

    @Override
    public  T getHint(@NotNull Key hintKey) {
      if (hintKey == NameHint.KEY) return null;

      return super.getHint(hintKey);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy