/*
* 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import consulo.annotations.RequiredReadAction;
import consulo.csharp.lang.psi.CSharpMethodDeclaration;
import consulo.csharp.lang.psi.CSharpReferenceExpression;
import consulo.csharp.lang.psi.impl.partial.CSharpCompositeTypeDeclaration;
import consulo.csharp.lang.psi.impl.resolve.CSharpElementGroupImpl;
import consulo.csharp.lang.psi.impl.resolve.CSharpResolveContextUtil;
import consulo.csharp.lang.psi.impl.source.CSharpReferenceExpressionImplUtil;
import consulo.csharp.lang.psi.impl.source.resolve.overrideSystem.OverrideProcessor;
import consulo.csharp.lang.psi.impl.source.resolve.overrideSystem.OverrideUtil;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpGenericExtractor;
import consulo.csharp.lang.psi.impl.source.resolve.type.wrapper.GenericUnwrapTool;
import consulo.csharp.lang.psi.impl.source.resolve.util.CSharpResolveUtil;
import consulo.csharp.lang.psi.resolve.CSharpElementGroup;
import consulo.csharp.lang.psi.resolve.CSharpResolveContext;
import consulo.csharp.lang.psi.resolve.CSharpResolveSelector;
import consulo.dotnet.psi.DotNetGenericParameter;
import consulo.dotnet.resolve.DotNetGenericExtractor;
import consulo.dotnet.resolve.DotNetTypeRef;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.ResolveState;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
/**
* @author VISTALL
* @since 17.12.13.
*/
public class MemberResolveScopeProcessor extends StubScopeProcessor
{
private final PsiElement myScopeElement;
protected Processor<ResolveResult> myResultProcessor;
private final GlobalSearchScope myResolveScope;
private final OverrideProcessor myOverrideProcessor;
public MemberResolveScopeProcessor(@NotNull CSharpResolveOptions options, @NotNull Processor<ResolveResult> resultProcessor, ExecuteTarget[] targets)
{
myScopeElement = options.getElement();
myResolveScope = myScopeElement.getResolveScope();
myResultProcessor = resultProcessor;
myOverrideProcessor = OverrideProcessor.ALWAYS_TRUE;
putUserData(ExecuteTargetUtil.EXECUTE_TARGETS, ExecuteTargetUtil.of(targets));
}
public MemberResolveScopeProcessor(@NotNull PsiElement scopeElement,
@NotNull Processor<ResolveResult> resultProcessor,
@Nullable ExecuteTarget[] targets,
@Nullable OverrideProcessor overrideProcessor)
{
myScopeElement = scopeElement;
myResultProcessor = resultProcessor;
myResolveScope = scopeElement.getResolveScope();
putUserData(ExecuteTargetUtil.EXECUTE_TARGETS, ExecuteTargetUtil.of(targets));
myOverrideProcessor = overrideProcessor;
}
@Override
public void pushResultExternally(@NotNull ResolveResult resolveResult)
{
myResultProcessor.process(resolveResult);
}
@RequiredReadAction
@Override
public boolean execute(@NotNull PsiElement element, ResolveState state)
{
CSharpResolveSelector selector = state.get(CSharpResolveUtil.SELECTOR);
if(selector == null)
{
return true;
}
DotNetGenericExtractor extractor = state.get(CSharpResolveUtil.EXTRACTOR);
assert extractor != null;
CSharpResolveContext context = CSharpResolveContextUtil.createContext(extractor, myResolveScope, element);
PsiElement[] psiElements = selector.doSelectElement(context, state.get(CSharpResolveUtil.WALK_DEEP) == Boolean.TRUE);
applyTypeArguments(psiElements);
psiElements = CSharpCompositeTypeDeclaration.wrapPartialTypes(myResolveScope, myScopeElement.getProject(), psiElements);
for(PsiElement psiElement : OverrideUtil.filterOverrideElements(this, myScopeElement, psiElements, myOverrideProcessor))
{
ProgressManager.checkCanceled();
if(!ExecuteTargetUtil.isMyElement(this, psiElement))
{
continue;
}
final CSharpResolveResult result = new CSharpResolveResult(psiElement);
result.setProvider(element);
result.setAssignable(myScopeElement);
if(!myResultProcessor.process(result))
{
return false;
}
}
return true;
}
@RequiredReadAction
private void applyTypeArguments(PsiElement[] psiElements)
{
if(!(myScopeElement instanceof CSharpReferenceExpression) || psiElements.length == 0)
{
return;
}
int typeArgumentListSize = CSharpReferenceExpressionImplUtil.getTypeArgumentListSize(myScopeElement);
if(typeArgumentListSize == 0)
{
return;
}
DotNetTypeRef[] typeArgumentListRefs = ((CSharpReferenceExpression) myScopeElement).getTypeArgumentListRefs();
for(int i = 0; i < psiElements.length; i++)
{
PsiElement psiElement = psiElements[i];
if(psiElement instanceof CSharpElementGroup)
{
@SuppressWarnings("unchecked") CSharpElementGroup<PsiElement> elementGroup = (CSharpElementGroup<PsiElement>) psiElement;
Collection<PsiElement> elements = elementGroup.getElements();
boolean changed = false;
final List<PsiElement> anotherItems = new ArrayList<PsiElement>(elements.size());
for(PsiElement temp : elements)
{
if(temp instanceof CSharpMethodDeclaration && ((CSharpMethodDeclaration) temp).getGenericParametersCount() == typeArgumentListSize)
{
DotNetGenericParameter[] genericParameters = ((CSharpMethodDeclaration) temp).getGenericParameters();
DotNetGenericExtractor extractor = CSharpGenericExtractor.create(genericParameters, typeArgumentListRefs);
anotherItems.add(GenericUnwrapTool.extract((CSharpMethodDeclaration) temp, extractor));
changed = true;
}
else
{
anotherItems.add(temp);
}
}
if(changed)
{
psiElements[i] = new CSharpElementGroupImpl<PsiElement>(elementGroup.getProject(), elementGroup.getKey(), anotherItems);
}
}
}
}
}