/*****************************************************************************
* Copyright (c) 2012 CEA LIST.
*
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.dnd.policy;
import java.text.Collator;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.papyrus.infra.gmfdiag.dnd.Activator;
import org.eclipse.papyrus.infra.gmfdiag.dnd.strategy.DropStrategy;
import org.osgi.service.prefs.BackingStoreException;
/**
* Singleton instance. This class is used to read and manage the various
* DropStrategies: activation, order (priority)
*
*
* @author Camille Letavernier
*
*/
public class DropStrategyManager {
/**
* The drop strategy extension point
*/
public static final String EXTENSION_ID = Activator.PLUGIN_ID + ".dropStrategy";
/**
* The priority property suffix (For preferences)
*/
public static final String PRIORITY_KEY = "priority"; //$NON-NLS-1$
/**
* The isActive property suffix (For preferences)
*/
public static final String IS_ACTIVE_KEY = "isActive"; //$NON-NLS-1$
/**
* The default strategy property suffix (For preferences)
*/
public static final String DEFAULT_STRATEGY_KEY = "defaultStrategy"; //$NON-NLS-1$
/**
* All DropStrategies, defined through an extension point
* The values are grouped by priority.
*
* (Including the DefaultDropStrategy)
*/
private final SortedMap<Integer, List<DropStrategy>> allAvailableStrategies;
/**
* A map to indicate whether each DropStrategy is active
*/
private final Map<DropStrategy, Boolean> activeStrategies;
/**
* The map of default strategies for each known conflicting case
*/
private final Map<Set<DropStrategy>, DropStrategy> defaultStrategies;
/**
* Stores a map of String/Integer (Strategy id / priority)
* and String/Boolean (Strategy id / boolean)
*
* The ids are suffixed by the property name, e.g. :
*
* oep.myStrategy.isActive=true
* oep.myStrategy.priority=12
*/
private final IPreferenceStore preferences;
private DropStrategyManager() {
allAvailableStrategies = new TreeMap<Integer, List<DropStrategy>>();
activeStrategies = new HashMap<DropStrategy, Boolean>();
defaultStrategies = new HashMap<Set<DropStrategy>, DropStrategy>();
preferences = Activator.getDefault().getPreferenceStore();
init();
}
private void init() {
initStrategies(); //Init all available strategies, reads the priorities
initActiveStrategies(); //Reads whether each available strategy is active
initDefaultPreferences(); //Inits the preference store's default values (priority + isActive)
initDefaultDropStrategies(); //Inits the default drop strategies
}
private void initStrategies() {
IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_ID);
//Loads all strategies from the extension point.
for(IConfigurationElement e : config) {
try {
if("strategy".equals(e.getName())) {
DropStrategy strategy = (DropStrategy)e.createExecutableExtension("strategy"); //$NON-NLS-1$
int priority = findPriority(strategy);
getStrategies(priority).add(strategy);
}
} catch (Exception ex) {
Activator.log.error("The plugin " + e.getContributor() + " contributed an invalid extension for " + EXTENSION_ID, ex); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
private void initActiveStrategies() {
for(List<DropStrategy> strategies : allAvailableStrategies.values()) {
for(DropStrategy strategy : strategies) {
activeStrategies.put(strategy, isActive(strategy));
}
}
}
private void initDefaultPreferences() {
for(Collection<DropStrategy> strategies : allAvailableStrategies.values()) {
for(DropStrategy strategy : strategies) {
preferences.setDefault(getPriorityKey(strategy), strategy.getPriority());
preferences.setDefault(getIsActiveKey(strategy), true);
}
}
}
private void initDefaultDropStrategies() {
try {
for(String key : InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID).keys()) {
if(key.endsWith(DEFAULT_STRATEGY_KEY)) {
parseDefaultDropStrategy(key);
}
}
} catch (BackingStoreException ex) {
Activator.log.error("Could not initialize the default Drag & Drop strategy choices", ex);
}
}
public DropStrategy findStrategy(String id) {
for(DropStrategy strategy : getAllStrategies()) {
if(strategy.getID().equals(id)) {
return strategy;
}
}
return null;
}
private void parseDefaultDropStrategy(String key) {
String[] strategyIds = key.substring(0, key.lastIndexOf(":")).split(":");
Set<DropStrategy> strategies = new HashSet<DropStrategy>();
for(String strategyId : strategyIds) {
DropStrategy strategy = findStrategy(strategyId);
if(strategy == null) {
return; //Invalid preference ; skip
}
strategies.add(strategy);
}
if(strategies.size() > 1) {
defaultStrategies.put(strategies, findStrategy(preferences.getString(key)));
}
}
/**
* Returns a collection of all DropStrategy with the given priority.
* Never returns null
*
* @param priority
* @return
*/
private List<DropStrategy> getStrategies(int priority) {
if(!allAvailableStrategies.containsKey(priority)) {
allAvailableStrategies.put(priority, new LinkedList<DropStrategy>());
}
return allAvailableStrategies.get(priority);
}
/**
* Returns a list of all active DropStrategy, ordered by priority. Never returns null.
*
* @return
*/
public List<DropStrategy> getActiveStrategies() {
List<DropStrategy> orderedActiveStrategies = new LinkedList<DropStrategy>();
for(List<DropStrategy> strategies : allAvailableStrategies.values()) {
for(DropStrategy strategy : strategies) {
if(isActive(strategy)) {
orderedActiveStrategies.add(strategy);
}
}
}
return orderedActiveStrategies;
}
/**
* All DropStrategies
* The values are grouped by priority.
*
* (Including the DefaultDropStrategy)
*/
public Map<Integer, List<DropStrategy>> getAllAvailableStrategies() {
return allAvailableStrategies;
}
public static String getPriorityKey(DropStrategy strategy) {
return strategy.getID() + "." + PRIORITY_KEY;
}
public static String getIsActiveKey(DropStrategy strategy) {
return strategy.getID() + "." + IS_ACTIVE_KEY;
}
public static String getDefaultStrategyKey(Collection<DropStrategy> conflict) {
List<DropStrategy> orderedStrategies = new LinkedList<DropStrategy>(conflict);
Collections.sort(orderedStrategies, new Comparator<DropStrategy>() {
public int compare(DropStrategy strategy1, DropStrategy strategy2) {
return Collator.getInstance().compare(strategy1.getID(), strategy2.getID());
}
});
String key = ""; //$NON-NLS-1$
for(DropStrategy strategy : conflict) {
key += strategy.getID() + ":";
}
key += DEFAULT_STRATEGY_KEY;
return key;
}
public int findPriority(DropStrategy strategy) {
String preferenceKey = getPriorityKey(strategy);
if(preferences.contains(preferenceKey)) {
return preferences.getInt(preferenceKey);
}
return strategy.getPriority(); //Default
}
public boolean isActive(DropStrategy strategy) {
String preferenceKey = getIsActiveKey(strategy);
if(preferences.contains(preferenceKey)) {
return preferences.getBoolean(preferenceKey);
}
return true; //Default
}
public void setActive(DropStrategy strategy, boolean active) {
preferences.setValue(getIsActiveKey(strategy), active);
activeStrategies.put(strategy, active);
}
public void setPriority(DropStrategy strategy, int priority) {
//Remove the DropStrategy from its previous priority
getStrategies(findPriority(strategy)).remove(strategy);
//Add it again at the right priority
preferences.setValue(getPriorityKey(strategy), priority);
getStrategies(priority).add(strategy);
}
public static final DropStrategyManager instance = new DropStrategyManager();
/**
* Returns a flat list of all available strategies.
*
* The strategies are ordered by priority
*
* @return
*/
public List<DropStrategy> getAllStrategies() {
List<DropStrategy> result = new LinkedList<DropStrategy>();
for(List<DropStrategy> strategies : allAvailableStrategies.values()) {
result.addAll(strategies);
}
return result;
}
/**
* Restores the default preferences
*/
public void restoreDefaults() {
try {
IEclipsePreferences preferenceStore = InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID);
preferenceStore.clear();
preferenceStore.flush();
} catch (BackingStoreException ex) {
Activator.log.error(ex);
}
activeStrategies.clear();
allAvailableStrategies.clear();
defaultStrategies.clear();
initStrategies();
initActiveStrategies();
initDefaultDropStrategies();
}
/**
* Returns the default drop strategy among the given list, or null if there is
* no default.
*
* @param strategies
* @return
*/
public DropStrategy getDefaultDropStrategy(Collection<DropStrategy> strategies) {
if(strategies.isEmpty()) {
return null;
}
DropStrategy defaultStrategy;
if(strategies.size() == 1) {
defaultStrategy = strategies.iterator().next();
} else {
Set<DropStrategy> conflictingStrategies = new HashSet<DropStrategy>(strategies);
defaultStrategy = defaultStrategies.get(conflictingStrategies);
}
if(defaultStrategy == null) {
return null;
}
return isActive(defaultStrategy) ? defaultStrategy : null;
}
/**
* Sets the default drop strategy for a set of conflicting strategies
*
* @param conflictingStrategies
* @param defaultStrategy
*/
public void setDefaultDropStrategy(Collection<DropStrategy> conflictingStrategies, DropStrategy defaultStrategy) {
if(conflictingStrategies.size() < 2) {
return;
}
Set<DropStrategy> conflict = new HashSet<DropStrategy>(conflictingStrategies);
defaultStrategies.put(conflict, defaultStrategy);
preferences.putValue(getDefaultStrategyKey(conflict), defaultStrategy.getID());
//Save the preferences
IEclipsePreferences preferenceStore = InstanceScope.INSTANCE.getNode(Activator.PLUGIN_ID);
try {
preferenceStore.flush();
} catch (BackingStoreException ex) {
Activator.log.error(ex);
}
}
}