/******************************************************************************* * Copyright (c) 2007, 2014 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Markus Schorn - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.ui.typehierarchy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.progress.IWorkbenchSiteProgressService; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexManager; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.internal.ui.viewsupport.IndexUI; import org.eclipse.cdt.internal.ui.viewsupport.WorkingSetFilterUI; class THHierarchyModel { public class BackgroundJob extends Job { public BackgroundJob() { super(Messages.THHierarchyModel_Job_title); } @Override protected IStatus run(IProgressMonitor monitor) { return onComputeGraph(this, monitor); } } static final int TYPE_HIERARCHY = 0; static final int SUB_TYPE_HIERARCHY = 1; static final int SUPER_TYPE_HIERARCHY = 2; static final int END_OF_COMPUTATION = 0; private static final ISchedulingRule RULE = new THSchedulingRule(); private static final Object[] NO_CHILDREN= {}; private ICElement fInput; private int fHierarchyKind; private boolean fShowInheritedMembers; private THGraph fGraph; private THNode[] fRootNodes; private THNode fSelectedTypeNode; private ICElement fTypeToSelect; private ICElement fSelectedMember; private String fMemberSignatureToSelect; private Job fJob; private Display fDisplay; private ITHModelPresenter fView; private WorkingSetFilterUI fFilter; private final boolean fHideNonImplementorLeaves; public THHierarchyModel(ITHModelPresenter view, Display display, boolean hideNonImplementors) { fDisplay= display; fView= view; fHideNonImplementorLeaves= hideNonImplementors; } public ICElement getInput() { return fInput; } public int getHierarchyKind() { return fHierarchyKind; } public void setHierarchyKind(int hierarchyKind) { fHierarchyKind = hierarchyKind; computeNodes(); } public boolean isShowInheritedMembers() { return fShowInheritedMembers; } public void setShowInheritedMembers(boolean showInheritedMembers) { fShowInheritedMembers = showInheritedMembers; updateSelectedMember(); updateImplementors(); } public Object[] getHierarchyRootElements() { if (fRootNodes == null) { return new Object[] { "..." }; //$NON-NLS-1$ } return fRootNodes; } public void setWorkingSetFilter(WorkingSetFilterUI filterUI) { fFilter= filterUI; computeNodes(); } synchronized public void setInput(ICElement input, ICElement member) { stopGraphComputation(); fInput= input; fSelectedMember= member; fMemberSignatureToSelect= TypeHierarchyUI.getLocalElementSignature(fSelectedMember); fRootNodes= null; fSelectedTypeNode= null; fTypeToSelect= input; } synchronized public void computeGraph() { if (fJob != null) { fJob.cancel(); } fJob= new BackgroundJob(); fJob.setRule(RULE); IWorkbenchSiteProgressService ps= fView.getProgressService(); if (ps != null) { ps.schedule(fJob, 0L, true); } else { fJob.schedule(); } } synchronized public void stopGraphComputation() { if (fJob != null) { fJob.cancel(); } fJob= null; } protected IStatus onComputeGraph(Job job, IProgressMonitor monitor) { THGraph graph= new THGraph(); try { ICProject[] scope= CoreModel.getDefault().getCModel().getCProjects(); IIndex index= CCorePlugin.getIndexManager().getIndex(scope, IIndexManager.ADD_EXTENSION_FRAGMENTS_TYPE_HIERARCHY); index.acquireReadLock(); try { if (monitor.isCanceled()) return Status.CANCEL_STATUS; graph.defineInputNode(index, fInput); graph.addSuperClasses(index, monitor); if (monitor.isCanceled()) return Status.CANCEL_STATUS; graph.addSubClasses(index, monitor); if (monitor.isCanceled()) return Status.CANCEL_STATUS; } finally { index.releaseReadLock(); } } catch (CoreException e) { CUIPlugin.log(e); } catch (InterruptedException e) { return Status.CANCEL_STATUS; } finally { onJobDone(graph, job); } return Status.OK_STATUS; } protected void computeNodes() { if (fGraph == null) { return; } boolean fwd= fHierarchyKind == SUPER_TYPE_HIERARCHY; ArrayList<THNode> stack= new ArrayList<>(); ArrayList<THNode> roots= new ArrayList<>(); ArrayList<THNode> leafs= new ArrayList<>(); THGraphNode inputNode= fGraph.getInputNode(); Collection<THGraphNode> groots; if (fHierarchyKind == TYPE_HIERARCHY) { groots= fGraph.getLeafNodes(); } else { THGraphNode node= fGraph.getInputNode(); if (node != null) { groots= Collections.singleton(node); } else { groots= Collections.emptySet(); } } for (THGraphNode gnode : groots) { THNode node = createNode(null, gnode, inputNode); roots.add(node); stack.add(node); } while (!stack.isEmpty()) { THNode node= stack.remove(stack.size() - 1); THGraphNode gnode= fGraph.getNode(node.getElement()); List<THGraphEdge> edges= fwd ? gnode.getOutgoing() : gnode.getIncoming(); if (edges.isEmpty()) { leafs.add(node); } else { for (THGraphEdge edge : edges) { THGraphNode gchildNode= fwd ? edge.getEndNode() : edge.getStartNode(); THNode childNode= createNode(node, gchildNode, inputNode); node.addChild(childNode); stack.add(childNode); } } } fRootNodes= roots.toArray(new THNode[roots.size()]); removeFilteredLeaves(fRootNodes); fSelectedTypeNode= findSelection(fRootNodes); if (fSelectedTypeNode != null) { fTypeToSelect= fSelectedTypeNode.getElement(); updateSelectedMember(); } updateImplementors(); if (!fwd && fHideNonImplementorLeaves && fSelectedMember != null && fMemberSignatureToSelect != null) removeNonImplementorLeaves(fRootNodes); } private void removeFilteredLeaves(THNode[] rootNodes) { for (THNode node : rootNodes) { node.removeFilteredLeaves(); } } private void removeNonImplementorLeaves(THNode[] rootNodes) { for (THNode node : rootNodes) { node.removeNonImplementorLeaves(); } } private THNode findSelection(THNode[] searchme) { THNode[] result= new THNode[2]; findSelection(searchme, result); if (result[0] != null) { return result[0]; } return result[1]; } private void findSelection(THNode[] searchme, THNode[] result) { for (THNode element : searchme) { findSelection(element, result); if (result[0] != null) { break; } } } private void findSelection(THNode node, THNode[] result) { if (node.equals(fSelectedTypeNode)) { result[0]= node; return; } else if (result[1] == null) { if (node.getElement().equals(fTypeToSelect)) { result[1]= node; } } THNode[] children= node.getChildren(); findSelection(children, result); } private void updateSelectedMember() { ICElement oldSelection= fSelectedMember; fSelectedMember= null; if (fSelectedTypeNode != null && fMemberSignatureToSelect != null) { THGraphNode gnode= fGraph.getNode(fSelectedTypeNode.getElement()); if (gnode != null) { ICElement[] members= gnode.getMembers(fShowInheritedMembers); if (members != null) { for (ICElement member : members) { if (member.equals(oldSelection)) { fSelectedMember= member; return; } } for (ICElement member : members) { if (fMemberSignatureToSelect.equals(TypeHierarchyUI.getLocalElementSignature(member))) { fSelectedMember= member; return; } } } } } } private THNode createNode(THNode parent, THGraphNode gnode, THGraphNode inputNode) { ICElement element = gnode.getElement(); THNode node= new THNode(parent, element); if (gnode != inputNode && fFilter != null && !fFilter.isPartOfWorkingSet(element)) { node.setIsFiltered(true); } return node; } synchronized private void onJobDone(final THGraph graph, Job job) { if (fJob == job) { fJob= null; fDisplay.asyncExec(new Runnable(){ @Override public void run() { fGraph= graph; THGraphNode inputNode= fGraph.getInputNode(); if (!fGraph.isFileIndexed()) { fView.setMessage(IndexUI.getFileNotIndexedMessage(fInput)); } else if (inputNode == null) { fView.setMessage(Messages.THHierarchyModel_errorComputingHierarchy); } else { if (fTypeToSelect == fInput) { fTypeToSelect= inputNode.getElement(); } fInput= inputNode.getElement(); } computeNodes(); notifyEvent(END_OF_COMPUTATION); } }); } } private void notifyEvent(int event) { fView.onEvent(event); } synchronized public void refresh() { computeGraph(); } public boolean isComputed() { return fRootNodes!=null; } public THNode getSelectionInHierarchy() { return fSelectedTypeNode; } public void onHierarchySelectionChanged(THNode node) { fSelectedTypeNode= node; if (node != null) { fTypeToSelect= node.getElement(); } updateSelectedMember(); updateImplementors(); } public Object[] getMembers() { if (fSelectedTypeNode != null) { THGraphNode gnode= fGraph.getNode(fSelectedTypeNode.getElement()); Object[] result= gnode.getMembers(fShowInheritedMembers); if (result != null) { return result; } } return NO_CHILDREN; } public void onMemberSelectionChanged(ICElement elem) { fSelectedMember= elem; if (fSelectedMember != null) { fMemberSignatureToSelect= TypeHierarchyUI.getLocalElementSignature(fSelectedMember); } updateImplementors(); } private void updateImplementors() { if (fRootNodes != null) { for (THNode node : fRootNodes) { updateImplementors(node); } } } private void updateImplementors(THNode node) { node.setIsImplementor(isImplementor(node.getElement())); THNode[] children= node.getChildren(); for (THNode child : children) { updateImplementors(child); } } private boolean isImplementor(ICElement element) { if (element == null || fSelectedMember == null || fMemberSignatureToSelect == null) { return false; } THGraphNode gnode= fGraph.getNode(element); if (gnode != null) { ICElement[] members= gnode.getMembers(false); if (members != null) { for (ICElement member : members) { if (member == fSelectedMember) { return true; } if (fMemberSignatureToSelect.equals(TypeHierarchyUI.getLocalElementSignature(member))) { return true; } } } } return false; } public ICElement getSelectedMember() { return fSelectedMember; } public boolean hasTrivialHierarchy() { if (fRootNodes == null || fRootNodes.length == 0) { return true; } return fRootNodes.length == 1 && !fRootNodes[0].hasChildren(); } }