/******************************************************************************* * Copyright (c) 2012 xored software, Inc. * 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: * xored software, Inc. - initial API and implementation (Ivan Lobachev) ******************************************************************************/ package com.xored.glance.ui.ccvs; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.TreeEvent; import org.eclipse.swt.events.TreeListener; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSTag; import org.eclipse.team.internal.ccvs.core.resources.RemoteFile; import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder; import org.eclipse.team.internal.ccvs.ui.repo.RepositoryRoot; import org.eclipse.ui.progress.PendingUpdateAdapter; import com.xored.glance.ui.controls.tree.TreeCell; import com.xored.glance.ui.controls.tree.TreeContent; import com.xored.glance.ui.controls.tree.TreeItemContent; import com.xored.glance.ui.controls.tree.TreeNode; @SuppressWarnings("restriction") public class CVSHistorySourceTree extends TreeContent implements ITreeExpandUpdater { private List<CVSHistoryTree> trees = new ArrayList<CVSHistoryTree>(); private Tree tree; private TreeListener listener; public CVSHistorySourceTree(Tree tree) { this.tree = tree; init(); } @Override public void dispose() { if (listener != null && !tree.isDisposed()) { tree.removeTreeListener(listener); listener = null; } } @Override public TreeItemContent getContent(TreeCell cell) { TreeItem item = cell.getTreeItem(); Object node = treeItemToCVSNode.get(item); if (node == null) { node = findCVSTreeElement(item); } TreeItemContent content = nodeToItemContent.get(node); return content; } public Map<ICVSHistoryNode, TreeItemContent> nodeToItemContent = new HashMap<ICVSHistoryNode, TreeItemContent>(); public TreeNode buildGlanceTree(ICVSHistoryNode parent, boolean foldersOnly) { if (parent.getCount() != 1) { return null; } TreeNode root = new TreeNode(null); TreeItemContent rootContent = new TreeItemContent(root, parent .getElementName(), 0); nodeToItemContent.put(parent, rootContent); for (ICVSHistoryNode node : parent.getNodeChildren()) { if (node.getCount() == 1) { if (node.getType() == ICVSHistoryNode.FILE && foldersOnly) { continue; } TreeNode childTree = buildGlanceTree(node, foldersOnly); root.add(new TreeNode[] { childTree }); } } return root; } @Override public void index(final IProgressMonitor monitor) { tree.getDisplay().asyncExec(new Runnable() { public void run() { trees = new ArrayList<CVSHistoryTree>(); final TreeItem[] items = tree.getItems(); if (items.length <= 0) { return; } final boolean isHeadTreeOnly = (items[0].getData() instanceof RemoteFolder); final RepositoryRoot[] repositories = isHeadTreeOnly ? new RepositoryRoot[1] : new RepositoryRoot[items.length]; for (int i = 0; i < repositories.length; i++) { if (items[i].getData() instanceof RepositoryRoot) { repositories[i] = ((RepositoryRoot) (items[i].getData())); } else if (items[i].getData() instanceof RemoteFolder) { repositories[i] = new RepositoryRoot( ((RemoteFolder) (items[i].getData())) .getRepository()); } } new Thread() { // tree.getDisplay().asyncExec(new Runnable() { @Override public void run() { // monitor.beginTask("", folders.length); monitor.beginTask("", repositories.length); // for (final ICVSRemoteFolder folder : folders) { for (final RepositoryRoot repoRoot : repositories) { SubProgressMonitor subMonitor = new SubProgressMonitor( monitor, 1); HistoryFetcher fetcher = new HistoryFetcher(); try { final CVSHistoryTree historyTree = fetcher .fetchHistory(subMonitor, /* folder */ repoRoot.getRemoteFolder("", new CVSTag(), new NullProgressMonitor())); TreeNode repoTree = null; if (isHeadTreeOnly) { ICVSHistoryNode[] nodes = historyTree .getChild("HEAD").getNodeChildren(); for (final ICVSHistoryNode node : nodes) { if (node.getCount() != 1) { continue; } repoTree = buildGlanceTree(node, true); add(new TreeNode[] { repoTree }); tree.getDisplay().asyncExec( new Runnable() { public void run() { cacheCVSNode( items, node .getElementName(), node); } }); } } else { repoTree = buildGlanceTree(historyTree, false); add(new TreeNode[] { repoTree }); } tree.getDisplay().asyncExec(new Runnable() { public void run() { cacheCVSNode(items, repoRoot.getRoot() .toString(), historyTree); } }); trees.add(historyTree); } catch (OperationCanceledException e) { // ignore this exception } catch (CVSException e) { e.printStackTrace(); } } monitor.done(); } }.start(); // }); } }); } private void cacheCVSNode(TreeItem[] items, String elementName, ICVSHistoryNode node) { for (TreeItem item : items) { if (item.getText().equals(elementName)) { treeItemToCVSNode.put(item, node); } } } public Map<TreeItem, ICVSHistoryNode> treeItemToCVSNode = new HashMap<TreeItem, ICVSHistoryNode>(); @SuppressWarnings("restriction") private ICVSHistoryNode findCVSTreeElement(TreeItem item) { Object data = item.getData(); if (data == null || data instanceof PendingUpdateAdapter) { return null; } String fullPath = TreeUtils.getFullPath(item); CVSHistoryTree curCVSTree = null; for (CVSHistoryTree cvsTree : trees) { if (fullPath.startsWith(cvsTree.getRepository().toString())) { curCVSTree = cvsTree; break; } } if (curCVSTree != null) { return findInRepo(curCVSTree, item.getData(), fullPath); } else { for (CVSHistoryTree cvsTree : trees) { ICVSHistoryNode node = findInRepo(cvsTree, item.getData(), cvsTree.getRepository().toString() + "/" + "HEAD/" + fullPath); if (node != null) { return node; } } } return null; } private ICVSHistoryNode findInRepo(CVSHistoryTree curCVSTree, Object itemData, String fullPath) { if (curCVSTree == null) { return null; } if (itemData instanceof RemoteFile) { int ind = fullPath.lastIndexOf("/"); if (ind != -1) { return curCVSTree.findFile(fullPath.substring(ind + 1), fullPath.substring(0, ind)); } } else { return curCVSTree.findFolder(fullPath); } return null; } /** * Fills cvsElemsToTree map which associate ICVSHistoryTreeElement with * TreeItee and updates Tree (highlighting, selection) * * @param item */ @SuppressWarnings("restriction") public void updateOnExpand(final TreeItem item) { Object data = item.getData(); if (data != null && data instanceof RepositoryRoot && treeItemToCVSNode.get(item) == null) { for (CVSHistoryTree tree : trees) { if (tree.getRepository().equals( ((RepositoryRoot) data).getRoot())) { treeItemToCVSNode.put(item, tree); // System.out.println("Added: " + item); } } } TreeItem[] items = item.getItems(); if (items.length > 0 && items[0].getData() instanceof PendingUpdateAdapter) { items[0].addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { cacheItemChildren(((TreeItem) e.getSource()) .getParentItem()); } }); return; } else if (items.length > 0) { cacheItemChildren(item); } } public void cacheItemChildren(TreeItem item) { TreeItem[] items = item.getItems(); for (TreeItem treeItem : items) { treeItemToCVSNode.put(treeItem, findCVSTreeElement(treeItem)); // System.out.println("Added: " + treeItem); } } @SuppressWarnings("restriction") private void init() { this.tree.addTreeListener(getListener()); } private TreeListener getListener() { if (listener == null) { listener = new TreeListener() { public void treeCollapsed(TreeEvent event) { if (event.item instanceof TreeItem) { } } public void treeExpanded(TreeEvent event) { if (event.item != null && event.item instanceof TreeItem) { updateOnExpand((TreeItem) event.item); } } }; } return listener; } }