/** * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.python.pydev.actions; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.python.pydev.core.IModule; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.log.Log; import org.python.pydev.editor.PyEdit; import org.python.pydev.editor.actions.PyOpenAction; import org.python.pydev.editor.actions.refactoring.PyRefactorAction; import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule; import org.python.pydev.editor.model.ItemPointer; import org.python.pydev.editor.refactoring.AbstractPyRefactoring; import org.python.pydev.editor.refactoring.IPyRefactoring; import org.python.pydev.editor.refactoring.RefactoringRequest; import org.python.pydev.parser.jython.SimpleNode; import org.python.pydev.parser.jython.ast.ClassDef; import org.python.pydev.parser.visitors.NodeUtils; import org.python.pydev.parser.visitors.scope.ASTEntry; import org.python.pydev.parser.visitors.scope.DefinitionsASTIteratorVisitor; import org.python.pydev.shared_core.structure.DataAndImageTreeNode; import org.python.pydev.shared_core.structure.Location; import org.python.pydev.shared_core.structure.Tuple; import org.python.pydev.shared_ui.EditorUtils; import org.python.pydev.shared_ui.quick_outline.BaseQuickOutlineSelectionDialog; import org.python.pydev.shared_ui.quick_outline.DataAndImageTreeNodeContentProvider; import org.python.pydev.shared_ui.tree.LabelProviderWithDecoration; import com.python.pydev.PydevPlugin; import com.python.pydev.refactoring.IPyRefactoring2; import com.python.pydev.ui.hierarchy.HierarchyNodeModel; /** * @author fabioz * */ public final class PyOutlineSelectionDialog extends BaseQuickOutlineSelectionDialog { /** * May be null (in which case the ast and nodeToModel should be used). */ private PyEdit pyEdit; /** * May be null (in which case the pyedit should be used to calculate it). */ private HashMap<SimpleNode, HierarchyNodeModel> nodeToModel; /** * May be null (in which case the pyedit should be used to calculate it). */ private SimpleNode ast; protected final Job jobCalculateParents = new Job("Calculate parents") { @Override public IStatus run(IProgressMonitor monitor) { rootWithParents = root.createCopy(null); if (nodeToModel == null) { //Step 2: create mapping: classdef to hierarchy model. nodeToModel = new HashMap<SimpleNode, HierarchyNodeModel>(); List<Tuple<ClassDef, DataAndImageTreeNode<Object>>> gathered = new ArrayList<Tuple<ClassDef, DataAndImageTreeNode<Object>>>(); gatherClasses(rootWithParents, monitor, gathered); monitor.beginTask("Calculate parents", gathered.size() + 1); IPyRefactoring pyRefactoring = AbstractPyRefactoring.getPyRefactoring(); IPyRefactoring2 r2 = (IPyRefactoring2) pyRefactoring; for (Tuple<ClassDef, DataAndImageTreeNode<Object>> t : gathered) { SubProgressMonitor subProgressMonitor = new SubProgressMonitor(monitor, 1); try { ClassDef classDef = t.o1; PySelection ps = new PySelection(pyEdit.getDocument(), classDef.name.beginLine - 1, classDef.name.beginColumn - 1); try { RefactoringRequest refactoringRequest = PyRefactorAction.createRefactoringRequest( subProgressMonitor, pyEdit, ps); HierarchyNodeModel model = r2.findClassHierarchy(refactoringRequest, true); nodeToModel.put(((OutlineEntry) t.o2.data).node, model); } catch (MisconfigurationException e) { Log.log(e); } } finally { subProgressMonitor.done(); } } } if (!monitor.isCanceled()) { fillHierarchy(rootWithParents); } if (!monitor.isCanceled()) { uiJobSetRootWithParentsInput.setPriority(Job.INTERACTIVE); uiJobSetRootWithParentsInput.schedule(); } else { //Will be recalculated if asked again! rootWithParents = null; } monitor.done(); return Status.OK_STATUS; } }; /** * Handle the creation for earlier versions of Eclipse. */ private static ILabelProvider createLabelProvider() { try { return new LabelProviderWithDecoration(new ShowOutlineLabelProvider(), PlatformUI.getWorkbench() .getDecoratorManager().getLabelDecorator(), null); } catch (Throwable e) { return new ShowOutlineLabelProvider(); } } /** * Constructor to be used if the pyedit is not available (info must be pre-calculated) */ public PyOutlineSelectionDialog(Shell shell, SimpleNode ast, HashMap<SimpleNode, HierarchyNodeModel> nodeToModel) { super(shell, PydevPlugin.PLUGIN_ID, createLabelProvider(), new DataAndImageTreeNodeContentProvider(), true); this.ast = ast; this.nodeToModel = nodeToModel; calculateHierarchy(); setInput(root); } /** * Constructor to be used if the pyedit is available (in which case the info will be calculated on demand) */ public PyOutlineSelectionDialog(Shell shell, PyEdit pyEdit) { super(shell, PydevPlugin.PLUGIN_ID, createLabelProvider(), new DataAndImageTreeNodeContentProvider(), true); this.pyEdit = pyEdit; PySelection ps = this.pyEdit.createPySelection(); startLineIndex = ps.getStartLineIndex() + 1; //+1 because the ast starts at 1 calculateHierarchy(); setInput(root); //After creating the tree viewer (and setting the input), let's set the initial selection! if (initialSelection != null) { this.setInitialSelections(new Object[] { initialSelection }); } } @Override protected Control createContents(Composite parent) { Control ret = super.createContents(parent); org.python.pydev.plugin.PydevPlugin.setCssId(parent, "py-outline-selection-dialog", true); return ret; } @Override protected void calculateHierarchy() { if (root != null) { return; } if (this.ast == null && pyEdit != null) { this.ast = pyEdit.getAST(); } if (ast == null) { return; } DefinitionsASTIteratorVisitor visitor = DefinitionsASTIteratorVisitor.create(ast); if (visitor == null) { return; } Map<ASTEntry, DataAndImageTreeNode<Object>> entryToTreeNode = new HashMap<ASTEntry, DataAndImageTreeNode<Object>>(); //Step 1: create 'regular' tree structure from the nodes. DataAndImageTreeNode<Object> root = new DataAndImageTreeNode<Object>(null, null, null); for (Iterator<ASTEntry> it = visitor.getOutline(); it.hasNext();) { ASTEntry next = it.next(); DataAndImageTreeNode<Object> n; if (next.parent != null) { DataAndImageTreeNode<Object> parent = entryToTreeNode.get(next.parent); if (parent == null) { Log.log("Unexpected condition: child found before parent!"); parent = root; } n = new DataAndImageTreeNode<Object>(parent, new OutlineEntry(next), null); } else { n = new DataAndImageTreeNode<Object>(root, new OutlineEntry(next), null); } if (((OutlineEntry) n.data).node.beginLine <= startLineIndex) { initialSelection = n; } entryToTreeNode.put(next, n); } this.root = root; } @Override protected void calculateHierarchyWithParents() { if (rootWithParents != null) { uiJobSetRootWithParentsInput.setPriority(Job.INTERACTIVE); uiJobSetRootWithParentsInput.schedule(); return; } calculateHierarchy(); //make sure the root is OK if (root == null) { return; } jobCalculateParents.setPriority(Job.INTERACTIVE); jobCalculateParents.schedule(); } @SuppressWarnings({ "unchecked", "rawtypes" }) private void fillHierarchy(DataAndImageTreeNode<Object> entry) { ArrayList<DataAndImageTreeNode<Object>> copy = new ArrayList(entry.getChildren()); for (DataAndImageTreeNode<Object> nextEntry : copy) { HierarchyNodeModel model = this.nodeToModel.get(((OutlineEntry) nextEntry.data).node); addMethods(nextEntry, model); fillHierarchy(nextEntry); } } private void addMethods(DataAndImageTreeNode<Object> nextEntry, HierarchyNodeModel model) { if (model == null || model.parents == null) { return; } for (HierarchyNodeModel parent : model.parents) { DefinitionsASTIteratorVisitor visitor = DefinitionsASTIteratorVisitor.createForChildren(parent.ast); if (visitor == null) { continue; } Iterator<ASTEntry> outline = visitor.getOutline(); while (outline.hasNext()) { ASTEntry entry = outline.next(); if (entry.parent == null) { //only direct children... new DataAndImageTreeNode<Object>(nextEntry, new OutlineEntry(entry, parent), null); } } addMethods(nextEntry, parent); } } @SuppressWarnings({ "rawtypes", "unchecked" }) private void gatherClasses(DataAndImageTreeNode<Object> entry, IProgressMonitor monitor, List<Tuple<ClassDef, DataAndImageTreeNode<Object>>> gathered) { List children = entry.getChildren(); if (children.size() == 0) { return; } //Iterate in a copy, since we may change the original... for (Object o : children) { DataAndImageTreeNode<Object> nextEntry = (DataAndImageTreeNode<Object>) o; if (((OutlineEntry) nextEntry.data).node instanceof ClassDef) { ClassDef classDef = (ClassDef) ((OutlineEntry) nextEntry.data).node; gathered.add(new Tuple<ClassDef, DataAndImageTreeNode<Object>>(classDef, nextEntry)); } //Enter the leaf to fill it too. gatherClasses(nextEntry, monitor, gathered); } } /* (non-Javadoc) * @see org.eclipse.ui.dialogs.ElementTreeSelectionDialog#open() */ @Override public int open() { int ret = super.open(); if (ret == OK) { Object[] result = getResult(); if (result != null && result.length > 0) { @SuppressWarnings("unchecked") DataAndImageTreeNode<Object> n = (DataAndImageTreeNode<Object>) result[0]; OutlineEntry outlineEntry = (OutlineEntry) n.data; if (outlineEntry.model == null) { Location location = new Location(NodeUtils.getNameLineDefinition(outlineEntry.node) - 1, NodeUtils.getNameColDefinition(outlineEntry.node) - 1); EditorUtils.showInEditor(pyEdit, location, location); } else { PyOpenAction pyOpenAction = new PyOpenAction(); IModule m = outlineEntry.model.module; if (m instanceof SourceModule) { SourceModule sourceModule = (SourceModule) m; File file = sourceModule.getFile(); if (file != null) { ItemPointer p = new ItemPointer(file, outlineEntry.node); pyOpenAction.run(p); } } } } } return ret; } }