/*******************************************************************************
* Copyright (c) 2014, 2106 Obeo.
* 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
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.impl;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.compare.rcp.internal.extension.impl.ItemUtil;
import org.eclipse.emf.compare.rcp.internal.tracer.TracingConstant;
import org.eclipse.emf.compare.rcp.ui.EMFCompareRCPUIPlugin;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.filters.IDeactivableDiffFilter;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.filters.IDifferenceFilter;
import org.eclipse.jface.preference.IPreferenceStore;
/**
* Manager of filters.
* <p>
* This manager handle addition and removal of filters
* </p>
* <p>
* It also allow to override registered filters with preferences.
* </p>
*
* @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a>
*/
public class DifferenceFilterManager {
/** Preference key for by default disabled filters. */
private static final String BY_DEFAULT_DISABLED_FILTER = "org.eclipse.emf.compare.rcp.ui.filters.disabled"; //$NON-NLS-1$
/** Preference key for inactive (fully ignored) filters. */
private static final String INACTIVE_FILTERS_PREF_KEY = "org.eclipse.emf.compare.rcp.ui.filters.inactive"; //$NON-NLS-1$
/** A map that associates the class name to their {@link IDifferenceFilter}s. */
private final Map<String, DifferenceFilterDefaultConfiguration> map;
/** The {@link IPreferenceStore} holding the value for filter preferences. */
private final IPreferenceStore preferenceStore;
/**
* Predicate use to transform {@link DifferenceFilterDefaultConfiguration} to {@link IDifferenceFilter}.
*/
private final static Function<DifferenceFilterDefaultConfiguration, IDifferenceFilter> TO_FILTER = new Function<DifferenceFilterManager.DifferenceFilterDefaultConfiguration, IDifferenceFilter>() {
public IDifferenceFilter apply(DifferenceFilterDefaultConfiguration arg0) {
if (arg0 != null) {
return arg0.getFilter();
}
return null;
}
};
/**
* Constructor.
*
* @param preferenceStore
* The {@link IPreferenceStore} holding the value for filter preferences.
*/
public DifferenceFilterManager(IPreferenceStore preferenceStore) {
map = Collections.synchronizedMap(new LinkedHashMap<String, DifferenceFilterDefaultConfiguration>());
this.preferenceStore = preferenceStore;
}
/**
* Add a new filter.
*
* @param filter
* {@link IDifferenceFilter}
* @return The old {@link IDifferenceFilter} register with the same key.
*/
IDifferenceFilter add(IDifferenceFilter filter) {
Preconditions.checkNotNull(filter);
DifferenceFilterDefaultConfiguration oldValue = map.put(filter.getClass().getName(),
new DifferenceFilterDefaultConfiguration(filter, filter.defaultSelected()));
if (oldValue != null) {
return oldValue.getFilter();
}
return null;
}
/**
* Remove a filter.
*
* @param className
* The class name of the filter.
* @return The {@link IDifferenceFilter} that has been removed or <code>null</code> if none.
*/
IDifferenceFilter remove(String className) {
DifferenceFilterDefaultConfiguration oldValue = map.remove(className);
if (oldValue != null) {
return oldValue.getFilter();
}
return null;
}
/**
* Get all {@link IDifferenceFilter} that should be used by default for next comparison.
*
* @return A {@link Collection} of {@link IDifferenceFilter} that should be used by default for next
* comparison.
*/
public Set<IDifferenceFilter> getCurrentByDefaultFilters() {
Set<IDifferenceFilter> storedFilter = getDisabledFilters();
if (storedFilter == null) {
return getInitialByDefaultFilters();
}
return Sets.difference(getAllFilters(), storedFilter);
}
/**
* Get all {@link IDifferenceFilter} that should be disabled for next comparison.
*
* @return A {@link Collection} of {@link IDifferenceFilter} that should be disabled for next comparison.
*/
public Collection<IDeactivableDiffFilter> getCurrentInactiveFilters() {
Set<IDeactivableDiffFilter> inactiveFilters = getInactiveFilters();
if (inactiveFilters == null) {
return Collections.emptyList();
}
return inactiveFilters;
}
/**
* {@link Set} of {@link IDifferenceFilter} that are initially enabled by default.
* <p>
* During the first addiction in the registry of these {@link IDifferenceFilter},
* {@link IDifferenceFilter#defaultSelected()} was equal to true
* </p>
*
* @return {@link Set} of {@link IDifferenceFilter} that are original enabled by default.
*/
public Set<IDifferenceFilter> getInitialByDefaultFilters() {
Set<IDifferenceFilter> result = Sets.newLinkedHashSet();
for (DifferenceFilterDefaultConfiguration f : map.values()) {
if (f.isDefaultSelectedInitialValue()) {
result.add(f.getFilter());
}
}
return result;
}
/**
* Set the filters that should be used by default for next comparison.
*
* @param enabledFilter
* {@link Set} of {@link IDifferenceFilter} to set.
*/
public void setCurrentByDefaultFilters(Set<IDifferenceFilter> enabledFilter) {
final Set<IDifferenceFilter> disabledFilter;
if (enabledFilter == null) {
disabledFilter = getAllFilters();
} else {
disabledFilter = Sets.difference(getAllFilters(), enabledFilter);
}
SetView<IDifferenceFilter> initialDisabledFilter = Sets.difference(getAllFilters(),
getInitialByDefaultFilters());
storeInPreferences(disabledFilter, initialDisabledFilter, BY_DEFAULT_DISABLED_FILTER);
// Trace preferences values
if (TracingConstant.CONFIGURATION_TRACING_ACTIVATED) {
StringBuilder builder = new StringBuilder();
// Print each preferences
builder.append("Preference ").append(BY_DEFAULT_DISABLED_FILTER).append(":\n"); //$NON-NLS-1$ //$NON-NLS-2$
String preferenceValue = preferenceStore.getString(BY_DEFAULT_DISABLED_FILTER);
String[] groups = preferenceValue.split(ItemUtil.PREFERENCE_DELIMITER);
for (int rank = 0; rank < groups.length; rank++) {
builder.append(rank).append(". ").append(groups[rank]).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
builder.append("\n\n"); //$NON-NLS-1$
EMFCompareRCPUIPlugin.getDefault().log(IStatus.INFO, builder.toString());
}
}
/**
* Set the filters that should be active for the next comparison.
*
* @param activeFilters
* {@link Set} of {@link IDifferenceFilter} to set.
*/
public void setCurrentActiveFilters(Set<IDifferenceFilter> activeFilters) {
final Set<IDeactivableDiffFilter> inactiveFilters;
Set<IDeactivableDiffFilter> deactivableFilters = Sets
.newLinkedHashSet(Iterables.filter(getAllFilters(), IDeactivableDiffFilter.class));
if (activeFilters == null) {
inactiveFilters = deactivableFilters;
} else {
inactiveFilters = Sets.difference(deactivableFilters, activeFilters);
}
storeInPreferences(inactiveFilters, Collections.<IDifferenceFilter> emptySet(),
INACTIVE_FILTERS_PREF_KEY);
// Trace preferences values
if (TracingConstant.CONFIGURATION_TRACING_ACTIVATED) {
StringBuilder builder = new StringBuilder();
// Print each preferences
builder.append("Preference ").append(INACTIVE_FILTERS_PREF_KEY).append(":\n"); //$NON-NLS-1$ //$NON-NLS-2$
String preferenceValue = preferenceStore.getString(INACTIVE_FILTERS_PREF_KEY);
String[] groups = preferenceValue.split(ItemUtil.PREFERENCE_DELIMITER);
for (int rank = 0; rank < groups.length; rank++) {
builder.append(rank).append(". ").append(groups[rank]).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
builder.append("\n\n"); //$NON-NLS-1$
EMFCompareRCPUIPlugin.getDefault().log(IStatus.INFO, builder.toString());
}
}
/**
* Get all registered filter.
*
* @return {@link Set} of all filter.
*/
public Set<IDifferenceFilter> getAllFilters() {
return Sets.newLinkedHashSet(Collections2.transform(map.values(), TO_FILTER));
}
/**
* A {@link Set} of disabled by default {@link IDifferenceFilter} from preferences.
* <p>
* Those filter will not be activated by default for next comparison
* </p>
*
* @return A {@link Set} of disabled by default {@link IDifferenceFilter} from preferences.
*/
private Set<IDifferenceFilter> getDisabledFilters() {
String diffEngineKey = preferenceStore.getString(BY_DEFAULT_DISABLED_FILTER);
Set<IDifferenceFilter> result = null;
if (diffEngineKey != null && !diffEngineKey.isEmpty()) {
String[] diffEngineKeys = diffEngineKey.split(ItemUtil.PREFERENCE_DELIMITER);
for (String nonTrimedKey : diffEngineKeys) {
String key = nonTrimedKey.trim();
DifferenceFilterDefaultConfiguration descriptor = map.get(key);
if (descriptor != null) {
if (result == null) {
result = new LinkedHashSet<IDifferenceFilter>();
}
result.add(descriptor.getFilter());
}
}
}
return result;
}
/**
* A {@link Set} of deactivated {@link IDeactivableDiffFilter} from preferences.
* <p>
* Those filters will be totally inactive for next comparisons.
* </p>
*
* @return A {@link Set} of deactivated {@link IDeactivableDiffFilter} from preferences (Filters that do
* not implement {@link IDeactivableDiffFilter} cannot be deactivated).
*/
private Set<IDeactivableDiffFilter> getInactiveFilters() {
String diffEngineKey = preferenceStore.getString(INACTIVE_FILTERS_PREF_KEY);
Set<IDeactivableDiffFilter> result = null;
if (diffEngineKey != null && !diffEngineKey.isEmpty()) {
String[] diffEngineKeys = diffEngineKey.split(ItemUtil.PREFERENCE_DELIMITER);
for (String nonTrimedKey : diffEngineKeys) {
String key = nonTrimedKey.trim();
DifferenceFilterDefaultConfiguration descriptor = map.get(key);
if (descriptor != null) {
IDifferenceFilter filter = descriptor.getFilter();
if (filter instanceof IDeactivableDiffFilter) {
if (result == null) {
result = new LinkedHashSet<IDeactivableDiffFilter>();
}
result.add((IDeactivableDiffFilter)filter);
}
}
}
}
return result;
}
/**
* Store value in preferences.
*
* @param currentValue
* Value to store.
* @param defaultConf
* Default value.
*/
private void storeInPreferences(Set<? extends IDifferenceFilter> currentValue,
Set<? extends IDifferenceFilter> defaultConf, String prefKey) {
if (currentValue != null && !currentValue.equals(defaultConf)) {
Map<String, IDifferenceFilter> toStore = Maps.filterValues(Maps.transformValues(map, TO_FILTER),
Predicates.in(currentValue));
String preferenceValue = Joiner.on(ItemUtil.PREFERENCE_DELIMITER).join(toStore.keySet());
preferenceStore.putValue(prefKey, preferenceValue);
} else {
preferenceStore.setToDefault(prefKey);
}
}
/**
* Clear all registered {@link IDifferenceFilter}.
*/
public void clear() {
map.clear();
}
/**
* Wrapper of {@link IDifferenceFilter} used to keep track of the initial value of
* {@link IDifferenceFilter#defaultSelected()} to be able to restore it.
*
* @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a>
*/
private static class DifferenceFilterDefaultConfiguration {
/** Base {@link IDifferenceFilter} */
private IDifferenceFilter filter;
/** Initial value of {@link IDifferenceFilter#defaultSelected()} during first addition. */
private boolean isDefaultSelectedInitialValue;
/**
* Constructor.
*
* @param filter
* Base filter
* @param isByDefaultSelected
* Initial value of {@link IDifferenceFilter#defaultSelected()} during first addition.
*/
public DifferenceFilterDefaultConfiguration(IDifferenceFilter filter, boolean isByDefaultSelected) {
super();
this.filter = filter;
this.isDefaultSelectedInitialValue = isByDefaultSelected;
}
/**
* @return Base filter.
*/
public IDifferenceFilter getFilter() {
return filter;
}
/**
* @return Initial value of {@link IDifferenceFilter#defaultSelected()} during first addition.
*/
public boolean isDefaultSelectedInitialValue() {
return isDefaultSelectedInitialValue;
}
}
}