Update org.apache.commons:commons-compress to 1.25.0
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / RepositoryStateCache.java
blob669fa10f74f26b7f372b2b2242527b6357e68dbd
1 /*******************************************************************************
2 * Copyright (C) 2019 Thomas Wolf <thomas.wolf@paranor.ch>
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11 package org.eclipse.egit.ui.internal;
13 import java.io.File;
14 import java.io.IOException;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
18 import org.eclipse.egit.core.UnitOfWork;
19 import org.eclipse.egit.ui.Activator;
20 import org.eclipse.jgit.annotations.NonNull;
21 import org.eclipse.jgit.lib.Constants;
22 import org.eclipse.jgit.lib.ObjectId;
23 import org.eclipse.jgit.lib.Ref;
24 import org.eclipse.jgit.lib.Repository;
25 import org.eclipse.jgit.lib.RepositoryState;
26 import org.eclipse.jgit.lib.StoredConfig;
27 import org.eclipse.jgit.revwalk.RevCommit;
28 import org.eclipse.jgit.revwalk.RevWalk;
30 /**
31 * A cache of some state of repositories. Concrete subclasses are responsible
32 * for clearing this cache in response to some event.
34 public abstract class RepositoryStateCache {
36 private enum RepositoryItem {
37 CONFIG, HEAD, HEAD_REF, HEAD_COMMIT, FULL_BRANCH_NAME, STATE
40 /** "null" marker in the maps. */
41 private static final Object NOTHING = new Object();
43 private final Map<File, Map<RepositoryItem, Object>> cache = new ConcurrentHashMap<>();
45 /**
46 * Initializes the {@link RepositoryStateCache} and makes it listen to changes
47 * that may affect the cache.
49 public abstract void initialize();
51 /**
52 * Disposes the {@link RepositoryStateCache}.
54 public void dispose() {
55 clear();
58 /**
59 * Completely clears the cache.
61 public void clear() {
62 cache.clear();
65 /**
66 * Clears all cached entries for the given {@link Repository}.
68 * @param repository
69 * to remove cached items of
71 public void clear(Repository repository) {
72 cache.remove(repository.getDirectory());
75 private Map<RepositoryItem, Object> getItems(Repository repository) {
76 return cache.computeIfAbsent(repository.getDirectory(),
77 gitDir -> new ConcurrentHashMap<>());
80 /**
81 * Retrieves the given repository's {@link StoredConfig}.
83 * @param repository
84 * @return the {@link StoredConfig} of the repository
86 public StoredConfig getConfig(Repository repository) {
87 Object value = getItems(repository).computeIfAbsent(
88 RepositoryItem.CONFIG, key -> repository.getConfig());
89 return (StoredConfig) value;
92 private ObjectId getHead(Repository repository,
93 String[] fullName, Ref[] ref) {
94 return UnitOfWork.get(repository, () -> {
95 ObjectId head = ObjectId.zeroId();
96 String name = null;
97 Ref r = null;
98 try {
99 r = repository.exactRef(Constants.HEAD);
100 } catch (IOException e) {
101 Activator.logError(e.getLocalizedMessage(), e);
103 ref[0] = r;
104 if (r != null) {
105 if (r.isSymbolic()) {
106 name = r.getTarget().getName();
108 head = r.getObjectId();
109 if (head != null) {
110 if (name == null) {
111 name = head.name();
113 } else {
114 head = ObjectId.zeroId();
117 fullName[0] = name != null ? name : ""; //$NON-NLS-1$
118 return head;
123 * Retrieves the {@link ObjectId} of the current HEAD.
125 * @param repository
126 * @return ObjectId of HEAD, or {@code null} if none
128 public ObjectId getHead(Repository repository) {
129 if (repository == null) {
130 return null;
132 Map<RepositoryItem, Object> items = getItems(repository);
133 Object value = items.get(RepositoryItem.HEAD);
134 if (value == null) {
135 String[] fullName = { null };
136 Ref[] headRef = { null };
137 value = items.computeIfAbsent(RepositoryItem.HEAD,
138 key -> getHead(repository, fullName, headRef));
139 items.computeIfAbsent(RepositoryItem.FULL_BRANCH_NAME,
140 key -> fullName[0]);
141 items.computeIfAbsent(RepositoryItem.HEAD_REF,
142 key -> headRef[0] == null ? NOTHING : headRef[0]);
144 ObjectId head = (ObjectId) value;
145 if (head == null || head.equals(ObjectId.zeroId())) {
146 return null;
148 return head;
153 * Retrieves the current HEAD ref.
155 * @param repository
156 * @return the HEAD ref, or {@code null} if none
158 public Ref getHeadRef(Repository repository) {
159 if (repository == null) {
160 return null;
162 Map<RepositoryItem, Object> items = getItems(repository);
163 Object value = items.get(RepositoryItem.HEAD_REF);
164 if (value == null) {
165 String[] fullName = { null };
166 Ref[] headRef = { null };
167 items.computeIfAbsent(RepositoryItem.HEAD,
168 key -> getHead(repository, fullName, headRef));
169 items.computeIfAbsent(RepositoryItem.FULL_BRANCH_NAME,
170 key -> fullName[0]);
171 value = items.computeIfAbsent(RepositoryItem.HEAD_REF,
172 key -> headRef[0] == null ? NOTHING : headRef[0]);
174 if (value == null || value == NOTHING) {
175 return null;
177 return (Ref) value;
181 * Retrieves the current HEAD commit of the repository.
183 * @param repository
184 * @return the commit, or {@code null} if none could be determined
186 public RevCommit getHeadCommit(Repository repository) {
187 if (repository == null) {
188 return null;
190 Map<RepositoryItem, Object> items = getItems(repository);
191 Object value = items.get(RepositoryItem.HEAD_COMMIT);
192 if (value == null) {
193 ObjectId headId = getHead(repository);
194 if (headId != null) {
195 try (RevWalk w = new RevWalk(repository)) {
196 RevCommit commit = w.parseCommit(headId);
197 items.put(RepositoryItem.HEAD_COMMIT, commit);
198 return commit;
199 } catch (IOException e) {
200 // Ignore here
203 items.put(RepositoryItem.HEAD_COMMIT, NOTHING);
204 return null;
205 } else if (value == NOTHING) {
206 return null;
208 return (RevCommit) value;
212 * Retrieves the full name of the current branch.
214 * @param repository
215 * @return the full branch name
217 public String getFullBranchName(Repository repository) {
218 if (repository == null) {
219 return null;
221 Map<RepositoryItem, Object> items = getItems(repository);
222 Object fullBranchName = items.get(RepositoryItem.FULL_BRANCH_NAME);
223 if (fullBranchName == null) {
224 String[] fullName = { null };
225 Ref[] headRef = { null };
226 items.computeIfAbsent(RepositoryItem.HEAD,
227 key -> getHead(repository, fullName, headRef));
228 fullBranchName = items.computeIfAbsent(
229 RepositoryItem.FULL_BRANCH_NAME, key -> fullName[0]);
230 items.computeIfAbsent(RepositoryItem.HEAD_REF,
231 key -> headRef[0] == null ? NOTHING : headRef[0]);
233 String name = (String) fullBranchName;
234 if (name == null || name.isEmpty()) {
235 return null;
237 return name;
241 * Retrieves the repository state.
243 * @param repository
244 * @return the {@link RepositoryState}
246 public @NonNull RepositoryState getRepositoryState(Repository repository) {
247 RepositoryState state = UnitOfWork.get(repository, () -> {
248 Object value = getItems(repository).computeIfAbsent(
249 RepositoryItem.STATE,
250 key -> repository.getRepositoryState());
251 return (RepositoryState) value;
253 assert state != null; // Keep the compiler happy.
254 return state;