package org.elixir_lang.structure_view.element; import com.intellij.ide.util.treeView.smartTree.TreeElement; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.util.Pair; import com.intellij.psi.ElementDescriptionLocation; import com.intellij.psi.PsiElement; import com.intellij.usageView.UsageViewTypeLocation; import org.elixir_lang.navigation.item_presentation.Parent; import org.elixir_lang.psi.ElixirAccessExpression; import org.elixir_lang.psi.ElixirList; import org.elixir_lang.psi.QuotableKeywordList; import org.elixir_lang.psi.QuotableKeywordPair; import org.elixir_lang.psi.call.Call; import org.elixir_lang.psi.impl.ElixirPsiImplUtil; import org.elixir_lang.structure_view.element.modular.Modular; import org.elixir_lang.structure_view.element.structure.Structure; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.elixir_lang.psi.call.name.Function.DEFEXCEPTION; import static org.elixir_lang.psi.call.name.Module.KERNEL; import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.stripAccessExpression; /** * A `defexception` with its fields and the callbacks `exception/1` and `message/1` if overridden. */ public class Exception extends Element<Call> { /* * Fields */ @Nullable private List<CallDefinition> callbacks = null; @NotNull private final Modular modular; /* * Static Methods */ public static String elementDescription(Call call, ElementDescriptionLocation location) { String elementDescription = null; if (location == UsageViewTypeLocation.INSTANCE) { elementDescription = "exception"; } return elementDescription; } public static boolean is(Call call) { return call.isCalling(KERNEL, DEFEXCEPTION, 1); } public static boolean isCallback(Pair<String, Integer> nameArity) { return nameArity.second == 1 && (nameArity.first.equals("exception") || nameArity.first.equals("message")); } /* * Constructors */ public Exception(@NotNull Modular modular, @NotNull Call call) { super(call); this.modular = modular; } /* * Instance Methods */ /** * Adds callback function * * @param callback the callback function: either exception/1 or message/1. */ @Contract(pure = false) public void callback(@NotNull final CallDefinition callback) { assert callback.arity() == 1; assert callback.time() == Timed.Time.RUN; String callbackName = callback.name(); assert callbackName.equals("exception") || callbackName.equals("message"); if (callbacks == null) { callbacks = new ArrayList<CallDefinition>(); } callbacks.add(callback); } /** * The default value elements for the struct defined for the exception. * * @return Maps the element for the key in the struct to the element in the default value. When the list form of * fields without default values is used, the Map value element is {@code null}. */ @NotNull public Map<PsiElement, PsiElement> defaultValueElementByKeyElement() { PsiElement[] finalArguments = ElixirPsiImplUtil.finalArguments(navigationItem); assert finalArguments != null; assert finalArguments.length == 1; PsiElement finalArgument = finalArguments[0]; Map<PsiElement, PsiElement> defaultValueElementByKeyElement = new HashMap<PsiElement, PsiElement>(finalArguments.length); if (finalArgument instanceof ElixirAccessExpression) { PsiElement accessExpressionChild = stripAccessExpression(finalArgument); assert accessExpressionChild instanceof ElixirList; ElixirList list = (ElixirList) accessExpressionChild; PsiElement[] listChildren = list.getChildren(); if (listChildren.length == 1) { PsiElement listChild = listChildren[0]; if (listChild instanceof QuotableKeywordList) { QuotableKeywordList quotableKeywordList = (QuotableKeywordList) listChild; putQuotableKeywordList(defaultValueElementByKeyElement, quotableKeywordList); } else { defaultValueElementByKeyElement.put(listChild, null); } } else { for (PsiElement key : list.getChildren()) { defaultValueElementByKeyElement.put(key, null); } } } else if (finalArgument instanceof QuotableKeywordList) { QuotableKeywordList quotableKeywordList = (QuotableKeywordList) finalArgument; putQuotableKeywordList(defaultValueElementByKeyElement, quotableKeywordList); } else { assert finalArgument != null; } return defaultValueElementByKeyElement; } /** * Returns the list of children of the tree element. * * @return the list of children. */ @NotNull @Override public TreeElement[] getChildren() { List<TreeElement> childList = new ArrayList<TreeElement>(); childList.add( new Structure(modular, navigationItem) ); if (callbacks != null) { childList.addAll(callbacks); } return childList.toArray(new TreeElement[childList.size()]); } /** * Returns the presentation of the tree element. * * @return the element presentation. */ @NotNull @Override public ItemPresentation getPresentation() { Parent parentPresentation = (Parent) modular.getPresentation(); String location = parentPresentation.getLocatedPresentableText(); int lastIndex = location.lastIndexOf('.'); String parentLocation; String name; if (lastIndex != -1) { parentLocation = location.substring(0, lastIndex); name = location.substring(lastIndex + 1, location.length()); } else { parentLocation = null; name = location; } return new org.elixir_lang.navigation.item_presentation.Exception( parentLocation, name ); } private void putQuotableKeywordList(Map<PsiElement, PsiElement> defaultValueElementByKeyElement, QuotableKeywordList quotableKeywordList) { for (QuotableKeywordPair quotableKeywordPair : quotableKeywordList.quotableKeywordPairList()) { PsiElement keyElement = quotableKeywordPair.getKeywordKey(); PsiElement valueElement = quotableKeywordPair.getKeywordValue(); defaultValueElementByKeyElement.put(keyElement, valueElement); } } }