blob: 8539b55835a66471fc2b2679a0e4369eb67c0d3f [file] [log] [blame]
/*
* Copyright 2000-2011 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;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* User: anna
*/
public class PsiDiamondTypeUtil {
private static final Logger LOG = Logger.getInstance("#" + PsiDiamondTypeUtil.class.getName());
private PsiDiamondTypeUtil() {
}
public static boolean canCollapseToDiamond(final PsiNewExpression expression,
final PsiNewExpression context,
@Nullable final PsiType expectedType) {
return canCollapseToDiamond(expression, context, expectedType, false);
}
public static boolean canChangeContextForDiamond(final PsiNewExpression expression, final PsiType expectedType) {
final PsiNewExpression copy = (PsiNewExpression)expression.copy();
return canCollapseToDiamond(copy, copy, expectedType, true);
}
private static boolean canCollapseToDiamond(final PsiNewExpression expression,
final PsiNewExpression context,
@Nullable final PsiType expectedType,
boolean skipDiamonds) {
if (PsiUtil.getLanguageLevel(context).isAtLeast(LanguageLevel.JDK_1_7)) {
final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiReferenceParameterList parameterList = classReference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements();
if (typeElements.length > 0) {
if (!skipDiamonds && typeElements.length == 1 && typeElements[0].getType() instanceof PsiDiamondType) return false;
final PsiDiamondTypeImpl.DiamondInferenceResult inferenceResult = PsiDiamondTypeImpl.resolveInferredTypes(expression, context);
if (inferenceResult.getErrorMessage() == null) {
final List<PsiType> types = inferenceResult.getInferredTypes();
PsiType[] typeArguments = null;
if (expectedType instanceof PsiClassType) {
typeArguments = ((PsiClassType)expectedType).getParameters();
}
if (typeArguments == null) {
typeArguments = parameterList.getTypeArguments();
}
if (types.size() == typeArguments.length) {
for (int i = 0, typeArgumentsLength = typeArguments.length; i < typeArgumentsLength; i++) {
PsiType typeArgument = typeArguments[i];
if (types.get(i) instanceof PsiWildcardType) {
final PsiWildcardType wildcardType = (PsiWildcardType)types.get(i);
final PsiType bound = wildcardType.getBound();
if (bound != null) {
if (wildcardType.isExtends()) {
if (bound.isAssignableFrom(typeArgument)) continue;
}
else {
if (typeArgument.isAssignableFrom(bound)) continue;
}
}
}
if (!typeArgument.equals(types.get(i))) {
return false;
}
}
}
return true;
}
}
}
}
}
return false;
}
public static PsiElement replaceExplicitWithDiamond(PsiElement psiElement) {
if (psiElement instanceof PsiReferenceParameterList) {
if (!FileModificationService.getInstance().prepareFileForWrite(psiElement.getContainingFile())) return psiElement;
final PsiNewExpression expression =
(PsiNewExpression)JavaPsiFacade.getElementFactory(psiElement.getProject()).createExpressionFromText("new a<>()", psiElement);
final PsiJavaCodeReferenceElement classReference = expression.getClassReference();
LOG.assertTrue(classReference != null);
final PsiReferenceParameterList parameterList = classReference.getParameterList();
LOG.assertTrue(parameterList != null);
return psiElement.replace(parameterList);
}
return psiElement;
}
public static PsiElement replaceDiamondWithExplicitTypes(PsiElement element) {
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiJavaCodeReferenceElement)) {
return parent;
}
final PsiJavaCodeReferenceElement javaCodeReferenceElement =
(PsiJavaCodeReferenceElement) parent;
final PsiReferenceParameterList referenceParameterList =
(PsiReferenceParameterList) element;
final StringBuilder text = new StringBuilder();
text.append(javaCodeReferenceElement.getQualifiedName());
text.append('<');
final PsiTypeElement[] typeElements = referenceParameterList.getTypeParameterElements();
final PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(typeElements[0], PsiNewExpression.class);
final PsiDiamondType.DiamondInferenceResult result = PsiDiamondTypeImpl.resolveInferredTypesNoCheck(newExpression, newExpression);
text.append(StringUtil.join(result.getInferredTypes(), new Function<PsiType, String>() {
@Override
public String fun(PsiType psiType) {
return psiType.getCanonicalText();
}
}, ","));
text.append('>');
final PsiElementFactory elementFactory =
JavaPsiFacade.getElementFactory(element.getProject());
final PsiJavaCodeReferenceElement newReference =
elementFactory.createReferenceFromText(text.toString(), element);
return CodeStyleManager.getInstance(javaCodeReferenceElement.getProject()).reformat(javaCodeReferenceElement.replace(newReference));
}
public static PsiExpression expandTopLevelDiamondsInside(PsiExpression expr) {
if (expr instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)expr).getClassReference();
if (classReference != null) {
final PsiReferenceParameterList parameterList = classReference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] typeParameterElements = parameterList.getTypeParameterElements();
if (typeParameterElements.length == 1 && typeParameterElements[0].getType() instanceof PsiDiamondType) {
return (PsiExpression)replaceDiamondWithExplicitTypes(parameterList).getParent();
}
}
}
}
return expr;
}
public static String getCollapsedType(PsiType type, PsiElement context) {
String typeText = type.getCanonicalText();
if (PsiUtil.isLanguageLevel7OrHigher(context)) {
final int idx = typeText.indexOf('<');
if (idx >= 0) {
return typeText.substring(0, idx) + "<>";
}
}
return typeText;
}
}