blob: 992654248e8338062817037450c51642ef50f91a [file] [log] [blame]
package com.intellij.dvcs.repo;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author Nadya Zabrodina
*/
public abstract class AbstractRepositoryManager<T extends Repository> extends AbstractProjectComponent
implements Disposable, RepositoryManager<T>, VcsListener {
private static final Logger LOG = Logger.getInstance(RepositoryManager.class);
@NotNull private final ProjectLevelVcsManager myVcsManager;
@NotNull private final AbstractVcs myVcs;
@NotNull private final String myRepoDirName;
@NotNull protected final Map<VirtualFile, T> myRepositories = new HashMap<VirtualFile, T>();
@NotNull protected final ReentrantReadWriteLock REPO_LOCK = new ReentrantReadWriteLock();
@NotNull private final CountDownLatch myInitializationWaiter = new CountDownLatch(1);
protected AbstractRepositoryManager(@NotNull Project project, @NotNull ProjectLevelVcsManager vcsManager, @NotNull AbstractVcs vcs,
@NotNull String repoDirName) {
super(project);
myVcsManager = vcsManager;
myVcs = vcs;
myRepoDirName = repoDirName;
}
@Override
public void initComponent() {
Disposer.register(myProject, this);
myProject.getMessageBus().connect().subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, this);
}
@Override
public void dispose() {
try {
REPO_LOCK.writeLock().lock();
myRepositories.clear();
}
finally {
REPO_LOCK.writeLock().unlock();
}
}
@Override
public void directoryMappingChanged() {
updateRepositoriesCollection();
}
@Override
@Nullable
public T getRepositoryForRoot(@Nullable VirtualFile root) {
if (root == null) {
return null;
}
try {
REPO_LOCK.readLock().lock();
return myRepositories.get(root);
}
finally {
REPO_LOCK.readLock().unlock();
}
}
@Override
@Nullable
public T getRepositoryForFile(@NotNull VirtualFile file) {
final VcsRoot vcsRoot = myVcsManager.getVcsRootObjectFor(file);
return getRepositoryForVcsRoot(vcsRoot, file.getPath());
}
@Override
public T getRepositoryForFile(@NotNull FilePath file) {
final VcsRoot vcsRoot = myVcsManager.getVcsRootObjectFor(file);
return getRepositoryForVcsRoot(vcsRoot, file.getPath());
}
@Nullable
private T getRepositoryForVcsRoot(@Nullable VcsRoot vcsRoot, @NotNull String filePath) {
if (vcsRoot == null) {
return null;
}
final AbstractVcs vcs = vcsRoot.getVcs();
if (!myVcs.equals(vcs)) {
if (vcs != null) {
LOG.debug(String.format("getRepositoryForFile returned non-(%s) root for file %s", myVcs.getDisplayName(), filePath));
}
return null;
}
return getRepositoryForRoot(vcsRoot.getPath());
}
@Override
@NotNull
public List<T> getRepositories() {
try {
REPO_LOCK.readLock().lock();
return RepositoryUtil.sortRepositories(myRepositories.values());
}
finally {
REPO_LOCK.readLock().unlock();
}
}
@Override
public boolean moreThanOneRoot() {
return myRepositories.size() > 1;
}
@Override
public void updateRepository(@Nullable VirtualFile root) {
T repo = getRepositoryForRoot(root);
if (repo != null) {
repo.update();
}
}
@Override
public void updateAllRepositories() {
Map<VirtualFile, T> repositories;
try {
REPO_LOCK.readLock().lock();
repositories = new HashMap<VirtualFile, T>(myRepositories);
}
finally {
REPO_LOCK.readLock().unlock();
}
for (VirtualFile root : repositories.keySet()) {
updateRepository(root);
}
}
// note: we are not calling this method during the project startup - it is called anyway by f.e the GitRootTracker
private void updateRepositoriesCollection() {
Map<VirtualFile, T> repositories;
try {
REPO_LOCK.readLock().lock();
repositories = new HashMap<VirtualFile, T>(myRepositories);
}
finally {
REPO_LOCK.readLock().unlock();
}
final VirtualFile[] roots = myVcsManager.getRootsUnderVcs(myVcs);
// remove repositories that are not in the roots anymore
for (Iterator<Map.Entry<VirtualFile, T>> iterator = repositories.entrySet().iterator(); iterator.hasNext(); ) {
if (!ArrayUtil.contains(iterator.next().getValue().getRoot(), roots)) {
iterator.remove();
}
}
// add Repositories for all roots that don't have correspondent appropriate Git or Hg Repositories yet.
for (VirtualFile root : roots) {
if (!repositories.containsKey(root)) {
if (isRootValid(root)) {
try {
T repository = createRepository(root);
repositories.put(root, repository);
}
catch (RepoStateException e) {
LOG.error("Couldn't initialize Repository in " + root.getPresentableUrl(), e);
}
}
else {
LOG.info("Invalid vcs root: " + root);
}
}
}
REPO_LOCK.writeLock().lock();
try {
myRepositories.clear();
myRepositories.putAll(repositories);
myInitializationWaiter.countDown();
}
finally {
REPO_LOCK.writeLock().unlock();
}
}
private boolean isRootValid(@NotNull VirtualFile root) {
VirtualFile vcsDir = root.findChild(myRepoDirName);
return vcsDir != null && vcsDir.exists();
}
@NotNull
protected abstract T createRepository(@NotNull VirtualFile root);
@Override
@NotNull
public String toString() {
return "RepositoryManager{myRepositories: " + myRepositories + '}';
}
@Override
public void waitUntilInitialized() {
try {
myInitializationWaiter.await();
}
catch (InterruptedException e) {
LOG.error(e);
}
}
}