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
;
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
;
25 import java
.util
.Properties
;
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
;
47 * This class keeps information about how a project is mapped to
50 public class GitProjectData
{
51 private static final Map
<IProject
, GitProjectData
> projectDataCache
= new HashMap
<IProject
, GitProjectData
>();
53 private static final Map
<File
, WeakReference
> repositoryCache
= new HashMap
<File
, WeakReference
>();
55 private static Set
<RepositoryChangeListener
> repositoryChangeListeners
= new HashSet
<RepositoryChangeListener
>();
57 @SuppressWarnings("synthetic-access")
58 private static final IResourceChangeListener rcl
= new RCL();
60 private static class RCL
implements IResourceChangeListener
{
61 @SuppressWarnings("synthetic-access")
62 public void resourceChanged(final IResourceChangeEvent event
) {
63 switch (event
.getType()) {
64 case IResourceChangeEvent
.PRE_CLOSE
:
65 uncache((IProject
) event
.getResource());
67 case IResourceChangeEvent
.PRE_DELETE
:
68 delete((IProject
) event
.getResource());
76 private static QualifiedName MAPPING_KEY
= new QualifiedName(
77 GitProjectData
.class.getName(), "RepositoryMapping");
80 * Start listening for resource changes.
82 * @param includeChange true to listen to content changes
84 public static void attachToWorkspace(final boolean includeChange
) {
85 trace("attachToWorkspace - addResourceChangeListener");
86 ResourcesPlugin
.getWorkspace().addResourceChangeListener(
88 (includeChange ? IResourceChangeEvent
.POST_CHANGE
: 0)
89 | IResourceChangeEvent
.PRE_CLOSE
90 | IResourceChangeEvent
.PRE_DELETE
);
94 * Stop listening to resource changes
96 public static void detachFromWorkspace() {
97 trace("detachFromWorkspace - removeResourceChangeListener");
98 ResourcesPlugin
.getWorkspace().removeResourceChangeListener(rcl
);
102 * Register a new listener for repository modification events.
104 * This is a no-op if <code>objectThatCares</code> has already been
108 * @param objectThatCares
109 * the new listener to register. Must not be null.
111 public static synchronized void addRepositoryChangeListener(
112 final RepositoryChangeListener objectThatCares
) {
113 if (objectThatCares
== null)
114 throw new NullPointerException();
115 repositoryChangeListeners
.add(objectThatCares
);
119 * Remove a registered {@link RepositoryChangeListener}
121 * @param objectThatCares
122 * The listener to remove
124 public static synchronized void removeRepositoryChangeListener(
125 final RepositoryChangeListener objectThatCares
) {
126 repositoryChangeListeners
.remove(objectThatCares
);
130 * Notify registered {@link RepositoryChangeListener}s of a change.
133 * the repository which has had changes occur within it.
135 static void fireRepositoryChanged(final RepositoryMapping which
) {
136 for (RepositoryChangeListener listener
: getRepositoryChangeListeners())
137 listener
.repositoryChanged(which
);
141 * Get a copy of the current set of repository change listeners
143 * The array has no references, so is safe for iteration and modification
145 * @return a copy of the current repository change listeners
147 private static synchronized RepositoryChangeListener
[] getRepositoryChangeListeners() {
148 return repositoryChangeListeners
149 .toArray(new RepositoryChangeListener
[repositoryChangeListeners
155 * @return {@link GitProjectData} for the specified project
157 public synchronized static GitProjectData
get(final IProject p
) {
159 GitProjectData d
= lookup(p
);
161 && RepositoryProvider
.getProvider(p
) instanceof GitProvider
) {
162 d
= new GitProjectData(p
).load();
166 } catch (IOException err
) {
167 Activator
.logError(CoreText
.GitProjectData_missing
, err
);
173 * Drop the Eclipse project from our association of projects/repositories
175 * @param p Eclipse project
177 public static void delete(final IProject p
) {
178 trace("delete(" + p
.getName() + ")");
179 GitProjectData d
= lookup(p
);
182 d
= new GitProjectData(p
).load();
183 } catch (IOException ioe
) {
184 d
= new GitProjectData(p
);
190 static void trace(final String m
) {
191 Activator
.trace("(GitProjectData) " + m
);
194 private synchronized static void cache(final IProject p
,
195 final GitProjectData d
) {
196 projectDataCache
.put(p
, d
);
199 private synchronized static void uncache(final IProject p
) {
200 if (projectDataCache
.remove(p
) != null) {
201 trace("uncacheDataFor(" + p
.getName() + ")");
205 private synchronized static GitProjectData
lookup(final IProject p
) {
206 return projectDataCache
.get(p
);
209 private synchronized static Repository
lookupRepository(final File gitDir
)
211 final Iterator i
= repositoryCache
.entrySet().iterator();
212 while (i
.hasNext()) {
213 final Map
.Entry e
= (Map
.Entry
) i
.next();
214 if (((Reference
) e
.getValue()).get() == null) {
219 final Reference r
= repositoryCache
.get(gitDir
);
220 Repository d
= r
!= null ?
(Repository
) r
.get() : null;
222 d
= new Repository(gitDir
);
223 repositoryCache
.put(gitDir
, new WeakReference
<Repository
>(d
));
229 * Update the settings for the global window cache of the workspace.
231 public static void reconfigureWindowCache() {
232 Preferences p
= Activator
.getDefault().getPluginPreferences();
233 int wLimit
= p
.getInt(GitCorePreferences
.core_packedGitLimit
);
234 int wSize
= p
.getInt(GitCorePreferences
.core_packedGitWindowSize
);
235 boolean mmap
= p
.getBoolean(GitCorePreferences
.core_packedGitMMAP
);
236 int dbLimit
= p
.getInt(GitCorePreferences
.core_deltaBaseCacheLimit
);
237 WindowCache
.reconfigure(wLimit
, wSize
, mmap
, dbLimit
);
240 private final IProject project
;
242 private final Collection
<RepositoryMapping
> mappings
= new ArrayList
<RepositoryMapping
>();
244 private final Set
<IResource
> protectedResources
= new HashSet
<IResource
>();
247 * Construct a {@link GitProjectData} for the mapping
250 * @param p Eclipse project
252 public GitProjectData(final IProject p
) {
257 * @return the Eclipse project mapped through this resource.
259 public IProject
getProject() {
264 * TODO: is this right?
268 public void setRepositoryMappings(final Collection
<RepositoryMapping
> newMappings
) {
270 mappings
.addAll(newMappings
);
275 * Hide our private parts from the navigators other browsers.
277 * @throws CoreException
279 public void markTeamPrivateResources() throws CoreException
{
280 for (final Object rmObj
: mappings
) {
281 final RepositoryMapping rm
= (RepositoryMapping
)rmObj
;
282 final IContainer c
= rm
.getContainer();
284 continue; // Not fully mapped yet?
286 final IResource dotGit
= c
.findMember(".git");
287 if (dotGit
!= null) {
289 final Repository r
= rm
.getRepository();
290 final File dotGitDir
= dotGit
.getLocation().toFile()
292 if (dotGitDir
.equals(r
.getDirectory())) {
293 trace("teamPrivate " + dotGit
);
294 dotGit
.setTeamPrivateMember(true);
296 } catch (IOException err
) {
297 throw Activator
.error(CoreText
.Error_CanonicalFile
, err
);
305 * @return true if a resource is protected in this repository
307 public boolean isProtected(final IResource f
) {
308 return protectedResources
.contains(f
);
312 * @param r any workbench resource contained within this project.
313 * @return the mapping for the specified project
315 public RepositoryMapping
getRepositoryMapping(IResource r
) {
317 for (; r
!= null; r
= r
.getParent()) {
318 final RepositoryMapping m
;
320 if (!r
.isAccessible())
322 m
= (RepositoryMapping
) r
.getSessionProperty(MAPPING_KEY
);
326 } catch (CoreException err
) {
327 Activator
.logError("Failed finding RepositoryMapping", err
);
332 private void delete() {
333 final File dir
= propertyFile().getParentFile();
334 final File
[] todel
= dir
.listFiles();
336 for (int k
= 0; k
< todel
.length
; k
++) {
337 if (todel
[k
].isFile()) {
343 trace("deleteDataFor(" + getProject().getName() + ")");
344 uncache(getProject());
348 * Store information about the repository connection in the workspace
350 * @throws CoreException
352 public void store() throws CoreException
{
353 final File dat
= propertyFile();
358 trace("save " + dat
);
359 tmp
= File
.createTempFile("gpd_", ".prop", dat
.getParentFile());
360 final FileOutputStream o
= new FileOutputStream(tmp
);
362 final Properties p
= new Properties();
363 final Iterator i
= mappings
.iterator();
364 while (i
.hasNext()) {
365 ((RepositoryMapping
) i
.next()).store(p
);
367 p
.store(o
, "GitProjectData");
375 } catch (IOException ioe
) {
376 throw Activator
.error(NLS
.bind(CoreText
.GitProjectData_saveFailed
,
381 if (!tmp
.renameTo(dat
)) {
383 throw Activator
.error(NLS
.bind(CoreText
.GitProjectData_saveFailed
,
388 private File
propertyFile() {
389 return new File(getProject()
390 .getWorkingLocation(Activator
.getPluginId()).toFile(),
391 "GitProjectData.properties");
394 private GitProjectData
load() throws IOException
{
395 final File dat
= propertyFile();
396 trace("load " + dat
);
398 final FileInputStream o
= new FileInputStream(dat
);
400 final Properties p
= new Properties();
404 final Iterator keyItr
= p
.keySet().iterator();
405 while (keyItr
.hasNext()) {
406 final String key
= keyItr
.next().toString();
407 if (RepositoryMapping
.isInitialKey(key
)) {
408 mappings
.add(new RepositoryMapping(p
, key
));
419 private void remapAll() {
420 protectedResources
.clear();
421 final Iterator i
= mappings
.iterator();
422 while (i
.hasNext()) {
423 map((RepositoryMapping
) i
.next());
427 private void map(final RepositoryMapping m
) {
430 final IResource dotGit
;
434 r
= getProject().findMember(m
.getContainerPath());
435 if (r
instanceof IContainer
) {
438 c
= (IContainer
) r
.getAdapter(IContainer
.class);
442 Activator
.logError(CoreText
.GitProjectData_mappedResourceGone
,
443 new FileNotFoundException(m
.getContainerPath().toString()));
449 git
= c
.getLocation().append(m
.getGitDirPath()).toFile();
450 if (!git
.isDirectory() || !new File(git
, "config").isFile()) {
451 Activator
.logError(CoreText
.GitProjectData_mappedResourceGone
,
452 new FileNotFoundException(m
.getContainerPath().toString()));
458 m
.setRepository(lookupRepository(git
));
459 } catch (IOException ioe
) {
460 Activator
.logError(CoreText
.GitProjectData_mappedResourceGone
,
461 new FileNotFoundException(m
.getContainerPath().toString()));
466 m
.fireRepositoryChanged();
468 trace("map " + c
+ " -> " + m
.getRepository());
470 c
.setSessionProperty(MAPPING_KEY
, m
);
471 } catch (CoreException err
) {
472 Activator
.logError("Failed to cache RepositoryMapping", err
);
475 dotGit
= c
.findMember(".git");
476 if (dotGit
!= null && dotGit
.getLocation().toFile().equals(git
)) {
481 private void protect(IResource c
) {
482 while (c
!= null && !c
.equals(getProject())) {
483 trace("protect " + c
);
484 protectedResources
.add(c
);