package org.elixir_lang.beam.psi.impl;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.StubBasedPsiElement;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.ArrayFactory;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.elixir_lang.beam.psi.Module;
import org.elixir_lang.psi.Modular;
import org.elixir_lang.psi.call.Call;
import org.elixir_lang.psi.call.MaybeExported;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static org.elixir_lang.beam.psi.stubs.ModuleStubElementTypes.CALL_DEFINITION;
// See com.intellij.psi.impl.compiled.ClsClassImpl
public class ModuleImpl<T extends StubElement> extends ModuleElementImpl implements Module, StubBasedPsiElement<T> {
private final T stub;
public ModuleImpl(T stub) {
this.stub = stub;
}
/**
* Returns the array of children for the PSI element.
* Important: In some implementations children are only composite elements, i.e. not a leaf elements
*
* @return the array of child elements.
*/
@NotNull
@Override
public PsiElement[] getChildren() {
return new PsiElement[0];
}
/**
* Returns the parent of the PSI element.
*
* @return the parent of the element, or null if the element has no parent.
*/
@Override
public PsiElement getParent() {
return stub.getParentStub().getPsi();
}
@Override
public IStubElementType getElementType() {
return stub.getStubType();
}
@Override
public T getStub() {
return stub;
}
@Override
public void appendMirrorText(@NotNull StringBuilder buffer, int indentLevel) {
assert buffer != null;
}
@Override
public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
setMirrorCheckingType(element, null);
setMirrors(exports(), exports(element));
}
@Contract(pure = true)
@NotNull
public static MaybeExported[] exports(@NotNull TreeElement mirror) {
PsiElement mirrorPsi = mirror.getPsi();
MaybeExported[] exports;
if (mirrorPsi instanceof Call) {
Call mirrorCall = (Call) mirrorPsi;
final List<MaybeExported> exportedList = new ArrayList<MaybeExported>();
Modular.callDefinitionClauseCallWhile(mirrorCall, new Function<Call, Boolean>() {
@Override
public Boolean fun(Call call) {
if (call instanceof MaybeExported) {
MaybeExported maybeExportedCall = (MaybeExported) call;
if (maybeExportedCall.isExported()) {
exportedList.add(maybeExportedCall);
}
}
return true;
}
}
);
exports = exportedList.toArray(new MaybeExported[exportedList.size()]);
} else {
exports = new MaybeExported[0];
}
return exports;
}
private MaybeExported[] exports() {
return (MaybeExported[]) getStub().getChildrenByType(
CALL_DEFINITION,
new ArrayFactory() {
@NotNull
@Override
public MaybeExported[] create(int count) {
return new CallDefinitionImpl[count];
}
}
);
}
/**
* @return {@code null} if it does not have a canonical name OR if it has more than one canonical name
*/
@Nullable
@Override
public String canonicalName() {
assert stub != null;
return null;
}
/**
* @return empty set if no canonical names
*/
@NotNull
@Override
public Set<String> canonicalNameSet() {
assert stub != null;
return null;
}
@Nullable
@Override
public PsiElement getNameIdentifier() {
return this;
}
/**
* Renames the element.
*
* @param name the new element name.
* @return the element corresponding to this element after the rename (either <code>this</code>
* or a different element if the rename caused the element to be replaced).
* @throws IncorrectOperationException if the modification is not supported or not possible for some reason.
*/
@Override
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Cannot modify module name in Beam files");
}
@NotNull
@Override
public PsiElement getNavigationElement() {
return getMirror();
}
@NotNull
@Override
public Project getProject() {
return getMirror().getProject();
}
}