/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.lang.psi.impl.source.resolve.type.wrapper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.util.Function; import consulo.annotations.RequiredReadAction; import consulo.csharp.lang.psi.*; import consulo.csharp.lang.psi.impl.light.*; import consulo.csharp.lang.psi.impl.source.CSharpReferenceExpressionImplUtil; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpArrayTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpErrorTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpGenericWrapperTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpPointerTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpRefTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpUserTypeRef; import consulo.dotnet.psi.DotNetGenericParameter; import consulo.dotnet.psi.DotNetLikeMethodDeclaration; import consulo.dotnet.psi.DotNetNamedElement; import consulo.dotnet.psi.DotNetParameter; import consulo.dotnet.psi.DotNetParameterList; import consulo.dotnet.psi.DotNetVirtualImplementOwner; import consulo.dotnet.resolve.DotNetGenericExtractor; import consulo.dotnet.resolve.DotNetGenericWrapperTypeRef; import consulo.dotnet.resolve.DotNetPointerTypeRef; import consulo.dotnet.resolve.DotNetTypeRef; /** * @author VISTALL * @since 13.01.14 */ public class GenericUnwrapTool { public static class GenericExtractFunction implements Function<PsiElement, DotNetTypeRef> { private DotNetGenericExtractor myExtractor; public GenericExtractFunction(DotNetGenericExtractor extractor) { myExtractor = extractor; } @Override public DotNetTypeRef fun(final PsiElement element) { if(element instanceof DotNetGenericParameter) { DotNetTypeRef extractedTypeRef = myExtractor.extract((DotNetGenericParameter) element); if(extractedTypeRef != null) { return extractedTypeRef; } } return null; } } public static class TypeDefCleanFunction implements Function<PsiElement, DotNetTypeRef> { public static final Function<PsiElement, DotNetTypeRef> INSTANCE = new TypeDefCleanFunction(); @Override public DotNetTypeRef fun(PsiElement element) { if(element instanceof CSharpTypeDefStatement) { return ((CSharpTypeDefStatement) element).toTypeRef(); } return null; } } @RequiredReadAction public static <T extends DotNetNamedElement> T extract(T namedElement, DotNetGenericExtractor extractor) { return extract(namedElement, extractor, null); } @RequiredReadAction public static <T extends DotNetNamedElement> T extract(T namedElement, DotNetGenericExtractor extractor, @Nullable PsiElement parent) { if(extractor == DotNetGenericExtractor.EMPTY) { return namedElement; } if(namedElement instanceof CSharpMethodDeclaration) { CSharpMethodDeclaration methodDeclaration = (CSharpMethodDeclaration) namedElement; DotNetParameterList parameterList = methodDeclaration.getParameterList(); DotNetParameter[] parameters = methodDeclaration.getParameters(); DotNetParameter[] newParameters = new DotNetParameter[parameters.length]; for(int i = 0; i < parameters.length; i++) { DotNetParameter parameter = parameters[i]; newParameters[i] = new CSharpLightParameter(parameter, exchangeTypeRef(parameter.toTypeRef(true), extractor, parameter)); } parameterList = new CSharpLightParameterList(parameterList == null ? namedElement : parameterList, newParameters); CSharpLightMethodDeclaration copy = new CSharpLightMethodDeclaration(methodDeclaration, parameterList); exchangeMethodTypeRefs(copy, methodDeclaration, extractor); return cast(copy, parent); } else if(namedElement instanceof CSharpTypeDeclaration) { return cast(new CSharpLightTypeDeclaration((CSharpTypeDeclaration) namedElement, extractor), parent); } else if(namedElement instanceof CSharpIndexMethodDeclaration) { CSharpIndexMethodDeclaration arrayMethodDeclaration = (CSharpIndexMethodDeclaration) namedElement; DotNetParameterList parameterList = arrayMethodDeclaration.getParameterList(); DotNetParameter[] parameters = arrayMethodDeclaration.getParameters(); DotNetParameter[] newParameters = new DotNetParameter[parameters.length]; for(int i = 0; i < parameters.length; i++) { DotNetParameter parameter = parameters[i]; newParameters[i] = new CSharpLightParameter(parameter, exchangeTypeRef(parameter.toTypeRef(true), extractor, parameter)); } parameterList = new CSharpLightParameterList(parameterList == null ? namedElement : parameterList, newParameters); CSharpLightIndexMethodDeclaration copy = new CSharpLightIndexMethodDeclaration(arrayMethodDeclaration, parameterList); exchangeMethodTypeRefs(copy, arrayMethodDeclaration, extractor); return cast(copy, parent); } else if(namedElement instanceof CSharpConversionMethodDeclaration) { CSharpConversionMethodDeclaration conversionMethodDeclaration = (CSharpConversionMethodDeclaration) namedElement; DotNetParameterList parameterList = conversionMethodDeclaration.getParameterList(); DotNetParameter[] parameters = conversionMethodDeclaration.getParameters(); DotNetParameter[] newParameters = new DotNetParameter[parameters.length]; for(int i = 0; i < parameters.length; i++) { DotNetParameter parameter = parameters[i]; newParameters[i] = new CSharpLightParameter(parameter, exchangeTypeRef(parameter.toTypeRef(true), extractor, parameter)); } parameterList = new CSharpLightParameterList(parameterList == null ? namedElement : parameterList, newParameters); DotNetTypeRef returnTypeRef = exchangeTypeRef(conversionMethodDeclaration.getReturnTypeRef(), extractor, namedElement); CSharpLightConversionMethodDeclaration copy = new CSharpLightConversionMethodDeclaration(conversionMethodDeclaration, parameterList, returnTypeRef); return cast(copy, parent); } else if(namedElement instanceof CSharpConstructorDeclaration) { CSharpConstructorDeclaration constructor = (CSharpConstructorDeclaration) namedElement; DotNetParameterList parameterList = constructor.getParameterList(); DotNetParameter[] parameters = constructor.getParameters(); DotNetParameter[] newParameters = new DotNetParameter[parameters.length]; for(int i = 0; i < parameters.length; i++) { DotNetParameter parameter = parameters[i]; newParameters[i] = new CSharpLightParameter(parameter, exchangeTypeRef(parameter.toTypeRef(true), extractor, parameter)); } parameterList = new CSharpLightParameterList(parameterList == null ? namedElement : parameterList, newParameters); CSharpLightConstructorDeclaration copy = new CSharpLightConstructorDeclaration(constructor, parameterList); return cast(copy, parent); } else if(namedElement instanceof CSharpPropertyDeclaration) { CSharpPropertyDeclaration e = (CSharpPropertyDeclaration) namedElement; DotNetTypeRef returnTypeRef = exchangeTypeRef(e.toTypeRef(true), extractor, e); DotNetTypeRef virtualTypeForImpl = exchangeTypeRef(e.getTypeRefForImplement(), extractor, e); return cast(new CSharpLightPropertyDeclaration(e, returnTypeRef, virtualTypeForImpl), parent); } else if(namedElement instanceof CSharpEventDeclaration) { CSharpEventDeclaration e = (CSharpEventDeclaration) namedElement; DotNetTypeRef returnTypeRef = exchangeTypeRef(e.toTypeRef(true), extractor, e); DotNetTypeRef virtualTypeForImpl = exchangeTypeRef(e.getTypeRefForImplement(), extractor, e); return cast(new CSharpLightEventDeclaration(e, returnTypeRef, virtualTypeForImpl), parent); } else if(namedElement instanceof CSharpFieldDeclaration) { CSharpFieldDeclaration e = (CSharpFieldDeclaration) namedElement; return cast(new CSharpLightFieldDeclaration(e, exchangeTypeRef(e.toTypeRef(true), extractor, e)), parent); } return namedElement; } @NotNull @SuppressWarnings("unchecked") private static <T extends DotNetNamedElement> T cast(@NotNull PsiElement target, @Nullable PsiElement parent) { if(parent != null && target instanceof CSharpLightElement) { return (T) ((CSharpLightElement) target).withParent(parent); } return (T) target; } @RequiredReadAction private static <S extends DotNetLikeMethodDeclaration & DotNetVirtualImplementOwner> void exchangeMethodTypeRefs(CSharpLightLikeMethodDeclarationWithImplType<?> copy, S original, DotNetGenericExtractor extractor) { copy.withReturnTypeRef(exchangeTypeRef(original.getReturnTypeRef(), extractor, original)); copy.withTypeRefForImplement(exchangeTypeRef(original.getTypeRefForImplement(), extractor, original)); } @NotNull @RequiredReadAction public static DotNetTypeRef[] exchangeTypeRefs(DotNetTypeRef[] typeRefs, DotNetGenericExtractor extractor, PsiElement element) { return exchangeTypeRefs(typeRefs, new GenericExtractFunction(extractor), element); } @NotNull @RequiredReadAction public static DotNetTypeRef exchangeTypeRef(@NotNull DotNetTypeRef typeRef, @NotNull DotNetGenericExtractor extractor, @NotNull PsiElement scope) { return exchangeTypeRef(typeRef, new GenericExtractFunction(extractor), scope); } @NotNull @RequiredReadAction public static DotNetTypeRef[] exchangeTypeRefs(@NotNull DotNetTypeRef[] typeRefs, @NotNull Function<PsiElement, DotNetTypeRef> func, @NotNull PsiElement element) { if(typeRefs.length == 0) { return DotNetTypeRef.EMPTY_ARRAY; } DotNetTypeRef[] newTypeRefs = new DotNetTypeRef[typeRefs.length]; for(int i = 0; i < typeRefs.length; i++) { DotNetTypeRef typeRef = typeRefs[i]; newTypeRefs[i] = exchangeTypeRef(typeRef, func, element); } return newTypeRefs; } @NotNull @RequiredReadAction public static DotNetTypeRef exchangeTypeRef(@NotNull DotNetTypeRef typeRef, @NotNull Function<PsiElement, DotNetTypeRef> func, @NotNull PsiElement scope) { if(typeRef == DotNetTypeRef.ERROR_TYPE) { return DotNetTypeRef.ERROR_TYPE; } if(typeRef instanceof DotNetGenericWrapperTypeRef) { DotNetGenericWrapperTypeRef wrapperTypeRef = (DotNetGenericWrapperTypeRef) typeRef; DotNetTypeRef inner = exchangeTypeRef(wrapperTypeRef.getInnerTypeRef(), func, scope); DotNetTypeRef[] oldArguments = wrapperTypeRef.getArgumentTypeRefs(); DotNetTypeRef[] arguments = new DotNetTypeRef[oldArguments.length]; for(int i = 0; i < oldArguments.length; i++) { DotNetTypeRef oldArgument = oldArguments[i]; arguments[i] = exchangeTypeRef(oldArgument, func, scope); } return new CSharpGenericWrapperTypeRef(inner, arguments); } else if(typeRef instanceof DotNetPointerTypeRef) { return new CSharpPointerTypeRef(scope, exchangeTypeRef(((DotNetPointerTypeRef) typeRef).getInnerTypeRef(), func, scope)); } else if(typeRef instanceof CSharpRefTypeRef) { return new CSharpRefTypeRef(((CSharpRefTypeRef) typeRef).getType(), exchangeTypeRef(((CSharpRefTypeRef) typeRef).getInnerTypeRef(), func, scope)); } else if(typeRef instanceof CSharpArrayTypeRef) { CSharpArrayTypeRef arrayType = (CSharpArrayTypeRef) typeRef; return new CSharpArrayTypeRef(scope, exchangeTypeRef(arrayType.getInnerTypeRef(), func, scope), arrayType.getDimensions()); } else if(typeRef instanceof CSharpUserTypeRef) { CSharpReferenceExpression referenceExpression = ((CSharpUserTypeRef) typeRef).getReferenceExpression(); DotNetTypeRef[] typeArgumentListRefs = referenceExpression.getTypeArgumentListRefs(); Pair<DotNetTypeRef, PsiElement> pair = extractTypeRef(typeRef, func); DotNetTypeRef innerTypeRef; if(pair.getFirst() == null && pair.getSecond() == null) { String referenceName = referenceExpression.getReferenceName(); innerTypeRef = referenceName == null ? DotNetTypeRef.ERROR_TYPE : new CSharpErrorTypeRef(referenceName); } else if(pair.getFirst() != null) { innerTypeRef = pair.getFirst(); } else { if(func instanceof GenericExtractFunction) { PsiElement psiElement = pair.getSecond(); innerTypeRef = CSharpReferenceExpressionImplUtil.toTypeRef(psiElement, ((GenericExtractFunction) func).myExtractor); } else { innerTypeRef = CSharpReferenceExpressionImplUtil.toTypeRef(pair.getSecond()); } } if(typeArgumentListRefs.length == 0) { return innerTypeRef; } DotNetTypeRef[] typeRefs = exchangeTypeRefs(typeArgumentListRefs, func, scope); return new CSharpGenericWrapperTypeRef(innerTypeRef, typeRefs); } else { Pair<DotNetTypeRef, PsiElement> pair = extractTypeRef(typeRef, func); if(pair.getFirst() == null && pair.getSecond() == null) { // nothing } else if(pair.getFirst() != null) { return pair.getFirst(); } else { if(func instanceof GenericExtractFunction) { PsiElement psiElement = pair.getSecond(); typeRef = CSharpReferenceExpressionImplUtil.toTypeRef(psiElement, ((GenericExtractFunction) func).myExtractor); } else { typeRef = CSharpReferenceExpressionImplUtil.toTypeRef(pair.getSecond()); } return typeRef; } } return typeRef; } @NotNull @RequiredReadAction private static Pair<DotNetTypeRef, PsiElement> extractTypeRef(@NotNull DotNetTypeRef typeRef, @NotNull Function<PsiElement, DotNetTypeRef> func) { PsiElement resolve = typeRef.resolve().getElement(); DotNetTypeRef extractedTypeRef = func.fun(resolve); if(extractedTypeRef != null) { return Pair.create(extractedTypeRef, resolve); } return Pair.create(null, resolve); } }