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

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

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.VolatileNotNullLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSafeCastExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterList;
import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Created by Max Medvedev on 20/05/14
 */
public class GrTraitType extends PsiClassType {

  private static final Logger LOG = Logger.getInstance(GrTraitType.class);

  private final GrExpression myOriginal;

  private final PsiClassType myExprType;
  private final List myTraitTypes;

  private final GlobalSearchScope myResolveScope;

  private final VolatileNotNullLazyValue myParameters = new VolatileNotNullLazyValue() {
    @NotNull
    @Override
    protected PsiType[] compute() {
      List result = ContainerUtil.newArrayList();
      ContainerUtil.addAll(result, myExprType.getParameters());
      for (PsiClassType type : myTraitTypes) {
        ContainerUtil.addAll(result, type.getParameters());
      }
      return result.toArray(new PsiType[result.size()]);
    }
  };


  public GrTraitType(@NotNull GrExpression original,
                     @NotNull PsiClassType exprType,
                     @NotNull List traitTypes,
                     @NotNull GlobalSearchScope resolveScope, LanguageLevel languageLevel) {
    super(languageLevel);
    myOriginal = original;
    myResolveScope = resolveScope;
    myExprType = exprType;
    myTraitTypes = ContainerUtil.newArrayList(traitTypes);
  }

  @NotNull
  @Override
  public String getPresentableText() {
    return myExprType.getPresentableText() + " as " + StringUtil.join(ContainerUtil.map(myTraitTypes, new Function() {
      @Override
      public String fun(PsiClassType type) {
        return type.getPresentableText();
      }
    }), ", ");
  }

  @NotNull
  @Override
  public String getCanonicalText() {
    return myExprType.getCanonicalText();
  }

  @NotNull
  @Override
  public String getInternalCanonicalText() {
    return myExprType.getCanonicalText() + " as " + StringUtil.join(ContainerUtil.map(myTraitTypes, new Function() {
      @Override
      public String fun(PsiClassType type) {
        return type.getCanonicalText();
      }
    }), ", ");
  }

  @Override
  public boolean isValid() {
    return myExprType.isValid() && ContainerUtil.find(myTraitTypes, new Condition() {
      @Override
      public boolean value(PsiClassType type) {
        return !type.isValid();
      }
    }) == null;
  }

  @Override
  public boolean equalsToText(@NotNull @NonNls String text) {
    return false;
  }

  @NotNull
  @Override
  public LanguageLevel getLanguageLevel() {
    return myLanguageLevel;
  }

  @NotNull
  @Override
  public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) {
    return new GrTraitType(myOriginal, myExprType, myTraitTypes, myResolveScope, languageLevel);
  }

  @Nullable
  @Override
  public PsiClass resolve() {
    return getMockTypeDefinition();
  }

  @Override
  public String getClassName() {
    return null;
  }

  @NotNull
  @Override
  public PsiType[] getParameters() {
    return myParameters.getValue();
  }

  @NotNull
  @Override
  public PsiType[] getSuperTypes() {
    PsiType[] result = new PsiType[myTraitTypes.size() + 1];
    result[0] = myExprType;
    ArrayUtil.copy(myTraitTypes, result, 1);
    return result;
  }

  @NotNull
  @Override
  public ClassResolveResult resolveGenerics() {
    return CachedValuesManager.getCachedValue(myOriginal, new CachedValueProvider() {
      @Nullable
      @Override
      public Result compute() {
        final GrTypeDefinition definition = new MockTypeBuilder().buildMockTypeDefinition();
        final PsiSubstitutor substitutor = new SubstitutorBuilder(definition).buildSubstitutor();

        return Result.create(new TraitResolveResult(definition, substitutor), PsiModificationTracker.MODIFICATION_COUNT);
      }
    });
  }

  @NotNull
  @Override
  public PsiClassType rawType() {
    return new GrTraitType(myOriginal, myExprType.rawType(), ContainerUtil.map(myTraitTypes, new Function() {
      @Override
      public PsiClassType fun(PsiClassType type) {
        return type.rawType();
      }
    }), myResolveScope, myLanguageLevel);
  }

  @NotNull
  @Override
  public GlobalSearchScope getResolveScope() {
    return myResolveScope;
  }

  @Nullable
  public GrTypeDefinition getMockTypeDefinition() {
    return CachedValuesManager.getCachedValue(myOriginal, new CachedValueProvider() {
      @Nullable
      @Override
      public Result compute() {
        return Result.create(new MockTypeBuilder().buildMockTypeDefinition(), PsiModificationTracker.MODIFICATION_COUNT);
      }
    });
  }

  public PsiClassType getExprType() {
    return myExprType;
  }

  public List getTraitTypes() {
    return Collections.unmodifiableList(myTraitTypes);
  }

  public GrTraitType erasure() {
    PsiClassType exprType = (PsiClassType)TypeConversionUtil.erasure(myExprType);
    List traitTypes = ContainerUtil.map(myTraitTypes, new Function() {
      @Override
      public PsiClassType fun(PsiClassType type) {
        return (PsiClassType)TypeConversionUtil.erasure(type);
      }
    });
    return new GrTraitType(myOriginal, exprType, traitTypes, myResolveScope, LanguageLevel.JDK_1_5);
  }

  @Nullable
  public static GrTraitType createTraitClassType(@NotNull GrSafeCastExpression safeCastExpression) {
    GrExpression operand = safeCastExpression.getOperand();
    PsiType exprType = operand.getType();
    if (!(exprType instanceof PsiClassType)) return null;

    GrTypeElement typeElement = safeCastExpression.getCastTypeElement();
    if (typeElement == null) return null;
    PsiType type = typeElement.getType();
    if (!GrTraitUtil.isTrait(PsiTypesUtil.getPsiClass(type))) return null;

    return new GrTraitType(safeCastExpression, ((PsiClassType)exprType), Collections.singletonList((PsiClassType)type), safeCastExpression.getResolveScope(),
                           LanguageLevel.JDK_1_5);
  }


  @NotNull
  public static GrTraitType createTraitClassType(@NotNull GrExpression context,
                                                 @NotNull PsiClassType exprType,
                                                 @NotNull List traitTypes,
                                                 @NotNull GlobalSearchScope resolveScope) {
    return new GrTraitType(context, exprType, traitTypes, resolveScope, LanguageLevel.JDK_1_5);
  }

  private static class TraitResolveResult implements ClassResolveResult {

    private final GrTypeDefinition myDefinition;
    private final PsiSubstitutor mySubstitutor;

    public TraitResolveResult(GrTypeDefinition definition, PsiSubstitutor substitutor) {
      myDefinition = definition;
      mySubstitutor = substitutor;
    }

    @Override
    public GrTypeDefinition getElement() {
      return myDefinition;
    }

    @NotNull
    @Override
    public PsiSubstitutor getSubstitutor() {
      return mySubstitutor;
    }

    @Override
    public boolean isPackagePrefixPackageReference() {
      return false;
    }

    @Override
    public boolean isAccessible() {
      return true;
    }

    @Override
    public boolean isStaticsScopeCorrect() {
      return true;
    }

    @Override
    public PsiElement getCurrentFileResolveScope() {
      return null;
    }

    @Override
    public boolean isValidResult() {
      return true;
    }
  }

  private class MockTypeBuilder {
    GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myOriginal.getProject());

    @Nullable
    public GrTypeDefinition buildMockTypeDefinition() {
      try {

        StringBuilder buffer = new StringBuilder("class _____________Temp___________ ");
        prepareGenerics(buffer);
        buffer.append(" extends Super implements Trait {}");
        GrTypeDefinition definition = factory.createTypeDefinition(buffer.toString());
        replaceReferenceWith(definition.getExtendsClause(), myExprType);
        addReferencesWith(definition.getImplementsClause(), myTraitTypes, myExprType.getParameterCount());
        return definition;
      }
      catch (IncorrectOperationException e) {
        return null;
      }
    }

    private void prepareGenerics(StringBuilder buffer) {
      int count = myExprType.getParameterCount();
      for (PsiClassType trait : myTraitTypes) {
        count += trait.getParameterCount();
      }
      if (count == 0) return;

      buffer.append('<');
      for (int i = 0; i < count; i++) {
        buffer.append("T").append(i).append(",");
      }
      buffer.replace(buffer.length() - 1, buffer.length(), ">");
    }

    private void addReferencesWith(@Nullable GrImplementsClause clause, @NotNull List traitTypes, int parameterOffset) {
      LOG.assertTrue(clause != null);
      clause.getReferenceElementsGroovy()[0].delete();
      for (PsiClassType type : traitTypes) {
        processType(clause, type, parameterOffset);
        parameterOffset += type.getParameterCount();
      }
    }

    private void replaceReferenceWith(@Nullable GrReferenceList clause, @NotNull PsiClassType type) {
      LOG.assertTrue(clause != null);
      clause.getReferenceElementsGroovy()[0].delete();
      processType(clause, type, 0);
    }

    private void processType(@NotNull GrReferenceList clause, @NotNull PsiClassType type, int parameterOffset) {
      PsiClass resolved = type.resolve();
      if (resolved != null) {
        String qname = resolved.getQualifiedName();
        StringBuilder buffer = new StringBuilder();
        buffer.append(qname);
        int parameterCount = type.getParameterCount();
        if (parameterCount > 0) {
          buffer.append('<');
          for (int i = 0; i < parameterCount; i++) {
            buffer.append("T").append(parameterOffset + i).append(',');
          }
          buffer.replace(buffer.length() - 1, buffer.length(), ">");
        }

        GrCodeReferenceElement ref = factory.createCodeReferenceElementFromText(buffer.toString());
        clause.add(ref);
      }
    }
  }

  private class SubstitutorBuilder {

    private final GrTypeParameter[] myParameters;
    private int myOffset = 0;

    public SubstitutorBuilder(@NotNull GrTypeDefinition definition) {
      GrTypeParameterList typeParameterList = definition.getTypeParameterList();
      myParameters = typeParameterList != null ? typeParameterList.getTypeParameters() : GrTypeParameter.EMPTY_ARRAY;
    }

    @NotNull
    public PsiSubstitutor buildSubstitutor() {
      if (myParameters.length == 0) return PsiSubstitutor.EMPTY;

      Map map = ContainerUtil.newLinkedHashMap();
      putMappingAndReturnOffset(map, myExprType);
      for (PsiClassType type : myTraitTypes) {
        putMappingAndReturnOffset(map, type);
      }

      return JavaPsiFacade.getElementFactory(myOriginal.getProject()).createSubstitutor(map);
    }

    private void putMappingAndReturnOffset(@NotNull Map map, @NotNull PsiClassType type) {
      PsiType[] args = type.getParameters();
      for (int i = 0; i < args.length; i++) {
        map.put(myParameters[myOffset + i], args[i]);
      }
      myOffset += args.length;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy