/* * 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 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() { @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; } }