blob: 0e2d4bde26e6a3f2d1d402bba84621baafc8ac82 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 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.update.internal.configurator.branding;
import java.io.*;
import java.net.*;
import java.text.MessageFormat; // Can't use ICU, possible launch problem?
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.update.internal.configurator.Messages;
import org.eclipse.update.internal.configurator.Utils;
import org.osgi.framework.*;
/**
* Reads the information found in an "INI" file. This file must be in a
* standard Java properties format. A properties file may also be provided
* to NL values in the INI file - values must start with the % prefix. A
* mapping file may also be provided that contains "fill-ins" for the
* properties file - format being "n = some text", where n is a number.
*/
public class IniFileReader {
private static final String PID = "org.eclipse.update.configurator"; //$NON-NLS-1$
private static final Status OK_STATUS = new Status(IStatus.OK, PID, 0, "", null); //$NON-NLS-1$
private static final String KEY_PREFIX = "%"; //$NON-NLS-1$
private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$
private static final String NLS_TAG = "$nl$"; //$NON-NLS-1$
private String featureId;
private String pluginId;
private String iniFilename;
private String propertiesFilename;
private String mappingsFilename;
private Properties ini = null;
private PropertyResourceBundle properties = null;
private String[] mappings = null;
private Bundle bundle;
/**
* Creates an INI file reader that can parse the contents into key,value pairs.
*
* @param featureId the unique identifier of the feature, must not be <code>null</code>
* @param pluginId the unique identifier of the feature plug-in, must not be <code>null</code>
* @param iniFilename the INI file name, must not be <code>null</code>
* @param propertiesFilename the properties filename, can be <code>null</code> if not required
* @param mappingsFilename the mappings filename, can be <code>null</code> if not required
*/
public IniFileReader(String featureId, String pluginId, String iniFilename, String propertiesFilename, String mappingsFilename) {
super();
if (featureId == null || pluginId == null || iniFilename == null) {
throw new IllegalArgumentException();
}
this.featureId = featureId;
this.pluginId = pluginId;
this.iniFilename = iniFilename;
this.propertiesFilename = propertiesFilename;
this.mappingsFilename = mappingsFilename;
}
/**
* Read the contents of the INI, properties, and mappings files.
* Does nothing if the content has already been read and parsed.
*
* @return an <code>IStatus</code> indicating the success or failure
* of reading and parsing the INI file content
*/
public IStatus load() {
if (ini != null)
return OK_STATUS;
// attempt to locate the corresponding plugin
bundle = Utils.getBundle(pluginId);
if (bundle == null || bundle.getState() == Bundle.UNINSTALLED || bundle.getState() == Bundle.INSTALLED) {
bundle = null; // make it null for other test down the road
String message = NLS.bind(Messages.IniFileReader_MissingDesc, (new String[] { featureId }));
return new Status(IStatus.ERROR, PID, 0, message, null);
}
// Determine the ini file location
URL iniURL = null;
IOException ioe = null;
iniURL = FileLocator.find(bundle, new Path(NLS_TAG).append(iniFilename), null);
if (iniURL == null) {
String message = NLS.bind(Messages.IniFileReader_OpenINIError, (new String[] { iniFilename }));
return new Status(IStatus.ERROR, PID, 0, message, ioe);
}
// Determine the properties file location
URL propertiesURL = null;
if (propertiesFilename != null & propertiesFilename.length() > 0) {
propertiesURL = FileLocator.find(bundle, new Path(NLS_TAG).append(propertiesFilename), null);
}
// Determine the mappings file location
URL mappingsURL = null;
if (mappingsFilename != null && mappingsFilename.length() > 0) {
mappingsURL = FileLocator.find(bundle, new Path(NLS_TAG).append(mappingsFilename), null);
}
// OK to pass null properties and/or mapping file
return load(iniURL, propertiesURL, mappingsURL);
}
/**
* Returns the string value for the given key, or <code>null</code>.
* The string value is NLS if requested.
*
* @return the string value for the given key, or <code>null</code>
*/
public String getString(String key, boolean doNls, Hashtable runtimeMappings) {
if (ini == null)
return null;
String value = ini.getProperty(key);
if (value != null && doNls)
return getResourceString(value, runtimeMappings);
return value;
}
/**
* Returns a URL for the given key, or <code>null</code>.
*
* @return a URL for the given key, or <code>null</code>
*/
public URL getURL(String key) {
if (ini == null)
return null;
URL url = null;
String fileName = ini.getProperty(key);
if (fileName != null) {
if (bundle == null)
return null;
url = FileLocator.find(bundle, new Path(fileName), null);
}
return url;
}
/**
* Returns a array of URL for the given key, or <code>null</code>. The
* property value should be a comma separated list of urls, tokens for
* which bundle cannot build an url will have a null entry.
*
* @param key name of the property containing the requested urls
* @return a URL for the given key, or <code>null</code>
* @since 3.0
*/
public URL[] getURLs(String key) {
if (ini == null || bundle == null)
return null;
String value = ini.getProperty(key);
if (value == null)
return null;
StringTokenizer tokens = new StringTokenizer(value, ","); //$NON-NLS-1$
ArrayList array = new ArrayList(10);
while (tokens.hasMoreTokens()) {
String str = tokens.nextToken().trim();
array.add(FileLocator.find(bundle, new Path(str), null));
}
URL[] urls = new URL[array.size()];
array.toArray(urls);
return urls;
}
/**
* Returns the feature plugin label, or <code>null</code>.
*
* @return the feature plugin lable, or <code>null</code> if none.
*/
public String getFeaturePluginLabel() {
if (bundle == null)
return null;
return (String)bundle.getHeaders().get(Constants.BUNDLE_NAME);
}
/**
* Returns the provider name for this feature, or <code>null</code>.
*
* @return the provider name for this feature, or <code>null</code>
*/
public String getProviderName() {
if (bundle == null)
return null;
return (String)bundle.getHeaders().get(Constants.BUNDLE_VENDOR);
}
/*
* Returns a resource string corresponding to the given argument
* value and bundle.
* If the argument value specifies a resource key, the string
* is looked up in the given resource bundle. If the argument does not
* specify a valid key, the argument itself is returned as the
* resource string. The key lookup is performed against the
* specified resource bundle. If a resource string
* corresponding to the key is not found in the resource bundle
* the key value, or any default text following the key in the
* argument value is returned as the resource string.
* A key is identified as a string begining with the "%" character.
* Note that the "%" character is stripped off prior to lookup
* in the resource bundle.
* <p>
* For example, assume resource bundle plugin.properties contains
* name = Project Name
* <pre>
* <li>getResourceString("Hello World") returns "Hello World"</li>
* <li>getResourceString("%name") returns "Project Name"</li>
* <li>getResourceString("%name Hello World") returns "Project Name"</li>
* <li>getResourceString("%abcd Hello World") returns "Hello World"</li>
* <li>getResourceString("%abcd") returns "%abcd"</li>
* <li>getResourceString("%%name") returns "%name"</li>
* <li>getResourceString(<code>null</code>) returns <code>null</code></li>
* </pre>
* </p>
*
* @param value the value or <code>null</code>
* @param runtimeMappings runtime mappings or <code>null</code>
* @return the resource string
*/
public String getResourceString(String value, Hashtable runtimeMappings) {
if (value == null)
return null;
String s = value.trim();
if (!s.startsWith(KEY_PREFIX))
return s;
if (s.startsWith(KEY_DOUBLE_PREFIX))
return s.substring(1);
int ix = s.indexOf(" "); //$NON-NLS-1$
String key = ix == -1 ? s : s.substring(0, ix);
String dflt = ix == -1 ? s : s.substring(ix + 1);
if (properties == null)
return dflt;
String result = null;
try {
result = properties.getString(key.substring(1));
} catch (MissingResourceException e) {
return dflt;
}
if (runtimeMappings != null) {
for (Enumeration e = runtimeMappings.keys(); e.hasMoreElements();) {
String keyValue = (String) e.nextElement();
int i = result.indexOf(keyValue);
if (i != -1) {
String s1 = result.substring(0,i);
String s2 = (String) runtimeMappings.get(keyValue);
String s3 = result.substring(i+keyValue.length());
result = s1 + s2 + s3;
}
}
}
if (result.indexOf('{') != -1) {
// We test for the curly braces since due to NL issues we do not
// want to use MessageFormat unless we have to.
try {
result = MessageFormat.format(result, mappings);
} catch (IllegalArgumentException e) {
//ignore and return string without bound parameters
}
}
return result;
}
/*
* Read the contents of the ini, properties, and mappings files.
*/
private IStatus load(URL iniURL, URL propertiesURL, URL mappingsURL) {
InputStream is = null;
try {
is = iniURL.openStream();
ini = new Properties();
ini.load(is);
} catch (IOException e) {
ini = null;
String message = NLS.bind(Messages.IniFileReader_ReadIniError, (new String[] { iniURL.toExternalForm() }));
return new Status(IStatus.ERROR, PID, 0, message, e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
if (propertiesURL != null) {
is = null;
try {
is = propertiesURL.openStream();
properties = new PropertyResourceBundle(is);
} catch (IOException e) {
properties = null;
String message = NLS.bind(Messages.IniFileReader_ReadPropError, (new String[] { propertiesURL.toExternalForm() }));
return new Status(IStatus.ERROR, PID, 0, message, e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
}
PropertyResourceBundle bundle = null;
if (mappingsURL != null) {
is = null;
try {
is = mappingsURL.openStream();
bundle = new PropertyResourceBundle(is);
} catch (IOException e) {
bundle = null;
String message = NLS.bind(Messages.IniFileReader_ReadMapError, (new String[] { mappingsURL.toExternalForm() }));
return new Status(IStatus.ERROR, PID, 0, message, e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
}
ArrayList mappingsList = new ArrayList();
if (bundle != null) {
boolean found = true;
int i = 0;
while (found) {
try {
mappingsList.add(bundle.getString(Integer.toString(i)));
} catch (MissingResourceException e) {
found = false;
}
i++;
}
}
mappings = (String[])mappingsList.toArray(new String[mappingsList.size()]);
return OK_STATUS;
}
}