Update org.apache.commons:commons-compress to 1.25.0
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / branch / BranchProjectTracker.java
blob7d00a3687050be60805c28a0774994971a79dc87
1 /******************************************************************************
2 * Copyright (c) 2012 GitHub Inc.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License 2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-2.0/
8 * SPDX-License-Identifier: EPL-2.0
10 * Contributors:
11 * Kevin Sawicki (GitHub Inc.) - initial API and implementation
12 *****************************************************************************/
13 package org.eclipse.egit.ui.internal.branch;
15 import java.io.File;
16 import java.io.IOException;
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.LinkedHashSet;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.Set;
24 import java.util.stream.Stream;
26 import org.eclipse.core.resources.IProject;
27 import org.eclipse.core.resources.IProjectDescription;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IPath;
30 import org.eclipse.core.runtime.IProgressMonitor;
31 import org.eclipse.egit.core.internal.util.ProjectUtil;
32 import org.eclipse.egit.core.internal.util.ResourceUtil;
33 import org.eclipse.egit.ui.Activator;
34 import org.eclipse.egit.ui.internal.clone.ProjectRecord;
35 import org.eclipse.egit.ui.internal.clone.ProjectUtils;
36 import org.eclipse.jgit.annotations.NonNull;
37 import org.eclipse.jgit.lib.Repository;
38 import org.eclipse.jgit.util.StringUtils;
40 /**
41 * Class to track which projects are imported for each branch.
42 * <p>
43 * A unique preference is set for each repository/branch combination that is
44 * persisted that includes information to be able to restore projects when the
45 * branch is later checked out.
46 * </p>
47 * <p>
48 * The workflow is as follows:
49 * </p>
50 * <ol>
51 * <li>Call {@link #snapshot()} to get the current projects for the currently
52 * checked out branch</li>
53 * <li>Call {@link #save(ProjectTrackerMemento)} after the new branch has been
54 * successfully checked out with the memento returned from step 1</li>
55 * <li>Call {@link #restore(IProgressMonitor)} to restore the projects for the
56 * newly checked out branch</li>
57 * </ol>
59 class BranchProjectTracker {
61 private static final String REPO_ROOT = "/"; //$NON-NLS-1$
63 private final Repository[] repositories;
65 /**
66 * Create tracker for repositories
68 * @param repositories
70 public BranchProjectTracker(final Repository[] repositories) {
71 this.repositories = repositories;
74 /**
75 * Create tracker for repository
77 * @param repository
79 public BranchProjectTracker(final Repository repository) {
80 this.repositories = new Repository[] { repository };
83 private String getBranch(Repository repo) {
84 try {
85 return repo.getBranch();
86 } catch (IOException e) {
87 return null;
91 /**
92 * Snapshot the projects currently associated with the repository
93 * <p>
94 * The memento returned can be later passed to
95 * {@link #save(ProjectTrackerMemento)} to persist it
97 * @see #save(ProjectTrackerMemento)
98 * @return memento
100 public ProjectTrackerMemento snapshot() {
101 ProjectTrackerMemento memento = new ProjectTrackerMemento();
102 Stream.of(repositories).map(this::takeSnapshot).filter(Objects::nonNull)
103 .forEach(memento::addSnapshot);
104 return memento;
107 private ProjectTrackerPreferenceSnapshot takeSnapshot(Repository repo) {
108 String branch = getBranch(repo);
109 if (StringUtils.isEmptyOrNull(branch)) {
110 return null;
112 List<String> projectPaths = getAssociatedProjectsPaths(repo);
113 if (projectPaths.isEmpty()) {
114 return null;
116 return new ProjectTrackerPreferenceSnapshot(repo, branch,
117 projectPaths);
120 @NonNull
121 private List<String> getAssociatedProjectsPaths(Repository repo) {
122 IProject[] projects = getValidOpenProjects(repo);
123 if (projects == null) {
124 return Collections.emptyList();
126 List<String> projectPaths = new ArrayList<>();
128 final String workDir = repo.getWorkTree().getAbsolutePath();
129 for (IProject project : projects) {
130 IPath path = project.getLocation();
131 if (path == null) {
132 continue;
134 // Only remember mapped projects
135 if (!ResourceUtil.isSharedWithGit(project)) {
136 continue;
138 String fullPath = path.toOSString();
139 if (fullPath.startsWith(workDir)) {
140 String relative = fullPath.substring(workDir.length());
141 if (relative.length() == 0) {
142 relative = REPO_ROOT;
144 projectPaths.add(relative);
147 return projectPaths;
150 private IProject[] getValidOpenProjects(Repository repo) {
151 try {
152 return ProjectUtil.getValidOpenProjects(repo);
153 } catch (CoreException e) {
154 return null;
159 * Associate projects with branch. The specified memento must the one
160 * previously returned from a call to {@link #snapshot()}.
162 * @see #snapshot()
163 * @param snapshot
164 * @return this tracker
166 public BranchProjectTracker save(ProjectTrackerMemento snapshot) {
167 snapshot.getSnapshots().stream()
168 .forEach(BranchProjectTracker::savePreference);
169 return this;
172 private static void savePreference(ProjectTrackerPreferenceSnapshot snapshot) {
173 Repository repo = snapshot.getRepository();
174 String branch = snapshot.getBranch();
175 List<String> projects = snapshot.getAssociatedProjects();
176 ProjectTrackerPreferenceHelper.saveToPreferences(repo, branch,
177 projects);
181 * Restore projects associated with the currently checked out branch to the
182 * workspace
184 * @param monitor
186 public void restore(final IProgressMonitor monitor) {
187 for (Repository repo : repositories) {
188 String branch = getBranch(repo);
189 if (branch != null) {
190 restore(repo, branch, monitor);
196 * Restore projects associated with the given branch to the workspace
198 * @param repo
199 * @param branch
200 * @param monitor
202 public void restore(Repository repo, final String branch,
203 final IProgressMonitor monitor) {
204 List<String> paths = ProjectTrackerPreferenceHelper
205 .restoreFromPreferences(repo, branch);
206 if (paths.isEmpty()) {
207 return;
209 Set<ProjectRecord> records = new LinkedHashSet<>();
210 File parent = repo.getWorkTree();
211 for (String path : paths) {
212 File root;
213 if (!REPO_ROOT.equals(path)) {
214 root = new File(parent, path);
215 } else {
216 root = parent;
218 if (!root.isDirectory()) {
219 continue;
221 File projectDescription = new File(root,
222 IProjectDescription.DESCRIPTION_FILE_NAME);
223 if (!projectDescription.isFile()) {
224 continue;
226 ProjectRecord record = new ProjectRecord(projectDescription);
227 if (record.getProjectDescription() == null) {
228 continue;
230 records.add(record);
232 if (records.isEmpty()) {
233 return;
235 try {
236 ProjectUtils.createProjects(records, true, null, monitor);
237 } catch (InvocationTargetException e) {
238 Activator
239 .logError("Error restoring branch-project associations", e); //$NON-NLS-1$
240 } catch (InterruptedException ignored) {
241 // Ignored