blob: 16ca6f83a5e115e00e48c34271d6828a2edea26a [file] [log] [blame]
/*
* Copyright (c) 2014-2018 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.setup.impl;
import org.eclipse.oomph.internal.setup.SetupPrompter;
import org.eclipse.oomph.preferences.PreferencesFactory;
import org.eclipse.oomph.preferences.util.PreferencesUtil;
import org.eclipse.oomph.preferences.util.PreferencesUtil.PreferenceProperty;
import org.eclipse.oomph.setup.Installation;
import org.eclipse.oomph.setup.PreferenceTask;
import org.eclipse.oomph.setup.SetupPackage;
import org.eclipse.oomph.setup.SetupTask;
import org.eclipse.oomph.setup.SetupTaskContext;
import org.eclipse.oomph.setup.Trigger;
import org.eclipse.oomph.setup.User;
import org.eclipse.oomph.setup.VariableTask;
import org.eclipse.oomph.setup.Workspace;
import org.eclipse.oomph.setup.impl.PreferenceTaskImpl.PreferenceHandler.Factory;
import org.eclipse.oomph.util.OS;
import org.eclipse.oomph.util.ObjectUtil;
import org.eclipse.oomph.util.ReflectUtil;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.oomph.util.UserCallback;
import org.eclipse.oomph.util.XMLUtil;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.BasicEMap;
import org.eclipse.emf.common.util.CommonUtil;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.osgi.service.prefs.Preferences;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.parsers.DocumentBuilder;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <!-- begin-user-doc -->
* An implementation of the model object '<em><b>Eclipse Preference Task</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* </p>
* <ul>
* <li>{@link org.eclipse.oomph.setup.impl.PreferenceTaskImpl#getKey <em>Key</em>}</li>
* <li>{@link org.eclipse.oomph.setup.impl.PreferenceTaskImpl#getValue <em>Value</em>}</li>
* </ul>
*
* @generated
*/
public class PreferenceTaskImpl extends SetupTaskImpl implements PreferenceTask
{
/**
* The default value of the '{@link #getKey() <em>Key</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getKey()
* @generated
* @ordered
*/
protected static final String KEY_EDEFAULT = null;
/**
* The cached value of the '{@link #getKey() <em>Key</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getKey()
* @generated
* @ordered
*/
protected String key = KEY_EDEFAULT;
/**
* The default value of the '{@link #getValue() <em>Value</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getValue()
* @generated
* @ordered
*/
protected static final String VALUE_EDEFAULT = null;
/**
* The cached value of the '{@link #getValue() <em>Value</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getValue()
* @generated
* @ordered
*/
protected String value = VALUE_EDEFAULT;
private transient PreferencesUtil.PreferenceProperty preferenceProperty;
private transient PreferenceHandler preferenceHandler;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
protected PreferenceTaskImpl()
{
super();
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass()
{
return SetupPackage.Literals.PREFERENCE_TASK;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getKey()
{
return key;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setKeyGen(String newKey)
{
String oldKey = key;
key = newKey;
if (eNotificationRequired())
{
eNotify(new ENotificationImpl(this, Notification.SET, SetupPackage.PREFERENCE_TASK__KEY, oldKey, key));
}
}
public void setKey(String newKey)
{
setKeyGen(CommonUtil.intern(newKey));
preferenceHandler = null;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getValue()
{
return value;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setValue(String newValue)
{
String oldValue = value;
value = newValue;
if (eNotificationRequired())
{
eNotify(new ENotificationImpl(this, Notification.SET, SetupPackage.PREFERENCE_TASK__VALUE, oldValue, value));
}
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType)
{
switch (featureID)
{
case SetupPackage.PREFERENCE_TASK__KEY:
return getKey();
case SetupPackage.PREFERENCE_TASK__VALUE:
return getValue();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eSet(int featureID, Object newValue)
{
switch (featureID)
{
case SetupPackage.PREFERENCE_TASK__KEY:
setKey((String)newValue);
return;
case SetupPackage.PREFERENCE_TASK__VALUE:
setValue((String)newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID)
{
switch (featureID)
{
case SetupPackage.PREFERENCE_TASK__KEY:
setKey(KEY_EDEFAULT);
return;
case SetupPackage.PREFERENCE_TASK__VALUE:
setValue(VALUE_EDEFAULT);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public boolean eIsSet(int featureID)
{
switch (featureID)
{
case SetupPackage.PREFERENCE_TASK__KEY:
return KEY_EDEFAULT == null ? key != null : !KEY_EDEFAULT.equals(key);
case SetupPackage.PREFERENCE_TASK__VALUE:
return VALUE_EDEFAULT == null ? value != null : !VALUE_EDEFAULT.equals(value);
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public String toString()
{
if (eIsProxy())
{
return super.toString();
}
StringBuilder result = new StringBuilder(super.toString());
result.append(" (key: ");
result.append(key);
result.append(", value: ");
result.append(value);
result.append(')');
return result.toString();
}
@Override
public int getPriority()
{
return getPreferenceHandler().getPriority();
}
private PreferenceHandler getPreferenceHandler()
{
if (preferenceHandler == null)
{
preferenceHandler = PreferenceHandler.getHandler(getKey());
}
return preferenceHandler;
}
@Override
public Object getOverrideToken()
{
return createToken(getKey());
}
@Override
public void overrideFor(SetupTask overriddenSetupTask)
{
super.overrideFor(overriddenSetupTask);
PreferenceTask overriddenPeferenceTask = (PreferenceTask)overriddenSetupTask;
PreferenceHandler preferenceHandler = getPreferenceHandler();
if (preferenceHandler.isNeeded(overriddenPeferenceTask.getValue(), getValue()))
{
setValue(preferenceHandler.merge());
}
}
@Override
public int getProgressMonitorWork()
{
return 0;
}
public boolean isNeeded(SetupTaskContext context) throws Exception
{
if (StringUtil.isEmpty(key))
{
return false;
}
preferenceProperty = new PreferencesUtil.PreferenceProperty(key);
String oldValue = preferenceProperty.getEffectiveProperty().get(null);
String newValue = getValue();
return getPreferenceHandler().isNeeded(oldValue, newValue);
}
public void perform(SetupTaskContext context) throws Exception
{
final String key = getKey();
// Ignore project-specific preferences for projects that don't exist in the workspace.
URI uri = PreferencesFactory.eINSTANCE.createURI(key);
if ("project".equals(uri.authority()))
{
if (!ResourcesPlugin.getWorkspace().getRoot().getProject(URI.decode(uri.segment(0))).isAccessible())
{
context.log("Ignoring preference " + key + " = " + value);
return;
}
}
final String value = getValue();
if (value != null && value.indexOf('\n') != -1)
{
context.log(value);
}
performUI(context, new RunnableWithContext()
{
public void run(SetupTaskContext context) throws Exception
{
String mergedValue = preferenceHandler.merge();
preferenceProperty.set(mergedValue);
preferenceHandler.apply(context);
}
});
Preferences node = preferenceProperty.getNode();
node.flush();
}
public boolean execute(final UserCallback callback) throws Exception
{
SetupTaskContext context = new SetupTaskContext()
{
public boolean isCanceled()
{
return false;
}
public void log(String line)
{
// Do nothing.
}
public void log(String line, Severity severity)
{
// Do nothing.
}
public void log(String line, boolean filter)
{
// Do nothing.
}
public void log(String line, boolean filter, Severity severity)
{
// Do nothing.
}
public void log(IStatus status)
{
// Do nothing.
}
public void log(Throwable t)
{
// Do nothing.
}
public void task(SetupTask setupTask)
{
// Do nothing.
}
public void setTerminating()
{
// Do nothing.
}
public IProgressMonitor getProgressMonitor(boolean working)
{
return new NullProgressMonitor();
}
public SetupPrompter getPrompter()
{
return new SetupPrompter()
{
public OS getOS()
{
return OS.INSTANCE;
}
public String getVMPath()
{
return null;
}
public UserCallback getUserCallback()
{
return callback;
}
public String getValue(VariableTask variable)
{
throw new UnsupportedOperationException();
}
public boolean promptVariables(List<? extends SetupTaskContext> performers)
{
throw new UnsupportedOperationException();
}
};
}
public Trigger getTrigger()
{
throw new UnsupportedOperationException();
}
public void checkCancelation()
{
// Do nothing.
}
public boolean isSelfHosting()
{
throw new UnsupportedOperationException();
}
public boolean isPerforming()
{
throw new UnsupportedOperationException();
}
public boolean isOffline()
{
throw new UnsupportedOperationException();
}
public boolean isMirrors()
{
throw new UnsupportedOperationException();
}
public boolean isRestartNeeded()
{
throw new UnsupportedOperationException();
}
public void setRestartNeeded(String reason)
{
// Do nothing.
}
public User getUser()
{
throw new UnsupportedOperationException();
}
public Workspace getWorkspace()
{
throw new UnsupportedOperationException();
}
public Installation getInstallation()
{
throw new UnsupportedOperationException();
}
public File getInstallationLocation()
{
throw new UnsupportedOperationException();
}
public File getProductLocation()
{
throw new UnsupportedOperationException();
}
public File getProductConfigurationLocation()
{
throw new UnsupportedOperationException();
}
public File getWorkspaceLocation()
{
throw new UnsupportedOperationException();
}
public String getRelativeProductFolder()
{
throw new UnsupportedOperationException();
}
public OS getOS()
{
throw new UnsupportedOperationException();
}
public URIConverter getURIConverter()
{
throw new UnsupportedOperationException();
}
public URI redirect(URI uri)
{
return uri;
}
public String redirect(String uri)
{
return uri;
}
public Object get(Object key)
{
return null;
}
public Object put(Object key, Object value)
{
throw new UnsupportedOperationException();
}
public Set<Object> keySet()
{
return Collections.emptySet();
}
public String getLauncherName()
{
throw new UnsupportedOperationException();
}
public boolean matchesFilterContext(String filter)
{
return false;
}
};
if (isNeeded(context))
{
perform(context);
return true;
}
return false;
}
/**
* Pathological list preferences:
* <ul>
* <li> //instance/org.eclipse.ui.browser/browsers (order matters for //instance/org.eclipse.ui.browser/browser-choice)
* <li> //instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.taskTags (order matters for //instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.taskPriorities)
* <li> //instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.typefilter.disabled (enabling an item does not remove the item)
* <li> //instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.typefilter.enabled (disabling an item does not remove the item)
* </ul>
*
* @author Ed Merks
*/
public static class PreferenceHandler
{
protected URI key;
protected String oldValue;
protected String newValue;
public PreferenceHandler(URI key)
{
this.key = key;
}
public int getPriority()
{
return key != null && "project".equals(key.authority()) ? PRIORITY_LATE : PRIORITY_EARLY;
}
public boolean isIgnored()
{
return false;
}
public final boolean isNeeded(String oldValue, String newValue)
{
this.oldValue = oldValue;
this.newValue = newValue;
return isNeeded();
}
protected boolean isNeeded()
{
if (isExcluded())
{
return false;
}
String mergedValue = merge();
return !ObjectUtil.equals(mergedValue, oldValue);
}
protected boolean isExcluded()
{
// If the old value is an XML blob...
if (oldValue != null && oldValue.startsWith("<?xml "))
{
try
{
DocumentBuilder documentBuilder = XMLUtil.createDocumentBuilder();
documentBuilder.setErrorHandler(new ErrorHandler()
{
public void warning(SAXParseException exception) throws SAXException
{
}
public void fatalError(SAXParseException exception) throws SAXException
{
throw exception;
}
public void error(SAXParseException exception) throws SAXException
{
}
});
Document document = documentBuilder.parse(new InputSource(new StringReader(oldValue)));
NodeList childNodes = document.getDocumentElement().getChildNodes();
for (int i = 0, length = childNodes.getLength(); i < length; ++i)
{
Node item = childNodes.item(i);
String nodeValue = item.getNodeValue();
// If there are any children that aren't simply whitespace content, then it's non-empty XML.
if (nodeValue == null || nodeValue.trim().length() != 0)
{
return true;
}
}
}
catch (Throwable throwable)
{
//$FALL-THROUGH$
}
}
return false;
}
public String merge()
{
return newValue;
}
public String delta()
{
return newValue;
}
public void apply(SetupTaskContext context)
{
}
public static void main(String[] args)
{
PreferenceHandlerFactoryRegistry uriMappingRegistryImpl = (PreferenceHandlerFactoryRegistry)Factory.Registry.INSTANCE;
uriMappingRegistryImpl.put(URI.createURI("//"), null);
uriMappingRegistryImpl.put(URI.createURI("///"), null);
uriMappingRegistryImpl.put(URI.createURI("//instance/foo/bar/"), null);
uriMappingRegistryImpl.put(URI.createURI("//instance"), new Factory()
{
public PreferenceHandler create(URI key)
{
return null;
}
});
uriMappingRegistryImpl.getFactory(URI.createURI("//instance/foo/bar/myproperty"));
uriMappingRegistryImpl.getFactory(URI.createURI("//instance/fudge/bar/myproperty"));
{
PreferenceHandler handler = new ListPreferenceHandler(URI.createURI(""), "(?s)([^\\n]+\\n[0-9]+)", "([^\n]+)", "\n");
if (handler.isNeeded("a\n2\n", "b\n2\na\n1\n"))
{
String result = handler.merge();
System.out.println(result);
}
else
{
System.out.println("has conflict");
}
}
{
PreferenceHandler handler = new ListPreferenceHandler(URI.createURI(""), "(?s)([^\\n]+\\n[0-9]+)", "([^ \n]+) ([^\n]+)", "\n");
if (handler.isNeeded("a b\n2\n", "a b\n3\nb a\n2\na a\n1\n"))
{
String result = handler.merge();
System.out.println(result);
}
else
{
System.out.println("has conflict");
}
}
}
public static PreferenceHandler getHandler(String key)
{
URI keyURI = PreferencesFactory.eINSTANCE.createURI(key);
return getHandler(keyURI);
}
public static PreferenceHandler getHandler(URI key)
{
Factory factory = Factory.Registry.INSTANCE.getFactory(key);
if (factory == null)
{
return new PreferenceHandler(key);
}
return factory.create(key);
}
protected static void registerListHandlerFactory(String key, char itemSeparator, char keySeparator)
{
final String itemSeparatorStr = new String(new char[] { itemSeparator });
final String itemPattern = "([^" + itemSeparatorStr + "]+)";
final String keyPattern = keySeparator == (char)0 ? "(.*)" : "([^" + new String(new char[] { keySeparator }) + "]+)";
PreferenceHandlerFactoryRegistry registry = (PreferenceHandlerFactoryRegistry)Factory.Registry.INSTANCE;
registry.put(URI.createURI(key), new Factory()
{
public PreferenceHandler create(URI key)
{
return new ListPreferenceHandler(key, itemPattern, keyPattern, itemSeparatorStr);
}
});
}
protected static void registerListHandlerFactory(String key, char itemSeparator)
{
registerListHandlerFactory(key, itemSeparator, (char)0);
}
static
{
PreferenceHandlerFactoryRegistry registry = (PreferenceHandlerFactoryRegistry)Factory.Registry.INSTANCE;
registry.put(URI.createURI("//"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new PreferenceHandler(key);
}
});
registerListHandlerFactory("//instance/org.eclipse.ui.workbench/ENABLED_DECORATORS", ',', ':');
registerListHandlerFactory("//instance/org.eclipse.ui.navigator/org.eclipse.ui.navigator.ProjectExplorer.activatedExtensions", ';', '=');
registerListHandlerFactory("//instance/org.eclipse.ui.navigator/org.eclipse.ui.navigator.ProjectExplorer.filterActivation", ':');
// See Javadoc on PreferenceHandler:
// registerListHandlerFactory("//instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.typefilter.disabled", ';');
// registerListHandlerFactory("//instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.typefilter.enabled", ';');
registry.put(URI.createURI("//instance/org.eclipse.ui.workbench/"), new Factory()
{
public PreferenceHandler create(URI key)
{
String lastSegment = key.lastSegment();
if ("editors".equals(lastSegment))
{
return new IgnoredPreferenceHandler(key);
}
if ("resourcetypes".equals(lastSegment))
{
return new XMLPreferenceHandler(key, "info", new String[] { "extension", "name" }, null);
}
if ("org.eclipse.ui.commands".equals(lastSegment))
{
return new PreferenceHandler(key)
{
@Override
protected boolean isExcluded()
{
return false;
}
};
}
return new PreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.core.runtime/content-types/"), new Factory()
{
public PreferenceHandler create(URI key)
{
if (key.segmentCount() == 4)
{
String property = key.lastSegment();
if (property.equals("file-extensions") || property.equals("file-names"))
{
return new ContentTypeFileExtensionPreferenceHandler(key);
}
if (property.equals("charset"))
{
return new ContentTypeCharsetPreferenceHandler(key);
}
}
return new PreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.core/"), new Factory()
{
public PreferenceHandler create(URI key)
{
String lastSegment = key.lastSegment();
if (lastSegment.startsWith("org.eclipse.jdt.core.formatter."))
{
return new IgnoredPreferenceHandler(key);
}
return new PreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/"), new Factory()
{
public PreferenceHandler create(URI key)
{
String lastSegment = key.lastSegment();
if (lastSegment.startsWith("cleanup."))
{
return new PreferenceHandler(key)
{
@Override
public boolean isIgnored()
{
return true;
}
};
}
return new PreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.formatterprofiles"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "profile", new String[] { "name" }, null);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/formatter_profile"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new JDTProfileChoicePreferenceHandler(key, "formatter");
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.cleanupprofiles"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "profile", new String[] { "name" }, null);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/cleanup_profile"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new JDTProfileChoicePreferenceHandler(key, "cleanup");
}
});
registry.put(URI.createURI("//instance/org.eclipse.team.core/file_types"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new TeamFileModePreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.team.core/cvs_mode_for_file_without_extensions"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new TeamFileModePreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.text.custom_templates"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "template", new String[] { "id" }, null);
}
});
registry.put(URI.createURI("//instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui.text.custom_code_templates"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "template", new String[] { "context", "description", "name" }, null);
}
});
registry.put(URI.createURI("//instance/org.eclipse.ant.ui/org.eclipse.ant.ui.customtemplates"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "template", new String[] { "context", "description", "name" }, new String[] { "id" });
}
});
registry.put(URI.createURI("//instance/org.eclipse.core.variables/org.eclipse.core.variables.valueVariables"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "valueVariable", new String[] { "name" }, null);
}
});
registry.put(URI.createURI("//instance/org.eclipse.m2e.editor.xml/org.eclipse.m2e.editor.xml.templates"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "template", new String[] { "context", "description", "name" }, new String[] { "id" });
}
});
registry.put(URI.createURI("//instance/org.eclipse.cdt.ui/org.eclipse.cdt.ui.formatterprofiles"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new XMLPreferenceHandler(key, "profile", new String[] { "name" }, null);
}
});
registry.put(URI.createURI("//instance/org.eclipse.cdt.ui/formatter_profile"), new Factory()
{
public PreferenceHandler create(URI key)
{
return new CDTProfileChoicePreferenceHandler(key);
}
});
registry.put(URI.createURI("//instance/org.eclipse.cdt.core/"), new Factory()
{
public PreferenceHandler create(URI key)
{
String lastSegment = key.lastSegment();
if (lastSegment.startsWith("org.eclipse.cdt.core.formatter."))
{
return new IgnoredPreferenceHandler(key);
}
return new PreferenceHandler(key);
}
});
}
/**
* @author Ed Merks
*/
public interface Factory
{
public PreferenceHandler create(URI key);
/**
* @author Ed Merks
*/
public interface Registry
{
public Registry INSTANCE = new PreferenceHandlerFactoryRegistry();
public Factory getFactory(URI key);
}
}
}
/**
* @author Ed Merks
*/
public static class IgnoredPreferenceHandler extends PreferenceHandler
{
public IgnoredPreferenceHandler(URI key)
{
super(key);
}
@Override
public boolean isIgnored()
{
return true;
}
}
/**
* @author Ed Merks
*/
public static class ListPreferenceHandler extends PreferenceHandler
{
protected final Pattern itemPattern;
protected final Pattern keyPattern;
protected final String separator;
protected final Set<String> changedItems = new LinkedHashSet<String>();
protected final Map<String, Item> oldItems = new LinkedHashMap<String, Item>();
protected final Map<String, Item> newItems = new LinkedHashMap<String, Item>();
protected final Map<String, Item> mergedItems = new LinkedHashMap<String, Item>();
private boolean keyCollision;
public ListPreferenceHandler(URI key, String itemPattern, String keyPattern, String separator)
{
super(key);
this.itemPattern = Pattern.compile(itemPattern);
this.keyPattern = Pattern.compile(keyPattern);
this.separator = separator;
}
@Override
protected boolean isExcluded()
{
return false;
}
@Override
protected boolean isNeeded()
{
return super.isNeeded() && !keyCollision;
}
@Override
public String merge()
{
keyCollision = false;
changedItems.clear();
oldItems.clear();
newItems.clear();
mergedItems.clear();
oldItems.putAll(getItems(oldValue));
newItems.putAll(getItems(newValue));
if (oldItems.isEmpty())
{
changedItems.addAll(newItems.keySet());
mergedItems.putAll(newItems);
return newValue;
}
mergedItems.putAll(oldItems);
for (Map.Entry<String, Item> entry : newItems.entrySet())
{
String entryKey = entry.getKey();
Item newItem = entry.getValue();
Item oldItem = mergedItems.put(entryKey, newItem);
if (oldItem == null || !oldItem.getValue().equals(newItem.getValue()))
{
changedItems.add(entryKey);
}
}
List<Item> oldItemValues = new ArrayList<Item>(oldItems.values());
StringBuilder result = new StringBuilder(oldItemValues.get(0).getHead());
for (Iterator<Item> it = mergedItems.values().iterator(); it.hasNext();)
{
Item item = it.next();
result.append(item.getValue());
if (it.hasNext())
{
result.append(separator);
}
}
result.append(oldItemValues.get(oldItemValues.size() - 1).getTail());
return result.toString();
}
@Override
public String delta()
{
merge();
if (oldItems.isEmpty())
{
return newValue;
}
List<Item> itemValues = new ArrayList<Item>(oldItems.values());
StringBuilder result = new StringBuilder(itemValues.get(0).getHead());
for (Iterator<String> it = changedItems.iterator(); it.hasNext();)
{
Item item = mergedItems.get(it.next());
result.append(item.getValue());
if (it.hasNext())
{
result.append(separator);
}
}
result.append(itemValues.get(itemValues.size() - 1).getTail());
return result.toString();
}
private Map<String, Item> getItems(String value)
{
Map<String, Item> items = new LinkedHashMap<String, Item>();
if (value != null)
{
for (Matcher matcher = itemPattern.matcher(value); matcher.find();)
{
String group = matcher.group(1);
Matcher keyMatcher = keyPattern.matcher(group);
if (keyMatcher.find())
{
StringBuilder key = new StringBuilder();
for (int i = 1, count = keyMatcher.groupCount(); i <= count; ++i)
{
if (i > 1)
{
key.append("_._");
}
key.append(keyMatcher.group(i));
}
Item oldItem = items.put(key.toString(), new Item(value, matcher.start(1), matcher.end(1)));
if (oldItem != null)
{
keyCollision = true;
}
}
}
}
return items;
}
/**
* @author Ed Merks
*/
public static final class Item
{
private final String value;
private final int start;
private final int end;
public Item(String value, int start, int end)
{
this.value = value;
this.start = start;
this.end = end;
}
public String getValue()
{
return value.substring(start, end);
}
public String getHead()
{
return value.substring(0, start);
}
public String getTail()
{
return value.substring(end);
}
@Override
public String toString()
{
return getValue();
}
}
}
/**
* @author Ed Merks
*/
public static class XMLPreferenceHandler extends ListPreferenceHandler
{
public XMLPreferenceHandler(URI key, String tag, String[] requiredAttributes, String[] optionalAttributes)
{
super(key, getItemPattern(tag), getKeyPattern(requiredAttributes, optionalAttributes), "\n");
}
private static String getItemPattern(String tag)
{
return "(?s)(<" + tag + " (?:[^>]*/>|.*?</" + tag + ">))";
}
private static String getKeyPattern(String[] requiredAttributes, String[] optionalAttributes)
{
Set<String> attributes = new TreeSet<String>(Arrays.asList(requiredAttributes));
List<String> optional = optionalAttributes == null ? Collections.<String> emptyList() : Arrays.asList(optionalAttributes);
attributes.addAll(optional);
StringBuilder result = new StringBuilder();
for (String attribute : attributes)
{
int length = result.length();
boolean isOptional = optional.contains(attribute);
if (isOptional)
{
result.append("(?:");
}
if (length != 0)
{
result.append("[^>]+");
}
result.append(attribute);
result.append("=\"([^\"]+)\"");
if (isOptional)
{
result.append(")?");
}
}
return result.toString();
}
}
/**
* @author Ed Merks
*/
public static class TeamFileModePreferenceHandler extends ListPreferenceHandler
{
protected static final Set<String> DEFAULT_NAME_MAPPINGS = new LinkedHashSet<String>();
protected static final Set<String> DEFAULT_EXTENSION_MAPPINGS = new LinkedHashSet<String>();
protected static final Object FILE_CONTENT_MANAGER;
protected boolean isExtension;
static
{
Object fileContentManager = null;
try
{
Class<?> teamClass = CommonPlugin.loadClass("org.eclipse.team.core", "org.eclipse.team.core.Team");
fileContentManager = ReflectUtil.invokeMethod("getFileContentManager", teamClass);
Object[] defaultNameMappings = (Object[])ReflectUtil.invokeMethod("getDefaultNameMappings", fileContentManager);
populateMappings(DEFAULT_NAME_MAPPINGS, defaultNameMappings);
Object[] defaultExtensionMappings = (Object[])ReflectUtil.invokeMethod("getDefaultExtensionMappings", fileContentManager);
populateMappings(DEFAULT_EXTENSION_MAPPINGS, defaultExtensionMappings);
}
catch (Throwable throwable)
{
//$FALL-THROUGH$
}
FILE_CONTENT_MANAGER = fileContentManager;
}
public TeamFileModePreferenceHandler(URI key)
{
super(key, "(?s)([^\\n]+\\n[0-9]+)", "([^\n]+)", "\n");
isExtension = "file_types".equals(key.lastSegment());
}
@Override
public String delta()
{
if (StringUtil.isEmpty(oldValue))
{
StringBuilder result = new StringBuilder(super.delta());
for (String mapping : isExtension ? DEFAULT_EXTENSION_MAPPINGS : DEFAULT_NAME_MAPPINGS)
{
int index = result.indexOf(mapping);
if (index == 0 || index > 0 && result.charAt(index - 1) == '\n')
{
result.delete(index, index + mapping.length());
}
}
return result.toString();
}
return super.delta();
}
@Override
public void apply(SetupTaskContext context)
{
if (FILE_CONTENT_MANAGER != null)
{
int size = changedItems.size();
String[] names = new String[size];
int[] types = new int[size];
int index = 0;
for (String changedItem : changedItems)
{
names[index] = changedItem;
String value = mergedItems.get(changedItem).getValue();
int offset = value.indexOf('\n');
types[index] = Integer.parseInt(value.substring(offset + 1));
++index;
}
ReflectUtil.invokeMethod(
ReflectUtil.getMethod(FILE_CONTENT_MANAGER.getClass(), isExtension ? "addExtensionMappings" : "addNameMappings", String[].class, int[].class),
FILE_CONTENT_MANAGER, names, types);
}
}
private static void populateMappings(Set<String> target, Object[] mappings)
{
for (Object mapping : mappings)
{
Object string = ReflectUtil.invokeMethod("getString", mapping);
int type = (Integer)ReflectUtil.invokeMethod("getType", mapping);
target.add(string + "\n" + type + "\n");
}
}
}
/**
* @author Ed Merks
*/
public static class ContentTypeFileExtensionPreferenceHandler extends ListPreferenceHandler
{
private final int type;
public ContentTypeFileExtensionPreferenceHandler(URI key)
{
super(key, "([^,]+)", "([^,]+)", ",");
type = "file-extensions".equals(key.lastSegment()) ? IContentType.FILE_EXTENSION_SPEC : IContentType.FILE_NAME_SPEC;
}
@Override
public void apply(SetupTaskContext context)
{
String contentTypeName = key.segment(key.segmentCount() - 2);
IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeName);
if (contentType != null)
{
for (String key : changedItems)
{
try
{
contentType.addFileSpec(key, type);
}
catch (CoreException ex)
{
context.log(ex);
}
}
}
}
}
/**
* @author Ed Merks
*/
public static class ContentTypeCharsetPreferenceHandler extends PreferenceHandler
{
public ContentTypeCharsetPreferenceHandler(URI key)
{
super(key);
}
@Override
public void apply(SetupTaskContext context)
{
String contentTypeName = key.segment(key.segmentCount() - 2);
IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeName);
if (contentType != null)
{
try
{
contentType.setDefaultCharset(newValue);
}
catch (CoreException ex)
{
context.log(ex);
}
}
}
}
/**
* @author Ed Merks
*/
public static abstract class ProfileChoicePreferenceHandler extends PreferenceHandler
{
private static final Pattern SETTING_PATTERN = Pattern.compile("<setting id=\"([^\"]+)\" value=\"([^\"]+)\"");
public ProfileChoicePreferenceHandler(URI key)
{
super(key);
}
@Override
public int getPriority()
{
return PRIORITY_EARLY + 1;
}
protected abstract PreferenceProperty getProfilesPreferenceProperty();
protected abstract PreferenceProperty getKeyPreferenceProperty(String propertyKey);
@Override
public void apply(SetupTaskContext context)
{
PreferenceProperty profiles = getProfilesPreferenceProperty();
Pattern pattern = Pattern.compile("(?s)(<profile[^\n]*name=\"" + newValue.substring(1) + "\".*?</profile>)");
String value = profiles.get(null);
if (value != null)
{
Matcher matcher = pattern.matcher(value);
if (matcher.find())
{
String profile = matcher.group(1);
for (Matcher settingMatcher = SETTING_PATTERN.matcher(profile); settingMatcher.find();)
{
String propertyKey = settingMatcher.group(1);
String propertyValue = settingMatcher.group(2);
PreferenceProperty property = getKeyPreferenceProperty(propertyKey);
property.set(propertyValue);
}
}
}
}
}
public static class JDTProfileChoicePreferenceHandler extends ProfileChoicePreferenceHandler
{
private String profileType;
public JDTProfileChoicePreferenceHandler(URI key, String profileType)
{
super(key);
this.profileType = profileType;
}
@Override
protected PreferenceProperty getProfilesPreferenceProperty()
{
return new PreferencesUtil.PreferenceProperty("/instance/org.eclipse.jdt.ui/org.eclipse.jdt.ui." + profileType + "profiles");
}
@Override
protected PreferenceProperty getKeyPreferenceProperty(String propertyKey)
{
return new PreferencesUtil.PreferenceProperty("/instance/org.eclipse.jdt.core/" + propertyKey);
}
}
public static class CDTProfileChoicePreferenceHandler extends ProfileChoicePreferenceHandler
{
public CDTProfileChoicePreferenceHandler(URI key)
{
super(key);
}
@Override
protected PreferenceProperty getProfilesPreferenceProperty()
{
return new PreferencesUtil.PreferenceProperty("/instance/org.eclipse.cdt.ui/org.eclipse.cdt.ui.formatterprofiles");
}
@Override
protected PreferenceProperty getKeyPreferenceProperty(String propertyKey)
{
return new PreferencesUtil.PreferenceProperty("/instance/org.eclipse.cdt.core/" + propertyKey);
}
}
/**
* @author Ed Merks
*/
public static class PreferenceHandlerFactoryRegistry extends BasicEMap<URI, PreferenceHandler.Factory> implements PreferenceHandler.Factory.Registry
{
private static final URI ROOT_PREFIX = URI.createURI("//");
private static final long serialVersionUID = 1L;
protected BasicEList<List<Map.Entry<URI, PreferenceHandler.Factory>>> prefixMaps;
public PreferenceHandlerFactoryRegistry()
{
super();
}
@Override
protected Entry<URI, PreferenceHandler.Factory> newEntry(int hash, URI key, PreferenceHandler.Factory value)
{
validateKey(key);
validateValue(value);
return new MappingEntryImpl(hash, key, value);
}
/**
* @author Ed Merks
*/
protected class MappingEntryImpl extends EntryImpl
{
public boolean isPrefixMapEntry;
public MappingEntryImpl(int hash, URI key, PreferenceHandler.Factory value)
{
super(hash, key, value);
determineEntryType();
}
public void determineEntryType()
{
isPrefixMapEntry = "".equals(key.authority()) ? key.segmentCount() == 0
: key.segmentCount() == 0 ? key.hasAbsolutePath() : key.hasTrailingPathSeparator();
}
}
public PreferenceHandler.Factory getFactory(URI uri)
{
PreferenceHandler.Factory result = get(uri);
if (result == null && prefixMaps != null && uri != null)
{
for (int i = Math.min(prefixMaps.size() - 1, uri.segmentCount()); i >= 0; --i)
{
List<Map.Entry<URI, PreferenceHandler.Factory>> prefixes = prefixMaps.get(i);
LOOP: for (int j = prefixes.size() - 1; j >= 0; --j)
{
Map.Entry<URI, PreferenceHandler.Factory> entry = prefixes.get(j);
URI key = entry.getKey();
if (ROOT_PREFIX.equals(key))
{
return entry.getValue();
}
for (int k = key.segmentCount() - 2; k >= 0; --k)
{
if (!key.segment(k).equals(uri.segment(k)))
{
continue LOOP;
}
}
if (key.authority().equals(uri.authority()))
{
return entry.getValue();
}
}
}
}
return result;
}
@Override
protected void validateKey(URI key)
{
if (key == null)
{
throw new RuntimeException("Key may not be null");
}
if (!key.isHierarchical())
{
throw new RuntimeException("Key is not hierarchical " + key);
}
if (key.authority() == null)
{
throw new RuntimeException("Key has no authority " + key);
}
if (key.scheme() != null)
{
throw new RuntimeException("Key should not have a scheme " + key);
}
}
@Override
protected void validateValue(Factory value)
{
if (value == null)
{
throw new RuntimeException("Value may not be null");
}
}
@Override
protected void didAdd(Entry<URI, PreferenceHandler.Factory> entry)
{
if (((MappingEntryImpl)entry).isPrefixMapEntry)
{
int length = entry.getKey().segmentCount();
if (prefixMaps == null)
{
prefixMaps = new BasicEList<List<Map.Entry<URI, PreferenceHandler.Factory>>>();
}
for (int i = prefixMaps.size() - 1; i <= length; ++i)
{
prefixMaps.add(new BasicEList<Map.Entry<URI, PreferenceHandler.Factory>>());
}
prefixMaps.get(length).add(entry);
}
}
@Override
protected void didModify(Entry<URI, PreferenceHandler.Factory> entry, PreferenceHandler.Factory oldValue)
{
didRemove(entry);
((MappingEntryImpl)entry).determineEntryType();
didAdd(entry);
}
@Override
protected void didRemove(Entry<URI, PreferenceHandler.Factory> entry)
{
if (((MappingEntryImpl)entry).isPrefixMapEntry)
{
int length = entry.getKey().segmentCount();
prefixMaps.get(length).remove(entry);
}
}
@Override
protected void didClear(BasicEList<Entry<URI, PreferenceHandler.Factory>>[] oldEntryData)
{
prefixMaps = null;
}
}
} // PreferenceTaskImpl