blob: d37c958be771a275c4609dfab033e721a64619aa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Sebastian Davids <sdavids@gmx.de> - bug 74959
* Maik Schreiber - bug 102461
* William Mitsuda (wmitsuda@gmail.com) - Bug 153879 [Wizards] configurable size of cvs commit comment history
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.repo;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import javax.xml.parsers.*;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.util.KnownRepositories;
import org.eclipse.team.internal.ccvs.ui.*;
import org.eclipse.team.internal.ccvs.ui.Policy;
import org.eclipse.ui.IWorkingSet;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* This class is repsible for maintaining the UI's list of known repositories,
* and a list of known tags within each of those repositories.
*
* It also provides a number of useful methods for assisting in repository operations.
*/
public class RepositoryManager {
// old state file
private static final String STATE_FILE = ".repositoryManagerState"; //$NON-NLS-1$
private static final int STATE_FILE_VERSION_1 = -1;
// new state file
private static final String REPOSITORIES_VIEW_FILE = "repositoriesView.xml"; //$NON-NLS-1$
private static final String COMMENT_HIST_FILE = "commitCommentHistory.xml"; //$NON-NLS-1$
private static final String COMMENT_TEMPLATES_FILE = "commentTemplates.xml"; //$NON-NLS-1$
static final String ELEMENT_COMMIT_COMMENT = "CommitComment"; //$NON-NLS-1$
static final String ELEMENT_COMMIT_HISTORY = "CommitComments"; //$NON-NLS-1$
static final String ELEMENT_COMMENT_TEMPLATES = "CommitCommentTemplates"; //$NON-NLS-1$
private Map<String, RepositoryRoot> repositoryRoots = new HashMap<>();
List<IRepositoryListener> listeners = new ArrayList<>();
// The previously remembered comment
static String[] previousComments = new String[0];
static String[] commentTemplates = new String[0];
public static boolean notifyRepoView = true;
// Cache of changed repository roots
private int notificationLevel = 0;
private Map<String, ICVSRepositoryLocation> changedRepositories = new HashMap<>();
public static final int DEFAULT_MAX_COMMENTS = 10;
private int maxComments = DEFAULT_MAX_COMMENTS;
public void setMaxComments(int maxComments) {
if (maxComments > 0) {
this.maxComments = maxComments;
if (maxComments < previousComments.length) {
String[] newComments = new String[maxComments];
System.arraycopy(previousComments, 0, newComments, 0, maxComments);
previousComments = newComments;
}
}
}
/**
* Answer an array of all known remote roots.
*/
public ICVSRepositoryLocation[] getKnownRepositoryLocations() {
return KnownRepositories.getInstance().getRepositories();
}
/**
* Method getRepositoryRoots.
* @param iCVSRepositoryLocations
* @return RepositoryRoot[]
*/
private RepositoryRoot[] getRepositoryRoots(ICVSRepositoryLocation[] locations) {
List<RepositoryRoot> roots = new ArrayList<>();
for (int i = 0; i < locations.length; i++) {
ICVSRepositoryLocation location = locations[i];
RepositoryRoot root = getRepositoryRootFor(location);
if (root != null)
roots.add(root);
}
return roots.toArray(new RepositoryRoot[roots.size()]);
}
public RepositoryRoot[] getKnownRepositoryRoots() {
return getRepositoryRoots(getKnownRepositoryLocations());
}
/**
* Get the list of known branch tags for a given remote root.
*/
public CVSTag[] getKnownTags(ICVSFolder project, int tagType) {
try {
CVSTag[] tags = getKnownTags(project);
Set<CVSTag> result = new HashSet<>();
for (int i = 0; i < tags.length; i++) {
CVSTag tag = tags[i];
if (tag.getType() == tagType)
result.add(tag);
}
return result.toArray(new CVSTag[result.size()]);
} catch(CVSException e) {
CVSUIPlugin.log(e);
return new CVSTag[0];
}
}
/**
* Get the list of known version tags for a given project.
*/
public CVSTag[] getKnownTags(ICVSRepositoryLocation location, int tagType) {
Set<CVSTag> result = new HashSet<>();
RepositoryRoot root = repositoryRoots.get(location.getLocation(false));
if (root != null) {
CVSTag[] tags = root.getAllKnownTags();
for (int i = 0; i < tags.length; i++) {
CVSTag tag = tags[i];
if (tag.getType() == tagType)
result.add(tag);
}
}
return result.toArray(new CVSTag[0]);
}
/**
* Method getKnownTags.
* @param repository
* @param set
* @param i
* @param monitor
* @return CVSTag[]
*/
public CVSTag[] getKnownTags(ICVSRepositoryLocation repository, IWorkingSet set, int tagType, IProgressMonitor monitor) throws CVSException {
if (set == null) {
return getKnownTags(repository, tagType);
}
ICVSRemoteResource[] folders = getFoldersForTag(repository, CVSTag.DEFAULT, monitor);
folders = filterResources(set, folders);
Set<CVSTag> tags = new HashSet<>();
for (int i = 0; i < folders.length; i++) {
ICVSRemoteFolder folder = (ICVSRemoteFolder)folders[i];
tags.addAll(Arrays.asList(getKnownTags(folder, tagType)));
}
return tags.toArray(new CVSTag[tags.size()]);
}
public CVSTag[] getKnownTags(ICVSFolder project) throws CVSException {
RepositoryRoot root = getRepositoryRootFor(project);
String remotePath = RepositoryRoot.getRemotePathFor(project);
return root.getAllKnownTags(remotePath);
}
/*
* XXX I hope this methos is not needed in this form
*/
public Map getKnownProjectsAndVersions(ICVSRepositoryLocation location) {
Map<String, Set<CVSTag>> knownTags = new HashMap<>();
RepositoryRoot root = getRepositoryRootFor(location);
String[] paths = root.getKnownRemotePaths();
for (int i = 0; i < paths.length; i++) {
String path = paths[i];
Set<CVSTag> result = new HashSet<>();
result.addAll(Arrays.asList(root.getAllKnownTags(path)));
knownTags.put(path, result);
}
return knownTags;
}
public ICVSRemoteResource[] getFoldersForTag(ICVSRepositoryLocation location, CVSTag tag, IProgressMonitor monitor) throws CVSException {
monitor = Policy.monitorFor(monitor);
try {
monitor.beginTask(NLS.bind(CVSUIMessages.RepositoryManager_fetchingRemoteFolders, new String[] { tag.getName() }), 100);
if (tag.getType() == CVSTag.HEAD) {
ICVSRemoteResource[] resources = location.members(tag, false, Policy.subMonitorFor(monitor, 60));
RepositoryRoot root = getRepositoryRootFor(location);
ICVSRemoteResource[] modules = root.getDefinedModules(tag, Policy.subMonitorFor(monitor, 40));
ICVSRemoteResource[] result = new ICVSRemoteResource[resources.length + modules.length];
System.arraycopy(resources, 0, result, 0, resources.length);
System.arraycopy(modules, 0, result, resources.length, modules.length);
return result;
}
if (tag.getType() == CVSTag.DATE) {
ICVSRemoteResource[] resources = location.members(tag, false, Policy.subMonitorFor(monitor, 60));
RepositoryRoot root = getRepositoryRootFor(location);
ICVSRemoteResource[] modules = root.getDefinedModules(tag, Policy.subMonitorFor(monitor, 40));
ICVSRemoteResource[] result = new ICVSRemoteResource[resources.length + modules.length];
System.arraycopy(resources, 0, result, 0, resources.length);
System.arraycopy(modules, 0, result, resources.length, modules.length);
return result;
}
Set<ICVSRemoteFolder> result = new HashSet<>();
// Get the tags for the location
RepositoryRoot root = getRepositoryRootFor(location);
String[] paths = root.getRemoteChildrenForTag(null, tag);
for (int i = 0; i < paths.length; i++) {
String path = paths[i];
ICVSRemoteFolder remote = root.getRemoteFolder(path, tag,
Policy.subMonitorFor(monitor, 100));
result.add(remote);
}
return result.toArray(new ICVSRemoteResource[result.size()]);
} finally {
monitor.done();
}
}
/**
* Returns a list of child resources for given folder that are known to
* contain given tag. If the return list is empty than given tag exists
* directly in given folder and its children should be retrieved directly
* from the repository.
*
* NOTE: Resources are cached only for tags of type CVSTag.Branch and
* CVSTag.Version. Other types of tags will always return empty list.
*
* @param location
* CVS repository location
* @param parentFolder
* folder to check tags for
* @param tag
* @param monitor
* @return a list of remote resources that are known to contain given tag or
* empty list if resources should be retrieved from the repository
* @throws CVSException
*/
public ICVSRemoteResource[] getCachedChildrenForTag(
ICVSRepositoryLocation location, ICVSRemoteFolder parentFolder,
CVSTag tag, IProgressMonitor monitor) throws CVSException {
if (tag == null || tag.getType() == CVSTag.HEAD
|| tag.getType() == CVSTag.DATE) {
// folders are kept in cache only for tags and versions
return new ICVSRemoteResource[0];
}
monitor = Policy.monitorFor(monitor);
Set<ICVSRemoteFolder> result = new HashSet<>();
RepositoryRoot root = getRepositoryRootFor(location);
// if remote folder is null return the subfolders of repository root
String[] paths = root.getRemoteChildrenForTag(
parentFolder == null ? null : RepositoryRoot
.getRemotePathFor(parentFolder), tag);
monitor.beginTask(NLS
.bind(CVSUIMessages.RemoteFolderElement_fetchingRemoteChildren,
new String[] { NLS.bind(
CVSUIMessages.RemoteFolderElement_nameAndTag,
new String[] { parentFolder.getName(),
tag.getName() }) }), 10 * paths.length);
try {
for (int i = 0; i < paths.length; i++) {
String path = paths[i];
ICVSRemoteFolder remote = root.getRemoteFolder(path, tag,
Policy.subMonitorFor(monitor, 10));
result.add(remote);
}
return result
.toArray(new ICVSRemoteResource[result.size()]);
} finally {
monitor.done();
}
}
/*
* Fetches tags from auto-refresh files if they exist. Then fetches tags from the user defined auto-refresh file
* list. The fetched tags are cached in the CVS ui plugin's tag cache.
*/
public CVSTag[] refreshDefinedTags(ICVSFolder folder, boolean recurse, boolean notify, IProgressMonitor monitor) throws TeamException {
RepositoryRoot root = getRepositoryRootFor(folder);
CVSTag[] tags = root.refreshDefinedTags(folder, recurse, monitor);
if (tags.length > 0 && notify)
broadcastRepositoryChange(root);
return tags;
}
/**
* A repository root has been added. Notify any listeners.
*/
public void rootAdded(ICVSRepositoryLocation root) {
Iterator it = listeners.iterator();
while (it.hasNext()) {
IRepositoryListener listener = (IRepositoryListener)it.next();
listener.repositoryAdded(root);
}
}
/**
* A repository root has been removed.
* Remove the tags defined for this root and notify any listeners
*/
public void rootRemoved(ICVSRepositoryLocation root) {
RepositoryRoot repoRoot = repositoryRoots.remove(root.getLocation(false));
if (repoRoot != null)
broadcastRepositoryChange(repoRoot);
}
/**
* Accept tags for any CVS resource. However, for the time being,
* the given version tags are added to the list of known tags for the
* remote ancestor of the resource that is a direct child of the remote root
*/
public void addTags(ICVSResource resource, CVSTag[] tags) throws CVSException {
RepositoryRoot root = getRepositoryRootFor(resource);
// XXX could be a file or folder
String remotePath = RepositoryRoot.getRemotePathFor(resource);
root.addTags(remotePath, tags);
broadcastRepositoryChange(root);
}
public void addDateTag(ICVSRepositoryLocation location, CVSTag tag) {
if(tag == null) return;
RepositoryRoot root = getRepositoryRootFor(location);
root.addDateTag(tag);
broadcastRepositoryChange(root);
}
public CVSTag[] getDateTags(ICVSRepositoryLocation location) {
RepositoryRoot root = getRepositoryRootFor(location);
return root.getDateTags();
}
public void removeDateTag(ICVSRepositoryLocation location, CVSTag tag){
RepositoryRoot root = getRepositoryRootFor(location);
root.removeDateTag(tag);
broadcastRepositoryChange(root);
}
public void setAutoRefreshFiles(ICVSFolder project, String[] filePaths) throws CVSException {
RepositoryRoot root = getRepositoryRootFor(project);
String remotePath = RepositoryRoot.getRemotePathFor(project);
root.setAutoRefreshFiles(remotePath, filePaths);
}
public String[] getAutoRefreshFiles(ICVSFolder project) throws CVSException {
RepositoryRoot root = getRepositoryRootFor(project);
String remotePath = RepositoryRoot.getRemotePathFor(project);
return root.getAutoRefreshFiles(remotePath);
}
/**
* Remove the given tags from the list of known tags for the
* given remote root.
*/
public void removeTags(ICVSFolder project, CVSTag[] tags) throws CVSException {
RepositoryRoot root = getRepositoryRootFor(project);
String remotePath = RepositoryRoot.getRemotePathFor(project);
root.removeTags(remotePath, tags);
broadcastRepositoryChange(root);
}
public void startup() {
loadState();
loadCommentHistory();
loadCommentTemplates();
CVSProviderPlugin.getPlugin().addRepositoryListener(new ICVSListener() {
@Override
public void repositoryAdded(ICVSRepositoryLocation root) {
rootAdded(root);
}
@Override
public void repositoryRemoved(ICVSRepositoryLocation root) {
rootRemoved(root);
}
});
IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
store.addPropertyChangeListener(event -> {
if (event.getProperty().equals(ICVSUIConstants.PREF_COMMIT_COMMENTS_MAX_HISTORY)) {
Object newValue = event.getNewValue();
if (newValue instanceof String) {
try {
setMaxComments(Integer.parseInt((String) newValue));
} catch (NumberFormatException e) {
// fail silently
}
}
}
});
setMaxComments(store.getInt(ICVSUIConstants.PREF_COMMIT_COMMENTS_MAX_HISTORY));
}
public void shutdown() throws TeamException {
saveState();
saveCommentHistory();
saveCommentTemplates();
}
private void loadState() {
IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation().append(REPOSITORIES_VIEW_FILE);
File file = pluginStateLocation.toFile();
if (file.exists()) {
try {
BufferedInputStream is = new BufferedInputStream(new FileInputStream(file));
try {
readState(is);
} finally {
is.close();
}
} catch (IOException e) {
CVSUIPlugin.log(IStatus.ERROR, CVSUIMessages.RepositoryManager_ioException, e);
} catch (TeamException e) {
CVSUIPlugin.log(e);
}
} else {
IPath oldPluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation().append(STATE_FILE);
file = oldPluginStateLocation.toFile();
if (file.exists()) {
try {
DataInputStream dis = new DataInputStream(new FileInputStream(file));
try {
readOldState(dis);
} finally {
dis.close();
}
saveState();
file.delete();
} catch (IOException e) {
CVSUIPlugin.log(IStatus.ERROR, CVSUIMessages.RepositoryManager_ioException, e);
} catch (TeamException e) {
CVSUIPlugin.log(e);
}
}
}
}
private void loadCommentHistory() {
IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation().append(COMMENT_HIST_FILE);
File file = pluginStateLocation.toFile();
if (!file.exists()) return;
try {
BufferedInputStream is = new BufferedInputStream(new FileInputStream(file));
try {
readCommentHistory(is);
} finally {
is.close();
}
} catch (IOException e) {
CVSUIPlugin.log(IStatus.ERROR, CVSUIMessages.RepositoryManager_ioException, e);
} catch (TeamException e) {
CVSUIPlugin.log(e);
}
}
private void loadCommentTemplates() {
IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation().append(COMMENT_TEMPLATES_FILE);
File file = pluginStateLocation.toFile();
if (!file.exists()) return;
try {
BufferedInputStream is = new BufferedInputStream(new FileInputStream(file));
try {
readCommentTemplates(is);
} finally {
is.close();
}
} catch (IOException e) {
CVSUIPlugin.log(IStatus.ERROR, CVSUIMessages.RepositoryManager_ioException, e);
} catch (TeamException e) {
CVSUIPlugin.log(e);
}
}
protected void saveState() throws TeamException {
IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation();
File tempFile = pluginStateLocation.append(REPOSITORIES_VIEW_FILE + ".tmp").toFile(); //$NON-NLS-1$
File stateFile = pluginStateLocation.append(REPOSITORIES_VIEW_FILE).toFile();
try {
XMLWriter writer = new XMLWriter(new BufferedOutputStream(new FileOutputStream(tempFile)));
try {
writeState(writer);
} finally {
writer.close();
}
if (stateFile.exists()) {
stateFile.delete();
}
boolean renamed = tempFile.renameTo(stateFile);
if (!renamed) {
throw new TeamException(new Status(IStatus.ERROR, CVSUIPlugin.ID, TeamException.UNABLE, NLS.bind(CVSUIMessages.RepositoryManager_rename, new String[] { tempFile.getAbsolutePath() }), null));
}
} catch (IOException e) {
throw new TeamException(new Status(IStatus.ERROR, CVSUIPlugin.ID, TeamException.UNABLE, NLS.bind(CVSUIMessages.RepositoryManager_save, new String[] { stateFile.getAbsolutePath() }), e));
}
}
private void writeState(XMLWriter writer) {
writer.startTag(RepositoriesViewContentHandler.REPOSITORIES_VIEW_TAG, null, true);
// Write the repositories
Collection repos = Arrays.asList(getKnownRepositoryLocations());
Iterator it = repos.iterator();
while (it.hasNext()) {
CVSRepositoryLocation location = (CVSRepositoryLocation)it.next();
RepositoryRoot root = getRepositoryRootFor(location);
root.writeState(writer);
}
writer.endTag(RepositoriesViewContentHandler.REPOSITORIES_VIEW_TAG);
}
private void readState(InputStream stream) throws IOException, TeamException {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(new InputSource(stream), new RepositoriesViewContentHandler(this));
} catch (SAXException ex) {
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSUIMessages.RepositoryManager_parsingProblem, new String[] { REPOSITORIES_VIEW_FILE }), ex);
throw new CVSException(status);
} catch (ParserConfigurationException ex) {
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSUIMessages.RepositoryManager_parsingProblem, new String[] { REPOSITORIES_VIEW_FILE }), ex);
throw new CVSException(status);
}
}
private void readCommentHistory(InputStream stream) throws IOException, TeamException {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(new InputSource(stream), new CommentHistoryContentHandler());
} catch (SAXException ex) {
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSUIMessages.RepositoryManager_parsingProblem, new String[] { COMMENT_HIST_FILE }), ex);
throw new CVSException(status);
} catch (ParserConfigurationException ex) {
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSUIMessages.RepositoryManager_parsingProblem, new String[] { COMMENT_HIST_FILE }), ex);
throw new CVSException(status);
}
}
private void readOldState(DataInputStream dis) throws IOException, TeamException {
int repoSize = dis.readInt();
boolean version1 = false;
if (repoSize == STATE_FILE_VERSION_1) {
version1 = true;
repoSize = dis.readInt();
}
for (int i = 0; i < repoSize; i++) {
ICVSRepositoryLocation root = KnownRepositories.getInstance().getRepository(dis.readUTF());
RepositoryRoot repoRoot = getRepositoryRootFor(root);
// read branch tags associated with this root
int tagsSize = dis.readInt();
CVSTag[] branchTags = new CVSTag[tagsSize];
for (int j = 0; j < tagsSize; j++) {
String tagName = dis.readUTF();
int tagType = dis.readInt();
branchTags[j] = new CVSTag(tagName, tagType);
}
// Ignore the branch tags since they are handled differently now
// addBranchTags(root, branchTags);
// read the number of projects for this root that have version tags
int projSize = dis.readInt();
if (projSize > 0) {
for (int j = 0; j < projSize; j++) {
String name = dis.readUTF();
Set<CVSTag> tagSet = new HashSet<>();
int numTags = dis.readInt();
for (int k = 0; k < numTags; k++) {
tagSet.add(new CVSTag(dis.readUTF(), CVSTag.VERSION));
}
CVSTag[] tags = tagSet.toArray(new CVSTag[tagSet.size()]);
repoRoot.addTags(name, tags);
}
}
// read the auto refresh filenames for this project
if (version1) {
try {
projSize = dis.readInt();
if (projSize > 0) {
for (int j = 0; j < projSize; j++) {
String name = dis.readUTF();
Set<String> filenames = new HashSet<>();
int numFilenames = dis.readInt();
for (int k = 0; k < numFilenames; k++) {
filenames.add(name + "/" + dis.readUTF()); //$NON-NLS-1$
}
repoRoot.setAutoRefreshFiles(name, filenames.toArray(new String[filenames.size()]));
}
}
} catch (EOFException e) {
// auto refresh files are not persisted, continue and save them next time.
}
}
broadcastRepositoryChange(repoRoot);
}
}
protected void saveCommentHistory() throws TeamException {
IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation();
File tempFile = pluginStateLocation.append(COMMENT_HIST_FILE + ".tmp").toFile(); //$NON-NLS-1$
File histFile = pluginStateLocation.append(COMMENT_HIST_FILE).toFile();
try {
XMLWriter writer = new XMLWriter(new BufferedOutputStream(new FileOutputStream(tempFile)));
try {
writeCommentHistory(writer);
} finally {
writer.close();
}
if (histFile.exists()) {
histFile.delete();
}
boolean renamed = tempFile.renameTo(histFile);
if (!renamed) {
throw new TeamException(new Status(IStatus.ERROR, CVSUIPlugin.ID, TeamException.UNABLE, NLS.bind(CVSUIMessages.RepositoryManager_rename, new String[] { tempFile.getAbsolutePath() }), null));
}
} catch (IOException e) {
throw new TeamException(new Status(IStatus.ERROR, CVSUIPlugin.ID, TeamException.UNABLE, NLS.bind(CVSUIMessages.RepositoryManager_save, new String[] { histFile.getAbsolutePath() }), e));
}
}
private void writeCommentHistory(XMLWriter writer) {
writer.startTag(ELEMENT_COMMIT_HISTORY, null, false);
for (int i = 0; i < previousComments.length && i < maxComments; i++)
writer.printSimpleTag(ELEMENT_COMMIT_COMMENT, previousComments[i]);
writer.endTag(ELEMENT_COMMIT_HISTORY);
}
public void addRepositoryListener(IRepositoryListener listener) {
listeners.add(listener);
}
public void removeRepositoryListener(IRepositoryListener listener) {
listeners.remove(listener);
}
/**
* Return the entered comment or null if canceled.
* @param proposedComment
*/
public String promptForComment(final Shell shell, IResource[] resourcesToCommit, String proposedComment) {
final int[] result = new int[1];
final ReleaseCommentDialog dialog = new ReleaseCommentDialog(shell, resourcesToCommit, proposedComment, IResource.DEPTH_INFINITE);
shell.getDisplay().syncExec(() -> {
result[0] = dialog.open();
if (result[0] != Window.OK)
return;
});
if (result[0] != Window.OK) return null;
return dialog.getComment();
}
/**
* Prompt to add all or some of the provided resources to version control.
* The value null is returned if the dialog is cancelled.
*
* @param shell
* @param unadded
* @return IResource[]
*/
public IResource[] promptForResourcesToBeAdded(Shell shell, IResource[] unadded) {
if (unadded == null) return new IResource[0];
if (unadded.length == 0) return unadded;
final IResource[][] result = new IResource[1][0];
result[0] = null;
final AddToVersionControlDialog dialog = new AddToVersionControlDialog(shell, unadded);
shell.getDisplay().syncExec(() -> {
int code = dialog.open();
if (code == IDialogConstants.YES_ID) {
result[0] = dialog.getResourcesToAdd();
} else if (code == IDialogConstants.NO_ID) {
// allow the commit to continue.
result[0] = new IResource[0];
}
});
return result[0];
}
public ICVSRepositoryLocation getRepositoryLocationFor(ICVSResource resource) {
try {
return internalGetRepositoryLocationFor(resource);
} catch (CVSException e) {
CVSUIPlugin.log(e);
return null;
}
}
private ICVSRepositoryLocation internalGetRepositoryLocationFor(ICVSResource resource) throws CVSException {
ICVSFolder folder;
if (resource.isFolder()) {
folder = (ICVSFolder)resource;
} else {
folder = resource.getParent();
}
if (folder.isCVSFolder()) {
ICVSRepositoryLocation location = KnownRepositories.getInstance().getRepository(folder.getFolderSyncInfo().getRoot());
return location;
}
// XXX This is asking for trouble
return null;
}
private RepositoryRoot getRepositoryRootFor(ICVSResource resource) throws CVSException {
ICVSRepositoryLocation location = internalGetRepositoryLocationFor(resource);
if (location == null) return null;
return getRepositoryRootFor(location);
}
public RepositoryRoot getRepositoryRootFor(ICVSRepositoryLocation location) {
RepositoryRoot root = repositoryRoots.get(location.getLocation(false));
if (root == null) {
root = new RepositoryRoot(location);
add(root);
}
return root;
}
/**
* Add the given repository root to the receiver. The provided instance of RepositoryRoot
* is used to provide extra information about the repository location
*
* @param currentRepositoryRoot
*/
public void add(RepositoryRoot root) {
repositoryRoots.put(root.getRoot().getLocation(false), root);
broadcastRepositoryChange(root);
}
private void broadcastRepositoryChange(RepositoryRoot root) {
if (notificationLevel == 0) {
broadcastRepositoriesChanged(new ICVSRepositoryLocation[] {root.getRoot()});
} else {
changedRepositories.put(root.getRoot().getLocation(false), root.getRoot());
}
}
private void broadcastRepositoriesChanged(ICVSRepositoryLocation[] roots) {
if (roots.length == 0) return;
Iterator it = listeners.iterator();
while (it.hasNext()) {
IRepositoryListener listener = (IRepositoryListener)it.next();
listener.repositoriesChanged(roots);
}
}
/**
* Run the given runnable, waiting until the end to perform a refresh
*
* @param runnable
* @param monitor
*/
public void run(IRunnableWithProgress runnable, IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
notificationLevel++;
runnable.run(monitor);
} finally {
notificationLevel = Math.max(0, notificationLevel - 1);
if (notificationLevel == 0) {
try {
Collection<ICVSRepositoryLocation> roots = changedRepositories.values();
broadcastRepositoriesChanged(roots.toArray(new ICVSRepositoryLocation[roots.size()]));
} finally {
changedRepositories.clear();
}
}
}
}
/**
* Method isDisplayingProjectVersions.
* @param repository
* @return boolean
*/
public boolean isDisplayingProjectVersions(ICVSRepositoryLocation repository) {
return true;
}
/**
* Method filterResources filters the given resources using the given
* working set.
*
* @param current
* @param resources
* @return ICVSRemoteResource[]
*/
public ICVSRemoteResource[] filterResources(IWorkingSet workingSet, ICVSRemoteResource[] resources) {
if (workingSet == null) return resources;
// get the projects associated with the working set
IAdaptable[] adaptables = workingSet.getElements();
Set<IProject> projects = new HashSet<>();
for (int i = 0; i < adaptables.length; i++) {
IAdaptable adaptable = adaptables[i];
Object adapted = adaptable.getAdapter(IResource.class);
if (adapted != null) {
// Can this code be generalized?
IProject project = ((IResource)adapted).getProject();
projects.add(project);
}
}
List<ICVSRemoteResource> result = new ArrayList<>();
for (int i = 0; i < resources.length; i++) {
ICVSRemoteResource resource = resources[i];
for (Iterator iter = projects.iterator(); iter.hasNext();) {
IProject project = (IProject) iter.next();
if (project.getName().equals(resource.getName())) {
result.add(resource);
break;
}
}
}
return result.toArray(new ICVSRemoteResource[result.size()]);
}
/**
* Method setLabel.
* @param location
* @param label
*/
public void setLabel(CVSRepositoryLocation location, String label) {
RepositoryRoot root = getRepositoryRootFor(location);
String oldLabel = root.getName();
if (oldLabel == null) {
if (label == null) return;
root.setName(label);
} else if (label == null) {
root.setName(label);
} else if (label.equals(oldLabel)) {
return;
} else {
root.setName(label);
}
broadcastRepositoryChange(root);
}
/**
* Replace the old repository location with the new one assuming that they
* are the same location with different authentication informations
* @param location
* @param newLocation
*/
public void replaceRepositoryLocation(
final ICVSRepositoryLocation oldLocation,
final CVSRepositoryLocation newLocation) {
try {
run(monitor -> {
RepositoryRoot root = getRepositoryRootFor(oldLocation);
// Disposing of the old location will result in the deletion of the
// cached root through a listener callback
KnownRepositories.getInstance().disposeRepository(oldLocation);
// Get the new location from the CVS plugin to ensure we use the
// instance that will be returned by future calls to getRepository()
boolean isNew = !KnownRepositories.getInstance().isKnownRepository(newLocation.getLocation());
root.setRepositoryLocation(
KnownRepositories.getInstance().addRepository(newLocation, isNew /* broadcast */));
add(root);
}, Policy.monitorFor(null));
} catch (InvocationTargetException e) {
CVSException.wrapException(e);
} catch (InterruptedException e) {
}
}
/**
* Purge any cahced information.
*/
public void purgeCache() {
for (Iterator iter = repositoryRoots.values().iterator(); iter.hasNext();) {
RepositoryRoot root = (RepositoryRoot) iter.next();
root.clearCache();
}
}
/**
* Answer the list of comments that were previously used when committing.
* @return String[]
*/
public String[] getPreviousComments() {
return previousComments;
}
/**
* Method addComment.
* @param string
*/
public void addComment(String comment) {
// Make comment first element if it's already there
int index = getCommentIndex(comment);
if (index != -1) {
makeFirstElement(index);
return;
}
if (containsCommentTemplate(comment))
return;
// Insert the comment as the first element
String[] newComments = new String[Math.min(previousComments.length + 1, maxComments)];
newComments[0] = comment;
for (int i = 1; i < newComments.length; i++) {
newComments[i] = previousComments[i-1];
}
previousComments = newComments;
}
private int getCommentIndex(String comment) {
for (int i = 0; i < previousComments.length; i++) {
if (previousComments[i].equals(comment)) {
return i;
}
}
return -1;
}
private void makeFirstElement(int index) {
String[] newComments = new String[previousComments.length];
newComments[0] = previousComments[index];
System.arraycopy(previousComments, 0, newComments, 1, index);
int maxIndex = previousComments.length - 1;
if (index != maxIndex) {
int nextIndex = (index + 1);
System.arraycopy(previousComments, nextIndex, newComments,
nextIndex, (maxIndex - index));
}
previousComments = newComments;
}
private void readCommentTemplates(InputStream stream) throws IOException, TeamException {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(new InputSource(stream),
new CommentTemplatesContentHandler());
} catch (SAXException ex) {
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(
CVSUIMessages.RepositoryManager_parsingProblem,
new String[] { COMMENT_TEMPLATES_FILE }), ex);
throw new CVSException(status);
} catch (ParserConfigurationException ex) {
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(
CVSUIMessages.RepositoryManager_parsingProblem,
new String[] { COMMENT_TEMPLATES_FILE }), ex);
throw new CVSException(status);
}
}
protected void saveCommentTemplates() throws TeamException {
IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation();
File tempFile = pluginStateLocation.append(
COMMENT_TEMPLATES_FILE + ".tmp").toFile(); //$NON-NLS-1$
File histFile = pluginStateLocation.append(COMMENT_TEMPLATES_FILE)
.toFile();
try {
XMLWriter writer = new XMLWriter(new BufferedOutputStream(
new FileOutputStream(tempFile)));
try {
writeCommentTemplates(writer);
} finally {
writer.close();
}
if (histFile.exists()) {
histFile.delete();
}
boolean renamed = tempFile.renameTo(histFile);
if (!renamed) {
throw new TeamException(new Status(IStatus.ERROR,
CVSUIPlugin.ID, TeamException.UNABLE, NLS.bind(
CVSUIMessages.RepositoryManager_rename,
new String[] { tempFile.getAbsolutePath() }),
null));
}
} catch (IOException e) {
throw new TeamException(new Status(IStatus.ERROR, CVSUIPlugin.ID,
TeamException.UNABLE, NLS.bind(
CVSUIMessages.RepositoryManager_save,
new String[] { histFile.getAbsolutePath() }), e));
}
}
private void writeCommentTemplates(XMLWriter writer) {
writer.startTag(ELEMENT_COMMENT_TEMPLATES, null, false);
for (int i = 0; i < commentTemplates.length; i++)
writer.printSimpleTag(ELEMENT_COMMIT_COMMENT, commentTemplates[i]);
writer.endTag(ELEMENT_COMMENT_TEMPLATES);
}
private boolean containsCommentTemplate(String comment) {
for (int i = 0; i < commentTemplates.length; i++) {
if (commentTemplates[i].equals(comment)) {
return true;
}
}
return false;
}
/**
* Get list of comment templates.
*/
public String[] getCommentTemplates() {
return commentTemplates;
}
public void replaceAndSaveCommentTemplates(String[] templates)
throws TeamException {
commentTemplates = templates;
saveCommentTemplates();
}
}