Remove System.out.println from RevWalkFilterTest
[jgit.git] / org.spearce.egit.core / src / org / spearce / egit / core / project / GitProjectData.java
blob31d54834c778347d7d689832bb4416131a8a88fe
1 /*******************************************************************************
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * Copyright (C) 2008, Google Inc.
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * See LICENSE for the full license text, also available.
9 *******************************************************************************/
10 package org.spearce.egit.core.project;
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.FileNotFoundException;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.lang.ref.Reference;
18 import java.lang.ref.WeakReference;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.Properties;
26 import java.util.Set;
28 import org.eclipse.core.resources.IContainer;
29 import org.eclipse.core.resources.IProject;
30 import org.eclipse.core.resources.IResource;
31 import org.eclipse.core.resources.IResourceChangeEvent;
32 import org.eclipse.core.resources.IResourceChangeListener;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.Preferences;
36 import org.eclipse.core.runtime.QualifiedName;
37 import org.eclipse.osgi.util.NLS;
38 import org.eclipse.team.core.RepositoryProvider;
39 import org.spearce.egit.core.Activator;
40 import org.spearce.egit.core.CoreText;
41 import org.spearce.egit.core.GitCorePreferences;
42 import org.spearce.egit.core.GitProvider;
43 import org.spearce.jgit.lib.Repository;
44 import org.spearce.jgit.lib.WindowCache;
45 import org.spearce.jgit.lib.WindowCacheConfig;
47 /**
48 * This class keeps information about how a project is mapped to
49 * a Git repository.
51 public class GitProjectData {
52 private static final Map<IProject, GitProjectData> projectDataCache = new HashMap<IProject, GitProjectData>();
54 private static final Map<File, WeakReference> repositoryCache = new HashMap<File, WeakReference>();
56 private static Set<RepositoryChangeListener> repositoryChangeListeners = new HashSet<RepositoryChangeListener>();
58 @SuppressWarnings("synthetic-access")
59 private static final IResourceChangeListener rcl = new RCL();
61 private static class RCL implements IResourceChangeListener {
62 @SuppressWarnings("synthetic-access")
63 public void resourceChanged(final IResourceChangeEvent event) {
64 switch (event.getType()) {
65 case IResourceChangeEvent.PRE_CLOSE:
66 uncache((IProject) event.getResource());
67 break;
68 case IResourceChangeEvent.PRE_DELETE:
69 delete((IProject) event.getResource());
70 break;
71 default:
72 break;
77 private static QualifiedName MAPPING_KEY = new QualifiedName(
78 GitProjectData.class.getName(), "RepositoryMapping");
80 /**
81 * Start listening for resource changes.
83 * @param includeChange true to listen to content changes
85 public static void attachToWorkspace(final boolean includeChange) {
86 trace("attachToWorkspace - addResourceChangeListener");
87 ResourcesPlugin.getWorkspace().addResourceChangeListener(
88 rcl,
89 (includeChange ? IResourceChangeEvent.POST_CHANGE : 0)
90 | IResourceChangeEvent.PRE_CLOSE
91 | IResourceChangeEvent.PRE_DELETE);
94 /**
95 * Stop listening to resource changes
97 public static void detachFromWorkspace() {
98 trace("detachFromWorkspace - removeResourceChangeListener");
99 ResourcesPlugin.getWorkspace().removeResourceChangeListener(rcl);
103 * Register a new listener for repository modification events.
104 * <p>
105 * This is a no-op if <code>objectThatCares</code> has already been
106 * registered.
107 * </p>
109 * @param objectThatCares
110 * the new listener to register. Must not be null.
112 public static synchronized void addRepositoryChangeListener(
113 final RepositoryChangeListener objectThatCares) {
114 if (objectThatCares == null)
115 throw new NullPointerException();
116 repositoryChangeListeners.add(objectThatCares);
120 * Remove a registered {@link RepositoryChangeListener}
122 * @param objectThatCares
123 * The listener to remove
125 public static synchronized void removeRepositoryChangeListener(
126 final RepositoryChangeListener objectThatCares) {
127 repositoryChangeListeners.remove(objectThatCares);
131 * Notify registered {@link RepositoryChangeListener}s of a change.
133 * @param which
134 * the repository which has had changes occur within it.
136 static void fireRepositoryChanged(final RepositoryMapping which) {
137 for (RepositoryChangeListener listener : getRepositoryChangeListeners())
138 listener.repositoryChanged(which);
142 * Get a copy of the current set of repository change listeners
143 * <p>
144 * The array has no references, so is safe for iteration and modification
146 * @return a copy of the current repository change listeners
148 private static synchronized RepositoryChangeListener[] getRepositoryChangeListeners() {
149 return repositoryChangeListeners
150 .toArray(new RepositoryChangeListener[repositoryChangeListeners
151 .size()]);
155 * @param p
156 * @return {@link GitProjectData} for the specified project
158 public synchronized static GitProjectData get(final IProject p) {
159 try {
160 GitProjectData d = lookup(p);
161 if (d == null
162 && RepositoryProvider.getProvider(p) instanceof GitProvider) {
163 d = new GitProjectData(p).load();
164 cache(p, d);
166 return d;
167 } catch (IOException err) {
168 Activator.logError(CoreText.GitProjectData_missing, err);
169 return null;
174 * Drop the Eclipse project from our association of projects/repositories
176 * @param p Eclipse project
178 public static void delete(final IProject p) {
179 trace("delete(" + p.getName() + ")");
180 GitProjectData d = lookup(p);
181 if (d == null) {
182 try {
183 d = new GitProjectData(p).load();
184 } catch (IOException ioe) {
185 d = new GitProjectData(p);
188 d.delete();
191 static void trace(final String m) {
192 Activator.trace("(GitProjectData) " + m);
195 private synchronized static void cache(final IProject p,
196 final GitProjectData d) {
197 projectDataCache.put(p, d);
200 private synchronized static void uncache(final IProject p) {
201 if (projectDataCache.remove(p) != null) {
202 trace("uncacheDataFor(" + p.getName() + ")");
206 private synchronized static GitProjectData lookup(final IProject p) {
207 return projectDataCache.get(p);
210 private synchronized static Repository lookupRepository(final File gitDir)
211 throws IOException {
212 final Iterator i = repositoryCache.entrySet().iterator();
213 while (i.hasNext()) {
214 final Map.Entry e = (Map.Entry) i.next();
215 if (((Reference) e.getValue()).get() == null) {
216 i.remove();
220 final Reference r = repositoryCache.get(gitDir);
221 Repository d = r != null ? (Repository) r.get() : null;
222 if (d == null) {
223 d = new Repository(gitDir);
224 repositoryCache.put(gitDir, new WeakReference<Repository>(d));
226 return d;
230 * Update the settings for the global window cache of the workspace.
232 public static void reconfigureWindowCache() {
233 final WindowCacheConfig c = new WindowCacheConfig();
234 Preferences p = Activator.getDefault().getPluginPreferences();
235 c.setPackedGitLimit(p.getInt(GitCorePreferences.core_packedGitLimit));
236 c.setPackedGitWindowSize(p.getInt(GitCorePreferences.core_packedGitWindowSize));
237 c.setPackedGitMMAP(p.getBoolean(GitCorePreferences.core_packedGitMMAP));
238 c.setDeltaBaseCacheLimit(p.getInt(GitCorePreferences.core_deltaBaseCacheLimit));
239 WindowCache.reconfigure(c);
242 private final IProject project;
244 private final Collection<RepositoryMapping> mappings = new ArrayList<RepositoryMapping>();
246 private final Set<IResource> protectedResources = new HashSet<IResource>();
249 * Construct a {@link GitProjectData} for the mapping
250 * of a project.
252 * @param p Eclipse project
254 public GitProjectData(final IProject p) {
255 project = p;
259 * @return the Eclipse project mapped through this resource.
261 public IProject getProject() {
262 return project;
266 * TODO: is this right?
268 * @param newMappings
270 public void setRepositoryMappings(final Collection<RepositoryMapping> newMappings) {
271 mappings.clear();
272 mappings.addAll(newMappings);
273 remapAll();
277 * Hide our private parts from the navigators other browsers.
279 * @throws CoreException
281 public void markTeamPrivateResources() throws CoreException {
282 for (final Object rmObj : mappings) {
283 final RepositoryMapping rm = (RepositoryMapping)rmObj;
284 final IContainer c = rm.getContainer();
285 if (c == null)
286 continue; // Not fully mapped yet?
288 final IResource dotGit = c.findMember(".git");
289 if (dotGit != null) {
290 try {
291 final Repository r = rm.getRepository();
292 final File dotGitDir = dotGit.getLocation().toFile()
293 .getCanonicalFile();
294 if (dotGitDir.equals(r.getDirectory())) {
295 trace("teamPrivate " + dotGit);
296 dotGit.setTeamPrivateMember(true);
298 } catch (IOException err) {
299 throw Activator.error(CoreText.Error_CanonicalFile, err);
306 * @param f
307 * @return true if a resource is protected in this repository
309 public boolean isProtected(final IResource f) {
310 return protectedResources.contains(f);
314 * @param r any workbench resource contained within this project.
315 * @return the mapping for the specified project
317 public RepositoryMapping getRepositoryMapping(IResource r) {
318 try {
319 for (; r != null; r = r.getParent()) {
320 final RepositoryMapping m;
322 if (!r.isAccessible())
323 continue;
324 m = (RepositoryMapping) r.getSessionProperty(MAPPING_KEY);
325 if (m != null)
326 return m;
328 } catch (CoreException err) {
329 Activator.logError("Failed finding RepositoryMapping", err);
331 return null;
334 private void delete() {
335 final File dir = propertyFile().getParentFile();
336 final File[] todel = dir.listFiles();
337 if (todel != null) {
338 for (int k = 0; k < todel.length; k++) {
339 if (todel[k].isFile()) {
340 todel[k].delete();
344 dir.delete();
345 trace("deleteDataFor(" + getProject().getName() + ")");
346 uncache(getProject());
350 * Store information about the repository connection in the workspace
352 * @throws CoreException
354 public void store() throws CoreException {
355 final File dat = propertyFile();
356 final File tmp;
357 boolean ok = false;
359 try {
360 trace("save " + dat);
361 tmp = File.createTempFile("gpd_", ".prop", dat.getParentFile());
362 final FileOutputStream o = new FileOutputStream(tmp);
363 try {
364 final Properties p = new Properties();
365 final Iterator i = mappings.iterator();
366 while (i.hasNext()) {
367 ((RepositoryMapping) i.next()).store(p);
369 p.store(o, "GitProjectData");
370 ok = true;
371 } finally {
372 o.close();
373 if (!ok) {
374 tmp.delete();
377 } catch (IOException ioe) {
378 throw Activator.error(NLS.bind(CoreText.GitProjectData_saveFailed,
379 dat), ioe);
382 dat.delete();
383 if (!tmp.renameTo(dat)) {
384 tmp.delete();
385 throw Activator.error(NLS.bind(CoreText.GitProjectData_saveFailed,
386 dat), null);
390 private File propertyFile() {
391 return new File(getProject()
392 .getWorkingLocation(Activator.getPluginId()).toFile(),
393 "GitProjectData.properties");
396 private GitProjectData load() throws IOException {
397 final File dat = propertyFile();
398 trace("load " + dat);
400 final FileInputStream o = new FileInputStream(dat);
401 try {
402 final Properties p = new Properties();
403 p.load(o);
405 mappings.clear();
406 final Iterator keyItr = p.keySet().iterator();
407 while (keyItr.hasNext()) {
408 final String key = keyItr.next().toString();
409 if (RepositoryMapping.isInitialKey(key)) {
410 mappings.add(new RepositoryMapping(p, key));
413 } finally {
414 o.close();
417 remapAll();
418 return this;
421 private void remapAll() {
422 protectedResources.clear();
423 final Iterator i = mappings.iterator();
424 while (i.hasNext()) {
425 map((RepositoryMapping) i.next());
429 private void map(final RepositoryMapping m) {
430 final IResource r;
431 final File git;
432 final IResource dotGit;
433 IContainer c = null;
435 m.clear();
436 r = getProject().findMember(m.getContainerPath());
437 if (r instanceof IContainer) {
438 c = (IContainer) r;
439 } else {
440 c = (IContainer) r.getAdapter(IContainer.class);
443 if (c == null) {
444 Activator.logError(CoreText.GitProjectData_mappedResourceGone,
445 new FileNotFoundException(m.getContainerPath().toString()));
446 m.clear();
447 return;
449 m.setContainer(c);
451 git = c.getLocation().append(m.getGitDirPath()).toFile();
452 if (!git.isDirectory() || !new File(git, "config").isFile()) {
453 Activator.logError(CoreText.GitProjectData_mappedResourceGone,
454 new FileNotFoundException(m.getContainerPath().toString()));
455 m.clear();
456 return;
459 try {
460 m.setRepository(lookupRepository(git));
461 } catch (IOException ioe) {
462 Activator.logError(CoreText.GitProjectData_mappedResourceGone,
463 new FileNotFoundException(m.getContainerPath().toString()));
464 m.clear();
465 return;
468 m.fireRepositoryChanged();
470 trace("map " + c + " -> " + m.getRepository());
471 try {
472 c.setSessionProperty(MAPPING_KEY, m);
473 } catch (CoreException err) {
474 Activator.logError("Failed to cache RepositoryMapping", err);
477 dotGit = c.findMember(".git");
478 if (dotGit != null && dotGit.getLocation().toFile().equals(git)) {
479 protect(dotGit);
483 private void protect(IResource c) {
484 while (c != null && !c.equals(getProject())) {
485 trace("protect " + c);
486 protectedResources.add(c);
487 c = c.getParent();