Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 9590b5c8edc58a89381c189c73cab8398a131262 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*******************************************************************************
 * 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 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
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;
		}
	}
}

Back to the top