/*******************************************************************************
* Copyright (C) 2013 Robin Stocker <robin@nibor.org>
*
* 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.synchronize.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.egit.core.synchronize.GitCommitsModelCache.Change;
import org.eclipse.jgit.lib.Repository;
/**
* For building trees of directory and file nodes out of a flat list of changes.
*/
class TreeBuilder {
/**
* This interface enables creating the right instances of
* {@link GitModelBlob} for files.
*/
interface FileModelFactory {
/**
* Creates proper instance of {@link GitModelBlob} for file nodes
*
* @param parent
* parent object
* @param repo
* repository associated with file that will be created
* @param change
* change associated with file that will be created
* @param fullPath
* absolute path
* @return instance of {@link GitModelBlob}
*/
GitModelBlob createFileModel(GitModelObjectContainer parent,
Repository repo, Change change, IPath fullPath);
/**
* Distinguish working tree from changed/staged tree
*
* @return {@code true} when this tree is working tree, {@code false}
* when it is a cached tree
*/
boolean isWorkingTree();
}
/**
* Interface for creating the desired instances of {@link GitModelTree}.
*/
interface TreeModelFactory {
GitModelTree createTreeModel(GitModelObjectContainer parent,
IPath fullPath, int kind);
}
/**
*
* @param root
* the root node of the tree to build, which will become the
* parent of the first level of children
* @param repo
* @param changes
* @param fileFactory
* @param treeFactory
* @return the children of the root nodes
*/
public static GitModelObject[] build(final GitModelObjectContainer root,
final Repository repo, final Map<String, Change> changes,
final FileModelFactory fileFactory,
final TreeModelFactory treeFactory) {
if (changes == null || changes.isEmpty())
return new GitModelObject[] {};
final IPath rootPath = new Path(repo.getWorkTree()
.getAbsolutePath());
final List<GitModelObject> rootChildren = new ArrayList<>();
final Map<IPath, Node> nodes = new HashMap<>();
for (Map.Entry<String, Change> entry : changes.entrySet()) {
String repoRelativePath = entry.getKey();
Change change = entry.getValue();
GitModelObjectContainer parent = root;
List<GitModelObject> children = rootChildren;
IPath path = rootPath;
String[] segments = repoRelativePath.split("/"); //$NON-NLS-1$
for (int i = 0; i < segments.length; i++) {
path = path.append(segments[i]);
// Changes represent files, so the last segment is the file name
boolean fileNode = (i == segments.length - 1);
if (!fileNode) {
Node node = nodes.get(path);
if (node == null) {
GitModelTree tree = treeFactory.createTreeModel(parent,
path, change.getKind());
node = new Node(tree);
nodes.put(path, node);
children.add(tree);
}
parent = node.tree;
children = node.children;
} else {
GitModelBlob file = fileFactory.createFileModel(parent,
repo, change, path);
children.add(file);
}
}
}
for (Node object : nodes.values()) {
GitModelTree tree = object.tree;
tree.setChildren(object.children);
}
return rootChildren.toArray(new GitModelObject[rootChildren.size()]);
}
private static class Node {
private final GitModelTree tree;
private final List<GitModelObject> children = new ArrayList<>();
public Node(GitModelTree tree) {
this.tree = tree;
}
}
}