com.intellij.lang.ant.config.execution.TreeView.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.lang.ant.config.execution.TreeView.java

Source

/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.lang.ant.config.execution;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.datatransfer.StringSelection;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.StringTokenizer;

import javax.annotation.Nullable;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.jetbrains.annotations.NonNls;
import javax.annotation.Nonnull;

import com.intellij.ide.CopyProvider;
import com.intellij.ide.DataManager;
import com.intellij.ide.OccurenceNavigator;
import com.intellij.ide.OccurenceNavigatorSupport;
import com.intellij.lang.ant.AntBundle;
import com.intellij.lang.ant.config.AntBuildFile;
import com.intellij.lang.ant.config.AntBuildModelBase;
import com.intellij.lang.ant.config.AntBuildTargetBase;
import com.intellij.lang.ant.config.AntConfigurationBase;
import com.intellij.lang.ant.config.impl.BuildTask;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.ActionPopupMenu;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.ui.AutoScrollToSourceHandler;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.OpenSourceUtil;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.ui.tree.TreeUtil;

public final class TreeView implements AntOutputView, OccurenceNavigator {
    private Tree myTree;
    private DefaultTreeModel myTreeModel;
    private TreePath myParentPath = null;
    private final ArrayList<MessageNode> myMessageItems = new ArrayList<MessageNode>();
    private final JPanel myPanel;
    private boolean myActionsEnabled = true;
    private String myCurrentTaskName;

    private final Project myProject;
    private final AntBuildFile myBuildFile;
    private DefaultMutableTreeNode myStatusNode;
    private final AutoScrollToSourceHandler myAutoScrollToSourceHandler;
    private OccurenceNavigatorSupport myOccurenceNavigatorSupport;
    @NonNls
    public static final String ROOT_TREE_USER_OBJECT = "root";
    @NonNls
    public static final String JUNIT_TASK_NAME = "junit";

    public TreeView(final Project project, final AntBuildFile buildFile) {
        myProject = project;
        myBuildFile = buildFile;
        myAutoScrollToSourceHandler = new AutoScrollToSourceHandler() {
            protected boolean isAutoScrollMode() {
                return AntConfigurationBase.getInstance(myProject).isAutoScrollToSource();
            }

            protected void setAutoScrollMode(boolean state) {
                AntConfigurationBase.getInstance(myProject).setAutoScrollToSource(state);
            }
        };
        myPanel = createPanel();
    }

    public JComponent getComponent() {
        return myPanel;
    }

    private JPanel createPanel() {
        createModel();
        myTree = new MyTree();
        myTree.setLineStyleAngled();
        myTree.setRootVisible(false);
        myTree.setShowsRootHandles(true);
        myTree.updateUI();
        myTree.setLargeModel(true);

        myTree.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myTree), false);
                }
            }
        });

        myTree.addMouseListener(new PopupHandler() {
            public void invokePopup(Component comp, int x, int y) {
                popupInvoked(comp, x, y);
            }
        });

        EditSourceOnDoubleClickHandler.install(myTree);

        myAutoScrollToSourceHandler.install(myTree);

        myOccurenceNavigatorSupport = new OccurenceNavigatorSupport(myTree) {
            protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
                if (!(node instanceof MessageNode)) {
                    return null;
                }
                MessageNode messageNode = (MessageNode) node;
                AntBuildMessageView.MessageType type = messageNode.getType();

                if (type != AntBuildMessageView.MessageType.MESSAGE
                        && type != AntBuildMessageView.MessageType.ERROR) {
                    return null;
                }

                if (!isValid(messageNode.getFile())) {
                    return null;
                }

                return new OpenFileDescriptor(myProject, messageNode.getFile(), messageNode.getOffset());
            }

            @Nullable
            public String getNextOccurenceActionName() {
                return AntBundle.message("ant.execution.next.error.warning.action.name");
            }

            @Nullable
            public String getPreviousOccurenceActionName() {
                return AntBundle.message("ant.execution.previous.error.warning.action.name");
            }
        };

        JPanel panel = new JPanel(new BorderLayout());

        JScrollPane scrollPane = MessageTreeRenderer.install(myTree);
        panel.add(scrollPane, BorderLayout.CENTER);
        return panel;
    }

    private void createModel() {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(ROOT_TREE_USER_OBJECT);
        myTreeModel = new DefaultTreeModel(rootNode);
        myParentPath = new TreePath(rootNode);
    }

    public void setActionsEnabled(boolean actionsEnabled) {
        myActionsEnabled = actionsEnabled;
        if (actionsEnabled) {
            myTreeModel.reload();
        }
    }

    public Object addMessage(AntMessage message) {
        MessageNode messageNode = createMessageNode(message);

        MutableTreeNode parentNode = (MutableTreeNode) myParentPath.getLastPathComponent();
        myTreeModel.insertNodeInto(messageNode, parentNode, parentNode.getChildCount());
        myMessageItems.add(messageNode);

        handleExpansion();
        return messageNode;
    }

    public void addMessages(AntMessage[] messages) {
        DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) myParentPath.getLastPathComponent();
        int[] indices = new int[messages.length];
        for (int i = 0; i < messages.length; i++) {
            AntMessage message = messages[i];
            MessageNode messageNode = createMessageNode(message);
            indices[i] = parentNode.getChildCount();
            parentNode.insert(messageNode, indices[i]);
            myMessageItems.add(messageNode);
        }
        myTreeModel.nodesWereInserted(parentNode, indices);
        handleExpansion();
    }

    private MessageNode createMessageNode(AntMessage message) {
        String text = message.getText();

        boolean allowToShowPosition = true;
        if (JUNIT_TASK_NAME.equals(myCurrentTaskName)) {
            HyperlinkUtil.PlaceInfo info = HyperlinkUtil.parseJUnitMessage(myProject, text);
            if (info != null) {
                message = new AntMessage(message.getType(), message.getPriority(), text, info.getFile(), 1, 1);
                allowToShowPosition = false;
            }
        }

        return new MessageNode(message, myProject, allowToShowPosition);
    }

    private void handleExpansion() {
        if (myActionsEnabled && !myTree.hasBeenExpanded(myParentPath)) {
            myTree.expandPath(myParentPath);
        }
    }

    void scrollToLastMessage() {
        if (myTree == null)
            return;
        int count = myTree.getRowCount();
        if (count > 0) {
            int row = count - 1;
            TreeUtil.selectPath(myTree, myTree.getPathForRow(row));
        }
    }

    public void addJavacMessage(AntMessage message, String url) {
        final StringBuilder builder = StringBuilderSpinAllocator.alloc();
        try {
            final VirtualFile file = message.getFile();
            if (message.getLine() > 0) {
                if (file != null) {
                    ApplicationManager.getApplication().runReadAction(new Runnable() {
                        public void run() {
                            String presentableUrl = file.getPresentableUrl();
                            builder.append(presentableUrl);
                            builder.append(' ');
                        }
                    });
                } else if (url != null) {
                    builder.append(url);
                    builder.append(' ');
                }
                builder.append('(');
                builder.append(message.getLine());
                builder.append(':');
                builder.append(message.getColumn());
                builder.append(") ");
            }
            addJavacMessageImpl(
                    new AntMessage(message.getType(), message.getPriority(), builder.toString() + message.getText(),
                            message.getFile(), message.getLine(), message.getColumn()));
        } finally {
            StringBuilderSpinAllocator.dispose(builder);
        }
    }

    private void addJavacMessageImpl(AntMessage message) {
        MutableTreeNode parentNode = (MutableTreeNode) myParentPath.getLastPathComponent();
        MessageNode messageNode = new MessageNode(message, myProject, false);

        myTreeModel.insertNodeInto(messageNode, parentNode, parentNode.getChildCount());
        myMessageItems.add(messageNode);

        handleExpansion();
    }

    public void addException(AntMessage exception, boolean showFullTrace) {
        MessageNode exceptionRootNode = null;

        StringTokenizer tokenizer = new StringTokenizer(exception.getText(), "\r\n");
        while (tokenizer.hasMoreElements()) {
            String line = (String) tokenizer.nextElement();
            if (exceptionRootNode == null) {
                AntMessage newMessage = new AntMessage(exception.getType(), exception.getPriority(), line,
                        exception.getFile(), exception.getLine(), exception.getColumn());
                exceptionRootNode = new MessageNode(newMessage, myProject, true);
                myMessageItems.add(exceptionRootNode);
            } else if (showFullTrace) {
                if (StringUtil.startsWithChar(line, '\t')) {
                    line = line.substring(1);
                }

                HyperlinkUtil.PlaceInfo info = HyperlinkUtil.parseStackLine(myProject, '\t' + line);
                VirtualFile file = info != null ? info.getFile() : null;
                int lineNumber = info != null ? info.getLine() : 0;
                int column = info != null ? info.getColumn() : 1;
                AntMessage newMessage = new AntMessage(exception.getType(), exception.getPriority(), line, file,
                        lineNumber, column);
                MessageNode child = new MessageNode(newMessage, myProject, false);
                exceptionRootNode.add(child);
                myMessageItems.add(child);
            }
        }
        if (exceptionRootNode == null)
            return;

        MutableTreeNode parentNode = (MutableTreeNode) myParentPath.getLastPathComponent();
        myTreeModel.insertNodeInto(exceptionRootNode, parentNode, parentNode.getChildCount());

        handleExpansion();
    }

    public void collapseAll() {
        TreeUtil.collapseAll(myTree, 2);
    }

    public void expandAll() {
        TreePath[] selectionPaths = myTree.getSelectionPaths();
        TreePath leadSelectionPath = myTree.getLeadSelectionPath();
        int row = 0;
        while (row < myTree.getRowCount()) {
            myTree.expandRow(row);
            row++;
        }

        if (selectionPaths != null) {
            // restore selection
            myTree.setSelectionPaths(selectionPaths);
        }
        if (leadSelectionPath != null) {
            // scroll to lead selection path
            myTree.scrollPathToVisible(leadSelectionPath);
        }
    }

    public void clearAllMessages() {
        for (MessageNode messageItem : myMessageItems) {
            messageItem.clearRangeMarker();
        }
        myMessageItems.clear();
        myStatusNode = null;
        createModel();
        myTree.setModel(myTreeModel);
    }

    public void startBuild(AntMessage message) {
    }

    public void buildFailed(AntMessage message) {
        addMessage(message);
    }

    public void startTarget(AntMessage message) {
        collapseTargets();
        MessageNode targetNode = (MessageNode) addMessage(message);
        myParentPath = myParentPath.pathByAddingChild(targetNode);
    }

    private void collapseTargets() {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) myTreeModel.getRoot();
        for (int i = 0; i < root.getChildCount(); i++) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(i);
            myTree.collapsePath(new TreePath(node.getPath()));
        }
    }

    public void startTask(AntMessage message) {
        myCurrentTaskName = message.getText();
        MessageNode taskNode = (MessageNode) addMessage(message);
        myParentPath = myParentPath.pathByAddingChild(taskNode);
    }

    private void popupInvoked(Component component, int x, int y) {
        final TreePath path = myTree.getLeadSelectionPath();
        if (path == null)
            return;
        if (!(path.getLastPathComponent() instanceof MessageNode))
            return;
        if (getData(PlatformDataKeys.NAVIGATABLE_ARRAY) == null)
            return;
        DefaultActionGroup group = new DefaultActionGroup();
        group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE));
        ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.ANT_MESSAGES_POPUP,
                group);
        menu.getComponent().show(component, x, y);
    }

    @Nullable
    private MessageNode getSelectedItem() {
        TreePath path = myTree.getSelectionPath();
        if (path == null)
            return null;
        if (!(path.getLastPathComponent() instanceof MessageNode))
            return null;
        return (MessageNode) path.getLastPathComponent();
    }

    @Nullable
    public Object getData(Key<?> dataId) {
        if (PlatformDataKeys.NAVIGATABLE == dataId) {
            MessageNode item = getSelectedItem();
            if (item == null)
                return null;
            if (isValid(item.getFile())) {
                return new OpenFileDescriptor(myProject, item.getFile(), item.getOffset());
            }
            if (item.getType() == AntBuildMessageView.MessageType.TARGET) {
                final OpenFileDescriptor descriptor = getDescriptorForTargetNode(item);
                if (descriptor != null && isValid(descriptor.getFile())) {
                    return descriptor;
                }
            }
            if (item.getType() == AntBuildMessageView.MessageType.TASK) {
                final OpenFileDescriptor descriptor = getDescriptorForTaskNode(item);
                if (descriptor != null && isValid(descriptor.getFile())) {
                    return descriptor;
                }
            }
        }
        return null;
    }

    @Nullable
    private OpenFileDescriptor getDescriptorForTargetNode(MessageNode node) {
        final String targetName = node.getText()[0];
        final AntBuildTargetBase target = (AntBuildTargetBase) myBuildFile.getModel().findTarget(targetName);
        return (target == null) ? null : target.getOpenFileDescriptor();
    }

    private @Nullable OpenFileDescriptor getDescriptorForTaskNode(MessageNode node) {
        final String[] text = node.getText();
        if (text == null || text.length == 0)
            return null;
        final String taskName = text[0];
        final TreeNode parentNode = node.getParent();
        if (!(parentNode instanceof MessageNode))
            return null;
        final MessageNode messageNode = (MessageNode) parentNode;
        if (messageNode.getType() != AntBuildMessageView.MessageType.TARGET)
            return null;
        final BuildTask task = ((AntBuildModelBase) myBuildFile.getModel()).findTask(messageNode.getText()[0],
                taskName);
        return (task == null) ? null : task.getOpenFileDescriptor();
    }

    private static boolean isValid(final VirtualFile file) {
        return file != null && ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
            public Boolean compute() {
                return file.isValid();
            }
        }).booleanValue();
    }

    public void finishBuild(String messageText) {
        collapseTargets();
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) myTreeModel.getRoot();
        myStatusNode = new DefaultMutableTreeNode(messageText);
        myTreeModel.insertNodeInto(myStatusNode, root, root.getChildCount());
    }

    public void scrollToStatus() {
        if (myStatusNode != null) {
            TreeUtil.selectPath(myTree, new TreePath(myStatusNode.getPath()));
        }
    }

    public void finishTarget() {
        final TreePath parentPath = myParentPath.getParentPath();
        if (parentPath != null) {
            myParentPath = parentPath;
        }
    }

    public void finishTask() {
        myCurrentTaskName = null;
        final TreePath parentPath = myParentPath.getParentPath();
        if (parentPath != null) {
            myParentPath = parentPath;
        }
    }

    @Nullable
    private static TreePath getFirstErrorPath(TreePath treePath) {
        TreeNode treeNode = (TreeNode) treePath.getLastPathComponent();
        if (treeNode instanceof MessageNode) {
            AntBuildMessageView.MessageType type = ((MessageNode) treeNode).getType();
            if (type == AntBuildMessageView.MessageType.ERROR) {
                return treePath;
            }
        }

        if (treeNode.getChildCount() == 0) {
            return null;
        }
        for (int i = 0; i < treeNode.getChildCount(); i++) {
            TreeNode childNode = treeNode.getChildAt(i);
            TreePath childPath = treePath.pathByAddingChild(childNode);
            TreePath usagePath = getFirstErrorPath(childPath);
            if (usagePath != null) {
                return usagePath;
            }
        }
        return null;
    }

    public void scrollToFirstError() {
        TreePath path = getFirstErrorPath(new TreePath(myTreeModel.getRoot()));
        if (path != null) {
            TreeUtil.selectPath(myTree, path);
        }
    }

    public static final class TreeSelection {
        public String mySelectedTarget;
        public String mySelectedTask;

        public boolean isEmpty() {
            return mySelectedTarget == null && mySelectedTask == null;
        }
    }

    public TreeSelection getSelection() {
        TreeSelection selection = new TreeSelection();

        TreePath path = myTree.getSelectionPath();
        if (path == null)
            return selection;

        Object[] paths = path.getPath();
        for (Object o : paths) {
            if (o instanceof MessageNode) {
                MessageNode messageNode = (MessageNode) o;
                AntBuildMessageView.MessageType type = messageNode.getType();
                if (type == AntBuildMessageView.MessageType.TARGET) {
                    selection.mySelectedTarget = messageNode.getText()[0];
                } else if (type == AntBuildMessageView.MessageType.TASK) {
                    selection.mySelectedTask = messageNode.getText()[0];
                }
            }
        }

        return selection;
    }

    public boolean restoreSelection(TreeSelection treeSelection) {
        if (treeSelection.isEmpty())
            return false;

        DefaultMutableTreeNode root = (DefaultMutableTreeNode) myTreeModel.getRoot();
        for (int i = 0; i < root.getChildCount(); i++) {
            TreeNode node = root.getChildAt(i);
            if (node instanceof MessageNode) {
                MessageNode messageNode = (MessageNode) node;
                String[] text = messageNode.getText();
                if (text.length == 0)
                    continue;
                if (Comparing.equal(treeSelection.mySelectedTarget, text[0])) {
                    TreePath pathToSelect = new TreePath(messageNode.getPath());
                    for (Enumeration enumeration = messageNode.children(); enumeration.hasMoreElements();) {
                        Object o = enumeration.nextElement();
                        if (o instanceof MessageNode) {
                            messageNode = (MessageNode) o;
                            if (Comparing.equal(treeSelection.mySelectedTask, text[0])) {
                                pathToSelect = new TreePath(messageNode.getPath());
                                break;
                            }
                        }
                    }
                    TreeUtil.selectPath(myTree, pathToSelect);
                    myTree.expandPath(pathToSelect);
                    return true;
                }
            }
        }

        return false;
    }

    ToggleAction createToggleAutoscrollAction() {
        return myAutoScrollToSourceHandler.createToggleAction();
    }

    public String getNextOccurenceActionName() {
        return myOccurenceNavigatorSupport.getNextOccurenceActionName();
    }

    public String getPreviousOccurenceActionName() {
        return myOccurenceNavigatorSupport.getPreviousOccurenceActionName();
    }

    public OccurenceNavigator.OccurenceInfo goNextOccurence() {
        return myOccurenceNavigatorSupport.goNextOccurence();
    }

    public OccurenceNavigator.OccurenceInfo goPreviousOccurence() {
        return myOccurenceNavigatorSupport.goPreviousOccurence();
    }

    public boolean hasNextOccurence() {
        return myOccurenceNavigatorSupport.hasNextOccurence();
    }

    public boolean hasPreviousOccurence() {
        return myOccurenceNavigatorSupport.hasPreviousOccurence();
    }

    private class MyTree extends Tree implements DataProvider {
        public MyTree() {
            super(myTreeModel);
        }

        public void setRowHeight(int i) {
            super.setRowHeight(0);
            // this is needed in order to make UI calculate the height for each particular row
        }

        public void updateUI() {
            super.updateUI();
            TreeUtil.installActions(this);
        }

        public Object getData(Key<?> dataId) {
            if (PlatformDataKeys.COPY_PROVIDER == dataId) {
                return new CopyProvider() {
                    public boolean isCopyEnabled(@Nonnull DataContext dataContext) {
                        return getSelectionPath() != null;
                    }

                    public boolean isCopyVisible(@Nonnull DataContext dataContext) {
                        return true;
                    }

                    public void performCopy(@Nonnull DataContext dataContext) {
                        TreePath selection = getSelectionPath();
                        Object value = selection.getLastPathComponent();
                        String text;
                        if (value instanceof MessageNode) {
                            MessageNode messageNode = ((MessageNode) value);
                            String[] lines = messageNode.getText();
                            text = "";
                            for (String line : lines) {
                                text += line + "\n";
                            }
                        } else {
                            text = value.toString();
                        }
                        CopyPasteManager.getInstance().setContents(new StringSelection(text));
                    }
                };
            }
            return null;
        }
    }
}