com.intellij.refactoring.changeSignature.CallerChooserBase.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.refactoring.changeSignature.CallerChooserBase.java

Source

/*
 * Copyright 2000-2010 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.refactoring.changeSignature;

import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.ide.highlighter.HighlighterFactory;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Splitter;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.ui.*;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.Query;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Set;

public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrapper {
    private final M myMethod;
    private final Alarm myAlarm = new Alarm();
    private MethodNodeBase<M> myRoot;
    protected final Project myProject;
    private Tree myTree;
    private final Consumer<Set<M>> myCallback;
    private TreeSelectionListener myTreeSelectionListener;
    private Editor myCallerEditor;
    private Editor myCalleeEditor;
    private boolean myInitDone;
    private final String myFileName;

    protected abstract MethodNodeBase<M> createTreeNode(M method, HashSet<M> called, Runnable cancelCallback);

    protected abstract M[] findDeepestSuperMethods(M method);

    public CallerChooserBase(M method, Project project, String title, Tree previousTree, String fileName,
            Consumer<Set<M>> callback) {
        super(true);
        myMethod = method;
        myProject = project;
        myTree = previousTree;
        myFileName = fileName;
        myCallback = callback;
        setTitle(title);
        init();
        myInitDone = true;
    }

    public Tree getTree() {
        return myTree;
    }

    @Override
    protected JComponent createCenterPanel() {
        Splitter splitter = new Splitter(false, (float) 0.6);
        JPanel result = new JPanel(new BorderLayout());
        if (myTree == null) {
            myTree = createTree();
        } else {
            final CheckedTreeNode root = (CheckedTreeNode) myTree.getModel().getRoot();
            myRoot = (MethodNodeBase) root.getFirstChild();
        }
        myTreeSelectionListener = new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                final TreePath path = e.getPath();
                if (path != null) {
                    final MethodNodeBase<M> node = (MethodNodeBase) path.getLastPathComponent();
                    myAlarm.cancelAllRequests();
                    myAlarm.addRequest(new Runnable() {
                        @Override
                        public void run() {
                            updateEditorTexts(node);
                        }
                    }, 300);
                }
            }
        };
        myTree.getSelectionModel().addTreeSelectionListener(myTreeSelectionListener);

        JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree);
        splitter.setFirstComponent(scrollPane);
        final JComponent callSitesViewer = createCallSitesViewer();
        TreePath selectionPath = myTree.getSelectionPath();
        if (selectionPath == null) {
            selectionPath = new TreePath(myRoot.getPath());
            myTree.getSelectionModel().addSelectionPath(selectionPath);
        }

        final MethodNodeBase<M> node = (MethodNodeBase) selectionPath.getLastPathComponent();
        updateEditorTexts(node);

        splitter.setSecondComponent(callSitesViewer);
        result.add(splitter);
        return result;
    }

    private void updateEditorTexts(final MethodNodeBase<M> node) {
        final MethodNodeBase<M> parentNode = (MethodNodeBase) node.getParent();
        final String callerText = node != myRoot ? getText(node.getMethod()) : "";
        final Document callerDocument = myCallerEditor.getDocument();
        final String calleeText = node != myRoot ? getText(parentNode.getMethod()) : "";
        final Document calleeDocument = myCalleeEditor.getDocument();

        ApplicationManager.getApplication().runWriteAction(new Runnable() {
            @Override
            public void run() {
                callerDocument.setText(callerText);
                calleeDocument.setText(calleeText);
            }
        });

        final M caller = node.getMethod();
        final PsiElement callee = parentNode != null ? parentNode.getElementToSearch() : null;
        if (caller != null && caller.isPhysical() && callee != null) {
            HighlightManager highlighter = HighlightManager.getInstance(myProject);
            EditorColorsManager colorManager = EditorColorsManager.getInstance();
            TextAttributes attributes = colorManager.getGlobalScheme()
                    .getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES);
            int start = getStartOffset(caller);
            for (PsiElement element : findElementsToHighlight(caller, callee)) {
                highlighter.addRangeHighlight(myCallerEditor, element.getTextRange().getStartOffset() - start,
                        element.getTextRange().getEndOffset() - start, attributes, false, null);
            }
        }
    }

    protected Collection<PsiElement> findElementsToHighlight(M caller, PsiElement callee) {
        Query<PsiReference> references = ReferencesSearch.search(callee, new LocalSearchScope(caller), false);
        return ContainerUtil.mapNotNull(references, new Function<PsiReference, PsiElement>() {
            @Override
            public PsiElement fun(PsiReference psiReference) {
                return psiReference.getElement();
            }
        });
    }

    @Override
    public void dispose() {
        if (myTree != null) {
            myTree.removeTreeSelectionListener(myTreeSelectionListener);
            EditorFactory.getInstance().releaseEditor(myCallerEditor);
            EditorFactory.getInstance().releaseEditor(myCalleeEditor);
        }
        super.dispose();
    }

    private String getText(final M method) {
        if (method == null)
            return "";
        final PsiFile file = method.getContainingFile();
        Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
        if (document != null) {
            final int start = document
                    .getLineStartOffset(document.getLineNumber(method.getTextRange().getStartOffset()));
            final int end = document.getLineEndOffset(document.getLineNumber(method.getTextRange().getEndOffset()));
            return document.getText().substring(start, end);
        }
        return "";
    }

    private int getStartOffset(@NotNull final M method) {
        final PsiFile file = method.getContainingFile();
        Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
        return document.getLineStartOffset(document.getLineNumber(method.getTextRange().getStartOffset()));
    }

    private JComponent createCallSitesViewer() {
        Splitter splitter = new Splitter(true);
        myCallerEditor = createEditor();
        myCalleeEditor = createEditor();
        final JComponent callerComponent = myCallerEditor.getComponent();
        callerComponent.setBorder(IdeBorderFactory
                .createTitledBorder(RefactoringBundle.message("caller.chooser.caller.method"), false));
        splitter.setFirstComponent(callerComponent);
        final JComponent calleeComponent = myCalleeEditor.getComponent();
        calleeComponent.setBorder(IdeBorderFactory
                .createTitledBorder(RefactoringBundle.message("caller.chooser.callee.method"), false));
        splitter.setSecondComponent(calleeComponent);
        splitter.setBorder(IdeBorderFactory.createRoundedBorder());
        return splitter;
    }

    private Editor createEditor() {
        final EditorFactory editorFactory = EditorFactory.getInstance();
        final Document document = editorFactory.createDocument("");
        final Editor editor = editorFactory.createViewer(document, myProject);
        ((EditorEx) editor).setHighlighter(HighlighterFactory.createHighlighter(myProject, myFileName));
        return editor;
    }

    private Tree createTree() {
        final Runnable cancelCallback = new Runnable() {
            @Override
            public void run() {
                if (myInitDone) {
                    close(CANCEL_EXIT_CODE);
                } else {
                    throw new ProcessCanceledException();
                }
            }
        };
        final CheckedTreeNode root = createTreeNode(null, new HashSet<M>(), cancelCallback);
        myRoot = createTreeNode(myMethod, new HashSet<M>(), cancelCallback);
        root.add(myRoot);
        final CheckboxTree.CheckboxTreeCellRenderer cellRenderer = new CheckboxTree.CheckboxTreeCellRenderer(true,
                false) {
            @Override
            public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded,
                    boolean leaf, int row, boolean hasFocus) {
                if (value instanceof MethodNodeBase) {
                    ((MethodNodeBase) value).customizeRenderer(getTextRenderer());
                }
            }
        };
        Tree tree = new CheckboxTree(cellRenderer, root,
                new CheckboxTreeBase.CheckPolicy(false, true, true, false));
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.getSelectionModel().setSelectionPath(new TreePath(myRoot.getPath()));

        return tree;
    }

    private void getSelectedMethods(Set<M> methods) {
        MethodNodeBase<M> node = myRoot;
        getSelectedMethodsInner(node, methods);
        methods.remove(node.getMethod());
    }

    private void getSelectedMethodsInner(final MethodNodeBase<M> node, final Set<M> allMethods) {
        if (node.isChecked()) {
            M method = node.getMethod();
            final M[] superMethods = findDeepestSuperMethods(method);
            if (superMethods.length == 0) {
                allMethods.add(method);
            } else {
                allMethods.addAll(Arrays.asList(superMethods));
            }

            final Enumeration children = node.children();
            while (children.hasMoreElements()) {
                getSelectedMethodsInner((MethodNodeBase) children.nextElement(), allMethods);
            }
        }
    }

    @Override
    protected void doOKAction() {
        final Set<M> selectedMethods = new HashSet<M>();
        getSelectedMethods(selectedMethods);
        myCallback.consume(selectedMethods);
        super.doOKAction();
    }

    @Override
    public JComponent getPreferredFocusedComponent() {
        return myTree;
    }

}