blob: b42912b36051dc268fd2712ab63674f1fc15a684 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others.
*
* All rights reserved. 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.repository;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.InvalidPathException;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.repository.tree.FilterableNode;
import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType;
import org.eclipse.jgit.util.StringUtils;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
/**
* Cache for filters in the {@link RepositoriesView}.
*/
class FilterCache {
/** The singleton instance. */
public static final FilterCache INSTANCE = new FilterCache();
private static final String TAG_REPO = "repo"; //$NON-NLS-1$
private static final String ATTR_DIR = "dir"; //$NON-NLS-1$
private static final String TAG_FILTER = "filter"; //$NON-NLS-1$
private static final String ATTR_NODE = "tag"; //$NON-NLS-1$
private static final String ATTR_FILTER = "filter"; //$NON-NLS-1$
private final Map<File, Map<RepositoryTreeNodeType, String>> cache = new ConcurrentHashMap<>();
private FilterCache() {
// Singleton
}
/**
* Records a filter for a node.
*
* @param node
* to record the filter for
* @param filter
* to record
*/
public void set(FilterableNode node, String filter) {
Map<RepositoryTreeNodeType, String> map = cache.computeIfAbsent(
node.getRepository().getDirectory(),
k -> new ConcurrentHashMap<>());
if (filter == null) {
map.remove(node.getType());
} else {
map.put(node.getType(), filter);
}
node.setFilter(filter);
}
/**
* Retrieves the cached filter, if any, for a node.
*
* @param node
* to get the filter of
* @return the filter, or {@code null} if none cached
*/
public String get(FilterableNode node) {
Map<RepositoryTreeNodeType, String> filters = cache
.get(node.getRepository().getDirectory());
return filters == null ? null : filters.get(node.getType());
}
/**
* Clears all cached entries from repositories not in the given collection.
*
* @param gitDirs
* collection of git directories of repositories for which to
* keep the cached filters
*/
public void keepOnly(Collection<File> gitDirs) {
cache.keySet().retainAll(gitDirs);
}
/**
* Loads the cache from the EGit UI preferences.
*/
public void load() {
String data = Activator.getDefault().getPreferenceStore()
.getString(UIPreferences.REPOSITORIES_VIEW_FILTERS);
if (StringUtils.isEmptyOrNull(data)) {
return;
}
XMLMemento memento;
try (StringReader reader = new StringReader(data)) {
memento = XMLMemento.createReadRoot(reader);
} catch (WorkbenchException e) {
Activator.logError(UIText.RepositoriesView_FilterErrorRead, e);
return;
}
for (IMemento repo : memento.getChildren(TAG_REPO)) {
String repoId = repo.getString(ATTR_DIR);
if (repoId == null || repoId.isEmpty()) {
continue;
}
try {
repoId = RepositoryUtil.INSTANCE
.getAbsoluteRepositoryPath(repoId);
} catch (InvalidPathException e) {
continue; // Skip invalid paths
}
Map<RepositoryTreeNodeType, String> values = new ConcurrentHashMap<>();
for (IMemento entry : repo.getChildren(TAG_FILTER)) {
String tag = entry.getString(ATTR_NODE);
if (StringUtils.isEmptyOrNull(tag)) {
continue;
}
String filter = entry.getString(ATTR_FILTER);
if (StringUtils.isEmptyOrNull(filter)) {
continue;
}
RepositoryTreeNodeType type = null;
try {
type = RepositoryTreeNodeType.valueOf(tag);
} catch (RuntimeException e) {
continue; // Just skip unknown values
}
values.put(type, filter);
}
if (!values.isEmpty()) {
cache.put(new File(repoId), values);
}
}
}
/**
* Persists the cache in the EGit UI preferences.
*/
public void save() {
XMLMemento memento = XMLMemento
.createWriteRoot(getClass().getSimpleName());
cache.entrySet().stream().forEach(entry -> {
Map<RepositoryTreeNodeType, String> values = entry.getValue();
if (values.isEmpty()) {
return;
}
IMemento repo = memento.createChild(TAG_REPO);
String path = entry.getKey().getAbsolutePath();
assert path != null;
repo.putString(ATTR_DIR,
RepositoryUtil.INSTANCE.relativizeToWorkspace(path));
values.entrySet().stream().forEach(e -> {
String filter = e.getValue();
if (StringUtils.isEmptyOrNull(filter)) {
return;
}
IMemento child = repo.createChild(TAG_FILTER);
child.putString(ATTR_NODE, e.getKey().name());
child.putString(ATTR_FILTER, filter);
});
});
try (StringWriter writer = new StringWriter()) {
memento.save(writer);
Activator.getDefault().getPreferenceStore().setValue(
UIPreferences.REPOSITORIES_VIEW_FILTERS, writer.toString());
} catch (IOException e) {
Activator.logError(UIText.RepositoriesView_FilterErrorSave, e);
}
}
}