Move NLS class from core to OSGi packages
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/NLS.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/NLS.java
new file mode 100644
index 0000000..ce03f5c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/NLS.java
@@ -0,0 +1,119 @@
+/**********************************************************************
+ * Copyright (c) 2005 IBM Corporation and others. All rights reserved.   This
+ * program and the accompanying materials are made available under the terms of
+ * the Common Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors: 
+ * IBM - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.osgi.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import org.eclipse.osgi.framework.internal.core.MessageResourceBundle;
+import org.eclipse.osgi.framework.msg.MessageFormat;
+
+
+/**
+ * Common superclass for all message bundle classes.  Provides convenience
+ * methods for manipulating messages.
+ * 
+ * @since 3.1
+ */
+public abstract class NLS {
+	
+	
+	public static boolean DEBUG_MESSAGE_BUNDLES = false;
+	/**
+	 * Creates a new NLS instance.
+	 */
+	protected NLS() {
+		super();
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param binding the object to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object binding) {
+		return bind(message, new Object[] {binding});
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param binding1 An object to be inserted into the message
+	 * @param binding2 A second object to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object binding1, Object binding2) {
+		return bind(message, new Object[] {binding1, binding2});
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param bindings[] An array of objects to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object[] bindings) {
+		if (message == null)
+			return "No message available"; //$NON-NLS-1$
+		if (bindings == null)
+			return message;
+		return MessageFormat.format(message, bindings);
+	}
+
+	/**
+	 * Initialize the given class with the values from the specified message bundle.
+	 * <p>
+	 * Note this is interim API and may change before the 3.1 release.
+	 * </p>
+	 * 
+	 * @param bundleName fully qualified path of the class name
+	 * @param clazz the class where the constants will exist
+	 */
+	
+	public static void initializeMessages(String bundleName, Class clazz) {
+		long start = System.currentTimeMillis();
+		// load the resource bundle and set the fields
+		final Field[] fields = clazz.getDeclaredFields();
+		MessageResourceBundle.load(bundleName, clazz.getClassLoader(), fields);
+
+		// iterate over the fields in the class to make sure that there aren't any empty ones
+		final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+		final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+		final int numFields = fields.length;
+		for (int i = 0; i < numFields; i++) {
+			Field field = fields[i];
+			if ((field.getModifiers() & MOD_MASK) != MOD_EXPECTED)
+				continue;
+			try {
+				// Set the value into the field if its empty. We should never get an exception here because
+				// we know we have a public static non-final field. If we do get an exception, silently
+				// log it and continue. This means that the field will (most likely) be un-initialized and
+				// will fail later in the code and if so then we will see both the NPE and this error.
+				if (field.get(clazz) == null) {
+					String value = "Missing message: " + field.getName() + " in: " + bundleName; //$NON-NLS-1$ //$NON-NLS-2$
+					if (DEBUG_MESSAGE_BUNDLES)
+						System.out.println(value);
+					field.set(null, value);
+				}
+			} catch (IllegalArgumentException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			} catch (IllegalAccessException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		if (DEBUG_MESSAGE_BUNDLES)
+			System.out.println("Time to load message bundle: " + bundleName + " was " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MessageResourceBundle.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MessageResourceBundle.java
new file mode 100644
index 0000000..287b5e2
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MessageResourceBundle.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class responsible for loading message values from a property file
+ * and assigning them directly to the fields of a messages class.
+ * @since 3.1
+ */
+public class MessageResourceBundle {
+
+	/**
+	 * Class which sub-classes java.util.Properties and uses the #put method
+	 * to set field values rather than storing the values in the table.
+	 * 
+	 * @since 3.1
+	 */
+	private static class MessagesProperties extends Properties {
+
+		private static final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+		private static final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+		private static final long serialVersionUID = 1L;
+
+		private final Map fields;
+		private final String bundleName;
+
+		public MessagesProperties(Field[] fieldArray, String bundleName) {
+			super();
+			this.bundleName = bundleName;
+			final int len = fieldArray.length;
+			fields = new HashMap(len * 2);
+			for (int i = 0; i < len; i++) {
+				fields.put(fieldArray[i].getName(), fieldArray[i]);
+			}
+		}
+
+		/* (non-Javadoc)
+		 * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
+		 */
+		public synchronized Object put(Object key, Object value) {
+			try {
+				Field field = (Field) fields.get(key);
+				if (field == null) {
+					if (NLS.DEBUG_MESSAGE_BUNDLES)
+						System.out.println("Unused message: " + key + " in: " + bundleName); //$NON-NLS-1$ //$NON-NLS-2$
+					return null;
+				}
+				//can only set value of public static non-final fields
+				if ((field.getModifiers() & MOD_MASK) != MOD_EXPECTED)
+					return null;
+				// Set the value into the field. We should never get an exception here because
+				// we know we have a public static non-final field. If we do get an exception, silently
+				// log it and continue. This means that the field will (most likely) be un-initialized and
+				// will fail later in the code and if so then we will see both the NPE and this error.
+				try {
+					field.set(null, value);
+				} catch (Exception e) {
+					// TODO externalize message
+					//IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.PLUGIN_ERROR, "Exception setting field value.", e);
+					//InternalPlatform.getDefault().log(status);
+					e.printStackTrace();
+				}
+			} catch (SecurityException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			return null;
+		}
+	}
+
+	private static final String EXTENSION = ".properties"; //$NON-NLS-1$
+	private static String[] nlSuffixes;
+
+	/*
+	 * Build an array of directories to search
+	 */
+	private static String[] buildVariants(String root) {
+		if (nlSuffixes == null) {
+			//build list of suffixes for loading resource bundles
+			String nl = Locale.getDefault().toString();
+			ArrayList result = new ArrayList(4);
+			int lastSeparator;
+			while (true) {
+				result.add('_' + nl + EXTENSION);
+				lastSeparator = nl.lastIndexOf('_');
+				if (lastSeparator == -1)
+					break;
+				nl = nl.substring(0, lastSeparator);
+			}
+			//add the empty suffix last (most general)
+			result.add(EXTENSION);
+			nlSuffixes = (String[]) result.toArray(new String[result.size()]);
+		}
+		root = root.replace('.', '/');
+		String[] variants = new String[nlSuffixes.length];
+		for (int i = 0; i < variants.length; i++)
+			variants[i] = root + nlSuffixes[i];
+		return variants;
+	}
+
+	/**
+	 * Load the given resource bundle using the specified class loader.
+	 */
+	public static void load(final String bundleName, final ClassLoader loader, final Field[] fields) {
+		final String[] variants = buildVariants(bundleName);
+		// search the dirs in reverse order so the cascading defaults is set correctly
+		for (int i = variants.length; --i >= 0;) {
+			final InputStream input = loader.getResourceAsStream(variants[i]);
+			if (input == null)
+				continue;
+			try {
+				final MessagesProperties properties = new MessagesProperties(fields, bundleName);
+				properties.load(input);
+			} catch (IOException e) {
+				// TODO log
+			} finally {
+				if (input != null)
+					try {
+						input.close();
+					} catch (IOException e) {
+						// ignore
+					}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/NLS.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/NLS.java
new file mode 100644
index 0000000..ce03f5c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/NLS.java
@@ -0,0 +1,119 @@
+/**********************************************************************
+ * Copyright (c) 2005 IBM Corporation and others. All rights reserved.   This
+ * program and the accompanying materials are made available under the terms of
+ * the Common Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors: 
+ * IBM - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.osgi.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import org.eclipse.osgi.framework.internal.core.MessageResourceBundle;
+import org.eclipse.osgi.framework.msg.MessageFormat;
+
+
+/**
+ * Common superclass for all message bundle classes.  Provides convenience
+ * methods for manipulating messages.
+ * 
+ * @since 3.1
+ */
+public abstract class NLS {
+	
+	
+	public static boolean DEBUG_MESSAGE_BUNDLES = false;
+	/**
+	 * Creates a new NLS instance.
+	 */
+	protected NLS() {
+		super();
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param binding the object to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object binding) {
+		return bind(message, new Object[] {binding});
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param binding1 An object to be inserted into the message
+	 * @param binding2 A second object to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object binding1, Object binding2) {
+		return bind(message, new Object[] {binding1, binding2});
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param bindings[] An array of objects to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object[] bindings) {
+		if (message == null)
+			return "No message available"; //$NON-NLS-1$
+		if (bindings == null)
+			return message;
+		return MessageFormat.format(message, bindings);
+	}
+
+	/**
+	 * Initialize the given class with the values from the specified message bundle.
+	 * <p>
+	 * Note this is interim API and may change before the 3.1 release.
+	 * </p>
+	 * 
+	 * @param bundleName fully qualified path of the class name
+	 * @param clazz the class where the constants will exist
+	 */
+	
+	public static void initializeMessages(String bundleName, Class clazz) {
+		long start = System.currentTimeMillis();
+		// load the resource bundle and set the fields
+		final Field[] fields = clazz.getDeclaredFields();
+		MessageResourceBundle.load(bundleName, clazz.getClassLoader(), fields);
+
+		// iterate over the fields in the class to make sure that there aren't any empty ones
+		final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+		final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+		final int numFields = fields.length;
+		for (int i = 0; i < numFields; i++) {
+			Field field = fields[i];
+			if ((field.getModifiers() & MOD_MASK) != MOD_EXPECTED)
+				continue;
+			try {
+				// Set the value into the field if its empty. We should never get an exception here because
+				// we know we have a public static non-final field. If we do get an exception, silently
+				// log it and continue. This means that the field will (most likely) be un-initialized and
+				// will fail later in the code and if so then we will see both the NPE and this error.
+				if (field.get(clazz) == null) {
+					String value = "Missing message: " + field.getName() + " in: " + bundleName; //$NON-NLS-1$ //$NON-NLS-2$
+					if (DEBUG_MESSAGE_BUNDLES)
+						System.out.println(value);
+					field.set(null, value);
+				}
+			} catch (IllegalArgumentException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			} catch (IllegalAccessException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		if (DEBUG_MESSAGE_BUNDLES)
+			System.out.println("Time to load message bundle: " + bundleName + " was " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+}
\ No newline at end of file