/******************************************************************************* * 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; } }