/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.lang.psi.impl.statements;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.BaseScopeProcessor;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import gnu.trove.THashSet;
import gw.lang.parser.IStatement;
import gw.plugin.ij.lang.parser.GosuCompositeElement;
import gw.plugin.ij.lang.psi.api.statements.IGosuStatement;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Set;
// PsiModifierListOwner added to make codeInspections work
public abstract class AbstractStatementWithLocalDeclarationsImpl<E extends IStatement> extends GosuBaseStatementImpl<E> implements IGosuStatement, PsiModifierListOwner {
public AbstractStatementWithLocalDeclarationsImpl(GosuCompositeElement node) {
super(node);
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable PsiElement lastParent, @NotNull PsiElement place) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
if (lastParent == null) {
// Parent element should not see our vars
return true;
}
Pair<Set<String>, Set<String>> pair = buildMaps();
boolean conflict = pair == null;
final Set<String> classesSet = conflict ? null : pair.getFirst();
final Set<String> variablesSet = conflict ? null : pair.getSecond();
final NameHint hint = processor.getHint(NameHint.KEY);
if (hint != null && !conflict) {
final ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
final String name = hint.getName(state);
if ((elementClassHint == null || elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) && classesSet.contains(name)) {
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
}
if ((elementClassHint == null || elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) && variablesSet.contains(name)) {
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
}
} else {
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
}
return true;
}
// return Pair(classesset, localsSet) or null if there was conflict
@Nullable
private Pair<Set<String>, Set<String>> buildMaps() {
final Set<String> localsSet = new THashSet<>();
final Set<String> classesSet = new THashSet<>();
final Ref<Boolean> conflict = new Ref<>(Boolean.FALSE);
PsiScopesUtil.walkChildrenScopes(this,
new BaseScopeProcessor() {
public boolean execute(PsiElement element, ResolveState state) {
if (element instanceof PsiLocalVariable) {
final PsiLocalVariable variable = (PsiLocalVariable) element;
final String name = variable.getName();
if (!localsSet.add(name)) {
conflict.set(Boolean.TRUE);
localsSet.clear();
classesSet.clear();
}
} else if (element instanceof PsiClass) {
final PsiClass psiClass = (PsiClass) element;
final String name = psiClass.getName();
if (!classesSet.add(name)) {
conflict.set(Boolean.TRUE);
localsSet.clear();
classesSet.clear();
}
}
return !conflict.get();
}
}, ResolveState.initial(), this, this);
Set<String> set1 = (classesSet.isEmpty() ? Collections.<String>emptySet() : classesSet);
Set<String> set2 = (localsSet.isEmpty() ? Collections.<String>emptySet() : localsSet);
return conflict.get() ? null : Pair.create(set1, set2);
}
@Override
public PsiModifierList getModifierList() {
return null;
}
@Override
public boolean hasModifierProperty(@PsiModifier.ModifierConstant @NonNls @NotNull String name) {
return false;
}
}