/*
* 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.resolve;
import gnu.trove.THashSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import consulo.annotations.RequiredReadAction;
import consulo.csharp.lang.psi.CSharpConstructorDeclaration;
import consulo.csharp.lang.psi.CSharpConversionMethodDeclaration;
import consulo.csharp.lang.psi.CSharpElementVisitor;
import consulo.csharp.lang.psi.CSharpIndexMethodDeclaration;
import consulo.csharp.lang.psi.CSharpMethodDeclaration;
import consulo.csharp.lang.psi.impl.resolve.baseResolveContext.MapElementGroupCollectors;
import consulo.csharp.lang.psi.impl.resolve.baseResolveContext.SimpleElementGroupCollectors;
import consulo.csharp.lang.psi.resolve.CSharpElementGroup;
import consulo.csharp.lang.psi.resolve.CSharpResolveContext;
import consulo.dotnet.psi.DotNetElement;
import consulo.dotnet.psi.DotNetModifierListOwner;
import consulo.dotnet.resolve.DotNetGenericExtractor;
import consulo.dotnet.resolve.DotNetTypeRef;
import consulo.dotnet.resolve.DotNetTypeResolveResult;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import consulo.csharp.lang.CSharpCastType;
/**
* @author VISTALL
* @since 26.10.14
*/
public abstract class CSharpBaseResolveContext<T extends DotNetElement & DotNetModifierListOwner> implements CSharpResolveContext
{
private final NotNullLazyValue<SimpleElementGroupCollectors.IndexMethod> myIndexMethodCollectorValue = new NotNullLazyValue<SimpleElementGroupCollectors.IndexMethod>()
{
@NotNull
@Override
protected SimpleElementGroupCollectors.IndexMethod compute()
{
return new SimpleElementGroupCollectors.IndexMethod(CSharpBaseResolveContext.this);
}
};
private final NotNullLazyValue<SimpleElementGroupCollectors.Constructor> myConstructorCollectorValue = new NotNullLazyValue<SimpleElementGroupCollectors.Constructor>()
{
@NotNull
@Override
protected SimpleElementGroupCollectors.Constructor compute()
{
return new SimpleElementGroupCollectors.Constructor(CSharpBaseResolveContext.this);
}
};
private final NotNullLazyValue<SimpleElementGroupCollectors.DeConstructor> myDeConstructorCollectorValue = new NotNullLazyValue<SimpleElementGroupCollectors.DeConstructor>()
{
@NotNull
@Override
protected SimpleElementGroupCollectors.DeConstructor compute()
{
return new SimpleElementGroupCollectors.DeConstructor(CSharpBaseResolveContext.this);
}
};
private final NotNullLazyValue<MapElementGroupCollectors.ConversionMethod> myConversionMethodCollectorValue = new NotNullLazyValue<MapElementGroupCollectors.ConversionMethod>()
{
@NotNull
@Override
protected MapElementGroupCollectors.ConversionMethod compute()
{
return new MapElementGroupCollectors.ConversionMethod(CSharpBaseResolveContext.this);
}
};
private final NotNullLazyValue<MapElementGroupCollectors.OperatorMethod> myOperatorMethodCollectorValue = new NotNullLazyValue<MapElementGroupCollectors.OperatorMethod>()
{
@NotNull
@Override
protected MapElementGroupCollectors.OperatorMethod compute()
{
return new MapElementGroupCollectors.OperatorMethod(CSharpBaseResolveContext.this);
}
};
private final NotNullLazyValue<MapElementGroupCollectors.Other> myOtherCollectorValue = new NotNullLazyValue<MapElementGroupCollectors.Other>()
{
@NotNull
@Override
protected MapElementGroupCollectors.Other compute()
{
return new MapElementGroupCollectors.Other(CSharpBaseResolveContext.this);
}
};
@NotNull
protected final T myElement;
@NotNull
protected final DotNetGenericExtractor myExtractor;
@Nullable
private Set<PsiElement> myRecursiveGuardSet;
@RequiredReadAction
public CSharpBaseResolveContext(@NotNull T element, @NotNull DotNetGenericExtractor extractor, @Nullable Set<PsiElement> recursiveGuardSet)
{
myElement = element;
myExtractor = extractor;
myRecursiveGuardSet = recursiveGuardSet;
}
public abstract void acceptChildren(CSharpElementVisitor visitor);
@NotNull
@RequiredReadAction
protected abstract List<DotNetTypeRef> getExtendTypeRefs();
@NotNull
@RequiredReadAction
private CSharpResolveContext getSuperContext()
{
THashSet<PsiElement> alreadyProcessedItem = new THashSet<PsiElement>();
if(myRecursiveGuardSet != null)
{
alreadyProcessedItem.addAll(myRecursiveGuardSet);
}
return getSuperContextImpl(alreadyProcessedItem);
}
@NotNull
@RequiredReadAction
private CSharpResolveContext getSuperContextImpl(Set<PsiElement> alreadyProcessedItem)
{
List<DotNetTypeRef> superTypes = getExtendTypeRefs();
if(superTypes.isEmpty())
{
return EMPTY;
}
List<CSharpResolveContext> contexts = new ArrayList<CSharpResolveContext>(superTypes.size());
for(DotNetTypeRef dotNetTypeRef : superTypes)
{
DotNetTypeResolveResult typeResolveResult = dotNetTypeRef.resolve();
PsiElement resolvedElement = typeResolveResult.getElement();
if(resolvedElement != null && alreadyProcessedItem.add(resolvedElement))
{
DotNetGenericExtractor genericExtractor = typeResolveResult.getGenericExtractor();
contexts.add(CSharpResolveContextUtil.createContext(genericExtractor, myElement.getResolveScope(), resolvedElement, alreadyProcessedItem));
}
}
if(contexts.isEmpty())
{
return EMPTY;
}
return new CSharpCompositeResolveContext(myElement.getProject(), contexts.toArray(new CSharpResolveContext[contexts.size()]));
}
@RequiredReadAction
@Nullable
@Override
public CSharpElementGroup<CSharpIndexMethodDeclaration> indexMethodGroup(boolean deep)
{
CSharpElementGroup<CSharpIndexMethodDeclaration> elementGroup = myIndexMethodCollectorValue.getValue().toGroup();
if(elementGroup == null)
{
return deep ? getSuperContext().indexMethodGroup(true) : null;
}
else
{
CSharpElementGroup<CSharpIndexMethodDeclaration> deepGroup = deep ? getSuperContext().indexMethodGroup(true) : null;
if(deepGroup == null)
{
return elementGroup;
}
return new CSharpCompositeElementGroupImpl<CSharpIndexMethodDeclaration>(myElement.getProject(), Arrays.asList(elementGroup, deepGroup));
}
}
@RequiredReadAction
@Nullable
@Override
public CSharpElementGroup<CSharpConstructorDeclaration> constructorGroup()
{
return myConstructorCollectorValue.getValue().toGroup();
}
@RequiredReadAction
@Nullable
@Override
public CSharpElementGroup<CSharpConstructorDeclaration> deConstructorGroup()
{
return myDeConstructorCollectorValue.getValue().toGroup();
}
@RequiredReadAction
@Nullable
@Override
public CSharpElementGroup<CSharpMethodDeclaration> findOperatorGroupByTokenType(@NotNull IElementType type, boolean deep)
{
Map<IElementType, CSharpElementGroup<CSharpMethodDeclaration>> map = myOperatorMethodCollectorValue.getValue().toMap();
if(map == null)
{
return deep ? getSuperContext().findOperatorGroupByTokenType(type, true) : null;
}
else
{
CSharpElementGroup<CSharpMethodDeclaration> deepGroup = deep ? getSuperContext().findOperatorGroupByTokenType(type, true) : null;
if(deepGroup == null)
{
return map.get(type);
}
CSharpElementGroup<CSharpMethodDeclaration> thisGroup = map.get(type);
if(thisGroup == null)
{
return deepGroup;
}
return new CSharpCompositeElementGroupImpl<CSharpMethodDeclaration>(myElement.getProject(), Arrays.asList(thisGroup, deepGroup));
}
}
@RequiredReadAction
@Nullable
@Override
public CSharpElementGroup<CSharpConversionMethodDeclaration> findConversionMethodGroup(@NotNull CSharpCastType castType, boolean deep)
{
Map<CSharpCastType, CSharpElementGroup<CSharpConversionMethodDeclaration>> map = myConversionMethodCollectorValue.getValue().toMap();
if(map == null)
{
return deep ? getSuperContext().findConversionMethodGroup(castType, true) : null;
}
else
{
CSharpElementGroup<CSharpConversionMethodDeclaration> deepGroup = deep ? getSuperContext().findConversionMethodGroup(castType, true) : null;
if(deepGroup == null)
{
return map.get(castType);
}
CSharpElementGroup<CSharpConversionMethodDeclaration> thisGroup = map.get(castType);
if(thisGroup == null)
{
return deepGroup;
}
return new CSharpCompositeElementGroupImpl<CSharpConversionMethodDeclaration>(myElement.getProject(), Arrays.asList(thisGroup, deepGroup));
}
}
@RequiredReadAction
@Nullable
@Override
public CSharpElementGroup<CSharpMethodDeclaration> findExtensionMethodGroupByName(@NotNull String name)
{
Map<String, CSharpElementGroup<PsiElement>> map = myOtherCollectorValue.getValue().toMap();
if(map == null)
{
return null;
}
CSharpElementGroup<PsiElement> elementGroup = map.get(name);
if(elementGroup == null)
{
return null;
}
return filterElementGroupToExtensionGroup(elementGroup);
}
@Nullable
private static CSharpElementGroup<CSharpMethodDeclaration> filterElementGroupToExtensionGroup(CSharpElementGroup<PsiElement> elementGroup)
{
final List<CSharpMethodDeclaration> extensions = new SmartList<CSharpMethodDeclaration>();
elementGroup.process(new Processor<PsiElement>()
{
@Override
public boolean process(PsiElement element)
{
if(element instanceof CSharpMethodDeclaration && ((CSharpMethodDeclaration) element).isExtension())
{
extensions.add((CSharpMethodDeclaration) element);
}
return true;
}
});
if(extensions.isEmpty())
{
return null;
}
return new CSharpElementGroupImpl<CSharpMethodDeclaration>(elementGroup.getProject(), elementGroup.getKey(), extensions);
}
@RequiredReadAction
@Override
public boolean processExtensionMethodGroups(@NotNull Processor<CSharpElementGroup<CSharpMethodDeclaration>> processor)
{
Map<String, CSharpElementGroup<PsiElement>> map = myOtherCollectorValue.getValue().toMap();
if(map == null)
{
return true;
}
for(CSharpElementGroup<PsiElement> elementGroup : map.values())
{
CSharpElementGroup<CSharpMethodDeclaration> group = filterElementGroupToExtensionGroup(elementGroup);
if(group == null)
{
continue;
}
if(!processor.process(group))
{
return false;
}
}
return true;
}
@RequiredReadAction
@Override
@NotNull
public PsiElement[] findByName(@NotNull String name, boolean deep, @NotNull UserDataHolder holder)
{
Map<String, CSharpElementGroup<PsiElement>> map = myOtherCollectorValue.getValue().toMap();
PsiElement[] selectedElements;
if(map == null)
{
selectedElements = PsiElement.EMPTY_ARRAY;
}
else
{
CSharpElementGroup<PsiElement> group = map.get(name);
if(group == null)
{
selectedElements = PsiElement.EMPTY_ARRAY;
}
else
{
selectedElements = new PsiElement[]{group};
}
}
if(deep)
{
selectedElements = ArrayUtil.mergeArrays(selectedElements, getSuperContext().findByName(name, true, holder));
}
return selectedElements;
}
@RequiredReadAction
@Override
public boolean processElements(@NotNull final Processor<PsiElement> processor, boolean deep)
{
if(processElementsImpl(processor))
{
return !deep || getSuperContext().processElements(processor, true);
}
else
{
return false;
}
}
@RequiredReadAction
public boolean processElementsImpl(@NotNull Processor<PsiElement> processor)
{
Map<String, CSharpElementGroup<PsiElement>> map = myOtherCollectorValue.getValue().toMap();
return map == null || ContainerUtil.process(map.values(), processor);
}
@NotNull
public DotNetGenericExtractor getExtractor()
{
return myExtractor;
}
@Override
@NotNull
public T getElement()
{
return myElement;
}
}