package com.intellij.lang.javascript.flex.presentation;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.projectView.ProjectViewNode;
import com.intellij.ide.projectView.TreeStructureProvider;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
import com.intellij.ide.structureView.StructureViewTreeElement;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.lang.javascript.DialectDetector;
import com.intellij.lang.javascript.JavaScriptSupportLoader;
import com.intellij.lang.javascript.flex.XmlBackedJSClassImpl;
import com.intellij.lang.javascript.psi.JSElement;
import com.intellij.lang.javascript.psi.JSFunction;
import com.intellij.lang.javascript.psi.JSNamedElement;
import com.intellij.lang.javascript.psi.JSVariable;
import com.intellij.lang.javascript.psi.ecmal4.JSClass;
import com.intellij.lang.javascript.psi.ecmal4.JSNamespaceDeclaration;
import com.intellij.lang.javascript.psi.ecmal4.JSQualifiedNamedElement;
import com.intellij.lang.javascript.psi.ecmal4.XmlBackedJSClassFactory;
import com.intellij.lang.javascript.psi.impl.JSFileImpl;
import com.intellij.lang.javascript.psi.resolve.ActionScriptResolveUtil;
import com.intellij.lang.javascript.structureView.JSStructureViewElement;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiCompiledFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.ui.SimpleTextAttributes;
import icons.JavaScriptPsiIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.intellij.lang.javascript.flex.presentation.FlexStructureViewProvider.FlexStructureViewElement;
public class FlexTreeStructureProvider implements TreeStructureProvider, DumbAware {
@NotNull
@Override
public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent, @NotNull Collection<AbstractTreeNode> children, ViewSettings settings) {
List<AbstractTreeNode> result = new ArrayList<>();
if (parent instanceof SwfQualifiedNamedElementNode || parent instanceof FlexFileNode) {
if (((ProjectViewNode)parent).getSettings().isShowMembers()) {
JSQualifiedNamedElement parentElement = getElement(parent);
if (parentElement != null) {
JSStructureViewElement structureViewElement =
parentElement instanceof XmlBackedJSClassImpl
? new FlexStructureViewElement(((XmlBackedJSClassImpl)parentElement), (XmlFile)parentElement.getContainingFile(), false)
: new JSStructureViewElement(parentElement, false, true);
StructureViewTreeElement[] structureViewChildren = structureViewElement.getChildren();
for (final StructureViewTreeElement structureViewChild : structureViewChildren) {
if (structureViewChild instanceof JSStructureViewElement) {
PsiElement childElement = ((JSStructureViewElement)structureViewChild).getValue();
result.add(new FlexClassMemberNode((JSElement)childElement, ((ProjectViewNode)parent).getSettings()));
}
else {
result.add(new UnknownNode(parentElement.getProject(), structureViewChild, ((ProjectViewNode)parent).getSettings()));
}
}
}
}
}
else {
for (final AbstractTreeNode child : children) {
Object o = child.getValue();
if (o instanceof JSFileImpl && !(o instanceof PsiCompiledFile) && DialectDetector.isActionScript((PsiFile)o) ||
o instanceof XmlFile && JavaScriptSupportLoader.isFlexMxmFile((PsiFile)o)) {
result.add(new FlexFileNode((PsiFile)o, ((ProjectViewNode)parent).getSettings()));
continue;
}
result.add(child);
}
}
return result;
}
@Nullable
private static JSQualifiedNamedElement getElement(AbstractTreeNode parent) {
if (parent instanceof SwfQualifiedNamedElementNode) {
return (JSQualifiedNamedElement)parent.getValue();
}
else {
PsiFile file = ((FlexFileNode)parent).getValue();
if (file instanceof JSFileImpl) {
JSNamedElement element = ActionScriptResolveUtil.findMainDeclaredElement((JSFileImpl)file);
if (element instanceof JSQualifiedNamedElement) {
return (JSQualifiedNamedElement)element;
}
}
else if (file instanceof XmlFile) {
return XmlBackedJSClassFactory.getXmlBackedClass((XmlFile)file);
}
}
return null;
}
private static final int INTERFACE_VALUE = 10;
private static final int NAMESPACE_VALUE = 7;
private static final int FUNCTION_VALUE = 5;
private static final int VARIABLE_VALUE = 4;
private static final int CLASS_VALUE = 20;
private static class FlexFileNode extends PsiFileNode {
public FlexFileNode(final PsiFile value, final ViewSettings viewSettings) {
super(value.getProject(), value, viewSettings);
}
@Override
protected void updateImpl(final PresentationData data) {
PsiFile value = getValue();
String className = null;
Icon icon = null;
if (value instanceof JSFileImpl) {
VirtualFile file = value.getVirtualFile();
if (file != null && ProjectRootManager.getInstance(myProject).getFileIndex().getSourceRootForFile(file) != null) {
JSNamedElement element = ActionScriptResolveUtil.findMainDeclaredElement((JSFileImpl)value);
if (element != null) {
className = element.getName();
icon = element.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS);
}
}
}
else if (value instanceof XmlFile) {
VirtualFile file = value.getVirtualFile();
if (file != null && ProjectRootManager.getInstance(myProject).getFileIndex().getSourceRootForFile(file) != null) {
className = file.getNameWithoutExtension();
icon = JavaScriptPsiIcons.Classes.XmlBackedClass;
}
}
if (className != null) {
data.setPresentableText(className);
data.setIcon(icon);
return;
}
super.updateImpl(data);
}
@Override
public int getTypeSortWeight(boolean sortByType) {
if(sortByType) {
PsiFile value = getValue();
if (value instanceof JSFileImpl) {
JSNamedElement element = ActionScriptResolveUtil.findMainDeclaredElement((JSFileImpl)value);
int weight = getElementWeight(element);
if (weight != -1) {
return weight;
}
} else if (value instanceof XmlFile) {
return CLASS_VALUE;
}
}
return super.getTypeSortWeight(sortByType);
}
}
public static int getElementWeight(JSElement element) {
if (element instanceof JSClass) {
return ((JSClass)element).isInterface() ? INTERFACE_VALUE : CLASS_VALUE;
}
else if (element instanceof JSVariable) {
return VARIABLE_VALUE;
}
else if (element instanceof JSFunction) {
return FUNCTION_VALUE;
}
else if (element instanceof JSNamespaceDeclaration) {
return NAMESPACE_VALUE;
}
return -1;
}
private static class UnknownNode extends ProjectViewNode<Object> {
private final StructureViewTreeElement myElement;
public UnknownNode(Project project,
final StructureViewTreeElement element, final ViewSettings viewSettings) {
super(project, element.getValue(), viewSettings);
myElement = element;
}
@Override
public boolean contains(@NotNull final VirtualFile file) {
return false;
}
@Override
@NotNull
public Collection<? extends AbstractTreeNode> getChildren() {
return Collections.emptyList();
}
@Override
protected void update(final PresentationData presentation) {
final ItemPresentation p = myElement.getPresentation();
presentation.setPresentableText(p.getPresentableText());
presentation.setIcon(p.getIcon(false));
presentation.addText(p.getPresentableText(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
presentation.addText(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES);
final String location = presentation.getLocationString();
if (!StringUtil.isEmpty(location)) {
presentation.addText(" (" + location + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
}
}
@Override
public VirtualFile getVirtualFile() {
final Object value = getValue();
return value instanceof PsiElement && ((PsiElement)value).isValid() ? ((PsiElement)value).getContainingFile().getVirtualFile() : null;
}
@Override
public boolean canNavigate() {
return myElement.canNavigate();
}
@Override
public boolean canNavigateToSource() {
return myElement.canNavigateToSource();
}
@Override
public void navigate(final boolean requestFocus) {
myElement.navigate(requestFocus);
}
}
}