package org.elixir_lang.psi.stub.call;
import com.intellij.psi.PsiReference;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.NamedStubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.containers.SmartHashSet;
import com.intellij.util.io.StringRef;
import org.elixir_lang.psi.call.Call;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
// I normally wouldn't add the redundant StubBased prefix, but it makes generating from Elixir.bnf work
public class Stub<T extends org.elixir_lang.psi.call.StubBased> extends NamedStubBase<T> implements Stubbic {
private static Set<String> setStringRefToSetString(Set<StringRef> stringRefSet) {
Set<String> stringSet = new SmartHashSet<String>(stringRefSet.size());
for (StringRef stringRef : stringRefSet) {
stringSet.add(StringRef.toString(stringRef));
}
return stringSet;
}
private static Set<StringRef> setStringToSetStringRef(Set<String> stringSet) {
Set<StringRef> stringRefSet = new SmartHashSet<StringRef>(stringSet.size());
for (String string : stringSet) {
stringRefSet.add(StringRef.fromString(string));
}
return stringRefSet;
}
/*
* Fields
*/
private final Set<StringRef> canonicalNameSet;
private final boolean hasDoBlockOrKeyword;
private final int resolvedFinalArity;
private final StringRef resolvedFunctionName;
@Nullable
private final StringRef resolvedModuleName;
/*
* Constructors
*/
public Stub(StubElement parent,
@NotNull IStubElementType elementType,
@Nullable String resolvedModuleName,
@Nullable String resolvedFunctionName,
int resolvedFinalArity,
boolean hasDoBlockOrKeyword,
@NotNull String name,
@NotNull Set<String> canonicalNameSet) {
this(
parent,
elementType,
StringRef.fromString(resolvedModuleName),
StringRef.fromString(resolvedFunctionName),
resolvedFinalArity,
hasDoBlockOrKeyword,
StringRef.fromString(name),
setStringToSetStringRef(canonicalNameSet)
);
}
public Stub(StubElement parent,
@NotNull IStubElementType elementType,
@Nullable StringRef resolvedModuleName,
@Nullable StringRef resolvedFunctionName,
int resolvedFinalArity,
boolean hasDoBlockOrKeyword,
@NotNull StringRef name,
@NotNull Set<StringRef> canonicalNameSet) {
super(parent, elementType, name);
this.canonicalNameSet = canonicalNameSet;
this.hasDoBlockOrKeyword = hasDoBlockOrKeyword;
this.resolvedFinalArity = resolvedFinalArity;
this.resolvedFunctionName = resolvedFunctionName;
this.resolvedModuleName = resolvedModuleName;
}
/*
* Instance Methods
*/
/**
* These names do not depend on aliases or nested modules.
*
* @return the canonical texts of the reference
* @see PsiReference#getCanonicalText()
*/
@Override
public Set<String> canonicalNameSet() {
return setStringRefToSetString(canonicalNameSet);
}
/**
* Whether this call has a {@code do} block or a {@code :do} keyword, so it is a macro
*
* @return {@code true} if {@link Call#getDoBlock()} is NOT {@code null} or there is a {@code "do"} keyword argument
* @see org.elixir_lang.psi.impl.ElixirPsiImplUtil#keywordArgument(Call, String)
*/
@Override
public boolean hasDoBlockOrKeyword() {
return hasDoBlockOrKeyword;
}
/**
* The final arity that is non-{@code null}.
*
* @return {@link Call#resolvedSecondaryArity()} if it is not {@code null}; {@link Call#resolvedPrimaryArity()} if
* it is not {@code null}; otherwise, {@code 0}.
*/
@Override
public Integer resolvedFinalArity() {
return resolvedFinalArity;
}
/**
* @return name of the function/macro after taking into account any imports
*/
@Override
@Nullable
public String resolvedFunctionName() {
return StringRef.toString(resolvedFunctionName);
}
/**
* @return name of the qualifying module after taking into account any aliases
*/
@Override
@Nullable
public String resolvedModuleName() {
return StringRef.toString(resolvedModuleName);
}
}