blob: 1ec791d934a5a894ce0df50b7571f173866ec5ac [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2006 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.preferences;
import java.io.*;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.*;
import org.eclipse.core.internal.preferences.exchange.IProductPreferencesService;
import org.eclipse.core.internal.runtime.RuntimeLog;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.preferences.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
/**
* @since 3.0
*/
public class DefaultPreferences extends EclipsePreferences {
// cache which nodes have been loaded from disk
private static Set loadedNodes = new HashSet();
private static final String KEY_PREFIX = "%"; //$NON-NLS-1$
private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$
private static final IPath NL_DIR = new Path("$nl$"); //$NON-NLS-1$
private static final String PROPERTIES_FILE_EXTENSION = "properties"; //$NON-NLS-1$
private static Properties productCustomization;
private static Properties productTranslation;
private static Properties commandLineCustomization;
private EclipsePreferences loadLevel;
// cached values
private String qualifier;
private int segmentCount;
private WeakReference pluginReference;
public static String pluginCustomizationFile = null;
/**
* Default constructor for this class.
*/
public DefaultPreferences() {
this(null, null);
}
private DefaultPreferences(EclipsePreferences parent, String name, Object context) {
this(parent, name);
this.pluginReference = new WeakReference(context);
}
private DefaultPreferences(EclipsePreferences parent, String name) {
super(parent, name);
if (parent instanceof DefaultPreferences)
this.pluginReference = ((DefaultPreferences) parent).pluginReference;
// cache the segment count
String path = absolutePath();
segmentCount = getSegmentCount(path);
if (segmentCount < 2)
return;
// cache the qualifier
qualifier = getSegment(path, 1);
}
/*
* Apply the values set in the bundle's install directory.
*
* In Eclipse 2.1 this is equivalent to:
* /eclipse/plugins/<pluginID>/prefs.ini
*/
private void applyBundleDefaults() {
Bundle bundle = PreferencesOSGiUtils.getDefault().getBundle(name());
if (bundle == null)
return;
URL url = FileLocator.find(bundle, new Path(IPreferencesConstants.PREFERENCES_DEFAULT_OVERRIDE_FILE_NAME), null);
if (url == null) {
if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
PrefsMessages.message("Preference default override file not found for bundle: " + bundle.getSymbolicName()); //$NON-NLS-1$
return;
}
URL transURL = FileLocator.find(bundle, NL_DIR.append(IPreferencesConstants.PREFERENCES_DEFAULT_OVERRIDE_BASE_NAME).addFileExtension(PROPERTIES_FILE_EXTENSION), null);
if (transURL == null && EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
PrefsMessages.message("Preference translation file not found for bundle: " + bundle.getSymbolicName()); //$NON-NLS-1$
applyDefaults(name(), loadProperties(url), loadProperties(transURL));
}
/*
* Apply the default values as specified in the file
* as an argument on the command-line.
*/
private void applyCommandLineDefaults() {
// prime the cache the first time
if (commandLineCustomization == null) {
String filename = pluginCustomizationFile;
if (filename == null) {
if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
PrefsMessages.message("Command-line preferences customization file not specified."); //$NON-NLS-1$
return;
}
if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
PrefsMessages.message("Using command-line preference customization file: " + filename); //$NON-NLS-1$
commandLineCustomization = loadProperties(filename);
}
applyDefaults(null, commandLineCustomization, null);
}
/*
* If the qualifier is null then the file is of the format:
* pluginID/key=value
* otherwise the file is of the format:
* key=value
*/
private void applyDefaults(String id, Properties defaultValues, Properties translations) {
for (Enumeration e = defaultValues.keys(); e.hasMoreElements();) {
String fullKey = (String) e.nextElement();
String value = defaultValues.getProperty(fullKey);
if (value == null)
continue;
IPath childPath = new Path(fullKey);
String key = childPath.lastSegment();
childPath = childPath.removeLastSegments(1);
String localQualifier = id;
if (id == null) {
localQualifier = childPath.segment(0);
childPath = childPath.removeFirstSegments(1);
}
if (name().equals(localQualifier)) {
value = translatePreference(value, translations);
if (EclipsePreferences.DEBUG_PREFERENCE_SET)
PrefsMessages.message("Setting default preference: " + (new Path(absolutePath()).append(childPath).append(key)) + '=' + value); //$NON-NLS-1$
((EclipsePreferences) internalNode(childPath.toString(), false, null)).internalPut(key, value);
}
}
}
public IEclipsePreferences node(String childName, Object context) {
return internalNode(childName, true, context);
}
/*
* Runtime defaults are the ones which are specified in code at runtime.
*
* In the Eclipse 2.1 world they were the ones which were specified in the
* over-ridden Plugin#initializeDefaultPluginPreferences() method.
*
* In Eclipse 3.0 they are set in the code which is indicated by the
* extension to the plug-in default customizer extension point.
*/
private void applyRuntimeDefaults() {
WeakReference ref = PreferencesService.getDefault().applyRuntimeDefaults(name(), pluginReference);
if (ref != null)
pluginReference = ref;
}
/*
* Apply the default values as specified by the file
* in the product extension.
*
* In Eclipse 2.1 this is equivalent to the plugin_customization.ini
* file in the primary feature's plug-in directory.
*/
private void applyProductDefaults() {
// prime the cache the first time
if (productCustomization == null) {
BundleContext context = Activator.getContext();
if (context != null) {
ServiceTracker productTracker = new ServiceTracker(context, IProductPreferencesService.class.getName(), null);
productTracker.open();
IProductPreferencesService productSpecials = (IProductPreferencesService) productTracker.getService();
if (productSpecials != null) {
productCustomization = productSpecials.getProductCustomization();
productTranslation = productSpecials.getProductTranslation();
}
productTracker.close();
} else
PrefsMessages.message("Product-specified preferences called before plugin is started"); //$NON-NLS-1$
}
applyDefaults(null, productCustomization, productTranslation);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#flush()
*/
public void flush() {
// default values are not persisted
}
protected IEclipsePreferences getLoadLevel() {
if (loadLevel == null) {
if (qualifier == null)
return null;
// Make it relative to this node rather than navigating to it from the root.
// Walk backwards up the tree starting at this node.
// This is important to avoid a chicken/egg thing on startup.
EclipsePreferences node = this;
for (int i = 2; i < segmentCount; i++)
node = (EclipsePreferences) node.parent();
loadLevel = node;
}
return loadLevel;
}
protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
return new DefaultPreferences(nodeParent, nodeName, context);
}
protected boolean isAlreadyLoaded(IEclipsePreferences node) {
return loadedNodes.contains(node.name());
}
/* (non-Javadoc)
* @see org.eclipse.core.internal.preferences.EclipsePreferences#load()
*/
protected void load() {
loadDefaults();
}
private void loadDefaults() {
applyRuntimeDefaults();
applyBundleDefaults();
applyProductDefaults();
applyCommandLineDefaults();
}
private Properties loadProperties(URL url) {
Properties result = new Properties();
if (url == null)
return result;
InputStream input = null;
try {
input = url.openStream();
result.load(input);
} catch (IOException e) {
if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL) {
PrefsMessages.message("Problem opening stream to preference customization file: " + url); //$NON-NLS-1$
e.printStackTrace();
}
} finally {
if (input != null)
try {
input.close();
} catch (IOException e) {
// ignore
}
}
return result;
}
private Properties loadProperties(String filename) {
Properties result = new Properties();
InputStream input = null;
try {
input = new BufferedInputStream(new FileInputStream(filename));
result.load(input);
} catch (FileNotFoundException e) {
if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
PrefsMessages.message("Preference customization file not found: " + filename); //$NON-NLS-1$
} catch (IOException e) {
String message = NLS.bind(PrefsMessages.preferences_loadException, filename);
IStatus status = new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
RuntimeLog.log(status);
} finally {
if (input != null)
try {
input.close();
} catch (IOException e) {
// ignore
}
}
return result;
}
protected void loaded() {
loadedNodes.add(name());
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#sync()
*/
public void sync() {
// default values are not persisted
}
/**
* Takes a preference value and a related resource bundle and
* returns the translated version of this value (if one exists).
*/
private String translatePreference(String value, Properties props) {
value = value.trim();
if (props == null || value.startsWith(KEY_DOUBLE_PREFIX))
return value;
if (value.startsWith(KEY_PREFIX)) {
int ix = value.indexOf(" "); //$NON-NLS-1$
String key = ix == -1 ? value.substring(1) : value.substring(1, ix);
String dflt = ix == -1 ? value : value.substring(ix + 1);
return props.getProperty(key, dflt);
}
return value;
}
}