/*******************************************************************************
* Copyright (C) 2011, Markus Duft <markus.duft@salomon.at>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.egit.ui.internal.decorators;
import static org.eclipse.jgit.lib.Repository.stripWorkDir;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.runtime.IPath;
import org.eclipse.egit.core.internal.indexdiff.IndexDiffData;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.internal.resources.ResourceStateFactory;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.ui.IWorkingSet;
/**
* Represents a decoratable resource mapping (i.e. a group of resources).
*/
public class DecoratableResourceMapping extends DecoratableResource {
private static final String MULTIPLE = "*"; //$NON-NLS-1$
/**
* Denotes the type of decoratable resource, used by the decoration helper.
*/
public static final int RESOURCE_MAPPING = 0x10;
/**
* Denotes the type of decoratable resource, used by the decoration helper.
*/
public static final int WORKING_SET = 0x9999;
/**
* Stores the actual mapping we are currently decorating.
*/
private ResourceMapping mapping;
/**
* Creates a decoratable resource mapping (used for e.g. working sets)
*
* @param mapping the resource mapping to decorate
* @throws IOException
*/
public DecoratableResourceMapping(ResourceMapping mapping) throws IOException {
super(null); // no resource ...
this.mapping = mapping;
IProject[] projects = mapping.getProjects();
if(projects == null || projects.length == 0)
return;
// collect repositories to allow decoration of mappings (bug 369969)
Set<Repository> repositories = new HashSet<>(projects.length);
// we could use DecoratableResourceAdapter for each project, but that would be too much overhead,
// as we need only very little information at all...
for(IProject prj : projects) {
RepositoryMapping repoMapping = RepositoryMapping.getMapping(prj);
if(repoMapping == null)
continue;
IndexDiffData diffData = ResourceStateFactory.getInstance()
.getIndexDiffDataOrNull(prj);
if(diffData == null)
continue;
// at least one contained resource is tracked for sure here.
setTracked(true);
Repository repository = repoMapping.getRepository();
String repoRelative = makeRepoRelative(repository, prj);
if (repoRelative == null) {
continue;
}
repoRelative += "/"; //$NON-NLS-1$
Set<String> modified = diffData.getModified();
Set<String> conflicting = diffData.getConflicting();
// attention - never reset these to false (so don't use the return value of the methods!)
if(containsPrefix(modified, repoRelative))
setDirty(true);
if(containsPrefix(conflicting, repoRelative))
setConflicts(true);
// collect repository
repositories.add(repository);
}
// collect repository info for decoration (bug 369969)
if(repositories.size() == 1) {
// single repo, single branch --> [repo branch]
Repository repository = repositories.iterator().next();
repositoryName = DecoratableResourceHelper
.getRepositoryName(repository);
branch = DecoratableResourceHelper.getShortBranch(repository);
branchStatus = DecoratableResourceHelper
.getBranchStatus(repository);
} else if(repositories.size() > 1) {
// collect branch names but skip branch status (doesn't make sense)
Set<String> branches = new HashSet<>(2);
for (Repository repository : repositories) {
branches.add(DecoratableResourceHelper
.getShortBranch(repository));
if (branches.size() > 1)
break;
}
// multiple repos, one branch --> [* branch]
if (branches.size() == 1) {
repositoryName = MULTIPLE;
branch = branches.iterator().next();
}
// we set nothing in the following case:
// multiple repos, multiple branches
}
}
@Override
public int getType() {
if (mapping.getModelObject() instanceof IWorkingSet)
return WORKING_SET;
return RESOURCE_MAPPING;
}
@Override
public String getName() {
// TODO: check whether something other than a WorkingSet can
// appear here, and calculate a proper name for it.
if(mapping.getModelObject() instanceof IWorkingSet) {
IWorkingSet ws = (IWorkingSet)mapping.getModelObject();
return ws.getLabel();
}
return "<unknown>"; //$NON-NLS-1$
}
@Nullable
private String makeRepoRelative(Repository repository, IResource res) {
if (repository.isBare()) {
return null;
}
IPath location = res.getLocation();
if (location == null) {
return null;
}
return stripWorkDir(repository.getWorkTree(), location.toFile());
}
private boolean containsPrefix(Set<String> collection, String prefix) {
// when prefix is empty we are handling repository root, therefore we
// should return true whenever collection isn't empty
if (prefix.length() == 1 && !collection.isEmpty())
return true;
for (String path : collection)
if (path.startsWith(prefix))
return true;
return false;
}
}