blob: 3762a9d062a70ab89dacfa9654202fa9129187aa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2017 Obeo and others.
* 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
* Philip Langer - bug 499986, refactorings
*******************************************************************************/
package org.eclipse.emf.compare.rcp.ui.internal.preferences;
import static org.eclipse.emf.compare.rcp.internal.preferences.EMFComparePreferences.CONFLICTS_DETECTOR;
import static org.eclipse.emf.compare.rcp.internal.preferences.EMFComparePreferences.DIFF_ENGINES;
import static org.eclipse.emf.compare.rcp.internal.preferences.EMFComparePreferences.EQUI_ENGINES;
import static org.eclipse.emf.compare.rcp.internal.preferences.EMFComparePreferences.MATCH_ENGINE_DISABLE_ENGINES;
import static org.eclipse.emf.compare.rcp.internal.preferences.EMFComparePreferences.REQ_ENGINES;
import static org.eclipse.emf.compare.rcp.ui.internal.EMFCompareRCPUIMessages.getString;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.emf.compare.conflict.IConflictDetector;
import org.eclipse.emf.compare.diff.IDiffEngine;
import org.eclipse.emf.compare.equi.IEquiEngine;
import org.eclipse.emf.compare.match.IMatchEngine;
import org.eclipse.emf.compare.match.IMatchEngine.Factory;
import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
import org.eclipse.emf.compare.rcp.internal.extension.IItemDescriptor;
import org.eclipse.emf.compare.rcp.internal.extension.IItemRegistry;
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.internal.EMFCompareRCPUIMessages;
import org.eclipse.emf.compare.rcp.ui.internal.configuration.ui.AbstractConfigurationUI;
import org.eclipse.emf.compare.rcp.ui.internal.configuration.ui.IConfigurationUIFactory;
import org.eclipse.emf.compare.rcp.ui.internal.preferences.impl.InteractiveUIContent;
import org.eclipse.emf.compare.rcp.ui.internal.preferences.impl.InteractiveUIContent.InteractiveUIBuilder;
import org.eclipse.emf.compare.req.IReqEngine;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
/**
* Preference page for engines preferences
*
* @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a>
*/
public class EnginesPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
/** The match engine registry. */
private static final IItemRegistry<Factory> MATCH_ENGINE_REGISTRY = EMFCompareRCPPlugin.getDefault()
.getMatchEngineFactoryDescriptorRegistry();
/** The req engine registry. */
private static final IItemRegistry<IReqEngine> REQ_ENGINE_REGISTRY = EMFCompareRCPPlugin.getDefault()
.getReqEngineDescriptorRegistry();
/** The diff engine registry. */
private static final IItemRegistry<IDiffEngine> DIFF_ENGINE_REGISTRY = EMFCompareRCPPlugin.getDefault()
.getDiffEngineDescriptorRegistry();
/** The equi engine registry. */
private static final IItemRegistry<IEquiEngine> EQUI_ENGINE_REGISTRY = EMFCompareRCPPlugin.getDefault()
.getEquiEngineDescriptorRegistry();
/** The conflict engine registry. */
private static final IItemRegistry<IConflictDetector> CONFLICT_DETECTOR_REGISTRY = EMFCompareRCPPlugin
.getDefault().getConflictDetectorDescriptorRegistry();
/** Option to specify what is the default, if no preference or default preference is available. */
private enum DefaultOption {
/** The highest ranked is the default. */
HIGHEST_RANKED,
/** All registered engines should be the default. */
ALL;
}
/** An option to specify what should be stored in the preferences. */
private enum StoreOption {
/** The enabled items. */
ENABLED_ITEMS,
/** The disabled items. */
DISABLED_ITEMS;
}
/** Pointer to all {@link InteractiveUIContent} of each tab */
private final Map<String, InteractiveUIContent> interactiveUis = new HashMap<String, InteractiveUIContent>();
/** Data regarding the Difference selected engine */
private final DataHolder<IDiffEngine> diffEngineData = new DataHolder<IDiffEngine>();
/** Data regarding the Equivalence selected engine */
private final DataHolder<IEquiEngine> equiEngineData = new DataHolder<IEquiEngine>();
/** Data regarding the Requirement selected engine */
private final DataHolder<IReqEngine> reqEngineData = new DataHolder<IReqEngine>();
/** Data regarding the Conflicts detector selected engine */
private final DataHolder<IConflictDetector> conflictsDetectorData = new DataHolder<IConflictDetector>();
/** Data regarding the selected match engine factories. */
private final DataHolder<IMatchEngine.Factory> matchEnginesData = new DataHolder<IMatchEngine.Factory>();
public EnginesPreferencePage() {
super();
}
public EnginesPreferencePage(String title) {
super(title);
}
public EnginesPreferencePage(String title, ImageDescriptor image) {
super(title, image);
}
public void init(IWorkbench workbench) {
ScopedPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE,
EMFCompareRCPPlugin.PLUGIN_ID);
store.setSearchContexts(new IScopeContext[] {InstanceScope.INSTANCE, ConfigurationScope.INSTANCE });
setPreferenceStore(store);
}
@Override
protected Control createContents(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
container.setLayout(new FillLayout(SWT.HORIZONTAL));
TabFolder tabFolder = new TabFolder(container, SWT.NONE);
createMatchEngineTab(tabFolder);
createDiffEngineTab(tabFolder);
createEquiEngineTab(tabFolder);
createReqEngineTab(tabFolder);
createConflictDetectorTab(tabFolder);
return container;
}
/**
* Create an {@link InteractiveUIContent} object of a specific type of engine.
*
* @param registry
* Registry holding engines.
* @param tabComposite
* Holding composite.
* @param dataHolder
* Data that will be synchronized with the UI.
* @param <T>
* type of engine.
* @return {@link InteractiveUIContent} for a specific type of engine.
*/
private <T> InteractiveUIContent createEngineUIBuilder(IItemRegistry<T> registry, Composite tabComposite,
DataHolder<T> dataHolder) {
String preferenceKey = preferenceKey(registry);
IItemDescriptor<T> defaultEngine = ItemUtil.getDefaultItemDescriptor(registry, preferenceKey);
InteractiveUIBuilder<T> uiBuilder = new InteractiveUIBuilder<T>(tabComposite, registry);
uiBuilder.setSimple(true).setDefaultCheck(Collections.singleton(defaultEngine))
.setDefaultSelection(defaultEngine).setHoldingData(dataHolder);
return uiBuilder.build();
}
/**
* Returns the preference key for the given registry.
*
* @param registry
* The registry to get the preference key for.
* @return The preference key.
*/
private String preferenceKey(IItemRegistry<?> registry) {
if (registry == MATCH_ENGINE_REGISTRY) {
return MATCH_ENGINE_DISABLE_ENGINES;
} else if (registry == DIFF_ENGINE_REGISTRY) {
return DIFF_ENGINES;
} else if (registry == REQ_ENGINE_REGISTRY) {
return REQ_ENGINES;
} else if (registry == EQUI_ENGINE_REGISTRY) {
return EQUI_ENGINES;
} else if (registry == CONFLICT_DETECTOR_REGISTRY) {
return CONFLICTS_DETECTOR;
}
throw new IllegalArgumentException("Unknown registry."); //$NON-NLS-1$
}
/**
* Create a tab to select one Conflict Detector.
*
* @param tabFolder
*/
private void createConflictDetectorTab(TabFolder tabFolder) {
Composite tabComposite = createTabSkeleton(tabFolder,
getString("EnginesPreferencePage.conflictDetector.tab.label"), //$NON-NLS-1$
getString("EnginesPreferencePage.conflictDetectorIntro.text"));//$NON-NLS-1$
InteractiveUIContent interactiveContent = createEngineUIBuilder(CONFLICT_DETECTOR_REGISTRY,
tabComposite, conflictsDetectorData);
interactiveUis.put(preferenceKey(CONFLICT_DETECTOR_REGISTRY), interactiveContent);
}
/**
* Create a tab to select one Requirement Engine.
*
* @param tabFolder
*/
private void createReqEngineTab(TabFolder tabFolder) {
Composite tabComposite = createTabSkeleton(tabFolder,
getString("EnginesPreferencePage.requirementEngine.tab.label"), //$NON-NLS-1$
getString("EnginesPreferencePage.reqEngineIntro.text")); //$NON-NLS-1$
InteractiveUIContent interactiveContent = createEngineUIBuilder(REQ_ENGINE_REGISTRY, tabComposite,
reqEngineData);
interactiveUis.put(preferenceKey(REQ_ENGINE_REGISTRY), interactiveContent);
}
/**
* Create a tab to select one Equivalence Engine.
*
* @param tabFolder
*/
private void createEquiEngineTab(TabFolder tabFolder) {
Composite tabComposite = createTabSkeleton(tabFolder,
getString("EnginesPreferencePage.equivalenceEngine.tab.label"), //$NON-NLS-1$
getString("EnginesPreferencePage.equiEngineIntro.text")); //$NON-NLS-1$
InteractiveUIContent interactiveContent = createEngineUIBuilder(EQUI_ENGINE_REGISTRY, tabComposite,
equiEngineData);
interactiveUis.put(preferenceKey(EQUI_ENGINE_REGISTRY), interactiveContent);
}
/**
* Create a tab to select one Difference Engine.
*
* @param tabFolder
*/
private void createDiffEngineTab(TabFolder tabFolder) {
Composite tabComposite = createTabSkeleton(tabFolder,
getString("EnginesPreferencePage.differenceEngine.tab.label"), //$NON-NLS-1$
getString("EnginesPreferencePage.diffEngineIntro.text")); //$NON-NLS-1$
InteractiveUIContent interactiveContent = createEngineUIBuilder(DIFF_ENGINE_REGISTRY, tabComposite,
diffEngineData);
interactiveUis.put(preferenceKey(DIFF_ENGINE_REGISTRY), interactiveContent);
}
/**
* Create a tab to disable/enable Match Engines.
*
* @param tabFolder
*/
private void createMatchEngineTab(TabFolder tabFolder) {
Composite tabComposite = createTabSkeleton(tabFolder,
getString("EnginesPreferencePage.matchEngine.tab.label"), //$NON-NLS-1$
getString("EnginesPreferencePage.matchEngineIntro.text")); //$NON-NLS-1$
Map<String, IConfigurationUIFactory> configuratorUIRegistry = EMFCompareRCPUIPlugin.getDefault()
.getMatchEngineConfiguratorRegistry();
Set<IItemDescriptor<Factory>> activeItems = ItemUtil.getActiveItems(MATCH_ENGINE_REGISTRY,
EMFCompareRCPPlugin.PLUGIN_ID, MATCH_ENGINE_DISABLE_ENGINES);
InteractiveUIBuilder<IMatchEngine.Factory> builder = new InteractiveUIBuilder<IMatchEngine.Factory>(
tabComposite, MATCH_ENGINE_REGISTRY);
builder.setConfiguratorUIRegistry(configuratorUIRegistry).setDefaultCheck(activeItems)
.setConfigurationNodeKey(MATCH_ENGINE_DISABLE_ENGINES).setHoldingData(matchEnginesData);
// Forbid unchecking all match engines
InteractiveUIContent uiContent = builder.build();
uiContent.getViewer().addCheckStateListener(new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event) {
Object element = event.getElement();
if (!event.getChecked()) {
// Prevent from nothing checked
if (((CheckboxTableViewer)event.getSource()).getCheckedElements().length == 0) {
((CheckboxTableViewer)event.getSource()).setCheckedElements(new Object[] {element });
MessageDialog.openWarning(getShell(),
EMFCompareRCPUIMessages
.getString("InteractiveUIContent.incorrectSelection.title"), //$NON-NLS-1$
EMFCompareRCPUIMessages
.getString("InteractiveUIContent.incorrectSelection.message")); //$NON-NLS-1$
}
}
}
});
// Save for reset default
interactiveUis.put(MATCH_ENGINE_DISABLE_ENGINES, uiContent);
}
/**
* Create skeleton of a tab.
*
* @param tabFolder
* @param tabLabel
* @param introText
* Text use as description a tab
* @return Main composite of the tab
*/
private Composite createTabSkeleton(TabFolder tabFolder, String tabLabel, String introText) {
TabItem tbtmMain = new TabItem(tabFolder, SWT.NONE);
tbtmMain.setText(tabLabel);
Composite tabComposite = new Composite(tabFolder, SWT.NONE);
tbtmMain.setControl(tabComposite);
tabComposite.setLayout(new GridLayout(1, true));
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
tabComposite.setLayoutData(layoutData);
Label descriptionText = new Label(tabComposite, SWT.WRAP);
descriptionText.setText(introText);
return tabComposite;
}
@Override
public boolean performOk() {
setEnginesPreferences();
storeConfigurations();
return super.performOk();
}
/**
* Set all engines preferences.
*/
private void setEnginesPreferences() {
setEnginePreferences(DIFF_ENGINE_REGISTRY, diffEngineData);
setEnginePreferences(EQUI_ENGINE_REGISTRY, equiEngineData);
setEnginePreferences(REQ_ENGINE_REGISTRY, reqEngineData);
setEnginePreferences(CONFLICT_DETECTOR_REGISTRY, conflictsDetectorData);
setEnginePreferences(MATCH_ENGINE_REGISTRY, matchEnginesData, DefaultOption.ALL,
StoreOption.DISABLED_ITEMS);
}
/**
* Store the value of the configuration of each engine into preferences.
*/
private void storeConfigurations() {
for (Entry<String, InteractiveUIContent> interactiveContentEntry : interactiveUis.entrySet()) {
for (Entry<String, AbstractConfigurationUI> configuratorEntry : interactiveContentEntry.getValue()
.getConfigurators().entrySet()) {
AbstractConfigurationUI configurator = configuratorEntry.getValue();
configurator.storeConfiguration();
}
}
if (TracingConstant.CONFIGURATION_TRACING_ACTIVATED) {
StringBuilder traceMessage = new StringBuilder("Engines preference serialization:\n"); //$NON-NLS-1$
String prefDelimiter = " :\n"; //$NON-NLS-1$
String newLine = "\n"; //$NON-NLS-1$
traceMessage.append(DIFF_ENGINES).append(prefDelimiter)
.append(getPreferenceStore().getString(DIFF_ENGINES)).append(newLine);
traceMessage.append(EQUI_ENGINES).append(prefDelimiter)
.append(getPreferenceStore().getString(EQUI_ENGINES)).append(newLine);
traceMessage.append(REQ_ENGINES).append(prefDelimiter)
.append(getPreferenceStore().getString(REQ_ENGINES)).append(newLine);
traceMessage.append(CONFLICTS_DETECTOR).append(prefDelimiter)
.append(getPreferenceStore().getString(CONFLICTS_DETECTOR)).append(newLine);
traceMessage.append(MATCH_ENGINE_DISABLE_ENGINES).append(prefDelimiter)
.append(getPreferenceStore().getString(MATCH_ENGINE_DISABLE_ENGINES)).append(newLine);
EMFCompareRCPPlugin.getDefault().log(IStatus.INFO, traceMessage.toString());
}
}
@Override
protected void performDefaults() {
resetDefaultPreferences(DIFF_ENGINE_REGISTRY, diffEngineData);
resetDefaultPreferences(REQ_ENGINE_REGISTRY, reqEngineData);
resetDefaultPreferences(EQUI_ENGINE_REGISTRY, equiEngineData);
resetDefaultPreferences(CONFLICT_DETECTOR_REGISTRY, conflictsDetectorData);
resetDefaultPreferences(MATCH_ENGINE_REGISTRY, matchEnginesData, DefaultOption.ALL,
StoreOption.DISABLED_ITEMS);
resetConfigurations();
super.performDefaults();
}
/**
* Reset all configuration of each engine to its default value.
*/
private void resetConfigurations() {
for (Entry<String, InteractiveUIContent> interactiveContentEntry : interactiveUis.entrySet()) {
for (AbstractConfigurationUI configurator : interactiveContentEntry.getValue().getConfigurators()
.values()) {
configurator.resetDefault();
}
}
}
/**
* Reset engine preference to default using highest rank strategy.
*
* @param registry
* @param preferenceKey
*/
private <T> void resetDefaultPreferences(IItemRegistry<T> registry, DataHolder<T> dataObject) {
resetDefaultPreferences(registry, dataObject, DefaultOption.HIGHEST_RANKED,
StoreOption.ENABLED_ITEMS);
}
/**
* Reset engine preference to default using the specified <code>defaultOption</code>.
*
* @param registry
* @param dataObject
* @param defaultOption
*/
private <T> void resetDefaultPreferences(IItemRegistry<T> registry, DataHolder<T> dataObject,
DefaultOption defaultOption, StoreOption storeOption) {
InteractiveUIContent interactiveContent = interactiveUis.get(preferenceKey(registry));
if (interactiveContent != null) {
Set<IItemDescriptor<T>> defaultEngines = getDefaultDescriptors(registry, defaultOption);
Set<IItemDescriptor<T>> defaultEnginesToSelect = getItemsToSelect(defaultEngines, registry,
storeOption);
interactiveContent.selectAll(defaultEnginesToSelect);
interactiveContent.checkElements(defaultEnginesToSelect);
dataObject.setData(defaultEnginesToSelect);
}
}
/**
* Returns the items to select in the UI.
* <p>
* This is equal to {@link #getItemsToStore(Set, IItemRegistry, StoreOption)}, which considers whether the
* {@link StoreOption#ENABLED_ITEMS} or {@link StoreOption#DISABLED_ITEMS} are stored, except that it
* returns <em>all</em> registered engines from the <code>registry</code> are returned, if
* <code>engines</code> is empty.
* </p>
*
* @param engines
* @param registry
* @param storeOption
* @return
*/
private <T> Set<IItemDescriptor<T>> getItemsToSelect(Set<IItemDescriptor<T>> engines,
IItemRegistry<T> registry, StoreOption storeOption) {
Set<IItemDescriptor<T>> itemsToStore = getItemsToStore(engines, registry, storeOption);
if (StoreOption.DISABLED_ITEMS.equals(storeOption) && itemsToStore.isEmpty()) {
return Sets.newHashSet(registry.getItemDescriptors());
}
return itemsToStore;
}
/**
* Returns the configured default item descriptors or the highest ranked item descriptor if there is no
* default.
*
* @param registry
* The registry to obtain item descriptors from.
* @param defaultOption
* Option specifying what is the default if no other default is pre-configured (all or
* highest-ranked).
* @return The default item descriptors.
*/
private <T> Set<IItemDescriptor<T>> getDefaultDescriptors(IItemRegistry<T> registry,
DefaultOption defaultOption) {
final String defaultValue = getPreferenceStore().getDefaultString(preferenceKey(registry));
final Set<IItemDescriptor<T>> defaultDescriptors = new LinkedHashSet<>();
if (!Strings.isNullOrEmpty(defaultValue)) {
for (String engineId : defaultValue.split(ItemUtil.PREFERENCE_DELIMITER)) {
final IItemDescriptor<T> itemDescriptor = registry.getItemDescriptor(engineId);
if (itemDescriptor != null) {
defaultDescriptors.add(itemDescriptor);
}
}
}
if (defaultDescriptors.isEmpty()) {
switch (defaultOption) {
case ALL:
defaultDescriptors.addAll(registry.getItemDescriptors());
break;
case HIGHEST_RANKED:
// fall through
default:
defaultDescriptors.add(registry.getHighestRankingDescriptor());
break;
}
}
return defaultDescriptors;
}
/**
* Stores the engines specified in <code>data</code> for the items in the given <code>registry</code>.
* <p>
* This method will store the enabled items considering the highest ranked item as the default.
* </p>
*
* @param registry
* The registry.
* @param data
* The data holding the selected items.
*/
private <T> void setEnginePreferences(IItemRegistry<T> registry, DataHolder<T> data) {
setEnginePreferences(registry, data, DefaultOption.HIGHEST_RANKED, StoreOption.ENABLED_ITEMS);
}
/**
* Stores the engines specified in <code>data</code> for the items in the given <code>registry</code>.
*
* @param registry
* The registry.
* @param data
* The data holding the selected items.
* @param defaultOption
* The default option to consider when storing the preferences.
* @param storeOption
* The store option to consider when storing the preferences.
*/
private <T> void setEnginePreferences(IItemRegistry<T> registry, DataHolder<T> data,
DefaultOption defaultOption, StoreOption storeOption) {
Set<IItemDescriptor<T>> selectedEngines = data.getData();
Set<IItemDescriptor<T>> toStore = getItemsToStore(selectedEngines, registry, storeOption);
if (deviatesFromDefaults(toStore, registry, defaultOption, storeOption)) {
StringBuilder descriptorsKey = new StringBuilder();
for (Iterator<IItemDescriptor<T>> iterator = toStore.iterator(); iterator.hasNext();) {
IItemDescriptor<T> iItemDescriptor = iterator.next();
descriptorsKey.append(iItemDescriptor.getID());
if (iterator.hasNext()) {
descriptorsKey.append(ItemUtil.PREFERENCE_DELIMITER);
}
}
getPreferenceStore().setValue(preferenceKey(registry), descriptorsKey.toString());
} else {
getPreferenceStore().setToDefault(preferenceKey(registry));
}
}
/**
* Returns the items to be stored to store <code>selectedEngines</code> considering the given
* <code>storeOption</code>.
*
* @param selectedEngines
* @param registry
* @param storeOption
* @return The items to be stored.
*/
private <T> Set<IItemDescriptor<T>> getItemsToStore(Set<IItemDescriptor<T>> selectedEngines,
IItemRegistry<T> registry, StoreOption storeOption) {
switch (storeOption) {
case DISABLED_ITEMS:
return Sets.difference(Sets.newHashSet(registry.getItemDescriptors()), selectedEngines);
case ENABLED_ITEMS:
// fall through
default:
return selectedEngines;
}
}
/**
* Specifies whether the list <code>itemsToStore</code> are different from the defaults.
* <p>
* This takes into account how the items should be stored, as specified by the <code>storeOptions</code>,
* as well as what the defaults are, as specified by the <code>defaultOptions</code>.
*
* @param itemsToStore
* @param registry
* @param defaultOption
* @param storeOption
* @return <code>true</code> if the items to store are different from the defaults and thus shall be
* stored, or <code>false</code> otherwise.
*/
private <T> boolean deviatesFromDefaults(Set<IItemDescriptor<T>> itemsToStore, IItemRegistry<T> registry,
DefaultOption defaultOption, StoreOption storeOption) {
Set<IItemDescriptor<T>> defaults = getDefaultDescriptors(registry, defaultOption);
return itemsToStore != null && !Sets.symmetricDifference(defaults, itemsToStore).isEmpty();
}
}