*** empty log message ***
diff --git a/bundles/org.eclipse.core.runtime/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.runtime/META-INF/MANIFEST.MF
index 6702cf7..2df3e11 100644
--- a/bundles/org.eclipse.core.runtime/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.core.runtime/META-INF/MANIFEST.MF
@@ -23,6 +23,7 @@
  org.eclipse.core.internal.boot

 Import-Package: 

  org.osgi.framework,

+ org.eclipse.osgi.service.debug,

  org.eclipse.osgi.service.environment,

  org.osgi.service.packageadmin,

  org.osgi.service.url,

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/DeadlockDetector.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/DeadlockDetector.java
index 15acbef..98742e0 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/DeadlockDetector.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/DeadlockDetector.java
@@ -1,564 +1,656 @@
-/*******************************************************************************

- * Copyright (c) 2003 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.core.internal.jobs;

-

-import java.util.ArrayList;

-import java.util.Arrays;

-import org.eclipse.core.internal.runtime.*;

-import org.eclipse.core.runtime.*;

-import org.eclipse.core.runtime.jobs.ILock;

-import org.eclipse.core.runtime.jobs.ISchedulingRule;

-

-class DeadlockDetector {

-	private static int NO_STATE = 0;

-	//state variables in the graph

-	private static int WAITING_FOR_LOCK = -10;

-	//whether deadlock exists between the threads that are in the graph

-	private boolean deadlockExists = false;

-	//matrix of relationships between threads and locks

-	private int[][] graph = new int[0][0];

-	//index is column in adjacency matrix for the lock

-	private final ArrayList locks = new ArrayList();

-	//index is row in adjacency matrix for the thread

-	private final ArrayList lockThreads = new ArrayList();

-	//whether the graph needs to be resized

-	private boolean resize = false;

-

-	/**

-	 * Add the thread that owns the given lock to the list of threads that are

-	 * waiting for some action. (these threads cannot continue until the lock

-	 * they are waiting for is free)

-	 */

-	private boolean addWaitingThread(int[] waitingThreads, int column) {

-		boolean found = false;

-		int index = -1;

-		//find the thread that owns the lock with the given index

-		//(go through all the threads in the graph as a type of error

-		// checking)

-		for (int i = 0; i < graph.length; i++) {

-			if (graph[i][column] > NO_STATE) {

-				Assert.isTrue(!found, "The lock " + locks.get(column).toString() + " is owned by 2 threads."); //$NON-NLS-1$ //$NON-NLS-2$

-				found = true;

-				index = i;

-			}

-		}

-		//matrix could be in an unsynchronized state so a lock could be

-		// unowned (index of -1)

-		if (index < NO_STATE)

-			return false;

-		if (waitingThreads[index] > NO_STATE)

-			deadlockExists = true;

-		waitingThreads[index] = 1;

-		return true;

-	}

-	/**

-	 * Get the thread that owns the lock this thread is waiting for.

-	 */

-	private Thread blockingThread(Thread current) {

-		ISchedulingRule lock = (ISchedulingRule) getWaitingLock(current);

-		return getThreadOwningLock(lock);

-	}

-	/**

-	 * Check that the addition of a waiting thread did not pruduce deadlock. If

-	 * deadlock is detected, the deadlockExists variable is set to true.

-	 */

-	private void checkWaitCycles(int[] waitingThreads, int column) {

-		if (!addWaitingThread(waitingThreads, column))

-			return;

-		if (deadlockExists)

-			return;

-

-		for (int i = 0; i < graph.length; i++) {

-			if (graph[i][column] > NO_STATE) {

-				for (int j = 0; j < graph[i].length; j++) {

-					if (graph[i][j] == WAITING_FOR_LOCK) {

-						checkWaitCycles(waitingThreads, j);

-					}

-					if (deadlockExists)

-						break;

-				}

-			}

-		}

-	}

-	/**

-	 * Combine the entries for two conflicting rule columns into a single column.

-	 */

-	private void combineColumns(int from, int to) {

-		for (int i = 0; i < graph.length; i++) {

-			if ((graph[i][to] != 0) && (graph[i][from] != 0))

-				Assert.isLegal(false, "Incorrect Graph."); //$NON-NLS-1$

-

-			graph[i][to] += graph[i][from];

-			graph[i][from] = NO_STATE;

-		}

-		removeExtraRows(0, from);

-	}

-	/**

-	 * Returns true IFF the matrix contains a row for the given thread.

-	 * (meaning the given thread either owns locks or is waiting for locks)

-	 */

-	boolean contains(Thread t) {

-		return lockThreads.contains(t);

-	}

-	/**

-	 * Returns an array of contested locks that are owned by the given thread.

-	 * Contested locks are locks that are owned by this thread, but have other

-	 * threads waiting for them.

-	 */

-	ISchedulingRule[] contestedLocksForThread(Thread owner) {

-		int threadIndex = indexOf(owner);

-		ArrayList ownedLocks = new ArrayList(1);

-

-		for (int j = 0; j < graph[threadIndex].length; j++) {

-			if ((graph[threadIndex][j] > NO_STATE) && (locks.get(j) instanceof ILock))

-				// && (isWaitedFor((ISchedulingRule)locks.get(j))))

-				ownedLocks.add(locks.get(j));

-		}

-		Assert.isLegal(ownedLocks.size() > 0, "A thread with no contested locks caused deadlock."); //$NON-NLS-1$

-		return (ISchedulingRule[]) ownedLocks.toArray(new ISchedulingRule[ownedLocks.size()]);

-	}

-	/**

-	 * Reset the deadlock exists variable.

-	 */

-	void deadlockSolved() {

-		deadlockExists = false;

-	}

-	/**

-	 * Make sure a thread owns, or is waiting for, at most 1 rule.  For debugging

-	 * purposes only.

-	 */

-	public void ensureGraphIntegrity(Thread owner, ISchedulingRule rule) {

-		try {

-			int threadIndex = lockThreads.indexOf(owner);

-			if (threadIndex < 0)

-				return;

-			for (int j = 0; j < graph[threadIndex].length; j++) {

-				if ((graph[threadIndex][j] != NO_STATE) && (!(locks.get(j) instanceof ILock)) && (!rule.isConflicting((ISchedulingRule) locks.get(j)))) {

-					graph[threadIndex][j] = NO_STATE;

-					removeExtraRows(threadIndex, j);

-				}

-			}

-		} catch (RuntimeException e) {

-			if (JobManager.DEBUG_LOCKS) {

-				Policy.debug(false, "Error while ensuring graph integrity: "); //$NON-NLS-1$

-				e.printStackTrace();

-			}

-		}

-	}

-	/**

-	 * Returns all the locks owned by the given thread

-	 */

-	private Object[] getOwnedLocks(Thread current) {

-		ArrayList ownedLocks = new ArrayList(1);

-		int index = indexOf(current);

-

-		for (int j = 0; j < graph[index].length; j++) {

-			if (graph[index][j] > NO_STATE)

-				ownedLocks.add(locks.get(j));

-		}

-		Assert.isLegal(ownedLocks.size() > 0, "A thread with no locks is part of a deadlock."); //$NON-NLS-1$

-		return ownedLocks.toArray();

-	}

-	/**

-	 * Returns the thread that owns the given lock.

-	 */

-	private Thread getThreadOwningLock(ISchedulingRule lock) {

-		int index = indexOf(lock);

-

-		for (int i = 0; i < graph.length; i++) {

-			if (graph[i][index] > NO_STATE)

-				return (Thread) lockThreads.get(i);

-		}

-		//toDebugString();

-		throw new IllegalStateException("Lock " + lock + " is involved in deadlock but is not owned by any thread."); //$NON-NLS-1$ //$NON-NLS-2$

-	}

-	/**

-	 * Returns the true index of the lock in the array. Uses the

-	 * ISchedulingRule.isConflicting relationship.

-	 */

-	private int getTrueLockIndex(ISchedulingRule lock) {

-		for (int i = 0; i < locks.size(); i++) {

-			ISchedulingRule present = (ISchedulingRule) locks.get(i);

-			if (present.isConflicting(lock))

-				return i;

-		}

-		return -1;

-	}

-	/**

-	 * Returns the lock the given thread is waiting for.

-	 */

-	private Object getWaitingLock(Thread current) {

-		int index = indexOf(current);

-

-		for (int j = 0; j < graph[index].length; j++) {

-			if (graph[index][j] == WAITING_FOR_LOCK)

-				return locks.get(j);

-		}

-		throw new IllegalStateException("Thread " + current.getName() + " is involved in deadlock but is not waiting for any lock."); //$NON-NLS-1$ //$NON-NLS-2$

-	}

-	/**

-	 * Returns the index of the given lock in the lock array. If the lock is

-	 * not present in the array, it is added to the end.

-	 */

-	private int indexOf(ISchedulingRule lock) {

-		int index = getTrueLockIndex(lock);

-		if (index < 0) {

-			locks.add(lock);

-			resize = true;

-			index = locks.size() - 1;

-		}

-		return index;

-	}

-	/**

-	 * Returns the index of the given thread in the thread array. If the thread

-	 * is not present in the array, it is added to the end.

-	 */

-	private int indexOf(Thread owner) {

-		int index = lockThreads.indexOf(owner);

-		if (index < 0) {

-			lockThreads.add(owner);

-			resize = true;

-			index = lockThreads.size() - 1;

-		}

-		return index;

-	}

-	/**

-	 * Returns true IFF deadlock exists

-	 */

-	boolean isDeadlocked() {

-		return deadlockExists;

-	}

-	/**

-	 * Returns true if the adjacency matrix is empty.

-	 */

-	boolean isEmpty() {

-		return (locks.size() == 0) && (lockThreads.size() == 0) && (graph.length == 0);

-	}

-	/**

-	 * The given lock was aquired by the given thread.

-	 */

-	void lockAcquired(Thread owner, ISchedulingRule lock) {

-		if (!(lock instanceof ILock))

-			ensureGraphIntegrity(owner, lock);

-		int lockIndex = indexOf(lock);

-		int threadIndex = indexOf(owner);

-		if (resize)

-			resizeGraph();

-

-		//the rule this thread is acquiring may not be the rule

-		//it was waiting for, so replace the entry in the graph

-		//also, transfer all entries for any rule that conflicts with this rule

-		//to the new rule and erase the columns that correspond to the old rules from the graph

-		if (!(lock instanceof ILock)) {

-			locks.set(lockIndex, lock);

-			transferConflictingRules(lock, lockIndex);

-		}

-		if (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK)

-			graph[threadIndex][lockIndex] = 1;

-		else

-			graph[threadIndex][lockIndex]++;

-	}

-	/**

-	 * The given lock was released by the given thread. Update the graph.

-	 */

-	void lockReleased(Thread owner, ISchedulingRule lock) {

-		if (!lockThreads.contains(owner)) {

-			if (JobManager.DEBUG_LOCKS)

-				System.out.println("[lockReleased] Lock " + lock + " was already released by thread " + owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$

-			return;

-		}

-		if (!locks.contains(lock)) {

-			if (JobManager.DEBUG_LOCKS)

-				System.out.println("[lockReleased] Thread " + owner.getName() + " already released lock " + lock); //$NON-NLS-1$ //$NON-NLS-2$

-			return;

-		}

-		int lockIndex = indexOf(lock);

-		int threadIndex = indexOf(owner);

-

-		//if this lock was suspended, set it to NO_STATE

-		if (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK) {

-			graph[threadIndex][lockIndex] = NO_STATE;

-			return;

-		}

-		if (!JobManager.DEBUG_LOCKS && graph[threadIndex][lockIndex] == NO_STATE)

-			return;

-		graph[threadIndex][lockIndex]--;

-		Assert.isTrue(graph[threadIndex][lockIndex] > -1, "More releases than acquires for thread " + owner.getName() + " and lock " + lock); //$NON-NLS-1$ //$NON-NLS-2$

-		if (graph[threadIndex][lockIndex] == NO_STATE)

-			removeExtraRows(threadIndex, lockIndex);

-	}

-	/**

-	 * The given scheduling rule is no longer used because the job that invoked

-	 * it is done Release this rule regardless of how many times it was

-	 * acquired.

-	 */

-	void lockReleasedCompletely(Thread owner, ISchedulingRule rule) {

-		//need to make sure that the given thread was not already removed from the graph

-		//and that the given rule (not a rule that conflicts with it) was not removed either

-		if (!lockThreads.contains(owner)) {

-			if (JobManager.DEBUG_LOCKS)

-				System.out.println("[lockReleasedCompletely] Lock " + rule + " was already released by thread " + owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$

-			return;

-		}

-		if (!locks.contains(rule)) {

-			if (JobManager.DEBUG_LOCKS)

-				System.out.println("[lockReleasedCompletely] Thread " + owner.getName() + " already released lock " + rule); //$NON-NLS-1$ //$NON-NLS-2$

-			return;

-		}

-

-		int lockIndex = indexOf(rule);

-		int threadIndex = indexOf(owner);

-		//set this lock to NO_STATE

-		graph[threadIndex][lockIndex] = NO_STATE;

-		removeExtraRows(threadIndex, lockIndex);

-	}

-	/**

-	 * The given thread could not get the given lock and is waiting for it.

-	 * Update the graph.

-	 */

-	void lockWaitStart(Thread client, ISchedulingRule lock) {

-		if (!(lock instanceof ILock))

-			ensureGraphIntegrity(client, lock);

-		setToWait(client, lock);

-		int lockIndex = indexOf(lock);

-		int[] temp = new int[lockThreads.size()];

-		Arrays.fill(temp, 0);

-		checkWaitCycles(temp, lockIndex);

-		temp = null;

-	}

-	/**

-	 * The given thread has stopped waiting for the given lock. Update the

-	 * graph.

-	 */

-	void lockWaitStop(Thread owner, ISchedulingRule lock) {

-		int lockIndex = getTrueLockIndex(lock);

-		int threadIndex = lockThreads.indexOf(owner);

-		if (threadIndex < 0) {

-			if (JobManager.DEBUG_LOCKS)

-				System.out.println("Thread " + owner.getName() + " was already removed."); //$NON-NLS-1$ //$NON-NLS-2$

-			return;

-		}

-		if (lockIndex < 0) {

-			if (JobManager.DEBUG_LOCKS)

-				System.out.println("Lock " + lock + " was already removed."); //$NON-NLS-1$ //$NON-NLS-2$

-			return;

-		}

-		if (graph[threadIndex][lockIndex] != WAITING_FOR_LOCK)

-			Assert.isTrue(false, "Thread " + owner.getName() + " was not waiting for lock " + lock.toString() + " so it could not time out."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

-		graph[threadIndex][lockIndex] = NO_STATE;

-		removeExtraRows(threadIndex, lockIndex);

-	}

-	private boolean ownsLocks(Thread owner) {

-		int index = indexOf(owner);

-

-		for (int j = 0; j < graph[index].length; j++) {

-			if (graph[index][j] > NO_STATE) {

-				Object lock = locks.get(j);

-				if (lock instanceof ILock)

-					return true;

-			}

-		}

-		return false;

-	}

-	/**

-	 * Return true IFF this thread owns rule locks (ie. implicit locks which

-	 * cannot be suspended)

-	 */

-	private boolean ownsRuleLocks(Thread owner) {

-		int index = indexOf(owner);

-

-		for (int j = 0; j < graph[index].length; j++) {

-			if (graph[index][j] > NO_STATE) {

-				Object lock = locks.get(j);

-				if (!(lock instanceof ILock))

-					return true;

-			}

-		}

-		return false;

-	}

-	/**

-	 * The matrix has been simplified. Check if any unnecessary rows or columns

-	 * can be removed.

-	 */

-	private void removeExtraRows(int row, int column) {

-		boolean rowEmpty = true;

-		boolean colEmpty = true;

-		for (int j = 0; j < graph[row].length; j++) {

-			if (graph[row][j] != NO_STATE) {

-				rowEmpty = false;

-				break;

-			}

-		}

-		for (int i = 0; i < graph.length; i++) {

-			if (graph[i][column] != NO_STATE) {

-				colEmpty = false;

-				break;

-			}

-		}

-		if ((!colEmpty) && (!rowEmpty))

-			return;

-

-		if (rowEmpty)

-			lockThreads.remove(row);

-		else

-			row = lockThreads.size();

-		if (colEmpty)

-			locks.remove(column);

-		else

-			column = locks.size();

-

-		int[][] temp = new int[lockThreads.size()][locks.size()];

-		for (int i = 0; i < row; i++) {

-			for (int j = 0; j < column; j++) {

-				temp[i][j] = graph[i][j];

-			}

-		}

-

-		for (int i = row; i < temp.length; i++) {

-			for (int j = 0; j < temp[i].length; j++) {

-				temp[i][j] = graph[i + 1][j];

-			}

-		}

-

-		for (int i = 0; i < temp.length; i++) {

-			for (int j = column; j < temp[i].length; j++) {

-				temp[i][j] = graph[i][j + 1];

-			}

-		}

-

-		for (int i = row; i < temp.length; i++) {

-			for (int j = column; j < temp[i].length; j++) {

-				temp[i][j] = graph[i + 1][j + 1];

-			}

-		}

-		graph = null;

-		graph = temp;

-	}

-	/**

-	 * Adds a 'deadlock detected' message to the log with a stack trace.

-	 */

-	void reportDeadlock(Thread thread, ISchedulingRule lock, Thread toSuspend) {

-		ArrayList deadlockedThreads = new ArrayList(2);

-		deadlockedThreads.add(thread);

-		Thread owner = getThreadOwningLock(lock);

-		while (!deadlockedThreads.contains(owner)) {

-			deadlockedThreads.add(owner);

-			ISchedulingRule waitingLock = (ISchedulingRule) getWaitingLock(owner);

-			owner = getThreadOwningLock(waitingLock);

-		}

-		String msg = "Deadlock detected. All locks owned by thread " + toSuspend.getName() + " will be suspended."; //$NON-NLS-1$ //$NON-NLS-2$

-		MultiStatus main = new MultiStatus(IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, msg, new IllegalStateException());

-		for (int i = 0; i < deadlockedThreads.size(); i++) {

-			Thread current = (Thread) deadlockedThreads.get(i);

-			Object[] ownedLocks = getOwnedLocks(current);

-			Object waitLock = getWaitingLock(current);

-			StringBuffer buf = new StringBuffer("Thread "); //$NON-NLS-1$

-			buf.append(current.getName());

-			buf.append(" has locks: "); //$NON-NLS-1$

-			for (int j = 0; j < ownedLocks.length; j++) {

-				buf.append(ownedLocks[j]);

-				buf.append((j < ownedLocks.length - 1) ? ", " : " "); //$NON-NLS-1$ //$NON-NLS-2$

-			}

-			buf.append("and is waiting for lock "); //$NON-NLS-1$

-			buf.append(waitLock);

-			Status child = new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, buf.toString(), null);

-			main.add(child);

-		}

-		InternalPlatform.getDefault().log(main);

-	}

-	/**

-	 * The number of threads/locks in the graph has changed. Update the

-	 * underlying matrix.

-	 */

-	private void resizeGraph() {

-		int[][] tempGraph = new int[lockThreads.size()][locks.size()];

-		for (int i = 0; i < graph.length; i++) {

-			for (int j = 0; j < graph[i].length; j++) {

-				tempGraph[i][j] = graph[i][j];

-			}

-		}

-		for (int i = 0; i < tempGraph.length; i++) {

-			for (int j = (graph.length == 0 ? 0 : graph[0].length); j < tempGraph[i].length; j++) {

-				tempGraph[i][j] = NO_STATE;

-			}

-		}

-		for (int i = graph.length; i < tempGraph.length; i++) {

-			for (int j = 0; j < tempGraph[i].length; j++) {

-				tempGraph[i][j] = NO_STATE;

-			}

-		}

-		graph = null;

-		graph = tempGraph;

-		resize = false;

-	}

-	/**

-	 * Get the thread whose locks can be suspended. (ie. all locks it owns are

-	 * actual locks and not rules) If not found, return the given thread.

-	 */

-	Thread resolutionCandidate(Thread thread, ISchedulingRule lock) {

-		Thread candidate = thread;

-		//first look for a candidate that has no scheduling rules

-		for (int i = 0; i < lockThreads.size(); i++) {

-			if (!ownsRuleLocks(candidate))

-				return candidate;

-			candidate = blockingThread(candidate);

-		}

-		//next look for any candidate with a lock

-		candidate = thread;

-		for (int i = 0; i < lockThreads.size(); i++) {

-			if (ownsLocks(candidate))

-				return candidate;

-			candidate = blockingThread(candidate);

-		}

-		return thread;

-	}

-	/**

-	 * The given thread is waiting for the given lock. Update the graph.

-	 */

-	void setToWait(Thread owner, ISchedulingRule lock) {

-		int lockIndex = indexOf(lock);

-		int threadIndex = indexOf(owner);

-		if (resize)

-			resizeGraph();

-

-		graph[threadIndex][lockIndex] = WAITING_FOR_LOCK;

-	}

-	/**

-	 * Prints out the current matrix to standard output. Only used for

-	 * debugging.

-	 */

-	public void toDebugString() {

-		System.out.println(" :: "); //$NON-NLS-1$

-		for (int j = 0; j < locks.size(); j++) {

-			System.out.print(" " + locks.get(j) + ','); //$NON-NLS-1$

-		}

-		System.out.println();

-		for (int i = 0; i < graph.length; i++) {

-			System.out.print(" " + ((Thread) lockThreads.get(i)).getName() + " : "); //$NON-NLS-1$ //$NON-NLS-2$

-			for (int j = 0; j < graph[i].length; j++) {

-				System.out.print(" " + graph[i][j] + ','); //$NON-NLS-1$

-			}

-			System.out.println();

-		}

-		System.out.println("-------"); //$NON-NLS-1$

-	}

-	/**

-	 * Remove a rule that is conflicting with another rule in the graph

-	 * (conflicting rules can only have 1 entry in the graph)

-	 */

-	private void transferConflictingRules(ISchedulingRule rule, int column) {

-		for (int i = 0; i < locks.size(); i++) {

-			if ((i != column) && (rule.isConflicting((ISchedulingRule) locks.get(i)))) {

-				combineColumns(i, column);

-			}

-		}

-	}

+/*******************************************************************************
+ * Copyright (c) 2003 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.core.internal.jobs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.eclipse.core.internal.runtime.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * Stores all the relationships between locks (rules are also considered locks), 
+ * and the threads that own them. All the relationships are stored in a 2D integer array.
+ * The rows in the array are threads, while the columns are locks.
+ * Two corresponding arrayLists store the actual threads and locks.
+ * The index of a thread in the first arrayList is the index of the row in the graph.
+ * The index of a lock in the second arrayList is the index of the column in the graph. 
+ * An entry greater than 0 in the graph is the number of times a thread in the entry's row
+ * acquired the lock in the entry's column.
+ * An entry of -1 means that the thread is waiting to acquire the lock.
+ * An enry of 0 means that the thread and the lock have no relationship.
+ * 
+ * The difference between rules and locks is that locks can be suspended, while
+ * rules are implicit locks and as such cannot be suspended.
+ * To resolve deadlock, the graph will first try to find a thread that only owns
+ * locks. Failing that, it will find a thread in the deadlock that owns at least
+ * one lock and suspend it.
+ * 
+ * Deadlock can only occur among locks, or among locks in combination with rules.
+ * Deadlock among rules only is impossible. Therefore, in any deadlock one can always
+ * find a thread that owns at least one lock that can be suspended.
+ * 
+ * The implementation of the graph assumes that a thread can only own 1 rule at
+ * any one time. It can acquire that rule several times, but a thread cannot
+ * acquire 2 non-conflicting rules at the same time.
+ * 
+ * The implementation of the graph will sometimes also find and resolve bogus deadlocks.
+ * 		graph:				assuming this rule hierarchy:
+ * 		   R2 R3 L1						R1
+ * 		J1  1  0  0					   /  \
+ * 		J2  0  1 -1					  R2  R3
+ * 		J3 -1  0  1
+ * 		 
+ * If in the above situation job4 decides to acquire rule1, then the graph will transform
+ * to the following:
+ * 		   R2 R3 R1 L1
+ * 		J1  1  0  1  0
+ * 		J2  1  1  1 -1
+ * 		J3 -1  0  0  1
+ * 		J4  0  0 -1  0
+ * 
+ * and the graph will assume that job2 and job3 are deadlocked and suspend lock1 of job3.
+ * The reason the deadlock is bogus is that the deadlock is unlikely to actually happen (the threads
+ * are currently not deadlocked, but might deadlock later on when it is too late to detect it)
+ * Therefore, in order to make sure that no deadlock is possible, 
+ * the deadlock will still be resolved at this point.
+ */
+class DeadlockDetector {
+	private static int NO_STATE = 0;
+	//state variables in the graph
+	private static int WAITING_FOR_LOCK = -1;
+	//matrix of relationships between threads and locks
+	private int[][] graph = new int[0][0];
+	//index is column in adjacency matrix for the lock
+	private final ArrayList locks = new ArrayList();
+	//index is row in adjacency matrix for the thread
+	private final ArrayList lockThreads = new ArrayList();
+	//whether the graph needs to be resized
+	private boolean resize = false;
+
+	/**
+	 * Recursively check if any of the threads that prevent the current thread from running
+	 * are actually deadlocked with the current thread.
+	 * Add the threads that form deadlock to the deadlockedThreads list.
+	 */
+	private boolean addCycleThreads(ArrayList deadlockedThreads, Thread next) {
+		//get the thread that block the given thread from running
+		Thread[] blocking = blockingThreads(next);
+		//if the thread is not blocked by other threads, then it is not part of a deadlock
+		if (blocking.length == 0)
+			return false;
+		boolean inCycle = false;
+		for (int i = 0; i < blocking.length; i++) {
+			//if we have already visited the given thread, then we found a cycle
+			if (deadlockedThreads.contains(blocking[i])) {
+				inCycle = true;
+			} else {
+				//otherwise, add the thread to our list and recurse deeper
+				deadlockedThreads.add(blocking[i]);
+				//if the thread is not part of a cycle, remove it from the list
+				if (addCycleThreads(deadlockedThreads, blocking[i]))
+					inCycle = true;
+				else
+					deadlockedThreads.remove(blocking[i]);
+			}
+		}
+		return inCycle;
+	}
+	/**
+	 * Get the thread(s) that own the lock this thread is waiting for.
+	 */
+	private Thread[] blockingThreads(Thread current) {
+		//find the lock this thread is waiting for
+		ISchedulingRule lock = (ISchedulingRule) getWaitingLock(current);
+		return getThreadsOwningLock(lock);
+	}
+	/**
+	 * Check that the addition of a waiting thread did not produce deadlock. 
+	 * If deadlock is detected return true, else return false.
+	 */
+	private boolean checkWaitCycles(int[] waitingThreads, int lockIndex) {
+		/**
+		 * find the lock that this thread is waiting for
+		 * recursively check if this is a cylce (ie. a thread waiting on itself)
+		 */
+		for (int i = 0; i < graph.length; i++) {
+			if (graph[i][lockIndex] > NO_STATE) {
+				if (waitingThreads[i] > NO_STATE) {
+					return true;
+				}
+				//keep track that we already visited this thread
+				waitingThreads[i]++;
+				for (int j = 0; j < graph[i].length; j++) {
+					if (graph[i][j] == WAITING_FOR_LOCK) {
+						if (checkWaitCycles(waitingThreads, j))
+							return true;
+					}
+				}
+				//this thread is not involved in a cycle yet, so remove the visited flag
+				waitingThreads[i]--;
+			}
+		}
+		return false;
+	}
+	/**
+	 * Returns true IFF the matrix contains a row for the given thread.
+	 * (meaning the given thread either owns locks or is waiting for locks)
+	 */
+	boolean contains(Thread t) {
+		return lockThreads.contains(t);
+	}
+	/**
+	 * A new rule was just added to the graph.
+	 * Find a rule it conflicts with and update the new rule with the number of times 
+	 * it was acquired implicitly when threads acquired conflicting rule.
+	 */
+	private void fillPresentEntries(ISchedulingRule newLock, int lockIndex) {
+		//fill in the entries for the new rule from rules it conflicts with
+		for (int j = 0; j < locks.size(); j++) {
+			if ((j != lockIndex) && (newLock.isConflicting((ISchedulingRule) locks.get(j)))) {
+				for (int i = 0; i < graph.length; i++) {
+					if ((graph[i][j] > NO_STATE) && (graph[i][lockIndex] == NO_STATE))
+						graph[i][lockIndex] = graph[i][j];
+				}
+			}
+		}
+		//now back fill the entries for rules the current rule conflicts with
+		for (int j = 0; j < locks.size(); j++) {
+			if ((j != lockIndex) && (newLock.isConflicting((ISchedulingRule) locks.get(j)))) {
+				for (int i = 0; i < graph.length; i++) {
+					if ((graph[i][lockIndex] > NO_STATE) && (graph[i][j] == NO_STATE))
+						graph[i][j] = graph[i][lockIndex];
+				}
+			}
+		}
+	}
+	/**
+	 * Returns all the locks owned by the given thread
+	 */
+	private Object[] getOwnedLocks(Thread current) {
+		ArrayList ownedLocks = new ArrayList(1);
+		int index = indexOf(current, false);
+
+		for (int j = 0; j < graph[index].length; j++) {
+			if (graph[index][j] > NO_STATE)
+				ownedLocks.add(locks.get(j));
+		}
+		if (ownedLocks.size() == 0)
+			Assert.isLegal(false, "A thread with no locks is part of a deadlock."); //$NON-NLS-1$
+		return ownedLocks.toArray();
+	}
+	/**
+	 * Returns an array of threads that form the deadlock (usually 2).
+	 */
+	private Thread[] getThreadsInDeadlock(Thread cause) {
+		ArrayList deadlockedThreads = new ArrayList(2);
+		/**
+		 * if the thread that caused deadlock doesn't own any locks, then it is not part
+		 * of the deadlock (it just caused it because of a rule it tried to acquire)
+		 */
+		if (ownsLocks(cause))
+			deadlockedThreads.add(cause);
+		addCycleThreads(deadlockedThreads, cause);
+		return (Thread[]) deadlockedThreads.toArray(new Thread[deadlockedThreads.size()]);
+	}
+	/**
+	 * Returns the thread(s) that own the given lock.
+	 */
+	private Thread[] getThreadsOwningLock(ISchedulingRule rule) {
+		if (rule == null)
+			return new Thread[0];
+		int lockIndex = indexOf(rule, false);
+		ArrayList blocking = new ArrayList(1);
+		for (int i = 0; i < graph.length; i++) {
+			if (graph[i][lockIndex] > NO_STATE)
+				blocking.add(lockThreads.get(i));
+		}
+		if ((blocking.size() == 0) && (JobManager.DEBUG_LOCKS))
+			System.out.println("Lock " + rule + " is involved in deadlock but is not owned by any thread."); //$NON-NLS-1$ //$NON-NLS-2$
+		if ((blocking.size() > 1) && (rule instanceof ILock) && (JobManager.DEBUG_LOCKS))
+			System.out.println("Lock " + rule + " is owned by more than 1 thread, but it is not a rule."); //$NON-NLS-1$ //$NON-NLS-2$
+		return (Thread[]) blocking.toArray(new Thread[blocking.size()]);
+	}
+	/**
+	 * Returns the lock the given thread is waiting for.
+	 */
+	private Object getWaitingLock(Thread current) {
+		int index = indexOf(current, false);
+		//find the lock that this thread is waiting for
+		for (int j = 0; j < graph[index].length; j++) {
+			if (graph[index][j] == WAITING_FOR_LOCK)
+				return locks.get(j);
+		}
+		//it can happen that a thread is not waiting for any lock (it is not really part of the deadlock)
+		return null;
+	}
+	/**
+	 * Returns the index of the given lock in the lock array. If the lock is
+	 * not present in the array, it is added to the end.
+	 */
+	private int indexOf(ISchedulingRule lock, boolean add) {
+		int index = locks.indexOf(lock);
+		if ((index < 0) && add) {
+			locks.add(lock);
+			resize = true;
+			index = locks.size() - 1;
+		}
+		return index;
+	}
+	/**
+	 * Returns the index of the given thread in the thread array. If the thread
+	 * is not present in the array, it is added to the end.
+	 */
+	private int indexOf(Thread owner, boolean add) {
+		int index = lockThreads.indexOf(owner);
+		if ((index < 0) && add) {
+			lockThreads.add(owner);
+			resize = true;
+			index = lockThreads.size() - 1;
+		}
+		return index;
+	}
+	/**
+	 * Returns true IFF the adjacency matrix is empty.
+	 */
+	boolean isEmpty() {
+		return (locks.size() == 0) && (lockThreads.size() == 0) && (graph.length == 0);
+	}
+	/**
+	 * The given lock was aquired by the given thread.
+	 */
+	void lockAcquired(Thread owner, ISchedulingRule lock) {
+		int lockIndex = indexOf(lock, true);
+		int threadIndex = indexOf(owner, true);
+		if (resize)
+			resizeGraph();
+		if (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK)
+			graph[threadIndex][lockIndex] = NO_STATE;
+		/**
+		 * acquire all locks that conflict with the given lock
+		 * or conflict with a lock the given lock will acquire implicitly
+		 * (locks are acquired implicitly when a conflicting lock is acquired)
+		 */
+		ArrayList conflicting = new ArrayList(1);
+		//only need two passes through all the locks to pick up all conflicting rules
+		int NUM_PASSES = 2;
+		conflicting.add(lock);
+		graph[threadIndex][lockIndex]++;
+		for (int i = 0; i < NUM_PASSES; i++) {
+			for (int k = 0; k < conflicting.size(); k++) {
+				ISchedulingRule current = (ISchedulingRule) conflicting.get(k);
+				for (int j = 0; j < locks.size(); j++) {
+					ISchedulingRule possible = (ISchedulingRule) locks.get(j);
+					if (current.isConflicting(possible) && !conflicting.contains(possible)) {
+						conflicting.add(possible);
+						graph[threadIndex][j]++;
+					}
+				}
+			}
+		}
+	}
+	/**
+	 * The given lock was released by the given thread. Update the graph.
+	 */
+	void lockReleased(Thread owner, ISchedulingRule lock) {
+		int lockIndex = indexOf(lock, false);
+		int threadIndex = indexOf(owner, false);
+		//make sure the lock and thread exist in the graph
+		if (threadIndex < 0) {
+			if (JobManager.DEBUG_LOCKS)
+				System.out.println("[lockReleased] Lock " + lock + " was already released by thread " + owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		if (lockIndex < 0) {
+			if (JobManager.DEBUG_LOCKS)
+				System.out.println("[lockReleased] Thread " + owner.getName() + " already released lock " + lock); //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		//if this lock was suspended, set it to NO_STATE
+		if ((lock instanceof ILock) && (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK)) {
+			graph[threadIndex][lockIndex] = NO_STATE;
+			return;
+		}
+		//release all locks that conflict with the given lock
+		//or release all rules that are owned by the given thread, if we are releasing a rule
+		for (int j = 0; j < graph[threadIndex].length; j++) {
+			if ((lock.isConflicting((ISchedulingRule) locks.get(j))) || (!(lock instanceof ILock) && !(locks.get(j) instanceof ILock) && (graph[threadIndex][j] > NO_STATE))) {
+				if (graph[threadIndex][j] == NO_STATE) {
+					if (JobManager.DEBUG_LOCKS)
+						System.out.println("[lockReleased] More releases than acquires for thread " + owner.getName() + " and lock " + lock); //$NON-NLS-1$ //$NON-NLS-2$
+				} else {
+					graph[threadIndex][j]--;
+				}
+			}
+		}
+		//if this thread just released the given lock, try to simplify the graph
+		if (graph[threadIndex][lockIndex] == NO_STATE)
+			reduceGraph(threadIndex, lock);
+	}
+	/**
+	 * The given scheduling rule is no longer used because the job that invoked it is done.
+	 * Release this rule regardless of how many times it was acquired.
+	 */
+	void lockReleasedCompletely(Thread owner, ISchedulingRule rule) {
+		int ruleIndex = indexOf(rule, false);
+		int threadIndex = indexOf(owner, false);
+		//need to make sure that the given thread and rule were not already removed from the graph
+		if (threadIndex < 0) {
+			if (JobManager.DEBUG_LOCKS)
+				System.out.println("[lockReleasedCompletely] Lock " + rule + " was already released by thread " + owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		if (ruleIndex < 0) {
+			if (JobManager.DEBUG_LOCKS)
+				System.out.println("[lockReleasedCompletely] Thread " + owner.getName() + " already released lock " + rule); //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		/**
+		 * set all rules that are owned by the given thread to NO_STATE 
+		 * (not just rules that conflict with the rule we are releasing)
+		 * if we are releasing a lock, then only update the one entry for the lock
+		 */
+		for (int j = 0; j < graph[threadIndex].length; j++) {
+			if (!(locks.get(j) instanceof ILock) && (graph[threadIndex][j] > NO_STATE))
+				graph[threadIndex][j] = NO_STATE;
+		}
+		reduceGraph(threadIndex, rule);
+	}
+	/**
+	 * The given thread could not get the given lock and is waiting for it.
+	 * Update the graph.
+	 */
+	Deadlock lockWaitStart(Thread client, ISchedulingRule lock) {
+		setToWait(client, lock, false);
+		int lockIndex = indexOf(lock, false);
+		int[] temp = new int[lockThreads.size()];
+		//check if the addition of the waiting thread caused deadlock
+		if (!checkWaitCycles(temp, lockIndex))
+			return null;
+		//there is a deadlock in the graph
+		Thread[] threads = getThreadsInDeadlock(client);
+		Thread candidate = resolutionCandidate(threads);
+		ISchedulingRule[] locks = realLocksForThread(candidate);
+		Deadlock deadlock = new Deadlock(threads, locks, candidate);
+		//find a thread whose locks can be suspended to resolve the deadlock
+		if (JobManager.DEBUG_LOCKS)
+			reportDeadlock(deadlock);
+		if (JobManager.DEBUG_DEADLOCK)
+			throw new IllegalStateException("Deadlock detected. Caused by thread " + client.getName() + '.'); //$NON-NLS-1$
+		//update the graph to indicate that the locks will now be suspended
+		// to indicate that the lock will be suspended, we set the thread to wait for the lock
+		// when the lock is forced to be released, the entry will be cleared
+		for (int i = 0; i < locks.length; i++) 
+			setToWait(deadlock.getCandidate(), locks[i], true);
+		return deadlock;
+	}
+	/**
+	 * The given thread has stopped waiting for the given lock. 
+	 * Update the graph.
+	 */
+	void lockWaitStop(Thread owner, ISchedulingRule lock) {
+		int lockIndex = indexOf(lock, false);
+		int threadIndex = indexOf(owner, false);
+		//make sure the thread and lock exist in the graph
+		if (threadIndex < 0) {
+			if (JobManager.DEBUG_LOCKS)
+				System.out.println("Thread " + owner.getName() + " was already removed."); //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		if (lockIndex < 0) {
+			if (JobManager.DEBUG_LOCKS)
+				System.out.println("Lock " + lock + " was already removed."); //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		if (graph[threadIndex][lockIndex] != WAITING_FOR_LOCK)
+			Assert.isTrue(false, "Thread " + owner.getName() + " was not waiting for lock " + lock.toString() + " so it could not time out."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		graph[threadIndex][lockIndex] = NO_STATE;
+		reduceGraph(threadIndex, lock);
+	}
+	/**
+	 * Returns true IFF the given thread owns a single lock
+	 */
+	private boolean ownsLocks(Thread cause) {
+		int threadIndex = indexOf(cause, false);
+		for (int j = 0; j < graph[threadIndex].length; j++) {
+			if (graph[threadIndex][j] > NO_STATE)
+				return true;
+		}
+		return false;
+	}
+	/**
+	 * Returns true IFF the given thread owns a single real lock.
+	 * A real lock is a lock that can be suspended.
+	 */
+	private boolean ownsRealLocks(Thread owner) {
+		int threadIndex = indexOf(owner, false);
+		for (int j = 0; j < graph[threadIndex].length; j++) {
+			if (graph[threadIndex][j] > NO_STATE) {
+				Object lock = locks.get(j);
+				if (lock instanceof ILock)
+					return true;
+			}
+		}
+		return false;
+	}
+	/**
+	 * Return true IFF this thread owns rule locks (ie. implicit locks which
+	 * cannot be suspended)
+	 */
+	private boolean ownsRuleLocks(Thread owner) {
+		int threadIndex = indexOf(owner, false);
+		for (int j = 0; j < graph[threadIndex].length; j++) {
+			if (graph[threadIndex][j] > NO_STATE) {
+				Object lock = locks.get(j);
+				if (!(lock instanceof ILock))
+					return true;
+			}
+		}
+		return false;
+	}
+	/**
+	 * Returns an array of real locks that are owned by the given thread.
+	 * Real locks are locks that implement the ILock interface and can be suspended.
+	 */
+	private ISchedulingRule[] realLocksForThread(Thread owner) {
+		int threadIndex = indexOf(owner, false);
+		ArrayList ownedLocks = new ArrayList(1);
+		for (int j = 0; j < graph[threadIndex].length; j++) {
+			if ((graph[threadIndex][j] > NO_STATE) && (locks.get(j) instanceof ILock))
+				ownedLocks.add(locks.get(j));
+		}
+		if (ownedLocks.size() == 0)
+			Assert.isLegal(false, "A thread with no real locks was chosen to resolve deadlock."); //$NON-NLS-1$
+		return (ISchedulingRule[]) ownedLocks.toArray(new ISchedulingRule[ownedLocks.size()]);
+	}
+	/**
+	 * The matrix has been simplified. Check if any unnecessary rows or columns
+	 * can be removed.
+	 */
+	private void reduceGraph(int row, ISchedulingRule lock) {
+		boolean[] emptyColumns = new boolean[locks.size()];
+		Arrays.fill(emptyColumns, false);
+
+		/**
+		 * find all columns that could possibly be empty
+		 * (consist of locks which conflict with the given lock, or of locks which are rules)
+		 */
+		for (int j = 0; j < locks.size(); j++) {
+			if ((lock.isConflicting((ISchedulingRule) locks.get(j))) || !(locks.get(j) instanceof ILock))
+				emptyColumns[j] = true;
+		}
+
+		boolean rowEmpty = true;
+		int numEmpty = 0;
+		//check is the given row is empty
+		for (int j = 0; j < graph[row].length; j++) {
+			if (graph[row][j] != NO_STATE) {
+				rowEmpty = false;
+				break;
+			}
+		}
+		/**
+		 * Check if the possibly empty columns are actually empty.
+		 * If a column is actually empty, remove the corresponding lock from the list of locks
+		 * Start at the last column so that when locks are removed from the list, 
+		 * the index of the remaining locks is unchanged. Store the number of empty columns.
+		 */
+		for (int j = emptyColumns.length - 1; j >= 0; j--) {
+			for (int i = 0; i < graph.length; i++) {
+				if (emptyColumns[j] && (graph[i][j] != NO_STATE)) {
+					emptyColumns[j] = false;
+					break;
+				}
+			}
+			if (emptyColumns[j]) {
+				locks.remove(j);
+				numEmpty++;
+			}
+		}
+		//if no columns or rows are empty, return right away
+		if ((numEmpty == 0) && (!rowEmpty))
+			return;
+
+		if (rowEmpty)
+			lockThreads.remove(row);
+
+		//new graph (the list of locks and the list of threads are  already updated)
+		int[][] temp = new int[lockThreads.size()][locks.size()];
+
+		//the number of rows we need to skip to get the correct entry from the old graph
+		int numRowsSkipped = 0;
+		for (int i = 0; i < graph.length - numRowsSkipped; i++) {
+			if ((i == row) && rowEmpty) {
+				numRowsSkipped++;
+				//check if we need to skip the last row
+				if (i >= graph.length - numRowsSkipped)
+					break;
+			}
+			//the nuber of columns we need to skip to get the correct entry from the old graph
+			//needs to be reset for every new row
+			int numColsSkipped = 0;
+			for (int j = 0; j < graph[i].length - numColsSkipped; j++) {
+				while (emptyColumns[j + numColsSkipped]) {
+					numColsSkipped++;
+					//check if we need to skip the last column
+					if (j >= graph[i].length - numColsSkipped)
+						break;
+				}
+				//need to break out of the outer loop
+				if (j >= graph[i].length - numColsSkipped)
+					break;
+				temp[i][j] = graph[i + numRowsSkipped][j + numColsSkipped];
+			}
+		}
+		graph = null;
+		graph = temp;
+		Assert.isTrue(lockThreads.size() == graph.length, "Rows and threads don't match."); //$NON-NLS-1$
+		Assert.isTrue(locks.size() == ((graph.length > 0) ? graph[0].length : 0), "Columns and locks don't match."); //$NON-NLS-1$
+	}
+	/**
+	 * Adds a 'deadlock detected' message to the log with a stack trace.
+	 */
+	private void reportDeadlock(Deadlock deadlock) {
+		String msg = "Deadlock detected. All locks owned by thread " + deadlock.getCandidate().getName() + " will be suspended."; //$NON-NLS-1$ //$NON-NLS-2$
+		MultiStatus main = new MultiStatus(IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, msg, new IllegalStateException());
+		Thread[] threads = deadlock.getThreads();
+		for (int i = 0; i < threads.length; i++) {
+			Object[] ownedLocks = getOwnedLocks(threads[i]);
+			Object waitLock = getWaitingLock(threads[i]);
+			StringBuffer buf = new StringBuffer("Thread "); //$NON-NLS-1$
+			buf.append(threads[i].getName());
+			buf.append(" has locks: "); //$NON-NLS-1$
+			for (int j = 0; j < ownedLocks.length; j++) {
+				buf.append(ownedLocks[j]);
+				buf.append((j < ownedLocks.length - 1) ? ", " : " "); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+			buf.append("and is waiting for lock "); //$NON-NLS-1$
+			buf.append(waitLock);
+			Status child = new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, buf.toString(), null);
+			main.add(child);
+		}
+		InternalPlatform.getDefault().log(main);
+	}
+	/**
+	 * The number of threads/locks in the graph has changed. Update the
+	 * underlying matrix.
+	 */
+	private void resizeGraph() {
+		// a new row and/or a new column was added to the graph.
+		// since new rows/columns are always added to the end, just transfer
+		// old entries to the new graph, with the same indices.
+		int[][] tempGraph = new int[lockThreads.size()][locks.size()];
+		for (int i = 0; i < graph.length; i++) 
+			System.arraycopy(graph[i], 0, tempGraph[i], 0, graph[i].length);
+		graph = tempGraph;
+		resize = false;
+	}
+	/**
+	 * Get the thread whose locks can be suspended. (ie. all locks it owns are
+	 * actual locks and not rules). Return the first thread in the array by default.
+	 */
+	private Thread resolutionCandidate(Thread[] candidates) {
+		//first look for a candidate that has no scheduling rules
+		for (int i = 0; i < candidates.length; i++) {
+			if (!ownsRuleLocks(candidates[i]))
+				return candidates[i];
+		}
+		//next look for any candidate with a real lock (a lock that can be suspended)
+		for (int i = 0; i < candidates.length; i++) {
+			if (ownsRealLocks(candidates[i]))
+				return candidates[i];
+		}
+		//unnecessary, return the first entry in the array by default
+		return candidates[0];
+	}
+	/**
+	 * The given thread is waiting for the given lock. Update the graph.
+	 */
+	private void setToWait(Thread owner, ISchedulingRule lock, boolean suspend) {
+		boolean needTransfer = false;
+		/**
+		 * if we are adding an entry where a thread is waiting on a scheduling rule,
+		 * then we need to transfer all positive entries for a conflicting rule to the
+		 * newly added rule in order to synchronize the graph.
+		 */
+		if (!suspend && !(lock instanceof ILock))
+			needTransfer = true;
+		int lockIndex = indexOf(lock, !suspend);
+		int threadIndex = indexOf(owner, !suspend);
+		if (resize)
+			resizeGraph();
+
+		graph[threadIndex][lockIndex] = WAITING_FOR_LOCK;
+		if (needTransfer)
+			fillPresentEntries(lock, lockIndex);
+	}
+	/**
+	 * Prints out the current matrix to standard output. 
+	 * Only used for debugging.
+	 */
+	public void toDebugString() {
+		System.out.println(" :: "); //$NON-NLS-1$
+		for (int j = 0; j < locks.size(); j++) {
+			System.out.print(" " + locks.get(j) + ','); //$NON-NLS-1$
+		}
+		System.out.println();
+		for (int i = 0; i < graph.length; i++) {
+			System.out.print(" " + ((Thread) lockThreads.get(i)).getName() + " : "); //$NON-NLS-1$ //$NON-NLS-2$
+			for (int j = 0; j < graph[i].length; j++) {
+				System.out.print(" " + graph[i][j] + ','); //$NON-NLS-1$
+			}
+			System.out.println();
+		}
+		System.out.println("-------"); //$NON-NLS-1$
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/LockManager.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/LockManager.java
index e16a09f..a3005a4 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/LockManager.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/LockManager.java
@@ -1,221 +1,221 @@
-/**********************************************************************

- * Copyright (c) 2003 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.core.internal.jobs;

-

-import java.util.HashMap;

-import java.util.Stack;

-import org.eclipse.core.internal.runtime.InternalPlatform;

-import org.eclipse.core.internal.runtime.Policy;

-import org.eclipse.core.runtime.*;

-import org.eclipse.core.runtime.jobs.ISchedulingRule;

-import org.eclipse.core.runtime.jobs.LockListener;

-

-public class LockManager {

-

-	/**

-	 * This class captures the state of suspended locks.  Locks are suspended if

-	 * deadlock is detected.

-	 */

-	private static class LockState {

-		private int depth;

-		private OrderedLock lock;

-		/**

-		 * Suspends ownership of the given lock, and returns the saved state.

-		 */

-		protected static LockState suspend(OrderedLock lock) {

-			LockState state = new LockState();

-			state.lock = lock;

-			state.depth = lock.forceRelease();

-			return state;

-		}

-		/**

-		 * Re-acquires a suspended lock and reverts to the correct lock depth.

-		 */

-		public void resume() {

-			//spin until the lock is successfully acquired

-			//NOTE: spinning here allows the UI thread to service pending syncExecs

-			//if the UI thread is waiting to acquire a lock.

-			while (true) {

-				try {

-					if (lock.acquire(Long.MAX_VALUE))

-						break;

-				} catch (InterruptedException e) {

-				}

-			}

-			lock.setDepth(depth);

-		}

-	}

-

-	//the lock listener for this lock manager

-	protected LockListener lockListener;

-	/* 

-	 * The internal data structure that stores all the relationships 

-	 * between the locks and the threads that own them.

-	 */

-	private DeadlockDetector locks = new DeadlockDetector();

-	/* 

-	 * Stores thread - stack pairs where every entry in the stack is an array 

-	 * of locks that were suspended while the thread was aquiring more locks

-	 * (a stack is needed because when a thread tries to reaquire suspended locks,

-	 * it can cause deadlock, and some locks it owns can be suspended again)

-	 */

-	private HashMap suspendedLocks = new HashMap();

-

-	public LockManager() {

-	}

-	/* (non-Javadoc)

-	 * Method declared on LockListener

-	 */

-	public void aboutToRelease() {

-		if (lockListener == null)

-			return;

-		try {

-			lockListener.aboutToRelease();

-		} catch (Exception e) {

-			handleException(e);

-		} catch (LinkageError e) {

-			handleException(e);

-		}

-	}

-	/* (non-Javadoc)

-	 * Method declared on LockListener

-	 */

-	public boolean aboutToWait(Thread lockOwner) {

-		if (lockListener == null)

-			return false;

-		try {

-			return lockListener.aboutToWait(lockOwner);

-		} catch (Exception e) {

-			handleException(e);

-		} catch (LinkageError e) {

-			handleException(e);

-		}

-		return false;

-	}

-	/**

-	 * This thread has just acquired a lock.  Update graph.

-	 */

-	void addLockThread(Thread thread, ISchedulingRule lock) {

-		synchronized (locks) {

-			locks.lockAcquired(thread, lock);

-		}

-	}

-	/**

-	 * This thread has just been refused a lock.  Update graph and check for deadlock.

-	 */

-	void addLockWaitThread(Thread thread, ISchedulingRule lock) {

-		synchronized (locks) {

-			locks.lockWaitStart(thread, lock);

-			if (locks.isDeadlocked()) {

-				Thread candidate = locks.resolutionCandidate(thread, lock);

-				if (JobManager.DEBUG_LOCKS)

-					locks.reportDeadlock(thread, lock, candidate);

-				if (JobManager.DEBUG_DEADLOCK)

-					throw new IllegalStateException("Deadlock detected. Caused by thread " + thread.getName() + '.'); //$NON-NLS-1$

-				ISchedulingRule[] toSuspend = locks.contestedLocksForThread(candidate);

-				LockState[] suspended = new LockState[toSuspend.length];

-				for (int i = 0; i < toSuspend.length; i++) {

-					locks.setToWait(candidate, toSuspend[i]);

-					suspended[i] = LockState.suspend((OrderedLock) toSuspend[i]);

-				}

-				synchronized (suspendedLocks) {

-					Stack prevLocks = (Stack) suspendedLocks.get(candidate);

-					if (prevLocks == null)

-						prevLocks = new Stack();

-

-					prevLocks.push(suspended);

-					suspendedLocks.put(candidate, prevLocks);

-				}

-				locks.deadlockSolved();

-			}

-		}

-	}

-	private static void handleException(Throwable e) {

-		String message = Policy.bind("jobs.internalError"); //$NON-NLS-1$

-		IStatus status;

-		if (e instanceof CoreException) {

-			status = new MultiStatus(IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, message, e);

-			((MultiStatus) status).merge(((CoreException) e).getStatus());

-		} else {

-			status = new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, message, e);

-		}

-		InternalPlatform.getDefault().log(status);

-	}

-	/**

-	 * Returns true IFF the underlying graph is empty.

-	 * Used in debugging.

-	 */

-	public boolean isEmpty() {

-		return locks.isEmpty();

-	}

-	/**

-	 * Returns true IFF this thread either owns, or is waiting for, any locks.

-	 */

-	public boolean isLockOwner() {

-		//all job threads have to be treated as lock owners because UI thread 

-		//may try to join a job

-		Thread current = Thread.currentThread();

-		if (current instanceof Worker)

-			return true;

-		synchronized (locks) {

-			return locks.contains(Thread.currentThread());

-		}

-	}

-	/**

-	 * Creates and returns a new lock.

-	 */

-	public synchronized OrderedLock newLock() {

-		OrderedLock result = new OrderedLock(this);

-		return result;

-	}

-	/**

-	 * Releases all the acquires that were called on the given rule. Needs to be called only once.

-	 */

-	void removeLockCompletely(Thread thread, ISchedulingRule rule) {

-		synchronized (locks) {

-			locks.lockReleasedCompletely(thread, rule);

-		}

-	}

-	/**

-	 * This thread has just released a lock.  Update graph.

-	 */

-	void removeLockThread(Thread thread, ISchedulingRule lock) {

-		synchronized (locks) {

-			locks.lockReleased(thread, lock);

-		}

-	}

-	/**

-	 * This thread has just stopped waiting for a lock. Update graph.

-	 */

-	void removeLockWaitThread(Thread thread, ISchedulingRule lock) {

-		synchronized (locks) {

-			locks.lockWaitStop(thread, lock);

-		}

-	}

-	/**

-	 * Returns all the locks that were suspended while this thread was waiting to acquire another lock.

-	 */

-	void resumeSuspendedLocks(Thread owner) {

-		LockState[] toResume;

-		synchronized (suspendedLocks) {

-			Stack prevLocks = (Stack) suspendedLocks.get(owner);

-			if (prevLocks == null)

-				return;

-			toResume = (LockState[]) prevLocks.pop();

-			if (prevLocks.empty())

-				suspendedLocks.remove(owner);

-		}

-		for (int i = 0; i < toResume.length; i++)

-			toResume[i].resume();

-	}

-	public void setLockListener(LockListener listener) {

-		this.lockListener = listener;

-	}

+/**********************************************************************
+ * Copyright (c) 2003 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.core.internal.jobs;
+
+import java.util.HashMap;
+import java.util.Stack;
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.internal.runtime.Policy;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.LockListener;
+
+/**
+ * Stores the only reference to the graph that contains all the known
+ * relationships between locks, rules, and the threads that own them.
+ * Synchronizes all access to the graph on the only instance that exists in this class.
+ * 
+ * Also stores the state of suspended locks so that they can be reacquired with 
+ * the proper lock depth. 
+ */
+public class LockManager {
+
+	/**
+	 * This class captures the state of suspended locks.
+	 * Locks are suspended if deadlock is detected.
+	 */
+	private static class LockState {
+		private int depth;
+		private OrderedLock lock;
+		/**
+		 * Suspends ownership of the given lock, and returns the saved state.
+		 */
+		protected static LockState suspend(OrderedLock lock) {
+			LockState state = new LockState();
+			state.lock = lock;
+			state.depth = lock.forceRelease();
+			return state;
+		}
+		/**
+		 * Re-acquires a suspended lock and reverts to the correct lock depth.
+		 */
+		public void resume() {
+			//spin until the lock is successfully acquired
+			//NOTE: spinning here allows the UI thread to service pending syncExecs
+			//if the UI thread is waiting to acquire a lock.
+			while (true) {
+				try {
+					if (lock.acquire(Long.MAX_VALUE))
+						break;
+				} catch (InterruptedException e) {
+				}
+			}
+			lock.setDepth(depth);
+		}
+	}
+
+	//the lock listener for this lock manager
+	protected LockListener lockListener;
+	/* 
+	 * The internal data structure that stores all the relationships 
+	 * between the locks (or rules) and the threads that own them.
+	 */
+	private DeadlockDetector locks = new DeadlockDetector();
+	/* 
+	 * Stores thread - stack pairs where every entry in the stack is an array 
+	 * of locks that were suspended while the thread was aquiring more locks
+	 * (a stack is needed because when a thread tries to reaquire suspended locks,
+	 * it can cause deadlock, and some locks it owns can be suspended again)
+	 */
+	private HashMap suspendedLocks = new HashMap();
+
+	public LockManager() {
+	}
+	/* (non-Javadoc)
+	 * Method declared on LockListener
+	 */
+	public void aboutToRelease() {
+		if (lockListener == null)
+			return;
+		try {
+			lockListener.aboutToRelease();
+		} catch (Exception e) {
+			handleException(e);
+		} catch (LinkageError e) {
+			handleException(e);
+		}
+	}
+	/* (non-Javadoc)
+	 * Method declared on LockListener
+	 */
+	public boolean aboutToWait(Thread lockOwner) {
+		if (lockListener == null)
+			return false;
+		try {
+			return lockListener.aboutToWait(lockOwner);
+		} catch (Exception e) {
+			handleException(e);
+		} catch (LinkageError e) {
+			handleException(e);
+		}
+		return false;
+	}
+	/**
+	 * This thread has just acquired a lock.  Update graph.
+	 */
+	void addLockThread(Thread thread, ISchedulingRule lock) {
+		synchronized (locks) {
+			locks.lockAcquired(thread, lock);
+		}
+	}
+	/**
+	 * This thread has just been refused a lock.  Update graph and check for deadlock.
+	 */
+	void addLockWaitThread(Thread thread, ISchedulingRule lock) {
+		synchronized (locks) {
+			Deadlock found = locks.lockWaitStart(thread, lock);
+			// if deadlock was detected, the found variable will contain all the information about it,
+			// including which locks to suspend for which thread to resolve the deadlock.
+			if (found != null) {
+				ISchedulingRule[] toSuspend = found.getLocks();
+				LockState[] suspended = new LockState[toSuspend.length];
+				for (int i = 0; i < toSuspend.length; i++) 
+					suspended[i] = LockState.suspend((OrderedLock) toSuspend[i]);
+				synchronized (suspendedLocks) {
+					Stack prevLocks = (Stack) suspendedLocks.get(found.getCandidate());
+					if (prevLocks == null)
+						prevLocks = new Stack();
+					prevLocks.push(suspended);
+					suspendedLocks.put(found.getCandidate(), prevLocks);
+				}
+			}
+		}
+	}
+	private static void handleException(Throwable e) {
+		String message = Policy.bind("jobs.internalError"); //$NON-NLS-1$
+		IStatus status;
+		if (e instanceof CoreException) {
+			status = new MultiStatus(IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, message, e);
+			((MultiStatus) status).merge(((CoreException) e).getStatus());
+		} else {
+			status = new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.PLUGIN_ERROR, message, e);
+		}
+		InternalPlatform.getDefault().log(status);
+	}
+	/**
+	 * Returns true IFF the underlying graph is empty.
+	 * For debugging purposes only.
+	 */
+	public boolean isEmpty() {
+		return locks.isEmpty();
+	}
+	/**
+	 * Returns true IFF this thread either owns, or is waiting for, any locks or rules.
+	 */
+	public boolean isLockOwner() {
+		//all job threads have to be treated as lock owners because UI thread 
+		//may try to join a job
+		Thread current = Thread.currentThread();
+		if (current instanceof Worker)
+			return true;
+		synchronized (locks) {
+			return locks.contains(Thread.currentThread());
+		}
+	}
+	/**
+	 * Creates and returns a new lock.
+	 */
+	public synchronized OrderedLock newLock() {
+		return new OrderedLock(this);
+	}
+	/**
+	 * Releases all the acquires that were called on the given rule. Needs to be called only once.
+	 */
+	void removeLockCompletely(Thread thread, ISchedulingRule rule) {
+		synchronized (locks) {
+			locks.lockReleasedCompletely(thread, rule);
+		}
+	}
+	/**
+	 * This thread has just released a lock.  Update graph.
+	 */
+	void removeLockThread(Thread thread, ISchedulingRule lock) {
+		synchronized (locks) {
+			locks.lockReleased(thread, lock);
+		}
+	}
+	/**
+	 * This thread has just stopped waiting for a lock. Update graph.
+	 */
+	void removeLockWaitThread(Thread thread, ISchedulingRule lock) {
+		synchronized (locks) {
+			locks.lockWaitStop(thread, lock);
+		}
+	}
+	/**
+	 * Resumes all the locks that were suspended while this thread was waiting to acquire another lock.
+	 */
+	void resumeSuspendedLocks(Thread owner) {
+		LockState[] toResume;
+		synchronized (suspendedLocks) {
+			Stack prevLocks = (Stack) suspendedLocks.get(owner);
+			if (prevLocks == null)
+				return;
+			toResume = (LockState[]) prevLocks.pop();
+			if (prevLocks.empty())
+				suspendedLocks.remove(owner);
+		}
+		for (int i = 0; i < toResume.length; i++)
+			toResume[i].resume();
+	}
+	public void setLockListener(LockListener listener) {
+		this.lockListener = listener;
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/OrderedLock.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/OrderedLock.java
index 1f68923..2a03e14 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/OrderedLock.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/jobs/OrderedLock.java
@@ -1,260 +1,264 @@
-/**********************************************************************

- * Copyright (c) 2003 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.core.internal.jobs;

-

-import org.eclipse.core.internal.runtime.Assert;

-import org.eclipse.core.runtime.jobs.ILock;

-import org.eclipse.core.runtime.jobs.ISchedulingRule;

-

-/**

- * A lock used to control write access to an exclusive resource.

- * 

- * The lock avoids circular waiting deadlocks by ensuring that locks

- * are always acquired in a strict order.  This makes it impossible for n such 

- * locks to deadlock while waiting for each other.  The downside is that this means

- * that during an interval when a process owns a lock, it can be forced

- * to give the lock up and wait until all locks it requires become

- * available.  This removes the feature of exclusive access to the

- * resource in contention for the duration between acquire() and

- * release() calls.

- * 

- * The lock implementation prevents starvation by granting the

- * lock in the same order in which acquire() requests arrive. In

- * this scheme, starvation is only possible if a thread retains

- * a lock indefinitely.

- */

-public class OrderedLock implements ILock, ISchedulingRule {

-

-	private static final boolean DEBUG = false;

-	/**

-	 * Locks are sequentially ordered for debugging purposes.

-	 */

-	private static int nextLockNumber = 0;

-	/**

-	 * The thread of the operation that currently owns the lock.

-	 */

-	private volatile Thread currentOperationThread;

-	/**

-	 * Records the number of successive acquires in the same

-	 * thread. The lock is released only when the depth

-	 * reaches zero.

-	 */

-	private int depth;

-	/**

-	 * The manager that implements the deadlock detection and resolution protocol.

-	 */

-	private final LockManager manager;

-	private final int number;

-	/**

-	 * Queue of semaphores for operations currently waiting

-	 * on the lock.

-	 */

-	private final Queue operations = new Queue();

-

-	/**

-	 * Creates a new workspace lock.

-	 */

-	OrderedLock(LockManager manager) {

-		this.manager = manager;

-		this.number = nextLockNumber++;

-	}

-	/* (non-Javadoc)

-	 * @see Locks.ILock#acquire()

-	 */

-	public void acquire() {

-		while (true) {

-			try {

-				if (acquire(Long.MAX_VALUE))

-					return;

-			} catch (InterruptedException e) {

-			}

-		}

-	}

-	/* (non-Javadoc)

-	 * @see Locks.ILock#acquire(long)

-	 */

-	public boolean acquire(long delay) throws InterruptedException {

-		if (Thread.interrupted())

-			throw new InterruptedException();

-

-		boolean success = false;

-		if (delay <= 0)

-			return attempt();

-		else {

-			Semaphore semaphore = createSemaphore();

-			if (semaphore == null)

-				return true;

-			else {

-				if (DEBUG)

-					System.out.println("[" + Thread.currentThread() + "] Operation waiting to be executed... " + this); //$NON-NLS-1$ //$NON-NLS-2$

-

-				success = doAcquire(semaphore, delay);

-				manager.resumeSuspendedLocks(Thread.currentThread());

-				if (DEBUG && success)

-					System.out.println("[" + Thread.currentThread() + "] Operation started... " + this); //$NON-NLS-1$ //$NON-NLS-2$

-				else if (DEBUG)

-					System.out.println("[" + Thread.currentThread() + "] Operation timed out... " + this); //$NON-NLS-1$ //$NON-NLS-2$	

-			}

-		}

-		return success;

-	}

-	/**

-	 * Attempts to acquire the lock.  Returns false if the lock is not available and

-	 * true if the lock has been successfully acquired.

-	 */

-	private synchronized boolean attempt() {

-		//return true if we already own the lock

-		//also, if nobody is waiting, grant the lock immediately

-		if ((currentOperationThread == Thread.currentThread()) || (currentOperationThread == null && operations.isEmpty())) {

-			depth++;

-			setCurrentOperationThread(Thread.currentThread());

-			return true;

-		}

-		return false;

-	}

-	/* (non-Javadoc)

-	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)

-	 */

-	public boolean contains(ISchedulingRule rule) {

-		return false;

-	}

-	/**

-	 * Returns null if acquired and a Semaphore object otherwise.

-	 */

-	private synchronized Semaphore createSemaphore() {

-		return attempt() ? null : enqueue(new Semaphore(Thread.currentThread()));

-	}

-	/**

-	 * Attempts to acquire this lock.  Callers will block  until this lock comes available to 

-	 * them, or until the specified delay has elapsed.

-	 */

-	private boolean doAcquire(Semaphore semaphore, long delay) throws InterruptedException {

-		boolean success = false;

-		//notify hook to service pending syncExecs before falling asleep

-		if (manager.aboutToWait(this.currentOperationThread)) {

-			//hook granted immediate access

-			//remove semaphore for the lock request from the queue

-			//do not log in graph because this thread did not really get the lock

-			operations.remove(semaphore);

-			depth++;

-			manager.addLockThread(currentOperationThread, this);

-			return true;

-		}

-		manager.addLockWaitThread(Thread.currentThread(), this);

-		try {

-			success = semaphore.acquire(delay);

-		} catch (InterruptedException e) {

-			if (DEBUG)

-				System.out.println("[" + Thread.currentThread() + "] Operation interrupted while waiting... :-|"); //$NON-NLS-1$ //$NON-NLS-2$

-			throw e;

-		}

-		if (success) {

-			depth++;

-			updateCurrentOperation();

-		} else {

-			//operation timed out

-			//remove request semaphore from queue and update graph

-			operations.remove(semaphore);

-			manager.removeLockWaitThread(Thread.currentThread(), this);

-		}

-		return success;

-	}

-	/**

-	 * Releases this lock from the thread that used to own it.

-	 * Grants this lock to the next thread in the queue.  

-	 */

-	private synchronized void doRelease() {

-		//notify hook

-		manager.aboutToRelease();

-		depth = 0;

-		Semaphore next = (Semaphore) operations.peek();

-		setCurrentOperationThread(null);

-		if (next != null)

-			next.release();

-	}

-	/**

-	 * If there is another semaphore with the same runnable in the

-	 * queue, the other is returned and the new one is not added.

-	 */

-	private synchronized Semaphore enqueue(Semaphore newSemaphore) {

-		Semaphore semaphore = (Semaphore) operations.get(newSemaphore);

-		if (semaphore == null) {

-			operations.enqueue(newSemaphore);

-			return newSemaphore;

-		}

-		return semaphore;

-	}

-	/**

-	 * Suspend this lock by granting the lock to the next lock in the queue.

-	 * Return the depth of the suspended lock. 

-	 */

-	protected int forceRelease() {

-		int oldDepth = depth;

-		doRelease();

-		return oldDepth;

-	}

-	/* (non-Javadoc)

-	 * @see Locks.ILock#getDepth()

-	 */

-	public int getDepth() {

-		return depth;

-	}

-	/* (non-Javadoc)

-	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)

-	 */

-	public boolean isConflicting(ISchedulingRule rule) {

-		return rule == this;

-	}

-	/* (non-Javadoc)

-	 * @see Locks.ILock#release()

-	 */

-	public void release() {

-		if (depth == 0)

-			return;

-		//only release the lock when the depth reaches zero

-		Assert.isTrue(depth >= 0, "Lock released too many times"); //$NON-NLS-1$

-		if (--depth == 0)

-			doRelease();

-		else

-			manager.removeLockThread(currentOperationThread, this);

-	}

-	/**

-	 * If newThread is null, release this lock from its previous owner.

-	 * If newThread is not null, grant this lock to newThread.

-	 */

-	private void setCurrentOperationThread(Thread newThread) {

-		if ((currentOperationThread != null) && (newThread == null))

-			manager.removeLockThread(currentOperationThread, this);

-		this.currentOperationThread = newThread;

-		if (currentOperationThread != null)

-			manager.addLockThread(currentOperationThread, this);

-	}

-	/**

-	 * Forces the lock to be at the given depth.  Used when re-acquiring a suspended

-	 * lock.

-	 */

-	protected void setDepth(int newDepth) {

-		this.depth = newDepth;

-	}

-	/**

-	 * For debugging purposes only.

-	 */

-	public String toString() {

-		return "OrderedLock (" + number + ")"; //$NON-NLS-1$ //$NON-NLS-2$

-	}

-	/**

-	 * This lock has just been granted to a new thread (the thread waited for it).

-	 * Remove the request from the queue and update both the graph and the lock.

-	 */

-	private synchronized void updateCurrentOperation() {

-		operations.dequeue();

-		setCurrentOperationThread(Thread.currentThread());

-	}

+/**********************************************************************
+ * Copyright (c) 2003 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.core.internal.jobs;
+
+import org.eclipse.core.internal.runtime.Assert;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * A lock used to control write access to an exclusive resource.
+ * 
+ * The lock avoids circular waiting deadlocks by detecting the deadlocks
+ * and resolving them through the suspension of all locks owned by one 
+ * of the threads involved in the deadlock. This makes it impossible for n such 
+ * locks to deadlock while waiting for each other.  The downside is that this means
+ * that during an interval when a process owns a lock, it can be forced
+ * to give the lock up and wait until all locks it requires become
+ * available.  This removes the feature of exclusive access to the
+ * resource in contention for the duration between acquire() and
+ * release() calls.
+ * 
+ * The lock implementation prevents starvation by granting the
+ * lock in the same order in which acquire() requests arrive. In
+ * this scheme, starvation is only possible if a thread retains
+ * a lock indefinitely.
+ */
+public class OrderedLock implements ILock, ISchedulingRule {
+
+	private static final boolean DEBUG = false;
+	/**
+	 * Locks are sequentially ordered for debugging purposes.
+	 */
+	private static int nextLockNumber = 0;
+	/**
+	 * The thread of the operation that currently owns the lock.
+	 */
+	private volatile Thread currentOperationThread;
+	/**
+	 * Records the number of successive acquires in the same
+	 * thread. The lock is released only when the depth
+	 * reaches zero.
+	 */
+	private int depth;
+	/**
+	 * The manager that implements the deadlock detection and resolution protocol.
+	 */
+	private final LockManager manager;
+	private final int number;
+	/**
+	 * Queue of semaphores for threads currently waiting
+	 * on the lock.
+	 */
+	private final Queue operations = new Queue();
+
+	/**
+	 * Creates a new workspace lock.
+	 */
+	OrderedLock(LockManager manager) {
+		this.manager = manager;
+		this.number = nextLockNumber++;
+	}
+	/* (non-Javadoc)
+	 * @see Locks.ILock#acquire()
+	 */
+	public void acquire() {
+		while (true) {
+			try {
+				if (acquire(Long.MAX_VALUE))
+					return;
+			} catch (InterruptedException e) {
+			}
+		}
+	}
+	/* (non-Javadoc)
+	 * @see Locks.ILock#acquire(long)
+	 */
+	public boolean acquire(long delay) throws InterruptedException {
+		if (Thread.interrupted())
+			throw new InterruptedException();
+
+		boolean success = false;
+		if (delay <= 0)
+			return attempt();
+		else {
+			Semaphore semaphore = createSemaphore();
+			if (semaphore == null)
+				return true;
+			else {
+				if (DEBUG)
+					System.out.println("[" + Thread.currentThread() + "] Operation waiting to be executed... " + this); //$NON-NLS-1$ //$NON-NLS-2$
+
+				success = doAcquire(semaphore, delay);
+				manager.resumeSuspendedLocks(Thread.currentThread());
+				if (DEBUG && success)
+					System.out.println("[" + Thread.currentThread() + "] Operation started... " + this); //$NON-NLS-1$ //$NON-NLS-2$
+				else if (DEBUG)
+					System.out.println("[" + Thread.currentThread() + "] Operation timed out... " + this); //$NON-NLS-1$ //$NON-NLS-2$	
+			}
+		}
+		return success;
+	}
+	/**
+	 * Attempts to acquire the lock.  Returns false if the lock is not available and
+	 * true if the lock has been successfully acquired.
+	 */
+	private synchronized boolean attempt() {
+		//return true if we already own the lock
+		//also, if nobody is waiting, grant the lock immediately
+		if ((currentOperationThread == Thread.currentThread()) || (currentOperationThread == null && operations.isEmpty())) {
+			depth++;
+			setCurrentOperationThread(Thread.currentThread());
+			return true;
+		}
+		return false;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+	 */
+	public boolean contains(ISchedulingRule rule) {
+		return false;
+	}
+	/**
+	 * Returns null if acquired and a Semaphore object otherwise.
+	 */
+	private synchronized Semaphore createSemaphore() {
+		return attempt() ? null : enqueue(new Semaphore(Thread.currentThread()));
+	}
+	/**
+	 * Attempts to acquire this lock.  Callers will block  until this lock comes available to 
+	 * them, or until the specified delay has elapsed.
+	 */
+	private boolean doAcquire(Semaphore semaphore, long delay) throws InterruptedException {
+		boolean success = false;
+		//notify hook to service pending syncExecs before falling asleep
+		if (manager.aboutToWait(this.currentOperationThread)) {
+			//hook granted immediate access
+			//remove semaphore for the lock request from the queue
+			//do not log in graph because this thread did not really get the lock
+			operations.remove(semaphore);
+			depth++;
+			manager.addLockThread(currentOperationThread, this);
+			return true;
+		}
+		manager.addLockWaitThread(Thread.currentThread(), this);
+		try {
+			success = semaphore.acquire(delay);
+		} catch (InterruptedException e) {
+			if (DEBUG)
+				System.out.println("[" + Thread.currentThread() + "] Operation interrupted while waiting... :-|"); //$NON-NLS-1$ //$NON-NLS-2$
+			throw e;
+		}
+		if (success) {
+			depth++;
+			updateCurrentOperation();
+		} else {
+			//operation timed out
+			//remove request semaphore from queue and update graph
+			operations.remove(semaphore);
+			manager.removeLockWaitThread(Thread.currentThread(), this);
+		}
+		return success;
+	}
+	/**
+	 * Releases this lock from the thread that used to own it.
+	 * Grants this lock to the next thread in the queue.  
+	 */
+	private synchronized void doRelease() {
+		//notify hook
+		manager.aboutToRelease();
+		depth = 0;
+		Semaphore next = (Semaphore) operations.peek();
+		setCurrentOperationThread(null);
+		if (next != null)
+			next.release();
+	}
+	/**
+	 * If there is another semaphore with the same runnable in the
+	 * queue, the other is returned and the new one is not added.
+	 */
+	private synchronized Semaphore enqueue(Semaphore newSemaphore) {
+		Semaphore semaphore = (Semaphore) operations.get(newSemaphore);
+		if (semaphore == null) {
+			operations.enqueue(newSemaphore);
+			return newSemaphore;
+		}
+		return semaphore;
+	}
+	/**
+	 * Suspend this lock by granting the lock to the next lock in the queue.
+	 * Return the depth of the suspended lock. 
+	 */
+	protected int forceRelease() {
+		int oldDepth = depth;
+		doRelease();
+		return oldDepth;
+	}
+	/* (non-Javadoc)
+	 * @see Locks.ILock#getDepth()
+	 */
+	public int getDepth() {
+		return depth;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+	 */
+	public boolean isConflicting(ISchedulingRule rule) {
+		return rule == this;
+	}
+	/* (non-Javadoc)
+	 * @see Locks.ILock#release()
+	 */
+	public void release() {
+		if (depth == 0)
+			return;
+		//only release the lock when the depth reaches zero
+		Assert.isTrue(depth >= 0, "Lock released too many times"); //$NON-NLS-1$
+		if (--depth == 0)
+			doRelease();
+		else
+			manager.removeLockThread(currentOperationThread, this);
+	}
+	/**
+	 * If newThread is null, release this lock from its previous owner.
+	 * If newThread is not null, grant this lock to newThread.
+	 */
+	private void setCurrentOperationThread(Thread newThread) {
+		if ((currentOperationThread != null) && (newThread == null))
+			manager.removeLockThread(currentOperationThread, this);
+		this.currentOperationThread = newThread;
+		if (currentOperationThread != null)
+			manager.addLockThread(currentOperationThread, this);
+	}
+	/**
+	 * Forces the lock to be at the given depth.
+	 * Used when re-acquiring a suspended lock.
+	 */
+	protected void setDepth(int newDepth) {
+		for (int i = depth; i < newDepth; i++) {
+			manager.addLockThread(currentOperationThread, this);
+		}
+		this.depth = newDepth;
+	}
+	/**
+	 * For debugging purposes only.
+	 */
+	public String toString() {
+		return "OrderedLock (" + number + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+	}
+	/**
+	 * This lock has just been granted to a new thread (the thread waited for it).
+	 * Remove the request from the queue and update both the graph and the lock.
+	 */
+	private synchronized void updateCurrentOperation() {
+		operations.dequeue();
+		setCurrentOperationThread(Thread.currentThread());
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/FindSupport.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/FindSupport.java
index 87b23fa..23c0b5c 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/FindSupport.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/FindSupport.java
@@ -1,3 +1,13 @@
+/*******************************************************************************
+ * Copyright (c) 2003 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.core.internal.runtime;
 
 import java.io.IOException;
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java
index e4cf3b2..d0d1252 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java
@@ -1,1222 +1,1219 @@
-/*******************************************************************************

- * Copyright (c) 2000, 2003 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.core.internal.runtime;

-

-import java.io.*;

-import java.net.*;

-import java.util.*;

-import org.eclipse.core.boot.IPlatformRunnable;

-import org.eclipse.core.internal.boot.*;

-import org.eclipse.core.internal.jobs.JobManager;

-import org.eclipse.core.runtime.*;

-import org.eclipse.core.runtime.jobs.IJobManager;

-import org.eclipse.osgi.service.environment.DebugOptions;

-import org.eclipse.osgi.service.environment.EnvironmentInfo;

-import org.osgi.framework.*;

-import org.osgi.util.tracker.ServiceTracker;

-

-/**

- * Bootstrap class for the platform. It is responsible for setting up the

- * platform class loader and passing control to the actual application class

- */

-public final class InternalPlatform implements IPlatform {

-	private BundleContext context;

-	private IExtensionRegistry registry;

-

-	// registry caching mode flags

-	public static boolean cacheRegistry = true;

-	public static boolean lazyRegistryCacheLoading = true;

-

-	private static IAdapterManager adapterManager;

-

-	static ServiceRegistration platformRegistration;

-	static EnvironmentInfo infoService;

-

-	// registry index - used to store last modified times for

-	// registry caching

-	// ASSUMPTION:  Only the plugin registry in 'registry' above

-	// will be cached

-	private static Map regIndex = null;

-

-	private static ArrayList logListeners = new ArrayList(5);

-	private static Map logs = new HashMap(5);

-	private static PlatformLogWriter platformLog = null;

-	private static PlatformMetaArea metaArea;

-	private static boolean initialized;

-	private static Runnable endOfInitializationHandler = null;

-	private static IPath location;

-

-	private ServiceTracker debugTracker;

-	private DebugOptions options = null;

-

-	// Command line args as seen by the Eclipse runtime. allArgs does NOT

-	// include args consumed by the underlying framework (e.g., OSGi)

-	private static String[] allArgs = new String[0];

-	private static String[] appArgs = new String[0];

-	private static String[] frameworkArgs = new String[0];

-

-	// the default workspace directory name

-	private static final String WORKSPACE = "workspace"; //$NON-NLS-1$	

-

-	private static boolean consoleLogEnabled = false;

-	private static ILogListener consoleLog = null;

-	private static AuthorizationDatabase keyring = null;

-	private static String keyringFile = null;

-	private static String password = ""; //$NON-NLS-1$

-	private static boolean splashDown = false;

-	private static String pluginCustomizationFile = null;

-	private static URL installLocation = null;

-

-	private static PlatformMetaAreaLock metaAreaLock = null;

-

-	/**

-	 * Whether to perform the workspace metadata version check.

-	 */

-	private static boolean doVersionCheck = true;

-

-	/**

-	 * Whether to write the version.ini file on shutdown.

-	 */

-	private static boolean writeVersion = true;

-

-	/**

-	 * Name of the plug-in customization file (value "plugin_customization.ini")

-	 * located in the root of the primary feature plug-in and it's 

-	 * companion nl-specific file with externalized strings (value

-	 * "plugin_customization.properties").  The companion file can

-	 * be contained in any nl-specific subdirectories of the primary

-	 * feature or any fragment of this feature.

-	 */

-	private static final String PLUGIN_CUSTOMIZATION_BASE_NAME = "plugin_customization"; //$NON-NLS-1$

-	private static final String PLUGIN_CUSTOMIZATION_FILE_NAME = PLUGIN_CUSTOMIZATION_BASE_NAME + ".ini"; //$NON-NLS-1$

-

-	// execution options

-	private static final String OPTION_DEBUG = PI_RUNTIME + "/debug"; //$NON-NLS-1$

-	private static final String OPTION_DEBUG_SYSTEM_CONTEXT = PI_RUNTIME + "/debug/context"; //$NON-NLS-1$

-	private static final String OPTION_DEBUG_SHUTDOWN = PI_RUNTIME + "/timing/shutdown"; //$NON-NLS-1$

-	private static final String OPTION_DEBUG_REGISTRY = PI_RUNTIME + "/registry/debug"; //$NON-NLS-1$

-	private static final String OPTION_REGISTRY_CACHE_TIMING = IPlatform.PI_RUNTIME + "/registry/cache/timing"; //$NON-NLS-1$

-	private static final String OPTION_DEBUG_REGISTRY_DUMP = PI_RUNTIME + "/registry/debug/dump"; //$NON-NLS-1$

-	private static final String OPTION_DEBUG_PREFERENCES = PI_RUNTIME + "/preferences/debug"; //$NON-NLS-1$

-

-	// command line options

-	private static final String ARG_APPLICATION = "-application"; //$NON-NLS-1$	

-	private static final String ARG_DATA = "-data"; //$NON-NLS-1$	

-	private static final String ARG_INSTALL = "-install"; //$NON-NLS-1$	

-	private static final String LOG = "-consolelog"; //$NON-NLS-1$

-	private static final String KEYRING = "-keyring"; //$NON-NLS-1$

-	protected static final String PASSWORD = "-password"; //$NON-NLS-1$

-	private static final String NOREGISTRYCACHE = "-noregistrycache"; //$NON-NLS-1$	

-	private static final String NO_LAZY_REGISTRY_CACHE_LOADING = "-noLazyRegistryCacheLoading"; //$NON-NLS-1$		

-	private static final String PLUGIN_CUSTOMIZATION = "-plugincustomization"; //$NON-NLS-1$

-	private static final String NO_PACKAGE_PREFIXES = "-noPackagePrefixes"; //$NON-NLS-1$

-	private static final String NO_VERSION_CHECK = "-noversioncheck"; //$NON-NLS-1$

-	private static final String CLASSLOADER_PROPERTIES = "-classloaderProperties"; //$NON-NLS-1$	

-

-	// debug support:  set in loadOptions()

-	public static boolean DEBUG = false;

-	public static boolean DEBUG_CONTEXT = false;

-	public static boolean DEBUG_REGISTRY = false;

-	public static boolean DEBUG_STARTUP = false;

-	public static boolean DEBUG_SHUTDOWN = false;

-	public static String DEBUG_REGISTRY_DUMP = null;

-	public static boolean DEBUG_PREFERENCES = false;

-

-	private static final String KEY_PREFIX = "%"; //$NON-NLS-1$

-	private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$

-

-	private static final String METADATA_VERSION_KEY = "org.eclipse.core.runtime"; //$NON-NLS-1$

-	private static final int METADATA_VERSION_VALUE = 1;

-

-	private static final String PLUGIN_PATH = ".plugin-path"; //$NON-NLS-1$

-

-	private static final InternalPlatform singleton = new InternalPlatform();

-

-	private IPath configMetadataLocation;

-

-	/**

-	 * Private constructor to block instance creation.

-	 */

-	private InternalPlatform() {

-		super();

-	}

-

-	public static InternalPlatform getDefault() {

-		return singleton;

-	}

-

-	/**

-	 * @see Platform

-	 */

-	public void addAuthorizationInfo(URL serverUrl, String realm, String authScheme, Map info) throws CoreException {

-		keyring.addAuthorizationInfo(serverUrl, realm, authScheme, new HashMap(info));

-		keyring.save();

-	}

-	/**

-	 * @see Platform#addLogListener

-	 */

-	public void addLogListener(ILogListener listener) {

-		assertInitialized();

-		synchronized (logListeners) {

-			// replace if already exists (Set behaviour but we use an array

-			// since we want to retain order)

-			logListeners.remove(listener);

-			logListeners.add(listener);

-		}

-	}

-	/**

-	 * @see Platform

-	 */

-	public void addProtectionSpace(URL resourceUrl, String realm) throws CoreException {

-		keyring.addProtectionSpace(resourceUrl, realm);

-		keyring.save();

-	}

-	/**

-	 * @see Platform

-	 */

-	public URL asLocalURL(URL url) throws IOException {

-		//TODO: this is bogus - only to satisfy clients that want to resolve bundle2 URLs

-		if (url.getProtocol().equals("bundle2")) {

-			String bundleName = url.getHost();

-			Bundle bundle = this.context.getBundle(bundleName.substring(0, bundleName.indexOf('_')));

-

-			if (bundle != null) {

-				URL localURL = bundle.getEntry(url.getPath());

-				if (localURL != null)

-					return localURL;

-			}

-			return url;

-		}

-		if (!url.getProtocol().equals(PlatformURLHandler.PROTOCOL))

-			return url;

-		URLConnection connection = url.openConnection();

-		if (!(connection instanceof PlatformURLConnection))

-			return url;

-		String file = connection.getURL().getFile();

-		if (file.endsWith("/") && !file.endsWith(PlatformURLHandler.JAR_SEPARATOR)) //$NON-NLS-1$

-			throw new IOException();

-		return ((PlatformURLConnection) connection).getURLAsLocal();

-	}

-	private void assertInitialized() {

-		//avoid the Policy.bind if assertion is true

-		if (!initialized)

-			Assert.isTrue(false, Policy.bind("meta.appNotInit")); //$NON-NLS-1$

-	}

-	/**

-	 * Closes the open lock file handle, and makes a silent best

-	 * attempt to delete the file.

-	 */

-	private static synchronized void clearLockFile() {

-		if (metaAreaLock != null)

-			metaAreaLock.release();

-	}

-	/**

-	 * Creates a lock file in the meta-area that indicates the meta-area

-	 * is in use, preventing other eclipse instances from concurrently

-	 * using the same meta-area.

-	 */

-	private static synchronized void createLockFile() throws CoreException {

-		if (System.getProperty("org.eclipse.core.runtime.ignoreLockFile") != null) //$NON-NLS-1$

-			return;

-		String lockLocation = metaArea.getLocation().append(PlatformMetaArea.F_LOCK_FILE).toOSString();

-		metaAreaLock = new PlatformMetaAreaLock(new File(lockLocation));

-		try {

-			if (!metaAreaLock.acquire()) {

-				String message = Policy.bind("meta.inUse", lockLocation); //$NON-NLS-1$

-				throw new CoreException(new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.FAILED_WRITE_METADATA, message, null));

-			}

-		} catch (IOException e) {

-			String message = Policy.bind("meta.failCreateLock", lockLocation); //$NON-NLS-1$

-			throw new CoreException(new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.FAILED_WRITE_METADATA, message, e));

-		}

-	}

-

-	/**

-	 * @see Platform

-	 */

-	public void endSplash() {

-		if (DEBUG) {

-			String startString = System.getProperty("eclipse.debug.startupTime");

-			if (startString != null)

-				try {

-					long start = Long.parseLong(startString);

-					long end = System.currentTimeMillis();

-					System.out.println("Startup complete: " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$

-				} catch (NumberFormatException e) {

-					//this is just debugging code -- ok to swallow exception

-				}

-		}

-		if (splashDown)

-			return;

-		splashDown = true;

-		run(endOfInitializationHandler);

-	}

-

-	/**

-	 * @see Platform

-	 */

-	public void flushAuthorizationInfo(URL serverUrl, String realm, String authScheme) throws CoreException {

-		keyring.flushAuthorizationInfo(serverUrl, realm, authScheme);

-		keyring.save();

-	}

-	/**

-	 * @see Platform#getAdapterManager

-	 */

-	public IAdapterManager getAdapterManager() {

-		assertInitialized();

-		if (adapterManager == null)

-			adapterManager = new AdapterManager();

-		return adapterManager;

-	}

-

-	/**

-	 * @see Platform

-	 */

-	public Map getAuthorizationInfo(URL serverUrl, String realm, String authScheme) {

-		Map info = keyring.getAuthorizationInfo(serverUrl, realm, authScheme);

-		return info == null ? null : new HashMap(info);

-	}

-

-	public boolean getBooleanOption(String option, boolean defaultValue) {

-		String value = getOption(option);

-		return (value != null && value.equalsIgnoreCase("true")) || defaultValue; //$NON-NLS-1$

-	}

-

-	public int getIntegerOption(String option, int defaultValue) {

-		String value = getOption(option);

-		if (value == null)

-			return defaultValue;

-		try {

-			return Integer.parseInt(value);

-		} catch (NumberFormatException e) {

-			return defaultValue;

-		}

-	}

-

-	public String[] getAllArgs() {

-		return allArgs;

-	}

-

-	public String[] getAppArgs() {

-		return appArgs;

-	}

-

-	public String[] getFrameworkArgs() {

-		return frameworkArgs;

-	}

-

-	/**

-	 * @see Platform

-	 */

-	public String getOption(String option) {

-		if (options != null)

-			return options.getOption(option);

-		return null;

-	}

-

-	public IJobManager getJobManager() {

-		return JobManager.getInstance();

-	}

-

-	public IPath getLogFileLocation() {

-		return getMetaArea().getLogLocation();

-	}

-

-	/**

-	 * @see Platform#getLocation

-	 */

-	public IPath getLocation() {

-		assertInitialized();

-		return location;

-	}

-	/**

-	 * Returns a log for the given plugin or <code>null</code> if none exists.

-	 */

-	public ILog getLog(Bundle bundle) {

-		ILog result = (ILog) logs.get(bundle);

-		if (result != null)

-			return result;

-		result = new Log(bundle);

-		logs.put(bundle, result);

-		return result;

-	}

-	/**

-	 * Returns the object which defines the location and organization

-	 * of the platform's meta area.

-	 */

-	public PlatformMetaArea getMetaArea() {

-		return metaArea;

-	}

-	/**

-	 * @see Platform

-	 */

-	public String getProtectionSpace(URL resourceUrl) {

-		return keyring.getProtectionSpace(resourceUrl);

-	}

-	private void handleException(ISafeRunnable code, Throwable e) {

-		try {

-			if (!(e instanceof OperationCanceledException)) {

-				// try to figure out which plugin caused the problem.  Derive this from the class

-				// of the code arg.  Attribute to the Runtime plugin if we can't figure it out.

-				Bundle bundle = context.getBundleFor(code);

-				String pluginId = bundle.getGlobalName();

-				String message = Policy.bind("meta.pluginProblems", pluginId); //$NON-NLS-1$

-				IStatus status;

-				if (e instanceof CoreException) {

-					status = new MultiStatus(pluginId, PLUGIN_ERROR, message, e);

-					((MultiStatus) status).merge(((CoreException) e).getStatus());

-				} else {

-					status = new Status(Status.ERROR, pluginId, PLUGIN_ERROR, message, e);

-				}

-				getLog(bundle).log(status);

-			}

-			code.handleException(e);

-		} catch (Throwable th) {

-			th.printStackTrace();

-		}

-	}

-

-	public IExtensionRegistry getRegistry() {

-		return registry;

-	}

-	/**

-	 * Check whether the workspace metadata version matches the expected version. 

-	 * If not, prompt the user for whether to proceed, or exit with no changes.

-	 * Side effects: 

-	 * <ul>

-	 * <li>remember whether to write the metadata version on exit</li>

-	 * <li>bring down the splash screen if exiting</li>

-	 * </ul> 

-	 * 

-	 * @return <code>true</code> to proceed, <code>false</code> to exit with no changes

-	 */

-	public boolean loaderCheckVersion() {

-		// if not doing the version check, then proceed with no check or prompt

-		boolean proceed = !doVersionCheck || checkVersionPrompt();

-		// remember whether to write the version on exit;

-		// don't write it if the user cancelled

-		writeVersion = proceed;

-		// bring down the splash screen if the user cancelled,

-		// since the application won't

-		if (!proceed)

-			endSplash();

-		return proceed;

-	}

-

-	/**

-	 * Internal method for finding and returning a runnable instance of the 

-	 * given class as defined in the specified plug-in.

-	 * The returned object is initialized with the supplied arguments.

-	 * <p>

-	 * This method is used by the platform boot loader; is must

-	 * not be called directly by client code.

-	 * </p>

-	 * @see BootLoader

-	 */

-	public IPlatformRunnable loaderGetRunnable(String applicationName) {

-		assertInitialized();

-		IExtension extension = getRegistry().getExtension(PI_RUNTIME, PT_APPLICATIONS, applicationName);

-		if (extension == null)

-			return null;

-		IConfigurationElement[] configs = extension.getConfigurationElements();

-		if (configs.length == 0)

-			return null;

-		try {

-			IConfigurationElement config = configs[0];

-			return (IPlatformRunnable) config.createExecutableExtension("run"); //$NON-NLS-1$

-		} catch (CoreException e) {

-			getLog(context.getBundle()).log(e.getStatus());

-			return null;

-		} catch (Throwable t) {

-			t.printStackTrace(System.err);

-			return null;

-		}

-	}

-

-	/**

-	 * Internal method for starting up the platform.  The platform is started at the 

-	 * given location.  The plug-ins found at the supplied 

-	 * collection of plug-in locations are loaded into the newly started platform.

-	 * <p>

-	 * This method is used by the platform boot loader; is must

-	 * not be called directly by client code.

-	 * </p>

-	 * @param pluginPath the list of places to look for plug-in specifications.  This may

-	 *		identify individual plug-in files or directories containing directories which contain

-	 *		plug-in files.

-	 * @param location the local filesystem location at which the newly started platform

-	 *		should be started.  If the location does not contain the saved state of a platform,

-	 *		the appropriate structures are created on disk (if required).

-	 * @param bootOptions the debug options loaded by the boot loader.  If the argument

-	 *		is <code>null</code> then debugging enablement was not requested by the

-	 *		person starting the platform.

-	 * @see BootLoader

-	 */

-

-	public void start(BundleContext context) throws Exception {

-		this.context = context;

-		// TODO figure out how to do the splash.  This really should be something 

-		// that is in the OSGi implementation

-		endOfInitializationHandler = getSplashHandler();

-		processCommandLine(infoService.getAllArgs());

-		setupMetaArea();

-		createLockFile();

-		debugTracker = new ServiceTracker(context, DebugOptions.class.getName(), null);

-		debugTracker.open();

-		options = (DebugOptions) debugTracker.getService(); //TODO This is not good, but is avoids problems

-		initializeDebugFlags();

-		initialized = true;

-		platformLog = new PlatformLogWriter(metaArea.getLogLocation().toFile());

-		addLogListener(platformLog);

-		if (consoleLogEnabled) {

-			consoleLog = new PlatformLogWriter(System.out);

-			addLogListener(consoleLog);

-		}

-		loadKeyring();

-		platformRegistration = context.registerService(IPlatform.class.getName(), this, null);

-	}

-	private Runnable getSplashHandler() {

-		ServiceReference[] ref;

-		try {

-			ref = context.getServiceReferences(Runnable.class.getName(), null);

-		} catch (InvalidSyntaxException e) {

-			return null;

-		}

-		// assumes the endInitializationHandler is available as a service

-		// see EclipseStarter.publishSplashScreen

-		for (int i = 0; i < ref.length; i++) {

-			String name = (String) ref[i].getProperty("name");

-			if (name != null && name.equals("splashscreen")) {

-				Runnable result = (Runnable) context.getService(ref[i]);

-				context.ungetService(ref[i]);

-				return result;

-			}

-		}

-		return null;

-	}

-

-	/**

-	 * Check whether the workspace metadata version matches the expected version. 

-	 * If not, prompt the user for whether to proceed, or exit with no changes.

-	 * Side effects: none

-	 * 

-	 * @return <code>true</code> to proceed, <code>false</code> to exit with no changes

-	 */

-	private boolean checkVersionPrompt() {

-		if (checkVersionNoPrompt())

-			return true;

-

-		// run the version check ui class to prompt the user

-		String appId = "org.eclipse.ui.versioncheck.prompt"; //$NON-NLS-1$

-		IPlatformRunnable runnable = loaderGetRunnable(appId);

-		// If there is no UI to confirm the metadata version difference, then just proceed.		

-		if (runnable == null)

-			return true;

-		try {

-			Object result = runnable.run(null);

-			return Boolean.TRUE.equals(result);

-		} catch (Exception e) {

-			// Fail silently since we don't have a UI, but don't proceed if we can't prompt the user.

-			log(new Status(IStatus.ERROR, PI_RUNTIME, 1, Policy.bind("meta.versionCheckRun", appId), null)); //$NON-NLS-1$

-			return false;

-		}

-	}

-

-	//TODO: what else must be done during the platform shutdown? See #loaderShutdown

-	public void stop(BundleContext bundleContext) {

-		assertInitialized();

-		//shutdown all running jobs

-		JobManager.getInstance().shutdown();

-		debugTracker.close();

-		if (writeVersion)

-			writeVersion();

-		clearLockFile();

-		if (platformLog != null)

-			platformLog.shutdown();

-		initialized = false;

-	}

-

-	/**

-	 * Return whether the workspace metadata version matches the expected version. 

-	 * 

-	 * @return <code>true</code> if they match, <code>false</code> if not

-	 */

-	private boolean checkVersionNoPrompt() {

-		File pluginsDir = metaArea.getLocation().append(PlatformMetaArea.F_PLUGIN_DATA).toFile();

-		if (!pluginsDir.exists())

-			return true;

-

-		int version = -1;

-		File versionFile = metaArea.getVersionPath().toFile();

-		if (versionFile.exists()) {

-			try {

-				// Although the version file is not spec'ed to be a Java properties file,

-				// it happens to follow the same format currently, so using Properties

-				// to read it is convenient.

-				Properties props = new Properties();

-				FileInputStream is = new FileInputStream(versionFile);

-				try {

-					props.load(is);

-				} finally {

-					try {

-						is.close();

-					} finally {

-						// ignore

-					}

-				}

-				String prop = props.getProperty(METADATA_VERSION_KEY);

-				// let any NumberFormatException be caught below

-				if (prop != null)

-					version = Integer.parseInt(prop);

-			} catch (Exception e) {

-				// Fail silently. Not a catastrophe if we can't read the version file. We don't

-				// want to fail execution.

-				log(new Status(IStatus.ERROR, PI_RUNTIME, 1, Policy.bind("meta.checkVersion", versionFile.toString()), e)); //$NON-NLS-1$

-			}

-		}

-		return version == METADATA_VERSION_VALUE;

-	}

-

-	/** 

-	 * Write out the version of the metadata into a known file. Overwrite

-	 * any existing file contents.

-	 */

-	private void writeVersion() {

-		File versionFile = metaArea.getVersionPath().toFile();

-		try {

-			OutputStream output = new BufferedOutputStream(new FileOutputStream(versionFile));

-			try {

-				String versionLine = METADATA_VERSION_KEY + "=" + METADATA_VERSION_VALUE; //$NON-NLS-1$

-				output.write(versionLine.getBytes("UTF-8")); //$NON-NLS-1$

-			} finally {

-				output.close();

-			}

-		} catch (Exception e) {

-			// Fail silently. Not a catastrophe if we can't write the version file. We don't

-			// want to fail execution.

-			log(new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, 1, Policy.bind("meta.writeVersion", versionFile.toString()), e)); //$NON-NLS-1$

-		}

-	}

-	/**

-	 * Opens the password database (if any) initally provided to the platform at startup.

-	 */

-	private void loadKeyring() {

-		if (keyringFile != null) {

-			try {

-				keyring = new AuthorizationDatabase(keyringFile, password);

-			} catch (CoreException e) {

-				log(e.getStatus());

-			}

-			if (keyring == null) {

-				//try deleting the file and loading again - format may have changed

-				new java.io.File(keyringFile).delete();

-				try {

-					keyring = new AuthorizationDatabase(keyringFile, password);

-				} catch (CoreException e) {

-					//don't bother logging a second failure

-				}

-			}

-		}

-		if (keyring == null)

-			keyring = new AuthorizationDatabase();

-	}

-	/*

-	 * Finds and loads the options file 

-	 */

-	void initializeDebugFlags() {

-		// load runtime options

-		DEBUG = getBooleanOption(OPTION_DEBUG, false);

-		if (DEBUG) {

-			DEBUG_CONTEXT = getBooleanOption(OPTION_DEBUG_SYSTEM_CONTEXT, false);

-			DEBUG_SHUTDOWN = getBooleanOption(OPTION_DEBUG_SHUTDOWN, false);

-			DEBUG_REGISTRY = getBooleanOption(OPTION_DEBUG_REGISTRY, false);

-			DEBUG_REGISTRY_DUMP = getOption(OPTION_DEBUG_REGISTRY_DUMP);

-			DEBUG_PREFERENCES = getBooleanOption(OPTION_DEBUG_PREFERENCES, false);

-		}

-	}

-	/**

-	 * Notifies all listeners of the platform log.  This includes the console log, if 

-	 * used, and the platform log file.  All Plugin log messages get funnelled

-	 * through here as well.

-	 */

-	public void log(final IStatus status) {

-		assertInitialized();

-		// create array to avoid concurrent access

-		ILogListener[] listeners;

-		synchronized (logListeners) {

-			listeners = (ILogListener[]) logListeners.toArray(new ILogListener[logListeners.size()]);

-		}

-		for (int i = 0; i < listeners.length; i++) {

-			final ILogListener listener = listeners[i];

-			ISafeRunnable code = new ISafeRunnable() {

-				public void run() throws Exception {

-					listener.logging(status, PI_RUNTIME);

-				}

-				public void handleException(Throwable e) {

-				}

-			};

-			run(code);

-		}

-	}

-

-	private String[] processCommandLine(String[] args) {

-		if (args == null)

-			return args;

-		allArgs = args;

-		int[] configArgs = new int[100];

-		//need to initialize the first element to something that could not be an index.

-		configArgs[0] = -1;

-		int configArgIndex = 0;

-		for (int i = 0; i < args.length; i++) {

-			boolean found = false;

-			// check for args without parameters (i.e., a flag arg)

-

-			// look for the log flag

-			if (args[i].equalsIgnoreCase(LOG)) {

-				consoleLogEnabled = true;

-				found = true;

-			}

-

-			// look for the no registry cache flag

-			if (args[i].equalsIgnoreCase(NOREGISTRYCACHE)) {

-				cacheRegistry = false;

-				found = true;

-			}

-

-			// check to see if we should NOT be lazily loading plug-in definitions from the registry cache file.

-			// This will be processed below.

-			if (args[i].equalsIgnoreCase(NO_LAZY_REGISTRY_CACHE_LOADING)) {

-				lazyRegistryCacheLoading = false;

-				found = true;

-			}

-

-			// look for the flag to turn off using package prefixes

-			if (args[i].equalsIgnoreCase(NO_PACKAGE_PREFIXES)) {

-				// ignored

-				// PluginClassLoader.usePackagePrefixes = false;

-				found = true;

-			}

-

-			// look for the flag to turn off the workspace metadata version check

-			if (args[i].equalsIgnoreCase(NO_VERSION_CHECK)) {

-				doVersionCheck = false;

-				found = true;

-			}

-

-			// this option (may have and argument) comes from InternalBootLoader.processCommandLine

-			if (args[i].equalsIgnoreCase(CLASSLOADER_PROPERTIES)) {

-				// ignored

-				found = true;

-			}

-

-			// done checking for args.  Remember where an arg was found 

-			if (found) {

-				configArgs[configArgIndex++] = i;

-				continue;

-			}

-			// check for args with parameters

-			if (i == args.length - 1 || args[i + 1].startsWith("-")) //$NON-NLS-1$

-				continue;

-			String arg = args[++i];

-

-			// look for the default data location

-			if (args[i - 1].equalsIgnoreCase(ARG_DATA)) {

-				location = new Path(arg);

-				found = true;

-			}

-

-			// look for the keyring file

-			if (args[i - 1].equalsIgnoreCase(KEYRING)) {

-				keyringFile = arg;

-				found = true;

-			}

-

-			// look for the user password.  

-			if (args[i - 1].equalsIgnoreCase(PASSWORD)) {

-				password = arg;

-				found = true;

-			}

-

-			// look for the application to run.  

-			if (args[i - 1].equalsIgnoreCase(ARG_APPLICATION)) {

-				System.setProperty("eclipse.application", arg); //$NON-NLS-1$

-				found = true;

-			}

-

-			// look for the plug-in customization file

-			if (args[i - 1].equalsIgnoreCase(PLUGIN_CUSTOMIZATION)) {

-				pluginCustomizationFile = arg;

-				found = true;

-			}

-

-			if (args[i - 1].equalsIgnoreCase(CLASSLOADER_PROPERTIES)) {

-				// ignored

-				found = true;

-			}

-

-			// done checking for args.  Remember where an arg was found 

-			if (found) {

-				configArgs[configArgIndex++] = i - 1;

-				configArgs[configArgIndex++] = i;

-			}

-		}

-

-		// remove all the arguments consumed by this argument parsing

-		if (configArgIndex == 0) {

-			appArgs = args;

-			return args;

-		}

-		appArgs = new String[args.length - configArgIndex];

-		frameworkArgs = new String[configArgIndex];

-		configArgIndex = 0;

-		int j = 0;

-		int k = 0;

-		for (int i = 0; i < args.length; i++) {

-			if (i == configArgs[configArgIndex]) {

-				frameworkArgs[k++] = args[i];

-				configArgIndex++;

-			} else

-				appArgs[j++] = args[i];

-		}

-		return appArgs;

-	}

-

-	/**

-	 * @see Platform#removeLogListener

-	 */

-	public void removeLogListener(ILogListener listener) {

-		assertInitialized();

-		synchronized (logListeners) {

-			logListeners.remove(listener);

-		}

-	}

-	/**

-	 * @see Platform

-	 */

-	public URL resolve(URL url) throws IOException {

-		if (!url.getProtocol().equals(PlatformURLHandler.PROTOCOL))

-			return url;

-		URLConnection connection = url.openConnection();

-		if (connection instanceof PlatformURLConnection)

-			return ((PlatformURLConnection) connection).getResolvedURL();

-		else

-			return url;

-	}

-	public void run(ISafeRunnable code) {

-		Assert.isNotNull(code);

-		try {

-			code.run();

-		} catch (Exception e) {

-			handleException(code, e);

-		} catch (LinkageError e) {

-			handleException(code, e);

-		}

-	}

-	private void run(Runnable handler) {

-		// run end-of-initialization handler

-		if (handler == null)

-			return;

-

-		final Runnable finalHandler = handler;

-		ISafeRunnable code = new ISafeRunnable() {

-			public void run() throws Exception {

-				finalHandler.run();

-			}

-			public void handleException(Throwable e) {

-				// just continue ... the exception has already been logged by

-				// the platform (see handleException(ISafeRunnable)

-			}

-		};

-		run(code);

-	}

-	public void setOption(String option, String value) {

-		if (options != null)

-			options.setOption(option, value);

-	}

-	private void setupMetaArea() throws CoreException {

-		// if a platform location was not found in the arguments, compute one.		

-		if (location == null) {

-			// Default location for the workspace is <user.dir>/workspace/

-			location = new Path(System.getProperty("user.dir")).append(WORKSPACE); //$NON-NLS-1$

-		}

-		if (!location.isAbsolute())

-			location = new Path(System.getProperty("user.dir")).append(location); //$NON-NLS-1$

-		// must create the meta area first as it defines all the other locations.

-		if (location.toFile().exists()) {

-			if (!location.toFile().isDirectory()) {

-				String message = Policy.bind("meta.notDir", location.toString()); //$NON-NLS-1$

-				throw new CoreException(new Status(IStatus.ERROR, PI_RUNTIME, FAILED_WRITE_METADATA, message, null));

-			}

-		}

-		//try infer the device if there isn't one (windows)

-		if (location.getDevice()==null)

-			location = new Path(location.toFile().getAbsolutePath());

-		metaArea = new PlatformMetaArea(location);

-		metaArea.createLocation();

-		if (keyringFile == null)

-			keyringFile = metaArea.getLocation().append(PlatformMetaArea.F_KEYRING).toOSString();

-	}

-

-	public void addLastModifiedTime(String pathKey, long lastModTime) {

-		if (regIndex == null)

-			regIndex = new HashMap(30);

-		regIndex.put(pathKey, new Long(lastModTime));

-	}

-	public Map getRegIndex() {

-		return regIndex;

-	}

-	public void clearRegIndex() {

-		regIndex = null;

-	}

-

-	/**

-	 * Look for the companion preference translation file for a group

-	 * of preferences.  This method will attempt to find a companion 

-	 * ".properties" file first.  This companion file can be in an

-	 * nl-specific directory for this plugin or any of its fragments or 

-	 * it can be in the root of this plugin or the root of any of the

-	 * plugin's fragments. This properties file can be used to translate

-	 * preference values.

-	 * 

-	 * @param pluginDescriptor the descriptor of the plugin

-	 *   who has the preferences

-	 * @param basePrefFileName the base name of the preference file

-	 *   This base will be used to construct the name of the 

-	 *   companion translation file.

-	 *   Example: If basePrefFileName is "plugin_customization",

-	 *   the preferences are in "plugin_customization.ini" and

-	 *   the translations are found in

-	 *   "plugin_customization.properties".

-	 * @return the properties file

-	 * 

-	 * @since 2.0

-	 */

-	public Properties getPreferenceTranslator(String uniqueIdentifier, String basePrefFileName) {

-		return new Properties();

-	}

-

-	/**

-	 * Takes a preference value and a related resource bundle and

-	 * returns the translated version of this value (if one exists).

-	 * 

-	 * @param value the preference value for potential translation

-	 * @param bundle the bundle containing the translated values

-	 * 

-	 * @since 2.0

-	 */

-	public 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 : value.substring(0, ix);

-			String dflt = ix == -1 ? value : value.substring(ix + 1);

-			return props.getProperty(key.substring(1), dflt);

-		}

-		return value;

-	}

-

-	/**

-	 * Applies primary feature-specific overrides to default preferences for the

-	 * plug-in with the given id.

-	 * <p>

-	 * Note that by the time this method is called, the default settings

-	 * for the plug-in itself should have already have been filled in.

-	 * </p>

-	 * 

-	 * @param id the unique identifier of the plug-in

-	 * @param preferences the preference store for the specified plug-in

-	 * 

-	 * @since 2.0

-	 */

-	public void applyPrimaryFeaturePluginDefaultOverrides(String id, Preferences preferences) {

-	}

-

-	/**

-	 * Applies command line-supplied overrides to default preferences for the

-	 * plug-in with the given id.

-	 * <p>

-	 * Note that by the time this method is called, the default settings

-	 * for the plug-in itself should have already have been filled in, along

-	 * with any default overrides supplied by the primary feature.

-	 * </p>

-	 * 

-	 * @param id the unique identifier of the plug-in

-	 * @param preferences the preference store for the specified plug-in

-	 * 

-	 * @since 2.0

-	 */

-	public void applyCommandLinePluginDefaultOverrides(String id, Preferences preferences) {

-

-		if (pluginCustomizationFile == null) {

-			// no command line overrides to process

-			if (DEBUG_PREFERENCES) {

-				System.out.println("Command line argument -pluginCustomization not used."); //$NON-NLS-1$

-			}

-			return;

-		}

-

-		try {

-			URL pluginCustomizationURL = new File(pluginCustomizationFile).toURL();

-			if (DEBUG_PREFERENCES) {

-				System.out.println("Loading preferences from " + pluginCustomizationURL); //$NON-NLS-1$

-			}

-			applyPluginDefaultOverrides(pluginCustomizationURL, id, preferences, null);

-		} catch (MalformedURLException e) {

-			// fail silently

-			if (DEBUG_PREFERENCES) {

-				System.out.println("MalformedURLException creating URL for plugin customization file " //$NON-NLS-1$

-				+pluginCustomizationFile);

-				e.printStackTrace();

-			}

-			return;

-		}

-	}

-

-	/**

-	 * Applies overrides to default preferences for the plug-in with the given id.

-	 * The data is contained in the <code>java.io.Properties</code> style file at 

-	 * the given URL. The property names consist of "/'-separated plug-in id and

-	 * name of preference; e.g., "com.example.myplugin/mypref".

-	 * 

-	 * @param propertiesURL the URL of a <code>java.io.Properties</code> style file

-	 * @param id the unique identifier of the plug-in

-	 * @param preferences the preference store for the specified plug-in

-	 * 

-	 * @since 2.0

-	 */

-	private void applyPluginDefaultOverrides(URL propertiesURL, String id, Preferences preferences, Properties props) {

-

-		// read the java.io.Properties file at the given URL

-		Properties overrides = new Properties();

-		SafeFileInputStream in = null;

-

-		try {

-			File inFile = new File(propertiesURL.getFile());

-			if (!inFile.exists()) {

-				// We don't have a preferences file to worry about

-				if (DEBUG_PREFERENCES) {

-					System.out.println("Preference file " + //$NON-NLS-1$

-					propertiesURL + " not found."); //$NON-NLS-1$

-				}

-				return;

-			}

-

-			in = new SafeFileInputStream(inFile);

-			if (in == null) {

-				// fail quietly

-				if (DEBUG_PREFERENCES) {

-					System.out.println("Failed to open " + //$NON-NLS-1$

-					propertiesURL);

-				}

-				return;

-			}

-			overrides.load(in);

-		} catch (IOException e) {

-			// cannot read ini file - fail silently

-			if (DEBUG_PREFERENCES) {

-				System.out.println("IOException reading preference file " + //$NON-NLS-1$

-				propertiesURL);

-				e.printStackTrace();

-			}

-			return;

-		} finally {

-			try {

-				if (in != null) {

-					in.close();

-				}

-			} catch (IOException e) {

-				// ignore problems closing file

-				if (DEBUG_PREFERENCES) {

-					System.out.println("IOException closing preference file " + //$NON-NLS-1$

-					propertiesURL);

-					e.printStackTrace();

-				}

-			}

-		}

-

-		for (Iterator it = overrides.entrySet().iterator(); it.hasNext();) {

-			Map.Entry entry = (Map.Entry) it.next();

-			String qualifiedKey = (String) entry.getKey();

-			// Keys consist of "/'-separated plug-in id and name of preference

-			// e.g., "com.example.myplugin/mypref"

-			int s = qualifiedKey.indexOf('/');

-			if (s < 0 || s == 0 || s == qualifiedKey.length() - 1) {

-				// skip mangled entry

-				continue;

-			}

-			// plug-in id is non-empty string before "/" 

-			String pluginId = qualifiedKey.substring(0, s);

-			if (pluginId.equals(id)) {

-				// override property in the given plug-in

-				// plig-in-specified property name is non-empty string after "/" 

-				String propertyName = qualifiedKey.substring(s + 1);

-				String value = (String) entry.getValue();

-				value = translatePreference(value, props);

-				preferences.setDefault(propertyName, value);

-			}

-		}

-		if (DEBUG_PREFERENCES) {

-			System.out.println("Preferences now set as follows:"); //$NON-NLS-1$

-			String[] prefNames = preferences.propertyNames();

-			for (int i = 0; i < prefNames.length; i++) {

-				String value = preferences.getString(prefNames[i]);

-				System.out.println("\t" + prefNames[i] + " = " + value); //$NON-NLS-1$ //$NON-NLS-2$

-			}

-			prefNames = preferences.defaultPropertyNames();

-			for (int i = 0; i < prefNames.length; i++) {

-				String value = preferences.getDefaultString(prefNames[i]);

-				System.out.println("\tDefault values: " + prefNames[i] + " = " + value); //$NON-NLS-1$ //$NON-NLS-2$

-			}

-		}

-	}

-	public void setExtensionRegistry(IExtensionRegistry value) {

-		registry = value;

-	}

-	public BundleContext getBundleContext() {

-		return context;

-	}

-	public Bundle getBundle(String id) {

-		return getBundleContext().getBundle(id);

-	}

-

-	public URL getInstallURL() {

-		if (installLocation == null)

-			try {

-				installLocation = new URL((String) System.getProperty("eclipse.installURL"));

-			} catch (MalformedURLException e) {

-				//This can't fail because eclipse.installURL has been set with a valid URL in BootLoader

-			}

-		return installLocation;

-	}

-	public EnvironmentInfo getEnvironmentInfoService() {

-		return infoService;

-	}

-	/**

-	 * @deprecated either look for the IPlatform service or see if the runtime bundle is started.

-	 */

-	public boolean isRunning() {

-		int state = context.getBundle(PI_RUNTIME).getState();

-		return state == Bundle.ACTIVE;

-	}

-

-	/*

-	 * This method is retained for R1.0 compatibility because it is defined as API.

-	 * It's function matches the API description (returns <code>null</code> when

-	 * argument URL is <code>null</code> or cannot be read).

-	 */

-	public URL[] getPluginPath(URL pluginPathLocation /*R1.0 compatibility*/

-	) {

-		InputStream input = null;

-		// first try and see if the given plugin path location exists.

-		if (pluginPathLocation == null)

-			return null;

-		try {

-			input = pluginPathLocation.openStream();

-		} catch (IOException e) {

-			//fall through

-		}

-

-		// if the given path was null or did not exist, look for a plugin path

-		// definition in the install location.

-		if (input == null)

-			try {

-				URL url = new URL(PlatformURLBaseConnection.PLATFORM_URL_STRING + PLUGIN_PATH);

-				input = url.openStream();

-			} catch (MalformedURLException e) {

-				//fall through

-			} catch (IOException e) {

-				//fall through

-			}

-

-		// nothing was found at the supplied location or in the install location

-		if (input == null)

-			return null;

-		// if we found a plugin path definition somewhere so read it and close the location.

-		URL[] result = null;

-		try {

-			try {

-				result = readPluginPath(input);

-			} finally {

-				input.close();

-			}

-		} catch (IOException e) {

-			//let it return null on failure to read

-		}

-		return result;

-	}

-

-	private URL[] readPluginPath(InputStream input) {

-		Properties ini = new Properties();

-		try {

-			ini.load(input);

-		} catch (IOException e) {

-			return null;

-		}

-		Vector result = new Vector(5);

-		for (Enumeration groups = ini.propertyNames(); groups.hasMoreElements();) {

-			String group = (String) groups.nextElement();

-			for (StringTokenizer entries = new StringTokenizer(ini.getProperty(group), ";"); entries.hasMoreElements();) { //$NON-NLS-1$

-				String entry = (String) entries.nextElement();

-				if (!entry.equals("")) //$NON-NLS-1$

-					try {

-						result.addElement(new URL(entry));

-					} catch (MalformedURLException e) {

-						//intentionally ignore bad URLs

-						System.err.println(Policy.bind("ignore.plugin", entry)); //$NON-NLS-1$

-					}

-			}

-		}

-		return (URL[]) result.toArray(new URL[result.size()]);

-	}

-	public IPath getConfigurationMetadataLocation() {

-		if (configMetadataLocation == null)

-			configMetadataLocation = new Path(System.getProperty("osgi.configuration.area"));

-		return configMetadataLocation;

-	}

-

-	public IPath getStateLocation(Bundle bundle, boolean create) {

-		assertInitialized();

-		IPath result = metaArea.getStateLocation(bundle);

-		if (create)

-			result.toFile().mkdirs();

-		return result;

-	}

-	public URL find(Bundle b, IPath path) {

-		return FindSupport.find(b, path);

-	}

-	public URL find(Bundle bundle, IPath path, Map override) {

-		return FindSupport.find(bundle, path, override);

-	}

-	public InputStream openStream(Bundle bundle, IPath file) throws IOException {

-		return FindSupport.openStream(bundle, file, false);

-	}

-	public InputStream openStream(Bundle bundle, IPath file, boolean localized) throws IOException {

-		return FindSupport.openStream(bundle, file, localized);

-	}

-	public IPath getStateLocation(Bundle bundle) {

-		return getStateLocation(bundle, true);

-	}

-	public ResourceBundle getResourceBundle(Bundle bundle) throws MissingResourceException {

-		//TODO

-		throw new NoSuchMethodError("getResourceBundleString");

-	}

-	public String getResourceString(Bundle bundle, String value) {

-		//TODO

-		throw new NoSuchMethodError("getResourceBundleString");

-	}

-	public String getResourceString(Bundle bundle, String value, ResourceBundle resourceBundle) {

-		//TODO

-		throw new NoSuchMethodError("getResourceBundleString");

-	}

-}

+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.core.internal.runtime;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.eclipse.core.boot.IPlatformRunnable;
+import org.eclipse.core.internal.boot.*;
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.osgi.service.environment.DebugOptions;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.osgi.framework.*;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Bootstrap class for the platform. It is responsible for setting up the
+ * platform class loader and passing control to the actual application class
+ */
+public final class InternalPlatform implements IPlatform {
+	private BundleContext context;
+	private IExtensionRegistry registry;
+
+	// registry caching mode flags
+	public static boolean cacheRegistry = true;
+	public static boolean lazyRegistryCacheLoading = true;
+
+	private static IAdapterManager adapterManager;
+
+	static ServiceRegistration platformRegistration;
+	static EnvironmentInfo infoService;
+
+	// registry index - used to store last modified times for
+	// registry caching
+	// ASSUMPTION:  Only the plugin registry in 'registry' above
+	// will be cached
+	private static Map regIndex = null;
+
+	private static ArrayList logListeners = new ArrayList(5);
+	private static Map logs = new HashMap(5);
+	private static PlatformLogWriter platformLog = null;
+	private static PlatformMetaArea metaArea;
+	private static boolean initialized;
+	private static Runnable endOfInitializationHandler = null;
+	private static IPath location;
+
+	private ServiceTracker debugTracker;
+	private DebugOptions options = null;
+
+	// Command line args as seen by the Eclipse runtime. allArgs does NOT
+	// include args consumed by the underlying framework (e.g., OSGi)
+	private static String[] allArgs = new String[0];
+	private static String[] appArgs = new String[0];
+	private static String[] frameworkArgs = new String[0];
+
+	// the default workspace directory name
+	private static final String WORKSPACE = "workspace"; //$NON-NLS-1$	
+
+	private static boolean consoleLogEnabled = false;
+	private static ILogListener consoleLog = null;
+	private static AuthorizationDatabase keyring = null;
+	private static String keyringFile = null;
+	private static String password = ""; //$NON-NLS-1$
+	private static boolean splashDown = false;
+	private static String pluginCustomizationFile = null;
+	private static URL installLocation = null;
+
+	private static PlatformMetaAreaLock metaAreaLock = null;
+
+	/**
+	 * Whether to perform the workspace metadata version check.
+	 */
+	private static boolean doVersionCheck = true;
+
+	/**
+	 * Whether to write the version.ini file on shutdown.
+	 */
+	private static boolean writeVersion = true;
+
+	/**
+	 * Name of the plug-in customization file (value "plugin_customization.ini")
+	 * located in the root of the primary feature plug-in and it's 
+	 * companion nl-specific file with externalized strings (value
+	 * "plugin_customization.properties").  The companion file can
+	 * be contained in any nl-specific subdirectories of the primary
+	 * feature or any fragment of this feature.
+	 */
+	private static final String PLUGIN_CUSTOMIZATION_BASE_NAME = "plugin_customization"; //$NON-NLS-1$
+	private static final String PLUGIN_CUSTOMIZATION_FILE_NAME = PLUGIN_CUSTOMIZATION_BASE_NAME + ".ini"; //$NON-NLS-1$
+
+	// execution options
+	private static final String OPTION_DEBUG = PI_RUNTIME + "/debug"; //$NON-NLS-1$
+	private static final String OPTION_DEBUG_SYSTEM_CONTEXT = PI_RUNTIME + "/debug/context"; //$NON-NLS-1$
+	private static final String OPTION_DEBUG_SHUTDOWN = PI_RUNTIME + "/timing/shutdown"; //$NON-NLS-1$
+	private static final String OPTION_DEBUG_REGISTRY = PI_RUNTIME + "/registry/debug"; //$NON-NLS-1$
+	private static final String OPTION_REGISTRY_CACHE_TIMING = IPlatform.PI_RUNTIME + "/registry/cache/timing"; //$NON-NLS-1$
+	private static final String OPTION_DEBUG_REGISTRY_DUMP = PI_RUNTIME + "/registry/debug/dump"; //$NON-NLS-1$
+	private static final String OPTION_DEBUG_PREFERENCES = PI_RUNTIME + "/preferences/debug"; //$NON-NLS-1$
+
+	// command line options
+	private static final String ARG_APPLICATION = "-application"; //$NON-NLS-1$	
+	private static final String ARG_DATA = "-data"; //$NON-NLS-1$	
+	private static final String ARG_INSTALL = "-install"; //$NON-NLS-1$	
+	private static final String LOG = "-consolelog"; //$NON-NLS-1$
+	private static final String KEYRING = "-keyring"; //$NON-NLS-1$
+	protected static final String PASSWORD = "-password"; //$NON-NLS-1$
+	private static final String NOREGISTRYCACHE = "-noregistrycache"; //$NON-NLS-1$	
+	private static final String NO_LAZY_REGISTRY_CACHE_LOADING = "-noLazyRegistryCacheLoading"; //$NON-NLS-1$		
+	private static final String PLUGIN_CUSTOMIZATION = "-plugincustomization"; //$NON-NLS-1$
+	private static final String NO_PACKAGE_PREFIXES = "-noPackagePrefixes"; //$NON-NLS-1$
+	private static final String NO_VERSION_CHECK = "-noversioncheck"; //$NON-NLS-1$
+	private static final String CLASSLOADER_PROPERTIES = "-classloaderProperties"; //$NON-NLS-1$	
+
+	// debug support:  set in loadOptions()
+	public static boolean DEBUG = false;
+	public static boolean DEBUG_CONTEXT = false;
+	public static boolean DEBUG_REGISTRY = false;
+	public static boolean DEBUG_STARTUP = false;
+	public static boolean DEBUG_SHUTDOWN = false;
+	public static String DEBUG_REGISTRY_DUMP = null;
+	public static boolean DEBUG_PREFERENCES = false;
+
+	private static final String KEY_PREFIX = "%"; //$NON-NLS-1$
+	private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$
+
+	private static final String METADATA_VERSION_KEY = "org.eclipse.core.runtime"; //$NON-NLS-1$
+	private static final int METADATA_VERSION_VALUE = 1;
+
+	private static final String PLUGIN_PATH = ".plugin-path"; //$NON-NLS-1$
+
+	private static final InternalPlatform singleton = new InternalPlatform();
+
+	private IPath configMetadataLocation;
+
+	/**
+	 * Private constructor to block instance creation.
+	 */
+	private InternalPlatform() {
+		super();
+	}
+
+	public static InternalPlatform getDefault() {
+		return singleton;
+	}
+
+	/**
+	 * @see Platform
+	 */
+	public void addAuthorizationInfo(URL serverUrl, String realm, String authScheme, Map info) throws CoreException {
+		keyring.addAuthorizationInfo(serverUrl, realm, authScheme, new HashMap(info));
+		keyring.save();
+	}
+	/**
+	 * @see Platform#addLogListener
+	 */
+	public void addLogListener(ILogListener listener) {
+		assertInitialized();
+		synchronized (logListeners) {
+			// replace if already exists (Set behaviour but we use an array
+			// since we want to retain order)
+			logListeners.remove(listener);
+			logListeners.add(listener);
+		}
+	}
+	/**
+	 * @see Platform
+	 */
+	public void addProtectionSpace(URL resourceUrl, String realm) throws CoreException {
+		keyring.addProtectionSpace(resourceUrl, realm);
+		keyring.save();
+	}
+	/**
+	 * @see Platform
+	 */
+	public URL asLocalURL(URL url) throws IOException {
+		//TODO: this is bogus - only to satisfy clients that want to resolve bundle2 URLs
+		if (url.getProtocol().equals("bundle2")) { //$NON-NLS-1$
+			String bundleName = url.getHost();
+			Bundle bundle = this.context.getBundle(bundleName.substring(0, bundleName.indexOf('_')));
+
+			if (bundle != null) {
+				URL localURL = bundle.getEntry(url.getPath());
+				if (localURL != null)
+					return localURL;
+			}
+			return url;
+		}
+		if (!url.getProtocol().equals(PlatformURLHandler.PROTOCOL))
+			return url;
+		URLConnection connection = url.openConnection();
+		if (!(connection instanceof PlatformURLConnection))
+			return url;
+		String file = connection.getURL().getFile();
+		if (file.endsWith("/") && !file.endsWith(PlatformURLHandler.JAR_SEPARATOR)) //$NON-NLS-1$
+			throw new IOException();
+		return ((PlatformURLConnection) connection).getURLAsLocal();
+	}
+	private void assertInitialized() {
+		//avoid the Policy.bind if assertion is true
+		if (!initialized)
+			Assert.isTrue(false, Policy.bind("meta.appNotInit")); //$NON-NLS-1$
+	}
+	/**
+	 * Closes the open lock file handle, and makes a silent best
+	 * attempt to delete the file.
+	 */
+	private static synchronized void clearLockFile() {
+		if (metaAreaLock != null)
+			metaAreaLock.release();
+	}
+	/**
+	 * Creates a lock file in the meta-area that indicates the meta-area
+	 * is in use, preventing other eclipse instances from concurrently
+	 * using the same meta-area.
+	 */
+	private static synchronized void createLockFile() throws CoreException {
+		if (System.getProperty("org.eclipse.core.runtime.ignoreLockFile") != null) //$NON-NLS-1$
+			return;
+		String lockLocation = metaArea.getLocation().append(PlatformMetaArea.F_LOCK_FILE).toOSString();
+		metaAreaLock = new PlatformMetaAreaLock(new File(lockLocation));
+		try {
+			if (!metaAreaLock.acquire()) {
+				String message = Policy.bind("meta.inUse", lockLocation); //$NON-NLS-1$
+				throw new CoreException(new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.FAILED_WRITE_METADATA, message, null));
+			}
+		} catch (IOException e) {
+			String message = Policy.bind("meta.failCreateLock", lockLocation); //$NON-NLS-1$
+			throw new CoreException(new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IPlatform.FAILED_WRITE_METADATA, message, e));
+		}
+	}
+
+	/**
+	 * @see Platform
+	 */
+	public void endSplash() {
+		if (DEBUG) {
+			String startString = System.getProperty("eclipse.debug.startupTime"); //$NON-NLS-1$
+			if (startString != null)
+				try {
+					long start = Long.parseLong(startString);
+					long end = System.currentTimeMillis();
+					System.out.println("Startup complete: " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+				} catch (NumberFormatException e) {
+					//this is just debugging code -- ok to swallow exception
+				}
+		}
+		if (splashDown)
+			return;
+		splashDown = true;
+		run(endOfInitializationHandler);
+	}
+
+	/**
+	 * @see Platform
+	 */
+	public void flushAuthorizationInfo(URL serverUrl, String realm, String authScheme) throws CoreException {
+		keyring.flushAuthorizationInfo(serverUrl, realm, authScheme);
+		keyring.save();
+	}
+	/**
+	 * @see Platform#getAdapterManager
+	 */
+	public IAdapterManager getAdapterManager() {
+		assertInitialized();
+		if (adapterManager == null)
+			adapterManager = new AdapterManager();
+		return adapterManager;
+	}
+
+	/**
+	 * @see Platform
+	 */
+	public Map getAuthorizationInfo(URL serverUrl, String realm, String authScheme) {
+		Map info = keyring.getAuthorizationInfo(serverUrl, realm, authScheme);
+		return info == null ? null : new HashMap(info);
+	}
+
+	public boolean getBooleanOption(String option, boolean defaultValue) {
+		String value = getOption(option);
+		return (value != null && value.equalsIgnoreCase("true")) || defaultValue; //$NON-NLS-1$
+	}
+
+	public int getIntegerOption(String option, int defaultValue) {
+		String value = getOption(option);
+		if (value == null)
+			return defaultValue;
+		try {
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e) {
+			return defaultValue;
+		}
+	}
+
+	public String[] getAllArgs() {
+		return allArgs;
+	}
+
+	public String[] getAppArgs() {
+		return appArgs;
+	}
+
+	public String[] getFrameworkArgs() {
+		return frameworkArgs;
+	}
+
+	/**
+	 * @see Platform
+	 */
+	public String getOption(String option) {
+		if (options != null)
+			return options.getOption(option);
+		return null;
+	}
+
+	public IJobManager getJobManager() {
+		return JobManager.getInstance();
+	}
+
+	public IPath getLogFileLocation() {
+		return getMetaArea().getLogLocation();
+	}
+
+	/**
+	 * @see Platform#getLocation
+	 */
+	public IPath getLocation() {
+		assertInitialized();
+		return location;
+	}
+	/**
+	 * Returns a log for the given plugin or <code>null</code> if none exists.
+	 */
+	public ILog getLog(Bundle bundle) {
+		ILog result = (ILog) logs.get(bundle);
+		if (result != null)
+			return result;
+		result = new Log(bundle);
+		logs.put(bundle, result);
+		return result;
+	}
+	/**
+	 * Returns the object which defines the location and organization
+	 * of the platform's meta area.
+	 */
+	public PlatformMetaArea getMetaArea() {
+		return metaArea;
+	}
+	/**
+	 * @see Platform
+	 */
+	public String getProtectionSpace(URL resourceUrl) {
+		return keyring.getProtectionSpace(resourceUrl);
+	}
+	private  void handleException(ISafeRunnable code, Throwable e) {
+		if (!(e instanceof OperationCanceledException)) {
+			String pluginId = PI_RUNTIME;
+			String message = Policy.bind("meta.pluginProblems", pluginId); //$NON-NLS-1$
+			IStatus status;
+			if (e instanceof CoreException) {
+				status = new MultiStatus(pluginId, IPlatform.PLUGIN_ERROR, message, e);
+				((MultiStatus)status).merge(((CoreException)e).getStatus());
+			} else {
+				status = new Status(IStatus.ERROR, pluginId, IPlatform.PLUGIN_ERROR, message, e);
+			}
+			log(status); //$NON-NLS-1$
+		}
+		code.handleException(e);
+	}
+
+	public IExtensionRegistry getRegistry() {
+		return registry;
+	}
+	/**
+	 * Check whether the workspace metadata version matches the expected version. 
+	 * If not, prompt the user for whether to proceed, or exit with no changes.
+	 * Side effects: 
+	 * <ul>
+	 * <li>remember whether to write the metadata version on exit</li>
+	 * <li>bring down the splash screen if exiting</li>
+	 * </ul> 
+	 * 
+	 * @return <code>true</code> to proceed, <code>false</code> to exit with no changes
+	 */
+	public boolean loaderCheckVersion() {
+		// if not doing the version check, then proceed with no check or prompt
+		boolean proceed = !doVersionCheck || checkVersionPrompt();
+		// remember whether to write the version on exit;
+		// don't write it if the user cancelled
+		writeVersion = proceed;
+		// bring down the splash screen if the user cancelled,
+		// since the application won't
+		if (!proceed)
+			endSplash();
+		return proceed;
+	}
+
+	/**
+	 * Internal method for finding and returning a runnable instance of the 
+	 * given class as defined in the specified plug-in.
+	 * The returned object is initialized with the supplied arguments.
+	 * <p>
+	 * This method is used by the platform boot loader; is must
+	 * not be called directly by client code.
+	 * </p>
+	 * @see BootLoader
+	 */
+	public IPlatformRunnable loaderGetRunnable(String applicationName) {
+		assertInitialized();
+		IExtension extension = getRegistry().getExtension(PI_RUNTIME, PT_APPLICATIONS, applicationName);
+		if (extension == null)
+			return null;
+		IConfigurationElement[] configs = extension.getConfigurationElements();
+		if (configs.length == 0)
+			return null;
+		try {
+			IConfigurationElement config = configs[0];
+			return (IPlatformRunnable) config.createExecutableExtension("run"); //$NON-NLS-1$
+		} catch (CoreException e) {
+			getLog(context.getBundle()).log(e.getStatus());
+			return null;
+		} catch (Throwable t) {
+			t.printStackTrace(System.err);
+			return null;
+		}
+	}
+
+	/**
+	 * Internal method for starting up the platform.  The platform is started at the 
+	 * given location.  The plug-ins found at the supplied 
+	 * collection of plug-in locations are loaded into the newly started platform.
+	 * <p>
+	 * This method is used by the platform boot loader; is must
+	 * not be called directly by client code.
+	 * </p>
+	 * @param pluginPath the list of places to look for plug-in specifications.  This may
+	 *		identify individual plug-in files or directories containing directories which contain
+	 *		plug-in files.
+	 * @param location the local filesystem location at which the newly started platform
+	 *		should be started.  If the location does not contain the saved state of a platform,
+	 *		the appropriate structures are created on disk (if required).
+	 * @param bootOptions the debug options loaded by the boot loader.  If the argument
+	 *		is <code>null</code> then debugging enablement was not requested by the
+	 *		person starting the platform.
+	 * @see BootLoader
+	 */
+
+	public void start(BundleContext context) throws Exception {
+		this.context = context;
+		// TODO figure out how to do the splash.  This really should be something 
+		// that is in the OSGi implementation
+		endOfInitializationHandler = getSplashHandler();
+		processCommandLine(infoService.getAllArgs());
+		setupMetaArea();
+		createLockFile();
+		debugTracker = new ServiceTracker(context, DebugOptions.class.getName(), null);
+		debugTracker.open();
+		options = (DebugOptions) debugTracker.getService(); //TODO This is not good, but is avoids problems
+		initializeDebugFlags();
+		initialized = true;
+		platformLog = new PlatformLogWriter(metaArea.getLogLocation().toFile());
+		addLogListener(platformLog);
+		if (consoleLogEnabled) {
+			consoleLog = new PlatformLogWriter(System.out);
+			addLogListener(consoleLog);
+		}
+		loadKeyring();
+		platformRegistration = context.registerService(IPlatform.class.getName(), this, null);
+	}
+	private Runnable getSplashHandler() {
+		ServiceReference[] ref;
+		try {
+			ref = context.getServiceReferences(Runnable.class.getName(), null);
+		} catch (InvalidSyntaxException e) {
+			return null;
+		}
+		// assumes the endInitializationHandler is available as a service
+		// see EclipseStarter.publishSplashScreen
+		for (int i = 0; i < ref.length; i++) {
+			String name = (String) ref[i].getProperty("name"); //$NON-NLS-1$
+			if (name != null && name.equals("splashscreen")) { //$NON-NLS-1$
+				Runnable result = (Runnable) context.getService(ref[i]);
+				context.ungetService(ref[i]);
+				return result;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Check whether the workspace metadata version matches the expected version. 
+	 * If not, prompt the user for whether to proceed, or exit with no changes.
+	 * Side effects: none
+	 * 
+	 * @return <code>true</code> to proceed, <code>false</code> to exit with no changes
+	 */
+	private boolean checkVersionPrompt() {
+		if (checkVersionNoPrompt())
+			return true;
+
+		// run the version check ui class to prompt the user
+		String appId = "org.eclipse.ui.versioncheck.prompt"; //$NON-NLS-1$
+		IPlatformRunnable runnable = loaderGetRunnable(appId);
+		// If there is no UI to confirm the metadata version difference, then just proceed.		
+		if (runnable == null)
+			return true;
+		try {
+			Object result = runnable.run(null);
+			return Boolean.TRUE.equals(result);
+		} catch (Exception e) {
+			// Fail silently since we don't have a UI, but don't proceed if we can't prompt the user.
+			log(new Status(IStatus.ERROR, PI_RUNTIME, 1, Policy.bind("meta.versionCheckRun", appId), null)); //$NON-NLS-1$
+			return false;
+		}
+	}
+
+	//TODO: what else must be done during the platform shutdown? See #loaderShutdown
+	public void stop(BundleContext bundleContext) {
+		assertInitialized();
+		//shutdown all running jobs
+		JobManager.getInstance().shutdown();
+		debugTracker.close();
+		if (writeVersion)
+			writeVersion();
+		clearLockFile();
+		if (platformLog != null)
+			platformLog.shutdown();
+		initialized = false;
+	}
+
+	/**
+	 * Return whether the workspace metadata version matches the expected version. 
+	 * 
+	 * @return <code>true</code> if they match, <code>false</code> if not
+	 */
+	private boolean checkVersionNoPrompt() {
+		File pluginsDir = metaArea.getLocation().append(PlatformMetaArea.F_PLUGIN_DATA).toFile();
+		if (!pluginsDir.exists())
+			return true;
+
+		int version = -1;
+		File versionFile = metaArea.getVersionPath().toFile();
+		if (versionFile.exists()) {
+			try {
+				// Although the version file is not spec'ed to be a Java properties file,
+				// it happens to follow the same format currently, so using Properties
+				// to read it is convenient.
+				Properties props = new Properties();
+				FileInputStream is = new FileInputStream(versionFile);
+				try {
+					props.load(is);
+				} finally {
+					try {
+						is.close();
+					} finally {
+						// ignore
+					}
+				}
+				String prop = props.getProperty(METADATA_VERSION_KEY);
+				// let any NumberFormatException be caught below
+				if (prop != null)
+					version = Integer.parseInt(prop);
+			} catch (Exception e) {
+				// Fail silently. Not a catastrophe if we can't read the version file. We don't
+				// want to fail execution.
+				log(new Status(IStatus.ERROR, PI_RUNTIME, 1, Policy.bind("meta.checkVersion", versionFile.toString()), e)); //$NON-NLS-1$
+			}
+		}
+		return version == METADATA_VERSION_VALUE;
+	}
+
+	/** 
+	 * Write out the version of the metadata into a known file. Overwrite
+	 * any existing file contents.
+	 */
+	private void writeVersion() {
+		File versionFile = metaArea.getVersionPath().toFile();
+		try {
+			OutputStream output = new BufferedOutputStream(new FileOutputStream(versionFile));
+			try {
+				String versionLine = METADATA_VERSION_KEY + "=" + METADATA_VERSION_VALUE; //$NON-NLS-1$
+				output.write(versionLine.getBytes("UTF-8")); //$NON-NLS-1$
+			} finally {
+				output.close();
+			}
+		} catch (Exception e) {
+			// Fail silently. Not a catastrophe if we can't write the version file. We don't
+			// want to fail execution.
+			log(new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, 1, Policy.bind("meta.writeVersion", versionFile.toString()), e)); //$NON-NLS-1$
+		}
+	}
+	/**
+	 * Opens the password database (if any) initally provided to the platform at startup.
+	 */
+	private void loadKeyring() {
+		if (keyringFile != null) {
+			try {
+				keyring = new AuthorizationDatabase(keyringFile, password);
+			} catch (CoreException e) {
+				log(e.getStatus());
+			}
+			if (keyring == null) {
+				//try deleting the file and loading again - format may have changed
+				new java.io.File(keyringFile).delete();
+				try {
+					keyring = new AuthorizationDatabase(keyringFile, password);
+				} catch (CoreException e) {
+					//don't bother logging a second failure
+				}
+			}
+		}
+		if (keyring == null)
+			keyring = new AuthorizationDatabase();
+	}
+	/*
+	 * Finds and loads the options file 
+	 */
+	void initializeDebugFlags() {
+		// load runtime options
+		DEBUG = getBooleanOption(OPTION_DEBUG, false);
+		if (DEBUG) {
+			DEBUG_CONTEXT = getBooleanOption(OPTION_DEBUG_SYSTEM_CONTEXT, false);
+			DEBUG_SHUTDOWN = getBooleanOption(OPTION_DEBUG_SHUTDOWN, false);
+			DEBUG_REGISTRY = getBooleanOption(OPTION_DEBUG_REGISTRY, false);
+			DEBUG_REGISTRY_DUMP = getOption(OPTION_DEBUG_REGISTRY_DUMP);
+			DEBUG_PREFERENCES = getBooleanOption(OPTION_DEBUG_PREFERENCES, false);
+		}
+	}
+	/**
+	 * Notifies all listeners of the platform log.  This includes the console log, if 
+	 * used, and the platform log file.  All Plugin log messages get funnelled
+	 * through here as well.
+	 */
+	public void log(final IStatus status) {
+		assertInitialized();
+		// create array to avoid concurrent access
+		ILogListener[] listeners;
+		synchronized (logListeners) {
+			listeners = (ILogListener[]) logListeners.toArray(new ILogListener[logListeners.size()]);
+		}
+		for (int i = 0; i < listeners.length; i++) {
+			final ILogListener listener = listeners[i];
+			ISafeRunnable code = new ISafeRunnable() {
+				public void run() throws Exception {
+					listener.logging(status, PI_RUNTIME);
+				}
+				public void handleException(Throwable e) {
+				}
+			};
+			run(code);
+		}
+	}
+
+	private String[] processCommandLine(String[] args) {
+		if (args == null)
+			return args;
+		allArgs = args;
+		int[] configArgs = new int[100];
+		//need to initialize the first element to something that could not be an index.
+		configArgs[0] = -1;
+		int configArgIndex = 0;
+		for (int i = 0; i < args.length; i++) {
+			boolean found = false;
+			// check for args without parameters (i.e., a flag arg)
+
+			// look for the log flag
+			if (args[i].equalsIgnoreCase(LOG)) {
+				consoleLogEnabled = true;
+				found = true;
+			}
+
+			// look for the no registry cache flag
+			if (args[i].equalsIgnoreCase(NOREGISTRYCACHE)) {
+				cacheRegistry = false;
+				found = true;
+			}
+
+			// check to see if we should NOT be lazily loading plug-in definitions from the registry cache file.
+			// This will be processed below.
+			if (args[i].equalsIgnoreCase(NO_LAZY_REGISTRY_CACHE_LOADING)) {
+				lazyRegistryCacheLoading = false;
+				found = true;
+			}
+
+			// look for the flag to turn off using package prefixes
+			if (args[i].equalsIgnoreCase(NO_PACKAGE_PREFIXES)) {
+				// ignored
+				// PluginClassLoader.usePackagePrefixes = false;
+				found = true;
+			}
+
+			// look for the flag to turn off the workspace metadata version check
+			if (args[i].equalsIgnoreCase(NO_VERSION_CHECK)) {
+				doVersionCheck = false;
+				found = true;
+			}
+
+			// this option (may have and argument) comes from InternalBootLoader.processCommandLine
+			if (args[i].equalsIgnoreCase(CLASSLOADER_PROPERTIES)) {
+				// ignored
+				found = true;
+			}
+
+			// done checking for args.  Remember where an arg was found 
+			if (found) {
+				configArgs[configArgIndex++] = i;
+				continue;
+			}
+			// check for args with parameters
+			if (i == args.length - 1 || args[i + 1].startsWith("-")) //$NON-NLS-1$
+				continue;
+			String arg = args[++i];
+
+			// look for the default data location
+			if (args[i - 1].equalsIgnoreCase(ARG_DATA)) {
+				location = new Path(arg);
+				found = true;
+			}
+
+			// look for the keyring file
+			if (args[i - 1].equalsIgnoreCase(KEYRING)) {
+				keyringFile = arg;
+				found = true;
+			}
+
+			// look for the user password.  
+			if (args[i - 1].equalsIgnoreCase(PASSWORD)) {
+				password = arg;
+				found = true;
+			}
+
+			// look for the application to run.  
+			if (args[i - 1].equalsIgnoreCase(ARG_APPLICATION)) {
+				System.setProperty("eclipse.application", arg); //$NON-NLS-1$
+				found = true;
+			}
+
+			// look for the plug-in customization file
+			if (args[i - 1].equalsIgnoreCase(PLUGIN_CUSTOMIZATION)) {
+				pluginCustomizationFile = arg;
+				found = true;
+			}
+
+			if (args[i - 1].equalsIgnoreCase(CLASSLOADER_PROPERTIES)) {
+				// ignored
+				found = true;
+			}
+
+			// done checking for args.  Remember where an arg was found 
+			if (found) {
+				configArgs[configArgIndex++] = i - 1;
+				configArgs[configArgIndex++] = i;
+			}
+		}
+
+		// remove all the arguments consumed by this argument parsing
+		if (configArgIndex == 0) {
+			appArgs = args;
+			return args;
+		}
+		appArgs = new String[args.length - configArgIndex];
+		frameworkArgs = new String[configArgIndex];
+		configArgIndex = 0;
+		int j = 0;
+		int k = 0;
+		for (int i = 0; i < args.length; i++) {
+			if (i == configArgs[configArgIndex]) {
+				frameworkArgs[k++] = args[i];
+				configArgIndex++;
+			} else
+				appArgs[j++] = args[i];
+		}
+		return appArgs;
+	}
+
+	/**
+	 * @see Platform#removeLogListener
+	 */
+	public void removeLogListener(ILogListener listener) {
+		assertInitialized();
+		synchronized (logListeners) {
+			logListeners.remove(listener);
+		}
+	}
+	/**
+	 * @see Platform
+	 */
+	public URL resolve(URL url) throws IOException {
+		if (!url.getProtocol().equals(PlatformURLHandler.PROTOCOL))
+			return url;
+		URLConnection connection = url.openConnection();
+		if (connection instanceof PlatformURLConnection)
+			return ((PlatformURLConnection) connection).getResolvedURL();
+		else
+			return url;
+	}
+	public void run(ISafeRunnable code) {
+		Assert.isNotNull(code);
+		try {
+			code.run();
+		} catch (Exception e) {
+			handleException(code, e);
+		} catch (LinkageError e) {
+			handleException(code, e);
+		}
+	}
+	private void run(Runnable handler) {
+		// run end-of-initialization handler
+		if (handler == null)
+			return;
+
+		final Runnable finalHandler = handler;
+		ISafeRunnable code = new ISafeRunnable() {
+			public void run() throws Exception {
+				finalHandler.run();
+			}
+			public void handleException(Throwable e) {
+				// just continue ... the exception has already been logged by
+				// the platform (see handleException(ISafeRunnable)
+			}
+		};
+		run(code);
+	}
+	public void setOption(String option, String value) {
+		if (options != null)
+			options.setOption(option, value);
+	}
+	private void setupMetaArea() throws CoreException {
+		// if a platform location was not found in the arguments, compute one.		
+		if (location == null) {
+			// Default location for the workspace is <user.dir>/workspace/
+			location = new Path(System.getProperty("user.dir")).append(WORKSPACE); //$NON-NLS-1$
+		}
+		if (!location.isAbsolute())
+			location = new Path(System.getProperty("user.dir")).append(location); //$NON-NLS-1$
+		// must create the meta area first as it defines all the other locations.
+		if (location.toFile().exists()) {
+			if (!location.toFile().isDirectory()) {
+				String message = Policy.bind("meta.notDir", location.toString()); //$NON-NLS-1$
+				throw new CoreException(new Status(IStatus.ERROR, PI_RUNTIME, FAILED_WRITE_METADATA, message, null));
+			}
+		}
+		//try infer the device if there isn't one (windows)
+		if (location.getDevice()==null)
+			location = new Path(location.toFile().getAbsolutePath());
+		metaArea = new PlatformMetaArea(location);
+		metaArea.createLocation();
+		if (keyringFile == null)
+			keyringFile = metaArea.getLocation().append(PlatformMetaArea.F_KEYRING).toOSString();
+	}
+
+	public void addLastModifiedTime(String pathKey, long lastModTime) {
+		if (regIndex == null)
+			regIndex = new HashMap(30);
+		regIndex.put(pathKey, new Long(lastModTime));
+	}
+	public Map getRegIndex() {
+		return regIndex;
+	}
+	public void clearRegIndex() {
+		regIndex = null;
+	}
+
+	/**
+	 * Look for the companion preference translation file for a group
+	 * of preferences.  This method will attempt to find a companion 
+	 * ".properties" file first.  This companion file can be in an
+	 * nl-specific directory for this plugin or any of its fragments or 
+	 * it can be in the root of this plugin or the root of any of the
+	 * plugin's fragments. This properties file can be used to translate
+	 * preference values.
+	 * 
+	 * @param pluginDescriptor the descriptor of the plugin
+	 *   who has the preferences
+	 * @param basePrefFileName the base name of the preference file
+	 *   This base will be used to construct the name of the 
+	 *   companion translation file.
+	 *   Example: If basePrefFileName is "plugin_customization",
+	 *   the preferences are in "plugin_customization.ini" and
+	 *   the translations are found in
+	 *   "plugin_customization.properties".
+	 * @return the properties file
+	 * 
+	 * @since 2.0
+	 */
+	public Properties getPreferenceTranslator(String uniqueIdentifier, String basePrefFileName) {
+		return new Properties();
+	}
+
+	/**
+	 * Takes a preference value and a related resource bundle and
+	 * returns the translated version of this value (if one exists).
+	 * 
+	 * @param value the preference value for potential translation
+	 * @param bundle the bundle containing the translated values
+	 * 
+	 * @since 2.0
+	 */
+	public 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 : value.substring(0, ix);
+			String dflt = ix == -1 ? value : value.substring(ix + 1);
+			return props.getProperty(key.substring(1), dflt);
+		}
+		return value;
+	}
+
+	/**
+	 * Applies primary feature-specific overrides to default preferences for the
+	 * plug-in with the given id.
+	 * <p>
+	 * Note that by the time this method is called, the default settings
+	 * for the plug-in itself should have already have been filled in.
+	 * </p>
+	 * 
+	 * @param id the unique identifier of the plug-in
+	 * @param preferences the preference store for the specified plug-in
+	 * 
+	 * @since 2.0
+	 */
+	public void applyPrimaryFeaturePluginDefaultOverrides(String id, Preferences preferences) {
+	}
+
+	/**
+	 * Applies command line-supplied overrides to default preferences for the
+	 * plug-in with the given id.
+	 * <p>
+	 * Note that by the time this method is called, the default settings
+	 * for the plug-in itself should have already have been filled in, along
+	 * with any default overrides supplied by the primary feature.
+	 * </p>
+	 * 
+	 * @param id the unique identifier of the plug-in
+	 * @param preferences the preference store for the specified plug-in
+	 * 
+	 * @since 2.0
+	 */
+	public void applyCommandLinePluginDefaultOverrides(String id, Preferences preferences) {
+
+		if (pluginCustomizationFile == null) {
+			// no command line overrides to process
+			if (DEBUG_PREFERENCES) {
+				System.out.println("Command line argument -pluginCustomization not used."); //$NON-NLS-1$
+			}
+			return;
+		}
+
+		try {
+			URL pluginCustomizationURL = new File(pluginCustomizationFile).toURL();
+			if (DEBUG_PREFERENCES) {
+				System.out.println("Loading preferences from " + pluginCustomizationURL); //$NON-NLS-1$
+			}
+			applyPluginDefaultOverrides(pluginCustomizationURL, id, preferences, null);
+		} catch (MalformedURLException e) {
+			// fail silently
+			if (DEBUG_PREFERENCES) {
+				System.out.println("MalformedURLException creating URL for plugin customization file " //$NON-NLS-1$
+				+pluginCustomizationFile);
+				e.printStackTrace();
+			}
+			return;
+		}
+	}
+
+	/**
+	 * Applies overrides to default preferences for the plug-in with the given id.
+	 * The data is contained in the <code>java.io.Properties</code> style file at 
+	 * the given URL. The property names consist of "/'-separated plug-in id and
+	 * name of preference; e.g., "com.example.myplugin/mypref".
+	 * 
+	 * @param propertiesURL the URL of a <code>java.io.Properties</code> style file
+	 * @param id the unique identifier of the plug-in
+	 * @param preferences the preference store for the specified plug-in
+	 * 
+	 * @since 2.0
+	 */
+	private void applyPluginDefaultOverrides(URL propertiesURL, String id, Preferences preferences, Properties props) {
+
+		// read the java.io.Properties file at the given URL
+		Properties overrides = new Properties();
+		SafeFileInputStream in = null;
+
+		try {
+			File inFile = new File(propertiesURL.getFile());
+			if (!inFile.exists()) {
+				// We don't have a preferences file to worry about
+				if (DEBUG_PREFERENCES) {
+					System.out.println("Preference file " + //$NON-NLS-1$
+					propertiesURL + " not found."); //$NON-NLS-1$
+				}
+				return;
+			}
+
+			in = new SafeFileInputStream(inFile);
+			if (in == null) {
+				// fail quietly
+				if (DEBUG_PREFERENCES) {
+					System.out.println("Failed to open " + //$NON-NLS-1$
+					propertiesURL);
+				}
+				return;
+			}
+			overrides.load(in);
+		} catch (IOException e) {
+			// cannot read ini file - fail silently
+			if (DEBUG_PREFERENCES) {
+				System.out.println("IOException reading preference file " + //$NON-NLS-1$
+				propertiesURL);
+				e.printStackTrace();
+			}
+			return;
+		} finally {
+			try {
+				if (in != null) {
+					in.close();
+				}
+			} catch (IOException e) {
+				// ignore problems closing file
+				if (DEBUG_PREFERENCES) {
+					System.out.println("IOException closing preference file " + //$NON-NLS-1$
+					propertiesURL);
+					e.printStackTrace();
+				}
+			}
+		}
+
+		for (Iterator it = overrides.entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Map.Entry) it.next();
+			String qualifiedKey = (String) entry.getKey();
+			// Keys consist of "/'-separated plug-in id and name of preference
+			// e.g., "com.example.myplugin/mypref"
+			int s = qualifiedKey.indexOf('/');
+			if (s < 0 || s == 0 || s == qualifiedKey.length() - 1) {
+				// skip mangled entry
+				continue;
+			}
+			// plug-in id is non-empty string before "/" 
+			String pluginId = qualifiedKey.substring(0, s);
+			if (pluginId.equals(id)) {
+				// override property in the given plug-in
+				// plig-in-specified property name is non-empty string after "/" 
+				String propertyName = qualifiedKey.substring(s + 1);
+				String value = (String) entry.getValue();
+				value = translatePreference(value, props);
+				preferences.setDefault(propertyName, value);
+			}
+		}
+		if (DEBUG_PREFERENCES) {
+			System.out.println("Preferences now set as follows:"); //$NON-NLS-1$
+			String[] prefNames = preferences.propertyNames();
+			for (int i = 0; i < prefNames.length; i++) {
+				String value = preferences.getString(prefNames[i]);
+				System.out.println("\t" + prefNames[i] + " = " + value); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+			prefNames = preferences.defaultPropertyNames();
+			for (int i = 0; i < prefNames.length; i++) {
+				String value = preferences.getDefaultString(prefNames[i]);
+				System.out.println("\tDefault values: " + prefNames[i] + " = " + value); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+	}
+	public void setExtensionRegistry(IExtensionRegistry value) {
+		registry = value;
+	}
+	public BundleContext getBundleContext() {
+		return context;
+	}
+	public Bundle getBundle(String id) {
+		return getBundleContext().getBundle(id);
+	}
+
+	public URL getInstallURL() {
+		if (installLocation == null)
+			try {
+				installLocation = new URL((String) System.getProperty("eclipse.installURL")); //$NON-NLS-1$
+			} catch (MalformedURLException e) {
+				//This can't fail because eclipse.installURL has been set with a valid URL in BootLoader
+			}
+		return installLocation;
+	}
+	public EnvironmentInfo getEnvironmentInfoService() {
+		return infoService;
+	}
+	/**
+	 * @deprecated either look for the IPlatform service or see if the runtime bundle is started.
+	 */
+	public boolean isRunning() {
+		int state = context.getBundle(PI_RUNTIME).getState();
+		return state == Bundle.ACTIVE;
+	}
+
+	/*
+	 * This method is retained for R1.0 compatibility because it is defined as API.
+	 * It's function matches the API description (returns <code>null</code> when
+	 * argument URL is <code>null</code> or cannot be read).
+	 */
+	public URL[] getPluginPath(URL pluginPathLocation /*R1.0 compatibility*/
+	) {
+		InputStream input = null;
+		// first try and see if the given plugin path location exists.
+		if (pluginPathLocation == null)
+			return null;
+		try {
+			input = pluginPathLocation.openStream();
+		} catch (IOException e) {
+			//fall through
+		}
+
+		// if the given path was null or did not exist, look for a plugin path
+		// definition in the install location.
+		if (input == null)
+			try {
+				URL url = new URL(PlatformURLBaseConnection.PLATFORM_URL_STRING + PLUGIN_PATH);
+				input = url.openStream();
+			} catch (MalformedURLException e) {
+				//fall through
+			} catch (IOException e) {
+				//fall through
+			}
+
+		// nothing was found at the supplied location or in the install location
+		if (input == null)
+			return null;
+		// if we found a plugin path definition somewhere so read it and close the location.
+		URL[] result = null;
+		try {
+			try {
+				result = readPluginPath(input);
+			} finally {
+				input.close();
+			}
+		} catch (IOException e) {
+			//let it return null on failure to read
+		}
+		return result;
+	}
+
+	private URL[] readPluginPath(InputStream input) {
+		Properties ini = new Properties();
+		try {
+			ini.load(input);
+		} catch (IOException e) {
+			return null;
+		}
+		Vector result = new Vector(5);
+		for (Enumeration groups = ini.propertyNames(); groups.hasMoreElements();) {
+			String group = (String) groups.nextElement();
+			for (StringTokenizer entries = new StringTokenizer(ini.getProperty(group), ";"); entries.hasMoreElements();) { //$NON-NLS-1$
+				String entry = (String) entries.nextElement();
+				if (!entry.equals("")) //$NON-NLS-1$
+					try {
+						result.addElement(new URL(entry));
+					} catch (MalformedURLException e) {
+						//intentionally ignore bad URLs
+						System.err.println(Policy.bind("ignore.plugin", entry)); //$NON-NLS-1$
+					}
+			}
+		}
+		return (URL[]) result.toArray(new URL[result.size()]);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IPlatform#getConfigurationMetadataLocation()
+	 */
+	public IPath getConfigurationMetadataLocation() {
+		if (configMetadataLocation == null)
+			configMetadataLocation = new Path(System.getProperty("osgi.configuration.area")); //$NON-NLS-1$
+		return configMetadataLocation;
+	}
+
+	public IPath getStateLocation(Bundle bundle, boolean create) {
+		assertInitialized();
+		IPath result = metaArea.getStateLocation(bundle);
+		if (create)
+			result.toFile().mkdirs();
+		return result;
+	}
+	public URL find(Bundle b, IPath path) {
+		return FindSupport.find(b, path);
+	}
+	public URL find(Bundle bundle, IPath path, Map override) {
+		return FindSupport.find(bundle, path, override);
+	}
+	public InputStream openStream(Bundle bundle, IPath file) throws IOException {
+		return FindSupport.openStream(bundle, file, false);
+	}
+	public InputStream openStream(Bundle bundle, IPath file, boolean localized) throws IOException {
+		return FindSupport.openStream(bundle, file, localized);
+	}
+	public IPath getStateLocation(Bundle bundle) {
+		return getStateLocation(bundle, true);
+	}
+	public ResourceBundle getResourceBundle(Bundle bundle) throws MissingResourceException {
+		//TODO
+		throw new NoSuchMethodError("getResourceBundleString");
+	}
+	public String getResourceString(Bundle bundle, String value) {
+		//TODO
+		throw new NoSuchMethodError("getResourceBundleString");
+	}
+	public String getResourceString(Bundle bundle, String value, ResourceBundle resourceBundle) {
+		//TODO
+		throw new NoSuchMethodError("getResourceBundleString");
+	}
+}
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformActivator.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformActivator.java
index 3bb9b9b..583cc47 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformActivator.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformActivator.java
@@ -1,217 +1,216 @@
-/*******************************************************************************

- * Copyright (c) 2000, 2003 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.core.internal.runtime;

-

-import java.io.File;

-import java.lang.reflect.InvocationTargetException;

-import java.lang.reflect.Method;

-import java.util.Hashtable;

-import org.eclipse.core.internal.boot.PlatformURLBaseConnection;

-import org.eclipse.core.internal.boot.PlatformURLHandler;

-import org.eclipse.core.internal.registry.*;

-import org.eclipse.core.runtime.*;

-import org.eclipse.osgi.service.environment.EnvironmentInfo;

-import org.osgi.framework.*;

-import org.osgi.service.url.URLConstants;

-import org.osgi.service.url.URLStreamHandlerService;

-

-/**

- * Activator for the Eclipse runtime.

- */

-public class PlatformActivator implements BundleActivator, ServiceListener {

-

-	private static BundleContext context;

-	private EclipseBundleListener pluginBundleListener;

-	private ExtensionRegistry registry;

-	private ServiceReference environmentServiceReference;

-	private ServiceRegistration converterRegistration;

-	

-	private static File cacheFile = InternalPlatform.getDefault().getConfigurationMetadataLocation().append(".registry").toFile();

-

-	public static BundleContext getContext() {

-		return context;

-	}

-

-	public void start(BundleContext context) throws Exception {

-		PlatformActivator.context = context;

-		context.addServiceListener(this);

-		tryToAcquireInfoService();

-		installPlatformURLSupport();

-	}

-

-	private void installBackwardCompatibleURLSupport() {

-		try {

-			Class handler = Class.forName("org.eclipse.core.internal.runtime.PlatformURLPluginHandlerFactory");

-			Method startupMethod = handler.getDeclaredMethod("startup", null);

-			startupMethod.invoke(handler, null);

-		} catch (ClassNotFoundException e) {

-			e.printStackTrace();

-			return; //TODO log a warning

-		} catch (SecurityException e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-		} catch (NoSuchMethodException e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-		} catch (IllegalArgumentException e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-		} catch (IllegalAccessException e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-		} catch (InvocationTargetException e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-		}

-	}

-

-	/**

-	 * Register the platform URL support as a service to the URLHandler service

-	 */

-	private void installPlatformURLSupport() {

-		PlatformURLPluginConnection.startup();

-		PlatformURLFragmentConnection.startup();

-

-		PlatformURLBaseConnection.startup(InternalPlatform.getDefault().getInstallURL());

-

-		Hashtable properties = new Hashtable();

-		properties.put(URLConstants.URL_HANDLER_PROTOCOL, new String[] { PlatformURLHandler.PROTOCOL });

-		context.registerService(URLStreamHandlerService.class.getName(), new PlatformURLHandler(), properties);

-	}

-

-	private void startRegistry(BundleContext context) {

-		boolean fromCache = true;

-		if (InternalPlatform.cacheRegistry) {

-			// Try to read the registry from the cache first. If that fails, create a new registry

-			MultiStatus problems = new MultiStatus(IPlatform.PI_RUNTIME, ExtensionsParser.PARSE_PROBLEM, "Registry cache problems", null); //$NON-NLS-1$

-			Factory factory = new Factory(problems);

-

-			long start = 0;

-			if (InternalPlatform.DEBUG)

-				start = System.currentTimeMillis();

-			registry = new RegistryCacheReader(cacheFile, factory, InternalPlatform.lazyRegistryCacheLoading).loadCache();

-

-			if (InternalPlatform.DEBUG && registry != null)

-				System.out.println("Reading registry cache: " + (System.currentTimeMillis() - start));

-

-			if (InternalPlatform.DEBUG_REGISTRY) {

-				if (registry == null)

-					System.out.println("Reloading registry from manifest files...");

-				else

-					System.out.println("Using registry cache " + (InternalPlatform.lazyRegistryCacheLoading ? "with" : "without") + " lazy element loading...");

-			}

-			// TODO log any problems that occurred in loading the cache.

-			if (!problems.isOK())

-				System.out.println(problems);

-		}

-		if (registry == null) {

-			fromCache = false;

-			registry = new ExtensionRegistry(new ExtensionLinker());

-		}

-

-		// register a listener to catch new bundle installations/resolutions.

-		pluginBundleListener = new EclipseBundleListener(registry);

-		context.addBundleListener(pluginBundleListener);

-

-		// populate the registry with all the currently installed bundles.

-		if (!fromCache)

-			pluginBundleListener.processBundles(context.getBundles());

-

-		context.registerService(IExtensionRegistry.class.getName(), registry, new Hashtable()); //$NON-NLS-1$

-		InternalPlatform.getDefault().setExtensionRegistry(registry);

-	}

-	public void stop(BundleContext context) throws Exception {

-		// Stop the registry

-		stopRegistry(context);

-		unregisterPluginConverter();

-		environmentInfoServiceReleased(environmentServiceReference);

-		// Stop the platform orderly.		

-		InternalPlatform.getDefault().stop(context);

-	}

-

-

-	private void stopRegistry(BundleContext context) {

-		context.removeBundleListener(this.pluginBundleListener);

-		if (registry != null && registry.isDirty()) {

-			new RegistryCacheWriter(cacheFile).saveCache(registry);

-			registry = null;

-		}

-	}

-

-	private void tryToAcquireInfoService() {

-		ServiceReference reference = context.getServiceReference(EnvironmentInfo.class.getName());

-		if (reference == null)

-			return;

-		environmentInfoServiceAquired(reference);

-	}

-

-	private void environmentInfoServiceAquired(ServiceReference reference) {

-		if (environmentServiceReference != null)

-			return;

-		environmentServiceReference = reference;

-		EnvironmentInfo infoService = (EnvironmentInfo) context.getService(environmentServiceReference);

-		InternalPlatform.infoService = infoService;

-		toStart();

-	}

-

-	private void toStart() {

-		try {

-			InternalPlatform.getDefault().start(context);

-		} catch (Exception e) {

-			// TODO Auto-generated catch block

-			e.printStackTrace();

-		}

-		registerPluginConverter();

-		startRegistry(context);

-		

-	}

-

-	private void registerPluginConverter() {

-		converterRegistration = context.registerService(IPluginConverter.class.getName(), new PluginConverter(), null);

-	}

-	

-	private void unregisterPluginConverter() {

-		converterRegistration.unregister();

-	}

-

-	private void environmentInfoServiceReleased(ServiceReference reference) {

-		if (environmentServiceReference == null)

-			return;

-		if (environmentServiceReference != reference)

-			return;

-

-		InternalPlatform.infoService = null;

-		context.ungetService(environmentServiceReference);

-		environmentServiceReference = null;

-	}

-

-	public void serviceChanged(ServiceEvent event) {

-		int type = event.getType();

-		ServiceReference reference = event.getServiceReference();

-		switch (type) {

-			case ServiceEvent.REGISTERED :

-				String[] servicesInterfaces = (String[]) reference.getProperty(Constants.OBJECTCLASS);

-				for (int i = 0; i < servicesInterfaces.length; i++) {

-					if (servicesInterfaces[i].equals(EnvironmentInfo.class.getName()))

-						environmentInfoServiceAquired(reference);

-				}

-				break;

-			case ServiceEvent.UNREGISTERING :

-				System.out.println("Service Unregistering:");

-				servicesInterfaces = (String[]) reference.getProperty(Constants.OBJECTCLASS);

-				for (int i = 0; i < servicesInterfaces.length; i++) {

-					if (servicesInterfaces[i].equals(EnvironmentInfo.class.getName()))

-						environmentInfoServiceReleased(reference);

-				}

-				break;

-		}

-	}

-}

+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.core.internal.runtime;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import org.eclipse.core.internal.boot.PlatformURLBaseConnection;
+import org.eclipse.core.internal.boot.PlatformURLHandler;
+import org.eclipse.core.internal.registry.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.osgi.framework.*;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+
+/**
+ * Activator for the Eclipse runtime.
+ */
+public class PlatformActivator implements BundleActivator, ServiceListener {
+
+	private static BundleContext context;
+	private EclipseBundleListener pluginBundleListener;
+	private ExtensionRegistry registry;
+	private ServiceReference environmentServiceReference;
+	private ServiceRegistration converterRegistration;
+	
+	private static File cacheFile = InternalPlatform.getDefault().getConfigurationMetadataLocation().append(".registry").toFile();
+
+	public static BundleContext getContext() {
+		return context;
+	}
+
+	public void start(BundleContext context) throws Exception {
+		PlatformActivator.context = context;
+		context.addServiceListener(this);
+		tryToAcquireInfoService();
+		installPlatformURLSupport();
+	}
+
+	private void installBackwardCompatibleURLSupport() {
+		try {
+			Class handler = Class.forName("org.eclipse.core.internal.runtime.PlatformURLPluginHandlerFactory");
+			Method startupMethod = handler.getDeclaredMethod("startup", null);
+			startupMethod.invoke(handler, null);
+		} catch (ClassNotFoundException e) {
+			e.printStackTrace();
+			return; //TODO log a warning
+		} catch (SecurityException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (NoSuchMethodException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (IllegalArgumentException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (IllegalAccessException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Register the platform URL support as a service to the URLHandler service
+	 */
+	private void installPlatformURLSupport() {
+		PlatformURLPluginConnection.startup();
+		PlatformURLFragmentConnection.startup();
+
+		PlatformURLBaseConnection.startup(InternalPlatform.getDefault().getInstallURL());
+
+		Hashtable properties = new Hashtable();
+		properties.put(URLConstants.URL_HANDLER_PROTOCOL, new String[] { PlatformURLHandler.PROTOCOL });
+		context.registerService(URLStreamHandlerService.class.getName(), new PlatformURLHandler(), properties);
+	}
+
+	private void startRegistry(BundleContext context) {
+		boolean fromCache = true;
+		if (InternalPlatform.cacheRegistry) {
+			// Try to read the registry from the cache first. If that fails, create a new registry
+			MultiStatus problems = new MultiStatus(IPlatform.PI_RUNTIME, ExtensionsParser.PARSE_PROBLEM, "Registry cache problems", null); //$NON-NLS-1$
+			Factory factory = new Factory(problems);
+
+			long start = 0;
+			if (InternalPlatform.DEBUG)
+				start = System.currentTimeMillis();
+			registry = new RegistryCacheReader(cacheFile, factory, InternalPlatform.lazyRegistryCacheLoading).loadCache();
+
+			if (InternalPlatform.DEBUG && registry != null)
+				System.out.println("Reading registry cache: " + (System.currentTimeMillis() - start));
+
+			if (InternalPlatform.DEBUG_REGISTRY) {
+				if (registry == null)
+					System.out.println("Reloading registry from manifest files...");
+				else
+					System.out.println("Using registry cache " + (InternalPlatform.lazyRegistryCacheLoading ? "with" : "without") + " lazy element loading...");
+			}
+			// TODO log any problems that occurred in loading the cache.
+			if (!problems.isOK())
+				System.out.println(problems);
+		}
+		if (registry == null) {
+			fromCache = false;
+			registry = new ExtensionRegistry(new ExtensionLinker());
+		}
+
+		// register a listener to catch new bundle installations/resolutions.
+		pluginBundleListener = new EclipseBundleListener(registry);
+		context.addBundleListener(pluginBundleListener);
+
+		// populate the registry with all the currently installed bundles.
+		if (!fromCache)
+			pluginBundleListener.processBundles(context.getBundles());
+
+		context.registerService(IExtensionRegistry.class.getName(), registry, new Hashtable()); //$NON-NLS-1$
+		InternalPlatform.getDefault().setExtensionRegistry(registry);
+	}
+	public void stop(BundleContext context) throws Exception {
+		// Stop the registry
+		stopRegistry(context);
+		unregisterPluginConverter();
+		environmentInfoServiceReleased(environmentServiceReference);
+		// Stop the platform orderly.		
+		InternalPlatform.getDefault().stop(context);
+	}
+
+
+	private void stopRegistry(BundleContext context) {
+		context.removeBundleListener(this.pluginBundleListener);
+		if (registry != null && registry.isDirty()) {
+			new RegistryCacheWriter(cacheFile).saveCache(registry);
+			registry = null;
+		}
+	}
+
+	private void tryToAcquireInfoService() {
+		ServiceReference reference = context.getServiceReference(EnvironmentInfo.class.getName());
+		if (reference == null)
+			return;
+		environmentInfoServiceAquired(reference);
+	}
+
+	private void environmentInfoServiceAquired(ServiceReference reference) {
+		if (environmentServiceReference != null)
+			return;
+		environmentServiceReference = reference;
+		EnvironmentInfo infoService = (EnvironmentInfo) context.getService(environmentServiceReference);
+		InternalPlatform.infoService = infoService;
+		toStart();
+	}
+
+	private void toStart() {
+		try {
+			InternalPlatform.getDefault().start(context);
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		registerPluginConverter();
+		startRegistry(context);
+		
+	}
+
+	private void registerPluginConverter() {
+		converterRegistration = context.registerService(IPluginConverter.class.getName(), new PluginConverter(), null);
+	}
+	
+	private void unregisterPluginConverter() {
+		converterRegistration.unregister();
+	}
+
+	private void environmentInfoServiceReleased(ServiceReference reference) {
+		if (environmentServiceReference == null)
+			return;
+		if (environmentServiceReference != reference)
+			return;
+
+		InternalPlatform.infoService = null;
+		context.ungetService(environmentServiceReference);
+		environmentServiceReference = null;
+	}
+
+	public void serviceChanged(ServiceEvent event) {
+		int type = event.getType();
+		ServiceReference reference = event.getServiceReference();
+		switch (type) {
+			case ServiceEvent.REGISTERED :
+				String[] servicesInterfaces = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+				for (int i = 0; i < servicesInterfaces.length; i++) {
+					if (servicesInterfaces[i].equals(EnvironmentInfo.class.getName()))
+						environmentInfoServiceAquired(reference);
+				}
+				break;
+			case ServiceEvent.UNREGISTERING :
+				servicesInterfaces = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+				for (int i = 0; i < servicesInterfaces.length; i++) {
+					if (servicesInterfaces[i].equals(EnvironmentInfo.class.getName()))
+						environmentInfoServiceReleased(reference);
+				}
+				break;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/IPlatform.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/IPlatform.java
index 5217fe0..617ca3f 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/IPlatform.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/IPlatform.java
@@ -1,439 +1,438 @@
-/*******************************************************************************

- * Copyright (c) 2000, 2003 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.core.runtime;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.net.URL;

-import java.util.*;

-import org.eclipse.core.runtime.jobs.IJobManager;

-import org.osgi.framework.Bundle;

-

-// TODO clarify the javadoc below.  Copy the signatures from Platform.

-// talk to jeem about the best way to do the triplication

-/**

- * The central class of the Eclipse Platform Runtime. This class cannot

- * be instantiated or subclassed by clients; all functionality is provided 

- * by static methods.  Features include:

- * <ul>

- * <li>the platform registry of installed plug-ins</li>

- * <li>the platform adapter manager</li>

- * <li>the platform log</li>

- * <li>the authorization info management</li>

- * </ul>

- * <p>

- * The platform is in one of two states, running or not running, at all

- * times. The only ways to start the platform running, or to shut it down,

- * are on the bootstrap <code>BootLoader</code> class. Code in plug-ins will

- * only observe the platform in the running state. The platform cannot

- * be shutdown from inside (code in plug-ins have no access to

- * <code>BootLoader</code>).

- * </p>

- * @deprecated Use Platform instead.

- */

-public interface IPlatform {

-	/**

-	 * Name of a preference for configuring the performance level for this system.

-	 *

-	 * <p>

-	 * This value can be used by all components to customize features to suit the 

-	 * speed of the user's machine.  The platform job manager uses this value to make

-	 * scheduling decisions about background jobs.

-	 * </p>

-	 * <p>

-	 * The preference value must be an integer between the constant values

-	 * MIN_PERFORMANCE and MAX_PERFORMANCE

-	 * </p>

-	 * @see #MIN_PERFORMANCE

-	 * @see #MAX_PERFORMANCE

-	 * @since 3.0

-	 */

-	public static final String PREF_PLATFORM_PERFORMANCE = "runtime.performance"; //$NON-NLS-1$

-	/**

-	 * The unique identifier constant (value "<code>org.eclipse.core.runtime</code>")

-	 * of the Core Runtime plug-in.

-	 */

-	public static final String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$

-	public static final String PI_RUNTIME_COMPATIBILITY = "org.eclipse.core.runtime.compatibility"; //$NON-NLS-1$ 

-	/** 

-	 * The simple identifier constant (value "<code>applications</code>") of

-	 * the extension point of the Core Runtime plug-in where plug-ins declare

-	 * the existence of runnable applications. A plug-in may define any

-	 * number of applications; however, the platform is only capable

-	 * of running one application at a time.

-	 * 

-	 * @see org.eclipse.core.boot.BootLoader#run

-	 */

-	public static final String PT_APPLICATIONS = "applications"; //$NON-NLS-1$

-

-	public static final String PT_URLHANDLERS = "urlHandlers"; //$NON-NLS-1$

-

-	public static final String PT_SHUTDOWN_HOOK = "applicationShutdownHook"; //$NON-NLS-1$	

-

-	/** 

-	 * Status code constant (value 1) indicating a problem in a plug-in

-	 * manifest (<code>plugin.xml</code>) file.

-	 */

-	public static final int PARSE_PROBLEM = 1;

-

-	/**

-	 * Status code constant (value 2) indicating an error occurred while running a plug-in.

-	 */

-	public static final int PLUGIN_ERROR = 2;

-

-	/**

-	 * Status code constant (value 3) indicating an error internal to the

-	 * platform has occurred.

-	 */

-	public static final int INTERNAL_ERROR = 3;

-

-	/**

-	 * Status code constant (value 4) indicating the platform could not read

-	 * some of its metadata.

-	 */

-	public static final int FAILED_READ_METADATA = 4;

-

-	/**

-	 * Status code constant (value 5) indicating the platform could not write

-	 * some of its metadata.

-	 */

-	public static final int FAILED_WRITE_METADATA = 5;

-

-	/**

-	 * Status code constant (value 6) indicating the platform could not delete

-	 * some of its metadata.

-	 */

-	public static final int FAILED_DELETE_METADATA = 6;

-

-	/**

-	 * Adds the given authorization information to the keyring. The

-	 * information is relevant for the specified protection space and the

-	 * given authorization scheme. The protection space is defined by the

-	 * combination of the given server URL and realm. The authorization 

-	 * scheme determines what the authorization information contains and how 

-	 * it should be used. The authorization information is a <code>Map</code> 

-	 * of <code>String</code> to <code>String</code> and typically

-	 * contains information such as usernames and passwords.

-	 *

-	 * @param serverUrl the URL identifying the server for this authorization

-	 *		information. For example, "http://www.example.com/".

-	 * @param realm the subsection of the given server to which this

-	 *		authorization information applies.  For example,

-	 *		"realm1@example.com" or "" for no realm.

-	 * @param authScheme the scheme for which this authorization information

-	 *		applies. For example, "Basic" or "" for no authorization scheme

-	 * @param info a <code>Map</code> containing authorization information 

-	 *		such as usernames and passwords (key type : <code>String</code>, 

-	 *		value type : <code>String</code>)

-	 * @exception CoreException if there are problems setting the

-	 *		authorization information. Reasons include:

-	 * <ul>

-	 * <li>The keyring could not be saved.</li>

-	 * </ul>

-	 */

-	public void addAuthorizationInfo(URL serverUrl, String realm, String authScheme, Map info) throws CoreException;

-

-	/** 

-	 * Adds the given log listener to the notification list of the platform.

-	 * <p>

-	 * Once registered, a listener starts receiving notification as entries

-	 * are added to plug-in logs via <code>ILog.log()</code>. The listener continues to

-	 * receive notifications until it is replaced or removed.

-	 * </p>

-	 *

-	 * @param listener the listener to register

-	 * @see ILog#addLogListener

-	 * @see #removeLogListener

-	 */

-	public void addLogListener(ILogListener listener);

-

-	/**

-	 * Adds the specified resource to the protection space specified by the

-	 * given realm. All targets at or deeper than the depth of the last

-	 * symbolic element in the path of the given resource URL are assumed to

-	 * be in the same protection space.

-	 *

-	 * @param resourceUrl the URL identifying the resources to be added to

-	 *		the specified protection space. For example,

-	 *		"http://www.example.com/folder/".

-	 * @param realm the name of the protection space. For example,

-	 *		"realm1@example.com"

-	 * @exception CoreException if there are problems setting the

-	 *		authorization information. Reasons include:

-	 * <ul>

-	 * <li>The keyring could not be saved.</li>

-	 * </ul>

-	 */

-	public void addProtectionSpace(URL resourceUrl, String realm) throws CoreException;

-

-	/**

-	 * Returns a URL which is the local equivalent of the

-	 * supplied URL. This method is expected to be used with 

-	 * plug-in-relative URLs returned by IPluginDescriptor.

-	 * If the specified URL is not a plug-in-relative URL, it 

-	 * is returned asis. If the specified URL is a plug-in-relative

-	 * URL of a file (incl. .jar archive), it is returned as 

-	 * a locally-accessible URL using "file:" or "jar:file:" protocol

-	 * (caching the file locally, if required). If the specified URL

-	 * is a plug-in-relative URL of a directory,

-	 * an exception is thrown.

-	 *

-	 * @param url original plug-in-relative URL.

-	 * @return the resolved URL

-	 * @exception IOException if unable to resolve URL

-	 * @see #resolve

-	 * @see IPluginDescriptor#getInstallURL

-	 */

-	public URL asLocalURL(URL url) throws IOException;

-

-	/**

-	 * Removes the authorization information for the specified protection

-	 * space and given authorization scheme. The protection space is defined

-	 * by the given server URL and realm.

-	 *

-	 * @param serverUrl the URL identifying the server to remove the

-	 *		authorization information for. For example,

-	 *		"http://www.example.com/".

-	 * @param realm the subsection of the given server to remove the

-	 *		authorization information for. For example,

-	 *		"realm1@example.com" or "" for no realm.

-	 * @param authScheme the scheme for which the authorization information

-	 *		to remove applies. For example, "Basic" or "" for no

-	 *		authorization scheme.

-	 * @exception CoreException if there are problems removing the

-	 *		authorization information. Reasons include:

-	 * <ul>

-	 * <li>The keyring could not be saved.</li>

-	 * </ul>

-	 */

-	public void flushAuthorizationInfo(URL serverUrl, String realm, String authScheme) throws CoreException;

-

-	/**

-	 * Returns the adapter manager used for extending

-	 * <code>IAdaptable</code> objects.

-	 *

-	 * @return the adapter manager for this platform

-	 * @see IAdapterManager

-	 */

-	public IAdapterManager getAdapterManager();

-

-	/**

-	 * Returns the authorization information for the specified protection

-	 * space and given authorization scheme. The protection space is defined

-	 * by the given server URL and realm. Returns <code>null</code> if no

-	 * such information exists.

-	 *

-	 * @param serverUrl the URL identifying the server for the authorization

-	 *		information. For example, "http://www.example.com/".

-	 * @param realm the subsection of the given server to which the

-	 *		authorization information applies.  For example,

-	 *		"realm1@example.com" or "" for no realm.

-	 * @param authScheme the scheme for which the authorization information

-	 *		applies. For example, "Basic" or "" for no authorization scheme

-	 * @return the authorization information for the specified protection

-	 *		space and given authorization scheme, or <code>null</code> if no

-	 *		such information exists

-	 */

-	public Map getAuthorizationInfo(URL serverUrl, String realm, String authScheme);

-

-	/**

-	 * Returns the location of the platform working directory.  This 

-	 * corresponds to the <i>-data</i> command line argument if

-	 * present or, if not, the current working directory when the platform

-	 * was started.

-	 *

-	 * @return the location of the platform

-	 */

-	public IPath getLocation();

-

-	/**

-	 * Returns the location of the platform log file.  This file may contain information

-	 * about errors that have previously occurred during this invocation of the Platform.

-	 * 

-	 * Note: it is very important that users of this method do not leave the log

-	 * file open for extended periods of time.  Doing so may prevent others

-	 * from writing to the log file, which could result in important error messages

-	 * being lost.  It is strongly recommended that clients wanting to read the

-	 * log file for extended periods should copy the log file contents elsewhere,

-	 * and immediately close the original file.

-	 * 

-	 * @return the path of the log file on disk.

-	 */

-	public IPath getLogFileLocation();

-

-	/**

-	 * Returns the protection space (realm) for the specified resource, or

-	 * <code>null</code> if the realm is unknown.

-	 *

-	 * @param resourceUrl the URL of the resource whose protection space is

-	 *		returned. For example, "http://www.example.com/folder/".

-	 * @return the protection space (realm) for the specified resource, or

-	 *		<code>null</code> if the realm is unknown

-	 */

-	public String getProtectionSpace(URL resourceUrl);

-

-	/** 

-	 * Removes the indicated (identical) log listener from the notification list

-	 * of the platform.  If no such listener exists, no action is taken.

-	 *

-	 * @param listener the listener to deregister

-	 * @see ILog#removeLogListener

-	 * @see #addLogListener

-	 */

-	public void removeLogListener(ILogListener listener);

-

-	/**

-	 * Returns a URL which is the resolved equivalent of the

-	 * supplied URL. This method is expected to be used with 

-	 * plug-in-relative URLs returned by IPluginDescriptor.

-	 * If the specified URL is not a plug-in-relative URL, it is returned

-	 * as is. If the specified URL is a plug-in-relative URL, it is

-	 * resolved to a URL using the actual URL protocol

-	 * (eg. file, http, etc)

-	 *

-	 * @param url original plug-in-relative URL.

-	 * @return the resolved URL

-	 * @exception IOException if unable to resolve URL

-	 * @see #asLocalURL

-	 * @see IPluginDescriptor#getInstallURL

-	 */

-	public URL resolve(URL url) throws IOException;

-

-	/**

-	 * Runs the given runnable in a protected mode.   Exceptions

-	 * thrown in the runnable are logged and passed to the runnable's

-	 * exception handler.  Such exceptions are not rethrown by this method.

-	 *

-	 * @param code the runnable to run

-	 */

-	public void run(ISafeRunnable code);

-	/**

-	 * Returns the log for the given bundle.  If no such log exists, one is created.

-	 *

-	 * @return the log for the given bundle

-	 */

-	public ILog getLog(Bundle bundle);

-

-	/**

-	 * Returns the platform job manager.

-	 *  

-	 * @return the job manager

-	 */

-	public IJobManager getJobManager();

-	

-	/**

-	 * Returns URL at which the Platform runtime executables and libraries are installed.

-	 * The returned value is distinct from the location of any given platform's data.

-	 *

-	 * @return the URL indicating where the platform runtime is installed.

-	 */	

-	public URL getInstallURL();

-

-	/**

-	 * Returns the location in the filesystem of the configuration information 

-	 * used to run this instance of Eclipse.  The configuration area typically

-	 * contains the list of plug-ins available for use, various user setttings

-	 * (those shared across different instances of the same configuration)

-	 * and any other such data needed by plug-ins.

-	 * 

-	 * @return the path indicating the directory containing the configuration 

-	 * metadata for this running Eclipse.

-	 */

-	public IPath getConfigurationMetadataLocation();

-	

-	/**

-	 * Takes down the splash screen if one was put up.

-	 */

-	public void endSplash();

-	

-	public URL find(Bundle b, IPath path);

-	

-	public URL find(Bundle b, IPath path, Map override);

-	

-	public InputStream openStream(Bundle b, IPath file)  throws IOException;

-	/**

-	 * Returns an input stream for the specified file. The file path

-	 * must be specified relative to this plug-in's installation location.

-	 * Optionally, the platform searches for the correct localized version

-	 * of the specified file using the users current locale, and Java

-	 * naming convention for localized resource files (locale suffix appended 

-	 * to the specified file extension).

-	 * <p>

-	 * The caller must close the returned stream when done.

-	 * </p>

-	 *

-	 * @param file path relative to plug-in installation location

-	 * @param localized <code>true</code> for the localized version

-	 *   of the file, and <code>false</code> for the file exactly

-	 *   as specified

-	 * @return an input stream

-	 */	

-	public InputStream openStream(Bundle b, IPath file, boolean localized) throws IOException;

-	

-	public IPath getStateLocation(Bundle bundle);

-	

-	public ResourceBundle getResourceBundle(Bundle bundle) throws MissingResourceException;

-	/**

-	 * Returns a resource string corresponding to the given argument value.

-	 * If the argument value specifies a resource key, the string

-	 * is looked up in the default 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 in the

-	 * plugin.properties 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>

-	 * Equivalent to <code>getResourceString(value, getResourceBundle())</code>

-	 * </p>

-	 *

-	 * @param value the value

-	 * @return the resource string

-	 * @see #getResourceBundle

-	 */

-	public String getResourceString(Bundle bundle, String value);

-	/**

-	 * 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>

-	 *     getResourceString("Hello World") returns "Hello World"</li>

-	 *     getResourceString("%name") returns "Project Name"</li>

-	 *     getResourceString("%name Hello World") returns "Project Name"</li>

-	 *     getResourceString("%abcd Hello World") returns "Hello World"</li>

-	 *     getResourceString("%abcd") returns "%abcd"</li>

-	 *     getResourceString("%%name") returns "%name"</li>

-	 * </pre>

-	 * </p>

-	 *

-	 * @param value the value

-	 * @param bundle the resource bundle

-	 * @return the resource string

-	 * @see #getResourceBundle

-	 */

-	public String getResourceString(Bundle bundle, String value, ResourceBundle resourceBundle);

-}

+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.core.runtime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.osgi.framework.Bundle;
+
+// TODO clarify the javadoc below.  Copy the signatures from Platform.
+// talk to jeem about the best way to do the triplication
+/**
+ * The central class of the Eclipse Platform Runtime. This class cannot
+ * be instantiated or subclassed by clients; all functionality is provided 
+ * by static methods.  Features include:
+ * <ul>
+ * <li>the platform registry of installed plug-ins</li>
+ * <li>the platform adapter manager</li>
+ * <li>the platform log</li>
+ * <li>the authorization info management</li>
+ * </ul>
+ * <p>
+ * The platform is in one of two states, running or not running, at all
+ * times. The only ways to start the platform running, or to shut it down,
+ * are on the bootstrap <code>BootLoader</code> class. Code in plug-ins will
+ * only observe the platform in the running state. The platform cannot
+ * be shutdown from inside (code in plug-ins have no access to
+ * <code>BootLoader</code>).
+ * </p>
+ */
+public interface IPlatform {
+	/**
+	 * Name of a preference for configuring the performance level for this system.
+	 *
+	 * <p>
+	 * This value can be used by all components to customize features to suit the 
+	 * speed of the user's machine.  The platform job manager uses this value to make
+	 * scheduling decisions about background jobs.
+	 * </p>
+	 * <p>
+	 * The preference value must be an integer between the constant values
+	 * MIN_PERFORMANCE and MAX_PERFORMANCE
+	 * </p>
+	 * @see #MIN_PERFORMANCE
+	 * @see #MAX_PERFORMANCE
+	 * @since 3.0
+	 */
+	public static final String PREF_PLATFORM_PERFORMANCE = "runtime.performance"; //$NON-NLS-1$
+	/**
+	 * The unique identifier constant (value "<code>org.eclipse.core.runtime</code>")
+	 * of the Core Runtime plug-in.
+	 */
+	public static final String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$
+	public static final String PI_RUNTIME_COMPATIBILITY = "org.eclipse.core.runtime.compatibility"; //$NON-NLS-1$ 
+	/** 
+	 * The simple identifier constant (value "<code>applications</code>") of
+	 * the extension point of the Core Runtime plug-in where plug-ins declare
+	 * the existence of runnable applications. A plug-in may define any
+	 * number of applications; however, the platform is only capable
+	 * of running one application at a time.
+	 * 
+	 * @see org.eclipse.core.boot.BootLoader#run
+	 */
+	public static final String PT_APPLICATIONS = "applications"; //$NON-NLS-1$
+
+	public static final String PT_URLHANDLERS = "urlHandlers"; //$NON-NLS-1$
+
+	public static final String PT_SHUTDOWN_HOOK = "applicationShutdownHook"; //$NON-NLS-1$	
+
+	/** 
+	 * Status code constant (value 1) indicating a problem in a plug-in
+	 * manifest (<code>plugin.xml</code>) file.
+	 */
+	public static final int PARSE_PROBLEM = 1;
+
+	/**
+	 * Status code constant (value 2) indicating an error occurred while running a plug-in.
+	 */
+	public static final int PLUGIN_ERROR = 2;
+
+	/**
+	 * Status code constant (value 3) indicating an error internal to the
+	 * platform has occurred.
+	 */
+	public static final int INTERNAL_ERROR = 3;
+
+	/**
+	 * Status code constant (value 4) indicating the platform could not read
+	 * some of its metadata.
+	 */
+	public static final int FAILED_READ_METADATA = 4;
+
+	/**
+	 * Status code constant (value 5) indicating the platform could not write
+	 * some of its metadata.
+	 */
+	public static final int FAILED_WRITE_METADATA = 5;
+
+	/**
+	 * Status code constant (value 6) indicating the platform could not delete
+	 * some of its metadata.
+	 */
+	public static final int FAILED_DELETE_METADATA = 6;
+
+	/**
+	 * Adds the given authorization information to the keyring. The
+	 * information is relevant for the specified protection space and the
+	 * given authorization scheme. The protection space is defined by the
+	 * combination of the given server URL and realm. The authorization 
+	 * scheme determines what the authorization information contains and how 
+	 * it should be used. The authorization information is a <code>Map</code> 
+	 * of <code>String</code> to <code>String</code> and typically
+	 * contains information such as usernames and passwords.
+	 *
+	 * @param serverUrl the URL identifying the server for this authorization
+	 *		information. For example, "http://www.example.com/".
+	 * @param realm the subsection of the given server to which this
+	 *		authorization information applies.  For example,
+	 *		"realm1@example.com" or "" for no realm.
+	 * @param authScheme the scheme for which this authorization information
+	 *		applies. For example, "Basic" or "" for no authorization scheme
+	 * @param info a <code>Map</code> containing authorization information 
+	 *		such as usernames and passwords (key type : <code>String</code>, 
+	 *		value type : <code>String</code>)
+	 * @exception CoreException if there are problems setting the
+	 *		authorization information. Reasons include:
+	 * <ul>
+	 * <li>The keyring could not be saved.</li>
+	 * </ul>
+	 */
+	public void addAuthorizationInfo(URL serverUrl, String realm, String authScheme, Map info) throws CoreException;
+
+	/** 
+	 * Adds the given log listener to the notification list of the platform.
+	 * <p>
+	 * Once registered, a listener starts receiving notification as entries
+	 * are added to plug-in logs via <code>ILog.log()</code>. The listener continues to
+	 * receive notifications until it is replaced or removed.
+	 * </p>
+	 *
+	 * @param listener the listener to register
+	 * @see ILog#addLogListener
+	 * @see #removeLogListener
+	 */
+	public void addLogListener(ILogListener listener);
+
+	/**
+	 * Adds the specified resource to the protection space specified by the
+	 * given realm. All targets at or deeper than the depth of the last
+	 * symbolic element in the path of the given resource URL are assumed to
+	 * be in the same protection space.
+	 *
+	 * @param resourceUrl the URL identifying the resources to be added to
+	 *		the specified protection space. For example,
+	 *		"http://www.example.com/folder/".
+	 * @param realm the name of the protection space. For example,
+	 *		"realm1@example.com"
+	 * @exception CoreException if there are problems setting the
+	 *		authorization information. Reasons include:
+	 * <ul>
+	 * <li>The keyring could not be saved.</li>
+	 * </ul>
+	 */
+	public void addProtectionSpace(URL resourceUrl, String realm) throws CoreException;
+
+	/**
+	 * Returns a URL which is the local equivalent of the
+	 * supplied URL. This method is expected to be used with 
+	 * plug-in-relative URLs returned by IPluginDescriptor.
+	 * If the specified URL is not a plug-in-relative URL, it 
+	 * is returned asis. If the specified URL is a plug-in-relative
+	 * URL of a file (incl. .jar archive), it is returned as 
+	 * a locally-accessible URL using "file:" or "jar:file:" protocol
+	 * (caching the file locally, if required). If the specified URL
+	 * is a plug-in-relative URL of a directory,
+	 * an exception is thrown.
+	 *
+	 * @param url original plug-in-relative URL.
+	 * @return the resolved URL
+	 * @exception IOException if unable to resolve URL
+	 * @see #resolve
+	 * @see IPluginDescriptor#getInstallURL
+	 */
+	public URL asLocalURL(URL url) throws IOException;
+
+	/**
+	 * Removes the authorization information for the specified protection
+	 * space and given authorization scheme. The protection space is defined
+	 * by the given server URL and realm.
+	 *
+	 * @param serverUrl the URL identifying the server to remove the
+	 *		authorization information for. For example,
+	 *		"http://www.example.com/".
+	 * @param realm the subsection of the given server to remove the
+	 *		authorization information for. For example,
+	 *		"realm1@example.com" or "" for no realm.
+	 * @param authScheme the scheme for which the authorization information
+	 *		to remove applies. For example, "Basic" or "" for no
+	 *		authorization scheme.
+	 * @exception CoreException if there are problems removing the
+	 *		authorization information. Reasons include:
+	 * <ul>
+	 * <li>The keyring could not be saved.</li>
+	 * </ul>
+	 */
+	public void flushAuthorizationInfo(URL serverUrl, String realm, String authScheme) throws CoreException;
+
+	/**
+	 * Returns the adapter manager used for extending
+	 * <code>IAdaptable</code> objects.
+	 *
+	 * @return the adapter manager for this platform
+	 * @see IAdapterManager
+	 */
+	public IAdapterManager getAdapterManager();
+
+	/**
+	 * Returns the authorization information for the specified protection
+	 * space and given authorization scheme. The protection space is defined
+	 * by the given server URL and realm. Returns <code>null</code> if no
+	 * such information exists.
+	 *
+	 * @param serverUrl the URL identifying the server for the authorization
+	 *		information. For example, "http://www.example.com/".
+	 * @param realm the subsection of the given server to which the
+	 *		authorization information applies.  For example,
+	 *		"realm1@example.com" or "" for no realm.
+	 * @param authScheme the scheme for which the authorization information
+	 *		applies. For example, "Basic" or "" for no authorization scheme
+	 * @return the authorization information for the specified protection
+	 *		space and given authorization scheme, or <code>null</code> if no
+	 *		such information exists
+	 */
+	public Map getAuthorizationInfo(URL serverUrl, String realm, String authScheme);
+
+	/**
+	 * Returns the location of the platform working directory.  This 
+	 * corresponds to the <i>-data</i> command line argument if
+	 * present or, if not, the current working directory when the platform
+	 * was started.
+	 *
+	 * @return the location of the platform
+	 */
+	public IPath getLocation();
+
+	/**
+	 * Returns the location of the platform log file.  This file may contain information
+	 * about errors that have previously occurred during this invocation of the Platform.
+	 * 
+	 * Note: it is very important that users of this method do not leave the log
+	 * file open for extended periods of time.  Doing so may prevent others
+	 * from writing to the log file, which could result in important error messages
+	 * being lost.  It is strongly recommended that clients wanting to read the
+	 * log file for extended periods should copy the log file contents elsewhere,
+	 * and immediately close the original file.
+	 * 
+	 * @return the path of the log file on disk.
+	 */
+	public IPath getLogFileLocation();
+
+	/**
+	 * Returns the protection space (realm) for the specified resource, or
+	 * <code>null</code> if the realm is unknown.
+	 *
+	 * @param resourceUrl the URL of the resource whose protection space is
+	 *		returned. For example, "http://www.example.com/folder/".
+	 * @return the protection space (realm) for the specified resource, or
+	 *		<code>null</code> if the realm is unknown
+	 */
+	public String getProtectionSpace(URL resourceUrl);
+
+	/** 
+	 * Removes the indicated (identical) log listener from the notification list
+	 * of the platform.  If no such listener exists, no action is taken.
+	 *
+	 * @param listener the listener to deregister
+	 * @see ILog#removeLogListener
+	 * @see #addLogListener
+	 */
+	public void removeLogListener(ILogListener listener);
+
+	/**
+	 * Returns a URL which is the resolved equivalent of the
+	 * supplied URL. This method is expected to be used with 
+	 * plug-in-relative URLs returned by IPluginDescriptor.
+	 * If the specified URL is not a plug-in-relative URL, it is returned
+	 * as is. If the specified URL is a plug-in-relative URL, it is
+	 * resolved to a URL using the actual URL protocol
+	 * (eg. file, http, etc)
+	 *
+	 * @param url original plug-in-relative URL.
+	 * @return the resolved URL
+	 * @exception IOException if unable to resolve URL
+	 * @see #asLocalURL
+	 * @see IPluginDescriptor#getInstallURL
+	 */
+	public URL resolve(URL url) throws IOException;
+
+	/**
+	 * Runs the given runnable in a protected mode.   Exceptions
+	 * thrown in the runnable are logged and passed to the runnable's
+	 * exception handler.  Such exceptions are not rethrown by this method.
+	 *
+	 * @param code the runnable to run
+	 */
+	public void run(ISafeRunnable code);
+	/**
+	 * Returns the log for the given bundle.  If no such log exists, one is created.
+	 *
+	 * @return the log for the given bundle
+	 */
+	public ILog getLog(Bundle bundle);
+
+	/**
+	 * Returns the platform job manager.
+	 *  
+	 * @return the job manager
+	 */
+	public IJobManager getJobManager();
+	
+	/**
+	 * Returns URL at which the Platform runtime executables and libraries are installed.
+	 * The returned value is distinct from the location of any given platform's data.
+	 *
+	 * @return the URL indicating where the platform runtime is installed.
+	 */	
+	public URL getInstallURL();
+
+	/**
+	 * Returns the location in the filesystem of the configuration information 
+	 * used to run this instance of Eclipse.  The configuration area typically
+	 * contains the list of plug-ins available for use, various user setttings
+	 * (those shared across different instances of the same configuration)
+	 * and any other such data needed by plug-ins.
+	 * 
+	 * @return the path indicating the directory containing the configuration 
+	 * metadata for this running Eclipse.
+	 */
+	public IPath getConfigurationMetadataLocation();
+	
+	/**
+	 * Takes down the splash screen if one was put up.
+	 */
+	public void endSplash();
+	
+	public URL find(Bundle b, IPath path);
+	
+	public URL find(Bundle b, IPath path, Map override);
+	
+	public InputStream openStream(Bundle b, IPath file)  throws IOException;
+	/**
+	 * Returns an input stream for the specified file. The file path
+	 * must be specified relative to this plug-in's installation location.
+	 * Optionally, the platform searches for the correct localized version
+	 * of the specified file using the users current locale, and Java
+	 * naming convention for localized resource files (locale suffix appended 
+	 * to the specified file extension).
+	 * <p>
+	 * The caller must close the returned stream when done.
+	 * </p>
+	 *
+	 * @param file path relative to plug-in installation location
+	 * @param localized <code>true</code> for the localized version
+	 *   of the file, and <code>false</code> for the file exactly
+	 *   as specified
+	 * @return an input stream
+	 */	
+	public InputStream openStream(Bundle b, IPath file, boolean localized) throws IOException;
+	
+	public IPath getStateLocation(Bundle bundle);
+	
+	public ResourceBundle getResourceBundle(Bundle bundle) throws MissingResourceException;
+	/**
+	 * Returns a resource string corresponding to the given argument value.
+	 * If the argument value specifies a resource key, the string
+	 * is looked up in the default 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 in the
+	 * plugin.properties 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>
+	 * Equivalent to <code>getResourceString(value, getResourceBundle())</code>
+	 * </p>
+	 *
+	 * @param value the value
+	 * @return the resource string
+	 * @see #getResourceBundle
+	 */
+	public String getResourceString(Bundle bundle, String value);
+	/**
+	 * 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>
+	 *     getResourceString("Hello World") returns "Hello World"</li>
+	 *     getResourceString("%name") returns "Project Name"</li>
+	 *     getResourceString("%name Hello World") returns "Project Name"</li>
+	 *     getResourceString("%abcd Hello World") returns "Hello World"</li>
+	 *     getResourceString("%abcd") returns "%abcd"</li>
+	 *     getResourceString("%%name") returns "%name"</li>
+	 * </pre>
+	 * </p>
+	 *
+	 * @param value the value
+	 * @param bundle the resource bundle
+	 * @return the resource string
+	 * @see #getResourceBundle
+	 */
+	public String getResourceString(Bundle bundle, String value, ResourceBundle resourceBundle);
+}