set up for custom build for logging. merged from carbide 2.1.x builds. this state is as it comes from platform. Next changelog will add the updates.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,272 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.Query;
+import org.eclipse.cdt.dsf.ui.concurrent.DisplayDsfExecutor;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+/**
+ * Data viewer based on a table, which reads data using asynchronous methods.
+ * <p>
+ * This viewer implements the {@link ILazyContentProvider} interface
+ * which is used by the JFace TableViewer class to populate a Table. This
+ * interface contains separate asynchronous methods for requesting the count
+ * and values for individual indexes, which neatly correspond to the methods
+ * in {@link IDataGenerator}. As an added optimization, this viewer
+ * implementation checks for the range of visible items in the view upon each
+ * request, and it cancels old requests which scroll out of view but have not
+ * been completed yet. However, it is up to the data generator implementation
+ * to check the canceled state of the requests and ignore them.
+ * </p>
+ */
+@ConfinedToDsfExecutor("fDisplayExecutor")
+public class AsyncDataViewer
+ implements ILazyContentProvider, IDataGenerator.Listener
+{
+ // Executor to use instead of Display.asyncExec().
+ @ThreadSafe
+ final private DsfExecutor fDisplayExecutor;
+
+ // The viewer and generator that this content provider using.
+ final private TableViewer fViewer;
+ final private IDataGenerator fDataGenerator;
+
+ // Fields used in request cancellation logic.
+ private List<ValueDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ValueDataRequestMonitor>();
+ private Set<Integer> fIndexesToCancel = new HashSet<Integer>();
+ private int fCancelCallsPending = 0;
+
+ public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) {
+ fViewer = viewer;
+ fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay());
+ fDataGenerator = generator;
+ fDataGenerator.addListener(this);
+ }
+
+ public void dispose() {
+ fDataGenerator.removeListener(this);
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // Set the initial count to the viewer after the input is set.
+ queryItemCount();
+ }
+
+ public void updateElement(final int index) {
+ // Calculate the visible index range.
+ final int topIdx = fViewer.getTable().getTopIndex();
+ final int botIdx = topIdx + getVisibleItemCount(topIdx);
+
+ // Request the item for the given index.
+ queryValue(index);
+
+ // Invoke a cancel task with a delay. The delay allows multiple cancel
+ // calls to be combined together improving performance of the viewer.
+ fCancelCallsPending++;
+ fDisplayExecutor.schedule(
+ new Runnable() { public void run() {
+ cancelStaleRequests(topIdx, botIdx);
+ }},
+ 1, TimeUnit.MILLISECONDS);
+ }
+
+ private int getVisibleItemCount(int top) {
+ Table table = fViewer.getTable();
+ int itemCount = table.getItemCount();
+ return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top);
+ }
+
+ @ThreadSafe
+ public void countChanged() {
+ queryItemCount();
+ }
+
+ @ThreadSafe
+ public void valuesChanged(final Set<Integer> indexes) {
+ // Mark the changed items in table viewer as dirty, this will
+ // trigger update requests for these indexes if they are
+ // visible in the viewer.
+ final TableViewer tableViewer = fViewer;
+ fDisplayExecutor.execute( new Runnable() {
+ public void run() {
+ if (!fViewer.getTable().isDisposed()) {
+ for (Integer index : indexes) {
+ tableViewer.clear(index);
+ }
+ }
+ }});
+ }
+
+
+ private void queryItemCount() {
+ // Request count from data provider. When the count is returned, we
+ // have to re-dispatch into the display thread to avoid calling
+ // the table widget on the DSF dispatch thread.
+ fIndexesToCancel.clear();
+ fDataGenerator.getCount(
+ // Use the display executor to construct the request monitor, this
+ // will cause the handleCompleted() method to be automatically
+ // called on the display thread.
+ new DataRequestMonitor<Integer>(fDisplayExecutor, null) {
+ @Override
+ protected void handleCompleted() {
+ if (!fViewer.getTable().isDisposed()) {
+ fViewer.setItemCount(getData());
+ fViewer.getTable().clearAll();
+ }
+ }
+ });
+
+ }
+
+
+ // Dedicated class for data item requests. This class holds the index
+ // argument so it can be examined when canceling stale requests.
+ private class ValueDataRequestMonitor extends DataRequestMonitor<String> {
+
+ /** Index is used when canceling stale requests. */
+ int fIndex;
+
+ ValueDataRequestMonitor(int index) {
+ super(fDisplayExecutor, null);
+ fIndex = index;
+ }
+
+ @Override
+ protected void handleCompleted() {
+ fItemDataRequestMonitors.remove(this);
+
+ // Check if the request completed successfully, otherwise ignore it.
+ if (isSuccess()) {
+ if (!fViewer.getTable().isDisposed()) {
+ fViewer.replace(getData(), fIndex);
+ }
+ }
+ }
+ }
+
+ private void queryValue(final int index) {
+ ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index);
+ fItemDataRequestMonitors.add(rm);
+ fDataGenerator.getValue(index, rm);
+ }
+
+ private void cancelStaleRequests(int topIdx, int botIdx) {
+ // Decrement the count of outstanding cancel calls.
+ fCancelCallsPending--;
+
+ // Must check again, in case disposed while re-dispatching.
+ if (fDataGenerator == null || fViewer.getTable().isDisposed()) return;
+
+ // Go through the outstanding requests and cancel any that
+ // are not visible anymore.
+ for (Iterator<ValueDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) {
+ ValueDataRequestMonitor item = itr.next();
+ if (item.fIndex < topIdx || item.fIndex > botIdx) {
+ // Set the item to canceled status, so that the data provider
+ // will ignore it.
+ item.cancel();
+
+ // Add the item index to list of indexes that were canceled,
+ // which will be sent to the table widget.
+ fIndexesToCancel.add(item.fIndex);
+
+ // Remove the item from the outstanding cancel requests.
+ itr.remove();
+ }
+ }
+ if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) {
+ Set<Integer> canceledIdxs = fIndexesToCancel;
+ fIndexesToCancel = new HashSet<Integer>();
+
+ // Clear the indexes of the canceled request, so that the
+ // viewer knows to request them again when needed.
+ // Note: clearing using TableViewer.clear(int) seems very
+ // inefficient, it's better to use Table.clear(int[]).
+ int[] canceledIdxsArray = new int[canceledIdxs.size()];
+ int i = 0;
+ for (Integer index : canceledIdxs) {
+ canceledIdxsArray[i++] = index;
+ }
+ fViewer.getTable().clear(canceledIdxsArray);
+ }
+ }
+
+
+ public static void main(String[] args) {
+ // Create the shell to hold the viewer.
+ Display display = new Display();
+ Shell shell = new Shell(display, SWT.SHELL_TRIM);
+ shell.setLayout(new GridLayout());
+ GridData data = new GridData(GridData.FILL_BOTH);
+ shell.setLayoutData(data);
+ Font font = new Font(display, "Courier", 10, SWT.NORMAL);
+
+ // Create the table viewer.
+ TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL);
+ tableViewer.getControl().setLayoutData(data);
+
+ // Create the data generator.
+ final IDataGenerator generator = new DataGeneratorWithExecutor();
+
+ // Create the content provider which will populate the viewer.
+ AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator);
+ tableViewer.setContentProvider(contentProvider);
+ tableViewer.setInput(new Object());
+
+ // Open the shell and service the display dispatch loop until user
+ // closes the shell.
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+
+ // The IDataGenerator.shutdown() method is asynchronous, this requires
+ // using a query again in order to wait for its completion.
+ Query<Object> shutdownQuery = new Query<Object>() {
+ @Override
+ protected void execute(DataRequestMonitor<Object> rm) {
+ generator.shutdown(rm);
+ }
+ };
+ ImmediateExecutor.getInstance().execute(shutdownQuery);
+ try {
+ shutdownQuery.get();
+ } catch (Exception e) {}
+
+ // Shut down the display.
+ font.dispose();
+ display.dispose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithExecutor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,336 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin;
+
+/**
+ * DSF Executor-based implementation of the data generator.
+ * <p>
+ * This generator uses a queue of client requests and processes these
+ * requests periodically using a DSF executor. The main feature of this
+ * generator is that it uses the executor as its only synchronization object.
+ * This means that all the fields with the exception of the executor can only
+ * be accessed while running in the executor thread.
+ * </p>
+ */
+//TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+//indicating allowed thread access to this class/method/member
+public class DataGeneratorWithExecutor implements IDataGenerator {
+
+ // Request objects are used to serialize the interface calls into objects
+ // which can then be pushed into a queue.
+ // TODO Ecercise 4 - Add an annotationindicating allowed concurrency access
+ // Hint: Request and its subclasses have all their fields declared as final.
+ abstract class Request {
+ final RequestMonitor fRequestMonitor;
+
+ Request(RequestMonitor rm) {
+ fRequestMonitor = rm;
+ }
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ class CountRequest extends Request {
+ CountRequest(DataRequestMonitor<Integer> rm) {
+ super(rm);
+ }
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ class ItemRequest extends Request {
+ final int fIndex;
+ ItemRequest(int index, DataRequestMonitor<String> rm) {
+ super(rm);
+ fIndex = index;
+ }
+ }
+
+ // The executor used to access all internal data of the generator.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ // Hint: If a member does not have an annotation, the programmer can assume
+ // that the concurrency rule that applies to the class also applies to this
+ // member.
+ private DsfExecutor fExecutor;
+
+ // Main request queue of the data generator. The getValue(), getCount(),
+ // and shutdown() methods write into the queue, while the serviceQueue()
+ // method reads from it.
+ // The executor used to access all internal data of the generator.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private List<Request> fQueue = new LinkedList<Request>();
+
+ // List of listeners is not synchronized, it also has to be accessed
+ // using the executor.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private List<Listener> fListeners = new LinkedList<Listener>();
+
+ // Current number of elements in this generator.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private int fCount = MIN_COUNT;
+
+ // Counter used to determine when to reset the element count.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private int fCountResetTrigger = 0;
+
+ // Elements which were modified since the last reset.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private Set<Integer> fChangedIndexes = new HashSet<Integer>();
+
+ // Flag used to ensure that requests are processed sequentially.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private boolean fServiceQueueInProgress = false;
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ public DataGeneratorWithExecutor() {
+ // Create the executor
+ fExecutor = new DefaultDsfExecutor("Supplier Executor");
+
+ // Schedule a runnable to make the random changes.
+ fExecutor.scheduleAtFixedRate(
+ new DsfRunnable() {
+ public void run() {
+ randomChanges();
+ }
+ },
+ RANDOM_CHANGE_INTERVAL,
+ RANDOM_CHANGE_INTERVAL,
+ TimeUnit.MILLISECONDS);
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ public void shutdown(final RequestMonitor rm) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ // Empty the queue of requests and fail them.
+ for (Request request : fQueue) {
+ request.fRequestMonitor.setStatus(
+ new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ request.fRequestMonitor.done();
+ }
+ fQueue.clear();
+
+ // Kill executor.
+ fExecutor.shutdown();
+ rm.done();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ public void getCount(final DataRequestMonitor<Integer> rm) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fQueue.add(new CountRequest(rm));
+ serviceQueue();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ public void getValue(final int index, final DataRequestMonitor<String> rm) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fQueue.add(new ItemRequest(index, rm));
+ serviceQueue();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ public void addListener(final Listener listener) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fListeners.add(listener);
+ }
+ });
+ } catch (RejectedExecutionException e) {}
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ public void removeListener(final Listener listener) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fListeners.remove(listener);
+ }
+ });
+ } catch (RejectedExecutionException e) {}
+ }
+
+ // Main processing function of this generator.
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private void serviceQueue() {
+
+ // TODO Exercise 3 - Add logic to discard cancelled requests from queue.
+ // Hint: Since serviceQueue() is called using the executor, and the
+ // fQueue list can only be modified when running in the executor
+ // thread. This method can safely iterate and modify fQueue without
+ // risk of race conditions or concurrent modification exceptions.
+
+ // If a queue servicing is already scheduled, do nothing.
+ if (fServiceQueueInProgress) {
+ return;
+ }
+
+ if (fQueue.size() != 0) {
+ // If there are requests to service, remove one from the queue and
+ // schedule a runnable to process the request after a processing
+ // delay.
+ fServiceQueueInProgress = true;
+ final Request request = fQueue.remove(0);
+ fExecutor.schedule(
+ new DsfRunnable() {
+ public void run() {
+ if (request instanceof CountRequest) {
+ processCountRequest((CountRequest)request);
+ } else if (request instanceof ItemRequest) {
+ processItemRequest((ItemRequest)request);
+ }
+
+ // Reset the processing flag and process next
+ // request.
+ fServiceQueueInProgress = false;
+ serviceQueue();
+ }
+ },
+ PROCESSING_DELAY, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private void processCountRequest(CountRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor;
+
+ rm.setData(fCount);
+ rm.done();
+ }
+
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private void processItemRequest(ItemRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor;
+
+ if (fChangedIndexes.contains(request.fIndex)) {
+ rm.setData("Changed: " + request.fIndex);
+ } else {
+ rm.setData(Integer.toString(request.fIndex));
+ }
+ rm.done();
+ }
+
+ /**
+ * This method simulates changes in the supplier's data set.
+ */
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private void randomChanges() {
+ // Once every number of changes, reset the count, the rest of the
+ // times just change certain values.
+ if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){
+ randomCountReset();
+ } else {
+ randomDataChange();
+ }
+ }
+
+ /**
+ * Calculates new size for provider's data set.
+ */
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private void randomCountReset() {
+ // Calculate the new count.
+ Random random = new java.util.Random();
+ fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT);
+
+ // Reset the changed values.
+ fChangedIndexes.clear();
+
+ // Notify listeners
+ for (Listener listener : fListeners) {
+ listener.countChanged();
+ }
+ }
+
+ /**
+ * Invalidates a random range of indexes.
+ */
+ // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+ // indicating allowed thread access to this class/method/member
+ private void randomDataChange() {
+ // Calculate the indexes to change.
+ Random random = new java.util.Random();
+ Set<Integer> set = new HashSet<Integer>();
+ for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
+ set.add( new Integer(Math.abs(random.nextInt()) % fCount) );
+ }
+
+ // Add the indexes to an overall set of changed indexes.
+ fChangedIndexes.addAll(set);
+
+ // Notify listeners
+ for (Listener listener : fListeners) {
+ listener.valuesChanged(set);
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/DataGeneratorWithThread.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin;
+
+/**
+ * Thread-based implementation of the data generator.
+ * <p>
+ * This generator is based around a queue of client requests and a thread which
+ * reads the requests from the queue and processes them. The distinguishing
+ * feature of this generator is that it uses a a blocking queue as the main
+ * synchronization object. However, fListeners, fShutdown, and fChangedIndexes
+ * fields also need to be thread-safe and so they implement their own
+ * synchronization.
+ * </p>
+ */
+public class DataGeneratorWithThread extends Thread implements IDataGenerator {
+
+ // Request objects are used to serialize the interface calls into objects
+ // which can then be pushed into a queue.
+ abstract class Request {
+ final RequestMonitor fRequestMonitor;
+
+ Request(RequestMonitor rm) {
+ fRequestMonitor = rm;
+ }
+ }
+
+ class CountRequest extends Request {
+ CountRequest(DataRequestMonitor<Integer> rm) {
+ super(rm);
+ }
+ }
+
+ class ItemRequest extends Request {
+ final int fIndex;
+ ItemRequest(int index, DataRequestMonitor<String> rm) {
+ super(rm);
+ fIndex = index;
+ }
+ }
+
+ class ShutdownRequest extends Request {
+ ShutdownRequest(RequestMonitor rm) {
+ super(rm);
+ }
+ }
+
+ // Main request queue of the data generator. The getValue(), getCount(),
+ // and shutdown() methods write into the queue, while the run() method
+ // reads from it.
+ private final BlockingQueue<Request> fQueue = new LinkedBlockingQueue<Request>();
+
+ // ListenerList class provides thread safety.
+ private ListenerList fListeners = new ListenerList();
+
+ // Current number of elements in this generator.
+ private int fCount = MIN_COUNT;
+
+ // Counter used to determine when to reset the element count.
+ private int fCountResetTrigger = 0;
+
+ // Elements which were modified since the last reset.
+ private Set<Integer> fChangedIndexes = Collections.synchronizedSet(new HashSet<Integer>());
+
+ // Used to determine when to make changes in data.
+ private long fLastChangeTime = System.currentTimeMillis();
+
+ // Flag indicating when the generator has been shut down.
+ private AtomicBoolean fShutdown = new AtomicBoolean(false);
+
+ public DataGeneratorWithThread() {
+ // Immediately kick off the request processing thread.
+ start();
+ }
+
+ public void shutdown(RequestMonitor rm) {
+ // Mark the generator as shut down. After the fShutdown flag is set,
+ // all new requests should be shut down.
+ if (!fShutdown.getAndSet(true)) {
+ fQueue.add(new ShutdownRequest(rm));
+ } else {
+ //
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void getCount(DataRequestMonitor<Integer> rm) {
+ if (!fShutdown.get()) {
+ fQueue.add(new CountRequest(rm));
+ } else {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void getValue(int index, DataRequestMonitor<String> rm) {
+ if (!fShutdown.get()) {
+ fQueue.add(new ItemRequest(index, rm));
+ } else {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void addListener(Listener listener) {
+ fListeners.add(listener);
+ }
+
+ public void removeListener(Listener listener) {
+ fListeners.remove(listener);
+ }
+
+ @Override
+ public void run() {
+ try {
+ while(true) {
+ // Get the next request from the queue. The time-out
+ // ensures that that the random changes get processed.
+ final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS);
+
+ // If a request was dequeued, process it.
+ if (request != null) {
+ // Simulate a processing delay.
+ Thread.sleep(PROCESSING_DELAY);
+
+ if (request instanceof CountRequest) {
+ processCountRequest((CountRequest)request);
+ } else if (request instanceof ItemRequest) {
+ processItemRequest((ItemRequest)request);
+ } else if (request instanceof ShutdownRequest) {
+ // If shutting down, just break out of the while(true)
+ // loop and thread will exit.
+ request.fRequestMonitor.done();
+ break;
+ }
+ }
+
+ // Simulate data changes.
+ randomChanges();
+ }
+ }
+ catch (InterruptedException x) {}
+ }
+
+ private void processCountRequest(CountRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor;
+
+ rm.setData(fCount);
+ rm.done();
+ }
+
+ private void processItemRequest(ItemRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor;
+
+ if (fChangedIndexes.contains(request.fIndex)) {
+ rm.setData("Changed: " + request.fIndex);
+ } else {
+ rm.setData(Integer.toString(request.fIndex));
+ }
+ rm.done();
+ }
+
+
+ private void randomChanges() {
+ // Check if enough time is elapsed.
+ if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) {
+ fLastChangeTime = System.currentTimeMillis();
+
+ // Once every number of changes, reset the count, the rest of the
+ // times just change certain values.
+ if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){
+ randomCountReset();
+ } else {
+ randomDataChange();
+ }
+ }
+ }
+
+ private void randomCountReset() {
+ // Calculate the new count.
+ Random random = new java.util.Random();
+ fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT);
+
+ // Reset the changed values.
+ fChangedIndexes.clear();
+
+ // Notify listeners
+ for (Object listener : fListeners.getListeners()) {
+ ((Listener)listener).countChanged();
+ }
+ }
+
+ private void randomDataChange() {
+ // Calculate the indexes to change.
+ Random random = new java.util.Random();
+ Set<Integer> set = new HashSet<Integer>();
+ for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
+ set.add( new Integer(Math.abs(random.nextInt()) % fCount) );
+ }
+
+ // Add the indexes to an overall set of changed indexes.
+ fChangedIndexes.addAll(set);
+
+ // Notify listeners
+ for (Object listener : fListeners.getListeners()) {
+ ((Listener)listener).valuesChanged(set);
+ }
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/IDataGenerator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer;
+
+import java.util.Set;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+
+/**
+ * Data generator is simple source of data used to populate the example table
+ * view. It contains two asynchronous methods for retrieving the data
+ * parameters: the count and the value for a given index. It also allows the
+ * view to receive events indicating when the data supplied by the generator
+ * is changed.
+ */
+// TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor)
+// indicating allowed thread access to this class/method/member
+public interface IDataGenerator {
+
+ // Constants which control the data generator behavior.
+ // Changing the count range can stress the scalability of the system, while
+ // changing of the process delay and random change interval can stress
+ // its performance.
+ final static int MIN_COUNT = 100;
+ final static int MAX_COUNT = 200;
+ final static int PROCESSING_DELAY = 10;
+ final static int RANDOM_CHANGE_INTERVAL = 10000;
+ final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
+ final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
+
+
+ // Listener interface that the view needs to implement to react
+ // to the changes in data.
+ public interface Listener {
+ void countChanged();
+ void valuesChanged(Set<Integer> indexes);
+ }
+
+ // Data access methods.
+ void getCount(DataRequestMonitor<Integer> rm);
+ void getValue(int index, DataRequestMonitor<String> rm);
+
+ // Method used to shutdown the data generator including any threads that
+ // it may use.
+ void shutdown(RequestMonitor rm);
+
+ // Methods for registering change listeners.
+ void addListener(Listener listener);
+ void removeListener(Listener listener);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/SyncDataViewer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer;
+
+import java.util.Set;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.Query;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Data viewer based on a table, which reads data using synchronous methods.
+ * <p>
+ * This viewer implements the {@link IStructuredContentProvider} interface
+ * which is used by the JFace TableViewer class to populate a Table. This
+ * interface contains one principal methods for reading data {@link #getElements(Object)},
+ * which synchronously returns an array of elements. In order to implement this
+ * method using the asynchronous data generator, this provider uses the
+ * {@link Query} object.
+ * </p>
+ */
+public class SyncDataViewer
+ implements IStructuredContentProvider, IDataGenerator.Listener
+{
+ // The viewer and generator that this content provider using.
+ final private TableViewer fViewer;
+ final private IDataGenerator fDataGenerator;
+
+ public SyncDataViewer(TableViewer viewer, IDataGenerator generator) {
+ fViewer = viewer;
+ fDataGenerator = generator;
+ fDataGenerator.addListener(this);
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // Not used
+ }
+
+
+ public Object[] getElements(Object inputElement) {
+
+ // Create the query object for reading data count.
+ Query<Integer> countQuery = new Query<Integer>() {
+ @Override
+ protected void execute(DataRequestMonitor<Integer> rm) {
+ fDataGenerator.getCount(rm);
+ }
+ };
+
+ // Submit the query to be executed. A query implements a runnable
+ // interface and it has to be executed in order to do its work.
+ ImmediateExecutor.getInstance().execute(countQuery);
+ int count = 0;
+
+ // Block until the query completes, which will happen when the request
+ // monitor of the execute() method is marked done.
+ try {
+ count = countQuery.get();
+ } catch (Exception e) {
+ // InterruptedException and ExecutionException can be thrown here.
+ // ExecutionException containing a CoreException will be thrown
+ // if an error status is set to the Query's request monitor.
+ return new Object[0];
+ }
+
+ // Create the array that will be filled with elements.
+ // For each index in the array execute a query to get the element at
+ // that index.
+ final Object[] elements = new Object[count];
+
+ for (int i = 0; i < count; i++) {
+ final int index = i;
+ Query<String> valueQuery = new Query<String>() {
+ @Override
+ protected void execute(DataRequestMonitor<String> rm) {
+ fDataGenerator.getValue(index, rm);
+ }
+ };
+ ImmediateExecutor.getInstance().execute(valueQuery);
+ try {
+ elements[i] = valueQuery.get();
+ } catch (Exception e) {
+ elements[i] = "error";
+ }
+ }
+ return elements;
+ }
+
+ public void dispose() {
+ fDataGenerator.removeListener(this);
+ }
+
+ public void countChanged() {
+ // For any event from the generator, refresh the whole viewer.
+ refreshViewer();
+ }
+
+ public void valuesChanged(Set<Integer> indexes) {
+ // For any event from the generator, refresh the whole viewer.
+ refreshViewer();
+ }
+
+ private void refreshViewer() {
+ // TODO Exercise 5 - Add a call to getElements() to force a deadlock.
+
+ // This method may be called on any thread, switch to the display
+ // thread before calling the viewer.
+ Display display = fViewer.getControl().getDisplay();
+ display.asyncExec( new Runnable() {
+ public void run() {
+ if (!fViewer.getControl().isDisposed()) {
+ fViewer.refresh();
+ }
+ }
+ });
+ }
+
+ public static void main(String[] args) {
+ // Create the shell to hold the viewer.
+ Display display = new Display();
+ Shell shell = new Shell(display, SWT.SHELL_TRIM);
+ shell.setLayout(new GridLayout());
+ GridData data = new GridData(GridData.FILL_BOTH);
+ shell.setLayoutData(data);
+ Font font = new Font(display, "Courier", 10, SWT.NORMAL);
+
+ // Create the table viewer.
+ TableViewer tableViewer = new TableViewer(shell, SWT.BORDER);
+ tableViewer.getControl().setLayoutData(data);
+
+ // Create the data generator.
+ // TODO Exercise 5 - Use the DataGeneratorWithExecutor() instead.
+ final IDataGenerator generator = new DataGeneratorWithThread();
+
+ // Create the content provider which will populate the viewer.
+ SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator);
+ tableViewer.setContentProvider(contentProvider);
+ tableViewer.setInput(new Object());
+
+ // Open the shell and service the display dispatch loop until user
+ // closes the shell.
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+
+ // The IDataGenerator.shutdown() method is asynchronous, this requires
+ // using a query again in order to wait for its completion.
+ Query<Object> shutdownQuery = new Query<Object>() {
+ @Override
+ protected void execute(DataRequestMonitor<Object> rm) {
+ generator.shutdown(rm);
+ }
+ };
+ ImmediateExecutor.getInstance().execute(shutdownQuery);
+ try {
+ shutdownQuery.get();
+ } catch (Exception e) {}
+
+ // Shut down the display.
+ font.dispose();
+ display.dispose();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/answers/AsyncDataViewer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,272 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer.answers;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.Query;
+import org.eclipse.cdt.dsf.ui.concurrent.DisplayDsfExecutor;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+/**
+ * Data viewer based on a table, which reads data using asynchronous methods.
+ * <p>
+ * This viewer implements the {@link ILazyContentProvider} interface
+ * which is used by the JFace TableViewer class to populate a Table. This
+ * interface contains separate asynchronous methods for requesting the count
+ * and values for individual indexes, which neatly correspond to the methods
+ * in {@link IDataGenerator}. As an added optimization, this viewer
+ * implementation checks for the range of visible items in the view upon each
+ * request, and it cancels old requests which scroll out of view but have not
+ * been completed yet. However, it is up to the data generator implementation
+ * to check the canceled state of the requests and ignore them.
+ * </p>
+ */
+@ConfinedToDsfExecutor("fDisplayExecutor")
+public class AsyncDataViewer
+ implements ILazyContentProvider, IDataGenerator.Listener
+{
+ // Executor to use instead of Display.asyncExec().
+ @ThreadSafe
+ final private DsfExecutor fDisplayExecutor;
+
+ // The viewer and generator that this content provider using.
+ final private TableViewer fViewer;
+ final private IDataGenerator fDataGenerator;
+
+ // Fields used in request cancellation logic.
+ private List<ValueDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ValueDataRequestMonitor>();
+ private Set<Integer> fIndexesToCancel = new HashSet<Integer>();
+ private int fCancelCallsPending = 0;
+
+ public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) {
+ fViewer = viewer;
+ fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay());
+ fDataGenerator = generator;
+ fDataGenerator.addListener(this);
+ }
+
+ public void dispose() {
+ fDataGenerator.removeListener(this);
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // Set the initial count to the viewer after the input is set.
+ queryItemCount();
+ }
+
+ public void updateElement(final int index) {
+ // Calculate the visible index range.
+ final int topIdx = fViewer.getTable().getTopIndex();
+ final int botIdx = topIdx + getVisibleItemCount(topIdx);
+
+ // Request the item for the given index.
+ queryValue(index);
+
+ // Invoke a cancel task with a delay. The delay allows multiple cancel
+ // calls to be combined together improving performance of the viewer.
+ fCancelCallsPending++;
+ fDisplayExecutor.schedule(
+ new Runnable() { public void run() {
+ cancelStaleRequests(topIdx, botIdx);
+ }},
+ 1, TimeUnit.MILLISECONDS);
+ }
+
+ private int getVisibleItemCount(int top) {
+ Table table = fViewer.getTable();
+ int itemCount = table.getItemCount();
+ return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top);
+ }
+
+ @ThreadSafe
+ public void countChanged() {
+ queryItemCount();
+ }
+
+ @ThreadSafe
+ public void valuesChanged(final Set<Integer> indexes) {
+ // Mark the changed items in table viewer as dirty, this will
+ // trigger update requests for these indexes if they are
+ // visible in the viewer.
+ final TableViewer tableViewer = fViewer;
+ fDisplayExecutor.execute( new Runnable() {
+ public void run() {
+ if (!fViewer.getTable().isDisposed()) {
+ for (Integer index : indexes) {
+ tableViewer.clear(index);
+ }
+ }
+ }});
+ }
+
+
+ private void queryItemCount() {
+ // Request count from data provider. When the count is returned, we
+ // have to re-dispatch into the display thread to avoid calling
+ // the table widget on the DSF dispatch thread.
+ fIndexesToCancel.clear();
+ fDataGenerator.getCount(
+ // Use the display executor to construct the request monitor, this
+ // will cause the handleCompleted() method to be automatically
+ // called on the display thread.
+ new DataRequestMonitor<Integer>(fDisplayExecutor, null) {
+ @Override
+ protected void handleCompleted() {
+ if (!fViewer.getTable().isDisposed()) {
+ fViewer.setItemCount(getData());
+ fViewer.getTable().clearAll();
+ }
+ }
+ });
+
+ }
+
+
+ // Dedicated class for data item requests. This class holds the index
+ // argument so it can be examined when canceling stale requests.
+ private class ValueDataRequestMonitor extends DataRequestMonitor<String> {
+
+ /** Index is used when canceling stale requests. */
+ int fIndex;
+
+ ValueDataRequestMonitor(int index) {
+ super(fDisplayExecutor, null);
+ fIndex = index;
+ }
+
+ @Override
+ protected void handleCompleted() {
+ fItemDataRequestMonitors.remove(this);
+
+ // Check if the request completed successfully, otherwise ignore it.
+ if (isSuccess()) {
+ if (!fViewer.getTable().isDisposed()) {
+ fViewer.replace(getData(), fIndex);
+ }
+ }
+ }
+ }
+
+ private void queryValue(final int index) {
+ ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index);
+ fItemDataRequestMonitors.add(rm);
+ fDataGenerator.getValue(index, rm);
+ }
+
+ private void cancelStaleRequests(int topIdx, int botIdx) {
+ // Decrement the count of outstanding cancel calls.
+ fCancelCallsPending--;
+
+ // Must check again, in case disposed while re-dispatching.
+ if (fDataGenerator == null || fViewer.getTable().isDisposed()) return;
+
+ // Go through the outstanding requests and cancel any that
+ // are not visible anymore.
+ for (Iterator<ValueDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) {
+ ValueDataRequestMonitor item = itr.next();
+ if (item.fIndex < topIdx || item.fIndex > botIdx) {
+ // Set the item to canceled status, so that the data provider
+ // will ignore it.
+ item.cancel();
+
+ // Add the item index to list of indexes that were canceled,
+ // which will be sent to the table widget.
+ fIndexesToCancel.add(item.fIndex);
+
+ // Remove the item from the outstanding cancel requests.
+ itr.remove();
+ }
+ }
+ if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) {
+ Set<Integer> canceledIdxs = fIndexesToCancel;
+ fIndexesToCancel = new HashSet<Integer>();
+
+ // Clear the indexes of the canceled request, so that the
+ // viewer knows to request them again when needed.
+ // Note: clearing using TableViewer.clear(int) seems very
+ // inefficient, it's better to use Table.clear(int[]).
+ int[] canceledIdxsArray = new int[canceledIdxs.size()];
+ int i = 0;
+ for (Integer index : canceledIdxs) {
+ canceledIdxsArray[i++] = index;
+ }
+ fViewer.getTable().clear(canceledIdxsArray);
+ }
+ }
+
+
+ public static void main(String[] args) {
+ // Create the shell to hold the viewer.
+ Display display = new Display();
+ Shell shell = new Shell(display, SWT.SHELL_TRIM);
+ shell.setLayout(new GridLayout());
+ GridData data = new GridData(GridData.FILL_BOTH);
+ shell.setLayoutData(data);
+ Font font = new Font(display, "Courier", 10, SWT.NORMAL);
+
+ // Create the table viewer.
+ TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL);
+ tableViewer.getControl().setLayoutData(data);
+
+ // Create the data generator.
+ final IDataGenerator generator = new DataGeneratorWithExecutor();
+
+ // Create the content provider which will populate the viewer.
+ AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator);
+ tableViewer.setContentProvider(contentProvider);
+ tableViewer.setInput(new Object());
+
+ // Open the shell and service the display dispatch loop until user
+ // closes the shell.
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+
+ // The IDataGenerator.shutdown() method is asynchronous, this requires
+ // using a query again in order to wait for its completion.
+ Query<Object> shutdownQuery = new Query<Object>() {
+ @Override
+ protected void execute(DataRequestMonitor<Object> rm) {
+ generator.shutdown(rm);
+ }
+ };
+ ImmediateExecutor.getInstance().execute(shutdownQuery);
+ try {
+ shutdownQuery.get();
+ } catch (Exception e) {}
+
+ // Shut down the display.
+ font.dispose();
+ display.dispose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/answers/DataGeneratorWithExecutor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer.answers;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.Immutable;
+import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin;
+
+/**
+ * DSF Executor-based implementation of the data generator.
+ * <p>
+ * This generator uses a queue of client requests and processes these
+ * requests periodically using a DSF executor. The main feature of this
+ * generator is that it uses the executor as its only synchronization object.
+ * This means that all the fields with the exception of the executor can only
+ * be accessed while running in the executor thread.
+ * </p>
+ */
+@ThreadSafe
+public class DataGeneratorWithExecutor implements IDataGenerator {
+
+ // Request objects are used to serialize the interface calls into objects
+ // which can then be pushed into a queue.
+ @Immutable
+ abstract class Request {
+ final RequestMonitor fRequestMonitor;
+
+ Request(RequestMonitor rm) {
+ fRequestMonitor = rm;
+ }
+ }
+
+ @Immutable
+ class CountRequest extends Request {
+ CountRequest(DataRequestMonitor<Integer> rm) {
+ super(rm);
+ }
+ }
+
+ @Immutable
+ class ItemRequest extends Request {
+ final int fIndex;
+ ItemRequest(int index, DataRequestMonitor<String> rm) {
+ super(rm);
+ fIndex = index;
+ }
+ }
+
+ // The executor used to access all internal data of the generator.
+ private DsfExecutor fExecutor;
+
+ // Main request queue of the data generator. The getValue(), getCount(),
+ // and shutdown() methods write into the queue, while the serviceQueue()
+ // method reads from it.
+ // The executor used to access all internal data of the generator.
+ @ConfinedToDsfExecutor("fExecutor")
+ private List<Request> fQueue = new LinkedList<Request>();
+
+ // List of listeners is not synchronized, it also has to be accessed
+ // using the executor.
+ @ConfinedToDsfExecutor("fExecutor")
+ private List<Listener> fListeners = new LinkedList<Listener>();
+
+ // Current number of elements in this generator.
+ @ConfinedToDsfExecutor("fExecutor")
+ private int fCount = MIN_COUNT;
+
+ // Counter used to determine when to reset the element count.
+ @ConfinedToDsfExecutor("fExecutor")
+ private int fCountResetTrigger = 0;
+
+ // Elements which were modified since the last reset.
+ @ConfinedToDsfExecutor("fExecutor")
+ private Set<Integer> fChangedIndexes = new HashSet<Integer>();
+
+ // Flag used to ensure that requests are processed sequentially.
+ @ConfinedToDsfExecutor("fExecutor")
+ private boolean fServiceQueueInProgress = false;
+
+ public DataGeneratorWithExecutor() {
+ // Create the executor
+ fExecutor = new DefaultDsfExecutor("Supplier Executor");
+
+ // Schedule a runnable to make the random changes.
+ fExecutor.scheduleAtFixedRate(
+ new DsfRunnable() {
+ public void run() {
+ randomChanges();
+ }
+ },
+ RANDOM_CHANGE_INTERVAL,
+ RANDOM_CHANGE_INTERVAL,
+ TimeUnit.MILLISECONDS);
+ }
+
+ public void shutdown(final RequestMonitor rm) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ // Empty the queue of requests and fail them.
+ for (Request request : fQueue) {
+ request.fRequestMonitor.setStatus(
+ new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ request.fRequestMonitor.done();
+ }
+ fQueue.clear();
+
+ // Kill executor.
+ fExecutor.shutdown();
+ rm.done();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void getCount(final DataRequestMonitor<Integer> rm) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fQueue.add(new CountRequest(rm));
+ serviceQueue();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void getValue(final int index, final DataRequestMonitor<String> rm) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fQueue.add(new ItemRequest(index, rm));
+ serviceQueue();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void addListener(final Listener listener) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fListeners.add(listener);
+ }
+ });
+ } catch (RejectedExecutionException e) {}
+ }
+
+ public void removeListener(final Listener listener) {
+ try {
+ fExecutor.execute( new DsfRunnable() {
+ public void run() {
+ fListeners.remove(listener);
+ }
+ });
+ } catch (RejectedExecutionException e) {}
+ }
+
+ // Main processing function of this generator.
+ @ConfinedToDsfExecutor("fExecutor")
+ private void serviceQueue() {
+
+ for (Iterator<Request> requestItr = fQueue.iterator(); requestItr.hasNext();) {
+ Request request = requestItr.next();
+ if (request.fRequestMonitor.isCanceled()) {
+ request.fRequestMonitor.setStatus(
+ new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, "Request canceled"));
+ request.fRequestMonitor.done();
+ requestItr.remove();
+ }
+ }
+
+ // If a queue servicing is already scheduled, do nothing.
+ if (fServiceQueueInProgress) {
+ return;
+ }
+
+ if (fQueue.size() != 0) {
+ // If there are requests to service, remove one from the queue and
+ // schedule a runnable to process the request after a processing
+ // delay.
+ fServiceQueueInProgress = true;
+ final Request request = fQueue.remove(0);
+ fExecutor.schedule(
+ new DsfRunnable() {
+ public void run() {
+ if (request instanceof CountRequest) {
+ processCountRequest((CountRequest)request);
+ } else if (request instanceof ItemRequest) {
+ processItemRequest((ItemRequest)request);
+ }
+
+ // Reset the processing flag and process next
+ // request.
+ fServiceQueueInProgress = false;
+ serviceQueue();
+ }
+ },
+ PROCESSING_DELAY, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @ConfinedToDsfExecutor("fExecutor")
+ private void processCountRequest(CountRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor;
+
+ rm.setData(fCount);
+ rm.done();
+ }
+
+ @ConfinedToDsfExecutor("fExecutor")
+ private void processItemRequest(ItemRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor;
+
+ if (fChangedIndexes.contains(request.fIndex)) {
+ rm.setData("Changed: " + request.fIndex);
+ } else {
+ rm.setData(Integer.toString(request.fIndex));
+ }
+ rm.done();
+ }
+
+ /**
+ * This method simulates changes in the supplier's data set.
+ */
+ @ConfinedToDsfExecutor("fExecutor")
+ private void randomChanges() {
+ // Once every number of changes, reset the count, the rest of the
+ // times just change certain values.
+ if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){
+ randomCountReset();
+ } else {
+ randomDataChange();
+ }
+ }
+
+ /**
+ * Calculates new size for provider's data set.
+ */
+ @ConfinedToDsfExecutor("fExecutor")
+ private void randomCountReset() {
+ // Calculate the new count.
+ Random random = new java.util.Random();
+ fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT);
+
+ // Reset the changed values.
+ fChangedIndexes.clear();
+
+ // Notify listeners
+ for (Listener listener : fListeners) {
+ listener.countChanged();
+ }
+ }
+
+ /**
+ * Invalidates a random range of indexes.
+ */
+ @ConfinedToDsfExecutor("fExecutor")
+ private void randomDataChange() {
+ // Calculate the indexes to change.
+ Random random = new java.util.Random();
+ Set<Integer> set = new HashSet<Integer>();
+ for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
+ set.add( new Integer(Math.abs(random.nextInt()) % fCount) );
+ }
+
+ // Add the indexes to an overall set of changed indexes.
+ fChangedIndexes.addAll(set);
+
+ // Notify listeners
+ for (Listener listener : fListeners) {
+ listener.valuesChanged(set);
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/answers/DataGeneratorWithThread.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer.answers;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin;
+
+/**
+ * Thread-based implementation of the data generator.
+ * <p>
+ * This generator is based around a queue of client requests and a thread which
+ * reads the requests from the queue and processes them. The distinguishing
+ * feature of this generator is that it uses a a blocking queue as the main
+ * synchronization object. However, fListeners, fShutdown, and fChangedIndexes
+ * fields also need to be thread-safe and so they implement their own
+ * synchronization.
+ * </p>
+ */
+public class DataGeneratorWithThread extends Thread implements IDataGenerator {
+
+ // Request objects are used to serialize the interface calls into objects
+ // which can then be pushed into a queue.
+ abstract class Request {
+ final RequestMonitor fRequestMonitor;
+
+ Request(RequestMonitor rm) {
+ fRequestMonitor = rm;
+ }
+ }
+
+ class CountRequest extends Request {
+ CountRequest(DataRequestMonitor<Integer> rm) {
+ super(rm);
+ }
+ }
+
+ class ItemRequest extends Request {
+ final int fIndex;
+ ItemRequest(int index, DataRequestMonitor<String> rm) {
+ super(rm);
+ fIndex = index;
+ }
+ }
+
+ class ShutdownRequest extends Request {
+ ShutdownRequest(RequestMonitor rm) {
+ super(rm);
+ }
+ }
+
+ // Main request queue of the data generator. The getValue(), getCount(),
+ // and shutdown() methods write into the queue, while the run() method
+ // reads from it.
+ private final BlockingQueue<Request> fQueue = new LinkedBlockingQueue<Request>();
+
+ // ListenerList class provides thread safety.
+ private ListenerList fListeners = new ListenerList();
+
+ // Current number of elements in this generator.
+ private int fCount = MIN_COUNT;
+
+ // Counter used to determine when to reset the element count.
+ private int fCountResetTrigger = 0;
+
+ // Elements which were modified since the last reset.
+ private Set<Integer> fChangedIndexes = Collections.synchronizedSet(new HashSet<Integer>());
+
+ // Used to determine when to make changes in data.
+ private long fLastChangeTime = System.currentTimeMillis();
+
+ // Flag indicating when the generator has been shut down.
+ private AtomicBoolean fShutdown = new AtomicBoolean(false);
+
+ public DataGeneratorWithThread() {
+ // Immediately kick off the request processing thread.
+ start();
+ }
+
+ public void shutdown(RequestMonitor rm) {
+ // Mark the generator as shut down. After the fShutdown flag is set,
+ // all new requests should be shut down.
+ if (!fShutdown.getAndSet(true)) {
+ fQueue.add(new ShutdownRequest(rm));
+ } else {
+ //
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void getCount(DataRequestMonitor<Integer> rm) {
+ if (!fShutdown.get()) {
+ fQueue.add(new CountRequest(rm));
+ } else {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void getValue(int index, DataRequestMonitor<String> rm) {
+ if (!fShutdown.get()) {
+ fQueue.add(new ItemRequest(index, rm));
+ } else {
+ rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
+ rm.done();
+ }
+ }
+
+ public void addListener(Listener listener) {
+ fListeners.add(listener);
+ }
+
+ public void removeListener(Listener listener) {
+ fListeners.remove(listener);
+ }
+
+ @Override
+ public void run() {
+ try {
+ while(true) {
+ // Get the next request from the queue. The time-out
+ // ensures that that the random changes get processed.
+ final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS);
+
+ // If a request was dequeued, process it.
+ if (request != null) {
+ // Simulate a processing delay.
+ Thread.sleep(PROCESSING_DELAY);
+
+ if (request instanceof CountRequest) {
+ processCountRequest((CountRequest)request);
+ } else if (request instanceof ItemRequest) {
+ processItemRequest((ItemRequest)request);
+ } else if (request instanceof ShutdownRequest) {
+ // If shutting down, just break out of the while(true)
+ // loop and thread will exit.
+ request.fRequestMonitor.done();
+ break;
+ }
+ }
+
+ // Simulate data changes.
+ randomChanges();
+ }
+ }
+ catch (InterruptedException x) {}
+ }
+
+ private void processCountRequest(CountRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor;
+
+ rm.setData(fCount);
+ rm.done();
+ }
+
+ private void processItemRequest(ItemRequest request) {
+ @SuppressWarnings("unchecked") // Suppress warning about lost type info.
+ DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor;
+
+ if (fChangedIndexes.contains(request.fIndex)) {
+ rm.setData("Changed: " + request.fIndex);
+ } else {
+ rm.setData(Integer.toString(request.fIndex));
+ }
+ rm.done();
+ }
+
+
+ private void randomChanges() {
+ // Check if enough time is elapsed.
+ if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) {
+ fLastChangeTime = System.currentTimeMillis();
+
+ // Once every number of changes, reset the count, the rest of the
+ // times just change certain values.
+ if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){
+ randomCountReset();
+ } else {
+ randomDataChange();
+ }
+ }
+ }
+
+ private void randomCountReset() {
+ // Calculate the new count.
+ Random random = new java.util.Random();
+ fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT);
+
+ // Reset the changed values.
+ fChangedIndexes.clear();
+
+ // Notify listeners
+ for (Object listener : fListeners.getListeners()) {
+ ((Listener)listener).countChanged();
+ }
+ }
+
+ private void randomDataChange() {
+ // Calculate the indexes to change.
+ Random random = new java.util.Random();
+ Set<Integer> set = new HashSet<Integer>();
+ for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
+ set.add( new Integer(Math.abs(random.nextInt()) % fCount) );
+ }
+
+ // Add the indexes to an overall set of changed indexes.
+ fChangedIndexes.addAll(set);
+
+ // Notify listeners
+ for (Object listener : fListeners.getListeners()) {
+ ((Listener)listener).valuesChanged(set);
+ }
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/answers/IDataGenerator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer.answers;
+
+import java.util.Set;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
+
+/**
+ * Data generator is simple source of data used to populate the example table
+ * view. It contains two asynchronous methods for retrieving the data
+ * parameters: the count and the value for a given index. It also allows the
+ * view to receive events indicating when the data supplied by the generator
+ * is changed.
+ */
+@ThreadSafe
+public interface IDataGenerator {
+
+ // Constants which control the data generator behavior.
+ // Changing the count range can stress the scalability of the system, while
+ // changing of the process delay and random change interval can stress
+ // its performance.
+ final static int MIN_COUNT = 100;
+ final static int MAX_COUNT = 200;
+ final static int PROCESSING_DELAY = 10;
+ final static int RANDOM_CHANGE_INTERVAL = 10000;
+ final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
+ final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
+
+
+ // Listener interface that the view needs to implement to react
+ // to the changes in data.
+ public interface Listener {
+ void countChanged();
+ void valuesChanged(Set<Integer> indexes);
+ }
+
+ // Data access methods.
+ void getCount(DataRequestMonitor<Integer> rm);
+ void getValue(int index, DataRequestMonitor<String> rm);
+
+ // Method used to shutdown the data generator including any threads that
+ // it may use.
+ void shutdown(RequestMonitor rm);
+
+ // Methods for registering change listeners.
+ void addListener(Listener listener);
+ void removeListener(Listener listener);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/dataviewer/answers/SyncDataViewer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.dataviewer.answers;
+
+import java.util.Set;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.Query;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Data viewer based on a table, which reads data using synchronous methods.
+ * <p>
+ * This viewer implements the {@link IStructuredContentProvider} interface
+ * which is used by the JFace TableViewer class to populate a Table. This
+ * interface contains one principal methods for reading data {@link #getElements(Object)},
+ * which synchronously returns an array of elements. In order to implement this
+ * method using the asynchronous data generator, this provider uses the
+ * {@link Query} object.
+ * </p>
+ */
+public class SyncDataViewer
+ implements IStructuredContentProvider, IDataGenerator.Listener
+{
+ // The viewer and generator that this content provider using.
+ final private TableViewer fViewer;
+ final private IDataGenerator fDataGenerator;
+
+ public SyncDataViewer(TableViewer viewer, IDataGenerator generator) {
+ fViewer = viewer;
+ fDataGenerator = generator;
+ fDataGenerator.addListener(this);
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // Not used
+ }
+
+
+ public Object[] getElements(Object inputElement) {
+
+ // Create the query object for reading data count.
+ Query<Integer> countQuery = new Query<Integer>() {
+ @Override
+ protected void execute(DataRequestMonitor<Integer> rm) {
+ fDataGenerator.getCount(rm);
+ }
+ };
+
+ // Submit the query to be executed. A query implements a runnable
+ // interface and it has to be executed in order to do its work.
+ ImmediateExecutor.getInstance().execute(countQuery);
+ int count = 0;
+
+ // Block until the query completes, which will happen when the request
+ // monitor of the execute() method is marked done.
+ try {
+ count = countQuery.get();
+ } catch (Exception e) {
+ // InterruptedException and ExecutionException can be thrown here.
+ // ExecutionException containing a CoreException will be thrown
+ // if an error status is set to the Query's request monitor.
+ return new Object[0];
+ }
+
+ // Create the array that will be filled with elements.
+ // For each index in the array execute a query to get the element at
+ // that index.
+ final Object[] elements = new Object[count];
+
+ for (int i = 0; i < count; i++) {
+ final int index = i;
+ Query<String> valueQuery = new Query<String>() {
+ @Override
+ protected void execute(DataRequestMonitor<String> rm) {
+ fDataGenerator.getValue(index, rm);
+ }
+ };
+ ImmediateExecutor.getInstance().execute(valueQuery);
+ try {
+ elements[i] = valueQuery.get();
+ } catch (Exception e) {
+ elements[i] = "error";
+ }
+ }
+ return elements;
+ }
+
+ public void dispose() {
+ fDataGenerator.removeListener(this);
+ }
+
+ public void countChanged() {
+ // For any event from the generator, refresh the whole viewer.
+ refreshViewer();
+ }
+
+ public void valuesChanged(Set<Integer> indexes) {
+ // For any event from the generator, refresh the whole viewer.
+ refreshViewer();
+ }
+
+ private void refreshViewer() {
+ getElements(null);
+
+ // This method may be called on any thread, switch to the display
+ // thread before calling the viewer.
+ Display display = fViewer.getControl().getDisplay();
+ display.asyncExec( new Runnable() {
+ public void run() {
+ if (!fViewer.getControl().isDisposed()) {
+ fViewer.refresh();
+ }
+ }
+ });
+ }
+
+ public static void main(String[] args) {
+ // Create the shell to hold the viewer.
+ Display display = new Display();
+ Shell shell = new Shell(display, SWT.SHELL_TRIM);
+ shell.setLayout(new GridLayout());
+ GridData data = new GridData(GridData.FILL_BOTH);
+ shell.setLayoutData(data);
+ Font font = new Font(display, "Courier", 10, SWT.NORMAL);
+
+ // Create the table viewer.
+ TableViewer tableViewer = new TableViewer(shell, SWT.BORDER);
+ tableViewer.getControl().setLayoutData(data);
+
+ // Create the data generator.
+ final IDataGenerator generator = new DataGeneratorWithExecutor();
+
+ // Create the content provider which will populate the viewer.
+ SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator);
+ tableViewer.setContentProvider(contentProvider);
+ tableViewer.setInput(new Object());
+
+ // Open the shell and service the display dispatch loop until user
+ // closes the shell.
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+
+ // The IDataGenerator.shutdown() method is asynchronous, this requires
+ // using a query again in order to wait for its completion.
+ Query<Object> shutdownQuery = new Query<Object>() {
+ @Override
+ protected void execute(DataRequestMonitor<Object> rm) {
+ generator.shutdown(rm);
+ }
+ };
+ ImmediateExecutor.getInstance().execute(shutdownQuery);
+ try {
+ shutdownQuery.get();
+ } catch (Exception e) {}
+
+ // Shut down the display.
+ font.dispose();
+ display.dispose();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/requestmonitor/Async2Plus2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.requestmonitor;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+
+/**
+ * Example of using a DataRequestMonitor to retrieve a result from an
+ * asynchronous method.
+ */
+public class Async2Plus2 {
+
+ public static void main(String[] args) {
+ Executor executor = ImmediateExecutor.getInstance();
+ DataRequestMonitor<Integer> rm =
+ new DataRequestMonitor<Integer>(executor, null) {
+ @Override
+ protected void handleCompleted() {
+ System.out.println("2 + 2 = " + getData());
+ }
+ };
+ asyncAdd(2, 2, rm);
+ }
+
+ static void asyncAdd(int value1, int value2, DataRequestMonitor<Integer> rm) {
+ rm.setData(value1 + value2);
+ rm.done();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncHelloWorld.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.requestmonitor;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+
+/**
+ * "Hello world" example which uses an asynchronous method to print out
+ * the result.
+ * <p>
+ * The main method uses an immediate executor, which executes runnables
+ * as soon as they are submitted, in creating its request monitor.
+ *
+ */
+public class AsyncHelloWorld {
+
+ public static void main(String[] args) {
+ Executor executor = ImmediateExecutor.getInstance();
+ RequestMonitor rm = new RequestMonitor(executor, null);
+ asyncHelloWorld(rm);
+ }
+
+ static void asyncHelloWorld(RequestMonitor rm) {
+ System.out.println("Hello world");
+ // TODO Exercise 1: - Call the second async. "Hello world 2" method.
+ // Hint: Calling an asynchronous method requires passing to it a
+ // request monitor. A new request monitor can be constructed with
+ // a parent RequestMonitor as an argument argument. The parent gets
+ // completed automatically when the lower level request monitor is
+ // completed.
+ rm.done();
+ }
+
+ // TODO: Exercise 1 - Add a second async. "Hello world 2" method.
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/requestmonitor/AsyncQuicksort.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.requestmonitor;
+
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+
+/**
+ * Example of using a CountingRequestMonitor to wait for multiple
+ * asynchronous calls to complete.
+ */
+public class AsyncQuicksort {
+
+ static Executor fgExecutor = ImmediateExecutor.getInstance();
+
+ public static void main(String[] args) {
+ final int[] array = {5, 7, 8, 3, 2, 1, 9, 5, 4};
+
+ System.out.println("To sort: " + Arrays.toString(array));
+ asyncQuicksort(
+ array, 0, array.length - 1,
+ new RequestMonitor(fgExecutor, null) {
+ @Override
+ protected void handleCompleted() {
+ System.out.println("Sorted: " + Arrays.toString(array));
+ }
+ });
+ }
+
+ static void asyncQuicksort(final int[] array, final int left,
+ final int right, final RequestMonitor rm)
+ {
+ if (right > left) {
+ int pivot = left;
+ // TODO: Exercise 2 - Convert the call to partition into an
+ // asynchronous call to asyncPartition().
+ // Hint: The rest of the code below should be executed inside
+ // the DataRequestMonitor.handleCompleted() overriding method.
+ int newPivot = partition(array, left, right, pivot);
+ printArray(array, left, right, newPivot);
+
+ CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm);
+ asyncQuicksort(array, left, newPivot - 1, countingRm);
+ asyncQuicksort(array, newPivot + 1, right, countingRm);
+ countingRm.setDoneCount(2);
+ } else {
+ rm.done();
+ }
+ }
+
+ // TODO Exercise 2 - Convert partition to an asynchronous method.
+ // Hint: a DataRequestMonitor<Integer> should be used to carry the
+ // return value to the caller.
+ static int partition(int[] array, int left, int right, int pivot)
+ {
+ int pivotValue = array[pivot];
+ array[pivot] = array[right];
+ array[right] = pivotValue;
+ int store = left;
+ for (int i = left; i < right; i++) {
+ if (array[i] <= pivotValue) {
+ int tmp = array[store];
+ array[store] = array[i];
+ array[i] = tmp;
+ store++;
+ }
+ }
+ array[right] = array[store];
+ array[store] = pivotValue;
+
+ // TODO: Request Monitors Exercise 2 - Return the data to caller using
+ // a request monitor.
+ return store;
+ }
+
+ static void printArray(int[] array, int left, int right, int pivot) {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < array.length; i++ ) {
+ if (i == left) {
+ buffer.append('>');
+ } else if (i == pivot) {
+ buffer.append('-');
+ } else {
+ buffer.append(' ');
+ }
+ buffer.append(array[i]);
+
+ if (i == right) {
+ buffer.append('<');
+ } else if (i == pivot) {
+ buffer.append('-');
+ } else {
+ buffer.append(' ');
+ }
+ }
+
+ System.out.println(buffer);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/requestmonitor/answers/Async2Plus2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.requestmonitor.answers;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+
+/**
+ * Example of using a DataRequestMonitor to retrieve a result from an
+ * asynchronous method.
+ */
+public class Async2Plus2 {
+
+ public static void main(String[] args) {
+ Executor executor = ImmediateExecutor.getInstance();
+ DataRequestMonitor<Integer> rm =
+ new DataRequestMonitor<Integer>(executor, null) {
+ @Override
+ protected void handleCompleted() {
+ System.out.println("2 + 2 = " + getData());
+ }
+ };
+ asyncAdd(2, 2, rm);
+ }
+
+ static void asyncAdd(int value1, int value2, DataRequestMonitor<Integer> rm) {
+ rm.setData(value1 + value2);
+ rm.done();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/requestmonitor/answers/AsyncHelloWorld.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.requestmonitor.answers;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+
+/**
+ * "Hello world" example which uses an asynchronous method to print out
+ * the result.
+ * <p>
+ * The main method uses an immediate executor, which executes runnables
+ * as soon as they are submitted, in creating its request monitor.
+ *
+ */
+public class AsyncHelloWorld {
+
+ public static void main(String[] args) {
+ Executor executor = ImmediateExecutor.getInstance();
+ RequestMonitor rm = new RequestMonitor(executor, null);
+ asyncHelloWorld(rm);
+ }
+
+ static void asyncHelloWorld(RequestMonitor rm) {
+ System.out.println("Hello world");
+ RequestMonitor rm2 = new RequestMonitor(ImmediateExecutor.getInstance(), rm);
+ asyncHelloWorld2(rm2);
+ }
+
+ static void asyncHelloWorld2(RequestMonitor rm) {
+ System.out.println("Hello world 2");
+ rm.done();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cdt/cdt_6_0_x/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/requestmonitor/answers/AsyncQuicksort.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.examples.dsf.requestmonitor.answers;
+
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+
+/**
+ * Example of using a CountingRequestMonitor to wait for multiple
+ * asynchronous calls to complete.
+ */
+public class AsyncQuicksort {
+
+ static Executor fgExecutor = ImmediateExecutor.getInstance();
+
+ public static void main(String[] args) {
+ final int[] array = {5, 7, 8, 3, 2, 1, 9, 5, 4};
+
+ System.out.println("To sort: " + Arrays.toString(array));
+ asyncQuicksort(
+ array, 0, array.length - 1,
+ new RequestMonitor(fgExecutor, null) {
+ @Override
+ protected void handleCompleted() {
+ System.out.println("Sorted: " + Arrays.toString(array));
+ }
+ });
+ }
+
+ static void asyncQuicksort(final int[] array, final int left,
+ final int right, final RequestMonitor rm)
+ {
+ if (right > left) {
+ int pivot = left;
+ asyncPartition(
+ array, left, right, pivot,
+ new DataRequestMonitor<Integer>(fgExecutor, rm) {
+ @Override
+ protected void handleCompleted() {
+ int newPivot = getData();
+ printArray(array, left, right, newPivot);
+
+ CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm);
+ asyncQuicksort(array, left, newPivot - 1, countingRm);
+ asyncQuicksort(array, newPivot + 1, right, countingRm);
+ countingRm.setDoneCount(2);
+ }
+ });
+ } else {
+ rm.done();
+ }
+ }
+
+ static void asyncPartition(int[] array, int left, int right, int pivot, DataRequestMonitor<Integer> rm)
+ {
+ int pivotValue = array[pivot];
+ array[pivot] = array[right];
+ array[right] = pivotValue;
+ int store = left;
+ for (int i = left; i < right; i++) {
+ if (array[i] <= pivotValue) {
+ int tmp = array[store];
+ array[store] = array[i];
+ array[i] = tmp;
+ store++;
+ }
+ }
+ array[right] = array[store];
+ array[store] = pivotValue;
+
+ // Java 5 automatically converts the int type of the store variable
+ // to an Integer object.
+ rm.setData(store);
+ rm.done();
+ }
+
+ static void printArray(int[] array, int left, int right, int pivot) {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < array.length; i++ ) {
+ if (i == left) {
+ buffer.append('>');
+ } else if (i == pivot) {
+ buffer.append('-');
+ } else {
+ buffer.append(' ');
+ }
+ buffer.append(array[i]);
+
+ if (i == right) {
+ buffer.append('<');
+ } else if (i == pivot) {
+ buffer.append('-');
+ } else {
+ buffer.append(' ');
+ }
+ }
+
+ System.out.println(buffer);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/foo.txt Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,557 @@
+# HG changeset patch
+# User dadubrow
+# Date 1243954767 18000
+# Node ID 06d88bb6aac0b30ae0bb2b46f620317d07b4c244
+# Parent 3ac8c55882b5055ce4206fab17cb2b5c01df9d9f
+Add refresh logging to core resources plugin
+
+diff -r 3ac8c55882b5 -r 06d88bb6aac0 platform/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java
+--- a/platform/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java Mon Jun 01 19:29:06 2009 -0500
++++ b/platform/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java Tue Jun 02 09:59:27 2009 -0500
+@@ -15,6 +15,7 @@
+ import java.net.URI;
+ import java.util.*;
+ import org.eclipse.core.filesystem.*;
++import org.eclipse.core.internal.refresh.RefreshJob;
+ import org.eclipse.core.internal.resources.*;
+ import org.eclipse.core.internal.resources.File;
+ import org.eclipse.core.internal.utils.*;
+@@ -731,6 +732,26 @@
+ }
+
+ public boolean refresh(IResource target, int depth, boolean updateAliases, IProgressMonitor monitor) throws CoreException {
++ long startTimeMs = System.currentTimeMillis();
++ boolean result = refreshImpl(target, depth, updateAliases, monitor);
++ if (!RefreshJob.isRefreshing()) {
++ long totalMs = System.currentTimeMillis() - startTimeMs;
++ StringBuffer sb = new StringBuffer();
++ sb.append("Refresh called on ");
++ sb.append(target.getFullPath());
++ sb.append(" completed in : ");
++ sb.append(totalMs);
++ sb.append("ms");
++ ResourcesPlugin.writeRefreshLog(sb.toString());
++ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
++ for (int i = 2; i < trace.length; i++) { // skip the getStackTrace and dump calls
++ ResourcesPlugin.writeRefreshLog("\tat " + trace[i]);
++ }
++ }
++ return result;
++ }
++
++ private boolean refreshImpl(IResource target, int depth, boolean updateAliases, IProgressMonitor monitor) throws CoreException {
+ switch (target.getType()) {
+ case IResource.ROOT :
+ return refreshRoot((IWorkspaceRoot) target, depth, updateAliases, monitor);
+@@ -787,7 +808,7 @@
+ // drop the depth by one level since processing the root counts as one level.
+ depth = depth == IResource.DEPTH_ONE ? IResource.DEPTH_ZERO : depth;
+ for (int i = 0; i < projects.length; i++)
+- changed |= refresh(projects[i], depth, updateAliases, Policy.subMonitorFor(monitor, 1));
++ changed |= refreshImpl(projects[i], depth, updateAliases, Policy.subMonitorFor(monitor, 1));
+ return changed;
+ } finally {
+ monitor.done();
+diff -r 3ac8c55882b5 -r 06d88bb6aac0 platform/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/RefreshJob.java
+--- a/platform/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/RefreshJob.java Mon Jun 01 19:29:06 2009 -0500
++++ b/platform/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/RefreshJob.java Tue Jun 02 09:59:27 2009 -0500
+@@ -11,6 +11,7 @@
+ package org.eclipse.core.internal.refresh;
+
+ import java.util.*;
++
+ import org.eclipse.core.internal.localstore.PrefixPool;
+ import org.eclipse.core.internal.utils.Messages;
+ import org.eclipse.core.internal.utils.Policy;
+@@ -27,6 +28,13 @@
+ */
+ public class RefreshJob extends WorkspaceJob {
+ private static final long UPDATE_DELAY = 200;
++
++ /**
++ * Flag indicating refreshing in progress if > 0
++ */
++ private static int refreshingLevel = 0;
++
++
+ /**
+ * List of refresh requests. Requests are processed in order from
+ * the end of the list. Requests can be added to either the beginning
+@@ -151,6 +159,13 @@
+ MultiStatus errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, 1, msg, null);
+ long longestRefresh = 0;
+ try {
++ refreshingLevel++;
++ ResourcesPlugin.writeRefreshLog(RefreshManager.DEBUG_PREFIX + " refreshing started..."); //$NON-NLS-1$
++ for (Iterator iterator = fRequests.iterator(); iterator.hasNext();) {
++ IResource resource = (IResource) iterator.next();
++ ResourcesPlugin.writeRefreshLog(RefreshManager.DEBUG_PREFIX + " requested resource to refresh: " + resource.getFullPath()); //$NON-NLS-1$
++ }
++
+ if (RefreshManager.DEBUG)
+ Policy.debug(RefreshManager.DEBUG_PREFIX + " starting refresh job"); //$NON-NLS-1$
+ int refreshCount = 0;
+@@ -193,6 +208,9 @@
+ monitor.done();
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " finished refresh job in: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
++
++ ResourcesPlugin.writeRefreshLog(RefreshManager.DEBUG_PREFIX + " finished refresh job in: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
++ refreshingLevel--;
+ }
+ if (!errors.isOK())
+ return errors;
+@@ -222,4 +240,8 @@
+ System.out.println(RefreshManager.DEBUG_PREFIX + " disabling auto-refresh"); //$NON-NLS-1$
+ cancel();
+ }
++
++ public static boolean isRefreshing() {
++ return refreshingLevel > 0;
++ }
+ }
+diff -r 3ac8c55882b5 -r 06d88bb6aac0 platform/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/platform/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties Tue Jun 02 09:59:27 2009 -0500
+@@ -0,0 +1,291 @@
++###############################################################################
++# Copyright (c) 2000, 2008 IBM Corporation and others.
++# All rights reserved. This program and the accompanying materials
++# are made available under the terms of the Eclipse Public License v1.0
++# which accompanies this distribution, and is available at
++# http://www.eclipse.org/legal/epl-v10.html
++#
++# Contributors:
++# IBM Corporation - initial API and implementation
++###############################################################################
++### Resources plugin messages.
++
++### dtree
++dtree_immutable = Illegal attempt to modify an immutable tree.
++dtree_malformedTree = Malformed tree.
++dtree_missingChild = Missing child node: {0}.
++dtree_notFound = Tree element ''{0}'' not found.
++dtree_notImmutable = Tree must be immutable.
++dtree_reverse = Tried to reverse a non-comparison tree.
++dtree_subclassImplement = Subclass should have implemented this.
++dtree_switchError = Switch error in DeltaTreeReader.readNode().
++
++### events
++events_builderError = Errors running builder ''{0}'' on project ''{1}''.
++events_building_0 = Building workspace
++events_building_1 = Building ''{0}''
++events_errors = Errors during build.
++events_instantiate_1 = Error instantiating builder ''{0}''.
++events_invoking_1 = Invoking builder on ''{0}''.
++events_invoking_2 = Invoking ''{0}'' on ''{1}''.
++events_skippingBuilder = Skipping builder ''{0}'' for project ''{1}''. Either the builder is missing from the install, or it belongs to a project nature that is missing or disabled.
++events_unknown = {0} encountered while running {1}.
++
++history_copyToNull = Unable to copy local history to or from a null location.
++history_copyToSelf = Unable to copy local history to and from the same location.
++history_errorContentDescription = Error retrieving content description for local history for: ''{0}''.
++history_notValid = State is not valid or might have expired.
++history_problemsCleaning = Problems cleaning up history store.
++
++links_creating = Creating link.
++links_errorLinkReconcile = Error processing changed links in project description file.
++links_invalidLocation = ''{0}'' is not a valid location for linked resources.
++links_localDoesNotExist = Cannot create linked resource. Local location ''{0}'' does not exist.
++links_locationOverlapsLink = ''{0}'' is not a valid location because the project contains a linked resource at that location.
++links_locationOverlapsProject = Cannot create a link to ''{0}'' because it overlaps the location of the project that contains the linked resource.
++links_natureVeto = Linking is not allowed because project nature ''{0}'' does not allow it.
++links_noPath = A Link location must be specified.
++links_overlappingResource = Location ''{0}'' may overlap another resource. This can cause unexpected side-effects.
++links_updatingDuplicate = Updating duplicate resource: ''{0}''.
++links_parentNotAccessible = Cannot create linked resource ''{0}''. The parent resource is not accessible.
++links_notFileFolder = Cannot create linked resource ''{0}''. Only files and folders can be linked.
++links_vetoNature = Cannot add nature because project ''{0}'' contains linked resources, and nature ''{1}'' does not allow it.
++links_workspaceVeto = Linked resources are not supported by this application.
++links_wrongLocalType = Cannot create linked resource ''{0}''. Files cannot be linked to folders.
++
++### local store
++localstore_copying = Copying ''{0}''.
++localstore_copyProblem = Problems encountered while copying resources.
++localstore_couldnotDelete = Could not delete ''{0}''.
++localstore_couldNotMove = Could not move ''{0}''.
++localstore_couldNotRead = Could not read file ''{0}''.
++localstore_couldNotWrite = Could not write file ''{0}''.
++localstore_couldNotWriteReadOnly = Could not write to read-only file: ''{0}''.
++localstore_deleteProblem = Problems encountered while deleting resources.
++localstore_deleting = Deleting ''{0}''.
++localstore_failedReadDuringWrite = Could not read from source when writing file ''{0}''
++localstore_fileExists = A resource already exists on disk ''{0}''.
++localstore_fileNotFound = File not found: {0}.
++localstore_locationUndefined = The location for ''{0}'' could not be determined because it is based on an undefined path variable.
++localstore_refreshing = Refreshing ''{0}''.
++localstore_refreshingRoot = Refreshing workspace.
++localstore_resourceExists = Resource already exists on disk: ''{0}''.
++localstore_resourceIsOutOfSync = Resource is out of sync with the file system: ''{0}''.
++
++### Resource mappings and models
++mapping_invalidDef = Model provider extension found with invalid definition: {0}.
++mapping_wrongType = Model provider ''{0}'' does not extend ModelProvider.
++mapping_noIdentifier = Found model provider extension with no identifier; ignoring extension.
++mapping_validate = Validating resource changes
++mapping_multiProblems = Multiple potential side effects have been identified.
++
++### internal.resources
++natures_duplicateNature = Duplicate nature: {0}.
++natures_hasCycle = Nature is invalid because its prerequisites form a cycle: {0}
++natures_missingIdentifier = Found nature extension with no identifier; ignoring extension.
++natures_missingNature = Nature does not exist: {0}.
++natures_missingPrerequisite = Nature {0} is missing prerequisite nature: {1}.
++natures_multipleSetMembers = Multiple natures found for nature set: {0}.
++natures_invalidDefinition = Nature extension found with invalid definition: {0}.
++natures_invalidRemoval = Cannot remove nature {0} because it is a prerequisite of nature {1}.
++natures_invalidSet = The set of natures is not valid.
++
++pathvar_length = Path variable name must have a length > 0.
++pathvar_beginLetter = Path variable name must begin with a letter or underscore.
++pathvar_invalidChar = Path variable name cannot contain character: {0}.
++pathvar_invalidValue = Path variable value must be valid and absolute.
++pathvar_undefined = ''{0}'' is not a valid location. The location is relative to undefined workspace path variable ''{1}''.
++pathvar_whitespace= Path variable name cannot contain whitespace
++
++### preferences
++preferences_deleteException=Exception deleting file: {0}.
++preferences_loadException=Exception loading preferences from: {0}.
++preferences_operationCanceled=Operation canceled.
++preferences_removeNodeException=Exception while removing preference node: {0}.
++preferences_clearNodeException=Exception while clearing preference node: {0}.
++preferences_saveProblems=Exception occurred while saving project preferences: {0}.
++preferences_syncException=Exception occurred while synchronizing node: {0}.
++
++projRead_badLinkName = Names ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
++projRead_badLinkType2 = Types ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
++projRead_badLocation = Locations ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
++projRead_emptyLinkName = Empty name detected for linked resource with type ''{0}'' and location ''{1}''.
++projRead_badLinkType = Illegal link type \"-1\" detected for linked resource with name ''{0}'' and location ''{1}''.
++projRead_badLinkLocation = Empty location detected for linked resource with name ''{0}'' and type ''{1}''.
++projRead_whichKey = Two values detected for an argument name in a build command: ''{0}'' and ''{1}''. Using ''{0}''.
++projRead_whichValue = Two values detected for an argument value in a build command: ''{0}'' and ''{1}''. Using ''{0}''.
++projRead_notProjectDescription = Encountered element name ''{0}'' instead of \"project\" when trying to read a project description file.
++projRead_failureReadingProjectDesc = Failure occurred reading .project file.
++
++properties_qualifierIsNull = Qualifier part of property key cannot be null.
++properties_readProperties = Failure while reading persistent properties for resource ''{0}'', file was corrupt. Some properties may have been lost.
++properties_valueTooLong = Could not set property: {0} {1}. Value is too long.
++properties_couldNotClose = Could not close property store for: {0}.
++
++### auto-refresh
++refresh_jobName = Refreshing workspace
++refresh_task = Resources to refresh: {0}
++refresh_pollJob = Searching for local changes
++refresh_refreshErr = Problems occurred while refreshing local changes
++refresh_installError = An error occurred while installing an auto-refresh monitor
++
++resources_cannotModify = The resource tree is locked for modifications.
++resources_changeInAdd = Trying to change a marker in an add method.
++resources_charsetBroadcasting = Reporting encoding changes.
++resources_charsetUpdating = Updating encoding settings.
++resources_closing_0 = Closing workspace.
++resources_closing_1 = Closing ''{0}''.
++resources_copyDestNotSub = Cannot copy ''{0}''. Destination should not be under source''s hierarchy.
++resources_copying = Copying ''{0}''.
++resources_copying_0 = Copying.
++resources_copyNotMet = Copy requirements not met.
++resources_copyProblem = Problems encountered while copying resources.
++resources_couldnotDelete = Could not delete ''{0}''.
++resources_create = Create.
++resources_creating = Creating resource ''{0}''.
++resources_deleteMeta = Could not delete metadata for ''{0}''.
++resources_deleteProblem = Problems encountered while deleting resources.
++resources_deleting = Deleting ''{0}''.
++resources_deleting_0 = Deleting.
++resources_destNotNull = Destination path should not be null.
++resources_errorContentDescription = Error retrieving content description for resource ''{0}''.
++resources_errorDeleting = Error deleting resource ''{0}'' from the workspace tree.
++resources_errorMarkersDelete = Error deleting markers for resource ''{0}''.
++resources_errorMarkersMove = Error moving markers from resource ''{0}'' to ''{1}''.
++resources_errorMembers = Error retrieving members of container ''{0}''.
++resources_errorMoving = Error moving resource ''{0}'' to ''{1}'' in the workspace tree.
++resources_errorNature = Error configuring nature ''{0}''.
++resources_errorPropertiesMove = Error moving properties for resource ''{0}'' to ''{1}''.
++resources_errorRefresh = Errors occurred during refresh of resource ''{0}''.
++resources_errorReadProject = Failed to read project description file from location ''{0}''.
++resources_errorMultiRefresh = Errors occurred while refreshing resources with the local file system.
++resources_errorValidator = Exception running validator code.
++resources_errorVisiting = An error occurred while traversing resources.
++resources_existsDifferentCase = A resource exists with a different case: ''{0}''.
++resources_existsLocalDifferentCase = A resource exists on disk with a different case: ''{0}''.
++resources_exMasterTable = Could not read master table.
++resources_exReadProjectLocation = Could not read the project location for ''{0}''.
++resources_exSafeRead = Could not read safe table.
++resources_exSafeSave = Could not save safe table.
++resources_exSaveMaster = Could not save master table.
++resources_exSaveProjectLocation = Could not save the project location for ''{0}''.
++resources_fileExists = A resource already exists on disk ''{0}''.
++resources_fileToProj = Cannot copy a file to a project.
++resources_flushingContentDescriptionCache = Flushing content description cache.
++resources_folderOverFile = Cannot overwrite folder with file ''{0}''.
++resources_format = Incompatible file format. Workspace was saved with an incompatible version: {0}.
++resources_initValidator = Unable to instantiate validator.
++resources_initHook = Unable to instantiate move/delete hook.
++resources_initTeamHook = Unable to instantiate team hook.
++resources_invalidCharInName = {0} is an invalid character in resource name ''{1}''.
++resources_invalidCharInPath = {0} is an invalid character in path ''{1}''.
++resources_invalidName = ''{0}'' is an invalid name on this platform.
++resources_invalidPath = ''{0}'' is an invalid resource path.
++resources_invalidProjDesc = Invalid project description.
++resources_invalidResourceName = ''{0}'' is an invalid resource name.
++resources_invalidRoot = Root (/) is an invalid resource path.
++resources_markerNotFound = Marker id {0} not found.
++resources_missingProjectMeta = The project description file (.project) for ''{0}'' is missing. This file contains important information about the project. The project will not function properly until this file is restored.
++resources_missingProjectMetaRepaired = The project description file (.project) for ''{0}'' was missing. This file contains important information about the project. A new project description file has been created, but some information about the project may have been lost.
++resources_moveDestNotSub = Cannot move ''{0}''. Destination should not be under source''s hierarchy.
++resources_moveMeta = Error moving metadata area from {0} to {1}.
++resources_moveNotMet = Move requirements not met.
++resources_moveNotProject = Cannot move ''{0}'' to ''{1}''. Source must be a project.
++resources_moveProblem = Problems encountered while moving resources.
++resources_moveRoot = Cannot move the workspace root.
++resources_moving = Moving ''{0}''.
++resources_moving_0 = Moving.
++resources_mustBeAbsolute = Path ''{0}'' must be absolute.
++resources_mustBeLocal = Resource ''{0}'' is not local.
++resources_mustBeOpen = Resource ''{0}'' is not open.
++resources_mustExist = Resource ''{0}'' does not exist.
++resources_mustNotExist = Resource ''{0}'' already exists.
++resources_nameEmpty = Names cannot be empty.
++resources_nameNull = Name must not be null.
++resources_natureClass = Missing project nature class for ''{0}''.
++resources_natureDeconfig = Error deconfiguring nature: {0}.
++resources_natureExtension = Missing project nature extension for {0}.
++resources_natureFormat = Project nature {0} does not specify a runtime attribute.
++resources_natureImplement = Project nature {0} does not implement IProjectNature.
++resources_notChild = Resource ''{0}'' is not a child of ''{1}''.
++resources_oneValidator = There must be exactly 0 or 1 validator extensions defined in the fileModificationValidator extension point.
++resources_oneHook = There must be exactly 0 or 1 hook extensions defined in the moveDeleteHook extension point.
++resources_oneTeamHook = There must be exactly 0 or 1 hook extensions defined in the teamHook extension point.
++resources_opening_1 = Opening ''{0}''.
++resources_overlapWorkspace = {0} overlaps the workspace location: {1}
++resources_overlapProject = {0} overlaps the location of another project: ''{1}''
++resources_pathNull = Paths must not be null.
++resources_projectDesc = Problems encountered while setting project description.
++resources_projectDescSync = Could not set the project description for ''{0}'' because the project description file (.project) is out of sync with the file system.
++resources_projectPath = Path for project must have only one segment.
++resources_reading = Reading.
++resources_readingSnap = Reading snapshot.
++resources_readingEncoding = Could not read encoding settings.
++resources_readMarkers = Failure while reading markers, the marker file was corrupt. Some markers may be lost.
++resources_readMeta = Could not read metadata for ''{0}''.
++resources_readMetaWrongVersion = Could not read metadata for ''{0}''. Unexpected version: {1}.
++resources_readOnly = Resource ''{0}'' is read-only.
++resources_readOnly2 = Cannot edit read-only resources.
++resources_readProjectMeta = Failed to read the project description file (.project) for ''{0}''. The file has been changed on disk, and it now contains invalid information. The project will not function properly until the description file is restored to a valid state.
++resources_readProjectTree = Problems reading project tree.
++resources_readSync = Errors reading sync info file: {0}.
++resources_readWorkspaceMeta = Could not read workspace metadata.
++resources_readWorkspaceMetaValue = Invalid attribute value in workspace metadata: {0}. Value will be ignored.
++resources_readWorkspaceSnap = Problems reading workspace tree snapshot.
++resources_readWorkspaceTree = Problems reading workspace tree.
++resources_refreshing = Refreshing ''{0}''.
++resources_refreshingRoot = Refreshing workspace.
++resources_resetMarkers = Could not reset markers snapshot file.
++resources_resetSync = Could not reset sync info snapshot file.
++resources_resourcePath = Invalid path for resource ''{0}''. Must include project and resource name.
++resources_saveOp = Save cannot be called from inside an operation.
++resources_saveProblem = Problems occurred during save.
++resources_saveWarnings = Save operation warnings.
++resources_saving_0 = Saving workspace.
++resources_savingEncoding = Could not save encoding settings.
++resources_setDesc = Setting project description.
++resources_setLocal = Setting resource local flag.
++resources_settingCharset = Setting character set for resource ''{0}''.
++resources_settingDefaultCharsetContainer = Setting default character set for resource ''{0}''.
++resources_settingContents = Setting contents for ''{0}''.
++resources_shutdown = Workspace was not properly initialized or has already shutdown.
++resources_shutdownProblems = Problem on shutdown.
++resources_snapInit = Could not initialize snapshot file.
++resources_snapRead = Could not read snapshot file.
++resources_snapRequest = Snapshot requested.
++resources_snapshot = Periodic workspace save.
++resources_startupProblems = Workspace restored, but some problems occurred.
++resources_touch = Touching resource ''{0}''.
++resources_updating = Updating workspace
++resources_updatingEncoding = Problems encountered while updating encoding settings.
++resources_workspaceClosed = Workspace is closed.
++resources_workspaceOpen = The workspace is already open.
++resources_writeMeta = Could not write metadata for ''{0}''.
++resources_writeWorkspaceMeta = Could not write workspace metadata ''{0}''.
++
++synchronizer_partnerNotRegistered = Sync partner: {0} not registered with the synchronizer.
++
++### URL
++url_badVariant = Unsupported \"platform:\" protocol variation {0}.
++url_couldNotResolve = Project ''{0}'' does not exist. Could not resolve URL: {1}.
++
++### utils
++utils_clone = Clone not supported.
++utils_stringJobName = Compacting memory
++
++### watson
++watson_elementNotFound = Element not found: {0}.
++watson_illegalSubtree = Illegal subtree passed to createSubtree().
++watson_immutable = Attempt to modify an immutable tree.
++watson_noModify = Cannot modify implicit root node.
++watson_nullArg = Null argument to {0}.
++watson_unknown = Unknown format.
++
++### auto-refresh win32 native
++WM_beginTask = finding out of sync resources
++WM_jobName = Win32 refresh daemon
++WM_errors = Problems occurred refreshing resources
++WM_nativeErr = Problem occurred in auto-refresh native code: {0}.
++WM_errCloseHandle = Problem closing native refresh handle: {0}.
++WM_errCreateHandle = Problem creating handle for {0}, code: {0}.
++WM_errFindChange = Problem finding next change, code: {0}
+diff -r 3ac8c55882b5 -r 06d88bb6aac0 platform/org.eclipse.core.resources/src/org/eclipse/core/resources/DiagnosticLog.java
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/platform/org.eclipse.core.resources/src/org/eclipse/core/resources/DiagnosticLog.java Tue Jun 02 09:59:27 2009 -0500
+@@ -0,0 +1,95 @@
++/*
++* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
++* All rights reserved.
++* This component and the accompanying materials are made available
++* under the terms of the License "Eclipse Public License v1.0"
++* which accompanies this distribution, and is available
++* at the URL "http://www.eclipse.org/legal/epl-v10.html".
++*
++* Initial Contributors:
++* Nokia Corporation - initial contribution.
++*
++* Contributors:
++*
++* Description:
++*
++*/
++package org.eclipse.core.resources;
++
++import java.io.File;
++import java.io.IOException;
++import java.util.logging.FileHandler;
++import java.util.logging.Handler;
++import java.util.logging.Level;
++import java.util.logging.LogRecord;
++import java.util.logging.Logger;
++
++public class DiagnosticLog {
++ private String name;
++ private String id;
++ private Logger logger;
++ private File file;
++
++ private class LogHandler extends Handler {
++
++ FileHandler fileHandler;
++ File file;
++
++ public LogHandler(File file) {
++ this.file = file;
++ }
++
++ public void close() throws SecurityException {
++ if (fileHandler != null)
++ fileHandler.close();
++ }
++
++ public void flush() {
++ if (fileHandler != null)
++ fileHandler.flush();
++ }
++
++ public void publish(LogRecord record) {
++ if (fileHandler == null)
++ {
++ try {
++ fileHandler= new FileHandler(file.getAbsolutePath());
++ } catch (SecurityException e) {
++ e.printStackTrace();
++ } catch (IOException e) {
++ e.printStackTrace();
++ }
++ }
++ if (fileHandler != null)
++ fileHandler.publish(record);
++ }
++
++ }
++
++ public DiagnosticLog(String name, String id, String logFilePath) {
++ this.name = name;
++ this.id = id;
++ this.logger = Logger.getLogger(id);
++ logger.setLevel(Level.OFF);
++ logger.setUseParentHandlers(false);
++ this.file = new File(logFilePath);
++ logger.addHandler(new LogHandler(file));
++ }
++
++ public String getName() {
++ return name;
++ }
++
++ public String getId() {
++ return id;
++ }
++
++ public File getFile() {
++ return file;
++ }
++
++ public Logger getLogger() {
++ return logger;
++ }
++
++}
+\ No newline at end of file
+diff -r 3ac8c55882b5 -r 06d88bb6aac0 platform/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
+--- a/platform/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java Mon Jun 01 19:29:06 2009 -0500
++++ b/platform/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java Tue Jun 02 09:59:27 2009 -0500
+@@ -18,6 +18,8 @@
+ import org.osgi.framework.BundleActivator;
+ import org.osgi.framework.BundleContext;
+
++import java.util.logging.Level;
++
+ /**
+ * The plug-in runtime class for the Resources plug-in. This is
+ * the starting point for all workspace and resource manipulation.
+@@ -29,6 +31,7 @@
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+ public final class ResourcesPlugin extends Plugin {
++
+ /**
+ * Unique identifier constant (value <code>"org.eclipse.core.resources"</code>)
+ * for the standard Resources plug-in.
+@@ -267,6 +270,10 @@
+ */
+ private static Workspace workspace = null;
+
++
++ private static final String REFRESH_LOG_ID = ".refresh.log";
++ private DiagnosticLog refreshLog;
++
+ /**
+ * Constructs an instance of this plug-in runtime class.
+ * <p>
+@@ -377,4 +384,18 @@
+ if (!result.isOK())
+ getLog().log(result);
+ }
++
++ public static void writeRefreshLog(String s) {
++ if (!plugin.getPluginPreferences().getBoolean(PI_RESOURCES + REFRESH_LOG_ID))
++ return;
++
++ if (plugin.refreshLog == null) {
++ IPath path = getWorkspace().getRoot().getLocation().append("refresh.log");
++ plugin.refreshLog = new DiagnosticLog("refresh.log", PI_RESOURCES + REFRESH_LOG_ID, path.toOSString());
++ plugin.refreshLog.getLogger().setLevel(Level.INFO);
++ }
++
++ plugin.refreshLog.getLogger().info(s);
++ System.out.println(s);
++ }
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/.api_description Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component name="org.eclipse.core.resources_3.5.0.v20090512" version="1.2">
+ <plugin id="org.eclipse.core.resources_3.5.0.v20090512"/>
+ <package name="org.eclipse.core.resources" visibility="1">
+ <type name="ICommand" restrictions="3"/>
+ <type name="IContainer" restrictions="3"/>
+ <type name="IFile" restrictions="3"/>
+ <type name="IFileState" restrictions="3"/>
+ <type name="IFolder" restrictions="3"/>
+ <type name="IMarker" restrictions="3"/>
+ <type name="IMarkerDelta" restrictions="3"/>
+ <type name="IPathVariableChangeEvent" restrictions="3"/>
+ <type name="IPathVariableManager" restrictions="3"/>
+ <type name="IProject" restrictions="3"/>
+ <type name="IProjectDescription" restrictions="3"/>
+ <type name="IProjectNatureDescriptor" restrictions="3"/>
+ <type name="IResource" restrictions="3"/>
+ <type name="IResourceChangeEvent" restrictions="3"/>
+ <type name="IResourceDelta" restrictions="3"/>
+ <type name="IResourceProxy" restrictions="3"/>
+ <type name="IResourceRuleFactory" restrictions="3"/>
+ <type name="IResourceStatus" restrictions="3"/>
+ <type name="ISaveContext" restrictions="3"/>
+ <type name="ISavedState" restrictions="3"/>
+ <type name="ISynchronizer" restrictions="3"/>
+ <type name="IWorkspace" restrictions="3"/>
+ <type name="IWorkspaceDescription" restrictions="3"/>
+ <type name="IWorkspaceRoot" restrictions="3"/>
+ <type name="ResourceAttributes" restrictions="2"/>
+ <type name="ResourcesPlugin" restrictions="4"/>
+ </package>
+ <package name="org.eclipse.core.resources.mapping" visibility="1">
+ <type name="IModelProviderDescriptor" restrictions="3"/>
+ <type name="IResourceChangeDescriptionFactory" restrictions="3"/>
+ </package>
+ <package name="org.eclipse.core.resources.refresh" visibility="1">
+ <type name="IRefreshResult" restrictions="3"/>
+ </package>
+ <package name="org.eclipse.core.resources.team" visibility="1">
+ <type name="IResourceTree" restrictions="1"/>
+ </package>
+</component>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/.classpath Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src/"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/.options Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,74 @@
+# Debugging options for the org.eclipse.core.resources plugin.
+
+# Turn on debugging for the org.eclipse.core.resources plugin.
+org.eclipse.core.resources/debug=false
+
+# Monitor builders and gather time statistics etc.
+org.eclipse.core.resources/perf/builders=10000
+
+# Monitor resource change listeners and gather time statistics etc.
+org.eclipse.core.resources/perf/listeners=500
+
+# Monitor workspace snapshot and gather time statistics etc.
+org.eclipse.core.resources/perf/snapshot=1000
+
+# Monitor workspace snapshot and gather time statistics etc.
+org.eclipse.core.resources/perf/save.participants=500
+
+# Debug build failure cases such as failure to retrieve deltas.
+org.eclipse.core.resources/build/failure=false
+
+# Reports the cause of autobuild interruption
+org.eclipse.core.resources/build/interrupt=false
+
+# Reports the start and end of all builder invocations
+org.eclipse.core.resources/build/invoking=false
+
+# Reports the start and end of build delta calculations
+org.eclipse.core.resources/build/delta=false
+
+# For incremental builds, displays which builder is being run
+# and because of changes in which project.
+org.eclipse.core.resources/build/needbuild=false
+
+# Prints a stack trace every time an operation finishes that requires a build
+org.eclipse.core.resources/build/needbuildstack=false
+
+# Prints a stack trace every time a build API method is called
+org.eclipse.core.resources/build/stacktrace=false
+
+# Reports configuration and deconfiguration of project natures
+org.eclipse.core.resources/natures=false
+
+# Report history store debugging.
+org.eclipse.core.resources/history=false
+
+# Report debugging for project preferences.
+org.eclipse.core.resources/preferences=false
+
+# Prints debug information (elapsed time, estimated savings) from string compaction
+org.eclipse.core.resources/strings=false
+
+# Report timings for restoring the workspace state
+org.eclipse.core.resources/restore=false
+org.eclipse.core.resources/restore/markers=false
+org.eclipse.core.resources/restore/syncinfo=false
+org.eclipse.core.resources/restore/tree=false
+org.eclipse.core.resources/restore/metainfo=false
+org.eclipse.core.resources/restore/snapshots=false
+org.eclipse.core.resources/restore/mastertable=false
+
+# Report timings for saving and snapshots on the workspace state
+org.eclipse.core.resources/save=false
+org.eclipse.core.resources/save/markers=false
+org.eclipse.core.resources/save/syncinfo=false
+org.eclipse.core.resources/save/tree=false
+org.eclipse.core.resources/save/metainfo=false
+org.eclipse.core.resources/save/mastertable=false
+
+# Report debug of workspace auto-refresh
+org.eclipse.core.resources/refresh=false
+
+# Prints debug information on resource content description
+org.eclipse.core.resources/contenttype=false
+org.eclipse.core.resources/contenttype/cache=false
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/.project Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.core.resources</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/.settings/org.eclipse.jdt.core.prefs Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,7 @@
+#Thu Jul 30 11:46:43 CDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/META-INF/MANIFEST.MF Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1140 @@
+Manifest-Version: 1.0
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.core.resources; singleton:=true
+Bundle-Activator: org.eclipse.core.resources.ResourcesPlugin
+Require-Bundle: org.eclipse.ant.core;bundle-version="[3.1.0,4.0.0)";re
+ solution:=optional,org.eclipse.core.expressions;bundle-version="[3.2.
+ 0,4.0.0)",org.eclipse.core.filesystem;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.2.0,4.0.0)"
+Bundle-Version: 3.5.0.v20090512
+Export-Package: org.eclipse.core.internal.dtree;x-internal:=true,org.e
+ clipse.core.internal.events;x-internal:=true,org.eclipse.core.interna
+ l.localstore;x-internal:=true,org.eclipse.core.internal.properties;x-
+ internal:=true,org.eclipse.core.internal.propertytester;x-internal:=t
+ rue,org.eclipse.core.internal.refresh;x-internal:=true,org.eclipse.co
+ re.internal.resources;x-internal:=true,org.eclipse.core.internal.reso
+ urces.mapping;x-internal:=true,org.eclipse.core.internal.resources.re
+ fresh.win32;x-internal:=true,org.eclipse.core.internal.utils;x-intern
+ al:=true,org.eclipse.core.internal.watson;x-internal:=true,org.eclips
+ e.core.resources,org.eclipse.core.resources.mapping,org.eclipse.core.
+ resources.refresh,org.eclipse.core.resources.team
+Bundle-ManifestVersion: 2
+
+Name: org/eclipse/core/internal/propertytester/FilePropertyTester.clas
+ s
+SHA1-Digest: 6dXDF8Sz2YIcxNbfuuXkuSpJZmE=
+
+Name: org/eclipse/core/internal/resources/WorkspaceRoot.class
+SHA1-Digest: cEho/y/fblIYWvtLnbN5MTZCOek=
+
+Name: org/eclipse/core/resources/mapping/ResourceChangeValidator$1.cla
+ ss
+SHA1-Digest: 5gCmjhvKpcuQP53iCz6bLITEfro=
+
+Name: org/eclipse/core/internal/dtree/DeletedNode.class
+SHA1-Digest: NNeOdR3gNkQYfP9xMyxlwFu8Nbk=
+
+Name: org/eclipse/core/internal/localstore/HistoryBucket.class
+SHA1-Digest: QqjDat4SKvi7VEaOKHyqGmMqqxI=
+
+Name: org/eclipse/core/internal/watson/ElementTreeReaderImpl_1.class
+SHA1-Digest: iV+3FnVY1oO3S5b9sNaAHP5wuSg=
+
+Name: org/eclipse/core/internal/resources/AliasManager$Doit.class
+SHA1-Digest: wL62eOzUGqGgP9amX45FrNFqKHA=
+
+Name: org/eclipse/core/internal/utils/Messages.class
+SHA1-Digest: TCweYOPTj5jdepKqcS4XkT98L1I=
+
+Name: org/eclipse/core/internal/events/BuilderPersistentInfo.class
+SHA1-Digest: Wj6nsmjIgA2cvFCa5OoJdi0CwC4=
+
+Name: org/eclipse/core/internal/resources/CharsetManager.class
+SHA1-Digest: OIgjOVHPQP4TgQyRATrIw8qjB18=
+
+Name: org/eclipse/core/resources/mapping/CompositeResourceMapping.clas
+ s
+SHA1-Digest: E0AIuTDpiHj+2h8ncaEuM5Di6y0=
+
+Name: org/eclipse/core/resources/ProjectScope.class
+SHA1-Digest: M4KoL63nVqOAZRTvB9nymraRu5s=
+
+Name: org/eclipse/core/internal/resources/LinkDescription.class
+SHA1-Digest: T8FFwneQOQftRZjzR5SncEDjHpk=
+
+Name: org/eclipse/core/internal/resources/Workspace$2.class
+SHA1-Digest: 9AReou6/RiKwvdnROKqwC15aQQc=
+
+Name: org/eclipse/core/internal/refresh/InternalRefreshProvider.class
+SHA1-Digest: fn6oR71Cgg4affNejKX6UTRDfeg=
+
+Name: org/eclipse/core/resources/mapping/IModelProviderDescriptor.clas
+ s
+SHA1-Digest: n2SxqxUPsAykd5EOJipJRQw8hME=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Monitor$C
+ hainedHandle.class
+SHA1-Digest: XVKoWDhsR/lwwReUGt4Uu2UFihQ=
+
+Name: org/eclipse/core/internal/resources/mapping/ResourceChangeDescri
+ ptionFactory.class
+SHA1-Digest: iop+k//CQslYpfX/FWgPu1yEeo0=
+
+Name: org/eclipse/core/internal/localstore/IsSynchronizedVisitor.class
+SHA1-Digest: Qr+SZ6zbr11Oz0CUUfIADaYsRjA=
+
+Name: org/eclipse/core/internal/dtree/AbstractDataTreeNode.class
+SHA1-Digest: VS+vg+VuV6Ir4THY6yWdHLGP5nM=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2$2.class
+SHA1-Digest: gx6f6a+reVxsXcPRRnei2Bml90U=
+
+Name: org/eclipse/core/internal/resources/MarkerReader_2.class
+SHA1-Digest: 7lIbU4Xtjd7bzH3jDu8ChLDl/44=
+
+Name: org/eclipse/core/resources/ISaveContext.class
+SHA1-Digest: Xqd8mcuyq4/9t1ZC3LGi7De8uWE=
+
+Name: org/eclipse/core/internal/localstore/IHistoryStore.class
+SHA1-Digest: dxFS1atlhRdNHLFm7alYTKZyyCI=
+
+Name: org/eclipse/core/internal/properties/PropertyBucket$1.class
+SHA1-Digest: ayJX6JCK1zSle2g9zCbEKYs54VU=
+
+Name: org/eclipse/core/resources/ISaveParticipant.class
+SHA1-Digest: YIsF9/CqJ3fwIhmws+3t8ZQPTf0=
+
+Name: org/eclipse/core/internal/utils/Cache$Entry.class
+SHA1-Digest: id82eVhDRpnqayNYqbdAhZD7pts=
+
+Name: org/eclipse/core/resources/IFile.class
+SHA1-Digest: 4FYI57XqVDr5czxCj/wPlNyXD1g=
+
+Name: org/eclipse/core/internal/resources/SaveManager$4.class
+SHA1-Digest: Sw6BwtfaX05jIoWUO1n2F5Auqjg=
+
+Name: org/eclipse/core/internal/localstore/IsSynchronizedVisitor$Resou
+ rceChangedException.class
+SHA1-Digest: iJ548VCsiF//few8+K/6P87QSWg=
+
+Name: org/eclipse/core/internal/events/NotificationManager.class
+SHA1-Digest: suYT2s/FZbByVjLkuV7d/bf5KAk=
+
+Name: org/eclipse/core/internal/resources/CharsetDeltaJob$ICharsetList
+ enerFilter.class
+SHA1-Digest: DuP5hiX0kwjVQX4SlnumAZVG8Cg=
+
+Name: org/eclipse/core/internal/utils/Policy.class
+SHA1-Digest: MN208OaEqM2yL1c83nxV9Qi+b90=
+
+Name: org/eclipse/core/resources/ICommand.class
+SHA1-Digest: NH3zyShc30pLXD4gGpl9MFrmMDY=
+
+Name: org/eclipse/core/internal/resources/ModelObject.class
+SHA1-Digest: 5crz/BR7BW1jTLgV8T1+WY6TB6A=
+
+Name: org/eclipse/core/internal/refresh/RefreshJob.class
+SHA1-Digest: R6s2qjqRqVhQJ0lEH+e358paadM=
+
+Name: org/eclipse/core/resources/mapping/ResourceChangeValidator.class
+SHA1-Digest: 4UiQjDYtZ07i1WJmR3gYTef9mFw=
+
+Name: org/eclipse/core/internal/resources/mapping/ResourceModelProvide
+ r.class
+SHA1-Digest: zk+IvAAXTQTh4Umm/7p0GlJta+c=
+
+Name: org/eclipse/core/internal/localstore/UnifiedTree.class
+SHA1-Digest: 3GCH+1R5wNbum4dn/12HnzLWk5s=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Monitor$L
+ inkedResourceHandle.class
+SHA1-Digest: /YUSkK4mZ70hdjT71xeu7JDdDMU=
+
+Name: org/eclipse/core/internal/dtree/NodeComparison.class
+SHA1-Digest: 5M5EEYsaCB4ivajaDbvaBQ6l+iY=
+
+Name: org/eclipse/core/resources/IResource.class
+SHA1-Digest: Okcw8/D+z5jKLfXLGQtP2qH/9Ag=
+
+Name: org/eclipse/core/internal/localstore/ILocalStoreConstants.class
+SHA1-Digest: RCZliuUmC/X+d9uki6qEM2hk3LY=
+
+Name: org/eclipse/core/internal/resources/ProjectPreferences$SortedPro
+ perties$IteratorWrapper.class
+SHA1-Digest: ZM6czNGzocX728BzN7StSr0bX+M=
+
+Name: org/eclipse/core/internal/utils/KeyedHashSet$KeyedElement.class
+SHA1-Digest: 8+j1+Cen+qBAvlEWAPVNwM1Jno4=
+
+Name: org/eclipse/core/internal/events/BuildManager$MissingBuilder.cla
+ ss
+SHA1-Digest: tnhcpaKyNlOAgjlQ/O7bXo7+HYw=
+
+Name: org/eclipse/core/internal/watson/IElementContentVisitor.class
+SHA1-Digest: xoM10jikp+wQ969PQtICNTE2iII=
+
+Name: org/eclipse/core/internal/events/ResourceChangeEvent.class
+SHA1-Digest: EIdg+LBUuxseiLmb0tHyBmtzCUY=
+
+Name: org/eclipse/core/resources/mapping/RemoteResourceMappingContext.
+ class
+SHA1-Digest: NvrlpyJj9/0yiYJGEBik+aP0euw=
+
+Name: org/eclipse/core/internal/events/AutoBuildJob.class
+SHA1-Digest: Zn3kPPWBQfcVpJmO5mP3TlLY4qc=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2.class
+SHA1-Digest: 7vVYU2AztHODpHdg9CMqM8/DmfA=
+
+Name: org/eclipse/core/internal/resources/CharsetDeltaJob$3.class
+SHA1-Digest: d7IG+PIdpuVDQ8G8fjVWxMMfjWA=
+
+Name: org/eclipse/core/internal/resources/MarkerManager$3.class
+SHA1-Digest: xwJpkpNwwDSkVZNwHrCc6CQlhLU=
+
+Name: org/eclipse/core/internal/localstore/HistoryBucket$1.class
+SHA1-Digest: IwxMuSsww9Uety98Zx9PaMdn/w8=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Natives.c
+ lass
+SHA1-Digest: gaMEwKvvoUKImOpqmBreRvkI/eE=
+
+Name: org/eclipse/core/internal/resources/AliasManager$AddToCollection
+ Doit.class
+SHA1-Digest: SODEH+3Jc0YTOa5Ms4NOBWfjnb4=
+
+Name: org/eclipse/core/internal/resources/SavedState.class
+SHA1-Digest: Q2yE5q85IkT5/hH2sUJAYchjuXg=
+
+Name: org/eclipse/core/internal/dtree/DataTreeNode.class
+SHA1-Digest: 6rQ0AIs6rIUMoWlQm6DycUcbDsk=
+
+Name: org/eclipse/core/internal/properties/PropertyManager2$1.class
+SHA1-Digest: ptVDgreTDLxsSEp9cRlwhJzOfTU=
+
+Name: org/eclipse/core/internal/dtree/DataDeltaNode.class
+SHA1-Digest: 56C5CVCOtt+UZg5UHnqQ00KZqCU=
+
+Name: org/eclipse/core/internal/resources/ProjectNatureDescriptor.clas
+ s
+SHA1-Digest: axcDTHm52eiCDmWXH1rdYSnEOBQ=
+
+Name: org/eclipse/core/resources/mapping/ModelProvider.class
+SHA1-Digest: rcEVQ0q2QODN/dTn71Fn29DHCI4=
+
+Name: org/eclipse/core/internal/resources/ComputeProjectOrder$Digraph.
+ class
+SHA1-Digest: 7NkkQdSwSSscbYCiGR1s6npPDbY=
+
+Name: org/eclipse/core/resources/IFileState.class
+SHA1-Digest: orlCCyGaRlkOY7ylGRQeBFHqk28=
+
+Name: org/eclipse/core/internal/resources/SaveContext.class
+SHA1-Digest: t9udNjUSrpmgjl2oaVcBHSo7ImI=
+
+Name: org/eclipse/core/internal/localstore/CopyVisitor.class
+SHA1-Digest: 0SwqRulbQ+HAhx/cmfyh3LNnA3Y=
+
+Name: org/eclipse/core/internal/resources/ComputeProjectOrder$Digraph$
+ Vertex.class
+SHA1-Digest: g5my8PvMh1u5TDL7j1V6VIFvEMs=
+
+Name: org/eclipse/core/internal/localstore/RefreshLocalVisitor.class
+SHA1-Digest: yVeaheyWO0XDEvo2WPl/SSCBt10=
+
+Name: org/eclipse/core/internal/resources/Resource$1.class
+SHA1-Digest: JaE6a3n3HeVd69go1TIGtmRn+Sw=
+
+Name: org/eclipse/core/internal/resources/TestingSupport.class
+SHA1-Digest: 1JtRAW9MwPBYXTycWoE8/CC/974=
+
+Name: org/eclipse/core/internal/resources/SaveManager$1.class
+SHA1-Digest: w4yxYpUk7hNpMtc8yGkiZwc2MYc=
+
+Name: org/eclipse/core/internal/events/NodeIDMap.class
+SHA1-Digest: SBvSwrFMykb/1GdLHL8WSIf3RnU=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Monitor$F
+ ileHandle.class
+SHA1-Digest: U89ZSY0oLzLh+OKS52HW1itwSrE=
+
+Name: org/eclipse/core/internal/resources/Container.class
+SHA1-Digest: 2uBODu4989IcNt2OUTUK5r6D40k=
+
+Name: org/eclipse/core/internal/resources/MarkerSet.class
+SHA1-Digest: 18o1PsEGJ4QYoCSql6iLa/Bzdfs=
+
+Name: org/eclipse/core/internal/utils/StringPoolJob$1.class
+SHA1-Digest: umt8oSmFY5RME432o7qN2pQHDkc=
+
+Name: org/eclipse/core/internal/watson/IElementTreeData.class
+SHA1-Digest: wK7Megdgrt1haUI4JpR6nV5EJKY=
+
+Name: org/eclipse/core/internal/events/BuildManager$1.class
+SHA1-Digest: kjRWpMsCfUPHy8rn2VZM6tZyHUI=
+
+Name: org/eclipse/core/resources/WorkspaceJob.class
+SHA1-Digest: MdpwkDq6sMHng7GGSZGscRePuIk=
+
+Name: org/eclipse/core/resources/IResourceProxyVisitor.class
+SHA1-Digest: JWNkS4crKJNaXlLgDnw3SFtX6Q0=
+
+Name: org/eclipse/core/resources/IWorkspace.class
+SHA1-Digest: NxFOlRuhzxySTSXsrSCiV1xVSOk=
+
+Name: org/eclipse/core/resources/IWorkspaceRoot.class
+SHA1-Digest: NvwOa5JaxpJIPaVMCb9IH0cDv28=
+
+Name: org/eclipse/core/internal/watson/IPathRequestor.class
+SHA1-Digest: 6QWITtSsII9Tyg7p1cLjjRoNIys=
+
+Name: org/eclipse/core/resources/mapping/ResourceMapping.class
+SHA1-Digest: oHyfhQsoQtI+zwM1U5o9i/uue3w=
+
+Name: org/eclipse/core/internal/events/InternalBuilder.class
+SHA1-Digest: pw3UVhKWqcRl/Uje2dElQGt5t7A=
+
+Name: org/eclipse/core/internal/resources/IMarkerSetElement.class
+SHA1-Digest: GEsYJZ0tcYVktIxVFRblg+t9Ap8=
+
+Name: org/eclipse/core/internal/resources/MarkerSnapshotReader.class
+SHA1-Digest: 50NmawnqySzdoyQ6gX6IrgT8z/Y=
+
+Name: org/eclipse/core/internal/resources/ProjectDescriptionReader.cla
+ ss
+SHA1-Digest: th/TF5vHGScrUgZ8YZ9vorpBQg8=
+
+Name: org/eclipse/core/internal/resources/Workspace$4.class
+SHA1-Digest: AHWE1k4cSV9GojhdssU1CDltl9I=
+
+Name: org/eclipse/core/internal/propertytester/ProjectPropertyTester.c
+ lass
+SHA1-Digest: DP+0G0W/2Q/U3pC4wFCKAGe975E=
+
+Name: org/eclipse/core/internal/propertytester/ResourcePropertyTester.
+ class
+SHA1-Digest: CShjPFlriVqnmbTTM0fKYfBXldc=
+
+Name: org/eclipse/core/internal/resources/ProjectInfo.class
+SHA1-Digest: XnEBTrchOyxdXIXz1sggSXyyTrY=
+
+Name: org/eclipse/core/resources/IWorkspaceDescription.class
+SHA1-Digest: uoNsuXJoGJ12rr4LHZFj6/cyAXE=
+
+Name: org/eclipse/core/internal/utils/ObjectMap.class
+SHA1-Digest: lTNc4vLtbV0D11mSttUUAZTIbdk=
+
+Name: org/eclipse/core/internal/resources/XMLWriter.class
+SHA1-Digest: 08k3vw05+tt9cpYwpjcPtCrJiQs=
+
+Name: org/eclipse/core/resources/ResourceAttributes.class
+SHA1-Digest: tfk3YnykqJQeogdLnvkTsi4nhDQ=
+
+Name: org/eclipse/core/resources/IProjectNatureDescriptor.class
+SHA1-Digest: 8BjTFVlv/zYvY99+V1EGvRrxsYo=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2$4.class
+SHA1-Digest: hKue10KHINp1vkl0+Awd2+I4hDg=
+
+Name: org/eclipse/core/internal/events/LifecycleEvent.class
+SHA1-Digest: PcN5DJVmPoZjsgUNRNJlqJHP+iA=
+
+Name: org/eclipse/core/internal/localstore/RefreshLocalAliasVisitor.cl
+ ass
+SHA1-Digest: hWIy7+abV60ioeEAKpZvVN+Zp3w=
+
+Name: META-INF/eclipse.inf
+SHA1-Digest: KyT9FF7C7t86NoBoa2kZT3ZJBfw=
+
+Name: org/eclipse/core/internal/resources/PathVariableManager$1.class
+SHA1-Digest: XxHzKbXxqcurwas4nbn8CKwsf1s=
+
+Name: org/eclipse/core/internal/resources/ResourceInfo.class
+SHA1-Digest: vrnL8ZSsROIQ6ANwcfKuE+ra8ag=
+
+Name: org/eclipse/core/internal/localstore/SafeFileInputStream.class
+SHA1-Digest: gBuWb2ruTUwSX8RvMqHfh0AzU64=
+
+Name: org/eclipse/core/internal/properties/PropertyManager2$PropertyCo
+ pyVisitor.class
+SHA1-Digest: qT7kC437bFQvDU7FGxd4T0BwIbs=
+
+Name: org/eclipse/core/internal/dtree/TestHelper.class
+SHA1-Digest: PNKHnGR7YDjHLyStpbDnhfFE+GU=
+
+Name: .options
+SHA1-Digest: nRvDqkPZnh/ZOcqL7XPH/bWbKvY=
+
+Name: org/eclipse/core/resources/IPathVariableChangeEvent.class
+SHA1-Digest: MsHYQjJHnVgqpJve15DtP9RL+NY=
+
+Name: org/eclipse/core/internal/resources/NatureManager.class
+SHA1-Digest: vBM+umghDaKo6Nqh0j9PhD8UESk=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2$HistoryCopyVi
+ sitor.class
+SHA1-Digest: 0NTu2Mrk0muALhV1jtMDf1CuC2Y=
+
+Name: org/eclipse/core/internal/watson/ElementTree$ChildIDsCache.class
+SHA1-Digest: h1VxDdmCoiGVs0eiHTKlOBK3d+A=
+
+Name: org/eclipse/core/internal/dtree/DataTree.class
+SHA1-Digest: pd+AjEV4dUFhsaLkNzBfPJNrCas=
+
+Name: org/eclipse/core/resources/team/TeamHook.class
+SHA1-Digest: sRQmaAq4YPiAdYOy9TlfoehHL5c=
+
+Name: org/eclipse/core/internal/resources/ProjectPreferences$1.class
+SHA1-Digest: W5D67mBCDzpCPonDV9xmYnxvA7I=
+
+Name: org/eclipse/core/resources/refresh/IRefreshMonitor.class
+SHA1-Digest: wXbbyLtEiyDAnpsEIfrBWLByxpU=
+
+Name: org/eclipse/core/internal/resources/mapping/ShallowContainer.cla
+ ss
+SHA1-Digest: PSIijzOF+wQXC/SMh78RAfk/Wrw=
+
+Name: org/eclipse/core/internal/localstore/SafeChunkyInputStream.class
+SHA1-Digest: vedG2A+tAwT/MLOhuK6SxYEE548=
+
+Name: org/eclipse/core/internal/resources/mapping/SimpleResourceMappin
+ g.class
+SHA1-Digest: JpsskryecEM1K0XRapyRcoAsJwc=
+
+Name: org/eclipse/core/internal/events/NotificationManager$1.class
+SHA1-Digest: 5DW2n4GPfWkpoptTTLDHDqo64r0=
+
+Name: org/eclipse/core/internal/resources/Container$1.class
+SHA1-Digest: m2cXFEOeZ6OAPMZTVlQhiAwGfKs=
+
+Name: org/eclipse/core/internal/utils/UniversalUniqueIdentifier.class
+SHA1-Digest: 4GlnDZRq552Tvg4f2n+148NvIiY=
+
+Name: org/eclipse/core/internal/resources/MarkerSnapshotReader_1.class
+SHA1-Digest: O9m3YsQSPVuy/ArwLRN8m5JRYxM=
+
+Name: org/eclipse/core/resources/WorkspaceLock.class
+SHA1-Digest: JUNdVQkjYgZjYeu91Ve4jqpboLo=
+
+Name: org/eclipse/core/internal/resources/SyncInfoWriter.class
+SHA1-Digest: oAAyma2ddKizlqPLMAIlKWTY9Cw=
+
+Name: org/eclipse/core/internal/localstore/Bucket$Entry.class
+SHA1-Digest: U2S9IEsg42hGdsMQqoIUTZhEyqw=
+
+Name: org/eclipse/core/internal/resources/WorkManager.class
+SHA1-Digest: tW3arEcp+NYEhNhL+CY2xfiaUjM=
+
+Name: org/eclipse/core/internal/events/BuildCommand.class
+SHA1-Digest: TEWAQGfaiSycN8KicxGX8JUJJvs=
+
+Name: org/eclipse/core/resources/IResourceStatus.class
+SHA1-Digest: f7gWqzlpr5GhGtF23mSS4cEqsXo=
+
+Name: org/eclipse/core/internal/resources/ResourceException.class
+SHA1-Digest: xf9zaaxaX5Kai3Tl7rfBfV2YRh4=
+
+Name: org/eclipse/core/internal/watson/DefaultElementComparator.class
+SHA1-Digest: +MY3t9kl+8ou3OJbjt8e1EifR+0=
+
+Name: org/eclipse/core/internal/resources/Workspace$1.class
+SHA1-Digest: xjrFUUEJm4OIHxfKBH7k8P5NQhI=
+
+Name: org/eclipse/core/internal/localstore/UnifiedTree$PatternHolder.c
+ lass
+SHA1-Digest: CDVNuc/PekV4QzbfcQwT851TEu8=
+
+Name: plugin.properties
+SHA1-Digest: vGrafu+W4Oko67jhpCg6YJrr688=
+
+Name: org/eclipse/core/internal/utils/BitMask.class
+SHA1-Digest: 6An8k8ll0B58ebTirjJKOWYsdSg=
+
+Name: org/eclipse/core/internal/properties/PropertyBucket.class
+SHA1-Digest: 1h8dZOWhfhr31z00oQKICgjJu6c=
+
+Name: org/eclipse/core/internal/resources/SaveManager$InternalMonitorW
+ rapper.class
+SHA1-Digest: Se096YctRE9MiiozQu8ck3fa+6k=
+
+Name: org/eclipse/core/internal/resources/PlatformURLResourceConnectio
+ n.class
+SHA1-Digest: 1ruCjV6AeTNtLL6iogBsd0jsA/w=
+
+Name: org/eclipse/core/internal/events/ResourceChangeListenerList$List
+ enerEntry.class
+SHA1-Digest: vfsRH17xcdvU96BguzdHjEmjyTY=
+
+Name: org/eclipse/core/internal/resources/mapping/ShallowResourceMappi
+ ng.class
+SHA1-Digest: 3RPf08A5PjsquD79tzrjqpGWQUg=
+
+Name: org/eclipse/core/internal/resources/Resource.class
+SHA1-Digest: epM0WTF4bDrrV0L5wxK6m89cEYM=
+
+Name: org/eclipse/core/internal/propertytester/ResourceMappingProperty
+ Tester.class
+SHA1-Digest: aamOb5HDPy5zWA2TBUCfhiP6lqc=
+
+Name: org/eclipse/core/internal/localstore/UnifiedTreeNode.class
+SHA1-Digest: 8wHOphKB4pUablAPTEPho5yphDU=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2$1.class
+SHA1-Digest: vt/V185JBptsK/+xxVVYmXdcPn8=
+
+Name: org/eclipse/core/internal/resources/MarkerReader_1.class
+SHA1-Digest: nebD3FIZRV4wlAgOMYYm3+4Bmq4=
+
+Name: org/eclipse/core/resources/refresh/RefreshProvider.class
+SHA1-Digest: D2Ab0y4cuwaGiYu1nwYpRvD8OZY=
+
+Name: org/eclipse/core/resources/ResourcesPlugin.class
+SHA1-Digest: Q9OTPdclRdTo5xWgbX0dvfk4gEc=
+
+Name: org/eclipse/core/internal/utils/FileUtil.class
+SHA1-Digest: E9RufuAM6ugivR5Fn+33HoCUwZ4=
+
+Name: org/eclipse/core/internal/events/NotificationManager$NotifyJob.c
+ lass
+SHA1-Digest: Yt+nnO3aBthVULedLVqHeRftnSM=
+
+Name: plugin.xml
+SHA1-Digest: CMblqdpev0seqKkniZBZoskVMGo=
+
+Name: org/eclipse/core/internal/resources/AliasManager$FindAliasesDoit
+ .class
+SHA1-Digest: KjjSrtaac3zyDruoviS2IDxtzuI=
+
+Name: org/eclipse/core/internal/utils/ArrayIterator.class
+SHA1-Digest: n0BpjenUX35dVm4K/tWoHzR51Uc=
+
+Name: org/eclipse/core/resources/mapping/ResourceMappingContext.class
+SHA1-Digest: qtda+h+WV/Ydtnpm8cfGWVH4N9s=
+
+Name: org/eclipse/core/internal/resources/Rules.class
+SHA1-Digest: ezP3iSR/OWMvw8RMIs2YaRJZgiA=
+
+Name: org/eclipse/core/internal/utils/StringPoolJob.class
+SHA1-Digest: F9fnj/tGXnKrgDES+4T0qt3ey/A=
+
+Name: org/eclipse/core/internal/resources/MoveDeleteHook.class
+SHA1-Digest: 75aP5EjpAJdMi/Ohofon9LE2XE4=
+
+Name: org/eclipse/core/internal/localstore/DeleteVisitor.class
+SHA1-Digest: +r3aIP03el4dpx53xglJAy6q6/Y=
+
+Name: org/eclipse/core/internal/resources/SaveManager$3.class
+SHA1-Digest: ANJvo5fDQxGPNz3VonXXHWKtLDo=
+
+Name: org/eclipse/core/internal/localstore/IUnifiedTreeVisitor.class
+SHA1-Digest: 7r0BIoWpHKz7336+7VzOziH84zY=
+
+Name: org/eclipse/core/internal/localstore/FileStoreRoot.class
+SHA1-Digest: plDVQMbFzSxbAe7eI47att3Ovtw=
+
+Name: org/eclipse/core/internal/resources/PreferenceInitializer.class
+SHA1-Digest: ZQXmIThgR+oYc0WgjT//tGlKR4s=
+
+Name: org/eclipse/core/internal/utils/WrappedRuntimeException.class
+SHA1-Digest: YrJyBobWxoM/OxEOjLyHQ2RE+08=
+
+Name: org/eclipse/core/internal/resources/mapping/ModelProviderDescrip
+ tor.class
+SHA1-Digest: RWpIVIlEn9th0Emc2ImVgkd7nwQ=
+
+Name: org/eclipse/core/resources/ISavedState.class
+SHA1-Digest: /2VJ4imJqHrrpw48sRvspCQJ9UY=
+
+Name: org/eclipse/core/internal/resources/Folder.class
+SHA1-Digest: O6HpuDs89c+RsV1dKhW2W/2vZJM=
+
+Name: org/eclipse/core/resources/IEncodedStorage.class
+SHA1-Digest: JBU7J8/8MiPYHQcbaeJcTo6TmLg=
+
+Name: org/eclipse/core/resources/IWorkspaceRunnable.class
+SHA1-Digest: SCPEoA4kWWJoSC8/GPZkN+3MxvE=
+
+Name: org/eclipse/core/internal/utils/IStringPoolParticipant.class
+SHA1-Digest: piZxbubvEN+egXYvpf8021RIldM=
+
+Name: org/eclipse/core/resources/team/FileModificationValidationContex
+ t.class
+SHA1-Digest: PSE/ZxsjERIQc4JPPB3YiGHcQmg=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Monitor$R
+ esourceHandle.class
+SHA1-Digest: vLBYa1FaapM7HtXX0tzgWJy3YA0=
+
+Name: org/eclipse/core/internal/resources/NatureManager$2.class
+SHA1-Digest: fmFjMzjHNSg/3nS2J0cIn9UtGPc=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32RefreshPr
+ ovider.class
+SHA1-Digest: lkQnk+gMRO9Bi6HMq/JKLevkJZI=
+
+Name: org/eclipse/core/internal/resources/CharsetManager$Listener.clas
+ s
+SHA1-Digest: jZ0seoJg/9uCch9GBdw9eb4C5iQ=
+
+Name: org/eclipse/core/resources/IResourceChangeEvent.class
+SHA1-Digest: cbyh99mtl94S5kQ9rU+GDzIR6RA=
+
+Name: org/eclipse/core/internal/propertytester/StringMatcher.class
+SHA1-Digest: NokfWue2zdSuM1YTTLM1O48sAXg=
+
+Name: org/eclipse/core/internal/refresh/MonitorManager.class
+SHA1-Digest: daJusoXE7GeMHwzi8ackc/UcI0k=
+
+Name: org/eclipse/core/resources/team/IMoveDeleteHook.class
+SHA1-Digest: NsNCBCg5oLp7LnpgbTXRVqdPe18=
+
+Name: org/eclipse/core/internal/resources/LocationValidator.class
+SHA1-Digest: gppBG+L9QIPr5QUgDNAfKNrKI9w=
+
+Name: org/eclipse/core/internal/resources/SyncInfoReader_3.class
+SHA1-Digest: QwHmdOZVnSv2UmmqTzBJOrXfEAc=
+
+Name: org/eclipse/core/internal/utils/StringPool.class
+SHA1-Digest: LAmlucKWmiKR2cBOQEoyXGNYf2A=
+
+Name: org/eclipse/core/internal/resources/CharsetDeltaJob$2.class
+SHA1-Digest: 01jGXl0006b4l85Bq1T/R/qgIpE=
+
+Name: org/eclipse/core/internal/resources/MarkerManager$2.class
+SHA1-Digest: NPfEObphRDN9krJxnHmRmLuORdM=
+
+Name: org/eclipse/core/resources/IProjectDescription.class
+SHA1-Digest: OkYIENn+V/Ivu9Jdhd/yaIMknTQ=
+
+Name: org/eclipse/core/internal/resources/SafeFileTable.class
+SHA1-Digest: wrFwqVWw+qHAPtajj3cprkWqxvQ=
+
+Name: org/eclipse/core/resources/IPathVariableChangeListener.class
+SHA1-Digest: 3Fx2qCPuDD6+gHwA13laLh4X3tg=
+
+Name: org/eclipse/core/internal/resources/Workspace$6.class
+SHA1-Digest: VVdIwSD4hjBML5hbyPmnwtfLS5U=
+
+Name: org/eclipse/core/internal/utils/KeyedHashSet.class
+SHA1-Digest: JFWtgIMobQuw54L+Eh3ho5+btsg=
+
+Name: org/eclipse/core/internal/resources/AliasManager$1.class
+SHA1-Digest: YdMlhXgASxgi/Qlw7DDb6xkl7a8=
+
+Name: org/eclipse/core/internal/resources/WorkspaceTreeReader_2.class
+SHA1-Digest: rracXXBlcjqLAjW1GFcdPEs1PII=
+
+Name: org/eclipse/core/internal/resources/SyncInfoReader.class
+SHA1-Digest: xlGz6FA4+AiioLvRg24CB9taa4E=
+
+Name: org/eclipse/core/internal/resources/PathVariableManager.class
+SHA1-Digest: rUhJRxpn7+AfW58L3Moirbf1E/k=
+
+Name: org/eclipse/core/internal/localstore/Bucket.class
+SHA1-Digest: JkA1FGmCcy3QUutImXfRvajRNS4=
+
+Name: org/eclipse/core/resources/IFolder.class
+SHA1-Digest: Iu7ruKFSg9ea4uSblhDKIvwy9x8=
+
+Name: org/eclipse/core/internal/events/ResourceStats.class
+SHA1-Digest: dXLq1E969kklZNGDzzeJdiD6KHA=
+
+Name: org/eclipse/core/resources/team/ResourceRuleFactory.class
+SHA1-Digest: PGWZb8IWfMP/wVeW4vgv+j1AaQ0=
+
+Name: org/eclipse/core/resources/IFileModificationValidator.class
+SHA1-Digest: AB6AhCSn7qrztOwa7yirlwAUVKs=
+
+Name: org/eclipse/core/internal/watson/IElementComparator.class
+SHA1-Digest: 3NB7ULuPA//fqjEqtrsyhKh0tnA=
+
+Name: org/eclipse/core/internal/refresh/RefreshManager.class
+SHA1-Digest: KgpFFQO6Y0b78K8oVrnCVqPveAM=
+
+Name: org/eclipse/core/internal/resources/MarkerTypeDefinitionCache.cl
+ ass
+SHA1-Digest: JWx+X4LdnjEzHkItkyyp4xWmwQ0=
+
+Name: org/eclipse/core/internal/resources/LocalMetaArea.class
+SHA1-Digest: avHrbfdctFoolib9lls6WbF38M0=
+
+Name: org/eclipse/core/internal/localstore/BlobStore.class
+SHA1-Digest: My/dQGmFslf9FbeRddjlwQP7vn0=
+
+Name: org/eclipse/core/internal/refresh/PollingMonitor.class
+SHA1-Digest: CLBFF8l/dbvREqA6VvLErWJ+q+c=
+
+Name: org/eclipse/core/internal/resources/ResourceProxy.class
+SHA1-Digest: 1GLMR5cCCdJ+baqYW8MVDwrfxvM=
+
+Name: org/eclipse/core/internal/watson/ElementTree.class
+SHA1-Digest: ytPbkKaoROu9HoWic+ycSCF22TI=
+
+Name: org/eclipse/core/resources/refresh/IRefreshResult.class
+SHA1-Digest: 3FjLmKM1RdUyvqWKB7y/Ze8D0Kw=
+
+Name: org/eclipse/core/internal/utils/Cache.class
+SHA1-Digest: tiAxY3TURbEDq6SBoq2gm59H8sM=
+
+Name: org/eclipse/core/internal/resources/Synchronizer$2.class
+SHA1-Digest: +afaF+uWyfBuGyjtwftXQJ+gC6o=
+
+Name: org/eclipse/core/internal/watson/ElementTreeIterator.class
+SHA1-Digest: kWw0YKsySP7kgiaVJCl1vDYWWsQ=
+
+Name: org/eclipse/core/internal/events/ResourceDeltaInfo.class
+SHA1-Digest: pUa/KI0PvK/geyu0drze7+P8AnU=
+
+Name: org/eclipse/core/internal/localstore/Bucket$Visitor.class
+SHA1-Digest: N+CpqBTSbxdVQXzjTSNwpYsZmy0=
+
+Name: org/eclipse/core/internal/resources/Project.class
+SHA1-Digest: rDpgATLLEz9QT4S1CE0v5f0NOgE=
+
+Name: org/eclipse/core/internal/resources/File.class
+SHA1-Digest: ignK170HOlVS/3Shp4QbYBFvnmM=
+
+Name: org/eclipse/core/resources/IMarkerDelta.class
+SHA1-Digest: ivpz38SN2+pi4Nsv2JU9l9bknfM=
+
+Name: org/eclipse/core/internal/resources/AliasManager.class
+SHA1-Digest: nKUq1XJ1szUFYZNVlVv53uNLdPE=
+
+Name: org/eclipse/core/internal/localstore/SafeChunkyOutputStream.clas
+ s
+SHA1-Digest: iZAc24mVTru7vD3jmQ7Vf0YmHdM=
+
+Name: org/eclipse/core/internal/dtree/DeltaDataTree.class
+SHA1-Digest: 1NEzISGn2xBgWeVCXOJ0Y6eUa/Q=
+
+Name: org/eclipse/core/internal/resources/IManager.class
+SHA1-Digest: JE53kk0g4F0DTG2CV7y4X6B30Hc=
+
+Name: org/eclipse/core/internal/resources/OS.class
+SHA1-Digest: zdkfwzBm153o5yCK4i41WUckekA=
+
+Name: org/eclipse/core/resources/ISynchronizer.class
+SHA1-Digest: cm1s23HoS6SLBlNLjmmAS+AHC0U=
+
+Name: org/eclipse/core/internal/dtree/DataTreeLookup.class
+SHA1-Digest: H6BsEiwdRW7vDZ6XUxW6GDcPEy8=
+
+Name: org/eclipse/core/internal/resources/InternalWorkspaceJob.class
+SHA1-Digest: wnmt03V9Qp9hHZFMKVFbAOFbpFk=
+
+Name: org/eclipse/core/internal/resources/Workspace$3.class
+SHA1-Digest: QxBly23GhYF3AIrf15lJVg3EzF0=
+
+Name: org/eclipse/core/internal/events/ResourceComparator.class
+SHA1-Digest: 7qYHDoVh5i85mY30I7QQ/hqjd4A=
+
+Name: org/eclipse/core/internal/resources/ICoreConstants.class
+SHA1-Digest: M3a1+8TrwzXlkXnfUbGj7KDRVSY=
+
+Name: org/eclipse/core/internal/localstore/HistoryBucket$HistoryEntry.
+ class
+SHA1-Digest: ovdpPM9hTMVWq1m391masbA3nt4=
+
+Name: org/eclipse/core/internal/resources/mapping/ResourceAdapterFacto
+ ry.class
+SHA1-Digest: W+/sm2lC+xWbnIDKDJUIKQmo238=
+
+Name: ant_tasks/resources-ant.jar
+SHA1-Digest: GDxGkAnsKUHojuCxYz7atN8yUtc=
+
+Name: org/eclipse/core/internal/localstore/BucketTree.class
+SHA1-Digest: l1gIL4buV99XFYITtdsp32xSDLw=
+
+Name: org/eclipse/core/internal/resources/MarkerWriter.class
+SHA1-Digest: zSWIPx2KQA3s34vnPbq9aCBrbe8=
+
+Name: org/eclipse/core/internal/dtree/NoDataDeltaNode.class
+SHA1-Digest: yYI0rdrZBQu1gwr3oXKnQ49PKQY=
+
+Name: org/eclipse/core/internal/localstore/SafeFileOutputStream.class
+SHA1-Digest: Yn+YKowQ8aTSVffINjKxfbFvzKA=
+
+Name: org/eclipse/core/resources/team/FileModificationValidator.class
+SHA1-Digest: dEBwGnde2OUKw2WKxC2ljxOJDiY=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2$3.class
+SHA1-Digest: kCFL68EKwAluqqP39e4Krry+M9s=
+
+Name: org/eclipse/core/internal/localstore/FileSystemResourceManager.c
+ lass
+SHA1-Digest: tgX9fi8+kF4eUCUhAgIqJv1V+pY=
+
+Name: org/eclipse/core/internal/resources/MarkerReader_3.class
+SHA1-Digest: L4FtqiITVEpSj5IGp1VDjQCumzY=
+
+Name: org/eclipse/core/internal/properties/IPropertyManager.class
+SHA1-Digest: wBlytkFgmw5Oz2Y+v0ZvRlXkz5Q=
+
+Name: org/eclipse/core/internal/resources/FileState.class
+SHA1-Digest: JB/tkexfr28OoOU69JJBPRbLd2U=
+
+Name: org/eclipse/core/internal/resources/WorkspaceTreeReader.class
+SHA1-Digest: tu4sYqfmvnJqo0CfLnV2FpU9QJA=
+
+Name: org/eclipse/core/internal/utils/messages.properties
+SHA1-Digest: +1pXOxr5Vu/dM88ox6/BICC/OPU=
+
+Name: org/eclipse/core/resources/IStorage.class
+SHA1-Digest: vYnwq3tgyWgOijA1kBy1+Z7xMRw=
+
+Name: org/eclipse/core/internal/dtree/IComparator.class
+SHA1-Digest: V0enFpWQlF/Nj33oGo0mK8I1Wug=
+
+Name: org/eclipse/core/internal/resources/ProjectPreferences.class
+SHA1-Digest: bF5jb+XY3rOmIug09YNaR72a9Hw=
+
+Name: org/eclipse/core/resources/IContainer.class
+SHA1-Digest: bZkdQ5qj7iGbyv+5RgmRaqIep4I=
+
+Name: org/eclipse/core/internal/properties/PropertyBucket$PropertyEntr
+ y.class
+SHA1-Digest: ya4PoVHX9yOXrv2maoFsUR9U/pI=
+
+Name: .api_description
+SHA1-Digest: WInIpJjqKbxpzar6owYs5jzZoMs=
+
+Name: org/eclipse/core/internal/utils/Convert.class
+SHA1-Digest: PJVMQexJT2zuSM2Bu1EyZgJ9zxE=
+
+Name: org/eclipse/core/internal/resources/Synchronizer.class
+SHA1-Digest: TwOydsf5mkdLddl0026SZQxYoII=
+
+Name: about.html
+SHA1-Digest: ejOZra0kypGLQQ2bJtGTX+LI8tU=
+
+Name: org/eclipse/core/internal/resources/ProjectDescription.class
+SHA1-Digest: wHsodwEA2HDEoE6xwD2ULeQKA3E=
+
+Name: org/eclipse/core/resources/IResourceDeltaVisitor.class
+SHA1-Digest: HbVWN3jUt5iwS+DIriz6K5LXlGo=
+
+Name: org/eclipse/core/resources/IResourceDelta.class
+SHA1-Digest: /j+SAhgNhskHvWx8TJY/s+5K2wE=
+
+Name: org/eclipse/core/internal/events/PathVariableChangeEvent.class
+SHA1-Digest: rfI+LhbRD+IWnJ951ZZ/hckVmnI=
+
+Name: org/eclipse/core/internal/resources/SyncInfoSnapReader_3.class
+SHA1-Digest: s0t01AXdRjkNWNeELMOrdiJuQ1c=
+
+Name: org/eclipse/core/internal/events/ResourceDelta.class
+SHA1-Digest: 6Pla+ZoRvEK7FUsaoaQcBcpoE30=
+
+Name: org/eclipse/core/resources/team/IResourceTree.class
+SHA1-Digest: oMegqTvRIE6cm/JPnLx0u6yVIIw=
+
+Name: org/eclipse/core/internal/dtree/AbstractDataTree.class
+SHA1-Digest: /3fawAxcLx5M+0GejoxnoETOheE=
+
+Name: org/eclipse/core/internal/resources/MarkerDeltaManager.class
+SHA1-Digest: Y2q3/uSyxtfosOz8aiaMHAaQIQ8=
+
+Name: org/eclipse/core/internal/resources/MarkerTypeDefinitionCache$Ma
+ rkerTypeDefinition.class
+SHA1-Digest: ocklbh/f3MyltXZ1dsmVEbawJ7k=
+
+Name: org/eclipse/core/internal/resources/CharsetDeltaJob.class
+SHA1-Digest: 3Da/TVXciQn2LbU+lt62SFdFRbM=
+
+Name: org/eclipse/core/internal/resources/ContentDescriptionManager$Fl
+ ushJob.class
+SHA1-Digest: fVuiz3gjpuFyfVqj2bu6ozqHyz8=
+
+Name: org/eclipse/core/internal/resources/mapping/ModelProviderManager
+ .class
+SHA1-Digest: FSgErZbZb85xQHSe17/WggEbuWU=
+
+Name: org/eclipse/core/internal/resources/Workspace.class
+SHA1-Digest: attJ1vub+549PXyYIQLQF3Qf7Gc=
+
+Name: org/eclipse/core/internal/resources/ProjectPreferences$SortedPro
+ perties.class
+SHA1-Digest: DyORZCe0xWiqDqGfZ5IUUeZnCvw=
+
+Name: org/eclipse/core/resources/IProject.class
+SHA1-Digest: HX1kq81NLSkZKdfYzwr+/PslKtg=
+
+Name: org/eclipse/core/internal/resources/CharsetManager$CharsetManage
+ rJob.class
+SHA1-Digest: VV+morBE0lCpGJfGPF5nf4Fu3EE=
+
+Name: org/eclipse/core/internal/resources/MarkerAttributeMap.class
+SHA1-Digest: nAthrCvU1EEHdY6Kv3bGMcileVs=
+
+Name: org/eclipse/core/internal/watson/ElementTreeWriter.class
+SHA1-Digest: gtAowIsst/jKYjJTW2+dRUr3X80=
+
+Name: org/eclipse/core/internal/resources/InternalTeamHook.class
+SHA1-Digest: 9VrTumUzml1mYmfsAimjy452+n4=
+
+Name: org/eclipse/core/internal/dtree/DataTreeWriter.class
+SHA1-Digest: UcIkRg1quNkvo6VdgYpWkuEbTNg=
+
+Name: org/eclipse/core/internal/watson/IElementInfoFlattener.class
+SHA1-Digest: DczX1H2181ib0pFaS5ujDN7Hbc4=
+
+Name: org/eclipse/core/internal/utils/Queue.class
+SHA1-Digest: CfL6QyJJVnkPlcnTn6cT3wId2BQ=
+
+Name: org/eclipse/core/internal/resources/MarkerDelta.class
+SHA1-Digest: ks2SQdhEt80Bqeo9cwWUIQlFa8Y=
+
+Name: org/eclipse/core/internal/dtree/IDataFlattener.class
+SHA1-Digest: wxTUMO1rRKHT/fYxXkGU4bWWH2w=
+
+Name: org/eclipse/core/internal/resources/ContentDescriptionManager$La
+ zyFileInputStream.class
+SHA1-Digest: STXM/jcf8EA9uONyN0mm5RxPkAc=
+
+Name: org/eclipse/core/internal/resources/MarkerManager$4.class
+SHA1-Digest: ivie+Az+AcfrAz7/oX4ADo5czs4=
+
+Name: org/eclipse/core/internal/resources/MarkerInfo.class
+SHA1-Digest: yF/AySot0YaNVSUiC307Jhh2JPw=
+
+Name: org/eclipse/core/internal/resources/Project$1.class
+SHA1-Digest: /tBT4jcRq+XYaBofx8JIzBuqRjc=
+
+Name: org/eclipse/core/internal/watson/ElementTreeWriter$1.class
+SHA1-Digest: ljE2+I9XHlKw1K+r/k3ikiZUfGg=
+
+Name: org/eclipse/core/internal/resources/ContentDescriptionManager.cl
+ ass
+SHA1-Digest: J27nDXONQ6OqT5G3iUziRBxTVJ0=
+
+Name: org/eclipse/core/internal/resources/ResourceTree.class
+SHA1-Digest: NbKFXr6yyVce+2TgJLXHrcA7XQI=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Convert.class
+SHA1-Digest: 2N6qN7/5rxciH8m46iUlLpP5XTw=
+
+Name: org/eclipse/core/internal/resources/mapping/ChangeDescription.cl
+ ass
+SHA1-Digest: rW+dwSqtyREGdFFrIoemunEXHKc=
+
+Name: org/eclipse/core/internal/resources/WorkManager$NotifyRule.class
+SHA1-Digest: 26uzRKfJ6CFMyPrvx2s3lOIsrP8=
+
+Name: org/eclipse/core/internal/properties/PropertyManager2$2.class
+SHA1-Digest: 1PO+bR1mNkdv90KlJ5MRlqFwHhY=
+
+Name: org/eclipse/core/internal/resources/IModelObjectConstants.class
+SHA1-Digest: lwtSoJ7eWLXELw5VYqq8L6Aw91A=
+
+Name: org/eclipse/core/resources/IncrementalProjectBuilder.class
+SHA1-Digest: upGM0yFnb9nlCJt9y92XUaikBgg=
+
+Name: org/eclipse/core/internal/resources/ContentDescriptionManager$1.
+ class
+SHA1-Digest: Lw1Ir0U1nT/n9zndNg3B7/pAC6Q=
+
+Name: org/eclipse/core/internal/resources/ResourceTree$1.class
+SHA1-Digest: j/u81f27yHEDYyoS4D/p4pAN3vI=
+
+Name: org/eclipse/core/internal/resources/ProjectContentTypes$ProjectC
+ ontentTypeSelectionPolicy.class
+SHA1-Digest: ggwfG3xGDVQs/yYrB3b+T5rgi/s=
+
+Name: org/eclipse/core/internal/localstore/CollectSyncStatusVisitor.cl
+ ass
+SHA1-Digest: 6yH1/FMNHMjgr5INxEDhiGPQHhs=
+
+Name: org/eclipse/core/internal/resources/Resource$2.class
+SHA1-Digest: 4EThtT26gh66Kg0yGsCaXB+GrQY=
+
+Name: org/eclipse/core/internal/resources/MarkerReader.class
+SHA1-Digest: y7Pf8o+X+dfZI+wBymqafMKbvaA=
+
+Name: org/eclipse/core/resources/IResourceVisitor.class
+SHA1-Digest: g2r3R9WyPQ4jMzkSJPWJv6+TgMM=
+
+Name: org/eclipse/core/resources/IResourceRuleFactory.class
+SHA1-Digest: noP1BmFqbWTWmrt3RmPfSRen1Kg=
+
+Name: org/eclipse/core/resources/IResourceProxy.class
+SHA1-Digest: f4176PfNakSCjnojX5kBfYebDIM=
+
+Name: org/eclipse/core/internal/resources/SaveManager$2.class
+SHA1-Digest: div6FFJEUifrJWYbinHYUklv4zA=
+
+Name: org/eclipse/core/internal/resources/AliasManager$LocationMap.cla
+ ss
+SHA1-Digest: sETrTO4mE/+a8mW8CmzgHeIx75M=
+
+Name: org/eclipse/core/internal/resources/ResourceStatus.class
+SHA1-Digest: GuZs/CVIfONiPwG0SYASy5kXw1c=
+
+Name: org/eclipse/core/internal/events/BuildManager.class
+SHA1-Digest: mK9eRuoW5S3+Awzlq2mRrGdRMas=
+
+Name: org/eclipse/core/internal/resources/WorkspacePreferences.class
+SHA1-Digest: 1/8Zbyk2WL9872TSPqDvgP9do8o=
+
+Name: org/eclipse/core/internal/resources/Rules$1.class
+SHA1-Digest: gbewQuULrQbz91ZYe/IbHFeSCOc=
+
+Name: org/eclipse/core/resources/IWorkspace$ProjectOrder.class
+SHA1-Digest: UCYzckNjTd7q5pAaF9fa6Nm1QVM=
+
+Name: org/eclipse/core/internal/resources/WorkspaceDescription.class
+SHA1-Digest: XiFBSlxvrXtLqftVY1FcRmbBRGY=
+
+Name: org/eclipse/core/internal/events/BuildManager$2.class
+SHA1-Digest: qJ9sK3FCQEOQF4V3GOgLz8EcT/U=
+
+Name: org/eclipse/core/internal/events/ResourceChangeListenerList.clas
+ s
+SHA1-Digest: Riqmb7OZJN4FsE/r3ZScjKSKhnM=
+
+Name: org/eclipse/core/internal/resources/NatureManager$1.class
+SHA1-Digest: iM2oXBZA5VWgSv1qJJvICviqik8=
+
+Name: org/eclipse/core/internal/resources/Marker.class
+SHA1-Digest: lD2/mI62VfMoXRMDMdMEHmsePbc=
+
+Name: org/eclipse/core/internal/resources/ModelObjectWriter.class
+SHA1-Digest: s9A20IqOfPUhyQz7b+XRvVf2iXg=
+
+Name: org/eclipse/core/resources/IProjectNature.class
+SHA1-Digest: FDIgdYfG4cv+QyMVc/VKV0DWFj4=
+
+Name: org/eclipse/core/internal/resources/SyncInfoReader_2.class
+SHA1-Digest: MoYVm8ntfmQ9YTitqGTZ6RqvCPU=
+
+Name: org/eclipse/core/internal/resources/CharsetDeltaJob$1.class
+SHA1-Digest: KbyCyC7xG6VMfG//3tQV0laAQkE=
+
+Name: org/eclipse/core/internal/properties/PropertyManager2.class
+SHA1-Digest: SKdUK67RP25micQTTjCXjAcGLoY=
+
+Name: org/eclipse/core/internal/resources/MarkerManager$1.class
+SHA1-Digest: aIXJn0xxZ7h/G5h6+GyF6+Cx4Vg=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Monitor$H
+ andle.class
+SHA1-Digest: jbdQzAXOuzj2gJQ2OBUG86ZQetM=
+
+Name: org/eclipse/core/internal/resources/Workspace$5.class
+SHA1-Digest: z8V00QoBxgb1iwNAuO2QxQJtDNc=
+
+Name: org/eclipse/core/internal/resources/WorkspaceTreeReader_1.class
+SHA1-Digest: UFt+jKmEGS/NfGMW0dO0dUo8nTM=
+
+Name: org/eclipse/core/internal/resources/WorkspaceDescriptionReader.c
+ lass
+SHA1-Digest: YdWXtUHgNRbiqE+on9k1Xh3yH+8=
+
+Name: org/eclipse/core/resources/IPathVariableManager.class
+SHA1-Digest: pwGSlxmd+BmecGyISexSifLLhpE=
+
+Name: org/eclipse/core/internal/watson/ElementTreeReader.class
+SHA1-Digest: wed5MucPguShThjTOFdgchyMeDE=
+
+Name: org/eclipse/core/resources/mapping/IResourceChangeDescriptionFac
+ tory.class
+SHA1-Digest: JOV2XOyqN5Tx1TOuiFc3ri/UIWg=
+
+Name: org/eclipse/core/internal/dtree/DataTreeReader.class
+SHA1-Digest: c6clvsn98XzE10eucW3wmz9hEsc=
+
+Name: org/eclipse/core/internal/events/BuildManager$DeltaCache.class
+SHA1-Digest: jGX97X5Yiik1iblk7BNupzKQ8/k=
+
+Name: org/eclipse/core/internal/resources/MarkerManager$1$MaxSeverityV
+ isitor.class
+SHA1-Digest: EAsyc285NOCVaqltjUWF60jiMRo=
+
+Name: org/eclipse/core/internal/resources/SaveManager.class
+SHA1-Digest: a0/K3p4ZV96mVk/t7GP3ou6IO6g=
+
+Name: org/eclipse/core/internal/localstore/HistoryStore2$5.class
+SHA1-Digest: 0Sa9pTEHdJKP+Ltt2lSmh5M1+DQ=
+
+Name: org/eclipse/core/resources/mapping/ModelStatus.class
+SHA1-Digest: m9SPnZGRWPDa6xtofqz3WAEHRzc=
+
+Name: org/eclipse/core/resources/IResourceChangeListener.class
+SHA1-Digest: H1+ZJzhGFhiadyFyOQxtzRzF+q0=
+
+Name: org/eclipse/core/internal/events/ILifecycleListener.class
+SHA1-Digest: hW9m8zgjFUF3fTlhKRKXzateF6Q=
+
+Name: org/eclipse/core/internal/resources/DelayedSnapshotJob.class
+SHA1-Digest: RSk9R7bgu3FrdYOLCLLcvY3TdB8=
+
+Name: org/eclipse/core/internal/watson/ElementTreeReader$1.class
+SHA1-Digest: 8IqtugaODHbbnW9IpO7sEZpf89c=
+
+Name: org/eclipse/core/internal/resources/ResourcesCompatibilityHelper
+ .class
+SHA1-Digest: HPG4gQx/492MdlGEhYKEDDzoEtg=
+
+Name: org/eclipse/core/internal/resources/mapping/ResourceChangeDescri
+ ptionFactory$1.class
+SHA1-Digest: 4gGV8HTi/n48VCjBKIgM6Zf7PsM=
+
+Name: org/eclipse/core/internal/resources/ProjectContentTypes.class
+SHA1-Digest: kEioB3gU5ZZFY+DXO1Fk4krMYZI=
+
+Name: org/eclipse/core/resources/IMarker.class
+SHA1-Digest: miXBLN6bi9Q9SY6jOB5Q2p3aGc8=
+
+Name: org/eclipse/core/internal/dtree/ObjectNotFoundException.class
+SHA1-Digest: 4+KQPOYuL15Dp1U4bkL0znETzKI=
+
+Name: org/eclipse/core/internal/events/ResourceDeltaFactory.class
+SHA1-Digest: HffRvErpk0upLK++bbZmFS55gbM=
+
+Name: org/eclipse/core/internal/resources/mapping/ProposedResourceDelt
+ a.class
+SHA1-Digest: I/zV5BaseF2/wH88MpxyeRqcqAY=
+
+Name: org/eclipse/core/internal/resources/refresh/win32/Win32Monitor.c
+ lass
+SHA1-Digest: SoFjfwChqUQY+Lfuy0fMcOwarYI=
+
+Name: org/eclipse/core/resources/mapping/ResourceTraversal.class
+SHA1-Digest: aJTYOmTdEY2vPWu7FAXFTYOGYwg=
+
+Name: org/eclipse/core/internal/resources/ProjectPreferences$2.class
+SHA1-Digest: bWYtURyU80AledXVihG58OIenUk=
+
+Name: org/eclipse/core/internal/resources/ComputeProjectOrder.class
+SHA1-Digest: vVeYDrf6iSGGTA5pv/l838LgjAo=
+
+Name: org/eclipse/core/internal/resources/Synchronizer$1.class
+SHA1-Digest: s0wpGX9gyAv2/q3wifX7RwBTh8Q=
+
+Name: org/eclipse/core/internal/resources/WorkspacePreferences$1.class
+SHA1-Digest: nbpZFmDHLgiFjil6Y+ba8sxfNZU=
+
+Name: org/eclipse/core/internal/resources/RootInfo.class
+SHA1-Digest: cxYVTOH7O740rNgDdrecHjJq6Mk=
+
+Name: org/eclipse/core/internal/events/NotificationManager$2.class
+SHA1-Digest: +FuqHnjY/QAVJDtw1eF9W7SpWL0=
+
+Name: org/eclipse/core/internal/localstore/PrefixPool.class
+SHA1-Digest: eNiuS/yvb0+8QVVraPyx1h28K0Y=
+
+Name: org/eclipse/core/internal/resources/MarkerSnapshotReader_2.class
+SHA1-Digest: SB24V9VKBu7RgSDCMi9OnwDte0Q=
+
+Name: org/eclipse/core/internal/resources/MarkerManager.class
+SHA1-Digest: HExMDZXNNx4OPWzk0tJ/ASTdhMY=
+
+Name: org/eclipse/core/internal/watson/ElementTree$1.class
+SHA1-Digest: Sb0Dm4ciQoueUedAmChPB6fMF7g=
+
+Name: org/eclipse/core/internal/resources/SyncInfoSnapReader.class
+SHA1-Digest: QmU4BYPRJG0nDjxMkJ7aCM8Ja1Q=
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/META-INF/eclipse.inf Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,3 @@
+#Processed using Jarprocessor
+pack200.args = -E4
+pack200.conditioned = true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/about.html Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 2, 2006</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
Binary file platform35/org.eclipse.core.resources/ant_tasks/resources-ant.jar has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/build.properties Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,9 @@
+source.. = src/
+bin.includes = META-INF/,\
+ plugin.xml,\
+ .api_description,\
+ .options,\
+ about.html,\
+ plugin.properties,\
+ ant_tasks/,\
+ .
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/natives/make.bat Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,11 @@
+REM build JNI header file
+cd ..\bin
+d:\vm\sun141\bin\javah org.eclipse.core.internal.resources.refresh.win32.ref
+move org_eclipse_core_internal_resources_refresh_win32_ref.h ..\a\ref.h
+
+REM compile and link
+cd ..\a
+set win_include=k:\dev\products\msvc60\vc98\include
+set jdk_include="d:\vm\sun141\include"
+set dll_name=win32refresh.dll
+cl -I%win_include% -I%jdk_include% -I%jdk_include%\win32 -LD ref.c -Fe%dll_name%
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/natives/readme.txt Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,3 @@
+This folder contains native code for supporting auto-refresh callbacks on Windows.
+This source is in the base plugin because there are multiple Windows fragments that
+share the same source.
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/natives/ref.c Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,287 @@
+#include <windows.h>
+#include "ref.h"
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindFirstChangeNotificationW
+ * Signature: (Ljava/lang/String;ZI)J
+ */
+JNIEXPORT jlong JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindFirstChangeNotificationW
+(JNIEnv * env, jclass this, jstring lpPathName, jboolean bWatchSubtree, jint dwNotifyFilter) {
+ jlong result;
+ jsize numberOfChars;
+ jchar *path;
+ const jchar *temp;
+
+ // create a new byte array to hold the prefixed and null terminated path
+ numberOfChars= (*env)->GetStringLength(env, lpPathName);
+ path= malloc((numberOfChars + 5) * sizeof(jchar));
+ //path= malloc((numberOfChars + 4) * sizeof(jchar));
+
+ // get the path characters from the vm, copy them, and release them
+ temp= (*env)->GetStringChars(env, lpPathName, JNI_FALSE);
+ memcpy(path + 4, temp, numberOfChars * sizeof(jchar));
+ (*env)->ReleaseStringChars(env, lpPathName, temp);
+
+ // prefix the path to enable long filenames, and null terminate it
+ path[0] = L'\\';
+ path[1] = L'\\';
+ path[2] = L'?';
+ path[3] = L'\\';
+ path[(numberOfChars + 4)] = L'\0';
+
+ // make the request and free the memory
+ //printf("%S\n", path);
+ result = (jlong) FindFirstChangeNotificationW(path, bWatchSubtree, dwNotifyFilter);
+ free(path);
+
+ return result;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindFirstChangeNotificationA
+ * Signature: ([BZI)J
+ */
+JNIEXPORT jlong JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindFirstChangeNotificationA
+(JNIEnv * env, jclass this, jbyteArray lpPathName, jboolean bWatchSubtree, jint dwNotifyFilter) {
+ jlong result;
+ jsize numberOfChars;
+ jbyte *path, *temp;
+
+ // create a new byte array to hold the null terminated path
+ numberOfChars = (*env)->GetArrayLength(env, lpPathName);
+ path = malloc((numberOfChars + 1) * sizeof(jbyte));
+
+ // get the path bytes from the vm, copy them, and release them
+ temp = (*env)->GetByteArrayElements(env, lpPathName, 0);
+ memcpy(path, temp, numberOfChars * sizeof(jbyte));
+ (*env)->ReleaseByteArrayElements(env, lpPathName, temp, 0);
+
+ // null terminate the path, make the request, and release the path memory
+ path[numberOfChars] = '\0';
+ result = (jlong) FindFirstChangeNotificationA(path, bWatchSubtree, dwNotifyFilter);
+ free(path);
+
+ return result;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindCloseChangeNotification
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindCloseChangeNotification
+(JNIEnv *env, jclass this, jlong hChangeHandle){
+ return (jboolean) FindCloseChangeNotification((HANDLE) hChangeHandle);
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindNextChangeNotification
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindNextChangeNotification
+(JNIEnv *env, jclass this, jlong hChangeHandle){
+ return (jboolean) FindNextChangeNotification((HANDLE) hChangeHandle);
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WaitForMultipleObjects
+ * Signature: (I[JZI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WaitForMultipleObjects
+(JNIEnv *env, jclass this, jint nCount, jlongArray lpHandles, jboolean bWaitAll, jint dwMilliseconds) {
+ int i;
+ jint result;
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ jlong *handlePointers = (*env)->GetLongArrayElements(env, lpHandles, 0);
+
+ for (i = 0; i < nCount; i++) {
+ handles[i] = (HANDLE) handlePointers[i];
+ }
+
+ result = WaitForMultipleObjects(nCount, handles, bWaitAll, dwMilliseconds);
+ (*env)->ReleaseLongArrayElements(env, lpHandles, handlePointers, 0);
+
+ return result;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: IsUnicode
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_IsUnicode
+ (JNIEnv *env, jclass this) {
+ OSVERSIONINFO osvi;
+ memset(&osvi, 0, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (! GetVersionEx (&osvi) )
+ return JNI_FALSE;
+ if (osvi.dwMajorVersion >= 5)
+ return JNI_TRUE;
+ return JNI_FALSE;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: GetLastError
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_GetLastError
+(JNIEnv *env, jclass this){
+ return GetLastError();
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_LAST_WRITE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1LAST_1WRITE
+(JNIEnv *env, jclass this) {
+ return FILE_NOTIFY_CHANGE_LAST_WRITE;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_DIR_NAME
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1DIR_1NAME
+(JNIEnv *env, jclass this) {
+ return FILE_NOTIFY_CHANGE_DIR_NAME;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_ATTRIBUTES
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1ATTRIBUTES
+(JNIEnv *env, jclass this) {
+ return FILE_NOTIFY_CHANGE_ATTRIBUTES;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_SIZE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1SIZE
+(JNIEnv *env, jclass this) {
+ return FILE_NOTIFY_CHANGE_SIZE;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_FILE_NAME
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1FILE_1NAME
+(JNIEnv *env, jclass this) {
+ return FILE_NOTIFY_CHANGE_FILE_NAME;
+}
+
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_SECURITY
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1SECURITY
+(JNIEnv *env, jclass this) {
+ return FILE_NOTIFY_CHANGE_SECURITY;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: MAXIMUM_WAIT_OBJECTS
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_MAXIMUM_1WAIT_1OBJECTS
+(JNIEnv *env, jclass this) {
+ return MAXIMUM_WAIT_OBJECTS;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: MAX_PATH
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_MAX_1PATH
+(JNIEnv *env, jclass this) {
+ return MAX_PATH;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: INFINITE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_INFINITE
+(JNIEnv *env, jclass this) {
+ return INFINITE;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WAIT_OBJECT_0
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WAIT_1OBJECT_10
+(JNIEnv *env, jclass this) {
+ return WAIT_OBJECT_0;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WAIT_FAILED
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WAIT_1FAILED
+(JNIEnv *env, jclass this) {
+ return WAIT_FAILED;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WAIT_TIMEOUT
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WAIT_1TIMEOUT
+(JNIEnv *env, jclass this) {
+ return WAIT_TIMEOUT;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: ERROR_INVALID_HANDLE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_ERROR_1INVALID_1HANDLE
+(JNIEnv *env, jclass this) {
+ return ERROR_INVALID_HANDLE;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: ERROR_SUCCESS
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_ERROR_1SUCCESS
+(JNIEnv *env, jclass this) {
+ return ERROR_SUCCESS;
+}
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: INVALID_HANDLE_VALUE
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_INVALID_1HANDLE_1VALUE
+(JNIEnv * env, jclass this) {
+ return (jlong)INVALID_HANDLE_VALUE;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/natives/ref.h Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_core_internal_resources_refresh_win32_Win32Natives */
+
+#ifndef _Included_org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+#define _Included_org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Inaccessible static: INVALID_HANDLE_VALUE */
+/* Inaccessible static: ERROR_SUCCESS */
+/* Inaccessible static: ERROR_INVALID_HANDLE */
+/* Inaccessible static: FILE_NOTIFY_ALL */
+/* Inaccessible static: MAXIMUM_WAIT_OBJECTS */
+/* Inaccessible static: MAX_PATH */
+/* Inaccessible static: INFINITE */
+/* Inaccessible static: WAIT_TIMEOUT */
+/* Inaccessible static: WAIT_OBJECT_0 */
+/* Inaccessible static: WAIT_FAILED */
+/* Inaccessible static: FILE_NOTIFY_CHANGE_FILE_NAME */
+/* Inaccessible static: FILE_NOTIFY_CHANGE_DIR_NAME */
+/* Inaccessible static: FILE_NOTIFY_CHANGE_ATTRIBUTES */
+/* Inaccessible static: FILE_NOTIFY_CHANGE_SIZE */
+/* Inaccessible static: FILE_NOTIFY_CHANGE_LAST_WRITE */
+/* Inaccessible static: FILE_NOTIFY_CHANGE_SECURITY */
+/* Inaccessible static: UNICODE */
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindFirstChangeNotificationW
+ * Signature: (Ljava/lang/String;ZI)J
+ */
+JNIEXPORT jlong JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindFirstChangeNotificationW
+ (JNIEnv *, jclass, jstring, jboolean, jint);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindFirstChangeNotificationA
+ * Signature: ([BZI)J
+ */
+JNIEXPORT jlong JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindFirstChangeNotificationA
+ (JNIEnv *, jclass, jbyteArray, jboolean, jint);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindCloseChangeNotification
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindCloseChangeNotification
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FindNextChangeNotification
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FindNextChangeNotification
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WaitForMultipleObjects
+ * Signature: (I[JZI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WaitForMultipleObjects
+ (JNIEnv *, jclass, jint, jlongArray, jboolean, jint);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: IsUnicode
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_IsUnicode
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: GetLastError
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_GetLastError
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_LAST_WRITE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1LAST_1WRITE
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_DIR_NAME
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1DIR_1NAME
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_ATTRIBUTES
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1ATTRIBUTES
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_SIZE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1SIZE
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_FILE_NAME
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1FILE_1NAME
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: FILE_NOTIFY_CHANGE_SECURITY
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_FILE_1NOTIFY_1CHANGE_1SECURITY
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: MAXIMUM_WAIT_OBJECTS
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_MAXIMUM_1WAIT_1OBJECTS
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: MAX_PATH
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_MAX_1PATH
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: INFINITE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_INFINITE
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WAIT_OBJECT_0
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WAIT_1OBJECT_10
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WAIT_FAILED
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WAIT_1FAILED
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: WAIT_TIMEOUT
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_WAIT_1TIMEOUT
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: ERROR_INVALID_HANDLE
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_ERROR_1INVALID_1HANDLE
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: ERROR_SUCCESS
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_ERROR_1SUCCESS
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_eclipse_core_internal_resources_refresh_win32_Win32Natives
+ * Method: INVALID_HANDLE_VALUE
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_eclipse_core_internal_resources_refresh_win32_Win32Natives_INVALID_1HANDLE_1VALUE
+ (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/platform/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,291 @@
+###############################################################################
+# Copyright (c) 2000, 2008 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+### Resources plugin messages.
+
+### dtree
+dtree_immutable = Illegal attempt to modify an immutable tree.
+dtree_malformedTree = Malformed tree.
+dtree_missingChild = Missing child node: {0}.
+dtree_notFound = Tree element ''{0}'' not found.
+dtree_notImmutable = Tree must be immutable.
+dtree_reverse = Tried to reverse a non-comparison tree.
+dtree_subclassImplement = Subclass should have implemented this.
+dtree_switchError = Switch error in DeltaTreeReader.readNode().
+
+### events
+events_builderError = Errors running builder ''{0}'' on project ''{1}''.
+events_building_0 = Building workspace
+events_building_1 = Building ''{0}''
+events_errors = Errors during build.
+events_instantiate_1 = Error instantiating builder ''{0}''.
+events_invoking_1 = Invoking builder on ''{0}''.
+events_invoking_2 = Invoking ''{0}'' on ''{1}''.
+events_skippingBuilder = Skipping builder ''{0}'' for project ''{1}''. Either the builder is missing from the install, or it belongs to a project nature that is missing or disabled.
+events_unknown = {0} encountered while running {1}.
+
+history_copyToNull = Unable to copy local history to or from a null location.
+history_copyToSelf = Unable to copy local history to and from the same location.
+history_errorContentDescription = Error retrieving content description for local history for: ''{0}''.
+history_notValid = State is not valid or might have expired.
+history_problemsCleaning = Problems cleaning up history store.
+
+links_creating = Creating link.
+links_errorLinkReconcile = Error processing changed links in project description file.
+links_invalidLocation = ''{0}'' is not a valid location for linked resources.
+links_localDoesNotExist = Cannot create linked resource. Local location ''{0}'' does not exist.
+links_locationOverlapsLink = ''{0}'' is not a valid location because the project contains a linked resource at that location.
+links_locationOverlapsProject = Cannot create a link to ''{0}'' because it overlaps the location of the project that contains the linked resource.
+links_natureVeto = Linking is not allowed because project nature ''{0}'' does not allow it.
+links_noPath = A Link location must be specified.
+links_overlappingResource = Location ''{0}'' may overlap another resource. This can cause unexpected side-effects.
+links_updatingDuplicate = Updating duplicate resource: ''{0}''.
+links_parentNotAccessible = Cannot create linked resource ''{0}''. The parent resource is not accessible.
+links_notFileFolder = Cannot create linked resource ''{0}''. Only files and folders can be linked.
+links_vetoNature = Cannot add nature because project ''{0}'' contains linked resources, and nature ''{1}'' does not allow it.
+links_workspaceVeto = Linked resources are not supported by this application.
+links_wrongLocalType = Cannot create linked resource ''{0}''. Files cannot be linked to folders.
+
+### local store
+localstore_copying = Copying ''{0}''.
+localstore_copyProblem = Problems encountered while copying resources.
+localstore_couldnotDelete = Could not delete ''{0}''.
+localstore_couldNotMove = Could not move ''{0}''.
+localstore_couldNotRead = Could not read file ''{0}''.
+localstore_couldNotWrite = Could not write file ''{0}''.
+localstore_couldNotWriteReadOnly = Could not write to read-only file: ''{0}''.
+localstore_deleteProblem = Problems encountered while deleting resources.
+localstore_deleting = Deleting ''{0}''.
+localstore_failedReadDuringWrite = Could not read from source when writing file ''{0}''
+localstore_fileExists = A resource already exists on disk ''{0}''.
+localstore_fileNotFound = File not found: {0}.
+localstore_locationUndefined = The location for ''{0}'' could not be determined because it is based on an undefined path variable.
+localstore_refreshing = Refreshing ''{0}''.
+localstore_refreshingRoot = Refreshing workspace.
+localstore_resourceExists = Resource already exists on disk: ''{0}''.
+localstore_resourceIsOutOfSync = Resource is out of sync with the file system: ''{0}''.
+
+### Resource mappings and models
+mapping_invalidDef = Model provider extension found with invalid definition: {0}.
+mapping_wrongType = Model provider ''{0}'' does not extend ModelProvider.
+mapping_noIdentifier = Found model provider extension with no identifier; ignoring extension.
+mapping_validate = Validating resource changes
+mapping_multiProblems = Multiple potential side effects have been identified.
+
+### internal.resources
+natures_duplicateNature = Duplicate nature: {0}.
+natures_hasCycle = Nature is invalid because its prerequisites form a cycle: {0}
+natures_missingIdentifier = Found nature extension with no identifier; ignoring extension.
+natures_missingNature = Nature does not exist: {0}.
+natures_missingPrerequisite = Nature {0} is missing prerequisite nature: {1}.
+natures_multipleSetMembers = Multiple natures found for nature set: {0}.
+natures_invalidDefinition = Nature extension found with invalid definition: {0}.
+natures_invalidRemoval = Cannot remove nature {0} because it is a prerequisite of nature {1}.
+natures_invalidSet = The set of natures is not valid.
+
+pathvar_length = Path variable name must have a length > 0.
+pathvar_beginLetter = Path variable name must begin with a letter or underscore.
+pathvar_invalidChar = Path variable name cannot contain character: {0}.
+pathvar_invalidValue = Path variable value must be valid and absolute.
+pathvar_undefined = ''{0}'' is not a valid location. The location is relative to undefined workspace path variable ''{1}''.
+pathvar_whitespace= Path variable name cannot contain whitespace
+
+### preferences
+preferences_deleteException=Exception deleting file: {0}.
+preferences_loadException=Exception loading preferences from: {0}.
+preferences_operationCanceled=Operation canceled.
+preferences_removeNodeException=Exception while removing preference node: {0}.
+preferences_clearNodeException=Exception while clearing preference node: {0}.
+preferences_saveProblems=Exception occurred while saving project preferences: {0}.
+preferences_syncException=Exception occurred while synchronizing node: {0}.
+
+projRead_badLinkName = Names ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
+projRead_badLinkType2 = Types ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
+projRead_badLocation = Locations ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
+projRead_emptyLinkName = Empty name detected for linked resource with type ''{0}'' and location ''{1}''.
+projRead_badLinkType = Illegal link type \"-1\" detected for linked resource with name ''{0}'' and location ''{1}''.
+projRead_badLinkLocation = Empty location detected for linked resource with name ''{0}'' and type ''{1}''.
+projRead_whichKey = Two values detected for an argument name in a build command: ''{0}'' and ''{1}''. Using ''{0}''.
+projRead_whichValue = Two values detected for an argument value in a build command: ''{0}'' and ''{1}''. Using ''{0}''.
+projRead_notProjectDescription = Encountered element name ''{0}'' instead of \"project\" when trying to read a project description file.
+projRead_failureReadingProjectDesc = Failure occurred reading .project file.
+
+properties_qualifierIsNull = Qualifier part of property key cannot be null.
+properties_readProperties = Failure while reading persistent properties for resource ''{0}'', file was corrupt. Some properties may have been lost.
+properties_valueTooLong = Could not set property: {0} {1}. Value is too long.
+properties_couldNotClose = Could not close property store for: {0}.
+
+### auto-refresh
+refresh_jobName = Refreshing workspace
+refresh_task = Resources to refresh: {0}
+refresh_pollJob = Searching for local changes
+refresh_refreshErr = Problems occurred while refreshing local changes
+refresh_installError = An error occurred while installing an auto-refresh monitor
+
+resources_cannotModify = The resource tree is locked for modifications.
+resources_changeInAdd = Trying to change a marker in an add method.
+resources_charsetBroadcasting = Reporting encoding changes.
+resources_charsetUpdating = Updating encoding settings.
+resources_closing_0 = Closing workspace.
+resources_closing_1 = Closing ''{0}''.
+resources_copyDestNotSub = Cannot copy ''{0}''. Destination should not be under source''s hierarchy.
+resources_copying = Copying ''{0}''.
+resources_copying_0 = Copying.
+resources_copyNotMet = Copy requirements not met.
+resources_copyProblem = Problems encountered while copying resources.
+resources_couldnotDelete = Could not delete ''{0}''.
+resources_create = Create.
+resources_creating = Creating resource ''{0}''.
+resources_deleteMeta = Could not delete metadata for ''{0}''.
+resources_deleteProblem = Problems encountered while deleting resources.
+resources_deleting = Deleting ''{0}''.
+resources_deleting_0 = Deleting.
+resources_destNotNull = Destination path should not be null.
+resources_errorContentDescription = Error retrieving content description for resource ''{0}''.
+resources_errorDeleting = Error deleting resource ''{0}'' from the workspace tree.
+resources_errorMarkersDelete = Error deleting markers for resource ''{0}''.
+resources_errorMarkersMove = Error moving markers from resource ''{0}'' to ''{1}''.
+resources_errorMembers = Error retrieving members of container ''{0}''.
+resources_errorMoving = Error moving resource ''{0}'' to ''{1}'' in the workspace tree.
+resources_errorNature = Error configuring nature ''{0}''.
+resources_errorPropertiesMove = Error moving properties for resource ''{0}'' to ''{1}''.
+resources_errorRefresh = Errors occurred during refresh of resource ''{0}''.
+resources_errorReadProject = Failed to read project description file from location ''{0}''.
+resources_errorMultiRefresh = Errors occurred while refreshing resources with the local file system.
+resources_errorValidator = Exception running validator code.
+resources_errorVisiting = An error occurred while traversing resources.
+resources_existsDifferentCase = A resource exists with a different case: ''{0}''.
+resources_existsLocalDifferentCase = A resource exists on disk with a different case: ''{0}''.
+resources_exMasterTable = Could not read master table.
+resources_exReadProjectLocation = Could not read the project location for ''{0}''.
+resources_exSafeRead = Could not read safe table.
+resources_exSafeSave = Could not save safe table.
+resources_exSaveMaster = Could not save master table.
+resources_exSaveProjectLocation = Could not save the project location for ''{0}''.
+resources_fileExists = A resource already exists on disk ''{0}''.
+resources_fileToProj = Cannot copy a file to a project.
+resources_flushingContentDescriptionCache = Flushing content description cache.
+resources_folderOverFile = Cannot overwrite folder with file ''{0}''.
+resources_format = Incompatible file format. Workspace was saved with an incompatible version: {0}.
+resources_initValidator = Unable to instantiate validator.
+resources_initHook = Unable to instantiate move/delete hook.
+resources_initTeamHook = Unable to instantiate team hook.
+resources_invalidCharInName = {0} is an invalid character in resource name ''{1}''.
+resources_invalidCharInPath = {0} is an invalid character in path ''{1}''.
+resources_invalidName = ''{0}'' is an invalid name on this platform.
+resources_invalidPath = ''{0}'' is an invalid resource path.
+resources_invalidProjDesc = Invalid project description.
+resources_invalidResourceName = ''{0}'' is an invalid resource name.
+resources_invalidRoot = Root (/) is an invalid resource path.
+resources_markerNotFound = Marker id {0} not found.
+resources_missingProjectMeta = The project description file (.project) for ''{0}'' is missing. This file contains important information about the project. The project will not function properly until this file is restored.
+resources_missingProjectMetaRepaired = The project description file (.project) for ''{0}'' was missing. This file contains important information about the project. A new project description file has been created, but some information about the project may have been lost.
+resources_moveDestNotSub = Cannot move ''{0}''. Destination should not be under source''s hierarchy.
+resources_moveMeta = Error moving metadata area from {0} to {1}.
+resources_moveNotMet = Move requirements not met.
+resources_moveNotProject = Cannot move ''{0}'' to ''{1}''. Source must be a project.
+resources_moveProblem = Problems encountered while moving resources.
+resources_moveRoot = Cannot move the workspace root.
+resources_moving = Moving ''{0}''.
+resources_moving_0 = Moving.
+resources_mustBeAbsolute = Path ''{0}'' must be absolute.
+resources_mustBeLocal = Resource ''{0}'' is not local.
+resources_mustBeOpen = Resource ''{0}'' is not open.
+resources_mustExist = Resource ''{0}'' does not exist.
+resources_mustNotExist = Resource ''{0}'' already exists.
+resources_nameEmpty = Names cannot be empty.
+resources_nameNull = Name must not be null.
+resources_natureClass = Missing project nature class for ''{0}''.
+resources_natureDeconfig = Error deconfiguring nature: {0}.
+resources_natureExtension = Missing project nature extension for {0}.
+resources_natureFormat = Project nature {0} does not specify a runtime attribute.
+resources_natureImplement = Project nature {0} does not implement IProjectNature.
+resources_notChild = Resource ''{0}'' is not a child of ''{1}''.
+resources_oneValidator = There must be exactly 0 or 1 validator extensions defined in the fileModificationValidator extension point.
+resources_oneHook = There must be exactly 0 or 1 hook extensions defined in the moveDeleteHook extension point.
+resources_oneTeamHook = There must be exactly 0 or 1 hook extensions defined in the teamHook extension point.
+resources_opening_1 = Opening ''{0}''.
+resources_overlapWorkspace = {0} overlaps the workspace location: {1}
+resources_overlapProject = {0} overlaps the location of another project: ''{1}''
+resources_pathNull = Paths must not be null.
+resources_projectDesc = Problems encountered while setting project description.
+resources_projectDescSync = Could not set the project description for ''{0}'' because the project description file (.project) is out of sync with the file system.
+resources_projectPath = Path for project must have only one segment.
+resources_reading = Reading.
+resources_readingSnap = Reading snapshot.
+resources_readingEncoding = Could not read encoding settings.
+resources_readMarkers = Failure while reading markers, the marker file was corrupt. Some markers may be lost.
+resources_readMeta = Could not read metadata for ''{0}''.
+resources_readMetaWrongVersion = Could not read metadata for ''{0}''. Unexpected version: {1}.
+resources_readOnly = Resource ''{0}'' is read-only.
+resources_readOnly2 = Cannot edit read-only resources.
+resources_readProjectMeta = Failed to read the project description file (.project) for ''{0}''. The file has been changed on disk, and it now contains invalid information. The project will not function properly until the description file is restored to a valid state.
+resources_readProjectTree = Problems reading project tree.
+resources_readSync = Errors reading sync info file: {0}.
+resources_readWorkspaceMeta = Could not read workspace metadata.
+resources_readWorkspaceMetaValue = Invalid attribute value in workspace metadata: {0}. Value will be ignored.
+resources_readWorkspaceSnap = Problems reading workspace tree snapshot.
+resources_readWorkspaceTree = Problems reading workspace tree.
+resources_refreshing = Refreshing ''{0}''.
+resources_refreshingRoot = Refreshing workspace.
+resources_resetMarkers = Could not reset markers snapshot file.
+resources_resetSync = Could not reset sync info snapshot file.
+resources_resourcePath = Invalid path for resource ''{0}''. Must include project and resource name.
+resources_saveOp = Save cannot be called from inside an operation.
+resources_saveProblem = Problems occurred during save.
+resources_saveWarnings = Save operation warnings.
+resources_saving_0 = Saving workspace.
+resources_savingEncoding = Could not save encoding settings.
+resources_setDesc = Setting project description.
+resources_setLocal = Setting resource local flag.
+resources_settingCharset = Setting character set for resource ''{0}''.
+resources_settingDefaultCharsetContainer = Setting default character set for resource ''{0}''.
+resources_settingContents = Setting contents for ''{0}''.
+resources_shutdown = Workspace was not properly initialized or has already shutdown.
+resources_shutdownProblems = Problem on shutdown.
+resources_snapInit = Could not initialize snapshot file.
+resources_snapRead = Could not read snapshot file.
+resources_snapRequest = Snapshot requested.
+resources_snapshot = Periodic workspace save.
+resources_startupProblems = Workspace restored, but some problems occurred.
+resources_touch = Touching resource ''{0}''.
+resources_updating = Updating workspace
+resources_updatingEncoding = Problems encountered while updating encoding settings.
+resources_workspaceClosed = Workspace is closed.
+resources_workspaceOpen = The workspace is already open.
+resources_writeMeta = Could not write metadata for ''{0}''.
+resources_writeWorkspaceMeta = Could not write workspace metadata ''{0}''.
+
+synchronizer_partnerNotRegistered = Sync partner: {0} not registered with the synchronizer.
+
+### URL
+url_badVariant = Unsupported \"platform:\" protocol variation {0}.
+url_couldNotResolve = Project ''{0}'' does not exist. Could not resolve URL: {1}.
+
+### utils
+utils_clone = Clone not supported.
+utils_stringJobName = Compacting memory
+
+### watson
+watson_elementNotFound = Element not found: {0}.
+watson_illegalSubtree = Illegal subtree passed to createSubtree().
+watson_immutable = Attempt to modify an immutable tree.
+watson_noModify = Cannot modify implicit root node.
+watson_nullArg = Null argument to {0}.
+watson_unknown = Unknown format.
+
+### auto-refresh win32 native
+WM_beginTask = finding out of sync resources
+WM_jobName = Win32 refresh daemon
+WM_errors = Problems occurred refreshing resources
+WM_nativeErr = Problem occurred in auto-refresh native code: {0}.
+WM_errCloseHandle = Problem closing native refresh handle: {0}.
+WM_errCreateHandle = Problem creating handle for {0}, code: {0}.
+WM_errFindChange = Problem finding next change, code: {0}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/platform/org.eclipse.core.resources/src/org/eclipse/core/resources/DiagnosticLog.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,95 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+package org.eclipse.core.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.FileHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+public class DiagnosticLog {
+ private String name;
+ private String id;
+ private Logger logger;
+ private File file;
+
+ private class LogHandler extends Handler {
+
+ FileHandler fileHandler;
+ File file;
+
+ public LogHandler(File file) {
+ this.file = file;
+ }
+
+ public void close() throws SecurityException {
+ if (fileHandler != null)
+ fileHandler.close();
+ }
+
+ public void flush() {
+ if (fileHandler != null)
+ fileHandler.flush();
+ }
+
+ public void publish(LogRecord record) {
+ if (fileHandler == null)
+ {
+ try {
+ fileHandler= new FileHandler(file.getAbsolutePath());
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (fileHandler != null)
+ fileHandler.publish(record);
+ }
+
+ }
+
+ public DiagnosticLog(String name, String id, String logFilePath) {
+ this.name = name;
+ this.id = id;
+ this.logger = Logger.getLogger(id);
+ logger.setLevel(Level.OFF);
+ logger.setUseParentHandlers(false);
+ this.file = new File(logFilePath);
+ logger.addHandler(new LogHandler(file));
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/plugin.properties Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,33 @@
+###############################################################################
+# Copyright (c) 2000, 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+pluginName = Core Resource Management
+providerName = Eclipse.org
+buildersName = Builders
+markersName = Markers
+naturesName = Project Natures
+validatorName = File Modification Validator
+hookName = Move/Delete Hook
+teamHookName = Team Hook
+preferencesContentTypeName = Preferences
+refreshProvidersName=Refresh Providers
+modelProviders=Model Providers
+preferencesExtPtName=Resource Preferences
+resourceModelName=File System Resources
+
+markerName = Marker
+problemName = Problem
+taskName = Task
+bookmarkName = Bookmark
+textName = Text
+
+win32FragmentName = Core Resource Management Win32 Fragment
+compatibilityFragmentName = Core Resource Management Compatibility Fragment
+win32MonitorFactoryName = Windows Auto-refresh monitor
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/plugin.xml Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+ <extension-point id="builders" name="%buildersName" schema="schema/builders.exsd"/>
+ <extension-point id="markers" name="%markersName" schema="schema/markers.exsd"/>
+ <extension-point id="natures" name="%naturesName" schema="schema/natures.exsd"/>
+ <extension-point id="fileModificationValidator" name="%validatorName" schema="schema/fileModificationValidator.exsd"/>
+ <extension-point id="moveDeleteHook" name="%hookName" schema="schema/moveDeleteHook.exsd"/>
+ <extension-point id="teamHook" name="%teamHookName" schema="schema/teamHook.exsd"/>
+ <extension-point id="refreshProviders" name="%refreshProvidersName" schema="schema/refreshProviders.exsd"/>
+ <extension-point id="modelProviders" name="%modelProviders" schema="schema/modelProviders.exsd"/>
+
+ <extension id="preferences" point="org.eclipse.core.runtime.preferences" name="%preferencesExtPtName">
+ <scope name="project" class="org.eclipse.core.internal.resources.ProjectPreferences"/>
+ <initializer class="org.eclipse.core.internal.resources.PreferenceInitializer"/>
+ </extension>
+
+ <extension point="org.eclipse.core.contenttype.contentTypes">
+ <file-association content-type="org.eclipse.core.runtime.xml" file-names=".project"/>
+ <content-type
+ file-extensions="prefs"
+ priority="normal"
+ name="%preferencesContentTypeName"
+ id="preferences"
+ base-type="org.eclipse.core.runtime.properties"/>
+ </extension>
+
+ <extension
+ id="marker"
+ name="%markerName"
+ point="org.eclipse.core.resources.markers">
+ <attribute
+ name="transient">
+ </attribute>
+ <attribute
+ name="generatedBy">
+ </attribute>
+ </extension>
+ <extension
+ id="problemmarker"
+ name="%problemName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="org.eclipse.core.resources.marker">
+ </super>
+ <persistent
+ value="true">
+ </persistent>
+ <attribute
+ name="severity">
+ </attribute>
+ <attribute
+ name="message">
+ </attribute>
+ <attribute
+ name="location">
+ </attribute>
+ </extension>
+ <extension
+ id="taskmarker"
+ name="%taskName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="org.eclipse.core.resources.marker">
+ </super>
+ <persistent
+ value="true">
+ </persistent>
+ <attribute
+ name="priority">
+ </attribute>
+ <attribute
+ name="message">
+ </attribute>
+ <attribute
+ name="done">
+ </attribute>
+ <attribute
+ name="userEditable">
+ </attribute>
+ </extension>
+ <extension
+ id="bookmark"
+ name="%bookmarkName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="org.eclipse.core.resources.marker">
+ </super>
+ <persistent
+ value="true">
+ </persistent>
+ <attribute
+ name="message">
+ </attribute>
+ <attribute
+ name="location">
+ </attribute>
+ </extension>
+ <extension
+ id="textmarker"
+ name="%textName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="org.eclipse.core.resources.marker">
+ </super>
+ <attribute
+ name="charStart">
+ </attribute>
+ <attribute
+ name="charEnd">
+ </attribute>
+ <attribute
+ name="lineNumber">
+ </attribute>
+ </extension>
+<!-- Tasks -->
+ <extension
+ point="org.eclipse.ant.core.antTasks">
+ <antTask
+ library="ant_tasks/resources-ant.jar"
+ name="eclipse.convertPath"
+ class="org.eclipse.core.resources.ant.ConvertPath">
+ </antTask>
+ <antTask
+ library="ant_tasks/resources-ant.jar"
+ name="eclipse.incrementalBuild"
+ class="org.eclipse.core.resources.ant.IncrementalBuild">
+ </antTask>
+ <antTask
+ library="ant_tasks/resources-ant.jar"
+ name="eclipse.refreshLocal"
+ class="org.eclipse.core.resources.ant.RefreshLocalTask">
+ </antTask>
+ </extension>
+<!-- Extra Classpath -->
+ <extension
+ point="org.eclipse.ant.core.extraClasspathEntries">
+ <extraClasspathEntry
+ library="ant_tasks/resources-ant.jar">
+ </extraClasspathEntry>
+ </extension>
+<!-- IResource to ResourceMapping adapter -->
+ <extension
+ id="org.eclipse.core.resources.resourceMappingAdapters"
+ point="org.eclipse.core.runtime.adapters">
+ <factory
+ class="org.eclipse.core.internal.resources.mapping.ResourceAdapterFactory"
+ adaptableType="org.eclipse.core.resources.IResource">
+ <adapter type="org.eclipse.core.resources.mapping.ResourceMapping"/>
+ </factory>
+ </extension>
+ <extension
+ id="modelProvider"
+ name="%resourceModelName"
+ point="org.eclipse.core.resources.modelProviders">
+ <modelProvider
+ class="org.eclipse.core.internal.resources.mapping.ResourceModelProvider"/>
+ <enablement>
+ <instanceof value="org.eclipse.core.resources.IResource" />
+ </enablement>
+ </extension>
+<!-- Property testers -->
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ id="org.eclipse.core.resources.resourcePropertyTester"
+ class="org.eclipse.core.internal.propertytester.ResourcePropertyTester"
+ namespace="org.eclipse.core.resources"
+ properties="name,path,extension,readOnly,projectNature,persistentProperty,projectPersistentProperty,sessionProperty,projectSessionProperty"
+ type="org.eclipse.core.resources.IResource"/>
+ <propertyTester
+ id="org.eclipse.core.resources.filePropertyTester"
+ class="org.eclipse.core.internal.propertytester.FilePropertyTester"
+ namespace="org.eclipse.core.resources"
+ properties="contentTypeId"
+ type="org.eclipse.core.resources.IFile"/>
+ <propertyTester
+ id="org.eclipse.core.resources.projectPropertyTester"
+ class="org.eclipse.core.internal.propertytester.ProjectPropertyTester"
+ namespace="org.eclipse.core.resources"
+ properties="open"
+ type="org.eclipse.core.resources.IProject"/>
+ <propertyTester
+ namespace="org.eclipse.core.resources"
+ type="org.eclipse.core.resources.mapping.ResourceMapping"
+ class="org.eclipse.core.internal.propertytester.ResourceMappingPropertyTester"
+ properties="projectPersistentProperty"
+ id="org.eclipse.core.resources.mappingPropertyTester"/>
+ </extension>
+
+</plugin>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/builders.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,201 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="builders" name="Incremental Project Builders"/>
+ </appInfo>
+ <documentation>
+ The workspace supports the notion of an incremental
+project builder (or "builder" for short"). The job
+of a builder is to process a set of resource changes
+(supplied as a resource delta). For example, a Java
+builder would recompile changed Java files and produce
+new class files.
+<p>
+Builders are configured on a per-project basis and run
+automatically when resources within their project are
+changed. As such, builders should be fast and scale
+with respect to the amount of change rather than the
+number of resources in the project. This typically
+implies that builders are able to incrementally update
+their "built state".
+<p>
+The builders extension-point allows builder writers
+to register their builder implementation under a
+symbolic name that is then used from within the
+workspace to find and run builders. The symbolic
+name is the id of the builder extension. When defining a builder extension, users are encouraged to include a human-readable value for the "name" attribute which identifies their builder and potentially may be presented to users.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="builder"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ an optional identifier of the extension instance
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ an optional name of the extension instance
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="builder">
+ <complexType>
+ <sequence>
+ <element ref="run" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="hasNature" type="boolean">
+ <annotation>
+ <documentation>
+ "<tt>true</tt>" or "<tt>false</tt>" indicating whether the builder is owned by
+a project nature. If "<tt>true</tt>" and no corresponding nature is
+found, this builder will not run but will remain in the project's
+build spec. If the attribute is not specified, it is assumed to be "<tt>false</tt>".
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="isConfigurable" type="boolean">
+ <annotation>
+ <documentation>
+ "<tt>true</tt>" or "<tt>false</tt>" indicating whether the builder allows customization of what build triggers it will respond to. If "<tt>true</tt>", clients will be able to use the API <tt>ICommand.setBuilding</tt> to specify if this builder should be run for a particular build trigger. If the attribute is not specified, it is assumed to be "<tt>false</tt>".
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="callOnEmptyDelta" type="boolean">
+ <annotation>
+ <documentation>
+ "<tt>true</tt>" or "<tt>false</tt>" indicating whether the builder should be called on <tt>INCREMENTAL_BUILD</tt> when the resource deltas for its affected projects are empty. If "<tt>true</tt>", the builder will always be called on builds of type <tt>INCREMENTAL_BUILD</tt>, regardless of whether any resources in the affected projects have changed. If "<tt>false</tt>" or unspecified, the builder will only be called when affected projects have changed. The value of this attribute does not affect the behaviour of builders for other build triggers, such as <tt>AUTO_BUILD</tt> or <tt>FULL_BUILD</tt>This attribute is intended to be used by builders that incrementally react to changing circumstances outside of the workspace, such as external libraries.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="run">
+ <complexType>
+ <sequence>
+ <element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified name of a subclass of
+<samp>org.eclipse.core.resources.IncrementalProjectBuilder</samp>.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.core.resources.IncrementalProjectBuilder"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <annotation>
+ <appInfo>
+ <meta.element labelAttribute="name"/>
+ </appInfo>
+ </annotation>
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ the name of this parameter made available to
+instances of the specified builder class
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="value" type="string" use="required">
+ <annotation>
+ <documentation>
+ an arbitrary value associated with the given
+name and made available to instances of the
+specified builder class
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ Following is an example of a builder configuration:
+
+<p>
+<pre>
+ <extension id="coolbuilder" name="Cool Builder" point="org.eclipse.core.resources.builders">
+ <builder hasNature="false">
+ <run class="com.xyz.builders.Cool">
+ <parameter name="optimize" value="true"/>
+ <parameter name="comment" value="Produced by the Cool Builder"/>
+ </run>
+ </builder>
+ </extension>
+</pre>
+</p>
+
+If this extension was defined in a plug-in with id "com.xyz.coolplugin", the fully qualified name of this builder would be "com.xyz.coolplugin.coolbuilder".
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the class attribute must represent a
+subclass of <samp>org.eclipse.core.resources.IncrementalProjectBuilder</samp>.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The platform itself does not have any predefined
+builders. Particular product installs may include
+builders as required.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2002, 2006 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies this distribution, and is available at
+<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/fileModificationValidator.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,111 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="fileModificationValidator" name="File Modification Validator"/>
+ </appInfo>
+ <documentation>
+ For providing an implementation of an IFileModificationValidator to be used in the validate-edit
+and validate-save mechanism. This extension point tolerates at most one extension.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="fileModificationValidator" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+ an optional identifier of the extension instance
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ an optional name of the extension instance
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="fileModificationValidator">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified name of a class which implements <samp>org.eclipse.core.resources.IFileModificationValidator</samp>.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.core.resources.IFileModificationValidator"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is an example of using the <tt>fileModificationValidator</tt> extension point:
+<p>
+<pre>
+ <extension point="org.eclipse.core.resources.fileModificationValidator">
+ <fileModificationValidator class="org.eclipse.vcm.internal.VCMFileModificationValidator"/>
+ </extension>
+</pre>
+</p>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the <samp>class</samp> attribute must represent an implementation of
+<samp>org.eclipse.core.resources.IFileModificationValidator</samp>.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The Team component will generally provide the implementation of the file modification validator. The extension point should be used by any other clients.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2002, 2005 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/markers.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,167 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="markers" name="Resource Markers"/>
+ </appinfo>
+ <documentation>
+ The workspace supports the notion of markers on arbitrary
+resources. A marker is a kind of metadata
+(similar to properties) which can be used to
+tag resources with user information. Markers are
+optionally persisted by the workspace whenever a
+workspace save or snapshot is done.
+<p>
+Users can define and query for markers of a given type.
+Marker types are defined in a hierarchy that supports
+multiple-inheritance. Marker type definitions also
+specify a number attributes which must or may be
+present on a marker of that type as well as whether
+or not markers of that type should be persisted.
+<p>
+The markers extension-point allows marker writers to
+register their marker types under a symbolic name that
+is then used from within the workspace to create and
+query markers. The symbolic name is the id of the
+marker extension. When defining a marker extension,
+users are encouraged to include a human-readable value
+for the "name" attribute which indentifies their marker
+and potentially may be presented to users.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="super" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="persistent" minOccurs="0" maxOccurs="1"/>
+ <element ref="attribute" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ a required identifier of the extension instance
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ an optional name of the extension instance
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="super">
+ <complexType>
+ <attribute name="type" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified id of a marker super type (i.e., a marker type defined by another marker extension)
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.core.resources.markers/@id"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="persistent">
+ <complexType>
+ <attribute name="value" type="boolean" use="required">
+ <annotation>
+ <documentation>
+ "<tt>true</tt>" or "<tt>false</tt>" indicating whether or not markers of this type
+should be persisted by the workspace. If the persistent characteristic
+is not specified, the marker type is <b>not</b> persisted.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="attribute">
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ the name of an attribute which may be present on markers of this type
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ Following is an example of a marker configuration:
+
+<p>
+<pre>
+ <extension id="com.xyz.coolMarker" point="org.eclipse.core.resources.markers" name="Cool Marker">
+ <persistent value="true"/>
+ <super type="org.eclipse.core.resources.problemmarker"/>
+ <super type="org.eclipse.core.resources.textmarker"/>
+ <attribute name="owner"/>
+ </extension>
+</pre>
+</p>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiInfo"/>
+ </appinfo>
+ <documentation>
+ All markers, regardless of their type, are instances of
+<samp>org.eclipse.core.resources.IMarker</samp>.
+ </documentation>
+ </annotation>
+
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ The platform itself has a number of pre-defined
+marker types. Particular product installs may
+include additional markers as required.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2002, 2008 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/modelProviders.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,151 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="modelProviders" name="Model Providers"/>
+ </appInfo>
+ <documentation>
+ A model provider serves two purposes. Firstly, it is a means of grouping the resource mappings of a single model together for the purposes of performing operations, display, etc. Also, it provides a means of mapping a set of file system resources to the resource mappings that describe how a model maps to resources.
+ </documentation>
+ </annotation>
+
+ <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="modelProvider"/>
+ <element ref="extends-model" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="enablement" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="modelProvider">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.core.resources.mapping.ModelProvider"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="extends-model">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 3.2
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is an example of using the modelProvider extension point.
+(in file plugin.xml)
+<pre>
+ <extension
+ id="modelProvider"
+ name="Library Model Provider"
+ point="org.eclipse.core.resources.modelProviders">
+ <modelProvider
+ class="org.eclipse.examples.library.LibraryModelProvider"
+ name="Library Model Provider"/>
+ <extends-model id="org.eclipse.core.resources.modelProvider"/>
+ <enablement>
+ <with variable="affectedNatures">
+ <iterate operator="or">
+ <equals value="org.eclipse.team.examples.library.nature"/>
+ </iterate>
+ </with>
+ <with variable="element">
+ <instanceof value="org.eclipse.core.resources.IFile"/>
+ </with>
+ </enablement>
+ </extension>
+</pre>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the class attribute must represent an implementation of <tt>org.eclipse.core.resources.mapping.ModelProvider</tt>.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The platform itself does not have any predefined
+model providers. Particular product installs may include
+model providers as required.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2005, 2007 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/moveDeleteHook.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,120 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="moveDeleteHook" name="Move/Delete Hook"/>
+ </appInfo>
+ <documentation>
+ For providing an implementation of an IMoveDeleteHook to be used in the IResource.move and IResource.delete mechanism. This extension point tolerates at most one extension.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="moveDeleteHook" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+ an optional identifier of the extension instance
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ an optional name of the extension instance
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="moveDeleteHook">
+ <complexType>
+ <sequence>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified name of a class which implements
+<samp>org.eclipse.core.resources.team.IMoveDeleteHook</samp>
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 2.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is an example of using the moveDeleteHook extension point.
+(in file plugin.xml)
+<pre>
+ <extension point="org.eclipse.core.resources.moveDeleteHook">
+ <moveDeleteHook class="org.eclipse.team.internal.MoveDeleteHook"/>
+ </extension>
+</pre>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the class attribute must represent an implementation of <tt>org.eclipse.core.resources.team.IMoveDeleteHook</tt>.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The Team component will generally provide the implementation of the move/delete hook. The extension point should not be used by any other clients.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2002, 2005 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/natures.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,330 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="natures" name="Project Natures"/>
+ </appinfo>
+ <documentation>
+ The workspace supports the notion of project natures
+(or "natures" for short"). A nature associates lifecycle
+behaviour with a project. Natures are installed on
+a per-project basis using the setDescription method defined on
+<samp>org.eclipse.core.resources.IProject</samp>. They are
+configured when added to a project and deconfigured
+when removed from the project. For example, the Java nature
+might install a Java builder and do other project
+configuration when added to a project
+<p>
+The natures extension-point allows nature writers
+to register their nature implementation under a
+symbolic name that is then used from within the
+workspace to find and configure natures.
+The symbolic name is id of the nature extension. When
+defining a nature extension, users are encouraged to include a
+human-readable value for the "name" attribute which identifies
+their meaning and potentially may be presented to users.
+</p>
+<p>
+Natures can specify relationship constraints with other natures.
+The "one-of-nature" constraint specifies that at most one nature
+belong to a given set can exist on a project at any given time.
+This enforces mutual exclusion between natures that are not compatible
+with each other. The "requires-nature" constraint specifies a
+dependency on another nature. When a nature is added to a project,
+all required natures must also be added. The natures are guaranteed
+to be configured and deconfigured in such a way that their required
+natures will always be configured before them and deconfigured after
+them. For this reason, cyclic dependencies between natures are not
+permitted.
+</p>
+<p>
+Natures cannot be added to or removed from a project if that change would
+violate any constraints that were previously satisfied. If a nature is
+configured on a project, but later finds that its constraints are not
+satisfied, that nature and all natures that require it are marked as
+<i>disabled</i>, but remain on the project. This can happen, for example,
+when a required nature goes missing from the install. Natures that are
+missing from the install, and natures involved in dependency cycles are
+also marked as disabled.
+</p>
+<p>
+Natures can also specify which incremental project builders, if any, are
+configured by them. With this information, the workspace will ensure that
+builders will only run when their corresponding nature is present and
+enabled on the project being built. If a nature is removed from a project,
+but the nature's deconfigure method fails to remove its corresponding builders,
+the workspace will remove those builders from the spec automatically. It
+is not permitted for two natures to specify the same incremental project builder
+in their markup.
+</p>
+<p>
+Natures also have the ability to disallow the creation of linked resources on projects they are associated with. By setting the <code>allowLinking</code> attribute to &quot;false&quot;, a nature can declare that linked resources should never be created. This feature is new in release 2.1.
+</p>
+<p>
+Starting with release 3.1, natures can declare affinity
+with arbitrary content types, affecting the way content
+type determination happens for files in the workspace.
+In case of conflicts (two or more content types deemed
+equally suitable for a given file), the content type having affinity with any of the natures configured for the corresponding project will be chosen.
+</p>
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="runtime"/>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="one-of-nature"/>
+ <element ref="requires-nature"/>
+ <element ref="builder"/>
+ <element ref="content-type"/>
+ </choice>
+ <element ref="options" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ The simple identifier of the nature. This is appended to the plug-in id to form the fully qualified nature id.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ an optional name of the extension instance
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="runtime">
+ <complexType>
+ <sequence>
+ <element ref="run"/>
+ </sequence>
+ </complexType>
+ </element>
+
+ <element name="run">
+ <annotation>
+ <appinfo>
+ <meta.element labelAttribute="name"/>
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified name of a class which implements
+<samp>org.eclipse.core.resources.IProjectNature</samp>
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.core.resources.IProjectNature"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <annotation>
+ <appinfo>
+ <meta.element labelAttribute="name"/>
+ </appinfo>
+ </annotation>
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ the name of this parameter made available to instances of the specified nature class
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="value" type="string" use="required">
+ <annotation>
+ <documentation>
+ an arbitrary value associated with the given name and made available to instances of the specificed nature class
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="one-of-nature">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ the name of an exclusive project nature category.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="requires-nature">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified id of another nature extension that this nature extension requires.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.core.resources.natures/@id"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="builder">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified id of an incremental project builder extension that this nature controls.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.core.resources.builders/@id"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="options">
+ <complexType>
+ <attribute name="allowLinking" type="boolean">
+ <annotation>
+ <documentation>
+ an option to specify whether this nature allows the creation of linked resources. By default, linking is allowed.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="content-type">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified id of a content type associated to this nature.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.core.runtime.contentTypes/@id"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ Following is an example of three nature configurations. The
+waterNature and fireNature belong
+to the same exclusive set, so they cannot co-exist on the same
+project. The snowNature requires
+waterNature, so snowNature will be disabled on a project that
+is missing waterNature. It
+naturally follows that snowNature cannot be enabled on a project
+with fireNature. The fireNature also doesn't allow the creation of linked resources.
+
+<p>
+<pre>
+ <extension id="fireNature" name="Fire Nature" point="org.eclipse.core.resources.natures">
+ <runtime>
+ <run class="com.xyz.natures.Fire"/>
+ </runtime>
+ <one-of-nature id="com.xyz.stateSet"/>
+ <options allowLinking="false"/>
+ </extension>
+
+ <extension id="waterNature" name="Water Nature" point="org.eclipse.core.resources.natures">
+ <runtime>
+ <run class="com.xyz.natures.Water"/>
+ </runtime>
+ <one-of-nature id="com.xyz.stateSet"/>
+ </extension>
+
+ <extension id="snowNature" name="Snow Nature" point="org.eclipse.core.resources.natures">
+ <runtime>
+ <run class="com.xyz.natures.Snow">
+ <parameter name="installBuilder" value="true"/>
+ </run>
+ </runtime>
+ <requires-nature id="com.xyz.coolplugin.waterNature"/>
+ <builder id="com.xyz.snowMaker"/>
+ </extension>
+</pre>
+</p>
+
+If these extensions were defined in a plug-in with id "com.xyz.coolplugin", the fully qualified name
+of these natures would be "com.xyz.coolplugin.fireNature", "com.xyz.coolplugin.waterNature" and
+"com.xyz.coolplugin.snowNature".
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiInfo"/>
+ </appinfo>
+ <documentation>
+ The value of the class attribute must represent an
+implementor of
+<samp>org.eclipse.core.resources.IProjectNature</samp>.
+Nature definitions can be examined using the
+<samp>org.eclipse.core.resources.IProjectNatureDescriptor</samp> interface.
+The descriptor objects can be obtained using the methods
+<samp>getNatureDescriptor(String)</samp> and <samp>getNatureDescriptors()</samp>
+on <samp>org.eclipse.core.resources.IWorkspace</samp>.
+ </documentation>
+ </annotation>
+
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ The platform itself does not have any predefined natures.
+Particular product installs may include natures as required.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2002, 2005 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/refreshProviders.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,133 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="refreshProviders" name="Auto-refresh providers"/>
+ </appInfo>
+ <documentation>
+ The workspace supports a mode where changes that occur in the file system are automatically detected and reconciled with the workspace in memory. By default, this is accomplished by creating a monitor that polls the file system and periodically searching for changes. The monitor factories extension point allows clients to create more efficient monitors, typically by hooking into some native file system facility for change callbacks.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="refreshProvider"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="refreshProvider">
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ A human-readable name for the monitor factory
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ The fully qualified name of a class implementing <code>org.eclipse.core.resources.refresh.RefreshProvider</code>.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.core.resources.refresh.RefreshProvider"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 3.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ Following is an example of an adapter declaration. This example declares that this plug-in will provide an adapter factory that will adapt objects of type IFile to objects of type MyFile.
+<p>
+<pre>
+ <extension
+ id="coolProvider"
+ point="org.eclipse.core.resources.refreshProviders">
+ <refreshProvider
+ name="Cool Refresh Provider"
+ class="com.xyz.CoolRefreshProvider">
+ </refreshProvider>
+ </extension>
+</pre>
+</p>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ Refresh provider implementations must subclass the abstract type <tt>RefreshProvider</tt> in the <tt>org.eclipse.core.resources.refresh</tt> package. Refresh requests and failures should be forward to the provide <tt>IRefreshResult</tt>. Clients must also provide an implementation of <tt>IRefreshMonitor</tt> through which the workspace can request that refresh monitors be uninstalled.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The <tt>org.eclipse.core.resources.win32</tt> fragment provides a native refresh monitor that uses win32 file system notification callbacks. The workspace also supplies a default naive polling-based monitor that can be used for file systems that do not have native refresh callbacks available.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2004, 2005 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/schema/teamHook.exsd Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,120 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.core.resources">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.core.resources" id="teamHook" name="Team Hook"/>
+ </appInfo>
+ <documentation>
+ For providing an implementation of a TeamHook that is used for mechanisms available only to team providers. This extension point tolerates at most one extension.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="teamHook"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified identifier of the target extension point
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+ an optional identifier of the extension instance
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+ an optional name of the extension instance
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="teamHook">
+ <complexType>
+ <sequence>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified name of a class which subclasses
+<samp>org.eclipse.core.resources.team.TeamHook</samp>
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 2.1
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is an example of using the teamHook extension point.
+(in file plugin.xml)
+<pre>
+ <extension point="org.eclipse.core.resources.teamHook">
+ <teamHook class="org.eclipse.team.internal.TeamHook"/>
+ </extension>
+</pre>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the class attribute must represent a subclass of <tt>org.eclipse.core.resources.team.TeamHook</tt>.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The Team component will generally provide the implementation of the team hook. The extension point should not be used by any other clients.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2002, 2005 IBM Corporation and others.<br>
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which
+accompanies
+this distribution, and is available at
+<a
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/AbstractDataTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Data trees can be viewed as generic multi-leaf trees. The tree points to a single
+ * rootNode, and each node can contain an arbitrary number of children.<p>
+ *
+ * <p>Internally, data trees can be either complete trees (DataTree class), or delta
+ * trees (<code>DeltaDataTree</code> class). A DataTree is a stand-alone tree
+ * that contains all its own data. A <code>DeltaDataTree</code> only stores the
+ * differences between itself and its parent tree. This sparse representation allows
+ * the API user to retain chains of delta trees that represent incremental changes to
+ * a system. Using the delta trees, the user can undo changes to a tree by going up to
+ * the parent tree.
+ *
+ * <p>Both representations of the tree support the same API, so the user of a tree
+ * never needs to know if they're dealing with a complete tree or a chain of deltas.
+ * Delta trees support an extended API of delta operations. See the <code>DeltaDataTree
+ * </code> class for details.
+ *
+ * @see DataTree
+ * @see DeltaDataTree
+ */
+
+public abstract class AbstractDataTree {
+
+ /**
+ * Whether modifications to the given source tree are allowed
+ */
+ private boolean immutable = false;
+
+ /**
+ * Singleton indicating no children
+ */
+ protected static final IPath[] NO_CHILDREN = new IPath[0];
+
+ /**
+ * Creates a new empty tree
+ */
+ public AbstractDataTree() {
+ this.empty();
+ }
+
+ /**
+ * Returns a copy of the receiver, which shares the receiver's
+ * instance variables.
+ */
+ protected AbstractDataTree copy() {
+ AbstractDataTree newTree = this.createInstance();
+ newTree.setImmutable(this.isImmutable());
+ newTree.setRootNode(this.getRootNode());
+ return newTree;
+ }
+
+ /**
+ * Returns a copy of the node subtree rooted at the given key.
+ *
+ */
+ public abstract AbstractDataTreeNode copyCompleteSubtree(IPath key);
+
+ /**
+ * Creates a new child in the tree. If a child with such a name exists,
+ * it is replaced with the new child
+ *
+ * @param parentKey key of parent for new child.
+ * @param localName name for new child.
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ * @exception RuntimeException
+ * receiver is immutable
+ */
+ public abstract void createChild(IPath parentKey, String localName);
+
+ /**
+ * Creates a new child in the tree. If a child with such a name exists,
+ * it is replaced with the new child
+ *
+ * @param parentKey key of parent for new child.
+ * @param localName name for new child.
+ * @param object the data for the new child
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ * @exception RuntimeException
+ * receiver is immutable
+ */
+ public abstract void createChild(IPath parentKey, String localName, Object object);
+
+ /**
+ * Creates and returns a new instance of the tree. This is an
+ * implementation of the factory method creational pattern for allowing
+ * abstract methods to create instances.
+ *
+ * @return the new tree.
+ */
+ protected abstract AbstractDataTree createInstance();
+
+ /**
+ * Creates or replaces a subtree in the tree. The parent node must exist.
+ *
+ * @param key key of parent of subtree to create/replace
+ * @param subtree new subtree to add to tree
+ * @exception RuntimeException receiver is immutable
+ */
+ public abstract void createSubtree(IPath key, AbstractDataTreeNode subtree);
+
+ /**
+ * Deletes a child from the tree.
+ *
+ * <p>Note: this method requires both parentKey and localName,
+ * making it impossible to delete the root node.
+ *
+ * @param parentKey parent of node to delete.
+ * @param localName name of node to delete.
+ * @exception ObjectNotFoundException
+ * a child of parentKey with name localName does not exist in the receiver
+ * @exception RuntimeException
+ * receiver is immutable
+ */
+ public abstract void deleteChild(IPath parentKey, String localName);
+
+ /**
+ * Initializes the receiver so that it is a complete, empty tree. The
+ * result does not represent a delta on another tree. An empty tree is
+ * defined to have a root node with null data and no children.
+ */
+ public abstract void empty();
+
+ /**
+ * Returns the key of a node in the tree.
+ *
+ * @param parentKey
+ * parent of child to retrieve.
+ * @param index
+ * index of the child to retrieve in its parent.
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ * @exception ArrayIndexOutOfBoundsException
+ * if no child with the given index (runtime exception)
+ */
+ public IPath getChild(IPath parentKey, int index) {
+ /* Get name of given child of the parent */
+ String child = getNameOfChild(parentKey, index);
+ return parentKey.append(child);
+ }
+
+ /**
+ * Returns the number of children of a node
+ *
+ * @param parentKey
+ * key of the node for which we want to retreive the number of children
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ */
+ public int getChildCount(IPath parentKey) {
+ return getNamesOfChildren(parentKey).length;
+ }
+
+ /**
+ * Returns the keys of all children of a node.
+ *
+ * @param parentKey
+ * key of parent whose children we want to retrieve.
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ */
+ public IPath[] getChildren(IPath parentKey) {
+ String names[] = getNamesOfChildren(parentKey);
+ int len = names.length;
+ if (len == 0)
+ return NO_CHILDREN;
+ IPath answer[] = new IPath[len];
+
+ for (int i = 0; i < len; i++) {
+ answer[i] = parentKey.append(names[i]);
+ }
+ return answer;
+ }
+
+ /**
+ * Returns the data of a node.
+ *
+ * @param key
+ * key of node for which we want to retrieve data.
+ * @exception ObjectNotFoundException
+ * key does not exist in the receiver
+ */
+ public abstract Object getData(IPath key);
+
+ /**
+ * Returns the local name of a node in the tree
+ *
+ * @param parentKey
+ * parent of node whose name we want to retrieve
+ * @param index
+ * index of node in its parent
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ * @exception ArrayIndexOutOfBoundsException
+ * if no child with the given index
+ */
+ public String getNameOfChild(IPath parentKey, int index) {
+ String childNames[] = getNamesOfChildren(parentKey);
+ /* Return the requested child as long as its in range */
+ return childNames[index];
+ }
+
+ /**
+ * Returns the local names for the children of a node
+ *
+ * @param parentKey
+ * key of node whose children we want to retrieve
+ * @exception ObjectNotFoundException
+ * parentKey does not exist in the receiver
+ */
+ public abstract String[] getNamesOfChildren(IPath parentKey);
+
+ /**
+ * Returns the root node of the tree.
+ *
+ * <p>Both subclasses must be able to return their root node. However subclasses
+ * can have different types of root nodes, so this is not enforced as an abstract
+ * method
+ */
+ AbstractDataTreeNode getRootNode() {
+ throw new AbstractMethodError(Messages.dtree_subclassImplement);
+ }
+
+ /**
+ * Handles the case where an attempt was made to modify
+ * the tree when it was in an immutable state. Throws
+ * an unchecked exception.
+ */
+ static void handleImmutableTree() {
+ throw new RuntimeException(Messages.dtree_immutable);
+ }
+
+ /**
+ * Handles the case where an attempt was made to manipulate
+ * an element in the tree that does not exist. Throws an
+ * unchecked exception.
+ */
+ static void handleNotFound(IPath key) {
+ throw new ObjectNotFoundException(NLS.bind(Messages.dtree_notFound, key));
+ }
+
+ /**
+ * Makes the tree immutable
+ */
+ public void immutable() {
+ immutable = true;
+ }
+
+ /**
+ * Returns true if the receiver includes a node with the given key, false
+ * otherwise.
+ *
+ * @param key
+ * key of node to find
+ */
+ public abstract boolean includes(IPath key);
+
+ /**
+ * Returns true if the tree is immutable, and false otherwise.
+ */
+ public boolean isImmutable() {
+ return immutable;
+ }
+
+ /**
+ * Returns an object containing:
+ * - a flag indicating whether the specified node was found
+ * - the data for the node, if it was found
+ * @param key
+ * key of node for which we want to retrieve data.
+ */
+ public abstract DataTreeLookup lookup(IPath key);
+
+ /**
+ * Returns the key of the root node.
+ */
+ public IPath rootKey() {
+ return Path.ROOT;
+ }
+
+ /**
+ * Sets the data of a node.
+ *
+ * @param key
+ * key of node for which to set data
+ * @param data
+ * new data value for node
+ * @exception ObjectNotFoundException
+ * the nodeKey does not exist in the receiver
+ * @exception IllegalArgumentException
+ * receiver is immutable
+ */
+ public abstract void setData(IPath key, Object data);
+
+ /**
+ * Sets the immutable field.
+ */
+ void setImmutable(boolean bool) {
+ immutable = bool;
+ }
+
+ /**
+ * Sets the root node of the tree.
+ *
+ * <p>Both subclasses must be able to set their root node. However subclasses
+ * can have different types of root nodes, so this is not enforced as an abstract
+ * method
+ */
+ void setRootNode(AbstractDataTreeNode node) {
+ throw new Error(Messages.dtree_subclassImplement);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/AbstractDataTreeNode.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,551 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.StringPool;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class and its subclasses are used to represent nodes of AbstractDataTrees.
+ * Refer to the DataTree API comments for more details.
+ * @see AbstractDataTree
+ */
+public abstract class AbstractDataTreeNode {
+ /**
+ * Singleton indicating no children.
+ */
+ static final AbstractDataTreeNode[] NO_CHILDREN = new AbstractDataTreeNode[0];
+ protected AbstractDataTreeNode children[];
+ protected String name;
+
+ /* Node types for comparison */
+ public static final int T_COMPLETE_NODE = 0;
+ public static final int T_DELTA_NODE = 1;
+ public static final int T_DELETED_NODE = 2;
+ public static final int T_NO_DATA_DELTA_NODE = 3;
+
+ /**
+ * Creates a new data tree node
+ *
+ * @param name
+ * name of new node
+ * @param children
+ * children of the new node
+ */
+ AbstractDataTreeNode(String name, AbstractDataTreeNode[] children) {
+ this.name = name;
+ if (children == null || children.length == 0)
+ this.children = AbstractDataTreeNode.NO_CHILDREN;
+ else
+ this.children = children;
+ }
+
+ /**
+ * Returns a node which if applied to the receiver will produce
+ * the corresponding node in the given parent tree.
+ *
+ * @param myTree tree to which the node belongs
+ * @param parentTree parent tree on which to base backward delta
+ * @param key key of node in its tree
+ */
+ abstract AbstractDataTreeNode asBackwardDelta(DeltaDataTree myTree, DeltaDataTree parentTree, IPath key);
+
+ /**
+ * If this node is a node in a comparison tree, this method reverses
+ * the comparison for this node and all children
+ */
+ AbstractDataTreeNode asReverseComparisonNode(IComparator comparator) {
+ return this;
+ }
+
+ /**
+ * Returns the result of assembling nodes with the given forward delta nodes.
+ * Both arrays must be sorted by name.
+ * The result is sorted by name.
+ * If keepDeleted is true, explicit representations of deletions are kept,
+ * otherwise nodes to be deleted are removed in the result.
+ */
+ static AbstractDataTreeNode[] assembleWith(AbstractDataTreeNode[] oldNodes, AbstractDataTreeNode[] newNodes, boolean keepDeleted) {
+
+ // Optimize the common case where the new list is empty.
+ if (newNodes.length == 0)
+ return oldNodes;
+
+ // Can't just return newNodes if oldNodes has length 0
+ // because newNodes may contain deleted nodes.
+
+ AbstractDataTreeNode[] resultNodes = new AbstractDataTreeNode[oldNodes.length + newNodes.length];
+
+ // do a merge
+ int oldIndex = 0;
+ int newIndex = 0;
+ int resultIndex = 0;
+ while (oldIndex < oldNodes.length && newIndex < newNodes.length) {
+ int compare = oldNodes[oldIndex].name.compareTo(newNodes[newIndex].name);
+ if (compare == 0) {
+ AbstractDataTreeNode node = oldNodes[oldIndex++].assembleWith(newNodes[newIndex++]);
+ if (node != null && (!node.isDeleted() || keepDeleted)) {
+ resultNodes[resultIndex++] = node;
+ }
+ } else if (compare < 0) {
+ resultNodes[resultIndex++] = oldNodes[oldIndex++];
+ } else if (compare > 0) {
+ AbstractDataTreeNode node = newNodes[newIndex++];
+ if (!node.isDeleted() || keepDeleted) {
+ resultNodes[resultIndex++] = node;
+ }
+ }
+ }
+ while (oldIndex < oldNodes.length) {
+ resultNodes[resultIndex++] = oldNodes[oldIndex++];
+ }
+ while (newIndex < newNodes.length) {
+ AbstractDataTreeNode resultNode = newNodes[newIndex++];
+ if (!resultNode.isDeleted() || keepDeleted) {
+ resultNodes[resultIndex++] = resultNode;
+ }
+ }
+
+ // trim size of result
+ if (resultIndex < resultNodes.length) {
+ System.arraycopy(resultNodes, 0, resultNodes = new AbstractDataTreeNode[resultIndex], 0, resultIndex);
+ }
+ return resultNodes;
+ }
+
+ /**
+ * Returns the result of assembling this node with the given forward delta node.
+ */
+ AbstractDataTreeNode assembleWith(AbstractDataTreeNode node) {
+
+ // If not a delta, or if the old node was deleted,
+ // then the new node represents the complete picture.
+ if (!node.isDelta() || this.isDeleted()) {
+ return node;
+ }
+
+ // node must be either a DataDeltaNode or a NoDataDeltaNode
+ if (node.hasData()) {
+ if (this.isDelta()) {
+ // keep deletions because they still need
+ // to hide child nodes in the parent.
+ AbstractDataTreeNode[] assembledChildren = assembleWith(children, node.children, true);
+ return new DataDeltaNode(name, node.getData(), assembledChildren);
+ }
+ // This is a complete picture, so deletions
+ // wipe out the child and are no longer useful
+ AbstractDataTreeNode[] assembledChildren = assembleWith(children, node.children, false);
+ return new DataTreeNode(name, node.getData(), assembledChildren);
+ }
+ if (this.isDelta()) {
+ AbstractDataTreeNode[] assembledChildren = assembleWith(children, node.children, true);
+ if (this.hasData())
+ return new DataDeltaNode(name, this.getData(), assembledChildren);
+ return new NoDataDeltaNode(name, assembledChildren);
+ }
+ AbstractDataTreeNode[] assembledChildren = assembleWith(children, node.children, false);
+ return new DataTreeNode(name, this.getData(), assembledChildren);
+ }
+
+ /**
+ * Returns the result of assembling this node with the given forward delta node.
+ */
+ AbstractDataTreeNode assembleWith(AbstractDataTreeNode node, IPath key, int keyIndex) {
+
+ // leaf case
+ int keyLen = key.segmentCount();
+ if (keyIndex == keyLen) {
+ return assembleWith(node);
+ }
+
+ // non-leaf case
+ int childIndex = indexOfChild(key.segment(keyIndex));
+ if (childIndex >= 0) {
+ AbstractDataTreeNode copy = copy();
+ copy.children[childIndex] = children[childIndex].assembleWith(node, key, keyIndex + 1);
+ return copy;
+ }
+
+ // Child not found. Build up NoDataDeltaNode hierarchy for rest of key
+ // and assemble with that.
+ for (int i = keyLen - 2; i >= keyIndex; --i) {
+ node = new NoDataDeltaNode(key.segment(i), node);
+ }
+ node = new NoDataDeltaNode(name, node);
+ return assembleWith(node);
+ }
+
+ /**
+ * Returns the child with the given local name. The child must exist.
+ */
+ AbstractDataTreeNode childAt(String localName) {
+ AbstractDataTreeNode node = childAtOrNull(localName);
+ if (node != null) {
+ return node;
+ }
+ throw new ObjectNotFoundException(NLS.bind(Messages.dtree_missingChild, localName));
+ }
+
+ /**
+ * Returns the child with the given local name. Returns null if the child
+ * does not exist.
+ *
+ * @param localName
+ * name of child to retrieve
+ */
+ AbstractDataTreeNode childAtOrNull(String localName) {
+ int index = indexOfChild(localName);
+ return index >= 0 ? children[index] : null;
+ }
+
+ /**
+ * Returns the child with the given local name, ignoring case.
+ * If multiple case variants exist, the search will favour real nodes
+ * over deleted nodes. If multiple real nodes are found, the first one
+ * encountered in case order is returned. Returns null if no matching
+ * children are found.
+ *
+ * @param localName name of child to retrieve
+ */
+ AbstractDataTreeNode childAtIgnoreCase(String localName) {
+ AbstractDataTreeNode result = null;
+ for (int i = 0; i < children.length; i++) {
+ if (children[i].getName().equalsIgnoreCase(localName)) {
+ //if we find a deleted child, keep looking for a real child
+ if (children[i].isDeleted())
+ result = children[i];
+ else
+ return children[i];
+ }
+ }
+ return result;
+ }
+
+ /**
+ */
+ protected static AbstractDataTreeNode[] compareWith(AbstractDataTreeNode[] oldNodes, AbstractDataTreeNode[] newNodes, IComparator comparator) {
+
+ int oldLen = oldNodes.length;
+ int newLen = newNodes.length;
+ int oldIndex = 0;
+ int newIndex = 0;
+ AbstractDataTreeNode[] comparedNodes = new AbstractDataTreeNode[oldLen + newLen];
+ int count = 0;
+
+ while (oldIndex < oldLen && newIndex < newLen) {
+ DataTreeNode oldNode = (DataTreeNode) oldNodes[oldIndex];
+ DataTreeNode newNode = (DataTreeNode) newNodes[newIndex];
+ int compare = oldNode.name.compareTo(newNode.name);
+ if (compare < 0) {
+ /* give the client a chance to say whether it should be in the delta */
+ int userComparison = comparator.compare(oldNode.getData(), null);
+ if (userComparison != 0) {
+ comparedNodes[count++] = convertToRemovedComparisonNode(oldNode, userComparison);
+ }
+ ++oldIndex;
+ } else if (compare > 0) {
+ /* give the client a chance to say whether it should be in the delta */
+ int userComparison = comparator.compare(null, newNode.getData());
+ if (userComparison != 0) {
+ comparedNodes[count++] = convertToAddedComparisonNode(newNode, userComparison);
+ }
+ ++newIndex;
+ } else {
+ AbstractDataTreeNode comparedNode = oldNode.compareWith(newNode, comparator);
+ NodeComparison comparison = (NodeComparison) comparedNode.getData();
+
+ /* skip empty comparisons */
+ if (!(comparison.isUnchanged() && comparedNode.size() == 0)) {
+ comparedNodes[count++] = comparedNode;
+ }
+ ++oldIndex;
+ ++newIndex;
+ }
+ }
+ while (oldIndex < oldLen) {
+ DataTreeNode oldNode = (DataTreeNode) oldNodes[oldIndex++];
+
+ /* give the client a chance to say whether it should be in the delta */
+ int userComparison = comparator.compare(oldNode.getData(), null);
+ if (userComparison != 0) {
+ comparedNodes[count++] = convertToRemovedComparisonNode(oldNode, userComparison);
+ }
+ }
+ while (newIndex < newLen) {
+ DataTreeNode newNode = (DataTreeNode) newNodes[newIndex++];
+
+ /* give the client a chance to say whether it should be in the delta */
+ int userComparison = comparator.compare(null, newNode.getData());
+ if (userComparison != 0) {
+ comparedNodes[count++] = convertToAddedComparisonNode(newNode, userComparison);
+ }
+ }
+
+ if (count == 0) {
+ return NO_CHILDREN;
+ }
+ if (count < comparedNodes.length) {
+ System.arraycopy(comparedNodes, 0, comparedNodes = new AbstractDataTreeNode[count], 0, count);
+ }
+ return comparedNodes;
+ }
+
+ /**
+ */
+ protected static AbstractDataTreeNode[] compareWithParent(AbstractDataTreeNode[] nodes, IPath key, DeltaDataTree parent, IComparator comparator) {
+
+ AbstractDataTreeNode[] comparedNodes = new AbstractDataTreeNode[nodes.length];
+ int count = 0;
+ for (int i = 0; i < nodes.length; ++i) {
+ AbstractDataTreeNode node = nodes[i];
+ AbstractDataTreeNode comparedNode = node.compareWithParent(key.append(node.getName()), parent, comparator);
+ NodeComparison comparison = (NodeComparison) comparedNode.getData();
+ // Skip it if it's an empty comparison (and no children).
+ if (!(comparison.isUnchanged() && comparedNode.size() == 0)) {
+ comparedNodes[count++] = comparedNode;
+ }
+ }
+ if (count == 0) {
+ return NO_CHILDREN;
+ }
+ if (count < comparedNodes.length) {
+ System.arraycopy(comparedNodes, 0, comparedNodes = new AbstractDataTreeNode[count], 0, count);
+ }
+ return comparedNodes;
+ }
+
+ abstract AbstractDataTreeNode compareWithParent(IPath key, DeltaDataTree parent, IComparator comparator);
+
+ static AbstractDataTreeNode convertToAddedComparisonNode(AbstractDataTreeNode newNode, int userComparison) {
+ AbstractDataTreeNode[] children = newNode.getChildren();
+ int n = children.length;
+ AbstractDataTreeNode[] convertedChildren;
+ if (n == 0) {
+ convertedChildren = NO_CHILDREN;
+ } else {
+ convertedChildren = new AbstractDataTreeNode[n];
+ for (int i = 0; i < n; ++i) {
+ convertedChildren[i] = convertToAddedComparisonNode(children[i], userComparison);
+ }
+ }
+ return new DataTreeNode(newNode.name, new NodeComparison(null, newNode.getData(), NodeComparison.K_ADDED, userComparison), convertedChildren);
+ }
+
+ static AbstractDataTreeNode convertToRemovedComparisonNode(AbstractDataTreeNode oldNode, int userComparison) {
+ AbstractDataTreeNode[] children = oldNode.getChildren();
+ int n = children.length;
+ AbstractDataTreeNode[] convertedChildren;
+ if (n == 0) {
+ convertedChildren = NO_CHILDREN;
+ } else {
+ convertedChildren = new AbstractDataTreeNode[n];
+ for (int i = 0; i < n; ++i) {
+ convertedChildren[i] = convertToRemovedComparisonNode(children[i], userComparison);
+ }
+ }
+ return new DataTreeNode(oldNode.name, new NodeComparison(oldNode.getData(), null, NodeComparison.K_REMOVED, userComparison), convertedChildren);
+ }
+
+ /**
+ * Returns a copy of the receiver which shares the receiver elements
+ */
+ abstract AbstractDataTreeNode copy();
+
+ /**
+ * Replaces the receiver's children between "from" and "to", with the children
+ * in otherNode starting at "start". This method replaces the Smalltalk
+ * #replaceFrom:to:with:startingAt: method for copying children in data nodes
+ */
+ protected void copyChildren(int from, int to, AbstractDataTreeNode otherNode, int start) {
+ int other = start;
+ for (int i = from; i <= to; i++, other++) {
+ this.children[i] = otherNode.children[other];
+ }
+ }
+
+ /**
+ * Returns an array of the node's children
+ */
+ public AbstractDataTreeNode[] getChildren() {
+ return children;
+ }
+
+ /**
+ * Returns the node's data
+ */
+ Object getData() {
+ throw new AbstractMethodError(Messages.dtree_subclassImplement);
+ }
+
+ /**
+ * return the name of the node
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns true if the receiver can carry data, false otherwise.
+ */
+ boolean hasData() {
+ return false;
+ }
+
+ /**
+ * Returns true if the receiver has a child with the given local name,
+ * false otherwise
+ */
+ boolean includesChild(String localName) {
+ return indexOfChild(localName) != -1;
+ }
+
+ /**
+ * Returns the index of the specified child's name in the receiver.
+ */
+ protected int indexOfChild(String localName) {
+ AbstractDataTreeNode[] nodes = this.children;
+ int left = 0;
+ int right = nodes.length - 1;
+ while (left <= right) {
+ int mid = (left + right) / 2;
+ int compare = localName.compareTo(nodes[mid].name);
+ if (compare < 0) {
+ right = mid - 1;
+ } else if (compare > 0) {
+ left = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns true if the receiver represents a deleted node, false otherwise.
+ */
+ boolean isDeleted() {
+ return false;
+ }
+
+ /**
+ * Returns true if the receiver represents delta information,
+ * false if it represents the complete information.
+ */
+ boolean isDelta() {
+ return false;
+ }
+
+ /**
+ * Returns true if the receiver is an empty delta node, false otherwise.
+ */
+ boolean isEmptyDelta() {
+ return false;
+ }
+
+ /**
+ * Returns the local names of the receiver's children.
+ */
+ String[] namesOfChildren() {
+ String names[] = new String[children.length];
+ /* copy child names (Reverse loop optimized) */
+ for (int i = children.length; --i >= 0;)
+ names[i] = children[i].getName();
+ return names;
+ }
+
+ /**
+ * Replaces the child with the given local name.
+ */
+ void replaceChild(String localName, DataTreeNode node) {
+ int i = indexOfChild(localName);
+ if (i >= 0) {
+ children[i] = node;
+ } else {
+ throw new ObjectNotFoundException(NLS.bind(Messages.dtree_missingChild, localName));
+ }
+ }
+
+ /**
+ * Set the node's children
+ */
+ protected void setChildren(AbstractDataTreeNode newChildren[]) {
+ children = newChildren;
+ }
+
+ /**
+ * Set the node's name
+ */
+ void setName(String s) {
+ name = s;
+ }
+
+ /**
+ * Simplifies the given nodes, and answers their replacements.
+ */
+ protected static AbstractDataTreeNode[] simplifyWithParent(AbstractDataTreeNode[] nodes, IPath key, DeltaDataTree parent, IComparator comparer) {
+
+ AbstractDataTreeNode[] simplifiedNodes = new AbstractDataTreeNode[nodes.length];
+ int simplifiedCount = 0;
+ for (int i = 0; i < nodes.length; ++i) {
+ AbstractDataTreeNode node = nodes[i];
+ AbstractDataTreeNode simplifiedNode = node.simplifyWithParent(key.append(node.getName()), parent, comparer);
+ if (!simplifiedNode.isEmptyDelta()) {
+ simplifiedNodes[simplifiedCount++] = simplifiedNode;
+ }
+ }
+ if (simplifiedCount == 0) {
+ return NO_CHILDREN;
+ }
+ if (simplifiedCount < simplifiedNodes.length) {
+ System.arraycopy(simplifiedNodes, 0, simplifiedNodes = new AbstractDataTreeNode[simplifiedCount], 0, simplifiedCount);
+ }
+ return simplifiedNodes;
+ }
+
+ /**
+ * Simplifies the given node, and answers its replacement.
+ */
+ abstract AbstractDataTreeNode simplifyWithParent(IPath key, DeltaDataTree parent, IComparator comparer);
+
+ /**
+ * Returns the number of children of the receiver
+ */
+ int size() {
+ return children.length;
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void storeStrings(StringPool set) {
+ name = set.add(name);
+ //copy children pointer in case of concurrent modification
+ AbstractDataTreeNode[] nodes = children;
+ if (nodes != null)
+ for (int i = nodes.length; --i >= 0;)
+ nodes[i].storeStrings(set);
+ }
+
+ /**
+ * Returns a unicode representation of the node. This method is used
+ * for debugging purposes only (no NLS support needed)
+ */
+ public String toString() {
+ return "an AbstractDataTreeNode(" + this.getName() + ") with " + getChildren().length + " children."; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Returns a constant describing the type of node.
+ */
+ abstract int type();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataDeltaNode.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A <code>DataDeltaNode</code> contains information that represents the differences
+ * between itself and a node in another tree. Refer to the <code>DeltaDataTree</code>
+ * API and comments for details.
+ *
+ * @see DeltaDataTree
+ */
+public class DataDeltaNode extends DataTreeNode {
+ /**
+ * Creates a node with the given name and data, but with no children.
+ */
+ DataDeltaNode(String name, Object data) {
+ super(name, data);
+ }
+
+ /**
+ * Creates a node with the given name, data and children.
+ */
+ DataDeltaNode(String name, Object data, AbstractDataTreeNode[] children) {
+ super(name, data, children);
+ }
+
+ /**
+ * @see AbstractDataTreeNode#asBackwardDelta(DeltaDataTree, DeltaDataTree, IPath)
+ */
+ AbstractDataTreeNode asBackwardDelta(DeltaDataTree myTree, DeltaDataTree parentTree, IPath key) {
+ AbstractDataTreeNode[] newChildren;
+ if (children.length == 0) {
+ newChildren = NO_CHILDREN;
+ } else {
+ newChildren = new AbstractDataTreeNode[children.length];
+ for (int i = children.length; --i >= 0;) {
+ newChildren[i] = children[i].asBackwardDelta(myTree, parentTree, key.append(children[i].getName()));
+ }
+ }
+ return new DataDeltaNode(name, parentTree.getData(key), newChildren);
+ }
+
+ AbstractDataTreeNode compareWithParent(IPath key, DeltaDataTree parent, IComparator comparator) {
+ AbstractDataTreeNode[] comparedChildren = compareWithParent(children, key, parent, comparator);
+ Object oldData = parent.getData(key);
+ Object newData = data;
+ /* don't compare data of root */
+ int userComparison = 0;
+ if (key != parent.rootKey()) {
+ /* allow client to specify user comparison bits */
+ userComparison = comparator.compare(oldData, newData);
+ }
+ return new DataTreeNode(key.lastSegment(), new NodeComparison(oldData, newData, NodeComparison.K_CHANGED, userComparison), comparedChildren);
+ }
+
+ /**
+ * Creates and returns a new copy of the receiver. Makes a deep copy of
+ * children, but a shallow copy of name and data.
+ */
+ AbstractDataTreeNode copy() {
+ AbstractDataTreeNode[] childrenCopy;
+ if (children.length == 0) {
+ childrenCopy = NO_CHILDREN;
+ } else {
+ childrenCopy = new AbstractDataTreeNode[children.length];
+ System.arraycopy(children, 0, childrenCopy, 0, children.length);
+ }
+ return new DataDeltaNode(name, data, childrenCopy);
+ }
+
+ /**
+ * Returns true if the receiver represents delta information,
+ * false if it represents the complete information.
+ */
+ boolean isDelta() {
+ return true;
+ }
+
+ /**
+ * Simplifies the given node, and answers its replacement.
+ */
+ AbstractDataTreeNode simplifyWithParent(IPath key, DeltaDataTree parent, IComparator comparer) {
+ AbstractDataTreeNode[] simplifiedChildren = simplifyWithParent(children, key, parent, comparer);
+ /* don't compare root nodes */
+ if (!key.isRoot() && comparer.compare(parent.getData(key), data) == 0)
+ return new NoDataDeltaNode(name, simplifiedChildren);
+ return new DataDeltaNode(name, data, simplifiedChildren);
+ }
+
+ /**
+ * Returns a unicode representation of the node. This method is used
+ * for debugging purposes only (no NLS support needed)
+ */
+ public String toString() {
+ return "a DataDeltaNode(" + this.getName() + ") with " + getChildren().length + " children."; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Returns a constant describing the type of node.
+ */
+ int type() {
+ return T_DELTA_NODE;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A <code>DataTree</code> represents the complete information of a source tree.
+ * The tree contains all information within its branches, and has no relation to any
+ * other source trees (no parent and no children).
+ */
+public class DataTree extends AbstractDataTree {
+
+ /**
+ * the root node of the tree
+ */
+ private DataTreeNode rootNode;
+
+ /**
+ * Creates a new empty tree
+ */
+ public DataTree() {
+ this.empty();
+ }
+
+ /**
+ * Returns a copy of the node subtree rooted at the given key.
+ *
+ * @param key
+ * Key of subtree to copy
+ */
+ public AbstractDataTreeNode copyCompleteSubtree(IPath key) {
+ DataTreeNode node = findNodeAt(key);
+ if (node == null) {
+ handleNotFound(key);
+ }
+ return copyHierarchy(node);
+ }
+
+ /**
+ * Returns a deep copy of a node and all its children.
+ *
+ * @param node
+ * Node to be copied.
+ */
+ DataTreeNode copyHierarchy(DataTreeNode node) {
+ DataTreeNode newNode;
+ int size = node.size();
+ if (size == 0) {
+ newNode = new DataTreeNode(node.getName(), node.getData());
+ } else {
+ AbstractDataTreeNode[] children = node.getChildren();
+ DataTreeNode[] newChildren = new DataTreeNode[size];
+ for (int i = size; --i >= 0;) {
+ newChildren[i] = this.copyHierarchy((DataTreeNode) children[i]);
+ }
+ newNode = new DataTreeNode(node.getName(), node.getData(), newChildren);
+ }
+
+ return newNode;
+ }
+
+ /**
+ * Creates a new child in the tree.
+ * @see AbstractDataTree#createChild(IPath, String)
+ */
+ public void createChild(IPath parentKey, String localName) {
+ createChild(parentKey, localName, null);
+ }
+
+ /**
+ * Creates a new child in the tree.
+ * @see AbstractDataTree#createChild(IPath, String, Object)
+ */
+ public void createChild(IPath parentKey, String localName, Object data) {
+ DataTreeNode node = findNodeAt(parentKey);
+ if (node == null)
+ handleNotFound(parentKey);
+ if (this.isImmutable())
+ handleImmutableTree();
+ /* If node already exists, replace */
+ if (node.includesChild(localName)) {
+ node.replaceChild(localName, new DataTreeNode(localName, data));
+ } else {
+ this.replaceNode(parentKey, node.copyWithNewChild(localName, new DataTreeNode(localName, data)));
+ }
+ }
+
+ /**
+ * Creates and returns an instance of the receiver. This is an
+ * implementation of the factory method creational pattern for allowing
+ * abstract methods to create instances
+ */
+ protected AbstractDataTree createInstance() {
+ return new DataTree();
+ }
+
+ /**
+ * Creates or replaces a subtree in the tree. The parent node must exist.
+ *
+ * @param key
+ * Key of parent node whose subtree we want to create/replace.
+ * @param subtree
+ * New node to insert into tree.
+ */
+ public void createSubtree(IPath key, AbstractDataTreeNode subtree) {
+
+ // Copy it since destructive mods are allowed on the original
+ // and shouldn't affect this tree.
+ DataTreeNode newNode = copyHierarchy((DataTreeNode) subtree);
+
+ if (this.isImmutable()) {
+ handleImmutableTree();
+ }
+
+ if (key.isRoot()) {
+ setRootNode(newNode);
+ } else {
+ String localName = key.lastSegment();
+ newNode.setName(localName); // Another mod, but it's OK we've already copied
+
+ IPath parentKey = key.removeLastSegments(1);
+
+ DataTreeNode node = findNodeAt(parentKey);
+ if (node == null) {
+ handleNotFound(parentKey);
+ }
+
+ /* If node already exists, replace */
+ if (node.includesChild(localName)) {
+ node.replaceChild(localName, newNode);
+ }
+
+ this.replaceNode(parentKey, node.copyWithNewChild(localName, newNode));
+ }
+ }
+
+ /**
+ * Deletes a child of the tree.
+ * @see AbstractDataTree#deleteChild(IPath, String)
+ */
+ public void deleteChild(IPath parentKey, String localName) {
+ if (this.isImmutable())
+ handleImmutableTree();
+ DataTreeNode node = findNodeAt(parentKey);
+ if (node == null || (!node.includesChild(localName))) {
+ handleNotFound(node == null ? parentKey : parentKey.append(localName));
+ } else {
+ this.replaceNode(parentKey, node.copyWithoutChild(localName));
+ }
+ }
+
+ /**
+ * Initializes the receiver.
+ * @see AbstractDataTree#empty()
+ */
+ public void empty() {
+ this.setRootNode(new DataTreeNode(null, null));
+ }
+
+ /**
+ * Returns the specified node if it is present, otherwise returns null.
+ *
+ * @param key
+ * Key of node to return
+ */
+ public DataTreeNode findNodeAt(IPath key) {
+ AbstractDataTreeNode node = this.getRootNode();
+ int keyLength = key.segmentCount();
+ for (int i = 0; i < keyLength; i++) {
+ try {
+ node = node.childAt(key.segment(i));
+ } catch (ObjectNotFoundException notFound) {
+ return null;
+ }
+ }
+ return (DataTreeNode) node;
+ }
+
+ /**
+ * Returns the data at the specified node.
+ *
+ * @param key
+ * Node whose data to return.
+ */
+ public Object getData(IPath key) {
+ DataTreeNode node = findNodeAt(key);
+ if (node == null) {
+ handleNotFound(key);
+ return null;
+ }
+ return node.getData();
+ }
+
+ /**
+ * Returns the names of the children of a node.
+ * @see AbstractDataTree#getNamesOfChildren(IPath)
+ */
+ public String[] getNamesOfChildren(IPath parentKey) {
+ DataTreeNode parentNode;
+ parentNode = findNodeAt(parentKey);
+ if (parentNode == null) {
+ handleNotFound(parentKey);
+ return null;
+ }
+ return parentNode.namesOfChildren();
+ }
+
+ /**
+ * Returns the root node of the tree
+ */
+ AbstractDataTreeNode getRootNode() {
+ return rootNode;
+ }
+
+ /**
+ * Returns true if the receiver includes a node with
+ * the given key, false otherwise.
+ */
+ public boolean includes(IPath key) {
+ return (findNodeAt(key) != null);
+ }
+
+ /**
+ * Returns an object containing:
+ * - a flag indicating whether the specified node was found
+ * - the data for the node, if it was found
+ * @param key
+ * key of node for which we want to retrieve data.
+ */
+ public DataTreeLookup lookup(IPath key) {
+ DataTreeNode node = this.findNodeAt(key);
+ if (node == null)
+ return DataTreeLookup.newLookup(key, false, null);
+ return DataTreeLookup.newLookup(key, true, node.getData());
+ }
+
+ /**
+ * Replaces the node at the specified key with the given node
+ */
+ protected void replaceNode(IPath key, DataTreeNode node) {
+ DataTreeNode found;
+ if (key.isRoot()) {
+ this.setRootNode(node);
+ } else {
+ found = this.findNodeAt(key.removeLastSegments(1));
+ found.replaceChild(key.lastSegment(), node);
+ }
+ }
+
+ /**
+ * Sets the data at the specified node.
+ * @see AbstractDataTree#setData(IPath, Object)
+ */
+ public void setData(IPath key, Object data) {
+ DataTreeNode node = this.findNodeAt(key);
+ if (this.isImmutable())
+ handleImmutableTree();
+ if (node == null) {
+ handleNotFound(key);
+ } else {
+ node.setData(data);
+ }
+ }
+
+ /**
+ * Sets the root node of the tree
+ * @see AbstractDataTree#setRootNode(AbstractDataTreeNode)
+ */
+ void setRootNode(DataTreeNode aNode) {
+ rootNode = aNode;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeLookup.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * The result of doing a lookup() in a data tree. Uses an instance
+ * pool that assumes no more than POOL_SIZE instance will ever be
+ * needed concurrently. Reclaims and reuses instances on an LRU basis.
+ */
+public class DataTreeLookup {
+ public IPath key;
+ public boolean isPresent;
+ public Object data;
+ public boolean foundInFirstDelta;
+ private static final int POOL_SIZE = 100;
+ /**
+ * The array of lookup instances available for use.
+ */
+ private static DataTreeLookup[] instancePool;
+ /**
+ * The index of the next available lookup instance.
+ */
+ private static int nextFree = 0;
+ static {
+ instancePool = new DataTreeLookup[POOL_SIZE];
+ //fill the pool with objects
+ for (int i = 0; i < POOL_SIZE; i++) {
+ instancePool[i] = new DataTreeLookup();
+ }
+ }
+
+ /**
+ * Constructors for internal use only. Use factory methods.
+ */
+ private DataTreeLookup() {
+ super();
+ }
+
+ /**
+ * Factory method for creating a new lookup object.
+ */
+ public static DataTreeLookup newLookup(IPath nodeKey, boolean isPresent, Object data) {
+ DataTreeLookup instance;
+ synchronized (instancePool) {
+ instance = instancePool[nextFree];
+ nextFree = ++nextFree % POOL_SIZE;
+ }
+ instance.key = nodeKey;
+ instance.isPresent = isPresent;
+ instance.data = data;
+ instance.foundInFirstDelta = false;
+ return instance;
+ }
+
+ /**
+ * Factory method for creating a new lookup object.
+ */
+ public static DataTreeLookup newLookup(IPath nodeKey, boolean isPresent, Object data, boolean foundInFirstDelta) {
+ DataTreeLookup instance;
+ synchronized (instancePool) {
+ instance = instancePool[nextFree];
+ nextFree = ++nextFree % POOL_SIZE;
+ }
+ instance.key = nodeKey;
+ instance.isPresent = isPresent;
+ instance.data = data;
+ instance.foundInFirstDelta = foundInFirstDelta;
+ return instance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeNode.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,359 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * <code>DataTreeNode</code>s are the nodes of a <code>DataTree</code>. Their
+ * information and their subtrees are complete, and do not represent deltas on
+ * another node or subtree.
+ */
+public class DataTreeNode extends AbstractDataTreeNode {
+ protected Object data;
+
+ /**
+ * Creates a new node
+ *
+ * @param name name of node
+ * @param data data for node
+ */
+ public DataTreeNode(String name, Object data) {
+ super(name, AbstractDataTreeNode.NO_CHILDREN);
+ this.data = data;
+ }
+
+ /**
+ * Creates a new node
+ *
+ * @param name name of node
+ * @param data data for node
+ * @param children children for new node
+ */
+ public DataTreeNode(String name, Object data, AbstractDataTreeNode[] children) {
+ super(name, children);
+ this.data = data;
+ }
+
+ /**
+ * @see AbstractDataTreeNode#asBackwardDelta(DeltaDataTree, DeltaDataTree, IPath)
+ */
+ AbstractDataTreeNode asBackwardDelta(DeltaDataTree myTree, DeltaDataTree parentTree, IPath key) {
+ if (parentTree.includes(key))
+ return parentTree.copyCompleteSubtree(key);
+ return new DeletedNode(name);
+ }
+
+ /**
+ * If this node is a node in a comparison tree, this method reverses
+ * the comparison for this node and all children. Returns null
+ * if this node should no longer be included in the comparison tree.
+ */
+ AbstractDataTreeNode asReverseComparisonNode(IComparator comparator) {
+ NodeComparison comparison = null;
+ try {
+ comparison = ((NodeComparison) data).asReverseComparison(comparator);
+ } catch (ClassCastException e) {
+ Assert.isTrue(false, Messages.dtree_reverse);
+ }
+
+ int nextChild = 0;
+ for (int i = 0; i < children.length; i++) {
+ AbstractDataTreeNode child = children[i].asReverseComparisonNode(comparator);
+ if (child != null) {
+ children[nextChild++] = child;
+ }
+ }
+
+ if (nextChild == 0 && comparison.getUserComparison() == 0) {
+ /* no children and no change */
+ return null;
+ }
+
+ /* set the new data */
+ data = comparison;
+
+ /* shrink child array as necessary */
+ if (nextChild < children.length) {
+ AbstractDataTreeNode[] newChildren = new AbstractDataTreeNode[nextChild];
+ System.arraycopy(children, 0, newChildren, 0, nextChild);
+ children = newChildren;
+ }
+
+ return this;
+ }
+
+ AbstractDataTreeNode compareWith(DataTreeNode other, IComparator comparator) {
+ AbstractDataTreeNode[] comparedChildren = compareWith(children, other.children, comparator);
+ Object oldData = data;
+ Object newData = other.data;
+
+ /* don't allow comparison of implicit root node */
+ int userComparison = 0;
+ if (name != null) {
+ userComparison = comparator.compare(oldData, newData);
+ }
+
+ return new DataTreeNode(name, new NodeComparison(oldData, newData, NodeComparison.K_CHANGED, userComparison), comparedChildren);
+ }
+
+ AbstractDataTreeNode compareWithParent(IPath key, DeltaDataTree parent, IComparator comparator) {
+ if (!parent.includes(key))
+ return convertToAddedComparisonNode(this, NodeComparison.K_ADDED);
+ DataTreeNode inParent = (DataTreeNode) parent.copyCompleteSubtree(key);
+ return inParent.compareWith(this, comparator);
+ }
+
+ /**
+ * Creates and returns a new copy of the receiver.
+ */
+ AbstractDataTreeNode copy() {
+ if (children.length > 0) {
+ AbstractDataTreeNode[] childrenCopy = new AbstractDataTreeNode[children.length];
+ System.arraycopy(children, 0, childrenCopy, 0, children.length);
+ return new DataTreeNode(name, data, childrenCopy);
+ }
+ return new DataTreeNode(name, data, children);
+ }
+
+ /**
+ * Returns a new node containing a child with the given local name in
+ * addition to the receiver's current children and data.
+ *
+ * @param localName
+ * name of new child
+ * @param childNode
+ * new child node
+ */
+ DataTreeNode copyWithNewChild(String localName, DataTreeNode childNode) {
+
+ AbstractDataTreeNode[] children = this.children;
+ int left = 0;
+ int right = children.length - 1;
+ while (left <= right) {
+ int mid = (left + right) / 2;
+ int compare = localName.compareTo(children[mid].name);
+ if (compare < 0) {
+ right = mid - 1;
+ } else if (compare > 0) {
+ left = mid + 1;
+ } else {
+ throw new Error(); // it shouldn't have been here yet
+ }
+ }
+
+ AbstractDataTreeNode[] newChildren = new AbstractDataTreeNode[children.length + 1];
+ System.arraycopy(children, 0, newChildren, 0, left);
+ childNode.setName(localName);
+ newChildren[left] = childNode;
+ System.arraycopy(children, left, newChildren, left + 1, children.length - left);
+ return new DataTreeNode(this.getName(), this.getData(), newChildren);
+ }
+
+ /**
+ * Returns a new node without the specified child, but with the rest
+ * of the receiver's current children and its data.
+ *
+ * @param localName
+ * name of child to exclude
+ */
+ DataTreeNode copyWithoutChild(String localName) {
+
+ int index, newSize;
+ DataTreeNode newNode;
+ AbstractDataTreeNode children[];
+
+ index = this.indexOfChild(localName);
+ if (index == -1) {
+ newNode = (DataTreeNode) this.copy();
+ } else {
+ newSize = this.size() - 1;
+ children = new AbstractDataTreeNode[newSize];
+ newNode = new DataTreeNode(this.getName(), this.getData(), children);
+ newNode.copyChildren(0, index - 1, this, 0); //#from:to:with:startingAt:
+ newNode.copyChildren(index, newSize - 1, this, index + 1);
+ }
+ return newNode;
+ }
+
+ /**
+ * Returns an array of delta nodes representing the forward delta between
+ * the given two lists of nodes.
+ * The given nodes must all be complete nodes.
+ */
+ protected static AbstractDataTreeNode[] forwardDeltaWith(AbstractDataTreeNode[] oldNodes, AbstractDataTreeNode[] newNodes, IComparator comparer) {
+ if (oldNodes.length == 0 && newNodes.length == 0) {
+ return NO_CHILDREN;
+ }
+
+ AbstractDataTreeNode[] childDeltas = null;
+ int numChildDeltas = 0;
+ int childDeltaMax = 0;
+
+ // do a merge
+ int oldIndex = 0;
+ int newIndex = 0;
+ while (oldIndex < oldNodes.length && newIndex < newNodes.length) {
+ String oldName = oldNodes[oldIndex].name;
+ String newName = newNodes[newIndex].name;
+ int compare = oldName.compareTo(newName);
+ if (compare == 0) {
+ AbstractDataTreeNode deltaNode = forwardDeltaWithOrNullIfEqual(oldNodes[oldIndex++], newNodes[newIndex++], comparer);
+ if (deltaNode != null) {
+ if (numChildDeltas >= childDeltaMax) {
+ if (childDeltas == null)
+ childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
+ else
+ System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
+ }
+ childDeltas[numChildDeltas++] = deltaNode;
+ }
+ } else if (compare < 0) {
+ if (numChildDeltas >= childDeltaMax) {
+ if (childDeltas == null)
+ childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
+ else
+ System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
+ }
+ childDeltas[numChildDeltas++] = new DeletedNode(oldName);
+ oldIndex++;
+ } else {
+ if (numChildDeltas >= childDeltaMax) {
+ if (childDeltas == null)
+ childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
+ else
+ System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
+ }
+ childDeltas[numChildDeltas++] = newNodes[newIndex++];
+ }
+ }
+ while (oldIndex < oldNodes.length) {
+ if (numChildDeltas >= childDeltaMax) {
+ if (childDeltas == null)
+ childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
+ else
+ System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
+ }
+ childDeltas[numChildDeltas++] = new DeletedNode(oldNodes[oldIndex++].name);
+ }
+ while (newIndex < newNodes.length) {
+ if (numChildDeltas >= childDeltaMax) {
+ if (childDeltas == null)
+ childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
+ else
+ System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
+ }
+ childDeltas[numChildDeltas++] = newNodes[newIndex++];
+ }
+
+ // trim size of result
+ if (numChildDeltas == 0) {
+ return NO_CHILDREN;
+ }
+ if (numChildDeltas < childDeltaMax) {
+ System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[numChildDeltas], 0, numChildDeltas);
+ }
+ return childDeltas;
+ }
+
+ /**
+ * Returns a node representing the forward delta between
+ * the given two (complete) nodes.
+ */
+ protected AbstractDataTreeNode forwardDeltaWith(DataTreeNode other, IComparator comparer) {
+ AbstractDataTreeNode deltaNode = forwardDeltaWithOrNullIfEqual(this, other, comparer);
+ if (deltaNode == null) {
+ return new NoDataDeltaNode(name, NO_CHILDREN);
+ }
+ return deltaNode;
+ }
+
+ /**
+ * Returns a node representing the forward delta between
+ * the given two (complete) nodes, or null if the two nodes are equal.
+ * Although typed as abstract nodes, the given nodes must be complete.
+ */
+ protected static AbstractDataTreeNode forwardDeltaWithOrNullIfEqual(AbstractDataTreeNode oldNode, AbstractDataTreeNode newNode, IComparator comparer) {
+ AbstractDataTreeNode[] childDeltas = forwardDeltaWith(oldNode.children, newNode.children, comparer);
+ Object newData = newNode.getData();
+ if (comparer.compare(oldNode.getData(), newData) == 0) {
+ if (childDeltas.length == 0) {
+ return null;
+ }
+ return new NoDataDeltaNode(newNode.name, childDeltas);
+ }
+ return new DataDeltaNode(newNode.name, newData, childDeltas);
+ }
+
+ /**
+ * Returns the data for the node
+ */
+ public Object getData() {
+ return data;
+ }
+
+ /**
+ * Returns true if the receiver can carry data, false otherwise.
+ */
+ boolean hasData() {
+ return true;
+ }
+
+ /**
+ * Sets the data for the node
+ */
+ void setData(Object o) {
+ data = o;
+ }
+
+ /**
+ * Simplifies the given node, and answers its replacement.
+ */
+ AbstractDataTreeNode simplifyWithParent(IPath key, DeltaDataTree parent, IComparator comparer) {
+ /* If not in parent, can't be simplified */
+ if (!parent.includes(key)) {
+ return this;
+ }
+ /* Can't just call simplify on children since this will miss the case
+ where a child exists in the parent but does not in this.
+ See PR 1FH5RYA. */
+ DataTreeNode parentsNode = (DataTreeNode) parent.copyCompleteSubtree(key);
+ return parentsNode.forwardDeltaWith(this, comparer);
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void storeStrings(StringPool set) {
+ super.storeStrings(set);
+ //copy data for thread safety
+ Object o = data;
+ if (o instanceof IStringPoolParticipant)
+ ((IStringPoolParticipant) o).shareStrings(set);
+ }
+
+ /**
+ * Returns a unicode representation of the node. This method is used
+ * for debugging purposes only (no NLS support needed)
+ */
+ public String toString() {
+ return "a DataTreeNode(" + this.getName() + ") with " + getChildren().length + " children."; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Returns a constant describing the type of node.
+ */
+ int type() {
+ return T_COMPLETE_NODE;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import java.io.DataInput;
+import java.io.IOException;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Class used for reading a single data tree (no parents) from an input stream
+ */
+public class DataTreeReader {
+ /**
+ * Callback for reading tree data
+ */
+ protected IDataFlattener flatener;
+
+ /**
+ * The stream to read the tree from
+ */
+ protected DataInput input;
+
+ /**
+ * Creates a new DeltaTreeReader.
+ */
+ public DataTreeReader(IDataFlattener f) {
+ flatener = f;
+ }
+
+ /**
+ * Returns true if the given node type has data.
+ */
+ protected boolean hasData(int nodeType) {
+ switch (nodeType) {
+ case AbstractDataTreeNode.T_COMPLETE_NODE :
+ case AbstractDataTreeNode.T_DELTA_NODE :
+ return true;
+ case AbstractDataTreeNode.T_DELETED_NODE :
+ case AbstractDataTreeNode.T_NO_DATA_DELTA_NODE :
+ default :
+ return false;
+ }
+ }
+
+ /**
+ * Reads a node from the given input stream
+ */
+ protected AbstractDataTreeNode readNode(IPath parentPath) throws IOException {
+ /* read the node name */
+ String name = input.readUTF();
+
+ /* read the node type */
+ int nodeType = readNumber();
+
+ /* maybe read the data */
+ IPath path;
+
+ /* if not the root node */
+ if (parentPath != null) {
+ path = parentPath.append(name);
+ } else {
+ path = Path.ROOT;
+ }
+
+ Object data = null;
+ if (hasData(nodeType)) {
+
+ /* read flag indicating if the data is null */
+ int dataFlag = readNumber();
+ if (dataFlag != 0) {
+ data = flatener.readData(path, input);
+ }
+ }
+
+ /* read the number of children */
+ int childCount = readNumber();
+
+ /* read the children */
+ AbstractDataTreeNode[] children;
+ if (childCount == 0) {
+ children = AbstractDataTreeNode.NO_CHILDREN;
+ } else {
+ children = new AbstractDataTreeNode[childCount];
+ for (int i = 0; i < childCount; i++) {
+ children[i] = readNode(path);
+ }
+ }
+
+ /* create the appropriate node */
+ switch (nodeType) {
+ case AbstractDataTreeNode.T_COMPLETE_NODE :
+ return new DataTreeNode(name, data, children);
+ case AbstractDataTreeNode.T_DELTA_NODE :
+ return new DataDeltaNode(name, data, children);
+ case AbstractDataTreeNode.T_DELETED_NODE :
+ return new DeletedNode(name);
+ case AbstractDataTreeNode.T_NO_DATA_DELTA_NODE :
+ return new NoDataDeltaNode(name, children);
+ default :
+ Assert.isTrue(false, Messages.dtree_switchError);
+ return null;
+ }
+ }
+
+ /**
+ * Reads an integer stored in compact format. Numbers between
+ * 0 and 254 inclusive occupy 1 byte; other numbers occupy 5 bytes,
+ * the first byte being 0xff and the next 4 bytes being the standard
+ * representation of an int.
+ */
+ protected int readNumber() throws IOException {
+ byte b = input.readByte();
+ int number = (b & 0xff); // not a no-op! converts unsigned byte to int
+
+ if (number == 0xff) { // magic escape value
+ number = input.readInt();
+ }
+ return number;
+ }
+
+ /**
+ * Reads a DeltaDataTree from the given input stream
+ */
+ public DeltaDataTree readTree(DeltaDataTree parent, DataInput input) throws IOException {
+ this.input = input;
+ AbstractDataTreeNode root = readNode(Path.ROOT);
+ return new DeltaDataTree(root, parent);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeWriter.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Class for writing a single data tree (no parents) to an output stream.
+ */
+public class DataTreeWriter {
+ /**
+ * Callback for serializing tree data
+ */
+ protected IDataFlattener flatener;
+
+ /**
+ * The stream to write output to
+ */
+ protected DataOutput output;
+
+ /**
+ * Constant representing infinite recursion depth
+ */
+ public static final int D_INFINITE = -1;
+
+ /**
+ * Creates a new DeltaTreeWriter.
+ */
+ public DataTreeWriter(IDataFlattener f) {
+ flatener = f;
+ }
+
+ /**
+ * Writes the subtree rooted at the given node.
+ * @param node The subtree to write.
+ * @param path The path of the current node.
+ * @param depth The depth of the subtree to write.
+ */
+ protected void writeNode(AbstractDataTreeNode node, IPath path, int depth) throws IOException {
+ int type = node.type();
+
+ /* write the node name */
+ String name = node.getName();
+ if (name == null) {
+ name = ""; //$NON-NLS-1$
+ }
+ output.writeUTF(name);
+
+ /* write the node type */
+ writeNumber(type);
+
+ /* maybe write the data */
+ if (node.hasData()) {
+ Object data = node.getData();
+
+ /**
+ * Write a flag indicating whether or not the data field is null.
+ * Zero means data is null, non-zero means data is present
+ */
+ if (data == null) {
+ writeNumber(0);
+ } else {
+ writeNumber(1);
+ flatener.writeData(path, node.getData(), output);
+ }
+
+ }
+
+ /* maybe write the children */
+ if (depth > 0 || depth == D_INFINITE) {
+ AbstractDataTreeNode[] children = node.getChildren();
+
+ /* write the number of children */
+ writeNumber(children.length);
+
+ /* write the children */
+ int newDepth = (depth == D_INFINITE) ? D_INFINITE : depth - 1;
+ for (int i = 0, imax = children.length; i < imax; i++) {
+ writeNode(children[i], path.append(children[i].getName()), newDepth);
+ }
+ } else {
+ /* write the number of children */
+ writeNumber(0);
+ }
+ }
+
+ /**
+ * Writes an integer in a compact format biased towards
+ * small non-negative numbers. Numbers between
+ * 0 and 254 inclusive occupy 1 byte; other numbers occupy 5 bytes.
+ */
+ protected void writeNumber(int number) throws IOException {
+ if (number >= 0 && number < 0xff) {
+ output.writeByte(number);
+ } else {
+ output.writeByte(0xff);
+ output.writeInt(number);
+ }
+ }
+
+ /**
+ * Writes a single node to the output. Does not recurse
+ * on child nodes, and does not write the number of children.
+ */
+ protected void writeSingleNode(AbstractDataTreeNode node, IPath path) throws IOException {
+ /* write the node name */
+ String name = node.getName();
+ if (name == null) {
+ name = ""; //$NON-NLS-1$
+ }
+ output.writeUTF(name);
+
+ /* write the node type */
+ writeNumber(node.type());
+
+ /* maybe write the data */
+ if (node.hasData()) {
+ Object data = node.getData();
+
+ /**
+ * Write a flag indicating whether or not the data field is null.
+ * Zero means data is null, non-zero means data is present
+ */
+ if (data == null) {
+ writeNumber(0);
+ } else {
+ writeNumber(1);
+ flatener.writeData(path, node.getData(), output);
+ }
+ }
+ }
+
+ /**
+ * Writes the given AbstractDataTree to the given stream. This
+ * writes a single DataTree or DeltaDataTree, ignoring parent
+ * trees.
+ *
+ * @param path Only writes data for the subtree rooted at the given path, and
+ * for all nodes directly between the root and the subtree.
+ * @param depth In the subtree rooted at the given path,
+ * only write up to this depth. A depth of infinity is given
+ * by the constant D_INFINITE.
+ */
+ public void writeTree(AbstractDataTree tree, IPath path, int depth, DataOutput output) throws IOException {
+ this.output = output;
+ /* tunnel down relevant path */
+ AbstractDataTreeNode node = tree.getRootNode();
+ IPath currentPath = Path.ROOT;
+ String[] segments = path.segments();
+ for (int i = 0; i < segments.length; i++) {
+ String nextSegment = segments[i];
+
+ /* write this node to the output */
+ writeSingleNode(node, currentPath);
+
+ currentPath = currentPath.append(nextSegment);
+ node = node.childAtOrNull(nextSegment);
+
+ /* write the number of children for this node */
+ if (node != null) {
+ writeNumber(1);
+ } else {
+ /* can't navigate down the path, just give up with what we have so far */
+ writeNumber(0);
+ return;
+ }
+ }
+
+ Assert.isTrue(currentPath.equals(path), "dtree.navigationError"); //$NON-NLS-1$
+
+ /* recursively write the subtree we're interested in */
+ writeNode(node, path, depth);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DeletedNode.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * A <code>DeletedNode</code> represents a node that has been deleted in a
+ * <code>DeltaDataTree</code>. It is a node that existed in the parent tree,
+ * but no longer exists in the current delta tree. It has no children or data.
+ */
+public class DeletedNode extends AbstractDataTreeNode {
+
+ /**
+ * Creates a new tree with the given name
+ */
+ DeletedNode(String localName) {
+ super(localName, NO_CHILDREN);
+ }
+
+ /**
+ * @see AbstractDataTreeNode#asBackwardDelta(DeltaDataTree, DeltaDataTree, IPath)
+ */
+ AbstractDataTreeNode asBackwardDelta(DeltaDataTree myTree, DeltaDataTree parentTree, IPath key) {
+ if (parentTree.includes(key))
+ return parentTree.copyCompleteSubtree(key);
+ return this;
+ }
+
+ /**
+ * Returns the child with the given local name
+ */
+ AbstractDataTreeNode childAt(String localName) {
+ /* deleted nodes do not have children */
+ throw new ObjectNotFoundException(NLS.bind(Messages.dtree_missingChild, localName));
+ }
+
+ /**
+ * Returns the child with the given local name
+ */
+ AbstractDataTreeNode childAtOrNull(String localName) {
+ /* deleted nodes do not have children */
+ return null;
+ }
+
+ AbstractDataTreeNode compareWithParent(IPath key, DeltaDataTree parent, IComparator comparator) {
+ /**
+ * Just because there is a deleted node, it doesn't mean there must
+ * be a corresponding node in the parent. Deleted nodes can live
+ * in isolation.
+ */
+ if (parent.includes(key))
+ return convertToRemovedComparisonNode(parent.copyCompleteSubtree(key), NodeComparison.K_REMOVED);
+ // Node doesn't exist in either tree. Return an empty comparison.
+ // Empty comparisons are omitted from the delta.
+ return new DataTreeNode(key.lastSegment(), new NodeComparison(null, null, 0, 0));
+ }
+
+ /**
+ * Creates and returns a new copy of the receiver. Makes a deep copy of
+ * children, but a shallow copy of name and data.
+ */
+ AbstractDataTreeNode copy() {
+ return new DeletedNode(name);
+ }
+
+ /**
+ * Returns true if the receiver represents a deleted node, false otherwise.
+ */
+ boolean isDeleted() {
+ return true;
+ }
+
+ /**
+ * Simplifies the given node, and returns its replacement.
+ */
+ AbstractDataTreeNode simplifyWithParent(IPath key, DeltaDataTree parent, IComparator comparer) {
+ if (parent.includes(key))
+ return this;
+ return new NoDataDeltaNode(name);
+ }
+
+ /**
+ * Returns the number of children of the receiver
+ */
+ int size() {
+ /* deleted nodes have no children */
+ return 0;
+ }
+
+ /**
+ * Return a unicode representation of the node. This method
+ * is used for debugging purposes only (no NLS please)
+ */
+ public String toString() {
+ return "a DeletedNode(" + this.getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Returns a string describing the type of node.
+ */
+ int type() {
+ return T_DELETED_NODE;
+ }
+
+ AbstractDataTreeNode childAtIgnoreCase(String localName) {
+ /* deleted nodes do not have children */
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DeltaDataTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,959 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.StringPool;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Externally, a <code>DeltaDataTree</code> appears to have the same content as
+ * a standard data tree. Internally, the delta tree may be complete, or it may
+ * just indicate the changes between itself and its parent.
+ *
+ * <p>Nodes that exist in the parent but do not exist in the delta, are represented
+ * as instances of <code>DeletedNode</code>. Nodes that are identical in the parent
+ * and the delta, but have differences in their subtrees, are represented as
+ * instances of <code>NoDataDeltaNode</code> in the delta tree. Nodes that differ
+ * between parent and delta are instances of <code>DataDeltaNode</code>. However,
+ * the <code>DataDeltaNode</code> only contains the children whose subtrees differ
+ * between parent and delta.
+ *
+ * A delta tree algebra is used to manipulate sets of delta trees. Given two trees,
+ * one can obtain the delta between the two using the method
+ * <code>forwardDeltaWith(aTree)</code>. Given a tree and a delta, one can assemble
+ * the complete tree that the delta represents using the method <code>
+ * assembleWithForwardDelta</code>. Refer to the public API methods of this class
+ * for further details.
+ */
+
+public class DeltaDataTree extends AbstractDataTree {
+ private AbstractDataTreeNode rootNode;
+ private DeltaDataTree parent;
+
+ /**
+ * Creates a new empty tree.
+ */
+ public DeltaDataTree() {
+ this.empty();
+ }
+
+ /**
+ * Creates a new tree.
+ * @param rootNode
+ * root node of new tree.
+ */
+ public DeltaDataTree(AbstractDataTreeNode rootNode) {
+ this.rootNode = rootNode;
+ this.parent = null;
+ }
+
+ protected DeltaDataTree(AbstractDataTreeNode rootNode, DeltaDataTree parent) {
+ this.rootNode = rootNode;
+ this.parent = parent;
+ }
+
+ /**
+ * Adds a child to the tree.
+ *
+ * @param parentKey parent for new child.
+ * @param localName name of child.
+ * @param childNode child node.
+ */
+ protected void addChild(IPath parentKey, String localName, AbstractDataTreeNode childNode) {
+ if (!includes(parentKey))
+ handleNotFound(parentKey);
+ childNode.setName(localName);
+ this.assembleNode(parentKey, new NoDataDeltaNode(parentKey.lastSegment(), childNode));
+ }
+
+ /**
+ * Returns the tree as a backward delta. If the delta is applied to the tree it
+ * will produce its parent. The receiver must have a forward
+ * delta representation. I.e.: Call the receiver's parent A,
+ * and the receiver B. The receiver's representation is A->B.
+ * Returns the delta A<-B. The result is equivalent to A, but has B as its parent.
+ */
+ DeltaDataTree asBackwardDelta() {
+ if (getParent() == null)
+ return newEmptyDeltaTree();
+ return new DeltaDataTree(getRootNode().asBackwardDelta(this, getParent(), rootKey()), this);
+ }
+
+ /**
+ * This method can only be called on a comparison tree created
+ * using DeltaDataTree.compareWith(). This method flips the orientation
+ * of the given comparison tree, so that additions become removals,
+ * and vice-versa. This method destructively changes the tree
+ * as opposed to making a copy.
+ */
+ public DeltaDataTree asReverseComparisonTree(IComparator comparator) {
+ /* don't reverse the root node if it's the absolute root (name==null) */
+ if (rootNode.getName() == null) {
+ AbstractDataTreeNode[] children = rootNode.getChildren();
+ int nextChild = 0;
+ for (int i = 0; i < children.length; i++) {
+ AbstractDataTreeNode newChild = children[i].asReverseComparisonNode(comparator);
+ if (newChild != null) {
+ children[nextChild++] = newChild;
+ }
+ }
+
+ if (nextChild < children.length) {
+ AbstractDataTreeNode[] newChildren = new AbstractDataTreeNode[nextChild];
+ System.arraycopy(children, 0, newChildren, 0, nextChild);
+ rootNode.setChildren(newChildren);
+ }
+ } else {
+ rootNode.asReverseComparisonNode(comparator);
+ }
+ return this;
+ }
+
+ /**
+ * Replaces a node in the tree with the result of assembling the node
+ * with the given delta node (which represents a forward delta on
+ * the existing node).
+ *
+ * @param key key of the node to replace.
+ * @param deltaNode delta node to use to assemble the new node.
+ */
+ protected void assembleNode(IPath key, AbstractDataTreeNode deltaNode) {
+ rootNode = rootNode.assembleWith(deltaNode, key, 0);
+ }
+
+ /**
+ * Assembles the receiver with the given delta tree and answer
+ * the resulting, mutable source tree. The given delta tree must be a
+ * forward delta based on the receiver (i.e. missing information is taken from
+ * the receiver). This operation is used to coalesce delta trees.
+ *
+ * <p>In detail, suppose that c is a forward delta over source tree a.
+ * Let d := a assembleWithForwardDelta: c.
+ * d has the same content as c, and is represented as a delta tree
+ * whose parent is the same as a's parent.
+ *
+ * <p>In general, if c is represented as a chain of deltas of length n,
+ * then d is represented as a chain of length n-1.
+ *
+ * <p>So if a is a complete tree (i.e., has no parent, length=0), then d
+ * will be a complete tree too.
+ *
+ * <p>Corollary: (a assembleWithForwardDelta: (a forwardDeltaWith: b)) = b
+ */
+ public DeltaDataTree assembleWithForwardDelta(DeltaDataTree deltaTree) {
+ return new DeltaDataTree(getRootNode().assembleWith(deltaTree.getRootNode()), this);
+ }
+
+ /**
+ * Compares this tree with another tree, starting from the given path. The
+ * given path will be the root node of the returned tree. Both this
+ * tree and other tree must contain the given path.
+ */
+ protected DeltaDataTree basicCompare(DeltaDataTree other, IComparator comparator, IPath path) {
+ DeltaDataTree newTree;
+ if (this == other) {
+ newTree = new DeltaDataTree();
+ newTree.setData(Path.ROOT, new NodeComparison(null, null, 0, 0));
+ } else if (other.hasAncestor(this)) {
+ AbstractDataTreeNode assembled = other.searchNodeAt(path);
+ DeltaDataTree tree = other;
+
+ /* Iterate through the receiver's ancestors until the receiver is reached */
+ while ((tree = tree.getParent()) != this) {
+ //ancestor may not contain the given path
+ AbstractDataTreeNode treeNode = tree.searchNodeAt(path);
+ if (treeNode != null) {
+ assembled = treeNode.assembleWith(assembled);
+ }
+ }
+ AbstractDataTreeNode comparedRoot = assembled.compareWithParent(path, this, comparator);
+ newTree = new DeltaDataTree(comparedRoot);
+ } else if (this.hasAncestor(other)) {
+ AbstractDataTreeNode assembled = this.asBackwardDelta().searchNodeAt(path);
+ DeltaDataTree tree = this;
+
+ /* Iterate through the receiver's ancestors until the other tree is reached */
+ while ((tree = tree.getParent()) != other) {
+ assembled = assembled.assembleWith(tree.asBackwardDelta().searchNodeAt(path));
+ }
+ AbstractDataTreeNode comparedRoot = assembled.compareWithParent(path, this, comparator);
+ newTree = new DeltaDataTree(comparedRoot);
+ } else {
+ //revert to naive comparison
+ DataTreeNode thisCompleteRoot = (DataTreeNode) this.copyCompleteSubtree(path);
+ DataTreeNode otherCompleteRoot = (DataTreeNode) other.copyCompleteSubtree(path);
+ AbstractDataTreeNode comparedRoot = thisCompleteRoot.compareWith(otherCompleteRoot, comparator);
+ newTree = new DeltaDataTree(comparedRoot);
+ }
+ newTree.immutable();
+ return newTree;
+ }
+
+ /**
+ * Collapses this tree so that the given ancestor becomes its
+ * immediate parent. Afterwards, this tree will still have exactly the
+ * same contents, but its internal structure will be compressed.
+ *
+ * <p> This operation should be used to collapse chains of
+ * delta trees that don't contain interesting intermediate states.
+ *
+ * <p>This is a destructive operation, since it modifies the structure of this
+ * tree instance. This tree must be immutable at the start of this operation,
+ * and will be immutable afterwards.
+ * @return this tree.
+ */
+ public DeltaDataTree collapseTo(DeltaDataTree collapseTo, IComparator comparator) {
+ if (this == collapseTo || getParent() == collapseTo) {
+ //already collapsed
+ return this;
+ }
+ //collapse my tree to be a forward delta of the parent's tree.
+ //c will have the same content as this tree, but its parent will be "parent".
+ DeltaDataTree c = collapseTo.forwardDeltaWith(this, comparator);
+
+ //update my internal root node and parent pointers.
+ this.parent = collapseTo;
+ this.rootNode = c.rootNode;
+ return this;
+ }
+
+ /**
+ * Returns a DeltaDataTree that describes the differences between
+ * this tree and "other" tree. Each node of the returned tree
+ * will contain a NodeComparison object that describes the differences
+ * between the two trees.
+ */
+ public DeltaDataTree compareWith(DeltaDataTree other, IComparator comparator) {
+
+ DeltaDataTree newTree;
+ if (this == other) {
+ newTree = new DeltaDataTree();
+ newTree.setData(Path.ROOT, new NodeComparison(null, null, 0, 0));
+ } else if (other.hasAncestor(this)) {
+
+ AbstractDataTreeNode assembled = other.getRootNode();
+ DeltaDataTree tree = other;
+
+ /* Iterate through the receiver's ancestors until the receiver is reached */
+ while ((tree = tree.getParent()) != this) {
+ assembled = tree.getRootNode().assembleWith(assembled);
+ }
+ AbstractDataTreeNode comparedRoot = assembled.compareWithParent(rootKey(), this, comparator);
+ newTree = new DeltaDataTree(comparedRoot);
+ } else if (this.hasAncestor(other)) {
+ AbstractDataTreeNode assembled = this.asBackwardDelta().getRootNode();
+ DeltaDataTree tree = this;
+
+ /* Iterate through the receiver's ancestors until the other tree is reached */
+ while ((tree = tree.getParent()) != other) {
+ assembled = assembled.assembleWith(tree.asBackwardDelta().getRootNode());
+ }
+ AbstractDataTreeNode comparedRoot = assembled.compareWithParent(rootKey(), this, comparator);
+ newTree = new DeltaDataTree(comparedRoot);
+ } else {
+ //revert to naive comparison if trees have no common ancestry
+ DataTreeNode thisCompleteRoot = (DataTreeNode) this.copyCompleteSubtree(rootKey());
+ DataTreeNode otherCompleteRoot = (DataTreeNode) other.copyCompleteSubtree(rootKey());
+ AbstractDataTreeNode comparedRoot = thisCompleteRoot.compareWith(otherCompleteRoot, comparator);
+ newTree = new DeltaDataTree(comparedRoot);
+ }
+ newTree.immutable();
+ return newTree;
+ }
+
+ /**
+ * Compares this tree with another tree, starting from the given path. The
+ * given path will be the root node of the returned tree.
+ */
+ public DeltaDataTree compareWith(DeltaDataTree other, IComparator comparator, IPath path) {
+ /* need to figure out if trees really contain the given path */
+ if (this.includes(path)) {
+ if (other.includes(path))
+ return basicCompare(other, comparator, path);
+ /* only exists in this tree */
+ return new DeltaDataTree(AbstractDataTreeNode.convertToRemovedComparisonNode(this.copyCompleteSubtree(path), comparator.compare(this.getData(path), null)));
+ }
+ if (other.includes(path))
+ /* only exists in other tree */
+ return new DeltaDataTree(AbstractDataTreeNode.convertToAddedComparisonNode(other.copyCompleteSubtree(path), comparator.compare(null, other.getData(path))));
+ /* doesn't exist in either tree */
+ return DeltaDataTree.createEmptyDelta();
+ }
+
+ /**
+ * Returns a copy of the tree which shares its instance variables.
+ */
+ protected AbstractDataTree copy() {
+ return new DeltaDataTree(rootNode, parent);
+ }
+
+ /**
+ * Returns a complete node containing the contents of a subtree of the tree.
+ *
+ * @param key
+ * key of subtree to copy
+ */
+ public AbstractDataTreeNode copyCompleteSubtree(IPath key) {
+ AbstractDataTreeNode node = searchNodeAt(key);
+ if (node == null) {
+ handleNotFound(key);
+ return null;
+ }
+ if (node.isDelta())
+ return naiveCopyCompleteSubtree(key);
+ //copy the node in case the user wants to hammer the subtree name
+ return node.copy();
+ }
+
+ /**
+ * @see AbstractDataTree#createChild(IPath, String)
+ */
+ public void createChild(IPath parentKey, String localName) {
+ createChild(parentKey, localName, null);
+ }
+
+ /**
+ * @see AbstractDataTree#createChild(IPath, String, Object)
+ */
+ public void createChild(IPath parentKey, String localName, Object data) {
+ if (isImmutable())
+ handleImmutableTree();
+ addChild(parentKey, localName, new DataTreeNode(localName, data));
+ }
+
+ /**
+ * Returns a delta data tree that represents an empty delta.
+ * (i.e. it represents a delta on another (unspecified) tree,
+ * but introduces no changes).
+ */
+ static DeltaDataTree createEmptyDelta() {
+
+ DeltaDataTree newTree = new DeltaDataTree();
+ newTree.emptyDelta();
+ return newTree;
+ }
+
+ /**
+ * Creates and returns an instance of the receiver.
+ * @see AbstractDataTree#createInstance()
+ */
+ protected AbstractDataTree createInstance() {
+ return new DeltaDataTree();
+ }
+
+ /**
+ * @see AbstractDataTree#createSubtree(IPath, AbstractDataTreeNode)
+ */
+ public void createSubtree(IPath key, AbstractDataTreeNode node) {
+ if (isImmutable())
+ handleImmutableTree();
+ if (key.isRoot()) {
+ setParent(null);
+ setRootNode(node);
+ } else {
+ addChild(key.removeLastSegments(1), key.lastSegment(), node);
+ }
+ }
+
+ /**
+ * @see AbstractDataTree#deleteChild(IPath, String)
+ */
+ public void deleteChild(IPath parentKey, String localName) {
+ if (isImmutable())
+ handleImmutableTree();
+ /* If the child does not exist */
+ IPath childKey = parentKey.append(localName);
+ if (!includes(childKey))
+ handleNotFound(childKey);
+ assembleNode(parentKey, new NoDataDeltaNode(parentKey.lastSegment(), new DeletedNode(localName)));
+ }
+
+ /**
+ * Initializes the receiver so that it is a complete, empty tree.
+ * @see AbstractDataTree#empty()
+ */
+ public void empty() {
+ rootNode = new DataTreeNode(null, null);
+ parent = null;
+ }
+
+ /**
+ * Initializes the receiver so that it represents an empty delta.
+ * (i.e. it represents a delta on another (unspecified) tree,
+ * it introduces no changes). The parent is left unchanged.
+ */
+ void emptyDelta() {
+ rootNode = new NoDataDeltaNode(null);
+ }
+
+ /**
+ * Returns a node of the tree if it is present, otherwise returns null
+ *
+ * @param key
+ * key of node to find
+ */
+ public AbstractDataTreeNode findNodeAt(IPath key) {
+ AbstractDataTreeNode node = rootNode;
+ int segmentCount = key.segmentCount();
+ for (int i = 0; i < segmentCount; i++) {
+ node = node.childAtOrNull(key.segment(i));
+ if (node == null)
+ return null;
+ }
+ return node;
+ }
+
+ /**
+ * Returns a forward delta between the receiver and the given source tree,
+ * using the given comparer to compare data objects.
+ * The result describes the changes which, if assembled with the receiver,
+ * will produce the given source tree.
+ * In more detail, let c = a.forwardDeltaWith(b).
+ * c has the same content as b, but is represented as a delta tree with a as the parent.
+ * Also, c is immutable.
+ *
+ * There is no requirement that a and b be related, although it is usually more
+ * efficient if they are. The node keys are used as the basis of correlation
+ * between trees.
+ *
+ * Note that if b is already represented as a delta over a,
+ * then c will have the same internal structure as b.
+ * Thus the very common case of previous forwardDeltaWith: current
+ * is actually very fast when current is a modification of previous.
+ *
+ * @param sourceTree second delta tree to create a delta between
+ * @param comparer the comparer used to compare data objects
+ * @return the new delta
+ */
+ public DeltaDataTree forwardDeltaWith(DeltaDataTree sourceTree, IComparator comparer) {
+ DeltaDataTree newTree;
+ if (this == sourceTree) {
+ newTree = this.newEmptyDeltaTree();
+ } else if (sourceTree.hasAncestor(this)) {
+ AbstractDataTreeNode assembled = sourceTree.getRootNode();
+ DeltaDataTree treeParent = sourceTree;
+
+ /* Iterate through the sourceTree's ancestors until the receiver is reached */
+ while ((treeParent = treeParent.getParent()) != this) {
+ assembled = treeParent.getRootNode().assembleWith(assembled);
+ }
+ newTree = new DeltaDataTree(assembled, this);
+ newTree.simplify(comparer);
+ } else if (this.hasAncestor(sourceTree)) {
+ //create the delta backwards and then reverse it
+ newTree = sourceTree.forwardDeltaWith(this, comparer);
+ newTree = newTree.asBackwardDelta();
+ } else {
+ DataTreeNode thisCompleteRoot = (DataTreeNode) this.copyCompleteSubtree(rootKey());
+ DataTreeNode sourceTreeCompleteRoot = (DataTreeNode) sourceTree.copyCompleteSubtree(rootKey());
+ AbstractDataTreeNode deltaRoot = thisCompleteRoot.forwardDeltaWith(sourceTreeCompleteRoot, comparer);
+ newTree = new DeltaDataTree(deltaRoot, this);
+ }
+ newTree.immutable();
+ return newTree;
+ }
+
+ /**
+ * @see AbstractDataTree#getChildCount(IPath)
+ */
+ public int getChildCount(IPath parentKey) {
+ return getChildNodes(parentKey).length;
+ }
+
+ /**
+ * Returns the child nodes of a node in the tree.
+ */
+ protected AbstractDataTreeNode[] getChildNodes(IPath parentKey) {
+
+ /* Algorithm:
+ * for each delta in chain (going backwards),
+ * get list of child nodes, if any in delta
+ * assemble with previously seen list, if any
+ * break when complete tree found,
+ * report error if parent is missing or has been deleted
+ */
+
+ AbstractDataTreeNode[] childNodes = null;
+ int keyLength = parentKey.segmentCount();
+ for (DeltaDataTree tree = this; tree != null; tree = tree.parent) {
+ AbstractDataTreeNode node = tree.rootNode;
+ boolean complete = !node.isDelta();
+ for (int i = 0; i < keyLength; i++) {
+ node = node.childAtOrNull(parentKey.segment(i));
+ if (node == null) {
+ break;
+ }
+ if (!node.isDelta()) {
+ complete = true;
+ }
+ }
+ if (node != null) {
+ if (node.isDeleted()) {
+ break;
+ }
+ if (childNodes == null) {
+ childNodes = node.children;
+ } else {
+ // Be sure to assemble(old, new) rather than (new, old).
+ // Keep deleted nodes if we haven't encountered the complete node yet.
+ childNodes = AbstractDataTreeNode.assembleWith(node.children, childNodes, !complete);
+ }
+ }
+ if (complete) {
+ if (childNodes != null) {
+ return childNodes;
+ }
+ // Not found, but complete node encountered, so should not check parent tree.
+ break;
+ }
+ }
+ if (childNodes != null) {
+ // Some deltas carry info about children, but there is
+ // no complete node against which they describe deltas.
+ Assert.isTrue(false, Messages.dtree_malformedTree);
+ }
+
+ // Node is missing or has been deleted.
+ handleNotFound(parentKey);
+ return null;//should not get here
+ }
+
+ /**
+ * @see AbstractDataTree#getChildren(IPath)
+ */
+ public IPath[] getChildren(IPath parentKey) {
+ AbstractDataTreeNode[] childNodes = getChildNodes(parentKey);
+ int len = childNodes.length;
+ if (len == 0)
+ return NO_CHILDREN;
+ IPath[] answer = new IPath[len];
+ for (int i = 0; i < len; ++i)
+ answer[i] = parentKey.append(childNodes[i].name);
+ return answer;
+ }
+
+ /**
+ * Returns the data at a node of the tree.
+ *
+ * @param key
+ * key of node for which to return data.
+ */
+ public Object getData(IPath key) {
+
+ /* Algorithm:
+ * for each delta in chain (going backwards),
+ * get node, if any in delta
+ * if it carries data, return it
+ * break when complete tree found
+ * report error if node is missing or has been deleted
+ */
+
+ int keyLength = key.segmentCount();
+ for (DeltaDataTree tree = this; tree != null; tree = tree.parent) {
+ AbstractDataTreeNode node = tree.rootNode;
+ boolean complete = !node.isDelta();
+ for (int i = 0; i < keyLength; i++) {
+ node = node.childAtOrNull(key.segment(i));
+ if (node == null) {
+ break;
+ }
+ if (!node.isDelta()) {
+ complete = true;
+ }
+ }
+ if (node != null) {
+ if (node.hasData()) {
+ return node.getData();
+ } else if (node.isDeleted()) {
+ break;
+ }
+ }
+ if (complete) {
+ // Not found, but complete node encountered, so should not check parent tree.
+ break;
+ }
+ }
+ handleNotFound(key);
+ return null; //can't get here
+ }
+
+ /**
+ * @see AbstractDataTree#getNameOfChild(IPath, int)
+ */
+ public String getNameOfChild(IPath parentKey, int index) {
+ AbstractDataTreeNode[] childNodes = getChildNodes(parentKey);
+ return childNodes[index].name;
+ }
+
+ /**
+ * Returns the local names for the children of a node of the tree.
+ *
+ * @see AbstractDataTree#getNamesOfChildren(IPath)
+ */
+ public String[] getNamesOfChildren(IPath parentKey) {
+ AbstractDataTreeNode[] childNodes = getChildNodes(parentKey);
+ int len = childNodes.length;
+ String[] namesOfChildren = new String[len];
+ for (int i = 0; i < len; ++i)
+ namesOfChildren[i] = childNodes[i].name;
+ return namesOfChildren;
+ }
+
+ /**
+ * Returns the parent of the tree.
+ */
+ public DeltaDataTree getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns the root node of the tree.
+ */
+ protected AbstractDataTreeNode getRootNode() {
+ return rootNode;
+ }
+
+ /**
+ * Returns true if the receiver's parent has the specified ancestor
+ *
+ * @param ancestor the ancestor in question
+ */
+ protected boolean hasAncestor(DeltaDataTree ancestor) {
+ DeltaDataTree myParent = this;
+ while ((myParent = myParent.getParent()) != null) {
+ if (myParent == ancestor) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the receiver includes a node with
+ * the given key, false otherwise.
+ */
+ public boolean includes(IPath key) {
+ return searchNodeAt(key) != null;
+ }
+
+ public boolean isEmptyDelta() {
+ return rootNode.getChildren().length == 0;
+ }
+
+ /**
+ * Returns an object containing:
+ * - the node key
+ * - a flag indicating whether the specified node was found
+ * - the data for the node, if it was found
+ *
+ * @param key key of node for which we want to retrieve data.
+ */
+ public DataTreeLookup lookup(IPath key) {
+ int keyLength = key.segmentCount();
+ for (DeltaDataTree tree = this; tree != null; tree = tree.parent) {
+ AbstractDataTreeNode node = tree.rootNode;
+ boolean complete = !node.isDelta();
+ for (int i = 0; i < keyLength; i++) {
+ node = node.childAtOrNull(key.segment(i));
+ if (node == null) {
+ break;
+ }
+ complete |= !node.isDelta();
+ }
+ if (node != null) {
+ if (node.hasData()) {
+ return DataTreeLookup.newLookup(key, true, node.getData(), tree == this);
+ } else if (node.isDeleted()) {
+ break;
+ }
+ }
+ if (complete) {
+ // Not found, but complete node encountered, so should not check parent tree.
+ break;
+ }
+ }
+ return DataTreeLookup.newLookup(key, false, null);
+ }
+
+ /**
+ * Returns an object containing:
+ * - the node key
+ * - a flag indicating whether the specified node was found
+ * - the data for the node, if it was found
+ *
+ * This is a case-insensitive variant of the <code>lookup</code>
+ * method.
+ *
+ * @param key key of node for which we want to retrieve data.
+ */
+ public DataTreeLookup lookupIgnoreCase(IPath key) {
+ int keyLength = key.segmentCount();
+ for (DeltaDataTree tree = this; tree != null; tree = tree.parent) {
+ AbstractDataTreeNode node = tree.rootNode;
+ boolean complete = !node.isDelta();
+ for (int i = 0; i < keyLength; i++) {
+ node = node.childAtIgnoreCase(key.segment(i));
+ if (node == null) {
+ break;
+ }
+ complete |= !node.isDelta();
+ }
+ if (node != null) {
+ if (node.hasData()) {
+ return DataTreeLookup.newLookup(key, true, node.getData(), tree == this);
+ } else if (node.isDeleted()) {
+ break;
+ }
+ }
+ if (complete) {
+ // Not found, but complete node encountered, so should not check parent tree.
+ break;
+ }
+ }
+ return DataTreeLookup.newLookup(key, false, null);
+ }
+
+ /**
+ * Converts this tree's representation to be a complete tree, not a delta.
+ * This disconnects this tree from its parents.
+ * The parent trees are unaffected.
+ */
+ public void makeComplete() {
+ AbstractDataTreeNode assembled = getRootNode();
+ DeltaDataTree myParent = getParent();
+ while (myParent != null) {
+ assembled = myParent.getRootNode().assembleWith(assembled);
+ myParent = myParent.getParent();
+ }
+ setRootNode(assembled);
+ setParent(null);
+ }
+
+ /**
+ * Returns a complete node containing the contents of the subtree
+ * rooted at <code>key</code> in the receiver. Uses the public API.
+ *
+ * @param key
+ * key of subtree whose contents we want to copy.
+ */
+ protected AbstractDataTreeNode naiveCopyCompleteSubtree(IPath key) {
+ String[] childNames = getNamesOfChildren(key);
+ int numChildren = childNames.length;
+ AbstractDataTreeNode[] childNodes;
+ if (numChildren == 0) {
+ childNodes = AbstractDataTreeNode.NO_CHILDREN;
+ } else {
+ childNodes = new AbstractDataTreeNode[numChildren];
+ /* do for each child */
+ for (int i = numChildren; --i >= 0;) {
+ childNodes[i] = copyCompleteSubtree(key.append(childNames[i]));
+ }
+ }
+ return new DataTreeNode(key.lastSegment(), getData(key), childNodes);
+ }
+
+ /**
+ * Returns a new tree which represents an empty, mutable delta on the
+ * receiver. It is not possible to obtain a new delta tree if the receiver is
+ * not immutable, as subsequent changes to the receiver would affect the
+ * resulting delta.
+ */
+ public DeltaDataTree newEmptyDeltaTree() {
+ if (!isImmutable())
+ throw new IllegalArgumentException(Messages.dtree_notImmutable);
+ DeltaDataTree newTree = (DeltaDataTree) this.copy();
+ newTree.setParent(this);
+ newTree.emptyDelta();
+ return newTree;
+ }
+
+ /**
+ * Makes the receiver the root tree in the list of trees on which it is based.
+ * The receiver's representation becomes a complete tree, while its parents'
+ * representations become backward deltas based on the receiver.
+ * It is not possible to re-root a source tree that is not immutable, as this
+ * would require that its parents be expressed as deltas on a source tree
+ * which could still change.
+ *
+ * @exception RuntimeException
+ * receiver is not immutable
+ */
+ public DeltaDataTree reroot() {
+ /* self mutex critical region */
+ reroot(this);
+ return this;
+ }
+
+ /**
+ * Makes the given source tree the root tree in the list of trees on which it is based.
+ * The source tree's representation becomes a complete tree, while its parents'
+ * representations become backward deltas based on the source tree.
+ * It is not possible to re-root a source tree that is not immutable, as this
+ * would require that its parents be expressed as deltas on a source tree
+ * which could still change.
+ *
+ * @param sourceTree
+ * source tree to set as the new root
+ * @exception RuntimeException
+ * sourceTree is not immutable
+ */
+ protected void reroot(DeltaDataTree sourceTree) {
+ if (!sourceTree.isImmutable())
+ handleImmutableTree();
+ DeltaDataTree sourceParent = sourceTree.getParent();
+ if (sourceParent == null)
+ return;
+ this.reroot(sourceParent);
+ DeltaDataTree backwardDelta = sourceTree.asBackwardDelta();
+ DeltaDataTree complete = sourceParent.assembleWithForwardDelta(sourceTree);
+ sourceTree.setRootNode(complete.getRootNode());
+ sourceTree.setParent(null);
+ sourceParent.setRootNode(backwardDelta.getRootNode());
+ sourceParent.setParent(sourceTree);
+ }
+
+ /**
+ * Returns a complete node containing the contents of a subtree of the tree.
+ * Returns null if the node at this key does not exist. This is a thread-safe
+ * version of copyCompleteSubtree
+ *
+ * @param key key of subtree to copy
+ */
+ public AbstractDataTreeNode safeCopyCompleteSubtree(IPath key) {
+ AbstractDataTreeNode node = searchNodeAt(key);
+ if (node == null)
+ return null;
+ if (node.isDelta())
+ return safeNaiveCopyCompleteSubtree(key);
+ //copy the node in case the user wants to hammer the subtree name
+ return node.copy();
+ }
+
+ /**
+ * Returns a complete node containing the contents of the subtree
+ * rooted at @key in the receiver. Returns null if this node does not exist in
+ * the tree. This is a thread-safe version of naiveCopyCompleteSubtree
+ *
+ * @param key
+ * key of subtree whose contents we want to copy.
+ */
+ protected AbstractDataTreeNode safeNaiveCopyCompleteSubtree(IPath key) {
+ try {
+ String[] childNames = getNamesOfChildren(key);
+ int numChildren = childNames.length;
+ AbstractDataTreeNode[] childNodes;
+ if (numChildren == 0) {
+ childNodes = AbstractDataTreeNode.NO_CHILDREN;
+ } else {
+ childNodes = new AbstractDataTreeNode[numChildren];
+ /* do for each child */
+ int actualChildCount = 0;
+ for (int i = numChildren; --i >= 0;) {
+ childNodes[i] = safeCopyCompleteSubtree(key.append(childNames[i]));
+ if (childNodes[i] != null)
+ actualChildCount++;
+ }
+ //if there are less actual children due to concurrent deletion, shrink the child array
+ if (actualChildCount < numChildren) {
+ AbstractDataTreeNode[] actualChildNodes = new AbstractDataTreeNode[actualChildCount];
+ for (int iOld = 0, iNew = 0; iOld < numChildren; iOld++)
+ if (childNodes[iOld] != null)
+ actualChildNodes[iNew++] = childNodes[iOld];
+ childNodes = actualChildNodes;
+ }
+ }
+ return new DataTreeNode(key.lastSegment(), getData(key), childNodes);
+ } catch (ObjectNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the specified node. Search in the parent if necessary. Return null
+ * if the node is not found or if it has been deleted
+ */
+ protected AbstractDataTreeNode searchNodeAt(IPath key) {
+ int keyLength = key.segmentCount();
+ for (DeltaDataTree tree = this; tree != null; tree = tree.parent) {
+ AbstractDataTreeNode node = tree.rootNode;
+ boolean complete = !node.isDelta();
+ for (int i = 0; i < keyLength; i++) {
+ node = node.childAtOrNull(key.segment(i));
+ if (node == null) {
+ break;
+ }
+ if (!node.isDelta()) {
+ complete = true;
+ }
+ }
+ if (node != null) {
+ if (node.isDeleted())
+ break;
+ return node;
+ }
+ if (complete) {
+ // Not found, but complete node encountered, so should not check parent tree.
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see AbstractDataTree#setData(IPath, Object)
+ */
+ public void setData(IPath key, Object data) {
+ if (isImmutable())
+ handleImmutableTree();
+ if (!includes(key))
+ handleNotFound(key);
+ assembleNode(key, new DataDeltaNode(key.lastSegment(), data));
+ }
+
+ /**
+ * Sets the parent of the tree.
+ */
+ protected void setParent(DeltaDataTree aTree) {
+ parent = aTree;
+ }
+
+ /**
+ * Sets the root node of the tree
+ */
+ void setRootNode(AbstractDataTreeNode aNode) {
+ rootNode = aNode;
+ }
+
+ /**
+ * Simplifies the receiver:
+ * - replaces any DataDelta nodes with the same data as the parent
+ * with a NoDataDelta node
+ * - removes any empty (leaf NoDataDelta) nodes
+ */
+ protected void simplify(IComparator comparer) {
+ if (parent == null)
+ return;
+ setRootNode(rootNode.simplifyWithParent(rootKey(), parent, comparer));
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IStringPoolParticipant
+ */
+ public void storeStrings(StringPool set){
+ AbstractDataTreeNode root = null;
+ for(DeltaDataTree dad = this ; dad != null; dad = dad.getParent()){
+ root = dad.getRootNode();
+ if (root != null)
+ root.storeStrings(set);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/IComparator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+/**
+ * An interface for comparing two data tree objects. Provides information
+ * on how an object has changed from one tree to another.
+ */
+public interface IComparator {
+ /**
+ * Returns an integer describing the changes between two data objects
+ * in a data tree. The first three bits of the returned integer are
+ * used during calculation of delta trees. The remaining bits can be
+ * assigned any meaning that is useful to the client. If there is no
+ * change in the two data objects, this method must return 0.
+ *
+ * @see NodeComparison
+ */
+ int compare(Object o1, Object o2);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/IDataFlattener.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import java.io.*;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * The <code>IElementInfoFlattener</code> interface supports
+ * reading and writing element info objects.
+ */
+public interface IDataFlattener {
+ /**
+ * Reads a data object from the given input stream.
+ * @param path the path of the element to be read
+ * @param input the stream from which the element info should be read.
+ * @return the object associated with the given path,
+ * which may be <code>null</code>.
+ */
+ public Object readData(IPath path, DataInput input) throws IOException;
+
+ /**
+ * Writes the given data to the output stream.
+ * <p> N.B. The bytes written must be sufficient for the
+ * purposes of reading the object back in.
+ * @param path the element's path in the tree
+ * @param data the object associated with the given path,
+ * which may be <code>null</code>.
+ */
+ public void writeData(IPath path, Object data, DataOutput output) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/NoDataDeltaNode.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A <code>NoDataDeltaNode</code>is a node in a delta tree whose subtree contains
+ * differences since the delta's parent. Refer to the <code>DeltaDataTree</code>
+ * API and class comment for details.
+ *
+ * @see DeltaDataTree
+ */
+public class NoDataDeltaNode extends AbstractDataTreeNode {
+ /**
+ * Creates a new empty delta.
+ */
+ public NoDataDeltaNode(String name) {
+ this(name, NO_CHILDREN);
+ }
+
+ /**
+ * Creates a new data tree node
+ *
+ * @param name name of new node
+ * @param children children of the new node
+ */
+ public NoDataDeltaNode(String name, AbstractDataTreeNode[] children) {
+ super(name, children);
+ }
+
+ /**
+ * Creates a new data tree node
+ *
+ * @param localName name of new node
+ * @param childNode single child for new node
+ */
+ NoDataDeltaNode(String localName, AbstractDataTreeNode childNode) {
+ super(localName, new AbstractDataTreeNode[] {childNode});
+ }
+
+ /**
+ * @see AbstractDataTreeNode#asBackwardDelta(DeltaDataTree, DeltaDataTree, IPath)
+ */
+ AbstractDataTreeNode asBackwardDelta(DeltaDataTree myTree, DeltaDataTree parentTree, IPath key) {
+ int numChildren = children.length;
+ if (numChildren == 0)
+ return new NoDataDeltaNode(name, NO_CHILDREN);
+ AbstractDataTreeNode[] newChildren = new AbstractDataTreeNode[numChildren];
+ for (int i = numChildren; --i >= 0;) {
+ newChildren[i] = children[i].asBackwardDelta(myTree, parentTree, key.append(children[i].getName()));
+ }
+ return new NoDataDeltaNode(name, newChildren);
+ }
+
+ /**
+ * @see AbstractDataTreeNode#compareWithParent(IPath, DeltaDataTree, IComparator)
+ */
+ AbstractDataTreeNode compareWithParent(IPath key, DeltaDataTree parent, IComparator comparator) {
+ AbstractDataTreeNode[] comparedChildren = compareWithParent(children, key, parent, comparator);
+ Object oldData = parent.getData(key);
+ return new DataTreeNode(key.lastSegment(), new NodeComparison(oldData, oldData, NodeComparison.K_CHANGED, 0), comparedChildren);
+ }
+
+ /**
+ * Creates and returns a new copy of the receiver. Makes a deep copy of
+ * children, but a shallow copy of name and data.
+ */
+ AbstractDataTreeNode copy() {
+ AbstractDataTreeNode[] childrenCopy;
+ if (children.length == 0) {
+ childrenCopy = NO_CHILDREN;
+ } else {
+ childrenCopy = new AbstractDataTreeNode[children.length];
+ System.arraycopy(children, 0, childrenCopy, 0, children.length);
+ }
+ return new NoDataDeltaNode(name, childrenCopy);
+ }
+
+ /**
+ * Returns true if the receiver represents delta information,
+ * false if it represents the complete information.
+ */
+ boolean isDelta() {
+ return true;
+ }
+
+ /**
+ * Returns true if the receiver is an empty delta node, false otherwise.
+ */
+ boolean isEmptyDelta() {
+ return this.size() == 0;
+ }
+
+ /**
+ * Simplifies the given node, and returns its replacement.
+ */
+ AbstractDataTreeNode simplifyWithParent(IPath key, DeltaDataTree parent, IComparator comparer) {
+ AbstractDataTreeNode[] simplifiedChildren = simplifyWithParent(children, key, parent, comparer);
+ return new NoDataDeltaNode(name, simplifiedChildren);
+ }
+
+ /**
+ * Returns a unicode representation of the node. This method is used
+ * for debugging purposes only (no NLS support needed)
+ */
+ public String toString() {
+ return "a NoDataDeltaNode(" + this.getName() + ") with " + getChildren().length + " children."; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Return a constant describing the type of node.
+ */
+ int type() {
+ return T_NO_DATA_DELTA_NODE;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/NodeComparison.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+/**
+ * This class represents the changes in a single node between two data trees.
+ */
+public final class NodeComparison {
+ /**
+ * The data of the old tree
+ */
+ private Object oldData;
+
+ /**
+ * The data of the new tree
+ */
+ private Object newData;
+
+ /**
+ * Integer describing changes between two data elements
+ */
+ private int comparison;
+
+ /**
+ * Extra integer that can be assigned by the client
+ */
+ private int userInt;
+
+ /**
+ * Special bits in the comparison flag to indicate the type of change
+ */
+ public final static int K_ADDED = 1;
+ public final static int K_REMOVED = 2;
+ public final static int K_CHANGED = 4;
+
+ NodeComparison(Object oldData, Object newData, int realComparison, int userComparison) {
+ this.oldData = oldData;
+ this.newData = newData;
+ this.comparison = realComparison;
+ this.userInt = userComparison;
+ }
+
+ /**
+ * Reverse the nature of the comparison.
+ */
+ NodeComparison asReverseComparison(IComparator comparator) {
+ /* switch the data */
+ Object tempData = oldData;
+ oldData = newData;
+ newData = tempData;
+
+ /* re-calculate user comparison */
+ userInt = comparator.compare(oldData, newData);
+
+ if (comparison == K_ADDED) {
+ comparison = K_REMOVED;
+ } else {
+ if (comparison == K_REMOVED) {
+ comparison = K_ADDED;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns an integer describing the changes between the two data objects.
+ * The four possible values are K_ADDED, K_REMOVED, K_CHANGED, or 0 representing
+ * no change.
+ */
+ public int getComparison() {
+ return comparison;
+ }
+
+ /**
+ * Returns the data of the new node.
+ */
+ public Object getNewData() {
+ return newData;
+ }
+
+ /**
+ * Returns the data of the old node.
+ */
+ public Object getOldData() {
+ return oldData;
+ }
+
+ /**
+ * Returns the client specified integer
+ */
+ public int getUserComparison() {
+ return userInt;
+ }
+
+ /**
+ * Returns true if this comparison has no change, and false otherwise.
+ */
+ boolean isUnchanged() {
+ return userInt == 0;
+ }
+
+ /**
+ * For debugging
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer("NodeComparison("); //$NON-NLS-1$
+ switch (comparison) {
+ case K_ADDED :
+ buf.append("Added, "); //$NON-NLS-1$
+ break;
+ case K_REMOVED :
+ buf.append("Removed, "); //$NON-NLS-1$
+ break;
+ case K_CHANGED :
+ buf.append("Changed, "); //$NON-NLS-1$
+ break;
+ case 0 :
+ buf.append("No change, "); //$NON-NLS-1$
+ break;
+ default :
+ buf.append("Corrupt(" + comparison + "), "); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ buf.append(userInt);
+ buf.append(")"); //$NON-NLS-1$
+ return buf.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/ObjectNotFoundException.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+/**
+ * This exception is thrown when an attempt is made to reference a source tree
+ * element that does not exist in the given tree.
+ */
+public class ObjectNotFoundException extends RuntimeException {
+ /**
+ * All serializable objects should have a stable serialVersionUID
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * ObjectNotFoundException constructor comment.
+ * @param s java.lang.String
+ */
+ public ObjectNotFoundException(String s) {
+ super(s);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/TestHelper.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dtree;
+
+/**
+ * Helper class for the test suite.
+ */
+public class TestHelper {
+ /**
+ * Returns the root node of a tree.
+ */
+ public static AbstractDataTreeNode getRootNode(AbstractDataTree tree) {
+ return tree.getRootNode();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/AutoBuildJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ * Warren Paul (Nokia) - Fix for build scheduling bug 209236
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.framework.Bundle;
+
+/**
+ * The job for performing workspace auto-builds, and pre- and post- autobuild
+ * notification. This job is run whenever the workspace changes regardless
+ * of whether autobuild is on or off.
+ */
+class AutoBuildJob extends Job implements Preferences.IPropertyChangeListener {
+ private boolean avoidBuild = false;
+ private boolean buildNeeded = false;
+ private boolean forceBuild = false;
+ /**
+ * Indicates that another thread tried to modify the workspace during
+ * the autobuild. The autobuild should be immediately rescheduled
+ * so that it will run as soon as the next workspace modification completes.
+ */
+ private boolean interrupted = false;
+ private boolean isAutoBuilding = false;
+ private long lastBuild = 0L;
+ private Preferences preferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+ private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
+ private Workspace workspace;
+
+ AutoBuildJob(Workspace workspace) {
+ super(Messages.events_building_0);
+ setRule(workspace.getRoot());
+ setPriority(BUILD);
+ isAutoBuilding = workspace.isAutoBuilding();
+ this.workspace = workspace;
+ this.preferences.addPropertyChangeListener(this);
+ }
+
+ /**
+ * Used to prevent auto-builds at the end of operations that contain
+ * explicit builds
+ */
+ synchronized void avoidBuild() {
+ avoidBuild = true;
+ }
+
+ public boolean belongsTo(Object family) {
+ return family == ResourcesPlugin.FAMILY_AUTO_BUILD;
+ }
+
+ /**
+ * Instructs the build job that a build is required. Ensure the build
+ * job is scheduled to run.
+ * @param needsBuild Whether a build is required, either due to
+ * workspace change or other factor that invalidates the built state.
+ */
+ synchronized void build(boolean needsBuild) {
+ buildNeeded |= needsBuild;
+ long delay = computeScheduleDelay();
+ int state = getState();
+ if (Policy.DEBUG_BUILD_NEEDED)
+ Policy.debug("Build requested, needsBuild: " + needsBuild + " state: " + state + " delay: " + delay); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (needsBuild && Policy.DEBUG_BUILD_NEEDED_STACK && state != Job.RUNNING)
+ new RuntimeException("Build Needed").printStackTrace(); //$NON-NLS-1$
+ //don't mess with the interrupt flag if the job is still running
+ if (state != Job.RUNNING)
+ setInterrupted(false);
+ switch (state) {
+ case Job.SLEEPING :
+ wakeUp(delay);
+ break;
+ case NONE :
+ setSystem(!isAutoBuilding);
+ schedule(delay);
+ break;
+ }
+ }
+
+ /**
+ * Computes the delay time that autobuild should be scheduled with. The
+ * value will be in the range (MIN_BUILD_DELAY, MAX_BUILD_DELAY).
+ */
+ private long computeScheduleDelay() {
+ // don't assume that the last build time is always less than the current system time
+ long maxDelay = Math.min(Policy.MAX_BUILD_DELAY, Policy.MAX_BUILD_DELAY + lastBuild - System.currentTimeMillis());
+ return Math.max(Policy.MIN_BUILD_DELAY, maxDelay);
+ }
+
+ /**
+ * The autobuild job has been canceled. There are two flavours of
+ * cancel, explicit user cancelation, and implicit interruption due to another
+ * thread trying to modify the workspace. In the latter case, we must
+ * make sure the build is immediately rescheduled if it was interrupted
+ * by another thread, so that clients waiting to join autobuild will properly
+ * continue waiting
+ * @return a status with severity <code>CANCEL</code>
+ */
+ private synchronized IStatus canceled() {
+ //regardless of the form of cancelation, the build state is not happy
+ buildNeeded = true;
+ //schedule a rebuild immediately if build was implicitly canceled
+ if (interrupted) {
+ if (Policy.DEBUG_BUILD_INTERRUPT)
+ System.out.println("Scheduling rebuild due to interruption"); //$NON-NLS-1$
+ setInterrupted(false);
+ schedule(computeScheduleDelay());
+ }
+ return Status.CANCEL_STATUS;
+ }
+
+ private void doBuild(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
+ final ISchedulingRule rule = workspace.getRuleFactory().buildRule();
+ try {
+ workspace.prepareOperation(rule, monitor);
+ workspace.beginOperation(true);
+ final int trigger = IncrementalProjectBuilder.AUTO_BUILD;
+ workspace.broadcastBuildEvent(workspace, IResourceChangeEvent.PRE_BUILD, trigger);
+ IStatus result = Status.OK_STATUS;
+ try {
+ if (shouldBuild())
+ result = workspace.getBuildManager().build(trigger, Policy.subMonitorFor(monitor, Policy.opWork));
+ } finally {
+ //always send POST_BUILD if there has been a PRE_BUILD
+ workspace.broadcastBuildEvent(workspace, IResourceChangeEvent.POST_BUILD, trigger);
+ }
+ if (!result.isOK())
+ throw new ResourceException(result);
+ buildNeeded = false;
+ } finally {
+ //building may close the tree, but we are still inside an
+ // operation so open it
+ if (workspace.getElementTree().isImmutable())
+ workspace.newWorkingTree();
+ workspace.endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Forces an autobuild to occur, even if nothing has changed since the last
+ * build. This is used to force a build after a clean.
+ */
+ public void forceBuild() {
+ forceBuild = true;
+ }
+
+ /**
+ * Another thread is attempting to modify the workspace. Flag the auto-build
+ * as interrupted so that it will cancel and reschedule itself
+ */
+ synchronized void interrupt() {
+ //if already interrupted, do nothing
+ if (interrupted)
+ return;
+ switch (getState()) {
+ case NONE :
+ return;
+ case WAITING :
+ //put the job to sleep if it is waiting to run
+ setInterrupted(!sleep());
+ break;
+ case RUNNING :
+ //make sure autobuild doesn't interrupt itself
+ if (Job.getJobManager().currentJob() == this)
+ return;
+ setInterrupted(true);
+ if (interrupted && Policy.DEBUG_BUILD_INTERRUPT) {
+ System.out.println("Autobuild was interrupted:"); //$NON-NLS-1$
+ new Exception().fillInStackTrace().printStackTrace();
+ }
+ break;
+ }
+ //clear the autobuild avoidance flag if we were interrupted
+ if (interrupted)
+ avoidBuild = false;
+ }
+
+ synchronized boolean isInterrupted() {
+ if (interrupted)
+ return true;
+ //check if another job is blocked by the build job
+ if (isBlocking())
+ setInterrupted(true);
+ return interrupted;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ if (!event.getProperty().equals(ResourcesPlugin.PREF_AUTO_BUILDING))
+ return;
+ // get the new value of auto-build directly from the preferences
+ boolean wasAutoBuilding = isAutoBuilding;
+ isAutoBuilding = preferences.getBoolean(ResourcesPlugin.PREF_AUTO_BUILDING);
+ //force a build if autobuild has been turned on
+ if (!forceBuild && !wasAutoBuilding && isAutoBuilding) {
+ forceBuild = true;
+ build(false);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus run(IProgressMonitor monitor) {
+ //synchronized in case build starts during checkCancel
+ synchronized (this) {
+ if (monitor.isCanceled()) {
+ return canceled();
+ }
+ }
+ //if the system is shutting down, don't build
+ if (systemBundle.getState() == Bundle.STOPPING)
+ return Status.OK_STATUS;
+ try {
+ doBuild(monitor);
+ lastBuild = System.currentTimeMillis();
+ //if the build was successful then it should not be recorded as interrupted
+ setInterrupted(false);
+ return Status.OK_STATUS;
+ } catch (OperationCanceledException e) {
+ return canceled();
+ } catch (CoreException sig) {
+ return sig.getStatus();
+ }
+ }
+
+ /**
+ * Sets or clears the interrupted flag.
+ */
+ private synchronized void setInterrupted(boolean value) {
+ interrupted = value;
+ }
+
+ /**
+ * Returns true if a build is actually needed, and false otherwise.
+ */
+ private synchronized boolean shouldBuild() {
+ try {
+ //if auto-build is off then we never run
+ if (!workspace.isAutoBuilding())
+ return false;
+ //build if the workspace requires a build (description changes)
+ if (forceBuild)
+ return true;
+ if (avoidBuild)
+ return false;
+ //return whether there have been any changes to the workspace tree.
+ return buildNeeded;
+ } finally {
+ //regardless of the result, clear the build flags for next time
+ forceBuild = avoidBuild = buildNeeded = false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/BuildCommand.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.core.internal.resources.ModelObject;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * The concrete implementation of <tt>ICommand</tt>. This object
+ * stores information about a particular builder, including a reference
+ * to the builder instance itself if it has been instantiated.
+ */
+public class BuildCommand extends ModelObject implements ICommand {
+ /**
+ * Internal flag masks for different build triggers.
+ */
+ private static final int MASK_AUTO = 0x01;
+ private static final int MASK_INCREMENTAL = 0x02;
+ private static final int MASK_FULL = 0x04;
+ private static final int MASK_CLEAN = 0x08;
+
+ /**
+ * Flag bit indicating if this build command is configurable
+ */
+ private static final int MASK_CONFIGURABLE = 0x10;
+
+ /**
+ * Flag bit indicating if the configurable bit has been loaded from
+ * the builder extension declaration in XML yet.
+ */
+ private static final int MASK_CONFIG_COMPUTED = 0x20;
+
+ private static final int ALL_TRIGGERS = MASK_AUTO | MASK_CLEAN | MASK_FULL | MASK_INCREMENTAL;
+
+ protected HashMap arguments;
+
+ /**
+ * The builder instance for this command. Null if the builder has
+ * not yet been instantiated.
+ */
+ protected IncrementalProjectBuilder builder;
+
+ /**
+ * The triggers that this builder will respond to. Since build triggers are not
+ * bit-maskable, we use internal bit masks to represent each
+ * trigger (MASK_* constants). By default, a command responds to all
+ * build triggers.
+ */
+ private int triggers = ALL_TRIGGERS;
+
+ /**
+ * Returns the trigger bit mask for the given trigger constant.
+ */
+ private static int maskForTrigger(int trigger) {
+ switch (trigger) {
+ case IncrementalProjectBuilder.AUTO_BUILD :
+ return MASK_AUTO;
+ case IncrementalProjectBuilder.INCREMENTAL_BUILD :
+ return MASK_INCREMENTAL;
+ case IncrementalProjectBuilder.FULL_BUILD :
+ return MASK_FULL;
+ case IncrementalProjectBuilder.CLEAN_BUILD :
+ return MASK_CLEAN;
+ }
+ return 0;
+ }
+
+ public BuildCommand() {
+ super(""); //$NON-NLS-1$
+ this.arguments = new HashMap(0);
+ }
+
+ public Object clone() {
+ BuildCommand result = null;
+ result = (BuildCommand) super.clone();
+ if (result == null)
+ return null;
+ result.setArguments(getArguments());
+ //don't let references to builder instances leak out because they reference trees
+ result.setBuilder(null);
+ return result;
+ }
+
+ /**
+ * Computes whether this build command allows configuration of its
+ * triggers, based on information in the builder extension declaration.
+ */
+ private void computeIsConfigurable() {
+ triggers |= MASK_CONFIG_COMPUTED;
+ IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, name);
+ if (extension != null) {
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length != 0) {
+ String value = configs[0].getAttribute("isConfigurable"); //$NON-NLS-1$
+ setConfigurable(value != null && value.equalsIgnoreCase(Boolean.TRUE.toString()));
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Object
+ */
+ public boolean equals(Object object) {
+ if (this == object)
+ return true;
+ if (!(object instanceof BuildCommand))
+ return false;
+ BuildCommand command = (BuildCommand) object;
+ // equal if same builder name and equal argument tables
+ return getBuilderName().equals(command.getBuilderName()) && getArguments(false).equals(command.getArguments(false)) && triggers == command.triggers;
+ }
+
+ /**
+ * @see ICommand#getArguments()
+ */
+ public Map getArguments() {
+ return getArguments(true);
+ }
+
+ public Map getArguments(boolean makeCopy) {
+ return arguments == null ? null : (makeCopy ? (Map) arguments.clone() : arguments);
+ }
+
+ public IncrementalProjectBuilder getBuilder() {
+ return builder;
+ }
+
+ /**
+ * @see ICommand#getBuilderName()
+ */
+ public String getBuilderName() {
+ return getName();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Object
+ */
+ public int hashCode() {
+ // hash on name alone
+ return 37 * getName().hashCode() + triggers;
+ }
+
+ /**
+ * @see ICommand#isBuilding(int)
+ */
+ public boolean isBuilding(int trigger) {
+ return (triggers & maskForTrigger(trigger)) != 0;
+ }
+
+ public boolean isConfigurable() {
+ if ((triggers & MASK_CONFIG_COMPUTED) == 0)
+ computeIsConfigurable();
+ return (triggers & MASK_CONFIGURABLE) != 0;
+ }
+
+ /**
+ * @see ICommand#setArguments(Map)
+ */
+ public void setArguments(Map value) {
+ // copy parameter for safety's sake
+ arguments = value == null ? null : new HashMap(value);
+ }
+
+ public void setBuilder(IncrementalProjectBuilder builder) {
+ this.builder = builder;
+ }
+
+ /**
+ * @see ICommand#setBuilderName(String)
+ */
+ public void setBuilderName(String value) {
+ //don't allow builder name to be null
+ setName(value == null ? "" : value); //$NON-NLS-1$
+ }
+
+ /**
+ * @see ICommand#setBuilding(int, boolean)
+ */
+ public void setBuilding(int trigger, boolean value) {
+ if (!isConfigurable())
+ return;
+ if (value)
+ triggers |= maskForTrigger(trigger);
+ else
+ triggers &= ~maskForTrigger(trigger);
+ }
+
+ /**
+ * Sets whether this build command allows its build triggers to be configured.
+ * This value should only be set when the builder extension declaration is
+ * read from the registry, or when a build command is read from the project
+ * description file on disk. The value is not otherwise mutable.
+ */
+ public void setConfigurable(boolean value) {
+ triggers |= MASK_CONFIG_COMPUTED;
+ if (value)
+ triggers |= MASK_CONFIGURABLE;
+ else
+ triggers = ALL_TRIGGERS;
+ }
+
+ /**
+ * For debugging purposes only
+ */
+ public String toString() {
+ return "BuildCommand(" + getName() + ")";//$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/BuildManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1022 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Isaac Pacht (isaacp3@gmail.com) - fix for bug 206540
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.*;
+import org.eclipse.core.internal.dtree.DeltaDataTree;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.*;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+
+public class BuildManager implements ICoreConstants, IManager, ILifecycleListener {
+
+ /**
+ * Cache used to optimize the common case of an autobuild against
+ * a workspace where only a single project has changed (and hence
+ * only a single delta is interesting).
+ */
+ class DeltaCache {
+ private Object delta;
+ private ElementTree newTree;
+ private ElementTree oldTree;
+ private IPath projectPath;
+
+ public void cache(IPath project, ElementTree anOldTree, ElementTree aNewTree, Object aDelta) {
+ this.projectPath = project;
+ this.oldTree = anOldTree;
+ this.newTree = aNewTree;
+ this.delta = aDelta;
+ }
+
+ public void flush() {
+ this.projectPath = null;
+ this.oldTree = null;
+ this.newTree = null;
+ this.delta = null;
+ }
+
+ /**
+ * Returns the cached resource delta for the given project and trees, or
+ * null if there is no matching delta in the cache.
+ */
+ public Object getDelta(IPath project, ElementTree anOldTree, ElementTree aNewTree) {
+ if (delta == null)
+ return null;
+ boolean pathsEqual = projectPath == null ? project == null : projectPath.equals(project);
+ if (pathsEqual && this.oldTree == anOldTree && this.newTree == aNewTree)
+ return delta;
+ return null;
+ }
+ }
+
+ /**
+ * These builders are added to build tables in place of builders that couldn't be instantiated
+ */
+ class MissingBuilder extends IncrementalProjectBuilder {
+ private boolean hasBeenBuilt = false;
+ private String name;
+
+ MissingBuilder(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Log an exception on the first build, and silently do nothing on subsequent builds.
+ */
+ protected IProject[] build(int kind, Map args, IProgressMonitor monitor) {
+ if (!hasBeenBuilt && Policy.DEBUG_BUILD_FAILURE) {
+ hasBeenBuilt = true;
+ String msg = NLS.bind(Messages.events_skippingBuilder, name, getProject().getName());
+ Policy.log(IStatus.WARNING, msg, null);
+ }
+ return null;
+ }
+ }
+
+ private static final int TOTAL_BUILD_WORK = Policy.totalWork * 1000;
+
+ //the job for performing background autobuild
+ final AutoBuildJob autoBuildJob;
+ private boolean building = false;
+ private final ArrayList builtProjects = new ArrayList();
+
+ //the following four fields only apply for the lifetime of a single builder invocation.
+ protected InternalBuilder currentBuilder;
+ private DeltaDataTree currentDelta;
+ private ElementTree currentLastBuiltTree;
+ private ElementTree currentTree;
+
+ /**
+ * Caches the IResourceDelta for a pair of trees
+ */
+ final private DeltaCache deltaCache = new DeltaCache();
+ /**
+ * Caches the DeltaDataTree used to determine if a build is necessary
+ */
+ final private DeltaCache deltaTreeCache = new DeltaCache();
+
+ private ILock lock;
+
+ //used for the build cycle looping mechanism
+ private boolean rebuildRequested = false;
+
+ private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
+
+ //used for debug/trace timing
+ private long timeStamp = -1;
+ private Workspace workspace;
+
+ public BuildManager(Workspace workspace, ILock workspaceLock) {
+ this.workspace = workspace;
+ this.autoBuildJob = new AutoBuildJob(workspace);
+ this.lock = workspaceLock;
+ InternalBuilder.buildManager = this;
+ }
+
+ private void basicBuild(int trigger, IncrementalProjectBuilder builder, Map args, MultiStatus status, IProgressMonitor monitor) {
+ try {
+ currentBuilder = builder;
+ //clear any old requests to forget built state
+ currentBuilder.clearForgetLastBuiltState();
+ // Figure out want kind of build is needed
+ boolean clean = trigger == IncrementalProjectBuilder.CLEAN_BUILD;
+ currentLastBuiltTree = currentBuilder.getLastBuiltTree();
+ // If no tree is available we have to do a full build
+ if (!clean && currentLastBuiltTree == null)
+ trigger = IncrementalProjectBuilder.FULL_BUILD;
+ //don't build if this builder doesn't respond to the given trigger
+ if (!builder.getCommand().isBuilding(trigger)) {
+ if (clean)
+ currentBuilder.setLastBuiltTree(null);
+ return;
+ }
+ // For incremental builds, grab a pointer to the current state before computing the delta
+ currentTree = ((trigger == IncrementalProjectBuilder.FULL_BUILD) || clean) ? null : workspace.getElementTree();
+ int depth = -1;
+ try {
+ //short-circuit if none of the projects this builder cares about have changed.
+ if (!needsBuild(currentBuilder, trigger)) {
+ //use up the progress allocated for this builder
+ monitor.beginTask("", 1); //$NON-NLS-1$
+ monitor.done();
+ return;
+ }
+ String name = currentBuilder.getLabel();
+ String message;
+ if (name != null)
+ message = NLS.bind(Messages.events_invoking_2, name, builder.getProject().getFullPath());
+ else
+ message = NLS.bind(Messages.events_invoking_1, builder.getProject().getFullPath());
+ monitor.subTask(message);
+ hookStartBuild(builder, trigger);
+ //release workspace lock while calling builders
+ depth = getWorkManager().beginUnprotected();
+ //do the build
+ SafeRunner.run(getSafeRunnable(trigger, args, status, monitor));
+ } finally {
+ if (depth >= 0)
+ getWorkManager().endUnprotected(depth);
+ // Be sure to clean up after ourselves.
+ if (clean || currentBuilder.wasForgetStateRequested()) {
+ currentBuilder.setLastBuiltTree(null);
+ } else {
+ // remember the current state as the last built state.
+ ElementTree lastTree = workspace.getElementTree();
+ lastTree.immutable();
+ currentBuilder.setLastBuiltTree(lastTree);
+ }
+ hookEndBuild(builder);
+ }
+ } finally {
+ currentBuilder = null;
+ currentTree = null;
+ currentLastBuiltTree = null;
+ currentDelta = null;
+ }
+ }
+
+ protected void basicBuild(IProject project, int trigger, ICommand[] commands, MultiStatus status, IProgressMonitor monitor) {
+ try {
+ for (int i = 0; i < commands.length; i++) {
+ checkCanceled(trigger, monitor);
+ BuildCommand command = (BuildCommand) commands[i];
+ IProgressMonitor sub = Policy.subMonitorFor(monitor, 1);
+ IncrementalProjectBuilder builder = getBuilder(project, command, i, status);
+ if (builder != null)
+ basicBuild(trigger, builder, command.getArguments(false), status, sub);
+ }
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ }
+
+ /**
+ * Runs all builders on the given project.
+ * @return A status indicating if the build succeeded or failed
+ */
+ private IStatus basicBuild(IProject project, int trigger, IProgressMonitor monitor) {
+ if (!canRun(trigger))
+ return Status.OK_STATUS;
+ try {
+ hookStartBuild(trigger);
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
+ basicBuild(project, trigger, status, monitor);
+ return status;
+ } finally {
+ hookEndBuild(trigger);
+ }
+ }
+
+ private void basicBuild(final IProject project, final int trigger, final MultiStatus status, final IProgressMonitor monitor) {
+ try {
+ final ICommand[] commands;
+ if (project.isAccessible())
+ commands = ((Project) project).internalGetDescription().getBuildSpec(false);
+ else
+ commands = null;
+ int work = commands == null ? 0 : commands.length;
+ monitor.beginTask(NLS.bind(Messages.events_building_1, project.getFullPath()), work);
+ if (work == 0)
+ return;
+ ISafeRunnable code = new ISafeRunnable() {
+ public void handleException(Throwable e) {
+ if (e instanceof OperationCanceledException) {
+ if (Policy.DEBUG_BUILD_INVOKING)
+ Policy.debug("Build canceled"); //$NON-NLS-1$
+ throw (OperationCanceledException) e;
+ }
+ // don't log the exception....it is already being logged in Workspace#run
+ // should never get here because the lower-level build code wrappers
+ // builder exceptions in core exceptions if required.
+ String errorText = e.getMessage();
+ if (errorText == null)
+ errorText = NLS.bind(Messages.events_unknown, e.getClass().getName(), project.getName());
+ status.add(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, errorText, e));
+ }
+
+ public void run() throws Exception {
+ basicBuild(project, trigger, commands, status, monitor);
+ }
+ };
+ SafeRunner.run(code);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Runs the builder with the given name on the given project.
+ * @return A status indicating if the build succeeded or failed
+ */
+ private IStatus basicBuild(IProject project, int trigger, String builderName, Map args, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.events_building_1, project.getFullPath());
+ monitor.beginTask(message, 1);
+ if (!canRun(trigger))
+ return Status.OK_STATUS;
+ try {
+ hookStartBuild(trigger);
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
+ ICommand command = getCommand(project, builderName, args);
+ try {
+ IncrementalProjectBuilder builder = getBuilder(project, command, -1, status);
+ if (builder != null)
+ basicBuild(trigger, builder, args, status, Policy.subMonitorFor(monitor, 1));
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ return status;
+ } finally {
+ hookEndBuild(trigger);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Loop the workspace build until no more builders request a rebuild.
+ */
+ private void basicBuildLoop(IProject[] ordered, IProject[] unordered, int trigger, MultiStatus status, IProgressMonitor monitor) {
+ int projectWork = ordered.length + unordered.length;
+ if (projectWork > 0)
+ projectWork = TOTAL_BUILD_WORK / projectWork;
+ int maxIterations = workspace.getDescription().getMaxBuildIterations();
+ if (maxIterations <= 0)
+ maxIterations = 1;
+ rebuildRequested = true;
+ for (int iter = 0; rebuildRequested && iter < maxIterations; iter++) {
+ rebuildRequested = false;
+ builtProjects.clear();
+ for (int i = 0; i < ordered.length; i++) {
+ if (ordered[i].isAccessible()) {
+ basicBuild(ordered[i], trigger, status, Policy.subMonitorFor(monitor, projectWork));
+ builtProjects.add(ordered[i]);
+ }
+ }
+ for (int i = 0; i < unordered.length; i++) {
+ if (unordered[i].isAccessible()) {
+ basicBuild(unordered[i], trigger, status, Policy.subMonitorFor(monitor, projectWork));
+ builtProjects.add(unordered[i]);
+ }
+ }
+ //subsequent builds should always be incremental
+ trigger = IncrementalProjectBuilder.INCREMENTAL_BUILD;
+ }
+ }
+
+ /**
+ * Runs all builders on all projects.
+ * @return A status indicating if the build succeeded or failed
+ */
+ public IStatus build(int trigger, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask(Messages.events_building_0, TOTAL_BUILD_WORK);
+ if (!canRun(trigger))
+ return Status.OK_STATUS;
+ try {
+ hookStartBuild(trigger);
+ IProject[] ordered = workspace.getBuildOrder();
+ HashSet leftover = new HashSet(Arrays.asList(workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN)));
+ leftover.removeAll(Arrays.asList(ordered));
+ IProject[] unordered = (IProject[]) leftover.toArray(new IProject[leftover.size()]);
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.BUILD_FAILED, Messages.events_errors, null);
+ basicBuildLoop(ordered, unordered, trigger, status, monitor);
+ return status;
+ } finally {
+ hookEndBuild(trigger);
+ }
+ } finally {
+ monitor.done();
+ if (trigger == IncrementalProjectBuilder.INCREMENTAL_BUILD || trigger == IncrementalProjectBuilder.FULL_BUILD)
+ autoBuildJob.avoidBuild();
+ }
+ }
+
+ /**
+ * Runs the builder with the given name on the given project.
+ * @return A status indicating if the build succeeded or failed
+ */
+ public IStatus build(IProject project, int trigger, String builderName, Map args, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ if (builderName == null)
+ return basicBuild(project, trigger, monitor);
+ return basicBuild(project, trigger, builderName, args, monitor);
+ }
+
+ private boolean canRun(int trigger) {
+ return !building;
+ }
+
+ /**
+ * Cancel the build if the user has canceled or if an auto-build has been interrupted.
+ */
+ private void checkCanceled(int trigger, IProgressMonitor monitor) {
+ //if the system is shutting down, don't build
+ if (systemBundle.getState() == Bundle.STOPPING)
+ throw new OperationCanceledException();
+ Policy.checkCanceled(monitor);
+ //check for auto-cancel only if we are auto-building
+ if (trigger != IncrementalProjectBuilder.AUTO_BUILD)
+ return;
+ //check for request to interrupt the auto-build
+ if (autoBuildJob.isInterrupted())
+ throw new OperationCanceledException();
+ }
+
+ /**
+ * Creates and returns an ArrayList of BuilderPersistentInfo.
+ * The list includes entries for all builders that are
+ * in the builder spec, and that have a last built state, even if they
+ * have not been instantiated this session.
+ */
+ public ArrayList createBuildersPersistentInfo(IProject project) throws CoreException {
+ /* get the old builders (those not yet instantiated) */
+ ArrayList oldInfos = getBuildersPersistentInfo(project);
+
+ ICommand[] commands = ((Project) project).internalGetDescription().getBuildSpec(false);
+ if (commands.length == 0)
+ return null;
+
+ /* build the new list */
+ ArrayList newInfos = new ArrayList(commands.length);
+ for (int i = 0; i < commands.length; i++) {
+ String builderName = commands[i].getBuilderName();
+ BuilderPersistentInfo info = null;
+ IncrementalProjectBuilder builder = ((BuildCommand) commands[i]).getBuilder();
+ if (builder == null) {
+ // if the builder was not instantiated, use the old info if any.
+ if (oldInfos != null)
+ info = getBuilderInfo(oldInfos, builderName, i);
+ } else if (!(builder instanceof MissingBuilder)) {
+ ElementTree oldTree = ((InternalBuilder) builder).getLastBuiltTree();
+ //don't persist build state for builders that have no last built state
+ if (oldTree != null) {
+ // if the builder was instantiated, construct a memento with the important info
+ info = new BuilderPersistentInfo(project.getName(), builderName, i);
+ info.setLastBuildTree(oldTree);
+ info.setInterestingProjects(((InternalBuilder) builder).getInterestingProjects());
+ }
+ }
+ if (info != null)
+ newInfos.add(info);
+ }
+ return newInfos;
+ }
+
+ private String debugBuilder() {
+ return currentBuilder == null ? "<no builder>" : currentBuilder.getClass().getName(); //$NON-NLS-1$
+ }
+
+ private String debugProject() {
+ if (currentBuilder == null)
+ return "<no project>"; //$NON-NLS-1$
+ return currentBuilder.getProject().getFullPath().toString();
+ }
+
+ /**
+ * Returns a string representation of a build trigger for debugging purposes.
+ * @param trigger The trigger to compute a representation of
+ * @return A string describing the trigger.
+ */
+ private String debugTrigger(int trigger) {
+ switch (trigger) {
+ case IncrementalProjectBuilder.FULL_BUILD :
+ return "FULL_BUILD"; //$NON-NLS-1$
+ case IncrementalProjectBuilder.CLEAN_BUILD :
+ return "CLEAN_BUILD"; //$NON-NLS-1$
+ case IncrementalProjectBuilder.AUTO_BUILD :
+ case IncrementalProjectBuilder.INCREMENTAL_BUILD :
+ default :
+ return "INCREMENTAL_BUILD"; //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * The outermost workspace operation has finished. Do an autobuild if necessary.
+ */
+ public void endTopLevel(boolean needsBuild) {
+ autoBuildJob.build(needsBuild);
+ }
+
+ /**
+ * Returns the value of the boolean configuration element attribute with the
+ * given name, or <code>false</code> if the attribute is missing.
+ */
+ private boolean getBooleanAttribute(IConfigurationElement element, String name) {
+ String valueString = element.getAttribute(name);
+ return valueString != null && valueString.equalsIgnoreCase(Boolean.TRUE.toString());
+ }
+
+ /**
+ * Returns the builder instance corresponding to the given command, or
+ * <code>null</code> if the builder was not valid.
+ * @param project The project this builder corresponds to
+ * @param command The build command
+ * @param buildSpecIndex The index of this builder in the build spec, or -1 if
+ * the index is unknown
+ * @param status MultiStatus for collecting errors
+ */
+ private IncrementalProjectBuilder getBuilder(IProject project, ICommand command, int buildSpecIndex, MultiStatus status) throws CoreException {
+ InternalBuilder result = ((BuildCommand) command).getBuilder();
+ if (result == null) {
+ result = initializeBuilder(command.getBuilderName(), project, buildSpecIndex, status);
+ ((BuildCommand) command).setBuilder((IncrementalProjectBuilder) result);
+ result.setCommand(command);
+ result.setProject(project);
+ result.startupOnInitialize();
+ }
+ if (!validateNature(result, command.getBuilderName())) {
+ //skip this builder and null its last built tree because it is invalid
+ //if the nature gets added or re-enabled a full build will be triggered
+ result.setLastBuiltTree(null);
+ return null;
+ }
+ return (IncrementalProjectBuilder) result;
+ }
+
+ /**
+ * Removes the builder persistent info from the map corresponding to the
+ * given builder name and build spec index, or <code>null</code> if not found
+ *
+ * @param buildSpecIndex The index in the build spec, or -1 if unknown
+ */
+ private BuilderPersistentInfo getBuilderInfo(ArrayList infos, String builderName, int buildSpecIndex) {
+ //try to match on builder index, but if not match is found, use the name alone
+ //this is because older workspace versions did not store builder infos in build spec order
+ BuilderPersistentInfo nameMatch = null;
+ for (Iterator it = infos.iterator(); it.hasNext();) {
+ BuilderPersistentInfo info = (BuilderPersistentInfo) it.next();
+ //match on name and build spec index if known
+ if (info.getBuilderName().equals(builderName)) {
+ //we have found a match on name alone
+ if (nameMatch == null)
+ nameMatch = info;
+ //see if the index matches
+ if (buildSpecIndex == -1 || info.getBuildSpecIndex() == -1 || buildSpecIndex == info.getBuildSpecIndex())
+ return info;
+ }
+ }
+ //no exact index match, so return name match, if any
+ return nameMatch;
+ }
+
+ /**
+ * Returns a list of BuilderPersistentInfo.
+ * The list includes entries for all builders that are in the builder spec,
+ * and that have a last built state but have not been instantiated this session.
+ */
+ public ArrayList getBuildersPersistentInfo(IProject project) throws CoreException {
+ return (ArrayList) project.getSessionProperty(K_BUILD_LIST);
+ }
+
+ /**
+ * Returns a build command for the given builder name and project.
+ * First looks for matching command in the project's build spec. If none
+ * is found, a new command is created and returned. This is necessary
+ * because IProject.build allows a builder to be executed that is not in the
+ * build spec.
+ */
+ private ICommand getCommand(IProject project, String builderName, Map args) {
+ ICommand[] buildSpec = ((Project) project).internalGetDescription().getBuildSpec(false);
+ for (int i = 0; i < buildSpec.length; i++)
+ if (buildSpec[i].getBuilderName().equals(builderName))
+ return buildSpec[i];
+ //none found, so create a new command
+ BuildCommand result = new BuildCommand();
+ result.setBuilderName(builderName);
+ result.setArguments(args);
+ return result;
+ }
+
+ IResourceDelta getDelta(IProject project) {
+ try {
+ lock.acquire();
+ if (currentTree == null) {
+ if (Policy.DEBUG_BUILD_FAILURE)
+ Policy.debug("Build: no tree for delta " + debugBuilder() + " [" + debugProject() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return null;
+ }
+ //check if this builder has indicated it cares about this project
+ if (!isInterestingProject(project)) {
+ if (Policy.DEBUG_BUILD_FAILURE)
+ Policy.debug("Build: project not interesting for this builder " + debugBuilder() + " [" + debugProject() + "] " + project.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return null;
+ }
+ //check if this project has changed
+ if (currentDelta != null && currentDelta.findNodeAt(project.getFullPath()) == null) {
+ //if the project never existed (not in delta and not in current tree), return null
+ if (!project.exists())
+ return null;
+ //just return an empty delta rooted at this project
+ return ResourceDeltaFactory.newEmptyDelta(project);
+ }
+ //now check against the cache
+ IResourceDelta result = (IResourceDelta) deltaCache.getDelta(project.getFullPath(), currentLastBuiltTree, currentTree);
+ if (result != null)
+ return result;
+
+ long startTime = 0L;
+ if (Policy.DEBUG_BUILD_DELTA) {
+ startTime = System.currentTimeMillis();
+ Policy.debug("Computing delta for project: " + project.getName()); //$NON-NLS-1$
+ }
+ result = ResourceDeltaFactory.computeDelta(workspace, currentLastBuiltTree, currentTree, project.getFullPath(), -1);
+ deltaCache.cache(project.getFullPath(), currentLastBuiltTree, currentTree, result);
+ if (Policy.DEBUG_BUILD_FAILURE && result == null)
+ Policy.debug("Build: no delta " + debugBuilder() + " [" + debugProject() + "] " + project.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (Policy.DEBUG_BUILD_DELTA)
+ Policy.debug("Finished computing delta, time: " + (System.currentTimeMillis() - startTime) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ return result;
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * Returns the safe runnable instance for invoking a builder
+ */
+ private ISafeRunnable getSafeRunnable(final int trigger, final Map args, final MultiStatus status, final IProgressMonitor monitor) {
+ return new ISafeRunnable() {
+ public void handleException(Throwable e) {
+ if (e instanceof OperationCanceledException) {
+ if (Policy.DEBUG_BUILD_INVOKING)
+ Policy.debug("Build canceled"); //$NON-NLS-1$
+ //just discard built state when a builder cancels, to ensure
+ //that it is called again on the very next build.
+ currentBuilder.forgetLastBuiltState();
+ throw (OperationCanceledException) e;
+ }
+ //ResourceStats.buildException(e);
+ // don't log the exception....it is already being logged in SafeRunner#run
+
+ //add a generic message to the MultiStatus
+ String builderName = currentBuilder.getLabel();
+ if (builderName == null || builderName.length() == 0)
+ builderName = currentBuilder.getClass().getName();
+ String pluginId = currentBuilder.getPluginId();
+ String message = NLS.bind(Messages.events_builderError, builderName, currentBuilder.getProject().getName());
+ status.add(new Status(IStatus.WARNING, pluginId, IResourceStatus.BUILD_FAILED, message, e));
+
+ //add the exception status to the MultiStatus
+ if (e instanceof CoreException)
+ status.add(((CoreException) e).getStatus());
+ }
+
+ public void run() throws Exception {
+ IProject[] prereqs = null;
+ //invoke the appropriate build method depending on the trigger
+ if (trigger != IncrementalProjectBuilder.CLEAN_BUILD)
+ prereqs = currentBuilder.build(trigger, args, monitor);
+ else
+ currentBuilder.clean(monitor);
+ if (prereqs == null)
+ prereqs = new IProject[0];
+ currentBuilder.setInterestingProjects((IProject[]) prereqs.clone());
+ }
+ };
+ }
+
+ /**
+ * We know the work manager is always available in the middle of
+ * a build.
+ */
+ private WorkManager getWorkManager() {
+ try {
+ return workspace.getWorkManager();
+ } catch (CoreException e) {
+ //cannot happen
+ }
+ //avoid compile error
+ return null;
+ }
+
+ public void handleEvent(LifecycleEvent event) {
+ IProject project = null;
+ switch (event.kind) {
+ case LifecycleEvent.PRE_PROJECT_DELETE :
+ case LifecycleEvent.PRE_PROJECT_MOVE :
+ project = (IProject) event.resource;
+ //make sure the builder persistent info is deleted for the project move case
+ if (project.isAccessible())
+ setBuildersPersistentInfo(project, null);
+ }
+ }
+
+ /**
+ * Returns true if the given project has been built during this build cycle, and
+ * false otherwise.
+ */
+ boolean hasBeenBuilt(IProject project) {
+ return builtProjects.contains(project);
+ }
+
+ /**
+ * Hook for adding trace options and debug information at the end of a build.
+ * This hook is called after each builder instance is called.
+ */
+ private void hookEndBuild(IncrementalProjectBuilder builder) {
+ if (ResourceStats.TRACE_BUILDERS)
+ ResourceStats.endBuild();
+ if (!Policy.DEBUG_BUILD_INVOKING || timeStamp == -1)
+ return; //builder wasn't called or we are not debugging
+ Policy.debug("Builder finished: " + toString(builder) + " time: " + (System.currentTimeMillis() - timeStamp) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ timeStamp = -1;
+ }
+
+ /**
+ * Hook for adding trace options and debug information at the end of a build.
+ * This hook is called at the end of a build cycle invoked by calling a
+ * build API method.
+ */
+ private void hookEndBuild(int trigger) {
+ building = false;
+ builtProjects.clear();
+ deltaCache.flush();
+ deltaTreeCache.flush();
+ //ensure autobuild runs after a clean
+ if (trigger == IncrementalProjectBuilder.CLEAN_BUILD)
+ autoBuildJob.forceBuild();
+ }
+
+ /**
+ * Hook for adding trace options and debug information at the start of a build.
+ * This hook is called before each builder instance is called.
+ */
+ private void hookStartBuild(IncrementalProjectBuilder builder, int trigger) {
+ if (ResourceStats.TRACE_BUILDERS)
+ ResourceStats.startBuild(builder);
+ if (Policy.DEBUG_BUILD_INVOKING) {
+ timeStamp = System.currentTimeMillis();
+ Policy.debug("Invoking (" + debugTrigger(trigger) + ") on builder: " + toString(builder)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Hook for adding trace options and debug information at the start of a build.
+ * This hook is called when a build API method is called, before any builders
+ * start running.
+ */
+ private void hookStartBuild(int trigger) {
+ building = true;
+ if (Policy.DEBUG_BUILD_STACK) {
+ IStatus info = new Status(IStatus.INFO, ResourcesPlugin.PI_RESOURCES, 1, "Starting build: " + debugTrigger(trigger), new RuntimeException().fillInStackTrace()); //$NON-NLS-1$
+ Policy.log(info);
+ }
+ }
+
+ /**
+ * Instantiates the builder with the given name. If the builder, its plugin, or its nature
+ * is missing, create a placeholder builder to takes its place. This is needed to generate
+ * appropriate exceptions when somebody tries to invoke the builder, and to
+ * prevent trying to instantiate it every time a build is run.
+ * This method NEVER returns null.
+ */
+ private IncrementalProjectBuilder initializeBuilder(String builderName, IProject project, int buildSpecIndex, MultiStatus status) throws CoreException {
+ IncrementalProjectBuilder builder = null;
+ try {
+ builder = instantiateBuilder(builderName);
+ } catch (CoreException e) {
+ status.add(new ResourceStatus(IResourceStatus.BUILD_FAILED, project.getFullPath(), NLS.bind(Messages.events_instantiate_1, builderName), e));
+ status.add(e.getStatus());
+ }
+ if (builder == null) {
+ //unable to create the builder, so create a placeholder to fill in for it
+ builder = new MissingBuilder(builderName);
+ }
+ // get the map of builders to get the last built tree
+ ArrayList infos = getBuildersPersistentInfo(project);
+ if (infos != null) {
+ BuilderPersistentInfo info = getBuilderInfo(infos, builderName, buildSpecIndex);
+ if (info != null) {
+ infos.remove(info);
+ ElementTree tree = info.getLastBuiltTree();
+ if (tree != null)
+ ((InternalBuilder) builder).setLastBuiltTree(tree);
+ ((InternalBuilder) builder).setInterestingProjects(info.getInterestingProjects());
+ }
+ // delete the build map if it's now empty
+ if (infos.size() == 0)
+ setBuildersPersistentInfo(project, null);
+ }
+ return builder;
+ }
+
+ /**
+ * Instantiates and returns the builder with the given name. If the builder, its plugin, or its nature
+ * is missing, returns null.
+ */
+ private IncrementalProjectBuilder instantiateBuilder(String builderName) throws CoreException {
+ IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, builderName);
+ if (extension == null)
+ return null;
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length == 0)
+ return null;
+ String natureId = null;
+ if (getBooleanAttribute(configs[0], "hasNature")) { //$NON-NLS-1$
+ //find the nature that owns this builder
+ String builderId = extension.getUniqueIdentifier();
+ natureId = workspace.getNatureManager().findNatureForBuilder(builderId);
+ if (natureId == null)
+ return null;
+ }
+ //The nature exists, or this builder doesn't specify a nature
+ InternalBuilder builder = (InternalBuilder) configs[0].createExecutableExtension("run"); //$NON-NLS-1$
+ builder.setPluginId(extension.getContributor().getName());
+ builder.setLabel(extension.getLabel());
+ builder.setNatureId(natureId);
+ builder.setCallOnEmptyDelta(getBooleanAttribute(configs[0], "callOnEmptyDelta")); //$NON-NLS-1$
+ return (IncrementalProjectBuilder) builder;
+ }
+
+ /**
+ * Another thread is attempting to modify the workspace. Cancel the
+ * autobuild and wait until it completes.
+ */
+ public void interrupt() {
+ autoBuildJob.interrupt();
+ }
+
+ /**
+ * Returns whether an autobuild is pending (requested but not yet completed).
+ */
+ public boolean isAutobuildBuildPending() {
+ return autoBuildJob.getState() != Job.NONE;
+
+ }
+
+ /**
+ * Returns true if the current builder is interested in changes
+ * to the given project, and false otherwise.
+ */
+ private boolean isInterestingProject(IProject project) {
+ if (project.equals(currentBuilder.getProject()))
+ return true;
+ IProject[] interestingProjects = currentBuilder.getInterestingProjects();
+ for (int i = 0; i < interestingProjects.length; i++) {
+ if (interestingProjects[i].equals(project)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the given builder needs to be invoked, and false
+ * otherwise.
+ *
+ * The algorithm is to compute the intersection of the set of projects that
+ * have changed since the last build, and the set of projects this builder
+ * cares about. This is an optimization, under the assumption that computing
+ * the forward delta once (not the resource delta) is more efficient than
+ * computing project deltas and invoking builders for projects that haven't
+ * changed.
+ */
+ private boolean needsBuild(InternalBuilder builder, int trigger) {
+ //on some triggers we build regardless of the delta
+ switch (trigger) {
+ case IncrementalProjectBuilder.CLEAN_BUILD :
+ return true;
+ case IncrementalProjectBuilder.FULL_BUILD :
+ return true;
+ case IncrementalProjectBuilder.INCREMENTAL_BUILD :
+ if (currentBuilder.callOnEmptyDelta())
+ return true;
+ //fall through and check if there is a delta
+ }
+
+ //compute the delta since the last built state
+ ElementTree oldTree = builder.getLastBuiltTree();
+ ElementTree newTree = workspace.getElementTree();
+ long start = System.currentTimeMillis();
+ currentDelta = (DeltaDataTree) deltaTreeCache.getDelta(null, oldTree, newTree);
+ if (currentDelta == null) {
+ if (Policy.DEBUG_BUILD_NEEDED) {
+ String message = "Checking if need to build. Starting delta computation between: " + oldTree.toString() + " and " + newTree.toString(); //$NON-NLS-1$ //$NON-NLS-2$
+ Policy.debug(message);
+ }
+ currentDelta = newTree.getDataTree().forwardDeltaWith(oldTree.getDataTree(), ResourceComparator.getBuildComparator());
+ if (Policy.DEBUG_BUILD_NEEDED)
+ Policy.debug("End delta computation. (" + (System.currentTimeMillis() - start) + "ms)."); //$NON-NLS-1$ //$NON-NLS-2$
+ deltaTreeCache.cache(null, oldTree, newTree, currentDelta);
+ }
+
+ //search for the builder's project
+ if (currentDelta.findNodeAt(builder.getProject().getFullPath()) != null) {
+ if (Policy.DEBUG_BUILD_NEEDED)
+ Policy.debug(toString(builder) + " needs building because of changes in: " + builder.getProject().getName()); //$NON-NLS-1$
+ return true;
+ }
+
+ //search for builder's interesting projects
+ IProject[] projects = builder.getInterestingProjects();
+ for (int i = 0; i < projects.length; i++) {
+ if (currentDelta.findNodeAt(projects[i].getFullPath()) != null) {
+ if (Policy.DEBUG_BUILD_NEEDED)
+ Policy.debug(toString(builder) + " needs building because of changes in: " + projects[i].getName()); //$NON-NLS-1$
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes all builders with the given ID from the build spec.
+ * Does nothing if there were no such builders in the spec
+ */
+ private void removeBuilders(IProject project, String builderId) throws CoreException {
+ IProjectDescription desc = project.getDescription();
+ ICommand[] oldSpec = desc.getBuildSpec();
+ int oldLength = oldSpec.length;
+ if (oldLength == 0)
+ return;
+ int remaining = 0;
+ //null out all commands that match the builder to remove
+ for (int i = 0; i < oldSpec.length; i++) {
+ if (oldSpec[i].getBuilderName().equals(builderId))
+ oldSpec[i] = null;
+ else
+ remaining++;
+ }
+ //check if any were actually removed
+ if (remaining == oldSpec.length)
+ return;
+ ICommand[] newSpec = new ICommand[remaining];
+ for (int i = 0, newIndex = 0; i < oldLength; i++) {
+ if (oldSpec[i] != null)
+ newSpec[newIndex++] = oldSpec[i];
+ }
+ desc.setBuildSpec(newSpec);
+ project.setDescription(desc, IResource.NONE, null);
+ }
+
+ /**
+ * Hook for builders to request a rebuild.
+ */
+ void requestRebuild() {
+ rebuildRequested = true;
+ }
+
+ /**
+ * Sets the builder infos for the given project. The builder infos are
+ * an ArrayList of BuilderPersistentInfo.
+ * The list includes entries for all builders that are
+ * in the builder spec, and that have a last built state, even if they
+ * have not been instantiated this session.
+ */
+ public void setBuildersPersistentInfo(IProject project, ArrayList list) {
+ try {
+ project.setSessionProperty(K_BUILD_LIST, list);
+ } catch (CoreException e) {
+ //project is missing -- build state will be lost
+ //can't throw an exception because this happens on startup
+ Policy.log(new ResourceStatus(IStatus.ERROR, 1, project.getFullPath(), "Project missing in setBuildersPersistentInfo", null)); //$NON-NLS-1$
+ }
+ }
+
+ public void shutdown(IProgressMonitor monitor) {
+ autoBuildJob.cancel();
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ workspace.addLifecycleListener(this);
+ }
+
+ /**
+ * Returns a string representation of the given builder.
+ * For debugging purposes only.
+ */
+ private String toString(InternalBuilder builder) {
+ String name = builder.getClass().getName();
+ name = name.substring(name.lastIndexOf('.') + 1);
+ return name + "(" + builder.getProject().getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Returns true if the nature membership rules are satisfied for the given
+ * builder extension on the given project, and false otherwise. A builder that
+ * does not specify that it belongs to a nature is always valid. A builder
+ * extension that belongs to a nature can be invalid for the following reasons:
+ * <ul>
+ * <li>The nature that owns the builder does not exist on the given project</li>
+ * <li>The nature that owns the builder is disabled on the given project</li>
+ * </ul>
+ * Furthermore, if the nature that owns the builder does not exist on the project,
+ * that builder will be removed from the build spec.
+ *
+ * Note: This method only validates nature constraints that can vary at runtime.
+ * Additional checks are done in the instantiateBuilder method for constraints
+ * that cannot vary once the plugin registry is initialized.
+ */
+ private boolean validateNature(InternalBuilder builder, String builderId) throws CoreException {
+ String nature = builder.getNatureId();
+ if (nature == null)
+ return true;
+ IProject project = builder.getProject();
+ if (!project.hasNature(nature)) {
+ //remove this builder from the build spec
+ removeBuilders(project, builderId);
+ return false;
+ }
+ return project.isNatureEnabled(nature);
+ }
+
+ /**
+ * Returns the scheduling rule that is required for building the project.
+ */
+ public ISchedulingRule getRule(IProject project, int trigger, String builderName, Map args) {
+
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
+ if (builderName == null) {
+ final ICommand[] commands;
+ if (project.isAccessible()) {
+ Set rules = new HashSet();
+ commands = ((Project) project).internalGetDescription().getBuildSpec(false);
+ for (int i = 0; i < commands.length; i++) {
+ BuildCommand command = (BuildCommand) commands[i];
+ try {
+ IncrementalProjectBuilder builder = getBuilder(project, command, i, status);
+ if (builder != null)
+ rules.add(builder.getRule());
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ }
+ return new MultiRule((ISchedulingRule[]) rules.toArray(new ISchedulingRule[rules.size()]));
+ }
+ } else {
+ // Returns the derived resources for the specified builderName
+ ICommand command = getCommand(project, builderName, args);
+ try {
+ IncrementalProjectBuilder builder = getBuilder(project, command, -1, status);
+ if (builder != null)
+ return builder.getRule();
+
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ }
+ // Log any errors
+ if (!status.isOK())
+ Policy.log(status);
+ return workspace.getRoot();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/BuilderPersistentInfo.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.internal.resources.ICoreConstants;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.IProject;
+
+public class BuilderPersistentInfo {
+ protected String builderName;
+ /**
+ * Index of this builder in the build spec. A value of -1 indicates
+ * that this index is unknown (it was not serialized in older workspace versions).
+ */
+ private int buildSpecIndex = -1;
+ protected IProject[] interestingProjects = ICoreConstants.EMPTY_PROJECT_ARRAY;
+ protected ElementTree lastBuildTree;
+ protected String projectName;
+
+ public BuilderPersistentInfo(String projectName, String builderName, int buildSpecIndex) {
+ this.projectName = projectName;
+ this.builderName = builderName;
+ this.buildSpecIndex = buildSpecIndex;
+ }
+ public String getBuilderName() {
+ return builderName;
+ }
+
+ public int getBuildSpecIndex() {
+ return buildSpecIndex;
+ }
+
+ public IProject[] getInterestingProjects() {
+ return interestingProjects;
+ }
+
+ public ElementTree getLastBuiltTree() {
+ return lastBuildTree;
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ public void setInterestingProjects(IProject[] projects) {
+ interestingProjects = projects;
+ }
+
+ public void setLastBuildTree(ElementTree tree) {
+ lastBuildTree = tree;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ILifecycleListener.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Interface for clients interested in receiving notification of workspace
+ * lifecycle events.
+ */
+public interface ILifecycleListener {
+ public void handleEvent(LifecycleEvent event) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/InternalBuilder.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.Map;
+import org.eclipse.core.internal.resources.ICoreConstants;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * This class is the internal basis for all builders. Plugin developers should not
+ * subclass this class.
+ *
+ * @see IncrementalProjectBuilder
+ */
+public abstract class InternalBuilder {
+ /**
+ * Hold a direct reference to the build manager as an optimization.
+ * This will be initialized by BuildManager when it is constructed.
+ */
+ static BuildManager buildManager;
+ private ICommand command;
+ private boolean forgetStateRequested = false;
+ private IProject[] interestingProjects = ICoreConstants.EMPTY_PROJECT_ARRAY;
+ /**
+ * Human readable builder name for progress reporting.
+ */
+ private String label;
+ private String natureId;
+ private ElementTree oldState;
+ /**
+ * The symbolic name of the plugin that defines this builder
+ */
+ private String pluginId;
+ private IProject project;
+
+ /**
+ * The value of the callOnEmptyDelta builder extension attribute.
+ */
+ private boolean callOnEmptyDelta = false;
+
+ /*
+ * @see IncrementalProjectBuilder#build
+ */
+ protected abstract IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns the value of the callOnEmptyDelta builder extension attribute.
+ */
+ final boolean callOnEmptyDelta() {
+ return callOnEmptyDelta;
+ }
+ /*
+ * @see IncrementalProjectBuilder
+ */
+ protected abstract void clean(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Clears the request to forget last built states.
+ */
+ final void clearForgetLastBuiltState() {
+ forgetStateRequested = false;
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#forgetLastBuiltState
+ */
+ protected void forgetLastBuiltState() {
+ oldState = null;
+ forgetStateRequested = true;
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#getCommand
+ */
+ protected ICommand getCommand() {
+ return (ICommand)((BuildCommand)command).clone();
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#forgetLastBuiltState
+ */
+ protected IResourceDelta getDelta(IProject aProject) {
+ return buildManager.getDelta(aProject);
+ }
+
+ final IProject[] getInterestingProjects() {
+ return interestingProjects;
+ }
+
+ final String getLabel() {
+ return label;
+ }
+
+ final ElementTree getLastBuiltTree() {
+ return oldState;
+ }
+
+ /**
+ * Returns the ID of the nature that owns this builder. Returns null if the
+ * builder does not belong to a nature.
+ */
+ final String getNatureId() {
+ return natureId;
+ }
+
+ final String getPluginId() {
+ return pluginId;
+ }
+
+ /**
+ * Returns the project for this builder
+ */
+ protected IProject getProject() {
+ return project;
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#hasBeenBuilt
+ */
+ protected boolean hasBeenBuilt(IProject aProject) {
+ return buildManager.hasBeenBuilt(aProject);
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#isInterrupted
+ */
+ public boolean isInterrupted() {
+ return buildManager.autoBuildJob.isInterrupted();
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#needRebuild
+ */
+ protected void needRebuild() {
+ buildManager.requestRebuild();
+ }
+
+ final void setCallOnEmptyDelta(boolean value) {
+ this.callOnEmptyDelta = value;
+ }
+
+ final void setCommand(ICommand value) {
+ this.command = value;
+ }
+
+ final void setInterestingProjects(IProject[] value) {
+ interestingProjects = value;
+ }
+
+ final void setLabel(String value) {
+ this.label = value;
+ }
+
+ final void setLastBuiltTree(ElementTree value) {
+ oldState = value;
+ }
+
+ final void setNatureId(String id) {
+ this.natureId = id;
+ }
+
+ final void setPluginId(String value) {
+ pluginId = value;
+ }
+
+ /**
+ * Sets the project for which this builder operates.
+ * @see #getProject()
+ */
+ final void setProject(IProject value) {
+ Assert.isTrue(project == null);
+ project = value;
+ }
+
+ /*
+ * @see IncrementalProjectBuilder#startupOnInitialize
+ */
+ protected abstract void startupOnInitialize();
+
+ /**
+ * Returns true if the builder requested that its last built state be
+ * forgotten, and false otherwise.
+ */
+ final boolean wasForgetStateRequested() {
+ return forgetStateRequested;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/LifecycleEvent.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * Class used for broadcasting internal workspace lifecycle events. There is a
+ * singleton instance, so no listener is allowed to keep references to the event
+ * after the notification is finished.
+ */
+public class LifecycleEvent {
+ //constants for kinds of internal workspace lifecycle events
+ public static final int PRE_PROJECT_CLOSE = 0x01;
+ public static final int PRE_PROJECT_CHANGE = 0x02;
+ public static final int PRE_PROJECT_COPY = 0x04;
+ public static final int PRE_PROJECT_CREATE = 0x08;
+
+ public static final int PRE_PROJECT_DELETE = 0x10;
+ public static final int PRE_PROJECT_OPEN = 0x20;
+ public static final int PRE_PROJECT_MOVE = 0x40;
+
+ public static final int PRE_LINK_COPY = 0x100;
+ public static final int PRE_LINK_CREATE = 0x200;
+ public static final int PRE_LINK_DELETE = 0x400;
+ public static final int PRE_LINK_MOVE = 0x800;
+ public static final int PRE_REFRESH = 0x1000;
+
+ /**
+ * The kind of event
+ */
+ public int kind;
+ /**
+ * For events that only involve one resource, this is it. More
+ * specifically, this is used for all events that don't involve a more or
+ * copy. For copy/move events, this resource represents the source of the
+ * copy/move.
+ */
+ public IResource resource;
+ /**
+ * For copy/move events, this resource represents the destination of the
+ * copy/move.
+ */
+ public IResource newResource;
+
+ /**
+ * The update flags for the event.
+ */
+ public int updateFlags;
+
+ private static final LifecycleEvent instance = new LifecycleEvent();
+
+ private LifecycleEvent() {
+ super();
+ }
+
+ public static LifecycleEvent newEvent(int kind, IResource resource) {
+ instance.kind = kind;
+ instance.resource = resource;
+ instance.newResource = null;
+ instance.updateFlags = 0;
+ return instance;
+ }
+
+ public static LifecycleEvent newEvent(int kind, IResource oldResource, IResource newResource, int updateFlags) {
+ instance.kind = kind;
+ instance.resource = oldResource;
+ instance.newResource = newResource;
+ instance.updateFlags = updateFlags;
+ return instance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/NodeIDMap.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A specialized map that maps Node IDs to their old and new paths.
+ * Used for calculating moves during resource change notification.
+ */
+public class NodeIDMap {
+ //using prime table sizes improves our hash function
+ private static final int[] SIZES = new int[] {13, 29, 71, 173, 349, 733, 1511, 3079, 6133, 16381, 32653, 65543, 131111, 262139, 524287, 1051601};
+ private static final double LOAD_FACTOR = 0.75;
+ //2^32 * golden ratio
+ private static final long LARGE_NUMBER = 2654435761L;
+
+ int sizeOffset = 0;
+ protected int elementCount = 0;
+ protected long[] ids;
+ protected IPath[] oldPaths;
+ protected IPath[] newPaths;
+
+ /**
+ * Creates a new node ID map of default capacity.
+ */
+ public NodeIDMap() {
+ this.sizeOffset = 0;
+ this.ids = new long[SIZES[sizeOffset]];
+ this.oldPaths = new IPath[SIZES[sizeOffset]];
+ this.newPaths = new IPath[SIZES[sizeOffset]];
+ }
+
+ /**
+ * The array isn't large enough so double its size and rehash
+ * all its current values.
+ */
+ protected void expand() {
+ int newLength;
+ try {
+ newLength = SIZES[++sizeOffset];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //will only occur if there are > 1 million elements in delta
+ newLength = ids.length * 2;
+ }
+ long[] grownIds = new long[newLength];
+ IPath[] grownOldPaths = new IPath[newLength];
+ IPath[] grownNewPaths = new IPath[newLength];
+ int maxArrayIndex = newLength - 1;
+ for (int i = 0; i < ids.length; i++) {
+ long id = ids[i];
+ if (id != 0) {
+ int hash = hashFor(id, newLength);
+ while (grownIds[hash] != 0) {
+ hash++;
+ if (hash > maxArrayIndex)
+ hash = 0;
+ }
+ grownIds[hash] = id;
+ grownOldPaths[hash] = oldPaths[i];
+ grownNewPaths[hash] = newPaths[i];
+ }
+ }
+ ids = grownIds;
+ oldPaths = grownOldPaths;
+ newPaths = grownNewPaths;
+ }
+
+ /**
+ * Returns the index of the given element in the map. If not
+ * found, returns -1.
+ */
+ private int getIndex(long searchID) {
+ final int len = ids.length;
+ int hash = hashFor(searchID, len);
+
+ // search the last half of the array
+ for (int i = hash; i < len; i++) {
+ if (ids[i] == searchID)
+ return i;
+ // marker info not found so return -1
+ if (ids[i] == 0)
+ return -1;
+ }
+
+ // search the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ if (ids[i] == searchID)
+ return i;
+ // marker info not found so return -1
+ if (ids[i] == 0)
+ return -1;
+ }
+ // marker info not found so return -1
+ return -1;
+ }
+
+ /**
+ * Returns the new path location for the given ID, or null
+ * if no new path is available.
+ */
+ public IPath getNewPath(long nodeID) {
+ int index = getIndex(nodeID);
+ if (index == -1)
+ return null;
+ return newPaths[index];
+ }
+
+ /**
+ * Returns the old path location for the given ID, or null
+ * if no old path is available.
+ */
+ public IPath getOldPath(long nodeID) {
+ int index = getIndex(nodeID);
+ if (index == -1)
+ return null;
+ return oldPaths[index];
+ }
+
+ private int hashFor(long id, int size) {
+ //Knuth's hash function from Art of Computer Programming section 6.4
+ return (int) Math.abs((id * LARGE_NUMBER) % size);
+ }
+
+ /**
+ * Returns true if there are no elements in the map, and
+ * false otherwise.
+ */
+ public boolean isEmpty() {
+ return elementCount == 0;
+ }
+
+ /**
+ * Adds the given path mappings to the map. If either oldPath
+ * or newPath is null, they are ignored (old map values are not overwritten).
+ */
+ private void put(long id, IPath oldPath, IPath newPath) {
+ if (oldPath == null && newPath == null)
+ return;
+ int hash = hashFor(id, ids.length);
+
+ // search for an empty slot at the end of the array
+ for (int i = hash; i < ids.length; i++) {
+ if (ids[i] == id) {
+ //replace value for existing entry
+ if (oldPath != null)
+ oldPaths[i] = oldPath;
+ if (newPath != null)
+ newPaths[i] = newPath;
+ return;
+ }
+ if (ids[i] == 0) {
+ //add a new entry to the map
+ ids[i] = id;
+ if (oldPath != null)
+ oldPaths[i] = oldPath;
+ if (newPath != null)
+ newPaths[i] = newPath;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return;
+ }
+ }
+
+ // search for an empty slot at the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ if (ids[i] == id) {
+ //replace value for existing entry
+ if (oldPath != null)
+ oldPaths[i] = oldPath;
+ if (newPath != null)
+ newPaths[i] = newPath;
+ return;
+ }
+ if (ids[i] == 0) {
+ //add a new entry to the map
+ ids[i] = id;
+ if (oldPath != null)
+ oldPaths[i] = oldPath;
+ if (newPath != null)
+ newPaths[i] = newPath;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return;
+ }
+ }
+ // if we didn't find a free slot, then try again with the expanded set
+ expand();
+ put(id, oldPath, newPath);
+ }
+
+ /**
+ * Adds an entry for a node's old path
+ */
+ public void putOldPath(long id, IPath path) {
+ put(id, path, null);
+ }
+
+ /**
+ * Adds an entry for a node's old path
+ */
+ public void putNewPath(long id, IPath path) {
+ put(id, null, path);
+ }
+
+ private boolean shouldGrow() {
+ return elementCount > ids.length * LOAD_FACTOR;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/NotificationManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,331 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.*;
+import org.eclipse.core.internal.resources.IManager;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+
+public class NotificationManager implements IManager, ILifecycleListener {
+ class NotifyJob extends Job {
+ private final IWorkspaceRunnable noop = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) {
+ // do nothing
+ }
+ };
+
+ public NotifyJob() {
+ super(Messages.resources_updating);
+ setSystem(true);
+ }
+
+ public IStatus run(IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ return Status.CANCEL_STATUS;
+ notificationRequested = true;
+ try {
+ workspace.run(noop, null, IResource.NONE, null);
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ return Status.OK_STATUS;
+ }
+ }
+
+ private static final long NOTIFICATION_DELAY = 1500;
+ /**
+ * The Threads that are currently avoiding notification.
+ */
+ private Set avoidNotify = new HashSet();
+
+ /**
+ * Indicates whether a notification is currently in progress. Used to avoid
+ * causing a notification to be requested as a result of another notification.
+ */
+ protected boolean isNotifying;
+
+ // if there are no changes between the current tree and the last delta state then we
+ // can reuse the lastDelta (if any). If the lastMarkerChangeId is different then the current
+ // one then we have to update that delta with new marker change info
+ /**
+ * last delta we broadcast
+ */
+ private ResourceDelta lastDelta;
+ /**
+ * the marker change Id the last time we computed a delta
+ */
+ private long lastDeltaId;
+ /**
+ * tree the last time we computed a delta
+ */
+ private ElementTree lastDeltaState;
+ protected long lastNotifyDuration = 0L;
+ /**
+ * the marker change id at the end of the last POST_AUTO_BUILD
+ */
+ private long lastPostBuildId = 0;
+ /**
+ * The state of the workspace at the end of the last POST_BUILD
+ * notification
+ */
+ private ElementTree lastPostBuildTree;
+ /**
+ * the marker change id at the end of the last POST_CHANGE
+ */
+ private long lastPostChangeId = 0;
+ /**
+ * The state of the workspace at the end of the last POST_CHANGE
+ * notification
+ */
+ private ElementTree lastPostChangeTree;
+
+ private ResourceChangeListenerList listeners;
+
+ protected boolean notificationRequested = false;
+ private Job notifyJob;
+ Workspace workspace;
+
+ public NotificationManager(Workspace workspace) {
+ this.workspace = workspace;
+ listeners = new ResourceChangeListenerList();
+ notifyJob = new NotifyJob();
+ }
+
+ public void addListener(IResourceChangeListener listener, int eventMask) {
+ listeners.add(listener, eventMask);
+ if (ResourceStats.TRACE_LISTENERS)
+ ResourceStats.listenerAdded(listener);
+ }
+
+ /**
+ * Indicates the beginning of a block where periodic notifications should be avoided.
+ * Returns true if notification avoidance really started, and false for nested
+ * operations.
+ */
+ public boolean beginAvoidNotify() {
+ return avoidNotify.add(Thread.currentThread());
+ }
+
+ /**
+ * Signals the beginning of the notification phase at the end of a top level operation.
+ */
+ public void beginNotify() {
+ notifyJob.cancel();
+ notificationRequested = false;
+ }
+
+ /**
+ * The main broadcast point for notification deltas
+ */
+ public void broadcastChanges(ElementTree lastState, ResourceChangeEvent event, boolean lockTree) {
+ final int type = event.getType();
+ try {
+ // Do the notification if there are listeners for events of the given type.
+ if (!listeners.hasListenerFor(type))
+ return;
+ isNotifying = true;
+ ResourceDelta delta = getDelta(lastState, type);
+ //don't broadcast POST_CHANGE or autobuild events if the delta is empty
+ if (delta == null || delta.getKind() == 0) {
+ int trigger = event.getBuildKind();
+ if (trigger == IncrementalProjectBuilder.AUTO_BUILD || trigger == 0)
+ return;
+ }
+ event.setDelta(delta);
+ long start = System.currentTimeMillis();
+ notify(getListeners(), event, lockTree);
+ lastNotifyDuration = System.currentTimeMillis() - start;
+ } finally {
+ // Update the state regardless of whether people are listening.
+ isNotifying = false;
+ cleanUp(lastState, type);
+ }
+ }
+
+ /**
+ * Performs cleanup at the end of a resource change notification
+ */
+ private void cleanUp(ElementTree lastState, int type) {
+ // Remember the current state as the last notified state if requested.
+ // Be sure to clear out the old delta
+ boolean postChange = type == IResourceChangeEvent.POST_CHANGE;
+ if (postChange || type == IResourceChangeEvent.POST_BUILD) {
+ long id = workspace.getMarkerManager().getChangeId();
+ lastState.immutable();
+ if (postChange) {
+ lastPostChangeTree = lastState;
+ lastPostChangeId = id;
+ } else {
+ lastPostBuildTree = lastState;
+ lastPostBuildId = id;
+ }
+ workspace.getMarkerManager().resetMarkerDeltas(Math.min(lastPostBuildId, lastPostChangeId));
+ lastDelta = null;
+ lastDeltaState = lastState;
+ }
+ }
+
+ /**
+ * Helper method for the save participant lifecycle computation. */
+ public void broadcastChanges(IResourceChangeListener listener, int type, IResourceDelta delta) {
+ ResourceChangeListenerList.ListenerEntry[] entries;
+ entries = new ResourceChangeListenerList.ListenerEntry[] {new ResourceChangeListenerList.ListenerEntry(listener, type)};
+ notify(entries, new ResourceChangeEvent(workspace, type, 0, delta), false);
+ }
+
+ /**
+ * Indicates the end of a block where periodic notifications should be avoided.
+ */
+ public void endAvoidNotify() {
+ avoidNotify.remove(Thread.currentThread());
+ }
+
+ /**
+ * Requests that a periodic notification be scheduled
+ */
+ public void requestNotify() {
+ //don't do intermediate notifications if the current thread doesn't want them
+ if (isNotifying || avoidNotify.contains(Thread.currentThread()))
+ return;
+ //notifications must never take more than one tenth of operation time
+ long delay = Math.max(NOTIFICATION_DELAY, lastNotifyDuration * 10);
+ if (notifyJob.getState() == Job.NONE)
+ notifyJob.schedule(delay);
+ }
+
+ /**
+ * Computes and returns the resource delta for the given event type and the
+ * given current tree state.
+ */
+ protected ResourceDelta getDelta(ElementTree tree, int type) {
+ long id = workspace.getMarkerManager().getChangeId();
+ // If we have a delta from last time and no resources have changed
+ // since then, we can reuse the delta structure.
+ // However, be sure not to mix deltas from post_change with build events, because they use
+ // a different reference point for delta computation.
+ boolean postChange = type == IResourceChangeEvent.POST_CHANGE;
+ if (!postChange && lastDelta != null && !ElementTree.hasChanges(tree, lastDeltaState, ResourceComparator.getNotificationComparator(), true)) {
+ // Markers may have changed since the delta was generated. If so, get the new
+ // marker state and insert it in to the delta which is being reused.
+ if (id != lastDeltaId) {
+ Map markerDeltas = workspace.getMarkerManager().getMarkerDeltas(lastPostBuildId);
+ lastDelta.updateMarkers(markerDeltas);
+ }
+ } else {
+ // We don't have a delta or something changed so recompute the whole deal.
+ ElementTree oldTree = postChange ? lastPostChangeTree : lastPostBuildTree;
+ long markerId = postChange ? lastPostChangeId : lastPostBuildId;
+ lastDelta = ResourceDeltaFactory.computeDelta(workspace, oldTree, tree, Path.ROOT, markerId + 1);
+ }
+ // remember the state of the world when this delta was consistent
+ lastDeltaState = tree;
+ lastDeltaId = id;
+ return lastDelta;
+ }
+
+ protected ResourceChangeListenerList.ListenerEntry[] getListeners() {
+ return listeners.getListeners();
+ }
+
+ public void handleEvent(LifecycleEvent event) {
+ switch (event.kind) {
+ case LifecycleEvent.PRE_PROJECT_CLOSE :
+ if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_CLOSE))
+ return;
+ IProject project = (IProject) event.resource;
+ notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_CLOSE, project), true);
+ break;
+ case LifecycleEvent.PRE_PROJECT_MOVE :
+ //only notify deletion on move if old project handle is going
+ // away
+ if (event.resource.equals(event.newResource))
+ return;
+ //fall through
+ case LifecycleEvent.PRE_PROJECT_DELETE :
+ if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_DELETE))
+ return;
+ project = (IProject) event.resource;
+ notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_DELETE, project), true);
+ break;
+ case LifecycleEvent.PRE_REFRESH :
+ if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_REFRESH))
+ return;
+ if (event.resource.getType() == IResource.PROJECT)
+ notify(getListeners(), new ResourceChangeEvent(event.resource, IResourceChangeEvent.PRE_REFRESH, event.resource), true);
+ else if (event.resource.getType() == IResource.ROOT)
+ notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_REFRESH, null), true);
+ break;
+ }
+ }
+
+ private void notify(ResourceChangeListenerList.ListenerEntry[] resourceListeners, final IResourceChangeEvent event, final boolean lockTree) {
+ int type = event.getType();
+ boolean oldLock = workspace.isTreeLocked();
+ if (lockTree)
+ workspace.setTreeLocked(true);
+ try {
+ for (int i = 0; i < resourceListeners.length; i++) {
+ if ((type & resourceListeners[i].eventMask) != 0) {
+ final IResourceChangeListener listener = resourceListeners[i].listener;
+ if (ResourceStats.TRACE_LISTENERS)
+ ResourceStats.startNotify(listener);
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable e) {
+ // exception logged in SafeRunner#run
+ }
+
+ public void run() throws Exception {
+ listener.resourceChanged(event);
+ }
+ });
+ if (ResourceStats.TRACE_LISTENERS)
+ ResourceStats.endNotify();
+ }
+ }
+ } finally {
+ if (lockTree)
+ workspace.setTreeLocked(oldLock);
+ }
+ }
+
+ public void removeListener(IResourceChangeListener listener) {
+ listeners.remove(listener);
+ if (ResourceStats.TRACE_LISTENERS)
+ ResourceStats.listenerRemoved(listener);
+ }
+
+ /**
+ * Returns true if a notification is needed. This happens if
+ * sufficient time has elapsed since the last notification
+ * @return true if a notification is needed, and false otherwise
+ */
+ public boolean shouldNotify() {
+ return !isNotifying && notificationRequested;
+ }
+
+ public void shutdown(IProgressMonitor monitor) {
+ //wipe out any existing listeners
+ listeners = new ResourceChangeListenerList();
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ // get the current state of the workspace as the starting point and
+ // tell the workspace to track changes from there. This gives the
+ // notification manager an initial basis for comparison.
+ lastPostBuildTree = lastPostChangeTree = workspace.getElementTree();
+ workspace.addLifecycleListener(this);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/PathVariableChangeEvent.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.events;
+
+import java.util.EventObject;
+import org.eclipse.core.resources.IPathVariableChangeEvent;
+import org.eclipse.core.resources.IPathVariableManager;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Describes a change in path variable. Core's default implementation for the
+ * <code>IPathVariableChangeEvent</code> interface.
+ */
+public class PathVariableChangeEvent extends EventObject implements IPathVariableChangeEvent {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The name of the changed variable.
+ */
+ private String variableName;
+
+ /**
+ * The value of the changed variable (may be null).
+ */
+ private IPath value;
+
+ /** The event type. */
+ private int type;
+
+ /**
+ * Constructor for this class.
+ */
+ public PathVariableChangeEvent(IPathVariableManager source, String variableName, IPath value, int type) {
+ super(source);
+ if (type < VARIABLE_CHANGED || type > VARIABLE_DELETED)
+ throw new IllegalArgumentException("Invalid event type: " + type); //$NON-NLS-1$
+ this.variableName = variableName;
+ this.value = value;
+ this.type = type;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableChangeEvent#getValue()
+ */
+ public IPath getValue() {
+ return value;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableChangeEvent#getVariableName()
+ */
+ public String getVariableName() {
+ return variableName;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableChangeEvent#getType()
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString() {
+ String[] typeStrings = {"VARIABLE_CHANGED", "VARIABLE_CREATED", "VARIABLE_DELETED"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ StringBuffer sb = new StringBuffer(getClass().getName());
+ sb.append("[variable = "); //$NON-NLS-1$
+ sb.append(variableName);
+ sb.append(", type = "); //$NON-NLS-1$
+ sb.append(typeStrings[type - 1]);
+ if (type != VARIABLE_DELETED) {
+ sb.append(", value = "); //$NON-NLS-1$
+ sb.append(value);
+ }
+ sb.append("]"); //$NON-NLS-1$
+ return sb.toString();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceChangeEvent.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.*;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.resources.*;
+
+public class ResourceChangeEvent extends EventObject implements IResourceChangeEvent {
+
+ private static final IMarkerDelta[] NO_MARKER_DELTAS = new IMarkerDelta[0];
+ private static final long serialVersionUID = 1L;
+ IResourceDelta delta;
+ IResource resource;
+
+ /**
+ * The build trigger for this event, or 0 if not applicable.
+ */
+ private int trigger = 0;
+ int type;
+
+ protected ResourceChangeEvent(Object source, int type, IResource resource) {
+ super(source);
+ this.resource = resource;
+ this.type = type;
+ }
+
+ public ResourceChangeEvent(Object source, int type, int buildKind, IResourceDelta delta) {
+ super(source);
+ this.delta = delta;
+ this.trigger = buildKind;
+ this.type = type;
+ }
+
+ /**
+ * @see IResourceChangeEvent#findMarkerDeltas(String, boolean)
+ */
+ public IMarkerDelta[] findMarkerDeltas(String findType, boolean includeSubtypes) {
+ if (delta == null)
+ return NO_MARKER_DELTAS;
+ ResourceDeltaInfo info = ((ResourceDelta) delta).getDeltaInfo();
+ if (info == null)
+ return NO_MARKER_DELTAS;
+ //Map of IPath -> MarkerSet containing MarkerDelta objects
+ Map markerDeltas = info.getMarkerDeltas();
+ if (markerDeltas == null || markerDeltas.size() == 0)
+ return NO_MARKER_DELTAS;
+ ArrayList matching = new ArrayList();
+ Iterator deltaSets = markerDeltas.values().iterator();
+ while (deltaSets.hasNext()) {
+ MarkerSet deltas = (MarkerSet) deltaSets.next();
+ IMarkerSetElement[] elements = deltas.elements();
+ for (int i = 0; i < elements.length; i++) {
+ MarkerDelta markerDelta = (MarkerDelta) elements[i];
+ //our inclusion test depends on whether we are considering subtypes
+ if (findType == null || (includeSubtypes ? markerDelta.isSubtypeOf(findType) : markerDelta.getType().equals(findType)))
+ matching.add(markerDelta);
+ }
+ }
+ return (IMarkerDelta[]) matching.toArray(new IMarkerDelta[matching.size()]);
+ }
+
+ /**
+ * @see IResourceChangeEvent#getBuildKind()
+ */
+ public int getBuildKind() {
+ return trigger;
+ }
+
+ /**
+ * @see IResourceChangeEvent#getDelta()
+ */
+ public IResourceDelta getDelta() {
+ return delta;
+ }
+
+ /**
+ * @see IResourceChangeEvent#getResource()
+ */
+ public IResource getResource() {
+ return resource;
+ }
+
+ /**
+ * @see IResourceChangeEvent#getType()
+ */
+ public int getType() {
+ return type;
+ }
+
+ public void setDelta(IResourceDelta value) {
+ delta = value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceChangeListenerList.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * This class is used to maintain a list of listeners. It is a fairly lightweight object,
+ * occupying minimal space when no listeners are registered.
+ * <p>
+ * Note that the <code>add</code> method checks for and eliminates
+ * duplicates based on identity (not equality). Likewise, the
+ * <code>remove</code> method compares based on identity.
+ * </p>
+ * <p>
+ * This implementation is thread safe. The listener list is copied every time
+ * it is modified, so readers do not need to copy or synchronize. This optimizes
+ * for frequent reads and infrequent writes, and assumes that readers can
+ * be trusted not to modify the returned array.
+ */
+public class ResourceChangeListenerList {
+
+ static class ListenerEntry {
+ int eventMask;
+ IResourceChangeListener listener;
+
+ ListenerEntry(IResourceChangeListener listener, int eventMask) {
+ this.listener = listener;
+ this.eventMask = eventMask;
+ }
+ }
+
+ /**
+ * The empty array singleton instance.
+ */
+ private static final ListenerEntry[] EMPTY_ARRAY = new ListenerEntry[0];
+
+ private int count1 = 0;
+ private int count2 = 0;
+ private int count4 = 0;
+ private int count8 = 0;
+ private int count16 = 0;
+ private int count32 = 0;
+
+ /**
+ * The list of listeners. Maintains invariant: listeners != null.
+ */
+ private volatile ListenerEntry[] listeners = EMPTY_ARRAY;
+
+ /**
+ * Adds the given listener to this list. Has no effect if an identical listener
+ * is already registered.
+ *
+ * @param listener the listener
+ * @param mask event types
+ */
+ public synchronized void add(IResourceChangeListener listener, int mask) {
+ Assert.isNotNull(listener);
+ if (mask == 0) {
+ remove(listener);
+ return;
+ }
+ ResourceChangeListenerList.ListenerEntry entry = new ResourceChangeListenerList.ListenerEntry(listener, mask);
+ final int oldSize = listeners.length;
+ // check for duplicates using identity
+ for (int i = 0; i < oldSize; ++i) {
+ if (listeners[i].listener == listener) {
+ removing(listeners[i].eventMask);
+ adding(mask);
+ listeners[i] = entry;
+ return;
+ }
+ }
+ adding(mask);
+ // Thread safety: copy on write to protect concurrent readers.
+ ListenerEntry[] newListeners = new ListenerEntry[oldSize + 1];
+ System.arraycopy(listeners, 0, newListeners, 0, oldSize);
+ newListeners[oldSize] = entry;
+ //atomic assignment
+ this.listeners = newListeners;
+ }
+
+ private void adding(int mask) {
+ if ((mask & 1) != 0)
+ count1++;
+ if ((mask & 2) != 0)
+ count2++;
+ if ((mask & 4) != 0)
+ count4++;
+ if ((mask & 8) != 0)
+ count8++;
+ if ((mask & 16) != 0)
+ count16++;
+ if ((mask & 32) != 0)
+ count32++;
+ }
+
+ /**
+ * Returns an array containing all the registered listeners.
+ * The resulting array is unaffected by subsequent adds or removes.
+ * If there are no listeners registered, the result is an empty array
+ * singleton instance (no garbage is created).
+ * Use this method when notifying listeners, so that any modifications
+ * to the listener list during the notification will have no effect on the
+ * notification itself.
+ * <p>
+ * Note: Clients must not modify the returned list
+ * @return the list of registered listeners that must not be modified
+ */
+ public ListenerEntry[] getListeners() {
+ return listeners;
+ }
+
+ public boolean hasListenerFor(int event) {
+ if (event == 1)
+ return count1 > 0;
+ if (event == 2)
+ return count2 > 0;
+ if (event == 4)
+ return count4 > 0;
+ if (event == 8)
+ return count8 > 0;
+ if (event == 16)
+ return count16 > 0;
+ if (event == 32)
+ return count32 > 0;
+ return false;
+ }
+
+ /**
+ * Removes the given listener from this list. Has no effect if an identical
+ * listener was not already registered.
+ *
+ * @param listener the listener to remove
+ */
+ public synchronized void remove(IResourceChangeListener listener) {
+ Assert.isNotNull(listener);
+ final int oldSize = listeners.length;
+ for (int i = 0; i < oldSize; ++i) {
+ if (listeners[i].listener == listener) {
+ removing(listeners[i].eventMask);
+ if (oldSize == 1) {
+ listeners = EMPTY_ARRAY;
+ } else {
+ // Thread safety: create new array to avoid affecting concurrent readers
+ ListenerEntry[] newListeners = new ListenerEntry[oldSize - 1];
+ System.arraycopy(listeners, 0, newListeners, 0, i);
+ System.arraycopy(listeners, i + 1, newListeners, i, oldSize - i - 1);
+ //atomic assignment to field
+ this.listeners = newListeners;
+ }
+ return;
+ }
+ }
+ }
+
+ private void removing(int mask) {
+ if ((mask & 1) != 0)
+ count1--;
+ if ((mask & 2) != 0)
+ count2--;
+ if ((mask & 4) != 0)
+ count4--;
+ if ((mask & 8) != 0)
+ count8--;
+ if ((mask & 16) != 0)
+ count16--;
+ if ((mask & 32) != 0)
+ count32--;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceComparator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.internal.resources.ICoreConstants;
+import org.eclipse.core.internal.resources.ResourceInfo;
+import org.eclipse.core.internal.watson.IElementComparator;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+
+/**
+ * Compares two Resources and returns flags describing how
+ * they have changed, for use in computing deltas.
+ * Implementation note: rather than defining a partial order
+ * as specified by IComparator, the compare operation returns
+ * a set of flags instead. The delta computation only cares
+ * whether the comparison is zero (equal) or non-zero (not equal).
+ */
+public class ResourceComparator implements IElementComparator, ICoreConstants {
+ /* Singleton instances */
+ protected static final ResourceComparator notificationSingleton = new ResourceComparator(true, false);
+ protected static final ResourceComparator buildSingleton = new ResourceComparator(false, false);
+
+ /**
+ * Boolean indicating whether or not this comparator is to be used for
+ * a notification. (as opposed to a build) Notifications include extra information
+ * like marker and sync info changes.
+ */
+ private boolean notification;
+
+ /**
+ * Boolean indicating whether or not this comparator is to be used for
+ * snapshot. Snapshots care about extra information such as the used bit.
+ */
+ private boolean save;
+
+ /**
+ * Returns a comparator which compares resource infos, suitable for computing
+ * save and snapshot deltas.
+ */
+ public static ResourceComparator getSaveComparator() {
+ return new ResourceComparator(false, true);
+ }
+
+ /**
+ * Returns a comparator which compares resource infos, suitable for computing
+ * build deltas.
+ */
+ public static ResourceComparator getBuildComparator() {
+ return buildSingleton;
+ }
+
+ /**
+ * Returns a comparator which compares resource infos, suitable for computing
+ * build deltas.
+ */
+ public static ResourceComparator getNotificationComparator() {
+ return notificationSingleton;
+ }
+
+ /**
+ * Create a comparator which compares resource infos.
+ * @param notification if true, check for marker deltas.
+ * @param save if true, check for all resource changes that snapshot needs
+ */
+ private ResourceComparator(boolean notification, boolean save) {
+ this.notification = notification;
+ this.save = save;
+ }
+
+ /**
+ * Compare the ElementInfos for two resources.
+ */
+ public int compare(Object o1, Object o2) {
+ // == handles null, null.
+ if (o1 == o2)
+ return IResourceDelta.NO_CHANGE;
+ int result = 0;
+ if (o1 == null)
+ return ((ResourceInfo) o2).isSet(M_PHANTOM) ? IResourceDelta.ADDED_PHANTOM : IResourceDelta.ADDED;
+ if (o2 == null)
+ return ((ResourceInfo) o1).isSet(M_PHANTOM) ? IResourceDelta.REMOVED_PHANTOM : IResourceDelta.REMOVED;
+ if (!(o1 instanceof ResourceInfo && o2 instanceof ResourceInfo))
+ return IResourceDelta.NO_CHANGE;
+ ResourceInfo oldElement = (ResourceInfo) o1;
+ ResourceInfo newElement = (ResourceInfo) o2;
+ if (!oldElement.isSet(M_PHANTOM) && newElement.isSet(M_PHANTOM))
+ return IResourceDelta.REMOVED;
+ if (oldElement.isSet(M_PHANTOM) && !newElement.isSet(M_PHANTOM))
+ return IResourceDelta.ADDED;
+ if (!compareOpen(oldElement, newElement))
+ result |= IResourceDelta.OPEN;
+ if (!compareContents(oldElement, newElement)) {
+ if (oldElement.getType() == IResource.PROJECT)
+ result |= IResourceDelta.DESCRIPTION;
+ else if (newElement.getType() == IResource.FILE || oldElement.getType() == IResource.FILE)
+ result |= IResourceDelta.CONTENT;
+ }
+ if (!compareType(oldElement, newElement))
+ result |= IResourceDelta.TYPE;
+ if (!compareNodeIDs(oldElement, newElement)) {
+ result |= IResourceDelta.REPLACED;
+ // if the node was replaced and the old and new were files, this is also a content change.
+ if (oldElement.getType() == IResource.FILE && newElement.getType() == IResource.FILE)
+ result |= IResourceDelta.CONTENT;
+ }
+ if (compareLocal(oldElement, newElement))
+ result |= IResourceDelta.LOCAL_CHANGED;
+ if (!compareCharsets(oldElement, newElement))
+ result |= IResourceDelta.ENCODING;
+ if (notification && !compareSync(oldElement, newElement))
+ result |= IResourceDelta.SYNC;
+ if (notification && !compareMarkers(oldElement, newElement))
+ result |= IResourceDelta.MARKERS;
+ if (save && !compareUsed(oldElement, newElement))
+ result |= IResourceDelta.CHANGED;
+ return result == 0 ? 0 : result | IResourceDelta.CHANGED;
+ }
+
+ private boolean compareCharsets(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.getCharsetGenerationCount() == newElement.getCharsetGenerationCount();
+ }
+
+ /**
+ * Compares the contents of the ResourceInfo.
+ */
+ private boolean compareContents(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.getContentId() == newElement.getContentId();
+ }
+
+ /**
+ * Compares the existence of local files/folders for two linked resources.
+ */
+ private boolean compareLocal(ResourceInfo oldElement, ResourceInfo newElement) {
+ //only applicable for linked resources
+ if (!oldElement.isSet(ICoreConstants.M_LINK) || !newElement.isSet(ICoreConstants.M_LINK))
+ return false;
+ long oldStamp = oldElement.getModificationStamp();
+ long newStamp = newElement.getModificationStamp();
+ return (oldStamp == -1 || newStamp == -1) && (oldStamp != newStamp);
+ }
+
+ private boolean compareMarkers(ResourceInfo oldElement, ResourceInfo newElement) {
+ // If both sets of markers are null then perhaps we added some markers
+ // but then deleted them right away before notification. In that case
+ // don't signify a marker change in the delta.
+ boolean bothNull = oldElement.getMarkers(false) == null && newElement.getMarkers(false) == null;
+ return bothNull || oldElement.getMarkerGenerationCount() == newElement.getMarkerGenerationCount();
+ }
+
+ /**
+ * Compares the node IDs of the ElementInfos for two resources.
+ */
+ private boolean compareNodeIDs(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.getNodeId() == newElement.getNodeId();
+ }
+
+ /**
+ * Compares the open state of the ElementInfos for two resources.
+ */
+ private boolean compareOpen(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.isSet(M_OPEN) == newElement.isSet(M_OPEN);
+ }
+
+ /**
+ * Compares the sync state for two resources.
+ */
+ private boolean compareSync(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.getSyncInfoGenerationCount() == newElement.getSyncInfoGenerationCount();
+ }
+
+ /**
+ * Compares the type of the ResourceInfo.
+ */
+ private boolean compareType(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.getType() == newElement.getType();
+ }
+
+ /**
+ * Compares the used state of the ElementInfos for two resources.
+ */
+ private boolean compareUsed(ResourceInfo oldElement, ResourceInfo newElement) {
+ return oldElement.isSet(M_USED) == newElement.isSet(M_USED);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDelta.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,572 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.Iterator;
+import java.util.Map;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Concrete implementation of the IResourceDelta interface. Each ResourceDelta
+ * object represents changes that have occurred between two states of the
+ * resource tree.
+ */
+public class ResourceDelta extends PlatformObject implements IResourceDelta {
+ protected IPath path;
+ protected ResourceDeltaInfo deltaInfo;
+ protected int status;
+ protected ResourceInfo oldInfo;
+ protected ResourceInfo newInfo;
+ protected ResourceDelta[] children;
+ // don't aggressively set this, but cache it if called once
+ protected IResource cachedResource;
+
+ //
+ protected static int KIND_MASK = 0xFF;
+ private static IMarkerDelta[] EMPTY_MARKER_DELTAS = new IMarkerDelta[0];
+
+ protected ResourceDelta(IPath path, ResourceDeltaInfo deltaInfo) {
+ this.path = path;
+ this.deltaInfo = deltaInfo;
+ }
+
+ /*
+ * @see IResourceDelta#accept(IResourceDeltaVisitor)
+ */
+ public void accept(IResourceDeltaVisitor visitor) throws CoreException {
+ accept(visitor, 0);
+ }
+
+ /*
+ * @see IResourceDelta#accept(IResourceDeltaVisitor, boolean)
+ */
+ public void accept(IResourceDeltaVisitor visitor, boolean includePhantoms) throws CoreException {
+ accept(visitor, includePhantoms ? IContainer.INCLUDE_PHANTOMS : 0);
+ }
+
+ /*
+ * @see IResourceDelta#accept(IResourceDeltaVisitor, int)
+ */
+ public void accept(IResourceDeltaVisitor visitor, int memberFlags) throws CoreException {
+ final boolean includePhantoms = (memberFlags & IContainer.INCLUDE_PHANTOMS) != 0;
+ final boolean includeTeamPrivate = (memberFlags & IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS) != 0;
+ final boolean includeHidden = (memberFlags & IContainer.INCLUDE_HIDDEN) != 0;
+ int mask = includePhantoms ? ALL_WITH_PHANTOMS : REMOVED | ADDED | CHANGED;
+ if ((getKind() & mask) == 0)
+ return;
+ if (!visitor.visit(this))
+ return;
+ for (int i = 0; i < children.length; i++) {
+ ResourceDelta childDelta = children[i];
+ // quietly exclude team-private, hidden and phantom members unless explicitly included
+ if (!includeTeamPrivate && childDelta.isTeamPrivate())
+ continue;
+ if (!includePhantoms && childDelta.isPhantom())
+ continue;
+ if (!includeHidden && childDelta.isHidden())
+ continue;
+ childDelta.accept(visitor, memberFlags);
+ }
+ }
+
+ /**
+ * Check for marker deltas, and set the appropriate change flag if there are any.
+ */
+ protected void checkForMarkerDeltas() {
+ if (deltaInfo.getMarkerDeltas() == null)
+ return;
+ int kind = getKind();
+ // Only need to check for added and removed, or for changes on the workspace.
+ // For changed, the bit is set in the comparator.
+ if (path.isRoot() || kind == ADDED || kind == REMOVED) {
+ MarkerSet changes = (MarkerSet) deltaInfo.getMarkerDeltas().get(path);
+ if (changes != null && changes.size() > 0) {
+ status |= MARKERS;
+ // If there have been marker changes, then ensure kind is CHANGED (if not ADDED or REMOVED).
+ // See 1FV9K20: ITPUI:WINNT - severe - task list - add or delete not working
+ if (kind == 0)
+ status |= CHANGED;
+ }
+ }
+ }
+
+ /**
+ * @see IResourceDelta#findMember(IPath)
+ */
+ public IResourceDelta findMember(IPath path) {
+ int segmentCount = path.segmentCount();
+ if (segmentCount == 0)
+ return this;
+
+ //iterate over the path and find matching child delta
+ ResourceDelta current = this;
+ segments: for (int i = 0; i < segmentCount; i++) {
+ IResourceDelta[] currentChildren = current.children;
+ for (int j = 0, jmax = currentChildren.length; j < jmax; j++) {
+ if (currentChildren[j].getFullPath().lastSegment().equals(path.segment(i))) {
+ current = (ResourceDelta) currentChildren[j];
+ continue segments;
+ }
+ }
+ //matching child not found, return
+ return null;
+ }
+ return current;
+ }
+
+ /**
+ * Delta information on moves and on marker deltas can only be computed after
+ * the delta has been built. This method fixes up the delta to accurately
+ * reflect moves (setting MOVED_FROM and MOVED_TO), and marker changes on
+ * added and removed resources.
+ */
+ protected void fixMovesAndMarkers(ElementTree oldTree) {
+ NodeIDMap nodeIDMap = deltaInfo.getNodeIDMap();
+ if (!path.isRoot() && !nodeIDMap.isEmpty()) {
+ int kind = getKind();
+ switch (kind) {
+ case CHANGED :
+ case ADDED :
+ IPath oldPath = nodeIDMap.getOldPath(newInfo.getNodeId());
+ if (oldPath != null && !oldPath.equals(path)) {
+ //get the old info from the old tree
+ ResourceInfo actualOldInfo = (ResourceInfo) oldTree.getElementData(oldPath);
+ // Replace change flags by comparing old info with new info,
+ // Note that we want to retain the kind flag, but replace all other flags
+ // This is done only for MOVED_FROM, not MOVED_TO, since a resource may be both.
+ status = (status & KIND_MASK) | (deltaInfo.getComparator().compare(actualOldInfo, newInfo) & ~KIND_MASK);
+ status |= MOVED_FROM;
+ //our API states that MOVED_FROM must be in conjunction with ADDED | (CHANGED + REPLACED)
+ if (kind == CHANGED)
+ status = status | REPLACED | CONTENT;
+ //check for gender change
+ if (oldInfo != null && newInfo != null && oldInfo.getType() != newInfo.getType())
+ status |= TYPE;
+ }
+ }
+ switch (kind) {
+ case REMOVED :
+ case CHANGED :
+ IPath newPath = nodeIDMap.getNewPath(oldInfo.getNodeId());
+ if (newPath != null && !newPath.equals(path)) {
+ status |= MOVED_TO;
+ //our API states that MOVED_TO must be in conjunction with REMOVED | (CHANGED + REPLACED)
+ if (kind == CHANGED)
+ status = status | REPLACED | CONTENT;
+ }
+ }
+ }
+
+ //check for marker deltas -- this is affected by move computation
+ //so must happen afterwards
+ checkForMarkerDeltas();
+
+ //recurse on children
+ for (int i = 0; i < children.length; i++)
+ children[i].fixMovesAndMarkers(oldTree);
+ }
+
+ /**
+ * @see IResourceDelta#getAffectedChildren()
+ */
+ public IResourceDelta[] getAffectedChildren() {
+ return getAffectedChildren(ADDED | REMOVED | CHANGED, IResource.NONE);
+ }
+
+ /**
+ * @see IResourceDelta#getAffectedChildren(int)
+ */
+ public IResourceDelta[] getAffectedChildren(int kindMask) {
+ return getAffectedChildren(kindMask, IResource.NONE);
+ }
+
+ /*
+ * @see IResourceDelta#getAffectedChildren(int, int)
+ */
+ public IResourceDelta[] getAffectedChildren(int kindMask, int memberFlags) {
+ int numChildren = children.length;
+ //if there are no children, they all match
+ if (numChildren == 0)
+ return children;
+ boolean includePhantoms = (memberFlags & IContainer.INCLUDE_PHANTOMS) != 0;
+ boolean includeTeamPrivate = (memberFlags & IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS) != 0;
+ boolean includeHidden = (memberFlags & IContainer.INCLUDE_HIDDEN) != 0;
+ // reduce INCLUDE_PHANTOMS member flag to kind mask
+ if (includePhantoms)
+ kindMask |= ADDED_PHANTOM | REMOVED_PHANTOM;
+
+ //first count the number of matches so we can allocate the exact array size
+ int matching = 0;
+ for (int i = 0; i < numChildren; i++) {
+ if ((children[i].getKind() & kindMask) == 0)
+ continue;// child has wrong kind
+ if (!includePhantoms && children[i].isPhantom())
+ continue;
+ if (!includeTeamPrivate && children[i].isTeamPrivate())
+ continue; // child has is a team-private member which are not included
+ if (!includeHidden && children[i].isHidden())
+ continue;
+ matching++;
+ }
+ //use arraycopy if all match
+ if (matching == numChildren) {
+ IResourceDelta[] result = new IResourceDelta[children.length];
+ System.arraycopy(children, 0, result, 0, children.length);
+ return result;
+ }
+ //create the appropriate sized array and fill it
+ IResourceDelta[] result = new IResourceDelta[matching];
+ int nextPosition = 0;
+ for (int i = 0; i < numChildren; i++) {
+ if ((children[i].getKind() & kindMask) == 0)
+ continue; // child has wrong kind
+ if (!includePhantoms && children[i].isPhantom())
+ continue;
+ if (!includeTeamPrivate && children[i].isTeamPrivate())
+ continue; // child has is a team-private member which are not included
+ if (!includeHidden && children[i].isHidden())
+ continue;
+ result[nextPosition++] = children[i];
+ }
+ return result;
+ }
+
+ protected ResourceDeltaInfo getDeltaInfo() {
+ return deltaInfo;
+ }
+
+ /**
+ * @see IResourceDelta#getFlags()
+ */
+ public int getFlags() {
+ return status & ~KIND_MASK;
+ }
+
+ /**
+ * @see IResourceDelta#getFullPath()
+ */
+ public IPath getFullPath() {
+ return path;
+ }
+
+ /**
+ * @see IResourceDelta#getKind()
+ */
+ public int getKind() {
+ return status & KIND_MASK;
+ }
+
+ /**
+ * @see IResourceDelta#getMarkerDeltas()
+ */
+ public IMarkerDelta[] getMarkerDeltas() {
+ Map markerDeltas = deltaInfo.getMarkerDeltas();
+ if (markerDeltas == null)
+ return EMPTY_MARKER_DELTAS;
+ if (path == null)
+ path = Path.ROOT;
+ MarkerSet changes = (MarkerSet) markerDeltas.get(path);
+ if (changes == null)
+ return EMPTY_MARKER_DELTAS;
+ IMarkerSetElement[] elements = changes.elements();
+ IMarkerDelta[] result = new IMarkerDelta[elements.length];
+ for (int i = 0; i < elements.length; i++)
+ result[i] = (IMarkerDelta) elements[i];
+ return result;
+ }
+
+ /**
+ * @see IResourceDelta#getMovedFromPath()
+ */
+ public IPath getMovedFromPath() {
+ if ((status & MOVED_FROM) != 0) {
+ return deltaInfo.getNodeIDMap().getOldPath(newInfo.getNodeId());
+ }
+ return null;
+ }
+
+ /**
+ * @see IResourceDelta#getMovedToPath()
+ */
+ public IPath getMovedToPath() {
+ if ((status & MOVED_TO) != 0) {
+ return deltaInfo.getNodeIDMap().getNewPath(oldInfo.getNodeId());
+ }
+ return null;
+ }
+
+ /**
+ * @see IResourceDelta#getProjectRelativePath()
+ */
+ public IPath getProjectRelativePath() {
+ IPath full = getFullPath();
+ int count = full.segmentCount();
+ if (count < 0)
+ return null;
+ if (count <= 1) // 0 or 1
+ return Path.EMPTY;
+ return full.removeFirstSegments(1);
+ }
+
+ /**
+ * @see IResourceDelta#getResource()
+ */
+ public IResource getResource() {
+ // return a cached copy if we have one
+ if (cachedResource != null)
+ return cachedResource;
+
+ // if this is a delta for the root then return the root resource
+ if (path.segmentCount() == 0)
+ return deltaInfo.getWorkspace().getRoot();
+ // if the delta is a remove then we have to look for the old info to find the type
+ // of resource to create.
+ ResourceInfo info = null;
+ if ((getKind() & (REMOVED | REMOVED_PHANTOM)) != 0)
+ info = oldInfo;
+ else
+ info = newInfo;
+ if (info == null)
+ Assert.isNotNull(null, "Do not have resource info for resource in delta: " + path); //$NON-NLS-1$
+ cachedResource = deltaInfo.getWorkspace().newResource(path, info.getType());
+ return cachedResource;
+ }
+
+ /**
+ * Returns true if this delta represents a phantom member, and false
+ * otherwise.
+ */
+ protected boolean isPhantom() {
+ //use old info for removals, and new info for added or changed
+ if ((status & (REMOVED | REMOVED_PHANTOM)) != 0)
+ return ResourceInfo.isSet(oldInfo.getFlags(), ICoreConstants.M_PHANTOM);
+ return ResourceInfo.isSet(newInfo.getFlags(), ICoreConstants.M_PHANTOM);
+ }
+
+ /**
+ * Returns true if this delta represents a team private member, and false
+ * otherwise.
+ */
+ protected boolean isTeamPrivate() {
+ //use old info for removals, and new info for added or changed
+ if ((status & (REMOVED | REMOVED_PHANTOM)) != 0)
+ return ResourceInfo.isSet(oldInfo.getFlags(), ICoreConstants.M_TEAM_PRIVATE_MEMBER);
+ return ResourceInfo.isSet(newInfo.getFlags(), ICoreConstants.M_TEAM_PRIVATE_MEMBER);
+ }
+
+ /**
+ * Returns true if this delta represents a hidden member, and false
+ * otherwise.
+ */
+ protected boolean isHidden() {
+ //use old info for removals, and new info for added or changed
+ if ((status & (REMOVED | REMOVED_PHANTOM)) != 0)
+ return ResourceInfo.isSet(oldInfo.getFlags(), ICoreConstants.M_HIDDEN);
+ return ResourceInfo.isSet(newInfo.getFlags(), ICoreConstants.M_HIDDEN);
+ }
+
+ protected void setChildren(ResourceDelta[] children) {
+ this.children = children;
+ }
+
+ protected void setNewInfo(ResourceInfo newInfo) {
+ this.newInfo = newInfo;
+ }
+
+ protected void setOldInfo(ResourceInfo oldInfo) {
+ this.oldInfo = oldInfo;
+ }
+
+ protected void setStatus(int status) {
+ this.status = status;
+ }
+
+ /**
+ * Returns a string representation of this delta's
+ * immediate structure suitable for debug purposes.
+ */
+ public String toDebugString() {
+ final StringBuffer buffer = new StringBuffer();
+ writeDebugString(buffer);
+ return buffer.toString();
+ }
+
+ /**
+ * Returns a string representation of this delta's
+ * deep structure suitable for debug purposes.
+ */
+ public String toDeepDebugString() {
+ final StringBuffer buffer = new StringBuffer("\n"); //$NON-NLS-1$
+ writeDebugString(buffer);
+ for (int i = 0; i < children.length; ++i)
+ buffer.append(children[i].toDeepDebugString());
+ return buffer.toString();
+ }
+
+ /**
+ * For debugging only
+ */
+ public String toString() {
+ return "ResourceDelta(" + path + ')'; //$NON-NLS-1$
+ }
+
+ /**
+ * Provides a new set of markers for the delta. This is used
+ * when the delta is reused in cases where the only changes
+ * are marker changes.
+ */
+ public void updateMarkers(Map markers) {
+ deltaInfo.setMarkerDeltas(markers);
+ }
+
+ /**
+ * Writes a string representation of this delta's
+ * immediate structure on the given string buffer.
+ */
+ public void writeDebugString(StringBuffer buffer) {
+ buffer.append(getFullPath());
+ buffer.append('[');
+ switch (getKind()) {
+ case ADDED :
+ buffer.append('+');
+ break;
+ case ADDED_PHANTOM :
+ buffer.append('>');
+ break;
+ case REMOVED :
+ buffer.append('-');
+ break;
+ case REMOVED_PHANTOM :
+ buffer.append('<');
+ break;
+ case CHANGED :
+ buffer.append('*');
+ break;
+ case NO_CHANGE :
+ buffer.append('~');
+ break;
+ default :
+ buffer.append('?');
+ break;
+ }
+ buffer.append("]: {"); //$NON-NLS-1$
+ int changeFlags = getFlags();
+ boolean prev = false;
+ if ((changeFlags & CONTENT) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("CONTENT"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & LOCAL_CHANGED) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("LOCAL_CHANGED"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & MOVED_FROM) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("MOVED_FROM(" + getMovedFromPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ prev = true;
+ }
+ if ((changeFlags & MOVED_TO) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("MOVED_TO(" + getMovedToPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ prev = true;
+ }
+ if ((changeFlags & OPEN) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("OPEN"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & TYPE) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("TYPE"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & SYNC) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("SYNC"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & MARKERS) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("MARKERS"); //$NON-NLS-1$
+ writeMarkerDebugString(buffer);
+ prev = true;
+ }
+ if ((changeFlags & REPLACED) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("REPLACED"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & DESCRIPTION) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("DESCRIPTION"); //$NON-NLS-1$
+ prev = true;
+ }
+ if ((changeFlags & ENCODING) != 0) {
+ if (prev)
+ buffer.append(" | "); //$NON-NLS-1$
+ buffer.append("ENCODING"); //$NON-NLS-1$
+ prev = true;
+ }
+ buffer.append("}"); //$NON-NLS-1$
+ if (isTeamPrivate())
+ buffer.append(" (team private)"); //$NON-NLS-1$
+ if (isHidden())
+ buffer.append(" (hidden)"); //$NON-NLS-1$
+ }
+
+ public void writeMarkerDebugString(StringBuffer buffer) {
+ Map markerDeltas = deltaInfo.getMarkerDeltas();
+ if (markerDeltas == null || markerDeltas.isEmpty())
+ return;
+ buffer.append('[');
+ for (Iterator e = markerDeltas.keySet().iterator(); e.hasNext();) {
+ IPath key = (IPath) e.next();
+ if (getResource().getFullPath().equals(key)) {
+ IMarkerSetElement[] deltas = ((MarkerSet) markerDeltas.get(key)).elements();
+ boolean addComma = false;
+ for (int i = 0; i < deltas.length; i++) {
+ IMarkerDelta delta = (IMarkerDelta) deltas[i];
+ if (addComma)
+ buffer.append(',');
+ switch (delta.getKind()) {
+ case IResourceDelta.ADDED :
+ buffer.append('+');
+ break;
+ case IResourceDelta.REMOVED :
+ buffer.append('-');
+ break;
+ case IResourceDelta.CHANGED :
+ buffer.append('*');
+ break;
+ }
+ buffer.append(delta.getId());
+ addComma = true;
+ }
+ }
+ }
+ buffer.append(']');
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaFactory.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.Map;
+import org.eclipse.core.internal.dtree.DeltaDataTree;
+import org.eclipse.core.internal.dtree.NodeComparison;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+/**
+ * This class is used for calculating and building resource delta trees for notification
+ * and build purposes.
+ */
+public class ResourceDeltaFactory {
+ /**
+ * Singleton indicating no delta children
+ */
+ protected static final ResourceDelta[] NO_CHILDREN = new ResourceDelta[0];
+
+ /**
+ * Returns the resource delta representing the changes made between the given old and new trees,
+ * starting from the given root element.
+ * @param markerGeneration the start generation for which deltas should be computed, or -1
+ * if marker deltas should not be provided.
+ */
+ public static ResourceDelta computeDelta(Workspace workspace, ElementTree oldTree, ElementTree newTree, IPath root, long markerGeneration) {
+ //compute the underlying delta tree.
+ ResourceComparator comparator = markerGeneration >= 0 ? ResourceComparator.getNotificationComparator() : ResourceComparator.getBuildComparator();
+ newTree.immutable();
+ DeltaDataTree delta = null;
+ if (Path.ROOT.equals(root))
+ delta = newTree.getDataTree().compareWith(oldTree.getDataTree(), comparator);
+ else
+ delta = newTree.getDataTree().compareWith(oldTree.getDataTree(), comparator, root);
+
+ delta = delta.asReverseComparisonTree(comparator);
+ IPath pathInTree = root.isRoot() ? Path.ROOT : root;
+ IPath pathInDelta = Path.ROOT;
+
+ // get the marker deltas for the delta info object....if needed
+ Map allMarkerDeltas = null;
+ if (markerGeneration >= 0)
+ allMarkerDeltas = workspace.getMarkerManager().getMarkerDeltas(markerGeneration);
+
+ //recursively walk the delta and create a tree of ResourceDelta objects.
+ ResourceDeltaInfo deltaInfo = new ResourceDeltaInfo(workspace, allMarkerDeltas, comparator);
+ ResourceDelta result = createDelta(workspace, delta, deltaInfo, pathInTree, pathInDelta);
+
+ //compute node ID map and fix up moves
+ deltaInfo.setNodeIDMap(computeNodeIDMap(result, new NodeIDMap()));
+ result.fixMovesAndMarkers(oldTree);
+
+ // check all the projects and if they were added and opened then tweek the flags
+ // so the delta reports both.
+ int segmentCount = result.getFullPath().segmentCount();
+ if (segmentCount <= 1)
+ checkForOpen(result, segmentCount);
+ return result;
+ }
+
+ /**
+ * Checks to see if added projects were also opens and tweaks the flags
+ * accordingly. Should only be called for root and projects. Pass the segment count
+ * in since we've already calculated it before.
+ */
+ protected static void checkForOpen(ResourceDelta delta, int segmentCount) {
+ if (delta.getKind() == IResourceDelta.ADDED)
+ if (delta.newInfo.isSet(ICoreConstants.M_OPEN))
+ delta.status |= IResourceDelta.OPEN;
+ // return for PROJECT
+ if (segmentCount == 1)
+ return;
+ // recurse for ROOT
+ IResourceDelta[] children = delta.children;
+ for (int i = 0; i < children.length; i++)
+ checkForOpen((ResourceDelta) children[i], 1);
+ }
+
+ /**
+ * Creates the map from node id to element id for the old and new states.
+ * Used for recognizing moves. Returns the map.
+ */
+ protected static NodeIDMap computeNodeIDMap(ResourceDelta delta, NodeIDMap nodeIDMap) {
+ IResourceDelta[] children = delta.children;
+ for (int i = 0; i < children.length; i++) {
+ ResourceDelta child = (ResourceDelta) children[i];
+ IPath path = child.getFullPath();
+ switch (child.getKind()) {
+ case IResourceDelta.ADDED :
+ nodeIDMap.putNewPath(child.newInfo.getNodeId(), path);
+ break;
+ case IResourceDelta.REMOVED :
+ nodeIDMap.putOldPath(child.oldInfo.getNodeId(), path);
+ break;
+ case IResourceDelta.CHANGED :
+ long oldID = child.oldInfo.getNodeId();
+ long newID = child.newInfo.getNodeId();
+ //don't add entries to the map if nothing has changed.
+ if (oldID != newID) {
+ nodeIDMap.putOldPath(oldID, path);
+ nodeIDMap.putNewPath(newID, path);
+ }
+ break;
+ }
+ //recurse
+ computeNodeIDMap(child, nodeIDMap);
+ }
+ return nodeIDMap;
+ }
+
+ /**
+ * Recursively creates the tree of ResourceDelta objects rooted at
+ * the given path.
+ */
+ protected static ResourceDelta createDelta(Workspace workspace, DeltaDataTree delta, ResourceDeltaInfo deltaInfo, IPath pathInTree, IPath pathInDelta) {
+ // create the delta and fill it with information
+ ResourceDelta result = new ResourceDelta(pathInTree, deltaInfo);
+
+ // fill the result with information
+ NodeComparison compare = (NodeComparison) delta.getData(pathInDelta);
+ int comparison = compare.getUserComparison();
+ result.setStatus(comparison);
+ if (comparison == IResourceDelta.NO_CHANGE || Path.ROOT.equals(pathInTree)) {
+ ResourceInfo info = workspace.getResourceInfo(pathInTree, true, false);
+ result.setOldInfo(info);
+ result.setNewInfo(info);
+ } else {
+ result.setOldInfo((ResourceInfo) compare.getOldData());
+ result.setNewInfo((ResourceInfo) compare.getNewData());
+ }
+ // recurse over the children
+ IPath[] childKeys = delta.getChildren(pathInDelta);
+ int numChildren = childKeys.length;
+ if (numChildren == 0) {
+ result.setChildren(NO_CHILDREN);
+ } else {
+ ResourceDelta[] children = new ResourceDelta[numChildren];
+ for (int i = 0; i < numChildren; i++) {
+ //reuse the delta path if tree-relative and delta-relative are the same
+ IPath newTreePath = pathInTree == pathInDelta ? childKeys[i] : pathInTree.append(childKeys[i].lastSegment());
+ children[i] = createDelta(workspace, delta, deltaInfo, newTreePath, childKeys[i]);
+ }
+ result.setChildren(children);
+ }
+
+ // if this delta has children but no other changes, mark it as changed
+ int status = result.status;
+ if ((status & IResourceDelta.ALL_WITH_PHANTOMS) == 0 && numChildren != 0)
+ result.setStatus(status |= IResourceDelta.CHANGED);
+
+ // return the delta
+ return result;
+ }
+
+ /**
+ * Returns an empty build delta describing the fact that no
+ * changes occurred in the given project. The returned delta
+ * is not appropriate for use as a notification delta because
+ * it is rooted at a project, and does not contain marker deltas.
+ */
+ public static IResourceDelta newEmptyDelta(IProject project) {
+ ResourceDelta result = new ResourceDelta(project.getFullPath(), new ResourceDeltaInfo(((Workspace) project.getWorkspace()), null, ResourceComparator.getBuildComparator()));
+ result.setStatus(0);
+ result.setChildren(NO_CHILDREN);
+ ResourceInfo info = ((Project) project).getResourceInfo(true, false);
+ result.setOldInfo(info);
+ result.setNewInfo(info);
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaInfo.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import java.util.Map;
+import org.eclipse.core.internal.resources.Workspace;
+
+public class ResourceDeltaInfo {
+ protected Workspace workspace;
+ protected Map allMarkerDeltas;
+ protected NodeIDMap nodeIDMap;
+ protected ResourceComparator comparator;
+
+ public ResourceDeltaInfo(Workspace workspace, Map markerDeltas, ResourceComparator comparator) {
+ super();
+ this.workspace = workspace;
+ this.allMarkerDeltas = markerDeltas;
+ this.comparator = comparator;
+ }
+
+ public ResourceComparator getComparator() {
+ return comparator;
+ }
+
+ /**
+ * Table of all marker deltas, IPath -> MarkerSet
+ */
+ public Map getMarkerDeltas() {
+ return allMarkerDeltas;
+ }
+
+ public NodeIDMap getNodeIDMap() {
+ return nodeIDMap;
+ }
+
+ public Workspace getWorkspace() {
+ return workspace;
+ }
+
+ public void setMarkerDeltas(Map value) {
+ allMarkerDeltas = value;
+ }
+
+ public void setNodeIDMap(NodeIDMap map) {
+ nodeIDMap = map;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceStats.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.events;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.PerformanceStats;
+
+/**
+ * An ResourceStats collects and aggregates timing data about an event such as
+ * a builder running, an editor opening, etc.
+ */
+public class ResourceStats {
+ /**
+ * The event that is currently occurring, maybe <code>null</code>
+ */
+ private static PerformanceStats currentStats;
+ //performance event names
+ public static final String EVENT_BUILDERS = ResourcesPlugin.PI_RESOURCES + "/perf/builders"; //$NON-NLS-1$
+ public static final String EVENT_LISTENERS = ResourcesPlugin.PI_RESOURCES + "/perf/listeners"; //$NON-NLS-1$
+ public static final String EVENT_SAVE_PARTICIPANTS = ResourcesPlugin.PI_RESOURCES + "/perf/save.participants"; //$NON-NLS-1$
+ public static final String EVENT_SNAPSHOT = ResourcesPlugin.PI_RESOURCES + "/perf/snapshot"; //$NON-NLS-1$
+
+ //performance event enablement
+ public static boolean TRACE_BUILDERS = PerformanceStats.isEnabled(ResourceStats.EVENT_BUILDERS);
+ public static boolean TRACE_LISTENERS = PerformanceStats.isEnabled(ResourceStats.EVENT_LISTENERS);
+ public static boolean TRACE_SAVE_PARTICIPANTS = PerformanceStats.isEnabled(ResourceStats.EVENT_SAVE_PARTICIPANTS);
+ public static boolean TRACE_SNAPSHOT = PerformanceStats.isEnabled(ResourceStats.EVENT_SNAPSHOT);
+
+ public static void endBuild() {
+ if (currentStats != null)
+ currentStats.endRun();
+ currentStats = null;
+ }
+
+ public static void endNotify() {
+ if (currentStats != null)
+ currentStats.endRun();
+ currentStats = null;
+ }
+
+ public static void endSave() {
+ if (currentStats != null)
+ currentStats.endRun();
+ currentStats = null;
+ }
+
+ public static void endSnapshot() {
+ if (currentStats != null)
+ currentStats.endRun();
+ currentStats = null;
+ }
+
+ /**
+ * Notifies the stats tool that a resource change listener has been added.
+ */
+ public static void listenerAdded(IResourceChangeListener listener) {
+ if (listener != null)
+ PerformanceStats.getStats(EVENT_LISTENERS, listener.getClass().getName());
+ }
+
+ /**
+ * Notifies the stats tool that a resource change listener has been removed.
+ */
+ public static void listenerRemoved(IResourceChangeListener listener) {
+ if (listener != null)
+ PerformanceStats.removeStats(EVENT_LISTENERS, listener.getClass().getName());
+ }
+
+ public static void startBuild(IncrementalProjectBuilder builder) {
+ currentStats = PerformanceStats.getStats(EVENT_BUILDERS, builder);
+ currentStats.startRun(builder.getProject().getName());
+ }
+
+ public static void startNotify(IResourceChangeListener listener) {
+ currentStats = PerformanceStats.getStats(EVENT_LISTENERS, listener);
+ currentStats.startRun();
+ }
+
+ public static void startSnapshot() {
+ currentStats = PerformanceStats.getStats(EVENT_SNAPSHOT, ResourcesPlugin.getWorkspace());
+ currentStats.startRun();
+ }
+
+ public static void startSave(ISaveParticipant participant) {
+ currentStats = PerformanceStats.getStats(EVENT_SAVE_PARTICIPANTS, participant);
+ currentStats.startRun();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/BlobStore.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Set;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.utils.UniversalUniqueIdentifier;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Blob store which maps UUIDs to blobs on disk. The UUID is mapped
+ * to a file in the file-system and the blob is the file contents. For scalability,
+ * the blobs are split among 255 directories with the names 00 to FF.
+ */
+public class BlobStore {
+ protected IFileStore localStore;
+
+ /** Limits the range of directories' names. */
+ protected byte mask;
+
+ //private static short[] randomArray = {213, 231, 37, 85, 211, 29, 161, 175, 187, 3, 147, 246, 170, 30, 202, 183, 242, 47, 254, 189, 25, 248, 193, 2, 119, 133, 125, 12, 76, 213, 219, 79, 69, 133, 202, 80, 150, 190, 157, 190, 80, 190, 219, 150, 169, 117, 95, 10, 77, 214, 233, 70, 5, 188, 44, 91, 165, 149, 177, 93, 17, 112, 4, 41, 230, 148, 188, 107, 213, 31, 52, 60, 111, 246, 226, 121, 129, 197, 144, 248, 92, 133, 96, 116, 104, 67, 74, 144, 185, 141, 96, 34, 182, 90, 36, 217, 28, 205, 107, 52, 201, 14, 8, 1, 27, 216, 60, 35, 251, 194, 7, 156, 32, 5, 145, 29, 96, 61, 110, 145, 50, 56, 235, 239, 170, 138, 17, 211, 56, 98, 101, 126, 27, 57, 211, 144, 206, 207, 179, 111, 160, 50, 243, 69, 106, 118, 155, 159, 28, 57, 11, 175, 43, 173, 96, 181, 99, 169, 171, 156, 246, 243, 30, 198, 251, 81, 77, 92, 160, 235, 215, 187, 23, 71, 58, 247, 127, 56, 118, 132, 79, 188, 42, 188, 158, 121, 255, 65, 154, 118, 172, 217, 4, 47, 105, 204, 135, 27, 43, 90, 9, 31, 59, 115, 193, 28, 55, 101, 9, 117, 211, 112, 61, 55, 23, 235, 51, 104, 123, 138, 76, 148, 115, 119, 81, 54, 39, 46, 149, 191, 79, 16, 222, 69, 219, 136, 148, 181, 77, 250, 101, 223, 140, 194, 141, 44, 195, 217, 31, 223, 207, 149, 245, 115, 243, 183};
+ private static byte[] randomArray = {-43, -25, 37, 85, -45, 29, -95, -81, -69, 3, -109, -10, -86, 30, -54, -73, -14, 47, -2, -67, 25, -8, -63, 2, 119, -123, 125, 12, 76, -43, -37, 79, 69, -123, -54, 80, -106, -66, -99, -66, 80, -66, -37, -106, -87, 117, 95, 10, 77, -42, -23, 70, 5, -68, 44, 91, -91, -107, -79, 93, 17, 112, 4, 41, -26, -108, -68, 107, -43, 31, 52, 60, 111, -10, -30, 121, -127, -59, -112, -8, 92, -123, 96, 116, 104, 67, 74, -112, -71, -115, 96, 34, -74, 90, 36, -39, 28, -51, 107, 52, -55, 14, 8, 1, 27, -40, 60, 35, -5, -62, 7, -100, 32, 5, -111, 29, 96, 61, 110, -111, 50, 56, -21, -17, -86, -118, 17, -45, 56, 98, 101, 126, 27, 57, -45, -112, -50, -49, -77, 111, -96, 50, -13, 69, 106, 118, -101, -97, 28, 57, 11, -81, 43, -83, 96, -75, 99, -87, -85, -100, -10, -13, 30,
+ -58, -5, 81, 77, 92, -96, -21, -41, -69, 23, 71, 58, -9, 127, 56, 118, -124, 79, -68, 42, -68, -98, 121, -1, 65, -102, 118, -84, -39, 4, 47, 105, -52, -121, 27, 43, 90, 9, 31, 59, 115, -63, 28, 55, 101, 9, 117, -45, 112, 61, 55, 23, -21, 51, 104, 123, -118, 76, -108, 115, 119, 81, 54, 39, 46, -107, -65, 79, 16, -34, 69, -37, -120, -108, -75, 77, -6, 101, -33, -116, -62, -115, 44, -61, -39, 31, -33, -49, -107, -11, 115, -13, -73,};
+
+ /**
+ * The limit is the maximum number of directories managed by this store.
+ * This number must be power of 2 and do not exceed 256. The location
+ * should be an existing valid directory.
+ */
+ public BlobStore(IFileStore store, int limit) {
+ Assert.isNotNull(store);
+ localStore = store;
+ Assert.isTrue(localStore.fetchInfo().isDirectory());
+ Assert.isTrue(limit == 256 || limit == 128 || limit == 64 || limit == 32 || limit == 16 || limit == 8 || limit == 4 || limit == 2 || limit == 1);
+ mask = (byte) (limit - 1);
+ }
+
+ public UniversalUniqueIdentifier addBlob(IFileStore target, boolean moveContents) throws CoreException {
+ UniversalUniqueIdentifier uuid = new UniversalUniqueIdentifier();
+ folderFor(uuid).mkdir(EFS.NONE, null);
+ IFileStore destination = fileFor(uuid);
+ if (moveContents)
+ target.move(destination, EFS.NONE, null);
+ else
+ target.copy(destination, EFS.NONE, null);
+ return uuid;
+ }
+
+ /* (non-Javadoc)
+ * @see UniversalUniqueIdentifier#appendByteString(StringBuffer, byte)
+ */
+ private void appendByteString(StringBuffer buffer, byte value) {
+ String hexString;
+ if (value < 0)
+ hexString = Integer.toHexString(256 + value);
+ else
+ hexString = Integer.toHexString(value);
+ if (hexString.length() == 1)
+ buffer.append("0"); //$NON-NLS-1$
+ buffer.append(hexString);
+ }
+
+ /* (non-Javadoc)
+ * Converts an array of bytes into a String.
+ *
+ * @see UniversalUniqueIdentifier#toString()
+ */
+ private String bytesToHexString(byte[] b) {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < b.length; i++)
+ appendByteString(buffer, b[i]);
+ return buffer.toString();
+ }
+
+ /**
+ * Deletes a blobFile.
+ */
+ public void deleteBlob(UniversalUniqueIdentifier uuid) {
+ Assert.isNotNull(uuid);
+ try {
+ fileFor(uuid).delete(EFS.NONE, null);
+ } catch (CoreException e) {
+ //ignore
+ }
+ }
+
+ /**
+ * Delete all of the blobs in the given set.
+ */
+ public void deleteBlobs(Set set) {
+ for (Iterator i = set.iterator(); i.hasNext();)
+ deleteBlob((UniversalUniqueIdentifier) i.next());
+ }
+
+ public IFileStore fileFor(UniversalUniqueIdentifier uuid) {
+ IFileStore root = folderFor(uuid);
+ return root.getChild(bytesToHexString(uuid.toBytes()));
+ }
+
+ /**
+ * Find out the name of the directory that fits better to this UUID.
+ */
+ public IFileStore folderFor(UniversalUniqueIdentifier uuid) {
+ byte hash = hashUUIDbytes(uuid);
+ hash &= mask; // limit the range of the directory
+ String dirName = Integer.toHexString(hash + (128 & mask)); // +(128 & mask) makes sure 00h is the lower value
+ return localStore.getChild(dirName);
+ }
+
+ public InputStream getBlob(UniversalUniqueIdentifier uuid) throws CoreException {
+ IFileStore blobFile = fileFor(uuid);
+ return blobFile.openInputStream(EFS.NONE, null);
+ }
+
+ /**
+ * Converts a byte array into a byte hash representation. It is used to
+ * get a directory name.
+ */
+ protected byte hashUUIDbytes(UniversalUniqueIdentifier uuid) {
+ byte[] bytes = uuid.toBytes();
+ byte hash = 0;
+ for (int i = 0; i < bytes.length; i++)
+ hash ^= randomArray[bytes[i] + 128]; // +128 makes sure the index is >0
+ return hash;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/Bucket.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,392 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.internal.resources.ResourceStatus;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * A bucket is a persistent dictionary having paths as keys. Values are determined
+ * by subclasses.
+ *
+ * @since 3.1
+ */
+public abstract class Bucket {
+
+ public static abstract class Entry {
+ /**
+ * This entry has not been modified in any way so far.
+ *
+ * @see #state
+ */
+ private final static int STATE_CLEAR = 0;
+ /**
+ * This entry has been requested for deletion.
+ *
+ * @see #state
+ */
+ private final static int STATE_DELETED = 0x02;
+ /**
+ * This entry has been modified.
+ *
+ * @see #state
+ */
+ private final static int STATE_DIRTY = 0x01;
+
+ /**
+ * Logical path of the object we are storing history for. This does not
+ * correspond to a file system path.
+ */
+ private IPath path;
+
+ /**
+ * State for this entry. Possible values are STATE_CLEAR, STATE_DIRTY and STATE_DELETED.
+ *
+ * @see #STATE_CLEAR
+ * @see #STATE_DELETED
+ * @see #STATE_DIRTY
+ */
+ private byte state = STATE_CLEAR;
+
+ protected Entry(IPath path) {
+ this.path = path;
+ }
+
+ public void delete() {
+ state = STATE_DELETED;
+ }
+
+ public abstract int getOccurrences();
+
+ public IPath getPath() {
+ return path;
+ }
+
+ public abstract Object getValue();
+
+ public boolean isDeleted() {
+ return state == STATE_DELETED;
+ }
+
+ public boolean isDirty() {
+ return state == STATE_DIRTY;
+ }
+
+ public boolean isEmpty() {
+ return getOccurrences() == 0;
+ }
+
+ public void markDirty() {
+ Assert.isTrue(state != STATE_DELETED);
+ state = STATE_DIRTY;
+ }
+
+ /**
+ * Called on the entry right after the visitor has visited it.
+ */
+ public void visited() {
+ // does not do anything by default
+ }
+ }
+
+ /**
+ * A visitor for bucket entries.
+ */
+ public static abstract class Visitor {
+ // should continue the traversal
+ public final static int CONTINUE = 0;
+ // should stop looking at any states immediately
+ public final static int STOP = 1;
+ // should stop looking at states for files in this container (or any of its children)
+ public final static int RETURN = 2;
+
+ /**
+ * Called after the bucket has been visited (and saved).
+ * @throws CoreException
+ */
+ public void afterSaving(Bucket bucket) throws CoreException {
+ // empty implementation, subclasses to override
+ }
+
+ /**
+ * @throws CoreException
+ */
+ public void beforeSaving(Bucket bucket) throws CoreException {
+ // empty implementation, subclasses to override
+ }
+
+ /**
+ * @return either STOP, CONTINUE or RETURN
+ */
+ public abstract int visit(Entry entry);
+ }
+
+ /**
+ * The segment name for the root directory for index files.
+ */
+ static final String INDEXES_DIR_NAME = ".indexes"; //$NON-NLS-1$
+
+ /**
+ * Map of the history entries in this bucket. Maps (String -> byte[][]),
+ * where the key is the path of the object we are storing history for, and
+ * the value is the history entry data (UUID,timestamp) pairs.
+ */
+ private final Map entries;
+ /**
+ * The file system location of this bucket index file.
+ */
+ private File location;
+ /**
+ * Whether the in-memory bucket is dirty and needs saving
+ */
+ private boolean needSaving = false;
+ /**
+ * The project name for the bucket currently loaded. <code>null</code> if this is the root bucket.
+ */
+ protected String projectName;
+
+ public Bucket() {
+ this.entries = new HashMap();
+ }
+
+ /**
+ * Applies the given visitor to this bucket index.
+ * @param visitor
+ * @param filter
+ * @param depth the number of trailing segments that can differ from the filter
+ * @return one of STOP, RETURN or CONTINUE constants
+ * @exception CoreException
+ */
+ public final int accept(Visitor visitor, IPath filter, int depth) throws CoreException {
+ if (entries.isEmpty())
+ return Visitor.CONTINUE;
+ try {
+ for (Iterator i = entries.entrySet().iterator(); i.hasNext();) {
+ Map.Entry mapEntry = (Map.Entry) i.next();
+ IPath path = new Path((String) mapEntry.getKey());
+ // check whether the filter applies
+ int matchingSegments = filter.matchingFirstSegments(path);
+ if (!filter.isPrefixOf(path) || path.segmentCount() - matchingSegments > depth)
+ continue;
+ // apply visitor
+ Entry bucketEntry = createEntry(path, mapEntry.getValue());
+ // calls the visitor passing all uuids for the entry
+ int outcome = visitor.visit(bucketEntry);
+ // notify the entry it has been visited
+ bucketEntry.visited();
+ if (bucketEntry.isDeleted()) {
+ needSaving = true;
+ i.remove();
+ } else if (bucketEntry.isDirty()) {
+ needSaving = true;
+ mapEntry.setValue(bucketEntry.getValue());
+ }
+ if (outcome != Visitor.CONTINUE)
+ return outcome;
+ }
+ return Visitor.CONTINUE;
+ } finally {
+ visitor.beforeSaving(this);
+ save();
+ visitor.afterSaving(this);
+ }
+ }
+
+ /**
+ * Tries to delete as many empty levels as possible.
+ */
+ private void cleanUp(File toDelete) {
+ if (!toDelete.delete())
+ // if deletion didn't go well, don't bother trying to delete the parent dir
+ return;
+ // don't try to delete beyond the root for bucket indexes
+ if (toDelete.getName().equals(INDEXES_DIR_NAME))
+ return;
+ // recurse to parent directory
+ cleanUp(toDelete.getParentFile());
+ }
+
+ /**
+ * Factory method for creating entries. Subclasses to override.
+ */
+ protected abstract Entry createEntry(IPath path, Object value);
+
+ /**
+ * Flushes this bucket so it has no contents and is not associated to any
+ * location. Any uncommitted changes are lost.
+ */
+ public void flush() {
+ projectName = null;
+ location = null;
+ entries.clear();
+ needSaving = false;
+ }
+
+ /**
+ * Returns how many entries there are in this bucket.
+ */
+ public final int getEntryCount() {
+ return entries.size();
+ }
+
+ /**
+ * Returns the value for entry corresponding to the given path (null if none found).
+ */
+ public final Object getEntryValue(String path) {
+ return entries.get(path);
+ }
+
+ /**
+ * Returns the file name used to persist the index for this bucket.
+ */
+ protected abstract String getIndexFileName();
+
+ /**
+ * Returns the version number for the file format used to persist this bucket.
+ */
+ protected abstract byte getVersion();
+
+ /**
+ * Returns the file name to be used to store bucket version information
+ */
+ protected abstract String getVersionFileName();
+
+ /**
+ * Loads the contents from a file under the given directory.
+ */
+ public void load(String newProjectName, File baseLocation) throws CoreException {
+ load(newProjectName, baseLocation, false);
+ }
+
+ /**
+ * Loads the contents from a file under the given directory. If <code>force</code> is
+ * <code>false</code>, if this bucket already contains the contents from the current location,
+ * avoids reloading.
+ */
+ public void load(String newProjectName, File baseLocation, boolean force) throws CoreException {
+ try {
+ // avoid reloading
+ if (!force && this.location != null && baseLocation.equals(this.location.getParentFile()) && (projectName == null ? (newProjectName == null) : projectName.equals(newProjectName))) {
+ this.projectName = newProjectName;
+ return;
+ }
+ // previously loaded bucket may not have been saved... save before loading new one
+ save();
+ this.projectName = newProjectName;
+ this.location = new File(baseLocation, getIndexFileName());
+ this.entries.clear();
+ if (!this.location.isFile())
+ return;
+ DataInputStream source = new DataInputStream(new BufferedInputStream(new FileInputStream(location), 8192));
+ try {
+ int version = source.readByte();
+ if (version != getVersion()) {
+ // unknown version
+ String message = NLS.bind(Messages.resources_readMetaWrongVersion, location.getAbsolutePath(), Integer.toString(version));
+ ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, message);
+ throw new ResourceException(status);
+ }
+ int entryCount = source.readInt();
+ for (int i = 0; i < entryCount; i++)
+ this.entries.put(readEntryKey(source), readEntryValue(source));
+ } finally {
+ source.close();
+ }
+ } catch (IOException ioe) {
+ String message = NLS.bind(Messages.resources_readMeta, location.getAbsolutePath());
+ ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, null, message, ioe);
+ throw new ResourceException(status);
+ }
+ }
+
+ private String readEntryKey(DataInputStream source) throws IOException {
+ if (projectName == null)
+ return source.readUTF();
+ return IPath.SEPARATOR + projectName + source.readUTF();
+ }
+
+ /**
+ * Defines how data for a given entry is to be read from a bucket file. To be implemented by subclasses.
+ */
+ protected abstract Object readEntryValue(DataInputStream source) throws IOException, CoreException;
+
+ /**
+ * Saves this bucket's contents back to its location.
+ */
+ public void save() throws CoreException {
+ if (!needSaving)
+ return;
+ try {
+ if (entries.isEmpty()) {
+ needSaving = false;
+ cleanUp(location);
+ return;
+ }
+ // ensure the parent location exists
+ File parent = location.getParentFile();
+ if (parent == null)
+ throw new IOException();//caught and rethrown below
+ parent.mkdirs();
+ DataOutputStream destination = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(location), 8192));
+ try {
+ destination.write(getVersion());
+ destination.writeInt(entries.size());
+ for (Iterator i = entries.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ writeEntryKey(destination, (String) entry.getKey());
+ writeEntryValue(destination, entry.getValue());
+ }
+ } finally {
+ destination.close();
+ }
+ needSaving = false;
+ } catch (IOException ioe) {
+ String message = NLS.bind(Messages.resources_writeMeta, location.getAbsolutePath());
+ ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, null, message, ioe);
+ throw new ResourceException(status);
+ }
+ }
+
+ /**
+ * Sets the value for the entry with the given path. If <code>value</code> is <code>null</code>,
+ * removes the entry.
+ */
+ public final void setEntryValue(String path, Object value) {
+ if (value == null)
+ entries.remove(path);
+ else
+ entries.put(path, value);
+ needSaving = true;
+ }
+
+ private void writeEntryKey(DataOutputStream destination, String path) throws IOException {
+ if (projectName == null) {
+ destination.writeUTF(path);
+ return;
+ }
+ // omit the project name
+ int pathLength = path.length();
+ int projectLength = projectName.length();
+ String key = (pathLength == projectLength + 1) ? "" : path.substring(projectLength + 1); //$NON-NLS-1$
+ destination.writeUTF(key);
+ }
+
+ /**
+ * Defines how an entry is to be persisted to the bucket file.
+ */
+ protected abstract void writeEntryValue(DataOutputStream destination, Object entryValue) throws IOException, CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/BucketTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+import org.eclipse.core.internal.localstore.Bucket.Visitor;
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+
+/**
+ * @since 3,1
+ */
+public class BucketTree {
+ public static final int DEPTH_INFINITE = Integer.MAX_VALUE;
+ public static final int DEPTH_ONE = 1;
+ public static final int DEPTH_ZERO = 0;
+
+ private final static int SEGMENT_QUOTA = 256; //two hex characters
+
+ /**
+ * Store all bucket names to avoid creating garbage when traversing the tree
+ */
+ private static final char[][] HEX_STRINGS;
+
+ static {
+ HEX_STRINGS = new char[SEGMENT_QUOTA][];
+ for (int i = 0; i < HEX_STRINGS.length; i++)
+ HEX_STRINGS[i] = Integer.toHexString(i).toCharArray();
+ }
+
+ protected Bucket current;
+
+ private Workspace workspace;
+
+ public BucketTree(Workspace workspace, Bucket bucket) {
+ this.current = bucket;
+ this.workspace = workspace;
+ }
+
+ /**
+ * From a starting point in the tree, visit all nodes under it.
+ * @param visitor
+ * @param base
+ * @param depth
+ */
+ public void accept(Bucket.Visitor visitor, IPath base, int depth) throws CoreException {
+ if (Path.ROOT.equals(base)) {
+ current.load(null, locationFor(Path.ROOT));
+ if (current.accept(visitor, base, DEPTH_ZERO) != Visitor.CONTINUE)
+ return;
+ if (depth == DEPTH_ZERO)
+ return;
+ boolean keepVisiting = true;
+ depth--;
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; keepVisiting && i < projects.length; i++) {
+ IPath projectPath = projects[i].getFullPath();
+ keepVisiting = internalAccept(visitor, projectPath, locationFor(projectPath), depth, 1);
+ }
+ } else
+ internalAccept(visitor, base, locationFor(base), depth, 0);
+ }
+
+ public void close() throws CoreException {
+ current.save();
+ saveVersion();
+ }
+
+ public Bucket getCurrent() {
+ return current;
+ }
+
+ public File getVersionFile() {
+ return new File(locationFor(Path.ROOT), current.getVersionFileName());
+ }
+
+ /**
+ * This will never be called for a bucket for the workspace root.
+ *
+ * @return whether to continue visiting other branches
+ */
+ private boolean internalAccept(Bucket.Visitor visitor, IPath base, File bucketDir, int depthRequested, int currentDepth) throws CoreException {
+ current.load(base.segment(0), bucketDir);
+ int outcome = current.accept(visitor, base, depthRequested);
+ if (outcome != Visitor.CONTINUE)
+ return outcome == Visitor.RETURN;
+ if (depthRequested <= currentDepth)
+ return true;
+ File[] subDirs = bucketDir.listFiles();
+ if (subDirs == null)
+ return true;
+ for (int i = 0; i < subDirs.length; i++)
+ if (subDirs[i].isDirectory())
+ if (!internalAccept(visitor, base, subDirs[i], depthRequested, currentDepth + 1))
+ return false;
+ return true;
+ }
+
+ public void loadBucketFor(IPath path) throws CoreException {
+ current.load(Path.ROOT.equals(path) ? null : path.segment(0), locationFor(path));
+ }
+
+ private File locationFor(IPath resourcePath) {
+ //optimized to avoid string and path creations
+ IPath baseLocation = workspace.getMetaArea().locationFor(resourcePath).removeTrailingSeparator();
+ int segmentCount = resourcePath.segmentCount();
+ String locationString = baseLocation.toOSString();
+ StringBuffer locationBuffer = new StringBuffer(locationString.length() + Bucket.INDEXES_DIR_NAME.length() + 16);
+ locationBuffer.append(locationString);
+ locationBuffer.append(File.separatorChar);
+ locationBuffer.append(Bucket.INDEXES_DIR_NAME);
+ // the last segment is ignored
+ for (int i = 1; i < segmentCount - 1; i++) {
+ // translate all segments except the first one (project name)
+ locationBuffer.append(File.separatorChar);
+ locationBuffer.append(translateSegment(resourcePath.segment(i)));
+ }
+ return new File(locationBuffer.toString());
+ }
+
+ /**
+ * Writes the version tag to a file on disk.
+ */
+ private void saveVersion() throws CoreException {
+ File versionFile = getVersionFile();
+ if (!versionFile.getParentFile().exists())
+ versionFile.getParentFile().mkdirs();
+ FileOutputStream stream = null;
+ boolean failed = false;
+ try {
+ stream = new FileOutputStream(versionFile);
+ stream.write(current.getVersion());
+ } catch (IOException e) {
+ failed = true;
+ String message = NLS.bind(Messages.resources_writeWorkspaceMeta, versionFile.getAbsolutePath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, null, message, e);
+ } finally {
+ try {
+ if (stream != null)
+ stream.close();
+ } catch (IOException e) {
+ if (!failed) {
+ String message = NLS.bind(Messages.resources_writeWorkspaceMeta, versionFile.getAbsolutePath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, null, message, e);
+ }
+ }
+ }
+ }
+
+ private char[] translateSegment(String segment) {
+ // String.hashCode algorithm is API
+ return HEX_STRINGS[Math.abs(segment.hashCode()) % SEGMENT_QUOTA];
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/CollectSyncStatusVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+//
+/**
+ * Visits a unified tree, and collects local sync information in
+ * a multi-status. At the end of the visit, the resource tree will NOT
+ * be synchronized with the file system, but all discrepancies between
+ * the two will be recorded in the returned status.
+ */
+public class CollectSyncStatusVisitor extends RefreshLocalVisitor {
+ protected List affectedResources;
+ /**
+ * Determines how to treat cases where the resource is missing from
+ * the local file system. When performing a deletion with force=false,
+ * we don't care about files that are out of sync because they do not
+ * exist in the file system.
+ */
+ private boolean ignoreLocalDeletions = false;
+ protected MultiStatus status;
+
+ /**
+ * Creates a new visitor, whose sync status will have the given title.
+ */
+ public CollectSyncStatusVisitor(String multiStatusTitle, IProgressMonitor monitor) {
+ super(monitor);
+ status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.INFO, multiStatusTitle, null);
+ }
+
+ protected void changed(Resource target) {
+ String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, target.getFullPath());
+ status.add(new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message));
+ if (affectedResources == null)
+ affectedResources = new ArrayList(20);
+ affectedResources.add(target);
+ resourceChanged = true;
+ }
+
+ protected void createResource(UnifiedTreeNode node, Resource target) {
+ changed(target);
+ }
+
+ protected void deleteResource(UnifiedTreeNode node, Resource target) {
+ if (!ignoreLocalDeletions)
+ changed(target);
+ }
+
+ protected void fileToFolder(UnifiedTreeNode node, Resource target) {
+ changed(target);
+ }
+
+ protected void folderToFile(UnifiedTreeNode node, Resource target) {
+ changed(target);
+ }
+
+ /**
+ * Returns the list of resources that were not synchronized with
+ * the local file system, or <code>null</code> if all resources
+ * are synchronized.
+ */
+ public List getAffectedResources() {
+ return affectedResources;
+ }
+
+ /**
+ * Returns the sync status that has been collected as a result of this visit.
+ */
+ public MultiStatus getSyncStatus() {
+ return status;
+ }
+
+ protected void makeLocal(UnifiedTreeNode node, Resource target) {
+ changed(target);
+ }
+
+ protected void refresh(Container parent) {
+ changed(parent);
+ }
+
+ protected void resourceChanged(UnifiedTreeNode node, Resource target) {
+ changed(target);
+ }
+
+ /**
+ * Instructs this visitor to ignore changes due to local deletions
+ * in the file system.
+ */
+ public void setIgnoreLocalDeletions(boolean value) {
+ this.ignoreLocalDeletions = value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/CopyVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+//
+public class CopyVisitor implements IUnifiedTreeVisitor {
+
+ /** root destination */
+ protected IResource rootDestination;
+
+ /** reports progress */
+ protected IProgressMonitor monitor;
+
+ /** update flags */
+ protected int updateFlags;
+
+ /** force flag */
+ protected boolean force;
+
+ /** deep copy flag */
+ protected boolean isDeep;
+
+ /** segments to drop from the source name */
+ protected int segmentsToDrop;
+
+ /** stores problems encountered while copying */
+ protected MultiStatus status;
+
+ /** visitor to refresh unsynchronized nodes */
+ protected RefreshLocalVisitor refreshLocalVisitor;
+
+ private FileSystemResourceManager localManager;
+
+ public CopyVisitor(IResource rootSource, IResource destination, int updateFlags, IProgressMonitor monitor) {
+ this.localManager = ((Resource)rootSource).getLocalManager();
+ this.rootDestination = destination;
+ this.updateFlags = updateFlags;
+ this.isDeep = (updateFlags & IResource.SHALLOW) == 0;
+ this.force = (updateFlags & IResource.FORCE) != 0;
+ this.monitor = monitor;
+ this.segmentsToDrop = rootSource.getFullPath().segmentCount();
+ this.status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.INFO, Messages.localstore_copyProblem, null);
+ }
+
+ protected boolean copy(UnifiedTreeNode node) {
+ Resource source = (Resource) node.getResource();
+ IPath sufix = source.getFullPath().removeFirstSegments(segmentsToDrop);
+ Resource destination = getDestinationResource(source, sufix);
+ if (!copyProperties(source, destination))
+ return false;
+ return copyContents(node, source, destination);
+ }
+
+ protected boolean copyContents(UnifiedTreeNode node, Resource source, Resource destination) {
+ try {
+ if (!isDeep && source.isLinked()) {
+ destination.createLink(source.getRawLocationURI(), updateFlags & IResource.ALLOW_MISSING_LOCAL, null);
+ return false;
+ }
+ IFileStore sourceStore = node.getStore();
+ IFileStore destinationStore = destination.getStore();
+ //ensure the parent of the root destination exists (bug 126104)
+ if (destination == rootDestination)
+ destinationStore.getParent().mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 0));
+ sourceStore.copy(destinationStore, EFS.SHALLOW, Policy.subMonitorFor(monitor, 0));
+ //create the destination in the workspace
+ ResourceInfo info = localManager.getWorkspace().createResource(destination, updateFlags);
+ localManager.updateLocalSync(info, destinationStore.fetchInfo().getLastModified());
+ //update timestamps on aliases
+ getWorkspace().getAliasManager().updateAliases(destination, destinationStore, IResource.DEPTH_ZERO, monitor);
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ return true;
+ }
+
+ protected boolean copyProperties(Resource target, Resource destination) {
+ try {
+ target.getPropertyManager().copy(target, destination, IResource.DEPTH_ZERO);
+ return true;
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ return false;
+ }
+ }
+
+ protected Resource getDestinationResource(Resource source, IPath suffix) {
+ if (suffix.segmentCount() == 0)
+ return (Resource)rootDestination;
+ IPath destinationPath = rootDestination.getFullPath().append(suffix);
+ return getWorkspace().newResource(destinationPath, source.getType());
+ }
+
+ /**
+ * This is done in order to generate less garbage.
+ */
+ protected RefreshLocalVisitor getRefreshLocalVisitor() {
+ if (refreshLocalVisitor == null)
+ refreshLocalVisitor = new RefreshLocalVisitor(Policy.monitorFor(null));
+ return refreshLocalVisitor;
+ }
+
+ public IStatus getStatus() {
+ return status;
+ }
+
+ protected Workspace getWorkspace() {
+ return (Workspace) rootDestination.getWorkspace();
+ }
+
+ protected boolean isSynchronized(UnifiedTreeNode node) {
+ /* does the resource exist in workspace and file system? */
+ if (!node.existsInWorkspace() || !node.existsInFileSystem())
+ return false;
+ /* we don't care about folder last modified */
+ if (node.isFolder() && node.getResource().getType() == IResource.FOLDER)
+ return true;
+ /* is lastModified different? */
+ Resource target = (Resource) node.getResource();
+ long lastModifed = target.getResourceInfo(false, false).getLocalSyncInfo();
+ if (lastModifed != node.getLastModified())
+ return false;
+ return true;
+ }
+
+ protected void synchronize(UnifiedTreeNode node) throws CoreException {
+ getRefreshLocalVisitor().visit(node);
+ }
+
+ public boolean visit(UnifiedTreeNode node) throws CoreException {
+ Policy.checkCanceled(monitor);
+ int work = 1;
+ try {
+ //location can be null if based on an undefined variable
+ if (node.getStore() == null) {
+ //should still be a best effort copy
+ IPath path = node.getResource().getFullPath();
+ String message = NLS.bind(Messages.localstore_locationUndefined, path);
+ status.add(new ResourceStatus(IResourceStatus.FAILED_READ_LOCAL, path, message, null));
+ return false;
+ }
+ boolean wasSynchronized = isSynchronized(node);
+ if (force && !wasSynchronized) {
+ synchronize(node);
+ // If not synchronized, the monitor did not take this resource into account.
+ // So, do not report work on it.
+ work = 0;
+ //if source still doesn't exist, then fail because we can't copy a missing resource
+ if (!node.existsInFileSystem()) {
+ IPath path = node.getResource().getFullPath();
+ String message = NLS.bind(Messages.resources_mustExist, path);
+ status.add(new ResourceStatus(IResourceStatus.RESOURCE_NOT_FOUND, path, message, null));
+ return false;
+ }
+ }
+ if (!force && !wasSynchronized) {
+ IPath path = node.getResource().getFullPath();
+ String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, path);
+ status.add(new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, path, message, null));
+ return true;
+ }
+ return copy(node);
+ } finally {
+ monitor.worked(work);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/DeleteVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Matt McCutchen - fix for bug 174492
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.util.Iterator;
+import java.util.List;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.filesystem.provider.FileInfo;
+import org.eclipse.core.internal.resources.ICoreConstants;
+import org.eclipse.core.internal.resources.Resource;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+public class DeleteVisitor implements IUnifiedTreeVisitor, ICoreConstants {
+ protected boolean force;
+ protected boolean keepHistory;
+ protected IProgressMonitor monitor;
+ protected List skipList;
+ protected MultiStatus status;
+
+ /**
+ * The number of tickets available on the progress monitor
+ */
+ private int ticks;
+
+ public DeleteVisitor(List skipList, int flags, IProgressMonitor monitor, int ticks) {
+ this.skipList = skipList;
+ this.ticks = ticks;
+ this.force = (flags & IResource.FORCE) != 0;
+ this.keepHistory = (flags & IResource.KEEP_HISTORY) != 0;
+ this.monitor = monitor;
+ status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_DELETE_LOCAL, Messages.localstore_deleteProblem, null);
+ }
+
+ /**
+ * Deletes a file from both the workspace resource tree and the file system.
+ */
+ protected void delete(UnifiedTreeNode node, boolean shouldKeepHistory) {
+ Resource target = (Resource) node.getResource();
+ try {
+ final boolean deleteLocalFile = !target.isLinked() && node.existsInFileSystem();
+ IFileStore localFile = deleteLocalFile ? node.getStore() : null;
+ if (deleteLocalFile && shouldKeepHistory)
+ recursiveKeepHistory(target.getLocalManager().getHistoryStore(), node);
+ node.removeChildrenFromTree();
+ //delete from disk
+ int work = ticks < 0 ? 0 : ticks;
+ ticks -= work;
+ if (deleteLocalFile)
+ localFile.delete(EFS.NONE, Policy.subMonitorFor(monitor, work));
+ else
+ monitor.worked(work);
+ //delete from tree
+ if (node.existsInWorkspace())
+ target.deleteResource(true, status);
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ // delete might have been partly successful, so refresh to ensure in sync
+ try {
+ target.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e1) {
+ //ignore secondary failure - we are just trying to cleanup from first failure
+ }
+ }
+ }
+
+ /**
+ * Only consider path in equality in order to handle gender changes
+ */
+ protected boolean equals(IResource one, IResource another) {
+ return one.getFullPath().equals(another.getFullPath());
+ }
+
+ public MultiStatus getStatus() {
+ return status;
+ }
+
+ protected boolean isAncestor(IResource one, IResource another) {
+ return one.getFullPath().isPrefixOf(another.getFullPath()) && !equals(one, another);
+ }
+
+ protected boolean isAncestorOfResourceToSkip(IResource resource) {
+ if (skipList == null)
+ return false;
+ for (int i = 0; i < skipList.size(); i++) {
+ IResource target = (IResource) skipList.get(i);
+ if (isAncestor(resource, target))
+ return true;
+ }
+ return false;
+ }
+
+ private void recursiveKeepHistory(IHistoryStore store, UnifiedTreeNode node) {
+ final IResource target = node.getResource();
+ //we don't delete linked content, so no need to keep history
+ if (target.isLinked() || node.isSymbolicLink())
+ return;
+ if (node.isFolder()) {
+ monitor.subTask(NLS.bind(Messages.localstore_deleting, target.getFullPath()));
+ for (Iterator children = node.getChildren(); children.hasNext();)
+ recursiveKeepHistory(store, (UnifiedTreeNode) children.next());
+ } else {
+ IFileInfo info = node.fileInfo;
+ if (info == null)
+ info = new FileInfo(node.getLocalName());
+ store.addState(target.getFullPath(), node.getStore(), info, true);
+ }
+ monitor.worked(1);
+ ticks--;
+ }
+
+ protected void removeFromSkipList(IResource resource) {
+ if (skipList != null)
+ skipList.remove(resource);
+ }
+
+ protected boolean shouldSkip(IResource resource) {
+ if (skipList == null)
+ return false;
+ for (int i = 0; i < skipList.size(); i++)
+ if (equals(resource, (IResource) skipList.get(i)))
+ return true;
+ return false;
+ }
+
+ public boolean visit(UnifiedTreeNode node) {
+ Policy.checkCanceled(monitor);
+ Resource target = (Resource) node.getResource();
+ if (shouldSkip(target)) {
+ removeFromSkipList(target);
+ int skipTicks = target.countResources(IResource.DEPTH_INFINITE, false);
+ monitor.worked(skipTicks);
+ ticks -= skipTicks;
+ return false;
+ }
+ if (isAncestorOfResourceToSkip(target))
+ return true;
+ delete(node, keepHistory);
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileStoreRoot.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.File;
+import java.net.URI;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.resources.IPathVariableManager;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Represents the root of a file system that is connected to the workspace.
+ * A file system can be rooted on any resource.
+ */
+public class FileStoreRoot {
+ private int chop;
+ /**
+ * When a root is changed, the old root object is marked invalid
+ * so that other resources with a cache of the root will know they need to update.
+ */
+ private boolean isValid = true;
+ /**
+ * If this root represents a resource in the local file system, this path
+ * represents the root location. This value is null if the root represents
+ * a non-local file system
+ */
+ private IPath localRoot = null;
+
+ private URI root;
+
+ private final IPathVariableManager variableManager;
+
+ /**
+ * Defines the root of a file system within the workspace tree.
+ * @param rootURI The virtual file representing the root of the file
+ * system that has been mounted
+ * @param workspacePath The workspace path at which this file
+ * system has been mounted
+ */
+ FileStoreRoot(URI rootURI, IPath workspacePath) {
+ Assert.isNotNull(rootURI);
+ Assert.isNotNull(workspacePath);
+ this.variableManager = ResourcesPlugin.getWorkspace().getPathVariableManager();
+ this.root = rootURI;
+ this.chop = workspacePath.segmentCount();
+ this.localRoot = toLocalPath(root);
+ }
+
+ /**
+ * Returns the resolved, absolute file system location of the resource
+ * corresponding to the given workspace path, or null if none could
+ * be computed.
+ */
+ public URI computeURI(IPath workspacePath) {
+ IPath childPath = workspacePath.removeFirstSegments(chop);
+ final URI rootURI = variableManager.resolveURI(root);
+ if (childPath.segmentCount() == 0)
+ return rootURI;
+ try {
+ return EFS.getStore(rootURI).getChild(childPath).toURI();
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Creates an IFileStore for a given workspace path.
+ * @exception CoreException If the file system for that resource is undefined
+ */
+ IFileStore createStore(IPath workspacePath) throws CoreException {
+ IPath childPath = workspacePath.removeFirstSegments(chop);
+ IFileStore rootStore;
+ final URI uri = variableManager.resolveURI(root);
+ if (!uri.isAbsolute()) {
+ //handles case where resource location cannot be resolved
+ //such as unresolved path variable or invalid file system scheme
+ return EFS.getNullFileSystem().getStore(workspacePath);
+ }
+ rootStore = EFS.getStore(uri);
+ if (childPath.segmentCount() == 0)
+ return rootStore;
+ return rootStore.getChild(childPath);
+ }
+
+ boolean isValid() {
+ return isValid;
+ }
+
+ IPath localLocation(IPath workspacePath) {
+ if (localRoot == null)
+ return null;
+ IPath location;
+ if (workspacePath.segmentCount() <= chop)
+ location = localRoot;
+ else
+ location = localRoot.append(workspacePath.removeFirstSegments(chop));
+ location = variableManager.resolvePath(location);
+ //if path is still relative then path variable could not be resolved
+ if (!location.isAbsolute())
+ return null;
+ return location;
+ }
+
+ void setValid(boolean value) {
+ this.isValid = value;
+ }
+
+ /**
+ * Returns the local path for the given URI, or null if not possible.
+ */
+ private IPath toLocalPath(URI uri) {
+ try {
+ final File localFile = EFS.getStore(uri).toLocalFile(EFS.NONE, null);
+ return localFile == null ? null : new Path(localFile.getAbsolutePath());
+ } catch (CoreException e) {
+ return FileUtil.toPath(uri);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1032 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Martin Oberhuber (Wind River) - [210664] descriptionChanged(): ignore LF style
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.resources.File;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.xml.sax.InputSource;
+
+/**
+ * Manages the synchronization between the workspace's view and the file system.
+ */
+public class FileSystemResourceManager implements ICoreConstants, IManager {
+
+ /**
+ * The history store is initialized lazily - always use the accessor method
+ */
+ protected IHistoryStore _historyStore;
+ protected Workspace workspace;
+
+ public FileSystemResourceManager(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ /**
+ * Returns the workspace paths of all resources that may correspond to
+ * the given file system location. Returns an empty ArrayList if there are no
+ * such paths. This method does not consider whether resources actually
+ * exist at the given locations.
+ * <p>
+ * The workspace paths of {@link IResource#HIDDEN} project and resources
+ * located in {@link IResource#HIDDEN} projects won't be added to the result.
+ * </p>
+ */
+ protected ArrayList allPathsForLocation(URI inputLocation) {
+ URI location = FileUtil.canonicalURI(inputLocation);
+ final boolean isFileLocation = EFS.SCHEME_FILE.equals(inputLocation.getScheme());
+ final IWorkspaceRoot root = getWorkspace().getRoot();
+ final ArrayList results = new ArrayList();
+ if (URIUtil.equals(location, root.getLocationURI())) {
+ //there can only be one resource at the workspace root's location
+ results.add(Path.ROOT);
+ return results;
+ }
+ IPathVariableManager varMan = workspace.getPathVariableManager();
+ IProject[] projects = root.getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ //check the project location
+ URI testLocation = project.getLocationURI();
+ if (testLocation == null)
+ continue;
+ // if we are looking for file: locations try to get a file: location for this project
+ if (isFileLocation && !EFS.SCHEME_FILE.equals(testLocation.getScheme()))
+ testLocation = getFileURI(testLocation);
+ if (testLocation == null)
+ continue;
+ URI relative = testLocation.relativize(location);
+ if (!relative.isAbsolute() && !relative.equals(testLocation)) {
+ IPath suffix = new Path(relative.getPath());
+ results.add(project.getFullPath().append(suffix));
+ }
+ ProjectDescription description = ((Project) project).internalGetDescription();
+ if (description == null)
+ continue;
+ HashMap links = description.getLinks();
+ if (links == null)
+ continue;
+ for (Iterator it = links.values().iterator(); it.hasNext();) {
+ LinkDescription link = (LinkDescription) it.next();
+ testLocation = varMan.resolveURI(link.getLocationURI());
+ // if we are looking for file: locations try to get a file: location for this link
+ if (isFileLocation && !EFS.SCHEME_FILE.equals(testLocation.getScheme()))
+ testLocation = getFileURI(testLocation);
+ if (testLocation == null)
+ continue;
+ relative = testLocation.relativize(location);
+ if (!relative.isAbsolute() && !relative.equals(testLocation)) {
+ IPath suffix = new Path(relative.getPath());
+ results.add(project.getFullPath().append(link.getProjectRelativePath()).append(suffix));
+ }
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Tries to obtain a file URI for the given URI. Returns <code>null</code> if the file system associated
+ * to the URI scheme does not map to the local file system.
+ * @param locationURI the URI to convert
+ * @return a file URI or <code>null</code>
+ */
+ private URI getFileURI(URI locationURI) {
+ try {
+ IFileStore testLocationStore = EFS.getStore(locationURI);
+ java.io.File storeAsFile = testLocationStore.toLocalFile(EFS.NONE, null);
+ if (storeAsFile != null)
+ return URIUtil.toURI(storeAsFile.getAbsolutePath());
+ } catch (CoreException e) {
+ // we don't know such file system or some other failure, just return null
+ }
+ return null;
+ }
+
+ /**
+ * Returns all resources that correspond to the given file system location,
+ * including resources under linked resources. Returns an empty array if
+ * there are no corresponding resources.
+ * <p>
+ * If the {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} flag is specified
+ * in the member flags, team private members will be included along with the
+ * others. If the {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} flag is
+ * not specified (recommended), the result will omit any team private member
+ * resources.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_HIDDEN} flag is specified in the member
+ * flags, hidden members will be included along with the others. If the
+ * {@link IContainer#INCLUDE_HIDDEN} flag is not specified (recommended),
+ * the result will omit any hidden member resources.
+ * </p>
+ *
+ * @param location
+ * the file system location
+ * @param files
+ * resources that may exist below the project level can be either
+ * files or folders. If this parameter is true, files will be
+ * returned, otherwise containers will be returned.
+ * @param memberFlags
+ * bit-wise or of member flag constants (
+ * {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} and
+ * {@link IContainer#INCLUDE_HIDDEN}) indicating which members are of
+ * interest
+ */
+ public IResource[] allResourcesFor(URI location, boolean files, int memberFlags) {
+ ArrayList result = allPathsForLocation(location);
+ int count = 0;
+ for (int i = 0, imax = result.size(); i < imax; i++) {
+ //replace the path in the list with the appropriate resource type
+ IResource resource = resourceFor((IPath) result.get(i), files);
+
+ if (resource == null || (((memberFlags & IContainer.INCLUDE_HIDDEN) == 0) && resource.isHidden(IResource.CHECK_ANCESTORS))
+ || (((memberFlags & IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS) == 0) && resource.isTeamPrivateMember(IResource.CHECK_ANCESTORS)))
+ resource = null;
+
+ result.set(i, resource);
+ //count actual resources - some paths won't have a corresponding resource
+ if (resource != null)
+ count++;
+ }
+ //convert to array and remove null elements
+ IResource[] toReturn = files ? (IResource[]) new IFile[count] : (IResource[]) new IContainer[count];
+ count = 0;
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ IResource resource = (IResource) it.next();
+ if (resource != null)
+ toReturn[count++] = resource;
+ }
+ return toReturn;
+ }
+
+ /* (non-javadoc)
+ * @see IResource.getResourceAttributes
+ */
+ public ResourceAttributes attributes(IResource resource) {
+ IFileStore store = getStore(resource);
+ IFileInfo fileInfo = store.fetchInfo();
+ if (!fileInfo.exists())
+ return null;
+ return FileUtil.fileInfoToAttributes(fileInfo);
+ }
+
+ /**
+ * Returns a container for the given file system location or null if there
+ * is no mapping for this path. If the path has only one segment, then an
+ * <code>IProject</code> is returned. Otherwise, the returned object
+ * is a <code>IFolder</code>. This method does NOT check the existence
+ * of a folder in the given location. Location cannot be null.
+ */
+ public IContainer containerForLocation(IPath location) {
+ IPath path = pathForLocation(location);
+ return path == null ? null : (IContainer) resourceFor(path, false);
+ }
+
+ public void copy(IResource target, IResource destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ int totalWork = ((Resource) target).countResources(IResource.DEPTH_INFINITE, false);
+ String title = NLS.bind(Messages.localstore_copying, target.getFullPath());
+ monitor.beginTask(title, totalWork);
+ IFileStore destinationStore = getStore(destination);
+ if (destinationStore.fetchInfo().exists()) {
+ String message = NLS.bind(Messages.localstore_resourceExists, destination.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, destination.getFullPath(), message, null);
+ }
+ getHistoryStore().copyHistory(target, destination, false);
+ CopyVisitor visitor = new CopyVisitor(target, destination, updateFlags, monitor);
+ UnifiedTree tree = new UnifiedTree(target);
+ tree.accept(visitor, IResource.DEPTH_INFINITE);
+ IStatus status = visitor.getStatus();
+ if (!status.isOK())
+ throw new ResourceException(status);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ public void delete(IResource target, int flags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ Resource resource = (Resource) target;
+ final int deleteWork = resource.countResources(IResource.DEPTH_INFINITE, false) * 2;
+ boolean force = (flags & IResource.FORCE) != 0;
+ int refreshWork = 0;
+ if (!force)
+ refreshWork = Math.min(deleteWork, 100);
+ String title = NLS.bind(Messages.localstore_deleting, resource.getFullPath());
+ monitor.beginTask(title, deleteWork + refreshWork);
+ monitor.subTask(""); //$NON-NLS-1$
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_DELETE_LOCAL, Messages.localstore_deleteProblem, null);
+ List skipList = null;
+ UnifiedTree tree = new UnifiedTree(target);
+ if (!force) {
+ IProgressMonitor sub = Policy.subMonitorFor(monitor, refreshWork);
+ sub.beginTask("", 1000); //$NON-NLS-1$
+ try {
+ CollectSyncStatusVisitor refreshVisitor = new CollectSyncStatusVisitor(Messages.localstore_deleteProblem, sub);
+ refreshVisitor.setIgnoreLocalDeletions(true);
+ tree.accept(refreshVisitor, IResource.DEPTH_INFINITE);
+ status.merge(refreshVisitor.getSyncStatus());
+ skipList = refreshVisitor.getAffectedResources();
+ } finally {
+ sub.done();
+ }
+ }
+ DeleteVisitor deleteVisitor = new DeleteVisitor(skipList, flags, monitor, deleteWork);
+ tree.accept(deleteVisitor, IResource.DEPTH_INFINITE);
+ status.merge(deleteVisitor.getStatus());
+ if (!status.isOK())
+ throw new ResourceException(status);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Returns true if the description on disk is different from the given byte array,
+ * and false otherwise.
+ * Since org.eclipse.core.resources 3.4.1 differences in line endings (CR, LF, CRLF)
+ * are not considered.
+ */
+ private boolean descriptionChanged(IFile descriptionFile, byte[] newContents) {
+ InputStream oldStream = null;
+ try {
+ //buffer size: twice the description length, but maximum 8KB
+ int bufsize = newContents.length > 4096 ? 8192 : newContents.length * 2;
+ oldStream = new BufferedInputStream(descriptionFile.getContents(true), bufsize);
+ InputStream newStream = new ByteArrayInputStream(newContents);
+ //compare streams char by char, ignoring line endings
+ int newChar = newStream.read();
+ int oldChar = oldStream.read();
+ while (newChar >= 0 && oldChar >= 0) {
+ if (newChar == oldChar) {
+ //streams are the same
+ newChar = newStream.read();
+ oldChar = oldStream.read();
+ } else if ((newChar == '\r' || newChar == '\n') && (oldChar == '\r' || oldChar == '\n')) {
+ //got a difference, but both sides are newlines: read over newlines
+ while (newChar == '\r' || newChar == '\n')
+ newChar = newStream.read();
+ while (oldChar == '\r' || oldChar == '\n')
+ oldChar = oldStream.read();
+ } else {
+ //streams are different
+ return true;
+ }
+ }
+ //test for excess data in one stream
+ if (newChar >= 0 || oldChar >= 0)
+ return true;
+ return false;
+ } catch (Exception e) {
+ Policy.log(e);
+ //if we failed to compare, just write the new contents
+ } finally {
+ FileUtil.safeClose(oldStream);
+ }
+ return true;
+ }
+
+ /**
+ * @deprecated
+ */
+ public int doGetEncoding(IFileStore store) throws CoreException {
+ InputStream input = null;
+ try {
+ input = store.openInputStream(EFS.NONE, null);
+ int first = input.read();
+ int second = input.read();
+ if (first == -1 || second == -1)
+ return IFile.ENCODING_UNKNOWN;
+ first &= 0xFF;//converts unsigned byte to int
+ second &= 0xFF;
+ //look for the UTF-16 Byte Order Mark (BOM)
+ if (first == 0xFE && second == 0xFF)
+ return IFile.ENCODING_UTF_16BE;
+ if (first == 0xFF && second == 0xFE)
+ return IFile.ENCODING_UTF_16LE;
+ int third = (input.read() & 0xFF);
+ if (third == -1)
+ return IFile.ENCODING_UNKNOWN;
+ //look for the UTF-8 BOM
+ if (first == 0xEF && second == 0xBB && third == 0xBF)
+ return IFile.ENCODING_UTF_8;
+ return IFile.ENCODING_UNKNOWN;
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.localstore_couldNotRead, store.toString());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, null, message, e);
+ } finally {
+ FileUtil.safeClose(input);
+ }
+ }
+
+ /**
+ * Optimized sync check for files. Returns true if the file exists and is in sync, and false
+ * otherwise. The intent is to let the default implementation handle the complex
+ * cases like gender change, case variants, etc.
+ */
+ public boolean fastIsSynchronized(File target) {
+ ResourceInfo info = target.getResourceInfo(false, false);
+ if (target.exists(target.getFlags(info), true)) {
+ IFileInfo fileInfo = getStore(target).fetchInfo();
+ if (!fileInfo.isDirectory() && info.getLocalSyncInfo() == fileInfo.getLastModified())
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns an IFile for the given file system location or null if there
+ * is no mapping for this path. This method does NOT check the existence
+ * of a file in the given location. Location cannot be null.
+ */
+ public IFile fileForLocation(IPath location) {
+ IPath path = pathForLocation(location);
+ return path == null ? null : (IFile) resourceFor(path, true);
+ }
+
+ /**
+ * @deprecated
+ */
+ public int getEncoding(File target) throws CoreException {
+ // thread safety: (the location can be null if the project for this file does not exist)
+ IFileStore store = getStore(target);
+ if (!store.fetchInfo().exists()) {
+ String message = NLS.bind(Messages.localstore_fileNotFound, store.toString());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, target.getFullPath(), message, null);
+ }
+ return doGetEncoding(store);
+ }
+
+ public IHistoryStore getHistoryStore() {
+ if (_historyStore == null) {
+ IPath location = getWorkspace().getMetaArea().getHistoryStoreLocation();
+ location.toFile().mkdirs();
+ _historyStore = ResourcesCompatibilityHelper.createHistoryStore(location, 256);
+ }
+ return _historyStore;
+ }
+
+ /**
+ * Returns the real name of the resource on disk. Returns null if no local
+ * file exists by that name. This is useful when dealing with
+ * case insensitive file systems.
+ */
+ public String getLocalName(IFileStore target) {
+ return target.fetchInfo().getName();
+ }
+
+ protected IPath getProjectDefaultLocation(IProject project) {
+ return workspace.getRoot().getLocation().append(project.getFullPath());
+ }
+
+ /**
+ * Never returns null
+ * @param target
+ * @return The file store for this resource
+ */
+ public IFileStore getStore(IResource target) {
+ try {
+ return getStoreRoot(target).createStore(target.getFullPath());
+ } catch (CoreException e) {
+ //callers aren't expecting failure here, so return null file system
+ return EFS.getNullFileSystem().getStore(target.getFullPath());
+ }
+ }
+
+ /**
+ * Returns the file store root for the provided resource. Never returns null.
+ */
+ private FileStoreRoot getStoreRoot(IResource target) {
+ ResourceInfo info = workspace.getResourceInfo(target.getFullPath(), true, false);
+ FileStoreRoot root;
+ if (info != null) {
+ root = info.getFileStoreRoot();
+ if (root != null && root.isValid())
+ return root;
+ if (info.isSet(ICoreConstants.M_LINK)) {
+ ProjectDescription description = ((Project) target.getProject()).internalGetDescription();
+ if (description != null) {
+ final URI linkLocation = description.getLinkLocationURI(target.getProjectRelativePath());
+ //if we can't determine the link location, fall through to parent resource
+ if (linkLocation != null) {
+ setLocation(target, info, linkLocation);
+ return info.getFileStoreRoot();
+ }
+ }
+ }
+ }
+ final IContainer parent = target.getParent();
+ if (parent == null) {
+ //this is the root, so we know where this must be located
+ //initialize root location
+ info = workspace.getResourceInfo(Path.ROOT, false, true);
+ final IWorkspaceRoot rootResource = workspace.getRoot();
+ setLocation(rootResource, info, URIUtil.toURI(rootResource.getLocation()));
+ return info.getFileStoreRoot();
+ }
+ root = getStoreRoot(parent);
+ if (info != null)
+ info.setFileStoreRoot(root);
+ return root;
+ }
+
+ protected Workspace getWorkspace() {
+ return workspace;
+ }
+
+ /**
+ * Returns whether the project has any local content on disk.
+ */
+ public boolean hasSavedContent(IProject project) {
+ return getStore(project).fetchInfo().exists();
+ }
+
+ /**
+ * Returns whether the project has a project description file on disk.
+ */
+ public boolean hasSavedDescription(IProject project) {
+ return getStore(project).getChild(IProjectDescription.DESCRIPTION_FILE_NAME).fetchInfo().exists();
+ }
+
+ /**
+ * Initializes the file store for a resource.
+ *
+ * @param target The resource to initialize the file store for.
+ * @param location the File system location of this resource on disk
+ * @return The file store for the provided resource
+ */
+ private IFileStore initializeStore(IResource target, URI location) throws CoreException {
+ ResourceInfo info = ((Resource) target).getResourceInfo(false, true);
+ setLocation(target, info, location);
+ FileStoreRoot root = getStoreRoot(target);
+ return root.createStore(target.getFullPath());
+ }
+
+ /**
+ * The target must exist in the workspace. This method must only ever
+ * be called from Project.writeDescription(), because that method ensures
+ * that the description isn't then immediately discovered as a new change.
+ * @return true if a new description was written, and false if it wasn't written
+ * because it was unchanged
+ */
+ public boolean internalWrite(IProject target, IProjectDescription description, int updateFlags, boolean hasPublicChanges, boolean hasPrivateChanges) throws CoreException {
+ //write the project's private description to the metadata area
+ if (hasPrivateChanges)
+ getWorkspace().getMetaArea().writePrivateDescription(target);
+ if (!hasPublicChanges)
+ return false;
+ //can't do anything if there's no description
+ if (description == null)
+ return false;
+
+ //write the model to a byte array
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ new ModelObjectWriter().write(description, out);
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.resources_writeMeta, target.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), msg, e);
+ }
+ byte[] newContents = out.toByteArray();
+
+ //write the contents to the IFile that represents the description
+ IFile descriptionFile = target.getFile(IProjectDescription.DESCRIPTION_FILE_NAME);
+ if (!descriptionFile.exists())
+ workspace.createResource(descriptionFile, false);
+ else {
+ //if the description has not changed, don't write anything
+ if (!descriptionChanged(descriptionFile, newContents))
+ return false;
+ }
+ ByteArrayInputStream in = new ByteArrayInputStream(newContents);
+ IFileStore descriptionFileStore = ((Resource) descriptionFile).getStore();
+ IFileInfo fileInfo = descriptionFileStore.fetchInfo();
+ if (fileInfo.getAttribute(EFS.ATTRIBUTE_READ_ONLY)) {
+ IStatus result = getWorkspace().validateEdit(new IFile[] {descriptionFile}, null);
+ if (!result.isOK())
+ throw new ResourceException(result);
+ // re-read the file info in case the file attributes were modified
+ fileInfo = descriptionFileStore.fetchInfo();
+ }
+ //write the project description file (don't use API because scheduling rule might not match)
+ write(descriptionFile, in, fileInfo, IResource.FORCE, false, Policy.monitorFor(null));
+ workspace.getAliasManager().updateAliases(descriptionFile, getStore(descriptionFile), IResource.DEPTH_ZERO, Policy.monitorFor(null));
+
+ //update the timestamp on the project as well so we know when it has
+ //been changed from the outside
+ long lastModified = ((Resource) descriptionFile).getResourceInfo(false, false).getLocalSyncInfo();
+ ResourceInfo info = ((Resource) target).getResourceInfo(false, true);
+ updateLocalSync(info, lastModified);
+
+ //for backwards compatibility, ensure the old .prj file is deleted
+ getWorkspace().getMetaArea().clearOldDescription(target);
+ return true;
+ }
+
+ /**
+ * Returns true if the given project's description is synchronized with
+ * the project description file on disk, and false otherwise.
+ */
+ public boolean isDescriptionSynchronized(IProject target) {
+ //sync info is stored on the description file, and on project info.
+ //when the file is changed by someone else, the project info modification
+ //stamp will be out of date
+ IFile descriptionFile = target.getFile(IProjectDescription.DESCRIPTION_FILE_NAME);
+ ResourceInfo projectInfo = ((Resource) target).getResourceInfo(false, false);
+ if (projectInfo == null)
+ return false;
+ return projectInfo.getLocalSyncInfo() == getStore(descriptionFile).fetchInfo().getLastModified();
+ }
+
+ /* (non-Javadoc)
+ * Returns true if the given resource is synchronized with the file system
+ * to the given depth. Returns false otherwise.
+ *
+ * @see IResource#isSynchronized(int)
+ */
+ public boolean isSynchronized(IResource target, int depth) {
+ switch (target.getType()) {
+ case IResource.ROOT :
+ if (depth == IResource.DEPTH_ZERO)
+ return true;
+ //check sync on child projects.
+ depth = depth == IResource.DEPTH_ONE ? IResource.DEPTH_ZERO : depth;
+ IProject[] projects = ((IWorkspaceRoot) target).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ if (!isSynchronized(projects[i], depth))
+ return false;
+ }
+ return true;
+ case IResource.PROJECT :
+ if (!target.isAccessible())
+ return true;
+ break;
+ case IResource.FILE :
+ if (fastIsSynchronized((File) target))
+ return true;
+ break;
+ }
+ IsSynchronizedVisitor visitor = new IsSynchronizedVisitor(Policy.monitorFor(null));
+ UnifiedTree tree = new UnifiedTree(target);
+ try {
+ tree.accept(visitor, depth);
+ } catch (CoreException e) {
+ Policy.log(e);
+ return false;
+ } catch (IsSynchronizedVisitor.ResourceChangedException e) {
+ //visitor throws an exception if out of sync
+ return false;
+ }
+ return true;
+ }
+
+ public void link(Resource target, URI location, IFileInfo fileInfo) throws CoreException {
+ initializeStore(target, location);
+ ResourceInfo info = target.getResourceInfo(false, true);
+ long lastModified = fileInfo == null ? 0 : fileInfo.getLastModified();
+ if (lastModified == 0)
+ info.clearModificationStamp();
+ updateLocalSync(info, lastModified);
+ }
+
+ /**
+ * Returns the resolved, absolute file system location of the given resource.
+ * Returns null if the location could not be resolved.
+ */
+ public IPath locationFor(IResource target) {
+ return getStoreRoot(target).localLocation(target.getFullPath());
+ }
+
+ /**
+ * Returns the resolved, absolute file system location of the given resource.
+ * Returns null if the location could not be resolved.
+ */
+ public URI locationURIFor(IResource target) {
+ return getStoreRoot(target).computeURI(target.getFullPath());
+ }
+
+ public void move(IResource source, IFileStore destination, int flags, IProgressMonitor monitor) throws CoreException {
+ //TODO figure out correct semantics for case where destination exists on disk
+ getStore(source).move(destination, EFS.NONE, monitor);
+ }
+
+ /**
+ * Returns a resource path to the given local location. Returns null if
+ * it is not under a project's location.
+ */
+ protected IPath pathForLocation(IPath location) {
+ if (workspace.getRoot().getLocation().equals(location))
+ return Path.ROOT;
+ IProject[] projects = getWorkspace().getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ IPath projectLocation = project.getLocation();
+ if (projectLocation != null && projectLocation.isPrefixOf(location)) {
+ int segmentsToRemove = projectLocation.segmentCount();
+ return project.getFullPath().append(location.removeFirstSegments(segmentsToRemove));
+ }
+ }
+ return null;
+ }
+
+ public InputStream read(IFile target, boolean force, IProgressMonitor monitor) throws CoreException {
+ IFileStore store = getStore(target);
+ if (!force) {
+ final IFileInfo fileInfo = store.fetchInfo();
+ if (!fileInfo.exists()) {
+ String message = NLS.bind(Messages.localstore_fileNotFound, store.toString());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, target.getFullPath(), message, null);
+ }
+ ResourceInfo info = ((Resource) target).getResourceInfo(true, false);
+ int flags = ((Resource) target).getFlags(info);
+ ((Resource) target).checkExists(flags, true);
+ if (fileInfo.getLastModified() != info.getLocalSyncInfo()) {
+ String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, target.getFullPath());
+ throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message, null);
+ }
+ }
+ return store.openInputStream(EFS.NONE, monitor);
+ }
+
+ /**
+ * Reads and returns the project description for the given project.
+ * Never returns null.
+ * @param target the project whose description should be read.
+ * @param creation true if this project is just being created, in which
+ * case the private project information (including the location) needs to be read
+ * from disk as well.
+ * @exception CoreException if there was any failure to read the project
+ * description, or if the description was missing.
+ */
+ public ProjectDescription read(IProject target, boolean creation) throws CoreException {
+ //read the project location if this project is being created
+ URI projectLocation = null;
+ ProjectDescription privateDescription = null;
+ if (creation) {
+ privateDescription = new ProjectDescription();
+ getWorkspace().getMetaArea().readPrivateDescription(target, privateDescription);
+ projectLocation = privateDescription.getLocationURI();
+ } else {
+ IProjectDescription description = ((Project) target).internalGetDescription();
+ if (description != null && description.getLocationURI() != null) {
+ projectLocation = description.getLocationURI();
+ }
+ }
+ final boolean isDefaultLocation = projectLocation == null;
+ if (isDefaultLocation) {
+ projectLocation = URIUtil.toURI(getProjectDefaultLocation(target));
+ }
+ IFileStore projectStore = initializeStore(target, projectLocation);
+ IFileStore descriptionStore = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME);
+ ProjectDescription description = null;
+ //hold onto any exceptions until after sync info is updated, then throw it
+ ResourceException error = null;
+ InputStream in = null;
+ try {
+ in = new BufferedInputStream(descriptionStore.openInputStream(EFS.NONE, null));
+ description = new ProjectDescriptionReader(target).read(new InputSource(in));
+ } catch (CoreException e) {
+ //try the legacy location in the meta area
+ description = getWorkspace().getMetaArea().readOldDescription(target);
+ if (description != null)
+ return description;
+ if (!descriptionStore.fetchInfo().exists()) {
+ String msg = NLS.bind(Messages.resources_missingProjectMeta, target.getName());
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, target.getFullPath(), msg, null);
+ }
+ String msg = NLS.bind(Messages.resources_readProjectMeta, target.getName());
+ error = new ResourceException(IResourceStatus.FAILED_READ_METADATA, target.getFullPath(), msg, e);
+ } finally {
+ FileUtil.safeClose(in);
+ }
+ if (error == null && description == null) {
+ String msg = NLS.bind(Messages.resources_readProjectMeta, target.getName());
+ error = new ResourceException(IResourceStatus.FAILED_READ_METADATA, target.getFullPath(), msg, null);
+ }
+ if (description != null) {
+ //don't trust the project name in the description file
+ description.setName(target.getName());
+ if (!isDefaultLocation)
+ description.setLocationURI(projectLocation);
+ if (creation && privateDescription != null)
+ description.setDynamicReferences(privateDescription.getDynamicReferences(false));
+ }
+ long lastModified = descriptionStore.fetchInfo().getLastModified();
+ IFile descriptionFile = target.getFile(IProjectDescription.DESCRIPTION_FILE_NAME);
+ //don't get a mutable copy because we might be in restore which isn't an operation
+ //it doesn't matter anyway because local sync info is not included in deltas
+ ResourceInfo info = ((Resource) descriptionFile).getResourceInfo(false, false);
+ if (info == null) {
+ //create a new resource on the sly -- don't want to start an operation
+ info = getWorkspace().createResource(descriptionFile, false);
+ updateLocalSync(info, lastModified);
+ }
+ //if the project description has changed between sessions, let it remain
+ //out of sync -- that way link changes will be reconciled on next refresh
+ if (!creation)
+ updateLocalSync(info, lastModified);
+
+ //update the timestamp on the project as well so we know when it has
+ //been changed from the outside
+ info = ((Resource) target).getResourceInfo(false, true);
+ updateLocalSync(info, lastModified);
+
+ if (error != null)
+ throw error;
+ return description;
+ }
+
+ public boolean refresh(IResource target, int depth, boolean updateAliases, IProgressMonitor monitor) throws CoreException {
+ switch (target.getType()) {
+ case IResource.ROOT :
+ return refreshRoot((IWorkspaceRoot) target, depth, updateAliases, monitor);
+ case IResource.PROJECT :
+ if (!target.isAccessible())
+ return false;
+ //fall through
+ case IResource.FOLDER :
+ case IResource.FILE :
+ return refreshResource(target, depth, updateAliases, monitor);
+ }
+ return false;
+ }
+
+ protected boolean refreshResource(IResource target, int depth, boolean updateAliases, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ int totalWork = RefreshLocalVisitor.TOTAL_WORK;
+ String title = NLS.bind(Messages.localstore_refreshing, target.getFullPath());
+ try {
+ monitor.beginTask(title, totalWork);
+ RefreshLocalVisitor visitor = updateAliases ? new RefreshLocalAliasVisitor(monitor) : new RefreshLocalVisitor(monitor);
+ IFileStore fileStore = ((Resource) target).getStore();
+ //try to get all info in one shot, if file system supports it
+ IFileTree fileTree = fileStore.getFileSystem().fetchFileTree(fileStore, new SubProgressMonitor(monitor, 0));
+ UnifiedTree tree = fileTree == null ? new UnifiedTree(target) : new UnifiedTree(target, fileTree);
+ tree.accept(visitor, depth);
+ IStatus result = visitor.getErrorStatus();
+ if (!result.isOK())
+ throw new ResourceException(result);
+ return visitor.resourcesChanged();
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Synchronizes the entire workspace with the local file system.
+ * The current implementation does this by synchronizing each of the
+ * projects currently in the workspace. A better implementation may
+ * be possible.
+ */
+ protected boolean refreshRoot(IWorkspaceRoot target, int depth, boolean updateAliases, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ IProject[] projects = target.getProjects(IContainer.INCLUDE_HIDDEN);
+ int totalWork = projects.length;
+ String title = Messages.localstore_refreshingRoot;
+ try {
+ monitor.beginTask(title, totalWork);
+ // if doing depth zero, there is nothing to do (can't refresh the root).
+ // Note that we still need to do the beginTask, done pair.
+ if (depth == IResource.DEPTH_ZERO)
+ return false;
+ boolean changed = false;
+ // drop the depth by one level since processing the root counts as one level.
+ depth = depth == IResource.DEPTH_ONE ? IResource.DEPTH_ZERO : depth;
+ for (int i = 0; i < projects.length; i++)
+ changed |= refresh(projects[i], depth, updateAliases, Policy.subMonitorFor(monitor, 1));
+ return changed;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Returns the resource corresponding to the given workspace path. The
+ * "files" parameter is used for paths of two or more segments. If true,
+ * a file is returned, otherwise a folder is returned. Returns null if files is true
+ * and the path is not of sufficient length.
+ */
+ protected IResource resourceFor(IPath path, boolean files) {
+ int numSegments = path.segmentCount();
+ if (files && numSegments < ICoreConstants.MINIMUM_FILE_SEGMENT_LENGTH)
+ return null;
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ if (path.isRoot())
+ return root;
+ if (numSegments == 1)
+ return root.getProject(path.segment(0));
+ return files ? (IResource) root.getFile(path) : (IResource) root.getFolder(path);
+ }
+
+ /* (non-javadoc)
+ * @see IResouce.setLocalTimeStamp
+ */
+ public long setLocalTimeStamp(IResource target, ResourceInfo info, long value) throws CoreException {
+ IFileStore store = getStore(target);
+ IFileInfo fileInfo = store.fetchInfo();
+ fileInfo.setLastModified(value);
+ store.putInfo(fileInfo, EFS.SET_LAST_MODIFIED, null);
+ //actual value may be different depending on file system granularity
+ fileInfo = store.fetchInfo();
+ long actualValue = fileInfo.getLastModified();
+ updateLocalSync(info, actualValue);
+ return actualValue;
+ }
+
+ /**
+ * The storage location for a resource has changed; update the location.
+ * @param target
+ * @param info
+ * @param location
+ */
+ public void setLocation(IResource target, ResourceInfo info, URI location) {
+ FileStoreRoot oldRoot = info.getFileStoreRoot();
+ if (location != null) {
+ info.setFileStoreRoot(new FileStoreRoot(location, target.getFullPath()));
+ } else {
+ //project is in default location so clear the store root
+ info.setFileStoreRoot(null);
+ }
+ if (oldRoot != null)
+ oldRoot.setValid(false);
+ }
+
+ /* (non-javadoc)
+ * @see IResource.setResourceAttributes
+ */
+ public void setResourceAttributes(IResource resource, ResourceAttributes attributes) throws CoreException {
+ IFileStore store = getStore(resource);
+ //when the executable bit is changed on a folder a refresh is required
+ boolean refresh = false;
+ if (resource instanceof IContainer && ((store.getFileSystem().attributes() & EFS.ATTRIBUTE_EXECUTABLE) != 0))
+ refresh = store.fetchInfo().getAttribute(EFS.ATTRIBUTE_EXECUTABLE) != attributes.isExecutable();
+ store.putInfo(FileUtil.attributesToFileInfo(attributes), EFS.SET_ATTRIBUTES, null);
+ //must refresh in the background because we are not inside an operation
+ if (refresh)
+ workspace.getRefreshManager().refresh(resource);
+ }
+
+ public void shutdown(IProgressMonitor monitor) throws CoreException {
+ if (_historyStore != null)
+ _historyStore.shutdown(monitor);
+ }
+
+ public void startup(IProgressMonitor monitor) throws CoreException {
+ //nothing to do
+ }
+
+ /**
+ * The ResourceInfo must be mutable.
+ */
+ public void updateLocalSync(ResourceInfo info, long localSyncInfo) {
+ info.setLocalSyncInfo(localSyncInfo);
+ if (localSyncInfo == I_NULL_SYNC_INFO)
+ info.clear(M_LOCAL_EXISTS);
+ else
+ info.set(M_LOCAL_EXISTS);
+ }
+
+ /**
+ * The target must exist in the workspace. The content InputStream is
+ * closed even if the method fails. If the force flag is false we only write
+ * the file if it does not exist or if it is already local and the timestamp
+ * has NOT changed since last synchronization, otherwise a CoreException
+ * is thrown.
+ */
+ public void write(IFile target, InputStream content, IFileInfo fileInfo, int updateFlags, boolean append, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(null);
+ try {
+ IFileStore store = getStore(target);
+ if (fileInfo.getAttribute(EFS.ATTRIBUTE_READ_ONLY)) {
+ String message = NLS.bind(Messages.localstore_couldNotWriteReadOnly, target.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, target.getFullPath(), message, null);
+ }
+ long lastModified = fileInfo.getLastModified();
+ if (BitMask.isSet(updateFlags, IResource.FORCE)) {
+ if (append && !target.isLocal(IResource.DEPTH_ZERO) && !fileInfo.exists()) {
+ // force=true, local=false, existsInFileSystem=false
+ String message = NLS.bind(Messages.resources_mustBeLocal, target.getFullPath());
+ throw new ResourceException(IResourceStatus.RESOURCE_NOT_LOCAL, target.getFullPath(), message, null);
+ }
+ } else {
+ if (target.isLocal(IResource.DEPTH_ZERO)) {
+ // test if timestamp is the same since last synchronization
+ ResourceInfo info = ((Resource) target).getResourceInfo(true, false);
+ if (lastModified != info.getLocalSyncInfo()) {
+ String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, target.getFullPath());
+ throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message, null);
+ }
+ } else {
+ if (fileInfo.exists()) {
+ String message = NLS.bind(Messages.localstore_resourceExists, target.getFullPath());
+ throw new ResourceException(IResourceStatus.EXISTS_LOCAL, target.getFullPath(), message, null);
+ }
+ if (append) {
+ String message = NLS.bind(Messages.resources_mustBeLocal, target.getFullPath());
+ throw new ResourceException(IResourceStatus.RESOURCE_NOT_LOCAL, target.getFullPath(), message, null);
+ }
+ }
+ }
+ // add entry to History Store.
+ if (BitMask.isSet(updateFlags, IResource.KEEP_HISTORY) && fileInfo.exists())
+ //never move to the history store, because then the file is missing if write fails
+ getHistoryStore().addState(target.getFullPath(), store, fileInfo, false);
+ if (!fileInfo.exists())
+ store.getParent().mkdir(EFS.NONE, null);
+ int options = append ? EFS.APPEND : EFS.NONE;
+ OutputStream out = store.openOutputStream(options, Policy.subMonitorFor(monitor, 0));
+ FileUtil.transferStreams(content, out, store.toString(), monitor);
+ // get the new last modified time and stash in the info
+ lastModified = store.fetchInfo().getLastModified();
+ ResourceInfo info = ((Resource) target).getResourceInfo(false, true);
+ updateLocalSync(info, lastModified);
+ info.incrementContentId();
+ info.clear(M_CONTENT_CACHE);
+ workspace.updateModificationStamp(info);
+ } finally {
+ FileUtil.safeClose(content);
+ }
+ }
+
+ /**
+ * If force is false, this method fails if there is already a resource in
+ * target's location.
+ */
+ public void write(IFolder target, boolean force, IProgressMonitor monitor) throws CoreException {
+ IFileStore store = getStore(target);
+ if (!force) {
+ IFileInfo fileInfo = store.fetchInfo();
+ if (fileInfo.isDirectory()) {
+ String message = NLS.bind(Messages.localstore_resourceExists, target.getFullPath());
+ throw new ResourceException(IResourceStatus.EXISTS_LOCAL, target.getFullPath(), message, null);
+ }
+ if (fileInfo.exists()) {
+ String message = NLS.bind(Messages.localstore_fileExists, target.getFullPath());
+ throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message, null);
+ }
+ }
+ store.mkdir(EFS.NONE, monitor);
+ ResourceInfo info = ((Resource) target).getResourceInfo(false, true);
+ updateLocalSync(info, store.fetchInfo().getLastModified());
+ }
+
+ /**
+ * Write the .project file without modifying the resource tree. This is called
+ * during save when it is discovered that the .project file is missing. The tree
+ * cannot be modified during save.
+ */
+ public void writeSilently(IProject target) throws CoreException {
+ IPath location = locationFor(target);
+ //if the project location cannot be resolved, we don't know if a description file exists or not
+ if (location == null)
+ return;
+ IFileStore projectStore = getStore(target);
+ projectStore.mkdir(EFS.NONE, null);
+ //can't do anything if there's no description
+ IProjectDescription desc = ((Project) target).internalGetDescription();
+ if (desc == null)
+ return;
+ //write the project's private description to the meta-data area
+ getWorkspace().getMetaArea().writePrivateDescription(target);
+
+ //write the file that represents the project description
+ IFileStore fileStore = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME);
+ OutputStream out = null;
+ try {
+ out = fileStore.openOutputStream(EFS.NONE, null);
+ new ModelObjectWriter().write(desc, out);
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.resources_writeMeta, target.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), msg, e);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // ignore failure to close stream
+ }
+ }
+ }
+ //for backwards compatibility, ensure the old .prj file is deleted
+ getWorkspace().getMetaArea().clearOldDescription(target);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/HistoryBucket.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.Comparator;
+import org.eclipse.core.internal.utils.UniversalUniqueIdentifier;
+import org.eclipse.core.runtime.IPath;
+
+public class HistoryBucket extends Bucket {
+
+ /**
+ * A entry in the bucket index. Each entry has one path and a collection
+ * of states, which by their turn contain a (UUID, timestamp) pair.
+ * <p>
+ * This class is intended as a lightweight way of hiding the internal data structure.
+ * Objects of this class are supposed to be short-lived. No instances
+ * of this class are kept stored anywhere. The real stuff (the internal data structure)
+ * is.
+ * </p>
+ */
+ public static final class HistoryEntry extends Bucket.Entry {
+
+ final static Comparator COMPARATOR = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ byte[] state1 = (byte[]) o1;
+ byte[] state2 = (byte[]) o2;
+ return compareStates(state1, state2);
+ }
+ };
+
+ // the length of each component of the data array
+ private final static byte[][] EMPTY_DATA = new byte[0][];
+ // the length of a long in bytes
+ private final static int LONG_LENGTH = 8;
+ // the length of a UUID in bytes
+ private final static int UUID_LENGTH = UniversalUniqueIdentifier.BYTES_SIZE;
+ public final static int DATA_LENGTH = UUID_LENGTH + LONG_LENGTH;
+
+ /**
+ * The history states. The first array dimension is the number of states. The
+ * second dimension is an encoding of the {UUID,timestamp} pair for that entry.
+ */
+ private byte[][] data;
+
+ /**
+ * Comparison logic for states in byte[] form.
+ *
+ * @see Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ private static int compareStates(byte[] state1, byte[] state2) {
+ long timestamp1 = getTimestamp(state1);
+ long timestamp2 = getTimestamp(state2);
+ if (timestamp1 == timestamp2)
+ return -UniversalUniqueIdentifier.compareTime(state1, state2);
+ return timestamp1 < timestamp2 ? +1 : -1;
+ }
+
+ /**
+ * Returns the byte array representation of a (UUID, timestamp) pair.
+ */
+ private static byte[] getState(UniversalUniqueIdentifier uuid, long timestamp) {
+ byte[] uuidBytes = uuid.toBytes();
+ byte[] state = new byte[DATA_LENGTH];
+ System.arraycopy(uuidBytes, 0, state, 0, uuidBytes.length);
+ for (int j = 0; j < LONG_LENGTH; j++) {
+ state[UUID_LENGTH + j] = (byte) (0xFF & timestamp);
+ timestamp >>>= 8;
+ }
+ return state;
+ }
+
+ private static long getTimestamp(byte[] state) {
+ long timestamp = 0;
+ for (int j = 0; j < LONG_LENGTH; j++)
+ timestamp += (state[UUID_LENGTH + j] & 0xFFL) << j * 8;
+ return timestamp;
+ }
+
+ /**
+ * Inserts the given item into the given array at the right position.
+ * Returns the resulting array. Returns null if the item already exists.
+ */
+ private static byte[][] insert(byte[][] existing, byte[] toAdd) {
+ // look for the right spot where to insert the new guy
+ int index = search(existing, toAdd);
+ if (index >= 0)
+ // already there - nothing else to be done
+ return null;
+ // not found - insert
+ int insertPosition = -index - 1;
+ byte[][] newValue = new byte[existing.length + 1][];
+ if (insertPosition > 0)
+ System.arraycopy(existing, 0, newValue, 0, insertPosition);
+ newValue[insertPosition] = toAdd;
+ if (insertPosition < existing.length)
+ System.arraycopy(existing, insertPosition, newValue, insertPosition + 1, existing.length - insertPosition);
+ return newValue;
+ }
+
+ /**
+ * Merges two entries (are always sorted). Duplicates are discarded.
+ */
+ private static byte[][] merge(byte[][] base, byte[][] additions) {
+ int additionPointer = 0;
+ int basePointer = 0;
+ int added = 0;
+ byte[][] result = new byte[base.length + additions.length][];
+ while (basePointer < base.length && additionPointer < additions.length) {
+ int comparison = compareStates(base[basePointer], additions[additionPointer]);
+ if (comparison == 0) {
+ result[added++] = base[basePointer++];
+ // duplicate, ignore
+ additionPointer++;
+ } else if (comparison < 0)
+ result[added++] = base[basePointer++];
+ else
+ result[added++] = additions[additionPointer++];
+ }
+ // copy the remaining states from either additions or base arrays
+ byte[][] remaining = basePointer == base.length ? additions : base;
+ int remainingPointer = basePointer == base.length ? additionPointer : basePointer;
+ int remainingCount = remaining.length - remainingPointer;
+ System.arraycopy(remaining, remainingPointer, result, added, remainingCount);
+ added += remainingCount;
+ if (added == base.length + additions.length)
+ // no collisions
+ return result;
+ // there were collisions, need to compact
+ byte[][] finalResult = new byte[added][];
+ System.arraycopy(result, 0, finalResult, 0, finalResult.length);
+ return finalResult;
+ }
+
+ private static int search(byte[][] existing, byte[] element) {
+ return Arrays.binarySearch(existing, element, COMPARATOR);
+ }
+
+ public HistoryEntry(IPath path, byte[][] data) {
+ super(path);
+ this.data = data;
+ }
+
+ public HistoryEntry(IPath path, HistoryEntry base) {
+ super(path);
+ this.data = new byte[base.data.length][];
+ System.arraycopy(base.data, 0, this.data, 0, this.data.length);
+ }
+
+ /**
+ * Compacts the data array removing any null slots. If non-null slots
+ * are found, the entry is marked for removal.
+ */
+ private void compact() {
+ if (!isDirty())
+ return;
+ int occurrences = 0;
+ for (int i = 0; i < data.length; i++)
+ if (data[i] != null)
+ data[occurrences++] = data[i];
+ if (occurrences == data.length)
+ // no states deleted
+ return;
+ if (occurrences == 0) {
+ // no states remaining
+ data = EMPTY_DATA;
+ delete();
+ return;
+ }
+ byte[][] result = new byte[occurrences][];
+ System.arraycopy(data, 0, result, 0, occurrences);
+ data = result;
+ }
+
+ public void deleteOccurrence(int i) {
+ markDirty();
+ data[i] = null;
+ }
+
+ byte[][] getData() {
+ return data;
+ }
+
+ public int getOccurrences() {
+ return data.length;
+ }
+
+ public long getTimestamp(int i) {
+ return getTimestamp(data[i]);
+ }
+
+ public UniversalUniqueIdentifier getUUID(int i) {
+ return new UniversalUniqueIdentifier(data[i]);
+ }
+
+ public Object getValue() {
+ return data;
+ }
+
+ public boolean isEmpty() {
+ return data.length == 0;
+ }
+
+ public void visited() {
+ compact();
+ }
+
+ }
+
+ /**
+ * Version number for the current implementation file's format.
+ * <p>
+ * Version 2 (3.1 M5):
+ * <pre>
+ * FILE ::= VERSION_ID ENTRY+
+ * ENTRY ::= PATH STATE_COUNT STATE+
+ * PATH ::= string (does not include project name)
+ * STATE_COUNT ::= int
+ * STATE ::= UUID LAST_MODIFIED
+ * UUID ::= byte[16]
+ * LAST_MODIFIED ::= byte[8]
+ * </pre>
+ * </p>
+ * <p>
+ * Version 1 (3.1 M4):
+ * <pre>
+ * FILE ::= VERSION_ID ENTRY+
+ * ENTRY ::= PATH STATE_COUNT STATE+
+ * PATH ::= string
+ * STATE_COUNT ::= int
+ * STATE ::= UUID LAST_MODIFIED
+ * UUID ::= byte[16]
+ * LAST_MODIFIED ::= byte[8]
+ * </pre>
+ * </p>
+ */
+ public final static byte VERSION = 2;
+
+ public HistoryBucket() {
+ super();
+ }
+
+ public void addBlob(IPath path, UniversalUniqueIdentifier uuid, long lastModified) {
+ byte[] state = HistoryEntry.getState(uuid, lastModified);
+ String pathAsString = path.toString();
+ byte[][] existing = (byte[][]) getEntryValue(pathAsString);
+ if (existing == null) {
+ setEntryValue(pathAsString, new byte[][] {state});
+ return;
+ }
+ byte[][] newValue = HistoryEntry.insert(existing, state);
+ if (newValue == null)
+ return;
+ setEntryValue(pathAsString, newValue);
+ }
+
+ public void addBlobs(HistoryEntry fileEntry) {
+ IPath path = fileEntry.getPath();
+ byte[][] additions = fileEntry.getData();
+ String pathAsString = path.toString();
+ byte[][] existing = (byte[][]) getEntryValue(pathAsString);
+ if (existing == null) {
+ setEntryValue(pathAsString, additions);
+ return;
+ }
+ setEntryValue(pathAsString, HistoryEntry.merge(existing, additions));
+ }
+
+ protected Bucket.Entry createEntry(IPath path, Object value) {
+ return new HistoryEntry(path, (byte[][]) value);
+ }
+
+ public HistoryEntry getEntry(IPath path) {
+ String pathAsString = path.toString();
+ byte[][] existing = (byte[][]) getEntryValue(pathAsString);
+ if (existing == null)
+ return null;
+ return new HistoryEntry(path, existing);
+ }
+
+ protected String getIndexFileName() {
+ return "history.index"; //$NON-NLS-1$
+ }
+
+ protected byte getVersion() {
+ return VERSION;
+ }
+
+ protected String getVersionFileName() {
+ return "history.version"; //$NON-NLS-1$
+ }
+
+ protected Object readEntryValue(DataInputStream source) throws IOException {
+ int length = source.readUnsignedShort();
+ byte[][] uuids = new byte[length][HistoryEntry.DATA_LENGTH];
+ for (int j = 0; j < uuids.length; j++)
+ source.read(uuids[j]);
+ return uuids;
+ }
+
+ protected void writeEntryValue(DataOutputStream destination, Object entryValue) throws IOException {
+ byte[][] uuids = (byte[][]) entryValue;
+ destination.writeShort(uuids.length);
+ for (int j = 0; j < uuids.length; j++)
+ destination.write(uuids[j]);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/HistoryStore2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,366 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.InputStream;
+import java.util.*;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.internal.localstore.Bucket.Entry;
+import org.eclipse.core.internal.localstore.HistoryBucket.HistoryEntry;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+public class HistoryStore2 implements IHistoryStore {
+
+ class HistoryCopyVisitor extends Bucket.Visitor {
+ private List changes = new ArrayList();
+ private IPath destination;
+ private IPath source;
+
+ public HistoryCopyVisitor(IPath source, IPath destination) {
+ this.source = source;
+ this.destination = destination;
+ }
+
+ public void afterSaving(Bucket bucket) throws CoreException {
+ saveChanges();
+ changes.clear();
+ }
+
+ private void saveChanges() throws CoreException {
+ if (changes.isEmpty())
+ return;
+ // make effective all changes collected
+ Iterator i = changes.iterator();
+ HistoryEntry entry = (HistoryEntry) i.next();
+ tree.loadBucketFor(entry.getPath());
+ HistoryBucket bucket = (HistoryBucket) tree.getCurrent();
+ bucket.addBlobs(entry);
+ while (i.hasNext())
+ bucket.addBlobs((HistoryEntry) i.next());
+ bucket.save();
+ }
+
+ public int visit(Entry sourceEntry) {
+ IPath destinationPath = destination.append(sourceEntry.getPath().removeFirstSegments(source.segmentCount()));
+ HistoryEntry destinationEntry = new HistoryEntry(destinationPath, (HistoryEntry) sourceEntry);
+ // we may be copying to the same source bucket, collect to make change effective later
+ // since we cannot make changes to it while iterating
+ changes.add(destinationEntry);
+ return CONTINUE;
+ }
+ }
+
+ private BlobStore blobStore;
+ private Set blobsToRemove = new HashSet();
+ final BucketTree tree;
+ private Workspace workspace;
+
+ public HistoryStore2(Workspace workspace, IFileStore store, int limit) {
+ this.workspace = workspace;
+ try {
+ store.mkdir(EFS.NONE, null);
+ } catch (CoreException e) {
+ //ignore the failure here because there is no way to surface it.
+ //any attempt to write to the store will throw an appropriate exception
+ }
+ this.blobStore = new BlobStore(store, limit);
+ this.tree = new BucketTree(workspace, new HistoryBucket());
+ }
+
+ /**
+ * @see IHistoryStore#addState(IPath, IFileStore, IFileInfo, boolean)
+ */
+ public synchronized IFileState addState(IPath key, IFileStore localFile, IFileInfo info, boolean moveContents) {
+ long lastModified = info.getLastModified();
+ if (Policy.DEBUG_HISTORY)
+ System.out.println("History: Adding state for key: " + key + ", file: " + localFile + ", timestamp: " + lastModified + ", size: " + localFile.fetchInfo().getLength()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ if (!isValid(localFile, info))
+ return null;
+ UniversalUniqueIdentifier uuid = null;
+ try {
+ uuid = blobStore.addBlob(localFile, moveContents);
+ tree.loadBucketFor(key);
+ HistoryBucket currentBucket = (HistoryBucket) tree.getCurrent();
+ currentBucket.addBlob(key, uuid, lastModified);
+ // currentBucket.save();
+ } catch (CoreException e) {
+ log(e);
+ }
+ return new FileState(this, key, lastModified, uuid);
+ }
+
+ public synchronized Set allFiles(IPath root, int depth, IProgressMonitor monitor) {
+ final Set allFiles = new HashSet();
+ try {
+ tree.accept(new Bucket.Visitor() {
+ public int visit(Entry fileEntry) {
+ allFiles.add(fileEntry.getPath());
+ return CONTINUE;
+ }
+ }, root, depth == IResource.DEPTH_INFINITE ? BucketTree.DEPTH_INFINITE : depth);
+ } catch (CoreException e) {
+ log(e);
+ }
+ return allFiles;
+ }
+
+ /**
+ * Applies the clean-up policy to an entry.
+ */
+ protected void applyPolicy(HistoryEntry fileEntry, int maxStates, long minTimeStamp) {
+ for (int i = 0; i < fileEntry.getOccurrences(); i++) {
+ if (i < maxStates && fileEntry.getTimestamp(i) >= minTimeStamp)
+ continue;
+ // "delete" the current uuid
+ blobsToRemove.add(fileEntry.getUUID(i));
+ fileEntry.deleteOccurrence(i);
+ }
+ }
+
+ /**
+ * Applies the clean-up policy to a subtree.
+ */
+ private void applyPolicy(IPath root) throws CoreException {
+ IWorkspaceDescription description = workspace.internalGetDescription();
+ final long minimumTimestamp = System.currentTimeMillis() - description.getFileStateLongevity();
+ final int maxStates = description.getMaxFileStates();
+ // apply policy to the given tree
+ tree.accept(new Bucket.Visitor() {
+ public int visit(Entry entry) {
+ applyPolicy((HistoryEntry) entry, maxStates, minimumTimestamp);
+ return CONTINUE;
+ }
+ }, root, BucketTree.DEPTH_INFINITE);
+ tree.getCurrent().save();
+ }
+
+ public synchronized void clean(final IProgressMonitor monitor) {
+ long start = System.currentTimeMillis();
+ try {
+ monitor.beginTask(Messages.resources_pruningHistory, IProgressMonitor.UNKNOWN);
+ IWorkspaceDescription description = workspace.internalGetDescription();
+ final long minimumTimestamp = System.currentTimeMillis() - description.getFileStateLongevity();
+ final int maxStates = description.getMaxFileStates();
+ final int[] entryCount = new int[1];
+ tree.accept(new Bucket.Visitor() {
+ public int visit(Entry fileEntry) {
+ if (monitor.isCanceled())
+ return STOP;
+ entryCount[0] += fileEntry.getOccurrences();
+ applyPolicy((HistoryEntry) fileEntry, maxStates, minimumTimestamp);
+ // remove unreferenced blobs, when blobsToRemove size is greater than 100
+ removeUnreferencedBlobs(100);
+ return monitor.isCanceled() ? STOP : CONTINUE;
+ }
+ }, Path.ROOT, BucketTree.DEPTH_INFINITE);
+ if (Policy.DEBUG_HISTORY) {
+ Policy.debug("Time to apply history store policies: " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$
+ Policy.debug("Total number of history store entries: " + entryCount[0]); //$NON-NLS-1$
+ }
+ // remove all remaining unreferenced blobs
+ removeUnreferencedBlobs(0);
+ } catch (Exception e) {
+ String message = Messages.history_problemsCleaning;
+ ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, null, message, e);
+ Policy.log(status);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /*
+ * Remove blobs from the blobStore. When the size of blobsToRemove exceeds the limit,
+ * remove the given blobs from blobStore. If the limit is zero or negative, remove blobs
+ * regardless of the limit.
+ */
+ void removeUnreferencedBlobs(int limit) {
+ if (limit <= 0 || limit <= blobsToRemove.size()) {
+ long start = System.currentTimeMillis();
+ // remove unreferenced blobs
+ blobStore.deleteBlobs(blobsToRemove);
+ if (Policy.DEBUG_HISTORY)
+ Policy.debug("Time to remove " + blobsToRemove.size() + " unreferenced blobs: " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ blobsToRemove = new HashSet();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.localstore.IHistoryStore#closeHistory(org.eclipse.core.resources.IResource)
+ */
+ public void closeHistoryStore(IResource resource) {
+ try {
+ tree.getCurrent().save();
+ tree.getCurrent().flush();
+ } catch (CoreException e) {
+ log(e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.internal.localstore.IHistoryStore#copyHistory(org.eclipse.core.resources.IResource, org.eclipse.core.resources.IResource, boolean)
+ */
+ public synchronized void copyHistory(IResource sourceResource, IResource destinationResource, boolean moving) {
+ // return early if either of the paths are null or if the source and
+ // destination are the same.
+ if (sourceResource == null || destinationResource == null) {
+ String message = Messages.history_copyToNull;
+ ResourceStatus status = new ResourceStatus(IResourceStatus.INTERNAL_ERROR, null, message, null);
+ Policy.log(status);
+ return;
+ }
+ if (sourceResource.equals(destinationResource)) {
+ String message = Messages.history_copyToSelf;
+ ResourceStatus status = new ResourceStatus(IResourceStatus.INTERNAL_ERROR, sourceResource.getFullPath(), message, null);
+ Policy.log(status);
+ return;
+ }
+
+ final IPath source = sourceResource.getFullPath();
+ final IPath destination = destinationResource.getFullPath();
+ Assert.isLegal(source.segmentCount() > 0);
+ Assert.isLegal(destination.segmentCount() > 0);
+ Assert.isLegal(source.segmentCount() > 1 || destination.segmentCount() == 1);
+
+ try {
+ // special case: we are moving a project
+ if (moving && sourceResource.getType() == IResource.PROJECT) {
+ // flush the tree to avoid confusion if another project is created with the same name
+ final Bucket bucket = tree.getCurrent();
+ bucket.save();
+ bucket.flush();
+ return;
+ }
+ // copy history by visiting the source tree
+ HistoryCopyVisitor copyVisitor = new HistoryCopyVisitor(source, destination);
+ tree.accept(copyVisitor, source, BucketTree.DEPTH_INFINITE);
+ // apply clean-up policy to the destination tree
+ applyPolicy(destinationResource.getFullPath());
+ } catch (CoreException e) {
+ log(e);
+ }
+ }
+
+ public boolean exists(IFileState target) {
+ return blobStore.fileFor(((FileState) target).getUUID()).fetchInfo().exists();
+ }
+
+ public InputStream getContents(IFileState target) throws CoreException {
+ if (!target.exists()) {
+ String message = Messages.history_notValid;
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, target.getFullPath(), message, null);
+ }
+ return blobStore.getBlob(((FileState) target).getUUID());
+ }
+
+ public synchronized IFileState[] getStates(IPath filePath, IProgressMonitor monitor) {
+ try {
+ tree.loadBucketFor(filePath);
+ HistoryBucket currentBucket = (HistoryBucket) tree.getCurrent();
+ HistoryEntry fileEntry = currentBucket.getEntry(filePath);
+ if (fileEntry == null || fileEntry.isEmpty())
+ return new IFileState[0];
+ IFileState[] states = new IFileState[fileEntry.getOccurrences()];
+ for (int i = 0; i < states.length; i++)
+ states[i] = new FileState(this, fileEntry.getPath(), fileEntry.getTimestamp(i), fileEntry.getUUID(i));
+ return states;
+ } catch (CoreException ce) {
+ log(ce);
+ return new IFileState[0];
+ }
+ }
+
+ public BucketTree getTree() {
+ return tree;
+ }
+
+ /**
+ * Return a boolean value indicating whether or not the given file
+ * should be added to the history store based on the current history
+ * store policies.
+ *
+ * @param localFile the file to check
+ * @return <code>true</code> if this file should be added to the history
+ * store and <code>false</code> otherwise
+ */
+ private boolean isValid(IFileStore localFile, IFileInfo info) {
+ WorkspaceDescription description = workspace.internalGetDescription();
+ long length = info.getLength();
+ boolean result = length <= description.getMaxFileStateSize();
+ if (Policy.DEBUG_HISTORY && !result)
+ System.out.println("History: Ignoring file (too large). File: " + localFile.toString() + //$NON-NLS-1$
+ ", size: " + length + //$NON-NLS-1$
+ ", max: " + description.getMaxFileStateSize()); //$NON-NLS-1$
+ return result;
+ }
+
+ /**
+ * Logs a CoreException
+ */
+ private void log(CoreException e) {
+ //create a new status to wrap the exception if there is no exception in the status
+ IStatus status = e.getStatus();
+ if (status.getException() == null)
+ status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_WRITE_METADATA, "Internal error in history store", e); //$NON-NLS-1$
+ Policy.log(status);
+ }
+
+ public synchronized void remove(IPath root, IProgressMonitor monitor) {
+ try {
+ final Set tmpBlobsToRemove = blobsToRemove;
+ tree.accept(new Bucket.Visitor() {
+ public int visit(Entry fileEntry) {
+ for (int i = 0; i < fileEntry.getOccurrences(); i++)
+ // remember we need to delete the files later
+ tmpBlobsToRemove.add(((HistoryEntry) fileEntry).getUUID(i));
+ fileEntry.delete();
+ return CONTINUE;
+ }
+ }, root, BucketTree.DEPTH_INFINITE);
+ } catch (CoreException ce) {
+ log(ce);
+ }
+ }
+
+ /**
+ * @see IHistoryStore#removeGarbage()
+ */
+ public synchronized void removeGarbage() {
+ try {
+ final Set tmpBlobsToRemove = blobsToRemove;
+ tree.accept(new Bucket.Visitor() {
+ public int visit(Entry fileEntry) {
+ for (int i = 0; i < fileEntry.getOccurrences(); i++)
+ // remember we need to delete the files later
+ tmpBlobsToRemove.remove(((HistoryEntry) fileEntry).getUUID(i));
+ return CONTINUE;
+ }
+ }, Path.ROOT, BucketTree.DEPTH_INFINITE);
+ blobStore.deleteBlobs(blobsToRemove);
+ blobsToRemove = new HashSet();
+ } catch (Exception e) {
+ String message = Messages.history_problemsCleaning;
+ ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, null, message, e);
+ Policy.log(status);
+ }
+ }
+
+ public synchronized void shutdown(IProgressMonitor monitor) throws CoreException {
+ tree.close();
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ // nothing to be done
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/IHistoryStore.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.InputStream;
+import java.util.Set;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.resources.IManager;
+import org.eclipse.core.resources.IFileState;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.*;
+
+/**
+ * The history store is an association of paths to file states.
+ * Typically the path is the full path of a resource in the workspace.
+ * <p>
+ * History store policies are stored in the org.eclipse.core.resources'
+ * plug-in preferences.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IHistoryStore extends IManager {
+
+ /**
+ * Add an entry to the history store, represented by the given key. Return the
+ * file state for the newly created entry or<code>null</code> if it couldn't
+ * be created.
+ * <p>
+ * Note: Depending on the history store implementation, some of the history
+ * store policies can be applied during this method call to determine whether
+ * or not the entry should be added to the store.
+ * </p>
+ * @param key full workspace path to resource being logged
+ * @param localFile local file system file handle
+ * @param fileInfo The IFileInfo for the entry
+ * @return the file state or <code>null</code>
+ *
+ * TODO: should this method take a progress monitor?
+ *
+ * TODO: look at #getFileFor(). Is there a case where we wouldn't want to
+ * copy over the file attributes to the local history? If we did that here then
+ * we wouldn't have to have that other API.
+ */
+ public IFileState addState(IPath key, IFileStore localFile, IFileInfo fileInfo, boolean moveContents);
+
+ /**
+ * Returns the paths of all files with entries in this history store at or below
+ * the given workspace resource path to the given depth. Returns an
+ * empty set if there are none.
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * @param path full workspace path to resource
+ * @param depth depth limit: one of <code>DEPTH_ZERO</code>, <code>DEPTH_ONE</code>
+ * or <code>DEPTH_INFINITE</code>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the set of paths for files that have at least one history entry
+ * (element type: <code>IPath</code>)
+ */
+ public Set allFiles(IPath path, int depth, IProgressMonitor monitor);
+
+ /**
+ * Clean this store applying the current policies.
+ * <p>
+ * Note: The history store policies are stored as part of
+ * the org.eclipse.core.resource plug-in's preferences and
+ * include such settings as: maximum file size, maximum number
+ * of states per file, file expiration date, etc.
+ * </p>
+ * <p>
+ * Note: Depending on the implementation of the history store,
+ * if all the history store policies are applying when the entries
+ * are first added to the store then this method might be a no-op.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ */
+ public void clean(IProgressMonitor monitor);
+
+ /**
+ * Closes the history store for the given resource.
+ */
+ public void closeHistoryStore(IResource resource);
+
+ /**
+ * Copies the history store information from the source path given destination path.
+ * Note that destination may already have some history store information. Also note
+ * that this is a DEPTH_INFINITY operation. That is, history will be copied for partial
+ * matches of the source path.
+ *
+ * @param source the resource containing the original copy of the history store information
+ * @param destination the target resource where to copy the history
+ * @param moving whether the history is being copied due to a resource move
+ *
+ * TODO: should this method take a progress monitor?
+ */
+ public void copyHistory(IResource source, IResource destination, boolean moving);
+
+ /**
+ * Verifies existence of specified resource in the history store. Returns
+ * <code>true</code> if the file state exists and <code>false</code>
+ * otherwise.
+ * <p>
+ * Note: This method cannot take a progress monitor since it is surfaced
+ * to the real API via IFileState#exists() which doesn't take a progress
+ * monitor.
+ * </p>
+ * @param target the file state to be verified
+ * @return <code>true</code> if file state exists,
+ * and <code>false</code> otherwise
+ */
+ public boolean exists(IFileState target);
+
+ /**
+ * Returns an input stream containing the file contents of the specified state.
+ * The user is responsible for closing the returned stream.
+ * <p>
+ * Note: This method cannot take a progress monitor since it is
+ * surfaced through to the real API via IFileState#getContents which
+ * doesn't take one.
+ * </p>
+ * @param target File state for which an input stream is requested
+ * @return the stream for requested file state
+ */
+ public InputStream getContents(IFileState target) throws CoreException;
+
+ /**
+ * Returns an array of all states available for the specified resource path or
+ * an empty array if none.
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * @param path the resource path
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the list of file states
+ */
+ public IFileState[] getStates(IPath path, IProgressMonitor monitor);
+
+ /**
+ * Remove all of the file states for the given resource path and
+ * all its children. If the workspace root path is the given argument,
+ * then all history for this store is removed.
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * @param path the resource path whose history is to be removed
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ */
+ public void remove(IPath path, IProgressMonitor monitor);
+
+ /**
+ * Go through the history store and remove all of the unreferenced states.
+ *
+ * As of 3.0, this method is used for testing purposes only. Otherwise the history
+ * store is garbage collected during the #clean method.
+ */
+ public void removeGarbage();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/ILocalStoreConstants.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+public interface ILocalStoreConstants {
+
+ /** Common constants for History Store classes. */
+ public final static int SIZE_LASTMODIFIED = 8;
+ public static final int SIZE_COUNTER = 1;
+ public static final int SIZE_KEY_SUFFIX = SIZE_LASTMODIFIED + SIZE_COUNTER;
+
+ /** constants for safe chunky streams */
+
+ // 40b18b8123bc00141a2596e7a393be1e
+ public static final byte[] BEGIN_CHUNK = {64, -79, -117, -127, 35, -68, 0, 20, 26, 37, -106, -25, -93, -109, -66, 30};
+
+ // c058fbf323bc00141a51f38c7bbb77c6
+ public static final byte[] END_CHUNK = {-64, 88, -5, -13, 35, -68, 0, 20, 26, 81, -13, -116, 123, -69, 119, -58};
+
+ /** chunk delimiter size */
+ // BEGIN_CHUNK and END_CHUNK must have the same length
+ public static final int CHUNK_DELIMITER_SIZE = BEGIN_CHUNK.length;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/IUnifiedTreeVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import org.eclipse.core.runtime.CoreException;
+
+public interface IUnifiedTreeVisitor {
+ /**
+ * Returns true to visit the members of this node and false otherwise.
+ */
+ public boolean visit(UnifiedTreeNode node) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/IsSynchronizedVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import org.eclipse.core.internal.resources.Resource;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Visits a unified tree, and throws a ResourceChangedException on the first
+ * node that is discovered to be out of sync. The exception that is thrown
+ * will not have any meaningful status, message, or stack trace. Nodes
+ * discovered to be out of sync are not brought to be in sync with the workspace.
+ */
+public class IsSynchronizedVisitor extends CollectSyncStatusVisitor {
+ static class ResourceChangedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+ }
+
+ protected static ResourceChangedException exception = new ResourceChangedException();
+
+ /**
+ * Creates a new IsSynchronizedVisitor.
+ */
+ public IsSynchronizedVisitor(IProgressMonitor monitor) {
+ super("", monitor); //$NON-NLS-1$
+ }
+
+ /**
+ * @see CollectSyncStatusVisitor#changed(Resource)
+ */
+ protected void changed(Resource target) {
+ throw exception;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/PrefixPool.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Martin Oberhuber (Wind River) - initial API and implementation for [105554]
+ *******************************************************************************/
+
+package org.eclipse.core.internal.localstore;
+
+import java.util.Arrays;
+
+/**
+ * A pool of Strings for doing prefix checks against multiple
+ * candidates.
+ * <p>
+ * Allows to enter a list of Strings, and then perform the
+ * following checks:
+ * <ul>
+ * <li>{@link #containsAsPrefix(String)} - check whether a given
+ * String s is a prefix of any String in the pool.</li>
+ * <li>{@link #hasPrefixOf(String)} - check whether any String
+ * in the pool is a prefix of the given String s.
+ * </ul>
+ * The prefix pool is always kept normalized, i.e. no element of
+ * the pool is a prefix of any other element in the pool. In order
+ * to maintain this constraint, there are two methods for adding
+ * Strings to the pool:
+ * <ul>
+ * <li>{@link #insertLonger(String)} - add a String s to the pool,
+ * and remove any existing prefix of s from the pool.</li>
+ * <li>{@link #insertShorter(String)} - add a String s to the pool,
+ * and remove any existing Strings sx from the pool which
+ * contain s as prefix.</li>
+ * </ul>
+ * The PrefixPool grows as needed when adding Strings. Typically,
+ * it is used for prefix checks on absolute paths of a tree.
+ * </p><p>
+ * This class is not thread-safe: no two threads may add or
+ * check items at the same time.
+ *
+ * @since 3.3
+ */
+public class PrefixPool {
+ private String[] pool;
+ private int size;
+
+ /**
+ * Constructor.
+ * @param initialCapacity the initial size of the
+ * internal array holding the String pool. Must
+ * be greater than 0.
+ */
+ public PrefixPool(int initialCapacity) {
+ if (initialCapacity <= 0)
+ throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); //$NON-NLS-1$
+ pool = new String[initialCapacity];
+ size = 0;
+ }
+
+ /**
+ * Clears the prefix pool, allowing all items to be
+ * garbage collected. Does not change the capacity
+ * of the pool.
+ */
+ public/*synchronized*/void clear() {
+ Arrays.fill(pool, 0, size, null);
+ size = 0;
+ }
+
+ /**
+ * Return the current size of prefix pool.
+ * @return the number of elements in the pool.
+ */
+ public/*synchronized*/int size() {
+ return size;
+ }
+
+ /**
+ * Ensure that there is room for at least one more element.
+ */
+ private void checkCapacity() {
+ if (size + 1 >= pool.length) {
+ String[] newprefixList = new String[2 * pool.length];
+ System.arraycopy(pool, 0, newprefixList, 0, pool.length);
+ Arrays.fill(pool, null); //help the garbage collector
+ pool = newprefixList;
+ }
+ }
+
+ /**
+ * Insert a String s into the pool of known prefixes, removing
+ * any existing prefix of it.
+ * <p>
+ * If any existing prefix of this String is found in the pool,
+ * it is replaced by the new longer one in order to maintain
+ * the constraint of keeping the pool normalized.
+ * </p><p>
+ * If it turns out that s is actually a prefix or equal to
+ * an existing element in the pool (so it is essentially
+ * shorter), this method returns with no operation in order
+ * to maintain the constraint that the pool remains normalized.
+ * </p>
+ * @param s the String to insert.
+ */
+ public/*synchronized*/void insertLonger(String s) {
+ //check in reverse order since we expect some locality
+ for (int i = size - 1; i >= 0; i--) {
+ if (pool[i].startsWith(s)) {
+ //prefix of an existing String --> no-op
+ return;
+ } else if (s.startsWith(pool[i])) {
+ //replace, since a longer s has more prefixes than a short one
+ pool[i] = s;
+ return;
+ }
+ }
+ checkCapacity();
+ pool[size] = s;
+ size++;
+ }
+
+ /**
+ * Insert a String s into the pool of known prefixes, removing
+ * any Strings that have s as prefix.
+ * <p>
+ * If this String is a prefix of any existing String in the pool,
+ * all elements that contain the new String as prefix are removed
+ * and return value <code>true</code> is returned.
+ * </p><p>
+ * Otherwise, the new String is added to the pool unless an
+ * equal String or e prefix of it exists there already (so
+ * it is essentially equal or longer than an existing prefix).
+ * In all these cases, <code>false</code> is returned since
+ * no prefixes were replaced.
+ * </p>
+ * @param s the String to insert.
+ * @return <code>true</code>if any longer elements have been
+ * removed.
+ */
+ public/*synchronized*/boolean insertShorter(String s) {
+ boolean replaced = false;
+ //check in reverse order since we expect some locality
+ for (int i = size - 1; i >= 0; i--) {
+ if (s.startsWith(pool[i])) {
+ //longer or equal to an existing prefix - nothing to do
+ return false;
+ } else if (pool[i].startsWith(s)) {
+ if (replaced) {
+ //replaced before, so shrink the array.
+ //Safe since we are iterating in reverse order.
+ System.arraycopy(pool, i + 1, pool, i, size - i - 1);
+ size--;
+ pool[size] = null;
+ } else {
+ //replace, since this is a shorter s
+ pool[i] = s;
+ replaced = true;
+ }
+ }
+ }
+ if (!replaced) {
+ //append at the end
+ checkCapacity();
+ pool[size] = s;
+ size++;
+ }
+ return replaced;
+ }
+
+ /**
+ * Check if the given String s is a prefix of any of Strings
+ * in the pool.
+ * @param s a s to check for being a prefix
+ * @return <code>true</code> if the passed s is a prefix
+ * of any of the Strings contained in the pool.
+ */
+ public/*synchronized*/boolean containsAsPrefix(String s) {
+ //check in reverse order since we expect some locality
+ for (int i = size - 1; i >= 0; i--) {
+ if (pool[i].startsWith(s)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test if the String pool contains any one that is a prefix
+ * of the given String s.
+ * @param s the String to test
+ * @return <code>true</code> if the String pool contains a
+ * prefix of the given String.
+ */
+ public/*synchronized*/boolean hasPrefixOf(String s) {
+ for (int i = size - 1; i >= 0; i--) {
+ if (s.startsWith(pool[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/RefreshLocalAliasVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.resources.Container;
+import org.eclipse.core.internal.resources.Resource;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Performs a local refresh, and additionally updates all aliases of the
+ * refreshed resource.
+ */
+public class RefreshLocalAliasVisitor extends RefreshLocalVisitor {
+ public RefreshLocalAliasVisitor(IProgressMonitor monitor) {
+ super(monitor);
+ }
+
+ protected void createResource(UnifiedTreeNode node, Resource target) throws CoreException {
+ super.createResource(node, target);
+ IFileStore store = node.getStore();
+ if (store == null)
+ return;
+ IResource[] aliases = workspace.getAliasManager().computeAliases(target, store);
+ if (aliases != null)
+ for (int i = 0; i < aliases.length; i++)
+ super.createResource(node, (Resource) aliases[i]);
+ }
+
+ protected void deleteResource(UnifiedTreeNode node, Resource target) throws CoreException {
+ super.deleteResource(node, target);
+ IFileStore store = node.getStore();
+ if (store == null)
+ return;
+ IResource[] aliases = workspace.getAliasManager().computeAliases(target, store);
+ if (aliases != null)
+ for (int i = 0; i < aliases.length; i++)
+ super.deleteResource(node, (Resource) aliases[i]);
+ }
+
+ protected void resourceChanged(UnifiedTreeNode node, Resource target) {
+ super.resourceChanged(node, target);
+ IFileStore store = node.getStore();
+ if (store == null)
+ return;
+ IResource[] aliases = workspace.getAliasManager().computeAliases(target, store);
+ if (aliases != null)
+ for (int i = 0; i < aliases.length; i++)
+ super.resourceChanged(node, (Resource) aliases[i]);
+ }
+
+ protected void fileToFolder(UnifiedTreeNode node, Resource target) throws CoreException {
+ super.fileToFolder(node, target);
+ IFileStore store = node.getStore();
+ if (store == null)
+ return;
+ IResource[] aliases = workspace.getAliasManager().computeAliases(target, store);
+ if (aliases != null)
+ for (int i = 0; i < aliases.length; i++)
+ super.fileToFolder(node, (Resource) aliases[i]);
+ }
+
+ protected void folderToFile(UnifiedTreeNode node, Resource target) throws CoreException {
+ super.folderToFile(node, target);
+ IFileStore store = node.getStore();
+ if (store == null)
+ return;
+ IResource[] aliases = workspace.getAliasManager().computeAliases(target, store);
+ if (aliases != null)
+ for (int i = 0; i < aliases.length; i++)
+ super.folderToFile(node, (Resource) aliases[i]);
+ }
+
+ protected void refresh(Container parent) throws CoreException {
+ parent.getLocalManager().refresh(parent, IResource.DEPTH_ZERO, true, null);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/RefreshLocalVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,328 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+//
+/**
+ * Visits a unified tree, and synchronizes the file system with the
+ * resource tree. After the visit is complete, the file system will
+ * be synchronized with the workspace tree with respect to
+ * resource existence, gender, and timestamp.
+ */
+public class RefreshLocalVisitor implements IUnifiedTreeVisitor, ILocalStoreConstants {
+ /** control constants */
+ protected static final int RL_UNKNOWN = 0;
+ protected static final int RL_IN_SYNC = 1;
+ protected static final int RL_NOT_IN_SYNC = 2;
+
+ /*
+ * Fields for progress monitoring algorithm.
+ * Initially, give progress for every 4 resources, double
+ * this value at halfway point, then reset halfway point
+ * to be half of remaining work. (this gives an infinite
+ * series that converges at total work after an infinite
+ * number of resources).
+ */
+ public static final int TOTAL_WORK = 250;
+ private int currentIncrement = 4;
+ private int halfWay = TOTAL_WORK / 2;
+ private int nextProgress = currentIncrement;
+ private int worked = 0;
+
+ protected MultiStatus errors;
+ protected IProgressMonitor monitor;
+ protected boolean resourceChanged;
+ protected Workspace workspace;
+
+ public RefreshLocalVisitor(IProgressMonitor monitor) {
+ this.monitor = monitor;
+ workspace = (Workspace) ResourcesPlugin.getWorkspace();
+ resourceChanged = false;
+ String msg = Messages.resources_errorMultiRefresh;
+ errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_LOCAL, msg, null);
+ }
+
+ /**
+ * This method has the same implementation as resourceChanged but as they are different
+ * cases, we prefer to use different methods.
+ */
+ protected void contentAdded(UnifiedTreeNode node, Resource target) {
+ resourceChanged(node, target);
+ }
+
+ protected void createResource(UnifiedTreeNode node, Resource target) throws CoreException {
+ ResourceInfo info = target.getResourceInfo(false, false);
+ int flags = target.getFlags(info);
+ if (target.exists(flags, false))
+ return;
+ /* make sure target's parent exists */
+ if (node.getLevel() == 0) {
+ IContainer parent = target.getParent();
+ if (parent.getType() == IResource.FOLDER)
+ ((Folder) target.getParent()).ensureExists(monitor);
+ }
+ /* Use the basic file creation protocol since we don't want to create any content on disk. */
+ info = workspace.createResource(target, false);
+ /* Mark this resource as having unknown children */
+ info.set(ICoreConstants.M_CHILDREN_UNKNOWN);
+ target.getLocalManager().updateLocalSync(info, node.getLastModified());
+ }
+
+ protected void deleteResource(UnifiedTreeNode node, Resource target) throws CoreException {
+ ResourceInfo info = target.getResourceInfo(false, false);
+ int flags = target.getFlags(info);
+ //don't delete linked resources
+ if (ResourceInfo.isSet(flags, ICoreConstants.M_LINK)) {
+ //just clear local sync info
+ info = target.getResourceInfo(false, true);
+ //handle concurrent deletion
+ if (info != null)
+ info.clearModificationStamp();
+ return;
+ }
+ if (target.exists(flags, false))
+ target.deleteResource(true, errors);
+ node.setExistsWorkspace(false);
+ }
+
+ protected void fileToFolder(UnifiedTreeNode node, Resource target) throws CoreException {
+ ResourceInfo info = target.getResourceInfo(false, false);
+ int flags = target.getFlags(info);
+ if (target.exists(flags, true)) {
+ target = (Folder) ((File) target).changeToFolder();
+ } else {
+ if (!target.exists(flags, false)) {
+ target = (Resource) workspace.getRoot().getFolder(target.getFullPath());
+ // Use the basic file creation protocol since we don't want to create any content on disk.
+ workspace.createResource(target, false);
+ }
+ }
+ node.setResource(target);
+ info = target.getResourceInfo(false, true);
+ target.getLocalManager().updateLocalSync(info, node.getLastModified());
+ }
+
+ protected void folderToFile(UnifiedTreeNode node, Resource target) throws CoreException {
+ ResourceInfo info = target.getResourceInfo(false, false);
+ int flags = target.getFlags(info);
+ if (target.exists(flags, true))
+ target = (File) ((Folder) target).changeToFile();
+ else {
+ if (!target.exists(flags, false)) {
+ target = (Resource) workspace.getRoot().getFile(target.getFullPath());
+ // Use the basic file creation protocol since we don't want to
+ // create any content on disk.
+ workspace.createResource(target, false);
+ }
+ }
+ node.setResource(target);
+ info = target.getResourceInfo(false, true);
+ target.getLocalManager().updateLocalSync(info, node.getLastModified());
+ }
+
+ /**
+ * Returns the status of the nodes visited so far. This will be a multi-status
+ * that describes all problems that have occurred, or an OK status if everything
+ * went smoothly.
+ */
+ public IStatus getErrorStatus() {
+ return errors;
+ }
+
+ protected void makeLocal(UnifiedTreeNode node, Resource target) {
+ ResourceInfo info = target.getResourceInfo(false, true);
+ if (info != null)
+ target.getLocalManager().updateLocalSync(info, node.getLastModified());
+ }
+
+ /**
+ * Refreshes the parent of a resource currently being synchronized.
+ */
+ protected void refresh(Container parent) throws CoreException {
+ parent.getLocalManager().refresh(parent, IResource.DEPTH_ZERO, false, null);
+ }
+
+ protected void resourceChanged(UnifiedTreeNode node, Resource target) {
+ ResourceInfo info = target.getResourceInfo(false, true);
+ if (info == null)
+ return;
+ target.getLocalManager().updateLocalSync(info, node.getLastModified());
+ info.incrementContentId();
+ // forget content-related caching flags
+ info.clear(ICoreConstants.M_CONTENT_CACHE);
+ workspace.updateModificationStamp(info);
+ }
+
+ public boolean resourcesChanged() {
+ return resourceChanged;
+ }
+
+ /**
+ * deletion or creation -- Returns:
+ * - RL_IN_SYNC - the resource is in-sync with the file system
+ * - RL_NOT_IN_SYNC - the resource is not in-sync with file system
+ * - RL_UNKNOWN - couldn't determine the sync status for this resource
+ */
+ protected int synchronizeExistence(UnifiedTreeNode node, Resource target) throws CoreException {
+ if (node.existsInWorkspace()) {
+ if (!node.existsInFileSystem()) {
+ //non-local files are always in sync
+ if (target.isLocal(IResource.DEPTH_ZERO)) {
+ deleteResource(node, target);
+ resourceChanged = true;
+ return RL_NOT_IN_SYNC;
+ }
+ return RL_IN_SYNC;
+ }
+ } else {
+ // do we have a gender variant in the workspace?
+ IResource genderVariant = workspace.getRoot().findMember(target.getFullPath());
+ if (genderVariant != null)
+ return RL_UNKNOWN;
+ if (node.existsInFileSystem()) {
+ Container parent = (Container) target.getParent();
+ if (!parent.exists()) {
+ refresh(parent);
+ if (!parent.exists())
+ return RL_NOT_IN_SYNC;
+ }
+ if (!target.getName().equals(node.getLocalName()))
+ return RL_IN_SYNC;
+ if (!Workspace.caseSensitive && node.getLevel() == 0) {
+ // do we have any alphabetic variants in the workspace?
+ IResource variant = target.findExistingResourceVariant(target.getFullPath());
+ if (variant != null) {
+ deleteResource(node, ((Resource) variant));
+ createResource(node, target);
+ resourceChanged = true;
+ return RL_NOT_IN_SYNC;
+ }
+ }
+ createResource(node, target);
+ resourceChanged = true;
+ return RL_NOT_IN_SYNC;
+ }
+ }
+ return RL_UNKNOWN;
+ }
+
+ /**
+ * gender change -- Returns true if gender was in sync.
+ */
+ protected boolean synchronizeGender(UnifiedTreeNode node, Resource target) throws CoreException {
+ if (!node.existsInWorkspace()) {
+ //may be an existing resource in the workspace of different gender
+ IResource genderVariant = workspace.getRoot().findMember(target.getFullPath());
+ if (genderVariant != null)
+ target = (Resource) genderVariant;
+ }
+ if (target.getType() == IResource.FILE) {
+ if (node.isFolder()) {
+ fileToFolder(node, target);
+ resourceChanged = true;
+ return false;
+ }
+ } else {
+ if (!node.isFolder()) {
+ folderToFile(node, target);
+ resourceChanged = true;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * lastModified
+ */
+ protected void synchronizeLastModified(UnifiedTreeNode node, Resource target) {
+ if (target.isLocal(IResource.DEPTH_ZERO))
+ resourceChanged(node, target);
+ else
+ contentAdded(node, target);
+ resourceChanged = true;
+ }
+
+ public boolean visit(UnifiedTreeNode node) throws CoreException {
+ Policy.checkCanceled(monitor);
+ try {
+ Resource target = (Resource) node.getResource();
+ int targetType = target.getType();
+ if (targetType == IResource.PROJECT)
+ return true;
+ if (node.existsInWorkspace() && node.existsInFileSystem()) {
+ /* for folders we only care about updating local status */
+ if (targetType == IResource.FOLDER && node.isFolder()) {
+ // if not local, mark as local
+ if (!target.isLocal(IResource.DEPTH_ZERO))
+ makeLocal(node, target);
+ ResourceInfo info = target.getResourceInfo(false, false);
+ if (info != null && info.getModificationStamp() != IResource.NULL_STAMP)
+ return true;
+ }
+ /* compare file last modified */
+ if (targetType == IResource.FILE && !node.isFolder()) {
+ ResourceInfo info = target.getResourceInfo(false, false);
+ if (info != null && info.getModificationStamp() != IResource.NULL_STAMP && info.getLocalSyncInfo() == node.getLastModified())
+ return true;
+ }
+ } else {
+ if (node.existsInFileSystem() && !Path.EMPTY.isValidSegment(node.getLocalName())) {
+ String message = NLS.bind(Messages.resources_invalidResourceName, node.getLocalName());
+ errors.merge(new ResourceStatus(IResourceStatus.INVALID_RESOURCE_NAME, message));
+ return false;
+ }
+ int state = synchronizeExistence(node, target);
+ if (state == RL_IN_SYNC || state == RL_NOT_IN_SYNC) {
+ if (targetType == IResource.FILE) {
+ try {
+ ((File) target).updateMetadataFiles();
+ } catch (CoreException e) {
+ errors.merge(e.getStatus());
+ }
+ }
+ return true;
+ }
+ }
+ if (synchronizeGender(node, target))
+ synchronizeLastModified(node, target);
+ if (targetType == IResource.FILE) {
+ try {
+ ((File) target).updateMetadataFiles();
+ } catch (CoreException e) {
+ errors.merge(e.getStatus());
+ }
+ }
+ return true;
+ } finally {
+ if (--nextProgress <= 0) {
+ //we have exhausted the current increment, so report progress
+ monitor.worked(1);
+ worked++;
+ if (worked >= halfWay) {
+ //we have passed the current halfway point, so double the
+ //increment and reset the halfway point.
+ currentIncrement *= 2;
+ halfWay += (TOTAL_WORK - halfWay) / 2;
+ }
+ //reset the progress counter to another full increment
+ nextProgress = currentIncrement;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/SafeChunkyInputStream.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+
+/**
+ * @see SafeChunkyOutputStream
+ */
+
+public class SafeChunkyInputStream extends InputStream {
+ protected static final int BUFFER_SIZE = 8192;
+ protected byte[] buffer;
+ protected int bufferLength = 0;
+ protected byte[] chunk;
+ protected int chunkLength = 0;
+ protected boolean endOfFile = false;
+ protected InputStream input;
+ protected int nextByteInBuffer = 0;
+ protected int nextByteInChunk = 0;
+
+ public SafeChunkyInputStream(File target) throws IOException {
+ this(target, BUFFER_SIZE);
+ }
+
+ public SafeChunkyInputStream(File target, int bufferSize) throws IOException {
+ input = new FileInputStream(target);
+ buffer = new byte[bufferSize];
+ }
+
+ protected void accumulate(byte[] data, int start, int end) {
+ byte[] result = new byte[chunk.length + end - start];
+ System.arraycopy(chunk, 0, result, 0, chunk.length);
+ System.arraycopy(data, start, result, chunk.length, end - start);
+ chunk = result;
+ chunkLength = chunkLength + end - start;
+ }
+
+ public int available() throws IOException {
+ return chunkLength - nextByteInChunk;
+ }
+
+ protected void buildChunk() throws IOException {
+ //read buffer loads of data until an entire chunk is accumulated
+ while (true) {
+ if (nextByteInBuffer + ILocalStoreConstants.CHUNK_DELIMITER_SIZE > bufferLength)
+ shiftAndFillBuffer();
+ int end = find(ILocalStoreConstants.END_CHUNK, nextByteInBuffer, bufferLength, true);
+ if (end != -1) {
+ accumulate(buffer, nextByteInBuffer, end);
+ nextByteInBuffer = end + ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ return;
+ }
+ accumulate(buffer, nextByteInBuffer, bufferLength);
+ bufferLength = input.read(buffer);
+ nextByteInBuffer = 0;
+ if (bufferLength == -1) {
+ endOfFile = true;
+ return;
+ }
+ }
+ }
+
+ public void close() throws IOException {
+ input.close();
+ }
+
+ protected boolean compare(byte[] source, byte[] target, int startIndex) {
+ for (int i = 0; i < target.length; i++) {
+ if (source[startIndex] != target[i])
+ return false;
+ startIndex++;
+ }
+ return true;
+ }
+
+ protected int find(byte[] pattern, int startIndex, int endIndex, boolean accumulate) throws IOException {
+ int pos = findByte(pattern[0], startIndex, endIndex);
+ if (pos == -1)
+ return -1;
+ if (pos + ILocalStoreConstants.CHUNK_DELIMITER_SIZE > bufferLength) {
+ if (accumulate)
+ accumulate(buffer, nextByteInBuffer, pos);
+ nextByteInBuffer = pos;
+ pos = 0;
+ shiftAndFillBuffer();
+ }
+ if (compare(buffer, pattern, pos))
+ return pos;
+ return find(pattern, pos + 1, endIndex, accumulate);
+ }
+
+ protected int findByte(byte target, int startIndex, int endIndex) {
+ while (startIndex < endIndex) {
+ if (buffer[startIndex] == target)
+ return startIndex;
+ startIndex++;
+ }
+ return -1;
+ }
+
+ protected void findChunkStart() throws IOException {
+ if (nextByteInBuffer + ILocalStoreConstants.CHUNK_DELIMITER_SIZE > bufferLength)
+ shiftAndFillBuffer();
+ int begin = find(ILocalStoreConstants.BEGIN_CHUNK, nextByteInBuffer, bufferLength, false);
+ if (begin != -1) {
+ nextByteInBuffer = begin + ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ return;
+ }
+ bufferLength = input.read(buffer);
+ nextByteInBuffer = 0;
+ if (bufferLength == -1) {
+ resetChunk();
+ endOfFile = true;
+ return;
+ }
+ findChunkStart();
+ }
+
+ public int read() throws IOException {
+ if (endOfFile)
+ return -1;
+ // if there are bytes left in the chunk, return the first available
+ if (nextByteInChunk < chunkLength)
+ return chunk[nextByteInChunk++] & 0xFF;
+ // Otherwise the chunk is empty so clear the current one, get the next
+ // one and recursively call read. Need to recur as the chunk may be
+ // real but empty.
+ resetChunk();
+ findChunkStart();
+ if (endOfFile)
+ return -1;
+ buildChunk();
+ refineChunk();
+ return read();
+ }
+
+ /**
+ * Skip over any begin chunks in the current chunk. This could be optimized
+ * to skip at the same time as we are scanning the buffer.
+ */
+ protected void refineChunk() {
+ int start = chunkLength - ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ if (start < 0)
+ return;
+ for (int i = start; i >= 0; i--) {
+ if (compare(chunk, ILocalStoreConstants.BEGIN_CHUNK, i)) {
+ nextByteInChunk = i + ILocalStoreConstants.CHUNK_DELIMITER_SIZE;
+ return;
+ }
+ }
+ }
+
+ protected void resetChunk() {
+ chunk = new byte[0];
+ chunkLength = 0;
+ nextByteInChunk = 0;
+ }
+
+ protected void shiftAndFillBuffer() throws IOException {
+ int length = bufferLength - nextByteInBuffer;
+ System.arraycopy(buffer, nextByteInBuffer, buffer, 0, length);
+ nextByteInBuffer = 0;
+ bufferLength = length;
+ int read = input.read(buffer, bufferLength, buffer.length - bufferLength);
+ if (read != -1)
+ bufferLength += read;
+ else {
+ resetChunk();
+ endOfFile = true;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/SafeChunkyOutputStream.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+
+/**
+ * Appends data, in chunks, to a file. Each chunk is defined by the moment
+ * the stream is opened (created) and a call to #succeed is made. It is
+ * necessary to use the <code>SafeChunkyInputStream</code> to read its
+ * contents back. The user of this class does not need to know explicitly about
+ * its chunk implementation.
+ * It is only an implementation detail. What really matters to the outside
+ * world is that it tries to keep the file data consistent.
+ * If some data becomes corrupted while writing or later, upon reading
+ * the file, the chunk that contains the corrupted data is skipped.
+ * <p>
+ * Because of this class purpose (keep data consistent), it is important that the
+ * user only calls <code>#succeed</code> when the chunk of data is successfully
+ * written. After this call, the user can continue writing data to the file and it
+ * will not be considered related to the previous chunk. So, if this data is
+ * corrupted, the previous one is still safe.
+ *
+ * @see SafeChunkyInputStream
+ */
+public class SafeChunkyOutputStream extends FilterOutputStream {
+ protected String filePath;
+ protected boolean isOpen;
+
+ public SafeChunkyOutputStream(File target) throws IOException {
+ this(target.getAbsolutePath());
+ }
+
+ public SafeChunkyOutputStream(String filePath) throws IOException {
+ super(new BufferedOutputStream(new FileOutputStream(filePath, true)));
+ this.filePath = filePath;
+ isOpen = true;
+ beginChunk();
+ }
+
+ protected void beginChunk() throws IOException {
+ write(ILocalStoreConstants.BEGIN_CHUNK);
+ }
+
+ protected void endChunk() throws IOException {
+ write(ILocalStoreConstants.END_CHUNK);
+ }
+
+ protected void open() throws IOException {
+ out = new BufferedOutputStream(new FileOutputStream(filePath, true));
+ isOpen = true;
+ beginChunk();
+ }
+
+ public void succeed() throws IOException {
+ try {
+ endChunk();
+ } finally {
+ isOpen = false;
+ close();
+ }
+ }
+
+ public void write(int b) throws IOException {
+ if (!isOpen)
+ open();
+ super.write(b);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/SafeFileInputStream.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+
+/**
+ * Given a target and a temporary locations, it tries to read the contents
+ * from the target. If a file does not exist at the target location, it tries
+ * to read the contents from the temporary location.
+ *
+ * @see SafeFileOutputStream
+ */
+public class SafeFileInputStream extends FilterInputStream {
+ protected static final String EXTENSION = ".bak"; //$NON-NLS-1$
+ private static final int DEFAUT_BUFFER_SIZE = 2048;
+
+ public SafeFileInputStream(File file) throws IOException {
+ this(file.getAbsolutePath(), null);
+ }
+
+ /**
+ * If targetPath is null, the file will be created in the default-temporary directory.
+ */
+ public SafeFileInputStream(String targetPath, String tempPath) throws IOException {
+ super(getInputStream(targetPath, tempPath, DEFAUT_BUFFER_SIZE));
+ }
+
+ /**
+ * If targetPath is null, the file will be created in the default-temporary directory.
+ */
+ public SafeFileInputStream(String targetPath, String tempPath, int bufferSize) throws IOException {
+ super(getInputStream(targetPath, tempPath, bufferSize));
+ }
+
+ private static InputStream getInputStream(String targetPath, String tempPath, int bufferSize) throws IOException {
+ File target = new File(targetPath);
+ if (!target.exists()) {
+ if (tempPath == null)
+ tempPath = target.getAbsolutePath() + EXTENSION;
+ target = new File(tempPath);
+ }
+ return new BufferedInputStream(new FileInputStream(target), bufferSize);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/SafeFileOutputStream.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.*;
+import org.eclipse.core.internal.utils.FileUtil;
+
+/**
+ * This class should be used when there's a file already in the
+ * destination and we don't want to lose its contents if a
+ * failure writing this stream happens.
+ * Basically, the new contents are written to a temporary location.
+ * If everything goes OK, it is moved to the right place.
+ * The user has the option to define the temporary location or
+ * it will be created in the default-temporary directory
+ * (see java.io.File for details).
+ */
+public class SafeFileOutputStream extends OutputStream {
+ protected File temp;
+ protected File target;
+ protected OutputStream output;
+ protected boolean failed;
+ protected static final String EXTENSION = ".bak"; //$NON-NLS-1$
+
+ public SafeFileOutputStream(File file) throws IOException {
+ this(file.getAbsolutePath(), null);
+ }
+
+ /**
+ * If targetPath is null, the file will be created in the default-temporary directory.
+ */
+ public SafeFileOutputStream(String targetPath, String tempPath) throws IOException {
+ failed = false;
+ target = new File(targetPath);
+ createTempFile(tempPath);
+ if (!target.exists()) {
+ if (!temp.exists()) {
+ output = new BufferedOutputStream(new FileOutputStream(target));
+ return;
+ }
+ // If we do not have a file at target location, but we do have at temp location,
+ // it probably means something wrong happened the last time we tried to write it.
+ // So, try to recover the backup file. And, if successful, write the new one.
+ copy(temp, target);
+ }
+ output = new BufferedOutputStream(new FileOutputStream(temp));
+ }
+
+ public void close() throws IOException {
+ try {
+ output.close();
+ } catch (IOException e) {
+ failed = true;
+ throw e; // rethrow
+ }
+ if (failed)
+ temp.delete();
+ else
+ commit();
+ }
+
+ protected void commit() throws IOException {
+ if (!temp.exists())
+ return;
+ target.delete();
+ copy(temp, target);
+ temp.delete();
+ }
+
+ protected void copy(File sourceFile, File destinationFile) throws IOException {
+ if (!sourceFile.exists())
+ return;
+ if (sourceFile.renameTo(destinationFile))
+ return;
+ InputStream source = null;
+ OutputStream destination = null;
+ try {
+ source = new BufferedInputStream(new FileInputStream(sourceFile));
+ destination = new BufferedOutputStream(new FileOutputStream(destinationFile));
+ transferStreams(source, destination);
+ } finally {
+ FileUtil.safeClose(source);
+ FileUtil.safeClose(destination);
+ }
+ }
+
+ protected void createTempFile(String tempPath) {
+ if (tempPath == null)
+ tempPath = target.getAbsolutePath() + EXTENSION;
+ temp = new File(tempPath);
+ }
+
+ public void flush() throws IOException {
+ try {
+ output.flush();
+ } catch (IOException e) {
+ failed = true;
+ throw e; // rethrow
+ }
+ }
+
+ public String getTempFilePath() {
+ return temp.getAbsolutePath();
+ }
+
+ protected void transferStreams(InputStream source, OutputStream destination) throws IOException {
+ byte[] buffer = new byte[8192];
+ while (true) {
+ int bytesRead = source.read(buffer);
+ if (bytesRead == -1)
+ break;
+ destination.write(buffer, 0, bytesRead);
+ }
+ }
+
+ public void write(int b) throws IOException {
+ try {
+ output.write(b);
+ } catch (IOException e) {
+ failed = true;
+ throw e; // rethrow
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,555 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Martin Oberhuber (Wind River) - [105554] handle cyclic symbolic links
+ * Martin Oberhuber (Wind River) - [232426] shared prefix histories for symlinks
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.regex.Pattern;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.internal.refresh.RefreshJob;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Queue;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * Represents the workspace's tree merged with the file system's tree.
+ */
+public class UnifiedTree {
+ /** special node to mark the separation of a node's children */
+ protected static final UnifiedTreeNode childrenMarker = new UnifiedTreeNode(null, null, null, null, false);
+
+ private static final Iterator EMPTY_ITERATOR = Collections.EMPTY_LIST.iterator();
+
+ /** special node to mark the beginning of a level in the tree */
+ protected static final UnifiedTreeNode levelMarker = new UnifiedTreeNode(null, null, null, null, false);
+
+ private static final IFileInfo[] NO_CHILDREN = new IFileInfo[0];
+
+ /** Singleton to indicate no local children */
+ private static final IResource[] NO_RESOURCES = new IResource[0];
+
+ /**
+ * True if the level of the children of the current node are valid according
+ * to the requested refresh depth, false otherwise
+ */
+ protected boolean childLevelValid = false;
+
+ /** an IFileTree which can be used to build a unified tree*/
+ protected IFileTree fileTree = null;
+
+ /** Spare node objects available for reuse */
+ protected ArrayList freeNodes = new ArrayList();
+ /** tree's actual level */
+ protected int level;
+ /** our queue */
+ protected Queue queue;
+
+ /** path prefixes for checking symbolic link cycles */
+ protected PrefixPool pathPrefixHistory, rootPathHistory;
+
+ /** tree's root */
+ protected IResource root;
+
+ /**
+ * The root must only be a file or a folder.
+ */
+ public UnifiedTree(IResource root) {
+ setRoot(root);
+ }
+
+ /**
+ * Pass in a a root for the tree, a file tree containing all of the entries for this
+ * tree and a flag indicating whether the UnifiedTree should consult the fileTree where
+ * possible for entries
+ * @param root
+ * @param fileTree
+ */
+ public UnifiedTree(IResource root, IFileTree fileTree) {
+ this(root);
+ this.fileTree = fileTree;
+ }
+
+ public void accept(IUnifiedTreeVisitor visitor) throws CoreException {
+ accept(visitor, IResource.DEPTH_INFINITE);
+ }
+
+ /**
+ * Performs a breadth-first traversal of the unified tree, passing each
+ * node to the provided visitor.
+ */
+ public void accept(IUnifiedTreeVisitor visitor, int depth) throws CoreException {
+ Assert.isNotNull(root);
+ initializeQueue();
+ setLevel(0, depth);
+ while (!queue.isEmpty()) {
+ UnifiedTreeNode node = (UnifiedTreeNode) queue.remove();
+ if (isChildrenMarker(node))
+ continue;
+ if (isLevelMarker(node)) {
+ if (!setLevel(getLevel() + 1, depth))
+ break;
+ continue;
+ }
+ if (visitor.visit(node))
+ addNodeChildrenToQueue(node);
+ else
+ removeNodeChildrenFromQueue(node);
+ //allow reuse of the node
+ freeNodes.add(node);
+ }
+ }
+
+ protected void addChildren(UnifiedTreeNode node) {
+ Resource parent = (Resource) node.getResource();
+
+ // is there a possibility to have children?
+ int parentType = parent.getType();
+ if (parentType == IResource.FILE && !node.isFolder())
+ return;
+
+ //don't refresh resources in closed or non-existent projects
+ if (!parent.getProject().isAccessible())
+ return;
+
+ // get the list of resources in the file system
+ // don't ask for local children if we know it doesn't exist locally
+ IFileInfo[] list = node.existsInFileSystem() ? getLocalList(node) : NO_CHILDREN;
+ int localIndex = 0;
+
+ // See if the children of this resource have been computed before
+ ResourceInfo resourceInfo = parent.getResourceInfo(false, false);
+ int flags = parent.getFlags(resourceInfo);
+ boolean unknown = ResourceInfo.isSet(flags, ICoreConstants.M_CHILDREN_UNKNOWN);
+
+ // get the list of resources in the workspace
+ if (!unknown && (parentType == IResource.FOLDER || parentType == IResource.PROJECT) && parent.exists(flags, true)) {
+ IResource target = null;
+ UnifiedTreeNode child = null;
+ IResource[] members;
+ try {
+ members = ((IContainer) parent).members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ } catch (CoreException e) {
+ members = NO_RESOURCES;
+ }
+ int workspaceIndex = 0;
+ //iterate simultaneously over file system and workspace members
+ while (workspaceIndex < members.length) {
+ target = members[workspaceIndex];
+ String name = target.getName();
+ IFileInfo localInfo = localIndex < list.length ? list[localIndex] : null;
+ int comp = localInfo != null ? name.compareTo(localInfo.getName()) : -1;
+ //special handling for linked resources
+ if (target.isLinked()) {
+ //child will be null if location is undefined
+ child = createChildForLinkedResource(target);
+ workspaceIndex++;
+ //if there is a matching local file, skip it - it will be blocked by the linked resource
+ if (comp == 0)
+ localIndex++;
+ } else if (comp == 0) {
+ // resource exists in workspace and file system --> localInfo is non-null
+ //create workspace-only node for symbolic link that creates a cycle
+ if (localInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK) && localInfo.isDirectory() && isRecursiveLink(node.getStore(), localInfo))
+ child = createNode(target, null, null, true);
+ else
+ child = createNode(target, null, localInfo, true);
+ localIndex++;
+ workspaceIndex++;
+ } else if (comp > 0) {
+ // resource exists only in file system
+ //don't create a node for symbolic links that create a cycle
+ if (!localInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK) || !localInfo.isDirectory() || !isRecursiveLink(node.getStore(), localInfo))
+ child = createChildNodeFromFileSystem(node, localInfo);
+ localIndex++;
+ } else {
+ // resource exists only in the workspace
+ child = createNode(target, null, null, true);
+ workspaceIndex++;
+ }
+ if (child != null)
+ addChildToTree(node, child);
+ }
+ }
+
+ /* process any remaining resource from the file system */
+ addChildrenFromFileSystem(node, list, localIndex);
+
+ /* Mark the children as now known */
+ if (unknown) {
+ // Don't open the info - we might not be inside a workspace-modifying operation
+ resourceInfo = parent.getResourceInfo(false, false);
+ if (resourceInfo != null)
+ resourceInfo.clear(ICoreConstants.M_CHILDREN_UNKNOWN);
+ }
+
+ /* if we added children, add the childMarker separator */
+ if (node.getFirstChild() != null)
+ addChildrenMarker();
+ }
+
+ protected void addChildrenFromFileSystem(UnifiedTreeNode node, IFileInfo[] childInfos, int index) {
+ if (childInfos == null)
+ return;
+ for (int i = index; i < childInfos.length; i++) {
+ IFileInfo info = childInfos[i];
+ //don't create a node for symbolic links that create a cycle
+ if (!info.getAttribute(EFS.ATTRIBUTE_SYMLINK) || !info.isDirectory() || !isRecursiveLink(node.getStore(), info))
+ addChildToTree(node, createChildNodeFromFileSystem(node, info));
+ }
+ }
+
+ protected void addChildrenMarker() {
+ addElementToQueue(childrenMarker);
+ }
+
+ protected void addChildToTree(UnifiedTreeNode node, UnifiedTreeNode child) {
+ if (node.getFirstChild() == null)
+ node.setFirstChild(child);
+ addElementToQueue(child);
+ }
+
+ protected void addElementToQueue(UnifiedTreeNode target) {
+ queue.add(target);
+ }
+
+ protected void addNodeChildrenToQueue(UnifiedTreeNode node) {
+ /* if the first child is not null we already added the children */
+ /* If the children won't be at a valid level for the refresh depth, don't bother adding them */
+ if (!childLevelValid || node.getFirstChild() != null)
+ return;
+ addChildren(node);
+ if (queue.isEmpty())
+ return;
+ //if we're about to change levels, then the children just added
+ //are the last nodes for their level, so add a level marker to the queue
+ UnifiedTreeNode nextNode = (UnifiedTreeNode) queue.peek();
+ if (isChildrenMarker(nextNode))
+ queue.remove();
+ nextNode = (UnifiedTreeNode) queue.peek();
+ if (isLevelMarker(nextNode))
+ addElementToQueue(levelMarker);
+ }
+
+ protected void addRootToQueue() {
+ //don't refresh in closed projects
+ if (!root.getProject().isAccessible())
+ return;
+ IFileStore store = ((Resource) root).getStore();
+ IFileInfo fileInfo = fileTree != null ? fileTree.getFileInfo(store) : store.fetchInfo();
+ UnifiedTreeNode node = createNode(root, store, fileInfo, root.exists());
+ if (node.existsInFileSystem() || node.existsInWorkspace())
+ addElementToQueue(node);
+ }
+
+ /**
+ * Creates a tree node for a resource that is linked in a different file system location.
+ */
+ protected UnifiedTreeNode createChildForLinkedResource(IResource target) {
+ IFileStore store = ((Resource) target).getStore();
+ return createNode(target, store, store.fetchInfo(), true);
+ }
+
+ /**
+ * Creates a child node for a location in the file system. Does nothing and returns null if the location does not correspond to a valid file/folder.
+ */
+ protected UnifiedTreeNode createChildNodeFromFileSystem(UnifiedTreeNode parent, IFileInfo info) {
+ IPath childPath = parent.getResource().getFullPath().append(info.getName());
+ int type = info.isDirectory() ? IResource.FOLDER : IResource.FILE;
+ IResource target = getWorkspace().newResource(childPath, type);
+ return createNode(target, null, info, false);
+ }
+
+ /**
+ * Factory method for creating a node for this tree. If the file exists on
+ * disk, either the parent store or child store can be provided. Providing
+ * only the parent store avoids creation of the child store in cases where
+ * it is not needed. The store object is only needed for directories for
+ * simple file system traversals, so this avoids creating store objects
+ * for all files.
+ */
+ protected UnifiedTreeNode createNode(IResource resource, IFileStore store, IFileInfo info, boolean existsWorkspace) {
+ //first check for reusable objects
+ UnifiedTreeNode node = null;
+ int size = freeNodes.size();
+ if (size > 0) {
+ node = (UnifiedTreeNode) freeNodes.remove(size - 1);
+ node.reuse(this, resource, store, info, existsWorkspace);
+ return node;
+ }
+ //none available, so create a new one
+ return new UnifiedTreeNode(this, resource, store, info, existsWorkspace);
+ }
+
+ protected Iterator getChildren(UnifiedTreeNode node) {
+ /* if first child is null we need to add node's children to queue */
+ if (node.getFirstChild() == null)
+ addNodeChildrenToQueue(node);
+
+ /* if the first child is still null, the node does not have any children */
+ if (node.getFirstChild() == null)
+ return EMPTY_ITERATOR;
+
+ /* get the index of the first child */
+ int index = queue.indexOf(node.getFirstChild());
+
+ /* if we do not have children, just return an empty enumeration */
+ if (index == -1)
+ return EMPTY_ITERATOR;
+
+ /* create an enumeration with node's children */
+ List result = new ArrayList(10);
+ while (true) {
+ UnifiedTreeNode child = (UnifiedTreeNode) queue.elementAt(index);
+ if (isChildrenMarker(child))
+ break;
+ result.add(child);
+ index = queue.increment(index);
+ }
+ return result.iterator();
+ }
+
+ protected int getLevel() {
+ return level;
+ }
+
+ protected IFileInfo[] getLocalList(UnifiedTreeNode node) {
+ try {
+ final IFileStore store = node.getStore();
+ IFileInfo[] list = fileTree != null ? fileTree.getChildInfos(store) : store.childInfos(EFS.NONE, null);
+ if (list == null)
+ return NO_CHILDREN;
+ int size = list.length;
+ if (size > 1)
+ quickSort(list, 0, size - 1);
+ return list;
+ } catch (CoreException e) {
+ //treat failure to access the directory as a non-existent directory
+ return NO_CHILDREN;
+ }
+ }
+
+ protected Workspace getWorkspace() {
+ return (Workspace) root.getWorkspace();
+ }
+
+ protected void initializeQueue() {
+ //initialize the queue
+ if (queue == null)
+ queue = new Queue(100, false);
+ else
+ queue.reset();
+ //initialize the free nodes list
+ if (freeNodes == null)
+ freeNodes = new ArrayList(100);
+ else
+ freeNodes.clear();
+ addRootToQueue();
+ addElementToQueue(levelMarker);
+ }
+
+ protected boolean isChildrenMarker(UnifiedTreeNode node) {
+ return node == childrenMarker;
+ }
+
+ protected boolean isLevelMarker(UnifiedTreeNode node) {
+ return node == levelMarker;
+ }
+
+ private static class PatternHolder {
+ //Initialize-on-demand Holder class to avoid compiling Pattern if never needed
+ //Pattern: A UNIX relative path that just points backward
+ public static Pattern trivialSymlinkPattern = Pattern.compile("\\.[./]*"); //$NON-NLS-1$
+ }
+
+ /**
+ * Initialize history stores for symbolic links.
+ * This may be done when starting a visitor, or later on demand.
+ */
+ protected void initLinkHistoriesIfNeeded() {
+ if (pathPrefixHistory == null) {
+ //Bug 232426: Check what life cycle we need for the histories
+ Job job = Job.getJobManager().currentJob();
+ if (job instanceof RefreshJob) {
+ //we are running from the RefreshJob: use the path history of the job
+ RefreshJob refreshJob = (RefreshJob) job;
+ pathPrefixHistory = refreshJob.getPathPrefixHistory();
+ rootPathHistory = refreshJob.getRootPathHistory();
+ } else {
+ //Local Histories
+ pathPrefixHistory = new PrefixPool(20);
+ rootPathHistory = new PrefixPool(20);
+ }
+ }
+ if (rootPathHistory.size() == 0) {
+ //add current root to history
+ IFileStore rootStore = ((Resource) root).getStore();
+ try {
+ java.io.File rootFile = rootStore.toLocalFile(EFS.NONE, null);
+ if (rootFile != null) {
+ IPath rootProjPath = root.getProject().getLocation();
+ if (rootProjPath != null) {
+ try {
+ java.io.File rootProjFile = new java.io.File(rootProjPath.toOSString());
+ rootPathHistory.insertShorter(rootProjFile.getCanonicalPath() + '/');
+ } catch (IOException ioe) {
+ /*ignore here*/
+ }
+ }
+ rootPathHistory.insertShorter(rootFile.getCanonicalPath() + '/');
+ }
+ } catch (CoreException e) {
+ /*ignore*/
+ } catch (IOException e) {
+ /*ignore*/
+ }
+ }
+ }
+
+ /**
+ * Check if the given child represents a recursive symbolic link.
+ * <p>
+ * On remote EFS stores, this check is not exhaustive and just
+ * finds trivial recursive symbolic links pointing up in the tree.
+ * </p><p>
+ * On local stores, where {@link java.io.File#getCanonicalPath()}
+ * is available, the test is exhaustive but may also find some
+ * false positives with transitive symbolic links. This may lead
+ * to suppressing duplicates of already known resources in the
+ * tree, but it will never lead to not finding a resource at
+ * all. See bug 105554 for details.
+ * </p>
+ * @param parentStore EFS IFileStore representing the parent folder
+ * @param localInfo child representing a symbolic link
+ * @return <code>true</code> if the given child represents a
+ * recursive symbolic link.
+ */
+ private boolean isRecursiveLink(IFileStore parentStore, IFileInfo localInfo) {
+ //Try trivial pattern first - works also on remote EFS stores
+ String linkTarget = localInfo.getStringAttribute(EFS.ATTRIBUTE_LINK_TARGET);
+ if (linkTarget != null && PatternHolder.trivialSymlinkPattern.matcher(linkTarget).matches()) {
+ return true;
+ }
+ //Need canonical paths to check all other possibilities
+ try {
+ java.io.File parentFile = parentStore.toLocalFile(EFS.NONE, null);
+ //If this store cannot be represented as a local file, there is nothing we can do
+ //In the future, we could try to resolve the link target
+ //against the remote file system to do more checks.
+ if (parentFile == null)
+ return false;
+ //get canonical path for both child and parent
+ java.io.File childFile = new java.io.File(parentFile, localInfo.getName());
+ String parentPath = parentFile.getCanonicalPath() + '/';
+ String childPath = childFile.getCanonicalPath() + '/';
+ //get or instantiate the prefix and root path histories.
+ //Might be done earlier - for now, do it on demand.
+ initLinkHistoriesIfNeeded();
+ //insert the parent for checking loops
+ pathPrefixHistory.insertLonger(parentPath);
+ if (pathPrefixHistory.containsAsPrefix(childPath)) {
+ //found a potential loop: is it spanning up a new tree?
+ if (!rootPathHistory.insertShorter(childPath)) {
+ //not spanning up a new tree, so it is a real loop.
+ return true;
+ }
+ } else if (rootPathHistory.hasPrefixOf(childPath)) {
+ //child points into a different portion of the tree that we visited already before, or will certainly visit.
+ //This does not introduce a loop yet, but introduces duplicate resources.
+ //TODO Ideally, such duplicates should be modelled as linked resources. See bug 105534
+ return false;
+ } else {
+ //child neither introduces a loop nor points to a known tree.
+ //It probably spans up a new tree of potential prefixes.
+ rootPathHistory.insertShorter(childPath);
+ }
+ } catch (IOException e) {
+ //ignore
+ } catch (CoreException e) {
+ //ignore
+ }
+ return false;
+ }
+
+ protected boolean isValidLevel(int currentLevel, int depth) {
+ switch (depth) {
+ case IResource.DEPTH_INFINITE :
+ return true;
+ case IResource.DEPTH_ONE :
+ return currentLevel <= 1;
+ case IResource.DEPTH_ZERO :
+ return currentLevel == 0;
+ default :
+ return currentLevel + 1000 <= depth;
+ }
+ }
+
+ /**
+ * Sorts the given array of strings in place. This is
+ * not using the sorting framework to avoid casting overhead.
+ */
+ protected void quickSort(IFileInfo[] infos, int left, int right) {
+ int originalLeft = left;
+ int originalRight = right;
+ IFileInfo mid = infos[(left + right) / 2];
+ do {
+ while (mid.compareTo(infos[left]) > 0)
+ left++;
+ while (infos[right].compareTo(mid) > 0)
+ right--;
+ if (left <= right) {
+ IFileInfo tmp = infos[left];
+ infos[left] = infos[right];
+ infos[right] = tmp;
+ left++;
+ right--;
+ }
+ } while (left <= right);
+ if (originalLeft < right)
+ quickSort(infos, originalLeft, right);
+ if (left < originalRight)
+ quickSort(infos, left, originalRight);
+ return;
+ }
+
+ /**
+ * Remove from the last element of the queue to the first child of the
+ * given node.
+ */
+ protected void removeNodeChildrenFromQueue(UnifiedTreeNode node) {
+ UnifiedTreeNode first = node.getFirstChild();
+ if (first == null)
+ return;
+ while (true) {
+ if (first.equals(queue.removeTail()))
+ break;
+ }
+ node.setFirstChild(null);
+ }
+
+ /**
+ * Increases the current tree level by one. Returns true if the new
+ * level is still valid for the given depth
+ */
+ protected boolean setLevel(int newLevel, int depth) {
+ level = newLevel;
+ childLevelValid = isValidLevel(level + 1, depth);
+ return isValidLevel(level, depth);
+ }
+
+ private void setRoot(IResource root) {
+ this.root = root;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTreeNode.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.localstore;
+
+import java.util.Iterator;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.internal.resources.Resource;
+import org.eclipse.core.resources.IResource;
+
+/**
+ * A node in a {@link UnifiedTree}. A node usually represents a file/folder
+ * in the workspace, the file system, or both. There are also special node
+ * instances to act as child and level markers in the tree.
+ */
+public class UnifiedTreeNode implements ILocalStoreConstants {
+ protected UnifiedTreeNode child;
+ protected boolean existsWorkspace;
+ protected IFileInfo fileInfo;
+ protected IResource resource;
+ protected IFileStore store;
+ protected UnifiedTree tree;
+
+ public UnifiedTreeNode(UnifiedTree tree, IResource resource, IFileStore store, IFileInfo fileInfo, boolean existsWorkspace) {
+ this.tree = tree;
+ this.resource = resource;
+ this.store = store;
+ this.fileInfo = fileInfo;
+ this.existsWorkspace = existsWorkspace;
+ }
+
+ public boolean existsInFileSystem() {
+ return fileInfo != null && fileInfo.exists();
+ }
+
+ public boolean existsInWorkspace() {
+ return existsWorkspace;
+ }
+
+ /**
+ * Returns an Iterator of UnifiedTreeNode.
+ */
+ public Iterator getChildren() {
+ return tree.getChildren(this);
+ }
+
+ protected UnifiedTreeNode getFirstChild() {
+ return child;
+ }
+
+ public long getLastModified() {
+ return fileInfo == null ? 0 : fileInfo.getLastModified();
+ }
+
+ public int getLevel() {
+ return tree.getLevel();
+ }
+
+ /**
+ * Gets the name of this node in the local file system.
+ * @return Returns a String
+ */
+ public String getLocalName() {
+ return fileInfo == null ? null : fileInfo.getName();
+ }
+
+ public IResource getResource() {
+ return resource;
+ }
+
+ /**
+ * Returns the local store of this resource. May be null.
+ */
+ public IFileStore getStore() {
+ //initialize store lazily, because it is not always needed
+ if (store == null)
+ store = ((Resource) resource).getStore();
+ return store;
+ }
+
+ public boolean isFolder() {
+ return fileInfo == null ? false : fileInfo.isDirectory();
+ }
+
+ public boolean isSymbolicLink() {
+ return fileInfo == null ? false : fileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK);
+ }
+
+ public void removeChildrenFromTree() {
+ tree.removeNodeChildrenFromQueue(this);
+ }
+
+ /**
+ * Reuses this object by assigning all new values for the fields.
+ */
+ public void reuse(UnifiedTree aTree, IResource aResource, IFileStore aStore, IFileInfo info, boolean existsInWorkspace) {
+ this.tree = aTree;
+ this.child = null;
+ this.resource = aResource;
+ this.store = aStore;
+ this.fileInfo = info;
+ this.existsWorkspace = existsInWorkspace;
+ }
+
+ public void setExistsWorkspace(boolean exists) {
+ this.existsWorkspace = exists;
+ }
+
+ protected void setFirstChild(UnifiedTreeNode child) {
+ this.child = child;
+ }
+
+ public void setResource(IResource resource) {
+ this.resource = resource;
+ }
+
+ public String toString() {
+ String s = resource == null ? "null" : resource.getFullPath().toString(); //$NON-NLS-1$
+ return "Node: " + s; //$NON-NLS-1$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/IPropertyManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.properties;
+
+import java.util.Map;
+import org.eclipse.core.internal.resources.IManager;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+
+public interface IPropertyManager extends IManager {
+ /**
+ * Closes the property store for a resource
+ *
+ * @param target The resource to close the property store for
+ * @exception CoreException
+ */
+ public void closePropertyStore(IResource target) throws CoreException;
+
+ /**
+ * Copy all the properties of one resource to another. Both resources
+ * must have a property store available.
+ */
+ public void copy(IResource source, IResource destination, int depth) throws CoreException;
+
+ /**
+ * Deletes all properties for the given resource and its children.
+ * <p>
+ * The subtree under the given resource is traversed to the supplied depth.
+ * </p>
+ * @param target
+ * @param depth
+ * @exception CoreException
+ */
+ public void deleteProperties(IResource target, int depth) throws CoreException;
+
+ /**
+ * The resource is being deleted so permanently erase its properties.
+ */
+ public void deleteResource(IResource target) throws CoreException;
+
+ /**
+ * Returns the value of the identified property on the given resource as
+ * maintained by this store.
+ * <p>
+ * The qualifier part of the property name must be the unique identifier
+ * of the declaring plug-in (e.g. <code>"com.example.plugin"</code>).
+ * </p>
+ */
+ public String getProperty(IResource target, QualifiedName name) throws CoreException;
+
+ /**
+ * Sets the value of the identified property on the given resource.
+ * <p>
+ * The qualifier part of the property name must be the unique identifier
+ * of the declaring plug-in (e.g. <code>"com.example.plugin"</code>).
+ * </p>
+ */
+ public void setProperty(IResource target, QualifiedName name, String value) throws CoreException;
+
+ /**
+ * Returns a map (<propertyKey: QualifiedName -> value: String>) containing
+ * all properties defined for the given resource. In case no properties can
+ * be found, returns an empty map.
+ */
+ public Map getProperties(IResource resource) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/PropertyBucket.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.properties;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.localstore.Bucket;
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+public class PropertyBucket extends Bucket {
+ public static class PropertyEntry extends Entry {
+
+ private final static Comparator COMPARATOR = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ int qualifierComparison = ((String[]) o1)[0].compareTo(((String[]) o2)[0]);
+ return qualifierComparison != 0 ? qualifierComparison : ((String[]) o1)[1].compareTo(((String[]) o2)[1]);
+ }
+ };
+ private static final String[][] EMPTY_DATA = new String[0][];
+ /**
+ * value is a String[][] of {{propertyKey.qualifier, propertyKey.localName, propertyValue}}
+ */
+ private String[][] value;
+
+ /**
+ * Deletes the property with the given name, and returns the result array. Returns the original
+ * array if the property to be deleted could not be found. Returns <code>null</code> if the property was found
+ * and the original array had size 1 (instead of a zero-length array).
+ */
+ static String[][] delete(String[][] existing, QualifiedName propertyName) {
+ // a size-1 array is a special case
+ if (existing.length == 1)
+ return (existing[0][0].equals(propertyName.getQualifier()) && existing[0][1].equals(propertyName.getLocalName())) ? null : existing;
+ // find the guy to delete
+ int deletePosition = search(existing, propertyName);
+ if (deletePosition < 0)
+ // not found, nothing to delete
+ return existing;
+ String[][] newValue = new String[existing.length - 1][];
+ if (deletePosition > 0)
+ // copy elements preceding the one to be removed
+ System.arraycopy(existing, 0, newValue, 0, deletePosition);
+ if (deletePosition < existing.length - 1)
+ // copy elements succeeding the one to be removed
+ System.arraycopy(existing, deletePosition + 1, newValue, deletePosition, newValue.length - deletePosition);
+ return newValue;
+ }
+
+ static String[][] insert(String[][] existing, QualifiedName propertyName, String propertyValue) {
+ // look for the right spot where to insert the new guy
+ int index = search(existing, propertyName);
+ if (index >= 0) {
+ // found existing occurrence - just replace the value
+ existing[index][2] = propertyValue;
+ return existing;
+ }
+ // not found - insert
+ int insertPosition = -index - 1;
+ String[][] newValue = new String[existing.length + 1][];
+ if (insertPosition > 0)
+ System.arraycopy(existing, 0, newValue, 0, insertPosition);
+ newValue[insertPosition] = new String[] {propertyName.getQualifier(), propertyName.getLocalName(), propertyValue};
+ if (insertPosition < existing.length)
+ System.arraycopy(existing, insertPosition, newValue, insertPosition + 1, existing.length - insertPosition);
+ return newValue;
+ }
+
+ /**
+ * Merges two entries (are always sorted). Duplicated additions replace existing ones.
+ */
+ static Object merge(String[][] base, String[][] additions) {
+ int additionPointer = 0;
+ int basePointer = 0;
+ int added = 0;
+ String[][] result = new String[base.length + additions.length][];
+ while (basePointer < base.length && additionPointer < additions.length) {
+ int comparison = COMPARATOR.compare(base[basePointer], additions[additionPointer]);
+ if (comparison == 0) {
+ result[added++] = additions[additionPointer++];
+ // duplicate, override
+ basePointer++;
+ } else if (comparison < 0)
+ result[added++] = base[basePointer++];
+ else
+ result[added++] = additions[additionPointer++];
+ }
+ // copy the remaining states from either additions or base arrays
+ String[][] remaining = basePointer == base.length ? additions : base;
+ int remainingPointer = basePointer == base.length ? additionPointer : basePointer;
+ int remainingCount = remaining.length - remainingPointer;
+ System.arraycopy(remaining, remainingPointer, result, added, remainingCount);
+ added += remainingCount;
+ if (added == base.length + additions.length)
+ // no collisions
+ return result;
+ // there were collisions, need to compact
+ String[][] finalResult = new String[added][];
+ System.arraycopy(result, 0, finalResult, 0, finalResult.length);
+ return finalResult;
+ }
+
+ private static int search(String[][] existing, QualifiedName propertyName) {
+ return Arrays.binarySearch(existing, new String[] {propertyName.getQualifier(), propertyName.getLocalName(), null}, COMPARATOR);
+ }
+
+ public PropertyEntry(IPath path, PropertyEntry base) {
+ super(path);
+ //copy 2-dimensional array [x][y]
+ int xLen = base.value.length;
+ this.value = new String[xLen][];
+ for (int i = 0; i < xLen; i++) {
+ int yLen = base.value[i].length;
+ this.value[i] = new String[yLen];
+ System.arraycopy(base.value[i], 0, value[i], 0, yLen);
+ }
+ }
+
+ /**
+ * @param path
+ * @param value is a String[][] {{propertyKey, propertyValue}}
+ */
+ protected PropertyEntry(IPath path, String[][] value) {
+ super(path);
+ this.value = value;
+ }
+
+ /**
+ * Compacts the data array removing any null slots. If non-null slots
+ * are found, the entry is marked for removal.
+ */
+ private void compact() {
+ if (!isDirty())
+ return;
+ int occurrences = 0;
+ for (int i = 0; i < value.length; i++)
+ if (value[i] != null)
+ value[occurrences++] = value[i];
+ if (occurrences == value.length)
+ // no states deleted
+ return;
+ if (occurrences == 0) {
+ // no states remaining
+ value = EMPTY_DATA;
+ delete();
+ return;
+ }
+ String[][] result = new String[occurrences][];
+ System.arraycopy(value, 0, result, 0, occurrences);
+ value = result;
+ }
+
+ public int getOccurrences() {
+ return value == null ? 0 : value.length;
+ }
+
+ public String getProperty(QualifiedName name) {
+ int index = search(value, name);
+ return index < 0 ? null : value[index][2];
+ }
+
+ public Object getPropertyName(int i) {
+ return new QualifiedName(this.value[i][0], this.value[i][1]);
+ }
+
+ public Object getPropertyValue(int i) {
+ return this.value[i][2];
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void visited() {
+ compact();
+ }
+ }
+
+ public static final byte INDEX = 1;
+
+ public static final byte QNAME = 2;
+
+ /** Version number for the current implementation file's format.
+ * <p>
+ * Version 1:
+ * <pre>
+ * FILE ::= VERSION_ID ENTRY+
+ * ENTRY ::= PATH PROPERTY_COUNT PROPERTY+
+ * PATH ::= string (does not contain project name)
+ * PROPERTY_COUNT ::= int
+ * PROPERTY ::= QUALIFIER LOCAL_NAME VALUE
+ * QUALIFIER ::= INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte string
+ * UUID ::= byte[16]
+ * LAST_MODIFIED ::= byte[8]
+ * </pre>
+ * </p>
+ */
+ private static final byte VERSION = 1;
+
+ private final List qualifierIndex = new ArrayList();
+
+ public PropertyBucket() {
+ super();
+ }
+
+ protected Entry createEntry(IPath path, Object value) {
+ return new PropertyEntry(path, (String[][]) value);
+ }
+
+ private PropertyEntry getEntry(IPath path) {
+ String pathAsString = path.toString();
+ String[][] existing = (String[][]) getEntryValue(pathAsString);
+ if (existing == null)
+ return null;
+ return new PropertyEntry(path, existing);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.localstore.Bucket#getIndexFileName()
+ */
+ protected String getIndexFileName() {
+ return "properties.index"; //$NON-NLS-1$
+ }
+
+ public String getProperty(IPath path, QualifiedName name) {
+ PropertyEntry entry = getEntry(path);
+ if (entry == null)
+ return null;
+ return entry.getProperty(name);
+ }
+
+ protected byte getVersion() {
+ return VERSION;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.localstore.Bucket#getVersionFileName()
+ */
+ protected String getVersionFileName() {
+ return "properties.version"; //$NON-NLS-1$
+ }
+
+ public void load(String newProjectName, File baseLocation, boolean force) throws CoreException {
+ qualifierIndex.clear();
+ super.load(newProjectName, baseLocation, force);
+ }
+
+ protected Object readEntryValue(DataInputStream source) throws IOException, CoreException {
+ int length = source.readUnsignedShort();
+ String[][] properties = new String[length][3];
+ for (int j = 0; j < properties.length; j++) {
+ // qualifier
+ byte constant = source.readByte();
+ switch (constant) {
+ case QNAME :
+ properties[j][0] = source.readUTF();
+ qualifierIndex.add(properties[j][0]);
+ break;
+ case INDEX :
+ properties[j][0] = (String) qualifierIndex.get(source.readInt());
+ break;
+ default :
+ //if we get here the properties file is corrupt
+ IPath resourcePath = projectName == null ? Path.ROOT : Path.ROOT.append(projectName);
+ String msg = NLS.bind(Messages.properties_readProperties, resourcePath.toString());
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ // localName
+ properties[j][1] = source.readUTF();
+ // propertyValue
+ properties[j][2] = source.readUTF();
+ }
+ return properties;
+ }
+
+ public void save() throws CoreException {
+ qualifierIndex.clear();
+ super.save();
+ }
+
+ public void setProperties(PropertyEntry entry) {
+ IPath path = entry.getPath();
+ String[][] additions = (String[][]) entry.getValue();
+ String pathAsString = path.toString();
+ String[][] existing = (String[][]) getEntryValue(pathAsString);
+ if (existing == null) {
+ setEntryValue(pathAsString, additions);
+ return;
+ }
+ setEntryValue(pathAsString, PropertyEntry.merge(existing, additions));
+ }
+
+ public void setProperty(IPath path, QualifiedName name, String value) {
+ String pathAsString = path.toString();
+ String[][] existing = (String[][]) getEntryValue(pathAsString);
+ if (existing == null) {
+ if (value != null)
+ setEntryValue(pathAsString, new String[][] {{name.getQualifier(), name.getLocalName(), value}});
+ return;
+ }
+ String[][] newValue;
+ if (value != null)
+ newValue = PropertyEntry.insert(existing, name, value);
+ else
+ newValue = PropertyEntry.delete(existing, name);
+ // even if newValue == existing we should mark as dirty (insert may just change the existing array)
+ setEntryValue(pathAsString, newValue);
+ }
+
+ protected void writeEntryValue(DataOutputStream destination, Object entryValue) throws IOException {
+ String[][] properties = (String[][]) entryValue;
+ destination.writeShort(properties.length);
+ for (int j = 0; j < properties.length; j++) {
+ // writes the property key qualifier
+ int index = qualifierIndex.indexOf(properties[j][0]);
+ if (index == -1) {
+ destination.writeByte(QNAME);
+ destination.writeUTF(properties[j][0]);
+ qualifierIndex.add(properties[j][0]);
+ } else {
+ destination.writeByte(INDEX);
+ destination.writeInt(index);
+ }
+ // then the local name
+ destination.writeUTF(properties[j][1]);
+ // then the property value
+ destination.writeUTF(properties[j][2]);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/properties/PropertyManager2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.properties;
+
+import java.io.File;
+import java.util.*;
+import org.eclipse.core.internal.localstore.Bucket;
+import org.eclipse.core.internal.localstore.BucketTree;
+import org.eclipse.core.internal.localstore.Bucket.Entry;
+import org.eclipse.core.internal.properties.PropertyBucket.PropertyEntry;
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @see org.eclipse.core.internal.properties.IPropertyManager
+ */
+public class PropertyManager2 implements IPropertyManager {
+ class PropertyCopyVisitor extends Bucket.Visitor {
+ private List changes = new ArrayList();
+ private IPath destination;
+ private IPath source;
+
+ public PropertyCopyVisitor(IPath source, IPath destination) {
+ this.source = source;
+ this.destination = destination;
+ }
+
+ public void afterSaving(Bucket bucket) throws CoreException {
+ saveChanges((PropertyBucket) bucket);
+ changes.clear();
+ }
+
+ private void saveChanges(PropertyBucket bucket) throws CoreException {
+ if (changes.isEmpty())
+ return;
+ // make effective all changes collected
+ Iterator i = changes.iterator();
+ PropertyEntry entry = (PropertyEntry) i.next();
+ tree.loadBucketFor(entry.getPath());
+ bucket.setProperties(entry);
+ while (i.hasNext())
+ bucket.setProperties((PropertyEntry) i.next());
+ bucket.save();
+ }
+
+ public int visit(Entry entry) {
+ PropertyEntry sourceEntry = (PropertyEntry) entry;
+ IPath destinationPath = destination.append(sourceEntry.getPath().removeFirstSegments(source.segmentCount()));
+ PropertyEntry destinationEntry = new PropertyEntry(destinationPath, sourceEntry);
+ changes.add(destinationEntry);
+ return CONTINUE;
+ }
+ }
+
+ BucketTree tree;
+
+ public PropertyManager2(Workspace workspace) {
+ this.tree = new BucketTree(workspace, new PropertyBucket());
+ }
+
+ public void closePropertyStore(IResource target) throws CoreException {
+ // ensure any uncommitted are written to disk
+ tree.getCurrent().save();
+ // flush in-memory state to avoid confusion if another project is later
+ // created with the same name
+ tree.getCurrent().flush();
+ }
+
+ public synchronized void copy(IResource source, IResource destination, int depth) throws CoreException {
+ copyProperties(source.getFullPath(), destination.getFullPath(), depth);
+ }
+
+ /**
+ * Copies all properties from the source path to the target path, to the given depth.
+ */
+ private void copyProperties(final IPath source, final IPath destination, int depth) throws CoreException {
+ Assert.isLegal(source.segmentCount() > 0);
+ Assert.isLegal(destination.segmentCount() > 0);
+ Assert.isLegal(source.segmentCount() > 1 || destination.segmentCount() == 1);
+
+ // copy history by visiting the source tree
+ PropertyCopyVisitor copyVisitor = new PropertyCopyVisitor(source, destination);
+ tree.accept(copyVisitor, source, BucketTree.DEPTH_INFINITE);
+ }
+
+ public synchronized void deleteProperties(IResource target, int depth) throws CoreException {
+ tree.accept(new PropertyBucket.Visitor() {
+ public int visit(Entry entry) {
+ entry.delete();
+ return CONTINUE;
+ }
+ }, target.getFullPath(), depth == IResource.DEPTH_INFINITE ? BucketTree.DEPTH_INFINITE : depth);
+ }
+
+ public void deleteResource(IResource target) throws CoreException {
+ deleteProperties(target, IResource.DEPTH_INFINITE);
+ }
+
+ public synchronized Map getProperties(IResource target) throws CoreException {
+ final Map result = new HashMap();
+ tree.accept(new PropertyBucket.Visitor() {
+ public int visit(Entry entry) {
+ PropertyEntry propertyEntry = (PropertyEntry) entry;
+ int propertyCount = propertyEntry.getOccurrences();
+ for (int i = 0; i < propertyCount; i++)
+ result.put(propertyEntry.getPropertyName(i), propertyEntry.getPropertyValue(i));
+ return CONTINUE;
+ }
+ }, target.getFullPath(), BucketTree.DEPTH_ZERO);
+ return result;
+ }
+
+ public synchronized String getProperty(IResource target, QualifiedName name) throws CoreException {
+ if (name.getQualifier() == null) {
+ String message = Messages.properties_qualifierIsNull;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, target.getFullPath(), message, null);
+ }
+ IPath resourcePath = target.getFullPath();
+ PropertyBucket current = (PropertyBucket) tree.getCurrent();
+ tree.loadBucketFor(resourcePath);
+ return current.getProperty(resourcePath, name);
+ }
+
+ public BucketTree getTree() {
+ return tree;
+ }
+
+ public File getVersionFile() {
+ return tree.getVersionFile();
+ }
+
+ public synchronized void setProperty(IResource target, QualifiedName name, String value) throws CoreException {
+ //resource may have been deleted concurrently
+ //must check for existence within synchronized method
+ Resource resource = (Resource) target;
+ ResourceInfo info = resource.getResourceInfo(false, false);
+ int flags = resource.getFlags(info);
+ resource.checkAccessible(flags);
+ // enforce the limit stated by the spec
+ if (value != null && value.length() > 2 * 1024) {
+ String message = NLS.bind(Messages.properties_valueTooLong, name.getQualifier(), name.getLocalName());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), message, null);
+ }
+ if (name.getQualifier() == null) {
+ String message = Messages.properties_qualifierIsNull;
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), message, null);
+ }
+
+ IPath resourcePath = target.getFullPath();
+ tree.loadBucketFor(resourcePath);
+ PropertyBucket current = (PropertyBucket) tree.getCurrent();
+ current.setProperty(resourcePath, name, value);
+ current.save();
+ }
+
+ public void shutdown(IProgressMonitor monitor) throws CoreException {
+ tree.close();
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ // nothing to do
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/propertytester/FilePropertyTester.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.propertytester;
+
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentType;
+
+/**
+ * A property tester for various properties of files.
+ *
+ * @since 3.2
+ */
+public class FilePropertyTester extends ResourcePropertyTester {
+
+ /**
+ * A property indicating that we are looking to verify that the file matches
+ * the content type matching the given identifier. The identifier is
+ * provided as the expected value.
+ */
+ private static final String CONTENT_TYPE_ID = "contentTypeId"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.internal.resources.ResourcePropertyTester#test(java.lang.Object,
+ * java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ public boolean test(Object receiver, String method, Object[] args, Object expectedValue) {
+ if ((receiver instanceof IFile) && method.equals(CONTENT_TYPE_ID))
+ return testContentType((IFile) receiver, toString(expectedValue));
+ return false;
+ }
+
+ /**
+ * Tests whether the content type for <code>file</code> matches the
+ * <code>contentTypeId</code>. It is possible that this method call could
+ * cause the file to be read. It is also possible (through poor plug-in
+ * design) for this method to load plug-ins.
+ *
+ * @param file
+ * The file for which the content type should be determined; must
+ * not be <code>null</code>.
+ * @param contentTypeId
+ * The expected content type; must not be <code>null</code>.
+ * @return <code>true</code> iff the best matching content type has an
+ * identifier that matches <code>contentTypeId</code>;
+ * <code>false</code> otherwise.
+ */
+ private boolean testContentType(final IFile file, String contentTypeId) {
+ final String expectedValue = contentTypeId.trim();
+
+ String actualValue = null;
+ try {
+ IContentDescription contentDescription = file.getContentDescription();
+ if (contentDescription != null) {
+ IContentType contentType = contentDescription.getContentType();
+ actualValue = contentType.getId();
+ }
+ } catch (CoreException e) {
+ Policy.log(IStatus.ERROR, "Core exception while retrieving the content description", e);//$NON-NLS-1$
+ }
+ return expectedValue.equals(actualValue);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/propertytester/ProjectPropertyTester.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.propertytester;
+
+import org.eclipse.core.resources.IProject;
+
+/**
+ * A property tester for various properties of projects.
+ *
+ * @since 3.2
+ */
+public class ProjectPropertyTester extends ResourcePropertyTester {
+
+ /**
+ * A property indicating whether the project is open (value <code>"open"</code>).
+ */
+ private static final String OPEN = "open"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.internal.resources.ResourcePropertyTester#test(java.lang.Object,
+ * java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ public boolean test(Object receiver, String method, Object[] args, Object expectedValue) {
+ if ((receiver instanceof IProject) && method.equals(OPEN))
+ return ((IProject) receiver).isOpen() == toBoolean(expectedValue);
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/propertytester/ResourceMappingPropertyTester.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.propertytester;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * A property tester for various properties of resource mappings
+ *
+ * @since 3.2
+ */
+public class ResourceMappingPropertyTester extends ResourcePropertyTester {
+ public boolean test(Object receiver, String method, Object[] args, Object expectedValue) {
+ if (!(receiver instanceof ResourceMapping))
+ return false;
+ if (!method.equals(PROJECT_PERSISTENT_PROPERTY))
+ return false;
+ //Note: we currently say the test is satisfied if any project associated
+ //with the mapping satisfies the test.
+ IProject[] projects = ((ResourceMapping) receiver).getProjects();
+ if (projects.length == 0)
+ return false;
+ String propertyName;
+ String expectedVal;
+ if (args.length == 0) {
+ propertyName = toString(expectedValue);
+ expectedVal = null;//any value will do
+ } else if (args.length == 1) {
+ propertyName = toString(args[0]);
+ expectedVal = null;//any value will do
+ } else {
+ propertyName = toString(args[0]);
+ expectedVal = toString(args[1]);
+ }
+ QualifiedName key = toQualifedName(propertyName);
+ boolean found = false;
+ for (int i = 0; i < projects.length; i++) {
+ try {
+ Object actualVal = projects[i].getPersistentProperty(key);
+ //the value is not set, so keep looking on other projects
+ if (actualVal == null)
+ continue;
+ //record that we have found at least one value
+ found = true;
+ //expected value of null means we expect *any* value, rather than expecting no value
+ if (expectedVal == null)
+ continue;
+ //if the value we find does not match, then the property is not satisfied
+ if (!expectedVal.equals(actualVal.toString()))
+ return false;
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+ //if any projects had the property set, the condition is satisfied
+ return found;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/propertytester/ResourcePropertyTester.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.propertytester;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * A property tester for various properties of resources.
+ *
+ * @since 3.2
+ */
+public class ResourcePropertyTester extends PropertyTester {
+
+ /**
+ * A property indicating the file extension (value <code>"extension"</code>).
+ * "*" and "?" wild cards are supported.
+ */
+ protected static final String EXTENSION = "extension"; //$NON-NLS-1$
+
+ /**
+ * A property indicating the file name (value <code>"name"</code>). "*"
+ * and "?" wild cards are supported.
+ */
+ protected static final String NAME = "name"; //$NON-NLS-1$
+
+ /**
+ * A property indicating the file path (value <code>"path"</code>). "*"
+ * and "?" wild cards are supported.
+ */
+ protected static final String PATH = "path"; //$NON-NLS-1$
+
+ /**
+ * A property indicating a persistent property on the selected resource
+ * (value <code>"persistentProperty"</code>). If two arguments are given,
+ * this treats the first as the property name, and the second as the expected
+ * property value. If only one argument (or just the expected value) is
+ * given, this treats it as the property name, and simply tests for existence of
+ * the property on the resource.
+ */
+ protected static final String PERSISTENT_PROPERTY = "persistentProperty"; //$NON-NLS-1$
+
+ /**
+ * A property indicating the project nature (value
+ * <code>"projectNature"</code>).
+ */
+ protected static final String PROJECT_NATURE = "projectNature"; //$NON-NLS-1$
+
+ /**
+ * A property indicating a persistent property on the selected resource's
+ * project. (value <code>"projectPersistentProperty"</code>). If two
+ * arguments are given, this treats the first as the property name, and the
+ * second as the expected property value. If only one argument (or just the
+ * expected value) is given, this treats it as the property name, and simply
+ * tests for existence of the property on the resource.
+ */
+ protected static final String PROJECT_PERSISTENT_PROPERTY = "projectPersistentProperty"; //$NON-NLS-1$
+
+ /**
+ * A property indicating a session property on the selected resource's
+ * project. (value <code>"projectSessionProperty"</code>). If two
+ * arguments are given, this treats the first as the property name, and the
+ * second as the expected property value. If only one argument (or just the
+ * expected value) is given, this treats it as the property name, and simply
+ * tests for existence of the property on the resource.
+ */
+ protected static final String PROJECT_SESSION_PROPERTY = "projectSessionProperty"; //$NON-NLS-1$
+
+ /**
+ * A property indicating whether the file is read only (value
+ * <code>"readOnly"</code>).
+ */
+ protected static final String READ_ONLY = "readOnly"; //$NON-NLS-1$
+
+ /**
+ * A property indicating a session property on the selected resource (value
+ * <code>"sessionProperty"</code>). If two arguments are given, this
+ * treats the first as the property name, and the second as the expected
+ * property value. If only one argument (or just the expected value) is
+ * given, this treats it as the property name, and simply tests for existence of
+ * the property on the resource.
+ */
+ protected static final String SESSION_PROPERTY = "sessionProperty"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object,
+ * java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ public boolean test(Object receiver, String method, Object[] args, Object expectedValue) {
+ if (!(receiver instanceof IResource))
+ return false;
+ IResource res = (IResource) receiver;
+ if (method.equals(NAME)) {
+ return new StringMatcher(toString(expectedValue)).match(res.getName());
+ } else if (method.equals(PATH)) {
+ return new StringMatcher(toString(expectedValue)).match(res.getFullPath().toString());
+ } else if (method.equals(EXTENSION)) {
+ return new StringMatcher(toString(expectedValue)).match(res.getFileExtension());
+ } else if (method.equals(READ_ONLY)) {
+ ResourceAttributes attr = res.getResourceAttributes();
+ return (attr != null && attr.isReadOnly()) == toBoolean(expectedValue);
+ } else if (method.equals(PROJECT_NATURE)) {
+ try {
+ IProject proj = res.getProject();
+ return proj != null && proj.isAccessible() && proj.hasNature(toString(expectedValue));
+ } catch (CoreException e) {
+ return false;
+ }
+ } else if (method.equals(PERSISTENT_PROPERTY)) {
+ return testProperty(res, true, args, expectedValue);
+ } else if (method.equals(PROJECT_PERSISTENT_PROPERTY)) {
+ return testProperty(res.getProject(), true, args, expectedValue);
+ } else if (method.equals(SESSION_PROPERTY)) {
+ return testProperty(res, false, args, expectedValue);
+ } else if (method.equals(PROJECT_SESSION_PROPERTY)) {
+ return testProperty(res.getProject(), false, args, expectedValue);
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether a session or persistent property on the resource or its
+ * project matches the given value.
+ *
+ * @param resource
+ * the resource to check
+ * @param persistentFlag
+ * <code>true</code> for a persistent property,
+ * <code>false</code> for a session property
+ * @param args
+ * additional arguments to evaluate the property.
+ * If of length 0, this treats the expectedValue as the property name
+ * and does a simple check for existence of the property.
+ * If of length 1, this treats the first argument as the property name
+ * and does a simple check for existence of the property.
+ * If of length 2, this treats the first argument as the property name,
+ * the second argument as the expected value, and checks for equality
+ * with the actual property value.
+ * @param expectedValue
+ * used only if args is of length 0 (see Javadoc for args parameter)
+ * @return whether there is a match
+ */
+ protected boolean testProperty(IResource resource, boolean persistentFlag, Object[] args, Object expectedValue) {
+ //the project of IWorkspaceRoot is null
+ if (resource == null)
+ return false;
+ String propertyName;
+ String expectedVal;
+ if (args.length == 0) {
+ propertyName = toString(expectedValue);
+ expectedVal = null;
+ } else if (args.length == 1) {
+ propertyName = toString(args[0]);
+ expectedVal = null;
+ } else {
+ propertyName = toString(args[0]);
+ expectedVal = toString(args[1]);
+ }
+ try {
+ QualifiedName key = toQualifedName(propertyName);
+ Object actualVal = persistentFlag ? resource.getPersistentProperty(key) : resource.getSessionProperty(key);
+ if (actualVal == null)
+ return false;
+ return expectedVal == null || expectedVal.equals(actualVal.toString());
+ } catch (CoreException e) {
+ //if the resource is not accessible, fall through and return false below
+ }
+ return false;
+ }
+
+ /**
+ * Converts the given expected value to a boolean.
+ *
+ * @param expectedValue
+ * the expected value (may be <code>null</code>).
+ * @return <code>false</code> if the expected value equals Boolean.FALSE,
+ * <code>true</code> otherwise
+ */
+ protected boolean toBoolean(Object expectedValue) {
+ if (expectedValue instanceof Boolean) {
+ return ((Boolean) expectedValue).booleanValue();
+ }
+ return true;
+ }
+
+ /**
+ * Converts the given name to a qualified name.
+ *
+ * @param name the name
+ * @return the qualified name
+ */
+ protected QualifiedName toQualifedName(String name) {
+ QualifiedName key;
+ int dot = name.lastIndexOf('.');
+ if (dot != -1) {
+ key = new QualifiedName(name.substring(0, dot), name.substring(dot + 1));
+ } else {
+ key = new QualifiedName(null, name);
+ }
+ return key;
+ }
+
+ /**
+ * Converts the given expected value to a <code>String</code>.
+ *
+ * @param expectedValue
+ * the expected value (may be <code>null</code>).
+ * @return the empty string if the expected value is <code>null</code>,
+ * otherwise the <code>toString()</code> representation of the
+ * expected value
+ */
+ protected String toString(Object expectedValue) {
+ return expectedValue == null ? "" : expectedValue.toString(); //$NON-NLS-1$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/propertytester/StringMatcher.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.propertytester;
+
+import java.util.ArrayList;
+
+/**
+ * A string pattern matcher, supporting "*" and "?" wild cards.
+ *
+ * @since 3.2
+ */
+public class StringMatcher {
+ private static final char SINGLE_WILD_CARD = '\u0000';
+
+ /**
+ * Boundary value beyond which we don't need to search in the text
+ */
+ private int bound = 0;
+
+ private boolean hasLeadingStar;
+
+ private boolean hasTrailingStar;
+
+ private final String pattern;
+
+ private final int patternLength;
+
+ /**
+ * The pattern split into segments separated by *
+ */
+ private String segments[];
+
+ /**
+ * StringMatcher constructor takes in a String object that is a simple
+ * pattern which may contain '*' for 0 and many characters and
+ * '?' for exactly one character.
+ *
+ * Literal '*' and '?' characters must be escaped in the pattern
+ * e.g., "\*" means literal "*", etc.
+ *
+ * Escaping any other character (including the escape character itself),
+ * just results in that character in the pattern.
+ * e.g., "\a" means "a" and "\\" means "\"
+ *
+ * If invoking the StringMatcher with string literals in Java, don't forget
+ * escape characters are represented by "\\".
+ *
+ * @param pattern the pattern to match text against
+ */
+ public StringMatcher(String pattern) {
+ if (pattern == null)
+ throw new IllegalArgumentException();
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ parseWildCards();
+ }
+
+ /**
+ * @param text a simple regular expression that may only contain '?'(s)
+ * @param start the starting index in the text for search, inclusive
+ * @param end the stopping point of search, exclusive
+ * @param p a simple regular expression that may contain '?'
+ * @return the starting index in the text of the pattern , or -1 if not found
+ */
+ private int findPosition(String text, int start, int end, String p) {
+ boolean hasWildCard = p.indexOf(SINGLE_WILD_CARD) >= 0;
+ int plen = p.length();
+ for (int i = start, max = end - plen; i <= max; ++i) {
+ if (hasWildCard) {
+ if (regExpRegionMatches(text, i, p, 0, plen))
+ return i;
+ } else {
+ if (text.regionMatches(true, i, p, 0, plen))
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Given the starting (inclusive) and the ending (exclusive) positions in the
+ * <code>text</code>, determine if the given substring matches with aPattern
+ * @return true if the specified portion of the text matches the pattern
+ * @param text a String object that contains the substring to match
+ */
+ public boolean match(String text) {
+ if (text == null)
+ return false;
+ final int end = text.length();
+ final int segmentCount = segments.length;
+ if (segmentCount == 0 && (hasLeadingStar || hasTrailingStar)) // pattern contains only '*'(s)
+ return true;
+ if (end == 0)
+ return patternLength == 0;
+ if (patternLength == 0)
+ return false;
+ int currentTextPosition = 0;
+ if ((end - bound) < 0)
+ return false;
+ int segmentIndex = 0;
+ String current = segments[segmentIndex];
+
+ /* process first segment */
+ if (!hasLeadingStar) {
+ int currentLength = current.length();
+ if (!regExpRegionMatches(text, 0, current, 0, currentLength))
+ return false;
+ segmentIndex++;
+ currentTextPosition = currentTextPosition + currentLength;
+ }
+ if ((segmentCount == 1) && (!hasLeadingStar) && (!hasTrailingStar)) {
+ // only one segment to match, no wild cards specified
+ return currentTextPosition == end;
+ }
+ /* process middle segments */
+ while (segmentIndex < segmentCount) {
+ current = segments[segmentIndex];
+ int currentMatch = findPosition(text, currentTextPosition, end, current);
+ if (currentMatch < 0)
+ return false;
+ currentTextPosition = currentMatch + current.length();
+ segmentIndex++;
+ }
+
+ /* process final segment */
+ if (!hasTrailingStar && currentTextPosition != end) {
+ int currentLength = current.length();
+ return regExpRegionMatches(text, end - currentLength, current, 0, currentLength);
+ }
+ return segmentIndex == segmentCount;
+ }
+
+ /**
+ * Parses the pattern into segments separated by wildcard '*' characters.
+ */
+ private void parseWildCards() {
+ if (pattern.startsWith("*"))//$NON-NLS-1$
+ hasLeadingStar = true;
+ if (pattern.endsWith("*")) {//$NON-NLS-1$
+ /* make sure it's not an escaped wildcard */
+ if (patternLength > 1 && pattern.charAt(patternLength - 2) != '\\') {
+ hasTrailingStar = true;
+ }
+ }
+
+ ArrayList temp = new ArrayList();
+
+ int pos = 0;
+ StringBuffer buf = new StringBuffer();
+ while (pos < patternLength) {
+ char c = pattern.charAt(pos++);
+ switch (c) {
+ case '\\' :
+ if (pos >= patternLength) {
+ buf.append(c);
+ } else {
+ char next = pattern.charAt(pos++);
+ /* if it's an escape sequence */
+ if (next == '*' || next == '?' || next == '\\') {
+ buf.append(next);
+ } else {
+ /* not an escape sequence, just insert literally */
+ buf.append(c);
+ buf.append(next);
+ }
+ }
+ break;
+ case '*' :
+ if (buf.length() > 0) {
+ /* new segment */
+ temp.add(buf.toString());
+ bound += buf.length();
+ buf.setLength(0);
+ }
+ break;
+ case '?' :
+ /* append special character representing single match wildcard */
+ buf.append(SINGLE_WILD_CARD);
+ break;
+ default :
+ buf.append(c);
+ }
+ }
+
+ /* add last buffer to segment list */
+ if (buf.length() > 0) {
+ temp.add(buf.toString());
+ bound += buf.length();
+ }
+ segments = (String[]) temp.toArray(new String[temp.size()]);
+ }
+
+ /**
+ *
+ * @return boolean
+ * @param text a String to match
+ * @param tStart the starting index of match, inclusive
+ * @param p a simple regular expression that may contain '?'
+ * @param pStart The start position in the pattern
+ * @param plen The length of the pattern
+ */
+ private boolean regExpRegionMatches(String text, int tStart, String p, int pStart, int plen) {
+ while (plen-- > 0) {
+ char tchar = text.charAt(tStart++);
+ char pchar = p.charAt(pStart++);
+
+ // process wild cards, skipping single wild cards
+ if (pchar == SINGLE_WILD_CARD)
+ continue;
+ if (pchar == tchar)
+ continue;
+ if (Character.toUpperCase(tchar) == Character.toUpperCase(pchar))
+ continue;
+ // comparing after converting to upper case doesn't handle all cases;
+ // also compare after converting to lower case
+ if (Character.toLowerCase(tchar) == Character.toLowerCase(pchar))
+ continue;
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/InternalRefreshProvider.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.refresh;
+
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.refresh.IRefreshMonitor;
+
+/**
+ * Internal abstract superclass of all refresh providers. This class must not be
+ * subclassed directly by clients. All refresh providers must subclass the public
+ * API class <code>org.eclipse.core.resources.refresh.RefreshProvider</code>.
+ *
+ * @since 3.0
+ */
+public class InternalRefreshProvider {
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.refresh.RefreshProvider#createPollingMonitor(IResource)
+ */
+ protected IRefreshMonitor createPollingMonitor(IResource resource) {
+ PollingMonitor monitor = ((Workspace)resource.getWorkspace()).getRefreshManager().monitors.pollMonitor;
+ monitor.monitor(resource);
+ return monitor;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.refresh.RefreshProvider#resetMonitors(IResource)
+ */
+ public void resetMonitors(IResource resource) {
+ MonitorManager manager = ((Workspace)resource.getWorkspace()).getRefreshManager().monitors;
+ manager.unmonitor(resource);
+ manager.monitor(resource);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/MonitorManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.refresh;
+
+import java.util.*;
+import org.eclipse.core.internal.events.ILifecycleListener;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.refresh.IRefreshMonitor;
+import org.eclipse.core.resources.refresh.RefreshProvider;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Manages monitors by creating new monitors when projects are added and
+ * removing monitors when projects are removed. Also handles the polling
+ * mechanism when contributed native monitors cannot handle a project.
+ *
+ * @since 3.0
+ */
+class MonitorManager implements ILifecycleListener, IPathVariableChangeListener, IResourceChangeListener, IResourceDeltaVisitor {
+ /**
+ * The PollingMonitor in charge of doing file-system polls.
+ */
+ protected final PollingMonitor pollMonitor;
+ /**
+ * The list of registered monitor factories.
+ */
+ private RefreshProvider[] providers;
+ /**
+ * Reference to the refresh manager.
+ */
+ protected final RefreshManager refreshManager;
+ /**
+ * A mapping of monitors to a list of resources each monitor is responsible for.
+ */
+ protected final Map registeredMonitors;
+ /**
+ * Reference to the workspace.
+ */
+ protected IWorkspace workspace;
+
+ public MonitorManager(IWorkspace workspace, RefreshManager refreshManager) {
+ this.workspace = workspace;
+ this.refreshManager = refreshManager;
+ registeredMonitors = Collections.synchronizedMap(new HashMap(10));
+ pollMonitor = new PollingMonitor(refreshManager);
+ }
+
+ /**
+ * Queries extensions of the refreshProviders extension point, and
+ * creates the provider classes. Will never return <code>null</code>.
+ *
+ * @return RefreshProvider[] The array of registered <code>RefreshProvider</code>
+ * objects or an empty array.
+ */
+ private RefreshProvider[] getRefreshProviders() {
+ if (providers != null)
+ return providers;
+ IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_REFRESH_PROVIDERS);
+ IConfigurationElement[] infos = extensionPoint.getConfigurationElements();
+ List providerList = new ArrayList(infos.length);
+ for (int i = 0; i < infos.length; i++) {
+ IConfigurationElement configurationElement = infos[i];
+ RefreshProvider provider = null;
+ try {
+ provider = (RefreshProvider) configurationElement.createExecutableExtension("class"); //$NON-NLS-1$
+ } catch (CoreException e) {
+ Policy.log(IStatus.WARNING, Messages.refresh_installError, e);
+ }
+ if (provider != null)
+ providerList.add(provider);
+ }
+ providers = (RefreshProvider[]) providerList.toArray(new RefreshProvider[providerList.size()]);
+ return providers;
+ }
+
+ /**
+ * Collects the set of root resources that required monitoring. This
+ * includes projects and all linked resources.
+ */
+ private List getResourcesToMonitor() {
+ final List resourcesToMonitor = new ArrayList(10);
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ if (!projects[i].isAccessible())
+ continue;
+ resourcesToMonitor.add(projects[i]);
+ try {
+ IResource[] members = projects[i].members();
+ for (int j = 0; j < members.length; j++)
+ if (members[j].isLinked())
+ resourcesToMonitor.add(members[j]);
+ } catch (CoreException e) {
+ Policy.log(IStatus.WARNING, Messages.refresh_refreshErr, e);
+ }
+ }
+ return resourcesToMonitor;
+ }
+
+ public void handleEvent(LifecycleEvent event) {
+ switch (event.kind) {
+ case LifecycleEvent.PRE_LINK_DELETE:
+ case LifecycleEvent.PRE_PROJECT_CLOSE:
+ case LifecycleEvent.PRE_PROJECT_DELETE:
+ unmonitor(event.resource);
+ break;
+ }
+ }
+
+ private boolean isMonitoring(IResource resource) {
+ synchronized (registeredMonitors) {
+ for (Iterator i = registeredMonitors.keySet().iterator(); i.hasNext();) {
+ List resources = (List) registeredMonitors.get(i.next());
+ if ((resources != null) && (resources.contains(resource)))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Installs a monitor on the given resource. Returns true if the polling
+ * monitor was installed, and false if a refresh provider was installed.
+ */
+ boolean monitor(IResource resource) {
+ if (isMonitoring(resource))
+ return false;
+ boolean pollingMonitorNeeded = true;
+ RefreshProvider[] refreshProviders = getRefreshProviders();
+ for (int i = 0; i < refreshProviders.length; i++) {
+ IRefreshMonitor monitor = safeInstallMonitor(refreshProviders[i], resource);
+ if (monitor != null) {
+ registerMonitor(monitor, resource);
+ pollingMonitorNeeded = false;
+ }
+ }
+ if (pollingMonitorNeeded) {
+ pollMonitor.monitor(resource);
+ registerMonitor(pollMonitor, resource);
+ }
+ return pollingMonitorNeeded;
+ }
+
+ /* (non-Javadoc)
+ * @see IRefreshResult#monitorFailed
+ */
+ public void monitorFailed(IRefreshMonitor monitor, IResource resource) {
+ if (RefreshManager.DEBUG)
+ System.err.println(RefreshManager.DEBUG_PREFIX + " monitor (" + monitor + ") failed to monitor resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$
+ if (registeredMonitors == null || monitor == null)
+ return;
+ if (resource == null) {
+ List resources = (List) registeredMonitors.get(monitor);
+ if (resources == null || resources.isEmpty()) {
+ registeredMonitors.remove(monitor);
+ return;
+ }
+ // synchronized: protect the collection during iteration
+ synchronized (registeredMonitors) {
+ for (Iterator i = resources.iterator(); i.hasNext();) {
+ resource = (IResource) i.next();
+ pollMonitor.monitor(resource);
+ registerMonitor(pollMonitor, resource);
+ }
+ registeredMonitors.remove(monitor);
+ }
+ } else {
+ removeMonitor(monitor, resource);
+ pollMonitor.monitor(resource);
+ registerMonitor(pollMonitor, resource);
+ }
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableChangeListener#pathVariableChanged(org.eclipse.core.resources.IPathVariableChangeEvent)
+ */
+ public void pathVariableChanged(IPathVariableChangeEvent event) {
+ if (registeredMonitors.isEmpty())
+ return;
+ String variableName = event.getVariableName();
+ Set invalidResources = new HashSet();
+ for (Iterator i = registeredMonitors.values().iterator(); i.hasNext();) {
+ for (Iterator j = ((List) i.next()).iterator(); j.hasNext();) {
+ IResource resource = (IResource) j.next();
+ IPath rawLocation = resource.getRawLocation();
+ if (rawLocation != null) {
+ if (rawLocation.segmentCount() > 0 && variableName.equals(rawLocation.segment(0)) && !invalidResources.contains(resource)) {
+ invalidResources.add(resource);
+ }
+ }
+ }
+ }
+ if (!invalidResources.isEmpty()) {
+ for (Iterator i = invalidResources.iterator(); i.hasNext();) {
+ IResource resource = (IResource) i.next();
+ unmonitor(resource);
+ monitor(resource);
+ }
+ }
+ }
+
+ private void registerMonitor(IRefreshMonitor monitor, IResource resource) {
+ // synchronized: protect the collection during add
+ synchronized (registeredMonitors) {
+ List resources = (List) registeredMonitors.get(monitor);
+ if (resources == null) {
+ resources = new ArrayList(1);
+ registeredMonitors.put(monitor, resources);
+ }
+ if (!resources.contains(resource))
+ resources.add(resource);
+ }
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " added monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private void removeMonitor(IRefreshMonitor monitor, IResource resource) {
+ // synchronized: protect the collection during remove
+ synchronized (registeredMonitors) {
+ List resources = (List) registeredMonitors.get(monitor);
+ if (resources != null && !resources.isEmpty())
+ resources.remove(resource);
+ else
+ registeredMonitors.remove(monitor);
+ }
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " removing monitor (" + monitor + ") on resource: " + resource); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private IRefreshMonitor safeInstallMonitor(RefreshProvider provider, IResource resource) {
+ Throwable t = null;
+ try {
+ return provider.installMonitor(resource, refreshManager);
+ } catch (Exception e) {
+ t = e;
+ } catch (LinkageError e) {
+ t = e;
+ }
+ IStatus error = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, Messages.refresh_installError, t);
+ Policy.log(error);
+ return null;
+ }
+
+ /**
+ * Start the monitoring of resources by all monitors.
+ */
+ public void start() {
+ boolean refreshNeeded = false;
+ for (Iterator i = getResourcesToMonitor().iterator(); i.hasNext();)
+ refreshNeeded |= !monitor((IResource) i.next());
+ workspace.getPathVariableManager().addChangeListener(this);
+ workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+ //adding the lifecycle listener twice does no harm
+ ((Workspace)workspace).addLifecycleListener(this);
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " starting monitor manager."); //$NON-NLS-1$
+ //If not exclusively using polling, create a polling monitor and run it once, to catch
+ //changes that occurred while the native monitor was turned off.
+ if (refreshNeeded)
+ new PollingMonitor(refreshManager).runOnce();
+ }
+
+ /**
+ * Stop the monitoring of resources by all monitors.
+ */
+ public void stop() {
+ workspace.removeResourceChangeListener(this);
+ workspace.getPathVariableManager().removeChangeListener(this);
+ // synchronized: protect the collection during iteration
+ synchronized (registeredMonitors) {
+ for (Iterator i = registeredMonitors.keySet().iterator(); i.hasNext();) {
+ IRefreshMonitor monitor = (IRefreshMonitor) i.next();
+ monitor.unmonitor(null);
+ }
+ }
+ registeredMonitors.clear();
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " stopping monitor manager."); //$NON-NLS-1$
+ pollMonitor.cancel();
+ }
+
+ void unmonitor(IResource resource) {
+ if (resource == null || !isMonitoring(resource))
+ return;
+ synchronized (registeredMonitors) {
+ for (Iterator i = registeredMonitors.entrySet().iterator(); i.hasNext();) {
+ Map.Entry current = (Map.Entry) i.next();
+ List resources = (List) current.getValue();
+ if ((resources != null) && !resources.isEmpty() && resources.contains(resource)) {
+ ((IRefreshMonitor) current.getKey()).unmonitor(resource);
+ resources.remove(resource);
+ }
+ }
+ }
+ if (resource.getType() == IResource.PROJECT)
+ unmonitorLinkedContents((IProject)resource);
+ }
+
+ private void unmonitorLinkedContents(IProject project) {
+ if (!project.isAccessible())
+ return;
+ IResource[] children = null;
+ try {
+ children = project.members();
+ } catch (CoreException e) {
+ Policy.log(IStatus.WARNING, Messages.refresh_refreshErr, e);
+ }
+ if (children != null && children.length > 0)
+ for (int i = 0; i < children.length; i++)
+ if (children[i].isLinked())
+ unmonitor(children[i]);
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ IResourceDelta delta = event.getDelta();
+ if (delta == null)
+ return;
+ try {
+ delta.accept(this);
+ } catch (CoreException e) {
+ //cannot happen as our visitor doesn't throw exceptions
+ }
+ }
+
+ public boolean visit(IResourceDelta delta) {
+ if (delta.getKind () == IResourceDelta.ADDED) {
+ IResource resource = delta.getResource();
+ if (resource.isLinked())
+ monitor(resource);
+ }
+ if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
+ IProject project = (IProject) delta.getResource();
+ if (project.isAccessible())
+ monitor(project);
+ }
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/PollingMonitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.refresh;
+
+import java.util.ArrayList;
+import org.eclipse.core.internal.resources.Resource;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.refresh.IRefreshMonitor;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.framework.Bundle;
+
+/**
+ * The <code>PollingMonitor</code> is an <code>IRefreshMonitor</code> that
+ * polls the file system rather than registering natively for call-backs.
+ *
+ * The polling monitor operates in iterations that span multiple invocations
+ * of the job's run method. At the beginning of an iteration, a set of
+ * all resource roots is collected. Each time the job runs, it removes items
+ * from the set and searches for changes for a fixed period of time.
+ * This ensures that the refresh job is broken into very small discrete
+ * operations that do not interrupt the user's main-line activity.
+ *
+ * @since 3.0
+ */
+public class PollingMonitor extends Job implements IRefreshMonitor {
+ /**
+ * The maximum duration of a single polling iteration
+ */
+ private static final long MAX_DURATION = 250;
+ /**
+ * The amount of time that a changed root should remain hot.
+ */
+ private static final long HOT_ROOT_DECAY = 90000;
+ /**
+ * The minimum delay between executions of the polling monitor
+ */
+ private static final long MIN_FREQUENCY = 4000;
+ /**
+ * The roots of resources which should be polled
+ */
+ private final ArrayList resourceRoots;
+ /**
+ * The resources remaining to be refreshed in this iteration
+ */
+ private final ArrayList toRefresh;
+ /**
+ * The root that has most recently been out of sync
+ */
+ private IResource hotRoot;
+ /**
+ * The time the hot root was last refreshed
+ */
+ private long hotRootTime;
+
+ private final RefreshManager refreshManager;
+ /**
+ * True if this job has never been run. False otherwise.
+ */
+ private boolean firstRun = true;
+
+ /**
+ * Creates a new polling monitor.
+ */
+ public PollingMonitor(RefreshManager manager) {
+ super(Messages.refresh_pollJob);
+ this.refreshManager = manager;
+ setPriority(Job.DECORATE);
+ setSystem(true);
+ resourceRoots = new ArrayList();
+ toRefresh = new ArrayList();
+ }
+
+ /**
+ * Add the given root to the list of roots that need to be polled.
+ */
+ public synchronized void monitor(IResource root) {
+ resourceRoots.add(root);
+ schedule(MIN_FREQUENCY);
+ }
+
+ /**
+ * Polls the file system under the root containers for changes.
+ */
+ protected IStatus run(IProgressMonitor monitor) {
+ //sleep until resources plugin has finished starting
+ if (firstRun) {
+ firstRun = false;
+ Bundle bundle = Platform.getBundle(ResourcesPlugin.PI_RESOURCES);
+ long waitStart = System.currentTimeMillis();
+ while (bundle.getState() == Bundle.STARTING) {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ //don't wait forever
+ if ((System.currentTimeMillis() - waitStart) > 90000)
+ break;
+ }
+ }
+ long time = System.currentTimeMillis();
+ //check to see if we need to start an iteration
+ if (toRefresh.isEmpty()) {
+ beginIteration();
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + "New polling iteration on " + toRefresh.size() + " roots"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ final int oldSize = toRefresh.size();
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + "started polling"); //$NON-NLS-1$
+ //refresh the hot root if applicable
+ if (time - hotRootTime > HOT_ROOT_DECAY)
+ hotRoot = null;
+ else if (hotRoot != null && !monitor.isCanceled())
+ poll(hotRoot);
+ //process roots that have not yet been refreshed this iteration
+ final long loopStart = System.currentTimeMillis();
+ while (!toRefresh.isEmpty()) {
+ if (monitor.isCanceled())
+ break;
+ poll((IResource) toRefresh.remove(toRefresh.size() - 1));
+ //stop the iteration if we have exceed maximum duration
+ if (System.currentTimeMillis() - loopStart > MAX_DURATION)
+ break;
+ }
+ time = System.currentTimeMillis() - time;
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + "polled " + (oldSize - toRefresh.size()) + " roots in " + time + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ //reschedule automatically - shouldRun will cancel if not needed
+ //make sure it doesn't run more than 5% of the time
+ long delay = Math.max(MIN_FREQUENCY, time * 20);
+ //back off even more if there are other jobs running
+ if (!getJobManager().isIdle())
+ delay *= 2;
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + "rescheduling polling job in: " + delay / 1000 + " seconds"); //$NON-NLS-1$ //$NON-NLS-2$
+ //don't reschedule the job if the resources plugin has been shut down
+ if (Platform.getBundle(ResourcesPlugin.PI_RESOURCES).getState() == Bundle.ACTIVE)
+ schedule(delay);
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Instructs the polling job to do one complete iteration of all workspace roots, and
+ * then discard itself. This is used when
+ * the refresh manager is first turned on if there is a native monitor installed (which
+ * don't handle changes that occurred while the monitor was turned off).
+ */
+ void runOnce() {
+ synchronized (this) {
+ //add all roots to the refresh list, but not to the real set of roots
+ //this will cause the job to never run again once it has exhausted
+ //the set of roots to refresh
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ toRefresh.add(projects[i]);
+ }
+ schedule(MIN_FREQUENCY);
+ }
+
+ private void poll(IResource resource) {
+ if (resource.isSynchronized(IResource.DEPTH_INFINITE))
+ return;
+ //don't refresh links with no local content
+ if (resource.isLinked() && !((Resource) resource).getStore().fetchInfo().exists())
+ return;
+ //submit refresh request
+ refreshManager.refresh(resource);
+ hotRoot = resource;
+ hotRootTime = System.currentTimeMillis();
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + "new hot root: " + resource); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see Job#shouldRun
+ */
+ public boolean shouldRun() {
+ //only run if there is something to refresh
+ return !resourceRoots.isEmpty() || !toRefresh.isEmpty();
+ }
+
+ /**
+ * Copies the resources to be polled into the list of resources
+ * to refresh this iteration. This method is synchronized to
+ * guard against concurrent access to the resourceRoots field.
+ */
+ private synchronized void beginIteration() {
+ toRefresh.addAll(resourceRoots);
+ if (hotRoot != null)
+ toRefresh.remove(hotRoot);
+ }
+
+ /*
+ * @see org.eclipse.core.resources.refresh.IRefreshMonitor#unmonitor(IContainer)
+ */
+ public synchronized void unmonitor(IResource resource) {
+ if (resource == null)
+ resourceRoots.clear();
+ else
+ resourceRoots.remove(resource);
+ if (resourceRoots.isEmpty())
+ cancel();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/RefreshJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,225 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.refresh;
+
+import java.util.*;
+import org.eclipse.core.internal.localstore.PrefixPool;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The <code>RefreshJob</code> class maintains a list of resources that
+ * need to be refreshed, and periodically schedules itself to perform the
+ * refreshes in the background.
+ *
+ * @since 3.0
+ */
+public class RefreshJob extends WorkspaceJob {
+ private static final long UPDATE_DELAY = 200;
+ /**
+ * List of refresh requests. Requests are processed in order from
+ * the end of the list. Requests can be added to either the beginning
+ * or the end of the list depending on whether they are explicit user
+ * requests or background refresh requests.
+ */
+ private final List fRequests;
+
+ /**
+ * The history of path prefixes visited during this refresh job invocation.
+ * This is used to prevent infinite refresh loops caused by symbolic links in the file system.
+ */
+ private PrefixPool pathPrefixHistory, rootPathHistory;
+
+ public RefreshJob() {
+ super(Messages.refresh_jobName);
+ fRequests = new ArrayList(1);
+ }
+
+ /**
+ * Adds the given resource to the set of resources that need refreshing.
+ * Synchronized in order to protect the collection during add.
+ * @param resource
+ */
+ private synchronized void addRequest(IResource resource) {
+ IPath toAdd = resource.getFullPath();
+ for (Iterator it = fRequests.iterator(); it.hasNext();) {
+ IPath request = ((IResource) it.next()).getFullPath();
+ //discard any existing requests the same or below the resource to be added
+ if (toAdd.isPrefixOf(request))
+ it.remove();
+ //nothing to do if the resource to be added is a child of an existing request
+ else if (request.isPrefixOf(toAdd))
+ return;
+ }
+ //finally add the new request to the front of the queue
+ fRequests.add(resource);
+ }
+
+ private synchronized void addRequests(List list) {
+ //add requests to the end of the queue
+ fRequests.addAll(0, list);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#belongsTo(Object)
+ */
+ public boolean belongsTo(Object family) {
+ return family == ResourcesPlugin.FAMILY_AUTO_REFRESH;
+ }
+
+ /**
+ * This method adds all members at the specified depth from the resource
+ * to the provided list.
+ */
+ private List collectChildrenToDepth(IResource resource, ArrayList children, int depth) {
+ if (resource.getType() == IResource.FILE)
+ return children;
+ IResource[] members;
+ try {
+ members = ((IContainer) resource).members();
+ } catch (CoreException e) {
+ //resource is not accessible - just return what we have
+ return children;
+ }
+ for (int i = 0; i < members.length; i++) {
+ if (members[i].getType() == IResource.FILE)
+ continue;
+ if (depth <= 1)
+ children.add(members[i]);
+ else
+ collectChildrenToDepth(members[i], children, depth - 1);
+ }
+ return children;
+ }
+
+ /**
+ * Returns the path prefixes visited by this job so far.
+ */
+ public PrefixPool getPathPrefixHistory() {
+ if (pathPrefixHistory == null)
+ pathPrefixHistory = new PrefixPool(20);
+ return pathPrefixHistory;
+ }
+
+ /**
+ * Returns the root paths visited by this job so far.
+ */
+ public PrefixPool getRootPathHistory() {
+ if (rootPathHistory == null)
+ rootPathHistory = new PrefixPool(20);
+ return rootPathHistory;
+ }
+
+ /**
+ * Returns the next item to refresh, or <code>null</code> if there are no requests
+ */
+ private synchronized IResource nextRequest() {
+ // synchronized: in order to atomically obtain and clear requests
+ int len = fRequests.size();
+ if (len == 0)
+ return null;
+ return (IResource) fRequests.remove(len - 1);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.refresh.IRefreshResult#refresh
+ */
+ public void refresh(IResource resource) {
+ if (resource == null)
+ return;
+ addRequest(resource);
+ schedule(UPDATE_DELAY);
+ }
+
+ /* (non-Javadoc)
+ * @see WorkspaceJob#runInWorkspace
+ */
+ public IStatus runInWorkspace(IProgressMonitor monitor) {
+ long start = System.currentTimeMillis();
+ String msg = Messages.refresh_refreshErr;
+ MultiStatus errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, 1, msg, null);
+ long longestRefresh = 0;
+ try {
+ if (RefreshManager.DEBUG)
+ Policy.debug(RefreshManager.DEBUG_PREFIX + " starting refresh job"); //$NON-NLS-1$
+ int refreshCount = 0;
+ int depth = 2;
+ monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
+ IResource toRefresh;
+ while ((toRefresh = nextRequest()) != null) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ try {
+ refreshCount++;
+ long refreshTime = -System.currentTimeMillis();
+ toRefresh.refreshLocal(1000 + depth, null);
+ refreshTime += System.currentTimeMillis();
+ if (refreshTime > longestRefresh)
+ longestRefresh = refreshTime;
+ //show occasional progress
+ if (refreshCount % 100 == 0)
+ monitor.subTask(NLS.bind(Messages.refresh_task, Integer.toString(fRequests.size())));
+ if (refreshCount % 1000 == 0) {
+ //be polite to other threads (no effect on some platforms)
+ Thread.yield();
+ //throttle depth if it takes too long
+ if (longestRefresh > 2000 && depth > 1) {
+ depth = 1;
+ }
+ if (longestRefresh < 1000) {
+ depth *= 2;
+ }
+ longestRefresh = 0;
+ }
+ addRequests(collectChildrenToDepth(toRefresh, new ArrayList(), depth));
+ } catch (CoreException e) {
+ errors.merge(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, errors.getMessage(), e));
+ }
+ }
+ } finally {
+ pathPrefixHistory = null;
+ rootPathHistory = null;
+ monitor.done();
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " finished refresh job in: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (!errors.isOK())
+ return errors;
+ return Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
+ */
+ public synchronized boolean shouldRun() {
+ return !fRequests.isEmpty();
+ }
+
+ /**
+ * Starts the refresh job
+ */
+ public void start() {
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " enabling auto-refresh"); //$NON-NLS-1$
+ }
+
+ /**
+ * Stops the refresh job
+ */
+ public void stop() {
+ if (RefreshManager.DEBUG)
+ System.out.println(RefreshManager.DEBUG_PREFIX + " disabling auto-refresh"); //$NON-NLS-1$
+ cancel();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/RefreshManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.refresh;
+
+import org.eclipse.core.internal.resources.IManager;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.refresh.IRefreshMonitor;
+import org.eclipse.core.resources.refresh.IRefreshResult;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+
+/**
+ * Manages auto-refresh functionality, including maintaining the active
+ * set of monitors and controlling the job that performs periodic refreshes
+ * on out of sync resources.
+ *
+ * @since 3.0
+ */
+public class RefreshManager implements IRefreshResult, IManager, Preferences.IPropertyChangeListener {
+ public static boolean DEBUG = Policy.DEBUG_AUTO_REFRESH;
+ public static final String DEBUG_PREFIX = "Auto-refresh: "; //$NON-NLS-1$
+ MonitorManager monitors;
+ private RefreshJob refreshJob;
+
+ /**
+ * The workspace.
+ */
+ private IWorkspace workspace;
+
+ public RefreshManager(IWorkspace workspace) {
+ this.workspace = workspace;
+ }
+
+ /*
+ * Starts or stops auto-refresh depending on the auto-refresh preference.
+ */
+ protected void manageAutoRefresh(boolean enabled) {
+ //do nothing if we have already shutdown
+ if (refreshJob == null)
+ return;
+ if (enabled) {
+ refreshJob.start();
+ monitors.start();
+ } else {
+ refreshJob.stop();
+ monitors.stop();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.refresh.IRefreshResult#monitorFailed(org.eclipse.core.resources.refresh.IRefreshMonitor, org.eclipse.core.resources.IResource)
+ */
+ public void monitorFailed(IRefreshMonitor monitor, IResource resource) {
+ monitors.monitorFailed(monitor, resource);
+ }
+
+ /**
+ * Checks for changes to the PREF_AUTO_UPDATE property.
+ * @see Preferences.IPropertyChangeListener#propertyChange(Preferences.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ String property = event.getProperty();
+ if (ResourcesPlugin.PREF_AUTO_REFRESH.equals(property)) {
+ Preferences preferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+ boolean autoRefresh = preferences.getBoolean(ResourcesPlugin.PREF_AUTO_REFRESH);
+ manageAutoRefresh(autoRefresh);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.refresh.IRefreshResult#refresh(org.eclipse.core.resources.IResource)
+ */
+ public void refresh(IResource resource) {
+ //do nothing if we have already shutdown
+ if (refreshJob != null)
+ refreshJob.refresh(resource);
+ }
+
+ /**
+ * Shuts down the refresh manager. This only happens when
+ * the resources plugin is going away.
+ */
+ public void shutdown(IProgressMonitor monitor) {
+ ResourcesPlugin.getPlugin().getPluginPreferences().removePropertyChangeListener(this);
+ if (monitors != null) {
+ monitors.stop();
+ monitors = null;
+ }
+ if (refreshJob != null) {
+ refreshJob.stop();
+ refreshJob = null;
+ }
+ }
+
+ /**
+ * Initializes the refresh manager. This does a minimal amount of work
+ * if auto-refresh is turned off.
+ */
+ public void startup(IProgressMonitor monitor) {
+ Preferences preferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+ preferences.addPropertyChangeListener(this);
+
+ refreshJob = new RefreshJob();
+ monitors = new MonitorManager(workspace, this);
+ boolean autoRefresh = preferences.getBoolean(ResourcesPlugin.PREF_AUTO_REFRESH);
+ if (autoRefresh)
+ manageAutoRefresh(autoRefresh);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/AliasManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,717 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * manklu@web.de - fix for bug 156082
+ * Bert Vingerhoets - fix for bug 169975
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.events.ILifecycleListener;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.internal.localstore.FileSystemResourceManager;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * An alias is a resource that occupies the same file system location as another
+ * resource in the workspace. When a resource is modified in a way that affects
+ * the file on disk, all aliases need to be updated. This class is used to
+ * maintain data structures for quickly computing the set of aliases for a given
+ * resource, and for efficiently updating all aliases when a resource changes on
+ * disk.
+ *
+ * The approach for computing aliases is optimized for alias-free workspaces and
+ * alias-free projects. That is, if the workspace contains no aliases, then
+ * updating should be very quick. If a resource is changed in a project that
+ * contains no aliases, it should also be very fast.
+ *
+ * The data structures maintained by the alias manager can be seen as a cache,
+ * that is, they store no information that cannot be recomputed from other
+ * available information. On shutdown, the alias manager discards all state; on
+ * startup, the alias manager eagerly rebuilds its state. The reasoning is
+ * that it's better to incur this cost on startup than on the first attempt to
+ * modify a resource. After startup, the state is updated incrementally on the
+ * following occasions:
+ * - when projects are deleted, opened, closed, or moved
+ * - when linked resources are created, deleted, or moved.
+ */
+public class AliasManager implements IManager, ILifecycleListener, IResourceChangeListener {
+ public class AddToCollectionDoit implements Doit {
+ Collection collection;
+
+ public void doit(IResource resource) {
+ collection.add(resource);
+ }
+
+ public void setCollection(Collection collection) {
+ this.collection = collection;
+ }
+ }
+
+ interface Doit {
+ public void doit(IResource resource);
+ }
+
+ class FindAliasesDoit implements Doit {
+ private int aliasType;
+ private IPath searchPath;
+
+ public void doit(IResource match) {
+ //don't record the resource we're computing aliases against as a match
+ if (match.getFullPath().isPrefixOf(searchPath))
+ return;
+ IPath aliasPath = null;
+ switch (match.getType()) {
+ case IResource.PROJECT :
+ //first check if there is a linked resource that blocks the project location
+ if (suffix.segmentCount() > 0) {
+ IResource testResource = ((IProject) match).findMember(suffix.segment(0));
+ if (testResource != null && testResource.isLinked())
+ return;
+ }
+ //there is an alias under this project
+ aliasPath = match.getFullPath().append(suffix);
+ break;
+ case IResource.FOLDER :
+ aliasPath = match.getFullPath().append(suffix);
+ break;
+ case IResource.FILE :
+ if (suffix.segmentCount() == 0)
+ aliasPath = match.getFullPath();
+ break;
+ }
+ if (aliasPath != null)
+ if (aliasType == IResource.FILE) {
+ aliases.add(workspace.getRoot().getFile(aliasPath));
+ } else {
+ if (aliasPath.segmentCount() == 1)
+ aliases.add(workspace.getRoot().getProject(aliasPath.lastSegment()));
+ else
+ aliases.add(workspace.getRoot().getFolder(aliasPath));
+ }
+ }
+
+ /**
+ * Sets the resource that we are searching for aliases for.
+ */
+ public void setSearchAlias(IResource aliasResource) {
+ this.aliasType = aliasResource.getType();
+ this.searchPath = aliasResource.getFullPath();
+ }
+ }
+
+ /**
+ * Maintains a mapping of FileStore->IResource, such that multiple resources
+ * mapped from the same location are tolerated.
+ */
+ class LocationMap {
+ /**
+ * Map of FileStore->IResource OR FileStore->ArrayList of (IResource)
+ */
+ private final SortedMap map = new TreeMap(getComparator());
+
+ /**
+ * Adds the given resource to the map, keyed by the given location.
+ * Returns true if a new entry was added, and false otherwise.
+ */
+ public boolean add(IFileStore location, IResource resource) {
+ Object oldValue = map.get(location);
+ if (oldValue == null) {
+ map.put(location, resource);
+ return true;
+ }
+ if (oldValue instanceof IResource) {
+ if (resource.equals(oldValue))
+ return false;//duplicate
+ ArrayList newValue = new ArrayList(2);
+ newValue.add(oldValue);
+ newValue.add(resource);
+ map.put(location, newValue);
+ return true;
+ }
+ ArrayList list = (ArrayList) oldValue;
+ if (list.contains(resource))
+ return false;//duplicate
+ list.add(resource);
+ return true;
+ }
+
+ /**
+ * Method clear.
+ */
+ public void clear() {
+ map.clear();
+ }
+
+ /**
+ * Invoke the given doit for every resource whose location has the
+ * given location as a prefix.
+ */
+ public void matchingPrefixDo(IFileStore prefix, Doit doit) {
+ SortedMap matching;
+ IFileStore prefixParent = prefix.getParent();
+ if (prefixParent != null) {
+ //endPoint is the smallest possible path greater than the prefix that doesn't
+ //match the prefix
+ IFileStore endPoint = prefixParent.getChild(prefix.getName() + "\0"); //$NON-NLS-1$
+ matching = map.subMap(prefix, endPoint);
+ } else {
+ matching = map;
+ }
+ for (Iterator it = matching.values().iterator(); it.hasNext();) {
+ Object value = it.next();
+ if (value == null)
+ return;
+ if (value instanceof List) {
+ Iterator duplicates = ((List) value).iterator();
+ while (duplicates.hasNext())
+ doit.doit((IResource) duplicates.next());
+ } else {
+ doit.doit((IResource) value);
+ }
+ }
+ }
+
+ /**
+ * Invoke the given doit for every resource that matches the given
+ * location.
+ */
+ public void matchingResourcesDo(IFileStore location, Doit doit) {
+ Object value = map.get(location);
+ if (value == null)
+ return;
+ if (value instanceof List) {
+ Iterator duplicates = ((List) value).iterator();
+ while (duplicates.hasNext())
+ doit.doit((IResource) duplicates.next());
+ } else {
+ doit.doit((IResource) value);
+ }
+ }
+
+ /**
+ * Calls the given doit with the project of every resource in the map
+ * whose location overlaps another resource in the map.
+ */
+ public void overLappingResourcesDo(Doit doit) {
+ Iterator entries = map.entrySet().iterator();
+ IFileStore previousStore = null;
+ IResource previousResource = null;
+ while (entries.hasNext()) {
+ Map.Entry current = (Map.Entry) entries.next();
+ //value is either single resource or List of resources
+ IFileStore currentStore = (IFileStore) current.getKey();
+ IResource currentResource = null;
+ Object value = current.getValue();
+ if (value instanceof List) {
+ //if there are several then they're all overlapping
+ Iterator duplicates = ((List) value).iterator();
+ while (duplicates.hasNext())
+ doit.doit(((IResource) duplicates.next()).getProject());
+ } else {
+ //value is a single resource
+ currentResource = (IResource) value;
+ }
+ if (previousStore != null) {
+ //check for overlap with previous
+ //Note: previous is always shorter due to map sorting rules
+ if (previousStore.isParentOf(currentStore)) {
+ //resources will be null if they were in a list, in which case
+ //they've already been passed to the doit
+ if (previousResource != null) {
+ doit.doit(previousResource.getProject());
+ //null out previous resource so we don't call doit twice with same resource
+ previousResource = null;
+ }
+ if (currentResource != null)
+ doit.doit(currentResource.getProject());
+ //keep iterating with the same previous store because there may be more overlaps
+ continue;
+ }
+ }
+ previousStore = currentStore;
+ previousResource = currentResource;
+ }
+ }
+
+ /**
+ * Removes the given location from the map. Returns true if anything
+ * was actually removed, and false otherwise.
+ */
+ public boolean remove(IFileStore location, IResource resource) {
+ Object oldValue = map.get(location);
+ if (oldValue == null)
+ return false;
+ if (oldValue instanceof IResource) {
+ if (resource.equals(oldValue)) {
+ map.remove(location);
+ return true;
+ }
+ return false;
+ }
+ ArrayList list = (ArrayList) oldValue;
+ boolean wasRemoved = list.remove(resource);
+ if (list.size() == 0)
+ map.remove(location);
+ return wasRemoved;
+ }
+ }
+
+ /**
+ * Doit convenience class for adding items to a list
+ */
+ private final AddToCollectionDoit addToCollection = new AddToCollectionDoit();
+
+ /**
+ * The set of IProjects that have aliases.
+ */
+ protected final Set aliasedProjects = new HashSet();
+
+ /**
+ * A temporary set of aliases. Used during computeAliases, but maintained
+ * as a field as an optimization to prevent recreating the set.
+ */
+ protected final HashSet aliases = new HashSet();
+
+ /**
+ * The set of resources that have had structure changes that might
+ * invalidate the locations map or aliased projects set. These will be
+ * updated incrementally on the next alias request.
+ */
+ private final Set changedLinks = new HashSet();
+
+ /**
+ * This flag is true when projects have been created or deleted and the
+ * location map has not been updated accordingly.
+ */
+ private boolean changedProjects = false;
+
+ /**
+ * The Doit class used for finding aliases.
+ */
+ private final FindAliasesDoit findAliases = new FindAliasesDoit();
+
+ /**
+ * This maps IFileStore ->IResource, associating a file system location
+ * with the projects and/or linked resources that are rooted at that location.
+ */
+ protected final LocationMap locationsMap = new LocationMap();
+ /**
+ * The total number of resources in the workspace that are not in the default
+ * location. This includes all linked resources, including linked resources
+ * that don't currently have valid locations due to an undefined path variable.
+ * This also includes projects that are not in their default location.
+ * This value is used as a quick optimization, because a workspace with
+ * all resources in their default locations cannot have any aliases.
+ */
+ private int nonDefaultResourceCount = 0;
+
+ /**
+ * The suffix object is also used only during the computeAliases method.
+ * In this case it is a field because it is referenced from an inner class
+ * and we want to avoid creating a pointer array. It is public to eliminate
+ * the need for synthetic accessor methods.
+ */
+ public IPath suffix;
+
+ /** the workspace */
+ protected final Workspace workspace;
+
+ public AliasManager(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ private void addToLocationsMap(IProject project) {
+ IFileStore location = ((Resource) project).getStore();
+ if (location != null)
+ locationsMap.add(location, project);
+ ProjectDescription description = ((Project) project).internalGetDescription();
+ if (description == null)
+ return;
+ if (description.getLocationURI() != null)
+ nonDefaultResourceCount++;
+ HashMap links = description.getLinks();
+ if (links == null)
+ return;
+ for (Iterator it = links.values().iterator(); it.hasNext();) {
+ LinkDescription linkDesc = (LinkDescription) it.next();
+ IResource link = project.findMember(linkDesc.getProjectRelativePath());
+ if (link != null) {
+ try {
+ addToLocationsMap(link, EFS.getStore(linkDesc.getLocationURI()));
+ } catch (CoreException e) {
+ //ignore links with invalid locations
+ }
+ }
+ }
+ }
+
+ private void addToLocationsMap(IResource link, IFileStore location) {
+ if (location != null)
+ if (locationsMap.add(location, link))
+ nonDefaultResourceCount++;
+ }
+
+ /**
+ * Builds the table of aliased projects from scratch.
+ */
+ private void buildAliasedProjectsSet() {
+ aliasedProjects.clear();
+ //if there are no resources in non-default locations then there can't be any aliased projects
+ if (nonDefaultResourceCount <= 0)
+ return;
+ //for every resource that overlaps another, marked its project as aliased
+ addToCollection.setCollection(aliasedProjects);
+ locationsMap.overLappingResourcesDo(addToCollection);
+ }
+
+ /**
+ * Builds the table of resource locations from scratch. Also computes an
+ * initial value for the linked resource counter.
+ */
+ private void buildLocationsMap() {
+ locationsMap.clear();
+ nonDefaultResourceCount = 0;
+ //build table of IPath (file system location) -> IResource (project or linked resource)
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ if (projects[i].isAccessible())
+ addToLocationsMap(projects[i]);
+ }
+
+ /**
+ * A project alias needs updating. If the project location has been deleted,
+ * then the project should be deleted from the workspace. This differs
+ * from the refresh local strategy, but operations performed from within
+ * the workspace must never leave a resource out of sync.
+ * @param project The project to check for deletion
+ * @param location The project location
+ * @return <code>true</code> if the project has been deleted, and <code>false</code> otherwise
+ * @exception CoreException
+ */
+ private boolean checkDeletion(Project project, IFileStore location) throws CoreException {
+ if (project.exists() && !location.fetchInfo().exists()) {
+ //perform internal deletion of project from workspace tree because
+ // it is already deleted from disk and we can't acquire a different
+ //scheduling rule in this context (none is needed because we are
+ //within scope of the workspace lock)
+ Assert.isTrue(workspace.getWorkManager().getLock().getDepth() > 0);
+ project.deleteResource(false, null);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns all aliases of the given resource, or null if there are none.
+ */
+ public IResource[] computeAliases(final IResource resource, IFileStore location) {
+ //nothing to do if we are or were in an alias-free workspace or project
+ if (hasNoAliases(resource))
+ return null;
+
+ aliases.clear();
+ internalComputeAliases(resource, location);
+ int size = aliases.size();
+ if (size == 0)
+ return null;
+ return (IResource[]) aliases.toArray(new IResource[size]);
+ }
+
+ /**
+ * Returns all aliases of this resource, and any aliases of subtrees of this
+ * resource. Returns null if no aliases are found.
+ */
+ private void computeDeepAliases(IResource resource, IFileStore location) {
+ //if the location is invalid then there won't be any aliases to update
+ if (location == null)
+ return;
+ //get the normal aliases (resources rooted in parent locations)
+ internalComputeAliases(resource, location);
+ //get all resources rooted below this resource's location
+ addToCollection.setCollection(aliases);
+ locationsMap.matchingPrefixDo(location, addToCollection);
+ //if this is a project, get all resources rooted below links in this project
+ if (resource.getType() == IResource.PROJECT) {
+ try {
+ IResource[] members = ((IProject) resource).members();
+ final FileSystemResourceManager localManager = workspace.getFileSystemManager();
+ for (int i = 0; i < members.length; i++) {
+ if (members[i].isLinked()) {
+ IFileStore linkLocation = localManager.getStore(members[i]);
+ if (linkLocation != null)
+ locationsMap.matchingPrefixDo(linkLocation, addToCollection);
+ }
+ }
+ } catch (CoreException e) {
+ //skip inaccessible projects
+ }
+ }
+ }
+
+ /**
+ * Returns the comparator to use when sorting the locations map. Comparison
+ * is based on segments, so that paths with the most segments in common will
+ * always be adjacent. This is equivalent to the natural order on the path
+ * strings, with the extra condition that the path separator is ordered
+ * before all other characters. (Ex: "/foo" < "/foo/zzz" < "/fooaaa").
+ */
+ private Comparator getComparator() {
+ return new Comparator() {
+ public int compare(Object o1, Object o2) {
+ IFileStore store1 = (IFileStore) o1;
+ IFileStore store2 = (IFileStore) o2;
+ //scheme takes precedence over all else
+ int compare = compareStringOrNull(store1.getFileSystem().getScheme(), store2.getFileSystem().getScheme());
+ if (compare != 0)
+ return compare;
+ // compare based on URI path segment values
+ final URI uri1;
+ final URI uri2;
+ try {
+ uri1 = store1.toURI();
+ uri2 = store2.toURI();
+ } catch (Exception e) {
+ //protect against misbehaving 3rd party code in file system implementations
+ Policy.log(e);
+ return 1;
+ }
+
+ IPath path1 = new Path(uri1.getPath());
+ IPath path2 = new Path(uri2.getPath());
+ // compare devices
+ compare = compareStringOrNull(path1.getDevice(), path2.getDevice());
+ if (compare != 0)
+ return compare;
+ // compare segments
+ int segmentCount1 = path1.segmentCount();
+ int segmentCount2 = path2.segmentCount();
+ for (int i = 0; (i < segmentCount1) && (i < segmentCount2); i++) {
+ compare = path1.segment(i).compareTo(path2.segment(i));
+ if (compare != 0)
+ return compare;
+ }
+ //all segments are equal, so compare based on number of segments
+ compare = segmentCount1 - segmentCount2;
+ if (compare != 0)
+ return compare;
+ //same number of segments, so compare query
+ return compareStringOrNull(uri1.getQuery(), uri2.getQuery());
+ }
+
+ /**
+ * Compares two strings that are possibly null.
+ */
+ private int compareStringOrNull(String string1, String string2) {
+ if (string1 == null) {
+ if (string2 == null)
+ return 0;
+ return 1;
+ }
+ if (string2 == null)
+ return -1;
+ return string1.compareTo(string2);
+
+ }
+ };
+ }
+
+ public void handleEvent(LifecycleEvent event) {
+ /*
+ * We can't determine the end state for most operations because they may
+ * fail after we receive pre-notification. In these cases, we remember
+ * the invalidated resources and recompute their state lazily on the
+ * next alias request.
+ */
+ switch (event.kind) {
+ case LifecycleEvent.PRE_LINK_DELETE :
+ Resource link = (Resource) event.resource;
+ if (link.isLinked())
+ removeFromLocationsMap(link, link.getStore());
+ //fall through
+ case LifecycleEvent.PRE_LINK_CREATE :
+ changedLinks.add(event.resource);
+ break;
+ case LifecycleEvent.PRE_LINK_COPY :
+ changedLinks.add(event.newResource);
+ break;
+ case LifecycleEvent.PRE_LINK_MOVE :
+ link = (Resource) event.resource;
+ if (link.isLinked())
+ removeFromLocationsMap(link, link.getStore());
+ changedLinks.add(event.newResource);
+ break;
+ }
+ }
+
+ /**
+ * Returns true if this resource is guaranteed to have no aliases, and false
+ * otherwise.
+ */
+ private boolean hasNoAliases(final IResource resource) {
+ //check if we're in an aliased project or workspace before updating structure changes. In the
+ //deletion case, we need to know if the resource was in an aliased project *before* deletion.
+ IProject project = resource.getProject();
+ boolean noAliases = !aliasedProjects.contains(project);
+
+ //now update any structure changes and check again if an update is needed
+ if (hasStructureChanges()) {
+ updateStructureChanges();
+ noAliases &= nonDefaultResourceCount <= 0 || !aliasedProjects.contains(project);
+ }
+ return noAliases;
+ }
+
+ /**
+ * Returns whether there are any structure changes that we have not yet processed.
+ */
+ private boolean hasStructureChanges() {
+ return changedProjects || !changedLinks.isEmpty();
+ }
+
+ /**
+ * Computes the aliases of the given resource at the given location, and
+ * adds them to the "aliases" collection.
+ */
+ private void internalComputeAliases(IResource resource, IFileStore location) {
+ IFileStore searchLocation = location;
+ if (searchLocation == null)
+ searchLocation = ((Resource) resource).getStore();
+ //if the location is invalid then there won't be any aliases to update
+ if (searchLocation == null)
+ return;
+
+ suffix = Path.EMPTY;
+ findAliases.setSearchAlias(resource);
+ /*
+ * Walk up the location segments for this resource, looking for a
+ * resource with a matching location. All matches are then added to the
+ * "aliases" set.
+ */
+ do {
+ locationsMap.matchingResourcesDo(searchLocation, findAliases);
+ suffix = new Path(searchLocation.getName()).append(suffix);
+ searchLocation = searchLocation.getParent();
+ } while (searchLocation != null);
+ }
+
+ private void removeFromLocationsMap(IResource link, IFileStore location) {
+ if (location != null)
+ if (locationsMap.remove(location, link))
+ nonDefaultResourceCount--;
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ final IResourceDelta delta = event.getDelta();
+ if (delta == null)
+ return;
+ //invalidate location map if there are added or removed projects.
+ if (delta.getAffectedChildren(IResourceDelta.ADDED | IResourceDelta.REMOVED).length > 0)
+ changedProjects = true;
+
+ // invalidate location map if any project has the description changed
+ // or was closed/opened
+ IResourceDelta[] changed = delta.getAffectedChildren(IResourceDelta.CHANGED);
+ for (int i = 0; i < changed.length; i++) {
+ if ((changed[i].getFlags() & IResourceDelta.DESCRIPTION) == IResourceDelta.DESCRIPTION
+ || (changed[i].getFlags() & IResourceDelta.OPEN) == IResourceDelta.OPEN) {
+ changedProjects = true;
+ break;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IManager#shutdown(IProgressMonitor)
+ */
+ public void shutdown(IProgressMonitor monitor) {
+ workspace.removeResourceChangeListener(this);
+ locationsMap.clear();
+ }
+
+ /* (non-Javadoc)
+ * @see IManager#startup(IProgressMonitor)
+ */
+ public void startup(IProgressMonitor monitor) {
+ workspace.addLifecycleListener(this);
+ workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+ buildLocationsMap();
+ buildAliasedProjectsSet();
+ }
+
+ /**
+ * The file underlying the given resource has changed on disk. Compute all
+ * aliases for this resource and update them. This method will not attempt
+ * to incur any units of work on the given progress monitor, but it may
+ * update the subtask to reflect what aliases are being updated.
+ * @param resource the resource to compute aliases for
+ * @param location the file system location of the resource (passed as a
+ * parameter because in the project deletion case the resource is no longer
+ * accessible at time of update).
+ * @param depth whether to search for aliases on all children of the given
+ * resource. Only depth ZERO and INFINITE are used.
+ */
+ public void updateAliases(IResource resource, IFileStore location, int depth, IProgressMonitor monitor) throws CoreException {
+ if (hasNoAliases(resource))
+ return;
+ aliases.clear();
+ if (depth == IResource.DEPTH_ZERO)
+ internalComputeAliases(resource, location);
+ else
+ computeDeepAliases(resource, location);
+ if (aliases.size() == 0)
+ return;
+ FileSystemResourceManager localManager = workspace.getFileSystemManager();
+ for (Iterator it = aliases.iterator(); it.hasNext();) {
+ IResource alias = (IResource) it.next();
+ monitor.subTask(NLS.bind(Messages.links_updatingDuplicate, alias.getFullPath()));
+ if (alias.getType() == IResource.PROJECT) {
+ if (checkDeletion((Project) alias, location))
+ continue;
+ //project did not require deletion, so fall through below and refresh it
+ }
+ localManager.refresh(alias, IResource.DEPTH_INFINITE, false, null);
+ }
+ }
+
+ /**
+ * Process any structural changes that have occurred since the last alias
+ * request.
+ */
+ private void updateStructureChanges() {
+ boolean hadChanges = false;
+ if (changedProjects) {
+ //if a project is added or removed, just recompute the whole world
+ changedProjects = false;
+ hadChanges = true;
+ buildLocationsMap();
+ } else {
+ //incrementally update location map for changed links
+ for (Iterator it = changedLinks.iterator(); it.hasNext();) {
+ IResource resource = (IResource) it.next();
+ hadChanges = true;
+ if (!resource.isAccessible())
+ continue;
+ if (resource.isLinked())
+ addToLocationsMap(resource, ((Resource) resource).getStore());
+ }
+ }
+ changedLinks.clear();
+ if (hadChanges)
+ buildAliasedProjectsSet();
+ changedProjects = false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CharsetDeltaJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.framework.Bundle;
+
+/**
+ * Detects changes to content types/project preferences and
+ * broadcasts any corresponding encoding changes as resource deltas.
+ */
+
+public class CharsetDeltaJob extends Job implements IContentTypeManager.IContentTypeChangeListener {
+
+ // this is copied in the runtime tests - if changed here, has to be changed there too
+ public final static String FAMILY_CHARSET_DELTA = ResourcesPlugin.PI_RESOURCES + "charsetJobFamily"; //$NON-NLS-1$
+
+ interface ICharsetListenerFilter {
+
+ /**
+ * Returns the path for the node in the tree we are interested in.
+ */
+ IPath getRoot();
+
+ /**
+ * Returns whether the corresponding resource is affected by this change.
+ */
+ boolean isAffected(ResourceInfo info, IPathRequestor requestor);
+ }
+
+ private ThreadLocal disabled = new ThreadLocal();
+
+ private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
+ private Queue work = new Queue();
+
+ Workspace workspace;
+
+ private static final int CHARSET_DELTA_DELAY = 500;
+
+ public CharsetDeltaJob(Workspace workspace) {
+ super(Messages.resources_charsetBroadcasting);
+ this.workspace = workspace;
+ }
+
+ private void addToQueue(ICharsetListenerFilter filter) {
+ synchronized (work) {
+ work.add(filter);
+ }
+ schedule(CHARSET_DELTA_DELAY);
+ }
+
+ public boolean belongsTo(Object family) {
+ return FAMILY_CHARSET_DELTA.equals(family);
+ }
+
+ public void charsetPreferencesChanged(final IProject project) {
+ // avoid reacting to changes made by ourselves
+ if (isDisabled())
+ return;
+ // ensure all resources under the affected project are
+ // reported as having encoding changes
+ ICharsetListenerFilter filter = new ICharsetListenerFilter() {
+
+ public IPath getRoot() {
+ // visit the project subtree
+ return project.getFullPath();
+ }
+
+ public boolean isAffected(ResourceInfo info, IPathRequestor requestor) {
+ // for now, mark all resources in the project as potential encoding resource changes
+ return true;
+ }
+ };
+ addToQueue(filter);
+ }
+
+ public void contentTypeChanged(final ContentTypeChangeEvent event) {
+ // check all files that may be affected by this change (taking
+ // only the current content type state into account
+ // dispatch a job to generate the deltas
+ ICharsetListenerFilter filter = new ICharsetListenerFilter() {
+
+ public IPath getRoot() {
+ // visit all resources in the workspace
+ return Path.ROOT;
+ }
+
+ public boolean isAffected(ResourceInfo info, IPathRequestor requestor) {
+ if (info.getType() != IResource.FILE)
+ return false;
+ return event.getContentType().isAssociatedWith(requestor.requestName());
+ }
+ };
+ addToQueue(filter);
+ }
+
+ private boolean isDisabled() {
+ return disabled.get() != null;
+ }
+
+ private void processNextEvent(final ICharsetListenerFilter filter, IProgressMonitor monitor) throws CoreException {
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (!filter.isAffected(info, requestor))
+ return true;
+ info = workspace.getResourceInfo(requestor.requestPath(), false, true);
+ if (info == null)
+ return false;
+ info.incrementCharsetGenerationCount();
+ return true;
+ }
+ };
+ try {
+ new ElementTreeIterator(workspace.getElementTree(), filter.getRoot()).iterate(visitor);
+ } catch (WrappedRuntimeException e) {
+ throw (CoreException) e.getTargetException();
+ }
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ }
+
+ private ICharsetListenerFilter removeFromQueue() {
+ synchronized (work) {
+ return (ICharsetListenerFilter) work.remove();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus run(IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = Messages.resources_charsetBroadcasting;
+ monitor.beginTask(message, Policy.totalWork);
+ try {
+ workspace.prepareOperation(null, monitor);
+ workspace.beginOperation(true);
+ ICharsetListenerFilter next;
+ //if the system is shutting down, don't broadcast
+ while (systemBundle.getState() != Bundle.STOPPING && (next = removeFromQueue()) != null)
+ processNextEvent(next, monitor);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ return Status.CANCEL_STATUS;
+ } finally {
+ workspace.endOperation(null, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ monitor.worked(Policy.opWork);
+ } catch (CoreException sig) {
+ return sig.getStatus();
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Turns off reaction to changes in the preference file.
+ */
+ public void setDisabled(boolean disabled) {
+ // using a thread local because this can be called by multiple threads concurrently
+ this.disabled.set(disabled ? Boolean.TRUE : null);
+ }
+
+ public void shutdown() {
+ Platform.getContentTypeManager().removeContentTypeChangeListener(this);
+ }
+
+ public void startup() {
+ Platform.getContentTypeManager().addContentTypeChangeListener(this);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CharsetManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.framework.Bundle;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * Manages user-defined encodings as preferences in the project content area.
+ *
+ * @since 3.0
+ */
+public class CharsetManager implements IManager {
+ /**
+ * This job implementation is used to allow the resource change listener
+ * to schedule operations that need to modify the workspace.
+ */
+ private class CharsetManagerJob extends Job {
+ private static final int CHARSET_UPDATE_DELAY = 500;
+ private List asyncChanges = new ArrayList();
+
+ public CharsetManagerJob() {
+ super(Messages.resources_charsetUpdating);
+ setSystem(true);
+ setPriority(Job.INTERACTIVE);
+ }
+
+ public void addChanges(Set newChanges) {
+ if (newChanges.isEmpty())
+ return;
+ synchronized (asyncChanges) {
+ asyncChanges.addAll(newChanges);
+ asyncChanges.notify();
+ }
+ schedule(CHARSET_UPDATE_DELAY);
+ }
+
+ public IProject getNextChange() {
+ synchronized (asyncChanges) {
+ return asyncChanges.isEmpty() ? null : (IProject) asyncChanges.remove(asyncChanges.size() - 1);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected IStatus run(IProgressMonitor monitor) {
+ MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_SETTING_CHARSET, Messages.resources_updatingEncoding, null);
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask(Messages.resources_charsetUpdating, Policy.totalWork);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(workspace.getRoot());
+ try {
+ workspace.prepareOperation(rule, monitor);
+ workspace.beginOperation(true);
+ IProject next;
+ while ((next = getNextChange()) != null) {
+ //just exit if the system is shutting down or has been shut down
+ //it is too late to change the workspace at this point anyway
+ if (systemBundle.getState() != Bundle.ACTIVE)
+ return Status.OK_STATUS;
+ try {
+ if (next.isAccessible()) {
+ Preferences projectPrefs = getPreferences(next, false);
+ if (projectPrefs != null)
+ projectPrefs.flush();
+ }
+ } catch (BackingStoreException e) {
+ // we got an error saving
+ String detailMessage = Messages.resources_savingEncoding;
+ result.add(new ResourceStatus(IResourceStatus.FAILED_SETTING_CHARSET, next.getFullPath(), detailMessage, e));
+ }
+ }
+ monitor.worked(Policy.opWork);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } catch (CoreException ce) {
+ return ce.getStatus();
+ } finally {
+ monitor.done();
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
+ */
+ public boolean shouldRun() {
+ synchronized (asyncChanges) {
+ return !asyncChanges.isEmpty();
+ }
+ }
+ }
+
+ class Listener implements IResourceChangeListener {
+
+ private void processEntryChanges(IResourceDelta projectDelta, Set projectsToSave) {
+ // check each resource with user-set encoding to see if it has
+ // been moved/deleted
+ boolean resourceChanges = false;
+ IProject currentProject = (IProject) projectDelta.getResource();
+ Preferences projectPrefs = getPreferences(currentProject, false);
+ if (projectPrefs == null)
+ // no preferences for this project, just bail
+ return;
+ String[] affectedResources;
+ try {
+ affectedResources = projectPrefs.keys();
+ } catch (BackingStoreException e) {
+ // problems with the project scope... we gonna miss the changes (but will log)
+ String message = Messages.resources_readingEncoding;
+ Policy.log(new ResourceStatus(IResourceStatus.FAILED_GETTING_CHARSET, currentProject.getFullPath(), message, e));
+ return;
+ }
+ for (int i = 0; i < affectedResources.length; i++) {
+ IResourceDelta memberDelta = projectDelta.findMember(new Path(affectedResources[i]));
+ // no changes for the given resource
+ if (memberDelta == null)
+ continue;
+ if (memberDelta.getKind() == IResourceDelta.REMOVED) {
+ resourceChanges = true;
+ // remove the setting for the original location - save its value though
+ String currentValue = projectPrefs.get(affectedResources[i], null);
+ projectPrefs.remove(affectedResources[i]);
+ if ((memberDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
+ // if moving, copy the setting for the new location
+ IProject targetProject = workspace.getRoot().getProject(memberDelta.getMovedToPath().segment(0));
+ Preferences targetPrefs = getPreferences(targetProject, true);
+ targetPrefs.put(getKeyFor(memberDelta.getMovedToPath()), currentValue);
+ if (targetProject != currentProject)
+ projectsToSave.add(targetProject);
+ }
+ }
+ }
+ if (resourceChanges)
+ projectsToSave.add(currentProject);
+ }
+
+ /**
+ * For any change to the encoding file or any resource with encoding
+ * set, just discard the cache for the corresponding project.
+ */
+ public void resourceChanged(IResourceChangeEvent event) {
+ IResourceDelta delta = event.getDelta();
+ if (delta == null)
+ return;
+ IResourceDelta[] projectDeltas = delta.getAffectedChildren();
+ // process each project in the delta
+ Set projectsToSave = new HashSet();
+ for (int i = 0; i < projectDeltas.length; i++)
+ //nothing to do if a project has been added/removed/moved
+ if (projectDeltas[i].getKind() == IResourceDelta.CHANGED && (projectDeltas[i].getFlags() & IResourceDelta.OPEN) == 0)
+ processEntryChanges(projectDeltas[i], projectsToSave);
+ job.addChanges(projectsToSave);
+ }
+ }
+
+ public static final String ENCODING_PREF_NODE = "encoding"; //$NON-NLS-1$
+ private static final String PROJECT_KEY = "<project>"; //$NON-NLS-1$
+ private CharsetDeltaJob charsetListener;
+ CharsetManagerJob job;
+ private IResourceChangeListener listener;
+ protected final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
+ Workspace workspace;
+
+ public CharsetManager(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ /**
+ * Returns the charset explicitly set by the user for the given resource,
+ * or <code>null</code>. If no setting exists for the given resource and
+ * <code>recurse</code> is <code>true</code>, every parent up to the
+ * workspace root will be checked until a charset setting can be found.
+ *
+ * @param resourcePath the path for the resource
+ * @param recurse whether the parent should be queried
+ * @return the charset setting for the given resource
+ */
+ public String getCharsetFor(IPath resourcePath, boolean recurse) {
+ Assert.isLegal(resourcePath.segmentCount() >= 1);
+ IProject project = workspace.getRoot().getProject(resourcePath.segment(0));
+ Preferences encodingSettings = getPreferences(project, false);
+ if (encodingSettings == null)
+ // no preferences found - for performance reasons, short-circuit
+ // lookup by falling back to workspace's default setting
+ return recurse ? ResourcesPlugin.getEncoding() : null;
+ return internalGetCharsetFor(resourcePath, encodingSettings, recurse);
+ }
+
+ String getKeyFor(IPath resourcePath) {
+ return resourcePath.segmentCount() > 1 ? resourcePath.removeFirstSegments(1).toString() : PROJECT_KEY;
+ }
+
+ Preferences getPreferences(IProject project, boolean create) {
+ if (create)
+ // create all nodes down to the one we are interested in
+ return new ProjectScope(project).getNode(ResourcesPlugin.PI_RESOURCES).node(ENCODING_PREF_NODE);
+ // be careful looking up for our node so not to create any nodes as side effect
+ Preferences node = Platform.getPreferencesService().getRootNode().node(ProjectScope.SCOPE);
+ try {
+ //TODO once bug 90500 is fixed, should be as simple as this:
+ // String path = project.getName() + IPath.SEPARATOR + ResourcesPlugin.PI_RESOURCES + IPath.SEPARATOR + ENCODING_PREF_NODE;
+ // return node.nodeExists(path) ? node.node(path) : null;
+ // for now, take the long way
+ if (!node.nodeExists(project.getName()))
+ return null;
+ node = node.node(project.getName());
+ if (!node.nodeExists(ResourcesPlugin.PI_RESOURCES))
+ return null;
+ node = node.node(ResourcesPlugin.PI_RESOURCES);
+ if (!node.nodeExists(ENCODING_PREF_NODE))
+ return null;
+ return node.node(ENCODING_PREF_NODE);
+ } catch (BackingStoreException e) {
+ // nodeExists failed
+ String message = Messages.resources_readingEncoding;
+ Policy.log(new ResourceStatus(IResourceStatus.FAILED_GETTING_CHARSET, project.getFullPath(), message, e));
+ }
+ return null;
+ }
+
+ private String internalGetCharsetFor(IPath resourcePath, Preferences encodingSettings, boolean recurse) {
+ String charset = encodingSettings.get(getKeyFor(resourcePath), null);
+ if (!recurse)
+ return charset;
+ while (charset == null && resourcePath.segmentCount() > 1) {
+ resourcePath = resourcePath.removeLastSegments(1);
+ charset = encodingSettings.get(getKeyFor(resourcePath), null);
+ }
+ // ensure we default to the workspace encoding if none is found
+ return charset == null ? ResourcesPlugin.getEncoding() : charset;
+ }
+
+ public void projectPreferencesChanged(IProject project) {
+ charsetListener.charsetPreferencesChanged(project);
+ }
+
+ public void setCharsetFor(IPath resourcePath, String newCharset) throws CoreException {
+ // for the workspace root we just set a preference in the instance scope
+ if (resourcePath.segmentCount() == 0) {
+ org.eclipse.core.runtime.Preferences resourcesPreferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+ if (newCharset != null)
+ resourcesPreferences.setValue(ResourcesPlugin.PREF_ENCODING, newCharset);
+ else
+ resourcesPreferences.setToDefault(ResourcesPlugin.PREF_ENCODING);
+ ResourcesPlugin.getPlugin().savePluginPreferences();
+ return;
+ }
+ // for all other cases, we set a property in the corresponding project
+ IProject project = workspace.getRoot().getProject(resourcePath.segment(0));
+ Preferences encodingSettings = getPreferences(project, true);
+ if (newCharset == null || newCharset.trim().length() == 0)
+ encodingSettings.remove(getKeyFor(resourcePath));
+ else
+ encodingSettings.put(getKeyFor(resourcePath), newCharset);
+ try {
+ // disable the listener so we don't react to changes made by ourselves
+ charsetListener.setDisabled(true);
+ // save changes
+ encodingSettings.flush();
+ } catch (BackingStoreException e) {
+ String message = Messages.resources_savingEncoding;
+ throw new ResourceException(IResourceStatus.FAILED_SETTING_CHARSET, project.getFullPath(), message, e);
+ } finally {
+ charsetListener.setDisabled(false);
+ }
+
+ }
+
+ public void shutdown(IProgressMonitor monitor) {
+ workspace.removeResourceChangeListener(listener);
+ if (charsetListener != null)
+ charsetListener.shutdown();
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ job = new CharsetManagerJob();
+ listener = new Listener();
+ workspace.addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
+ charsetListener = new CharsetDeltaJob(workspace);
+ charsetListener.startup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ComputeProjectOrder.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+
+/**
+ * Implementation of a sort algorithm for computing the project order. This
+ * algorithm handles cycles in the project reference graph in a reasonable way.
+ *
+ * @since 2.1
+ */
+class ComputeProjectOrder {
+
+ /*
+ * Prevent class from being instantiated.
+ */
+ private ComputeProjectOrder() {
+ // not allowed
+ }
+
+ /**
+ * A directed graph. Once the vertexes and edges of the graph have been
+ * defined, the graph can be queried for the depth-first finish time of each
+ * vertex.
+ * <p>
+ * Ref: Cormen, Leiserson, and Rivest <it>Introduction to Algorithms</it>,
+ * McGraw-Hill, 1990. The depth-first search algorithm is in section 23.3.
+ * </p>
+ */
+ private static class Digraph {
+ /**
+ * struct-like object for representing a vertex along with various
+ * values computed during depth-first search (DFS).
+ */
+ public static class Vertex {
+ /**
+ * White is for marking vertexes as unvisited.
+ */
+ public static final String WHITE = "white"; //$NON-NLS-1$
+
+ /**
+ * Grey is for marking vertexes as discovered but visit not yet
+ * finished.
+ */
+ public static final String GREY = "grey"; //$NON-NLS-1$
+
+ /**
+ * Black is for marking vertexes as visited.
+ */
+ public static final String BLACK = "black"; //$NON-NLS-1$
+
+ /**
+ * Color of the vertex. One of <code>WHITE</code> (unvisited),
+ * <code>GREY</code> (visit in progress), or <code>BLACK</code>
+ * (visit finished). <code>WHITE</code> initially.
+ */
+ public String color = WHITE;
+
+ /**
+ * The DFS predecessor vertex, or <code>null</code> if there is no
+ * predecessor. <code>null</code> initially.
+ */
+ public Vertex predecessor = null;
+
+ /**
+ * Timestamp indicating when the vertex was finished (became BLACK)
+ * in the DFS. Finish times are between 1 and the number of
+ * vertexes.
+ */
+ public int finishTime;
+
+ /**
+ * The id of this vertex.
+ */
+ public Object id;
+
+ /**
+ * Ordered list of adjacent vertexes. In other words, "this" is the
+ * "from" vertex and the elements of this list are all "to"
+ * vertexes.
+ *
+ * Element type: <code>Vertex</code>
+ */
+ public List adjacent = new ArrayList(3);
+
+ /**
+ * Creates a new vertex with the given id.
+ *
+ * @param id the vertex id
+ */
+ public Vertex(Object id) {
+ this.id = id;
+ }
+ }
+
+ /**
+ * Ordered list of all vertexes in this graph.
+ *
+ * Element type: <code>Vertex</code>
+ */
+ private List vertexList = new ArrayList(100);
+
+ /**
+ * Map from id to vertex.
+ *
+ * Key type: <code>Object</code>; value type: <code>Vertex</code>
+ */
+ private Map vertexMap = new HashMap(100);
+
+ /**
+ * DFS visit time. Non-negative.
+ */
+ private int time;
+
+ /**
+ * Indicates whether the graph has been initialized. Initially
+ * <code>false</code>.
+ */
+ private boolean initialized = false;
+
+ /**
+ * Indicates whether the graph contains cycles. Initially
+ * <code>false</code>.
+ */
+ private boolean cycles = false;
+
+ /**
+ * Creates a new empty directed graph object.
+ * <p>
+ * After this graph's vertexes and edges are defined with
+ * <code>addVertex</code> and <code>addEdge</code>, call
+ * <code>freeze</code> to indicate that the graph is all there, and then
+ * call <code>idsByDFSFinishTime</code> to read off the vertexes ordered
+ * by DFS finish time.
+ * </p>
+ */
+ public Digraph() {
+ super();
+ }
+
+ /**
+ * Freezes this graph. No more vertexes or edges can be added to this
+ * graph after this method is called. Has no effect if the graph is
+ * already frozen.
+ */
+ public void freeze() {
+ if (!initialized) {
+ initialized = true;
+ // only perform depth-first-search once
+ DFS();
+ }
+ }
+
+ /**
+ * Defines a new vertex with the given id. The depth-first search is
+ * performed in the relative order in which vertexes were added to the
+ * graph.
+ *
+ * @param id the id of the vertex
+ * @exception IllegalArgumentException if the vertex id is
+ * already defined or if the graph is frozen
+ */
+ public void addVertex(Object id) throws IllegalArgumentException {
+ if (initialized) {
+ throw new IllegalArgumentException();
+ }
+ Vertex vertex = new Vertex(id);
+ Object existing = vertexMap.put(id, vertex);
+ // nip problems with duplicate vertexes in the bud
+ if (existing != null) {
+ throw new IllegalArgumentException();
+ }
+ vertexList.add(vertex);
+ }
+
+ /**
+ * Adds a new directed edge between the vertexes with the given ids.
+ * Vertexes for the given ids must be defined beforehand with
+ * <code>addVertex</code>. The depth-first search is performed in the
+ * relative order in which adjacent "to" vertexes were added to a given
+ * "from" index.
+ *
+ * @param fromId the id of the "from" vertex
+ * @param toId the id of the "to" vertex
+ * @exception IllegalArgumentException if either vertex is undefined or
+ * if the graph is frozen
+ */
+ public void addEdge(Object fromId, Object toId) throws IllegalArgumentException {
+ if (initialized) {
+ throw new IllegalArgumentException();
+ }
+ Vertex fromVertex = (Vertex) vertexMap.get(fromId);
+ Vertex toVertex = (Vertex) vertexMap.get(toId);
+ // nip problems with bogus vertexes in the bud
+ if (fromVertex == null) {
+ throw new IllegalArgumentException();
+ }
+ if (toVertex == null) {
+ throw new IllegalArgumentException();
+ }
+ fromVertex.adjacent.add(toVertex);
+ }
+
+ /**
+ * Returns the ids of the vertexes in this graph ordered by depth-first
+ * search finish time. The graph must be frozen.
+ *
+ * @param increasing <code>true</code> if objects are to be arranged
+ * into increasing order of depth-first search finish time, and
+ * <code>false</code> if objects are to be arranged into decreasing
+ * order of depth-first search finish time
+ * @return the list of ids ordered by depth-first search finish time
+ * (element type: <code>Object</code>)
+ * @exception IllegalArgumentException if the graph is not frozen
+ */
+ public List idsByDFSFinishTime(boolean increasing) {
+ if (!initialized) {
+ throw new IllegalArgumentException();
+ }
+ int len = vertexList.size();
+ Object[] r = new Object[len];
+ for (Iterator allV = vertexList.iterator(); allV.hasNext();) {
+ Vertex vertex = (Vertex) allV.next();
+ int f = vertex.finishTime;
+ // note that finish times start at 1, not 0
+ if (increasing) {
+ r[f - 1] = vertex.id;
+ } else {
+ r[len - f] = vertex.id;
+ }
+ }
+ return Arrays.asList(r);
+ }
+
+ /**
+ * Returns whether the graph contains cycles. The graph must be frozen.
+ *
+ * @return <code>true</code> if this graph contains at least one cycle,
+ * and <code>false</code> if this graph is cycle free
+ * @exception IllegalArgumentException if the graph is not frozen
+ */
+ public boolean containsCycles() {
+ if (!initialized) {
+ throw new IllegalArgumentException();
+ }
+ return cycles;
+ }
+
+ /**
+ * Returns the non-trivial components of this graph. A non-trivial
+ * component is a set of 2 or more vertexes that were traversed
+ * together. The graph must be frozen.
+ *
+ * @return the possibly empty list of non-trivial components, where
+ * each component is an array of ids (element type:
+ * <code>Object[]</code>)
+ * @exception IllegalArgumentException if the graph is not frozen
+ */
+ public List nonTrivialComponents() {
+ if (!initialized) {
+ throw new IllegalArgumentException();
+ }
+ // find the roots of each component
+ // Map<Vertex,List<Object>> components
+ Map components = new HashMap();
+ for (Iterator it = vertexList.iterator(); it.hasNext();) {
+ Vertex vertex = (Vertex) it.next();
+ if (vertex.predecessor == null) {
+ // this vertex is the root of a component
+ // if component is non-trivial we will hit a child
+ } else {
+ // find the root ancestor of this vertex
+ Vertex root = vertex;
+ while (root.predecessor != null) {
+ root = root.predecessor;
+ }
+ List component = (List) components.get(root);
+ if (component == null) {
+ component = new ArrayList(2);
+ component.add(root.id);
+ components.put(root, component);
+ }
+ component.add(vertex.id);
+ }
+ }
+ List result = new ArrayList(components.size());
+ for (Iterator it = components.values().iterator(); it.hasNext();) {
+ List component = (List) it.next();
+ if (component.size() > 1) {
+ result.add(component.toArray());
+ }
+ }
+ return result;
+ }
+
+ // /**
+ // * Performs a depth-first search of this graph and records interesting
+ // * info with each vertex, including DFS finish time. Employs a recursive
+ // * helper method <code>DFSVisit</code>.
+ // * <p>
+ // * Although this method is not used, it is the basis of the
+ // * non-recursive <code>DFS</code> method.
+ // * </p>
+ // */
+ // private void recursiveDFS() {
+ // // initialize
+ // // all vertex.color initially Vertex.WHITE;
+ // // all vertex.predecessor initially null;
+ // time = 0;
+ // for (Iterator allV = vertexList.iterator(); allV.hasNext();) {
+ // Vertex nextVertex = (Vertex) allV.next();
+ // if (nextVertex.color == Vertex.WHITE) {
+ // DFSVisit(nextVertex);
+ // }
+ // }
+ // }
+ //
+ // /**
+ // * Helper method. Performs a depth first search of this graph.
+ // *
+ // * @param vertex the vertex to visit
+ // */
+ // private void DFSVisit(Vertex vertex) {
+ // // mark vertex as discovered
+ // vertex.color = Vertex.GREY;
+ // List adj = vertex.adjacent;
+ // for (Iterator allAdjacent=adj.iterator(); allAdjacent.hasNext();) {
+ // Vertex adjVertex = (Vertex) allAdjacent.next();
+ // if (adjVertex.color == Vertex.WHITE) {
+ // // explore edge from vertex to adjVertex
+ // adjVertex.predecessor = vertex;
+ // DFSVisit(adjVertex);
+ // } else if (adjVertex.color == Vertex.GREY) {
+ // // back edge (grey vertex means visit in progress)
+ // cycles = true;
+ // }
+ // }
+ // // done exploring vertex
+ // vertex.color = Vertex.BLACK;
+ // time++;
+ // vertex.finishTime = time;
+ // }
+
+ /**
+ * Performs a depth-first search of this graph and records interesting
+ * info with each vertex, including DFS finish time. Does not employ
+ * recursion.
+ */
+ private void DFS() {
+ // state machine rendition of the standard recursive DFS algorithm
+ int state;
+ final int NEXT_VERTEX = 1;
+ final int START_DFS_VISIT = 2;
+ final int NEXT_ADJACENT = 3;
+ final int AFTER_NEXTED_DFS_VISIT = 4;
+ // use precomputed objects to avoid garbage
+ final Integer NEXT_VERTEX_OBJECT = new Integer(NEXT_VERTEX);
+ final Integer AFTER_NEXTED_DFS_VISIT_OBJECT = new Integer(AFTER_NEXTED_DFS_VISIT);
+ // initialize
+ // all vertex.color initially Vertex.WHITE;
+ // all vertex.predecessor initially null;
+ time = 0;
+ // for a stack, append to the end of an array-based list
+ List stack = new ArrayList(Math.max(1, vertexList.size()));
+ Iterator allAdjacent = null;
+ Vertex vertex = null;
+ Iterator allV = vertexList.iterator();
+ state = NEXT_VERTEX;
+ nextStateLoop: while (true) {
+ switch (state) {
+ case NEXT_VERTEX :
+ // on entry, "allV" contains vertexes yet to be visited
+ if (!allV.hasNext()) {
+ // all done
+ break nextStateLoop;
+ }
+ Vertex nextVertex = (Vertex) allV.next();
+ if (nextVertex.color == Vertex.WHITE) {
+ stack.add(NEXT_VERTEX_OBJECT);
+ vertex = nextVertex;
+ state = START_DFS_VISIT;
+ continue nextStateLoop;
+ }
+ //else
+ state = NEXT_VERTEX;
+ continue nextStateLoop;
+ case START_DFS_VISIT :
+ // on entry, "vertex" contains the vertex to be visited
+ // top of stack is return code
+ // mark the vertex as discovered
+ vertex.color = Vertex.GREY;
+ allAdjacent = vertex.adjacent.iterator();
+ state = NEXT_ADJACENT;
+ continue nextStateLoop;
+ case NEXT_ADJACENT :
+ // on entry, "allAdjacent" contains adjacent vertexes to
+ // be visited; "vertex" contains vertex being visited
+ if (allAdjacent.hasNext()) {
+ Vertex adjVertex = (Vertex) allAdjacent.next();
+ if (adjVertex.color == Vertex.WHITE) {
+ // explore edge from vertex to adjVertex
+ adjVertex.predecessor = vertex;
+ stack.add(allAdjacent);
+ stack.add(vertex);
+ stack.add(AFTER_NEXTED_DFS_VISIT_OBJECT);
+ vertex = adjVertex;
+ state = START_DFS_VISIT;
+ continue nextStateLoop;
+ }
+ if (adjVertex.color == Vertex.GREY) {
+ // back edge (grey means visit in progress)
+ cycles = true;
+ }
+ state = NEXT_ADJACENT;
+ continue nextStateLoop;
+ }
+ //else done exploring vertex
+ vertex.color = Vertex.BLACK;
+ time++;
+ vertex.finishTime = time;
+ state = ((Integer) stack.remove(stack.size() - 1)).intValue();
+ continue nextStateLoop;
+ case AFTER_NEXTED_DFS_VISIT :
+ // on entry, stack contains "vertex" and "allAjacent"
+ vertex = (Vertex) stack.remove(stack.size() - 1);
+ allAdjacent = (Iterator) stack.remove(stack.size() - 1);
+ state = NEXT_ADJACENT;
+ continue nextStateLoop;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Sorts the given list of probject in a manner that honors the given
+ * project reference relationships. That is, if project A references project
+ * B, then the resulting order will list B before A if possible. For graphs
+ * that do not contain cycles, the result is the same as a conventional
+ * topological sort. For graphs containing cycles, the order is based on
+ * ordering the strongly connected components of the graph. This has the
+ * effect of keeping each knot of projects together without otherwise
+ * affecting the order of projects not involved in a cycle. For a graph G,
+ * the algorithm performs in O(|G|) space and time.
+ * <p>
+ * When there is an arbitrary choice, vertexes are ordered as supplied.
+ * Arranged projects in descending alphabetical order generally results in
+ * an order that builds "A" before "Z" when there are no other constraints.
+ * </p>
+ * <p> Ref: Cormen, Leiserson, and Rivest <it>Introduction to
+ * Algorithms</it>, McGraw-Hill, 1990. The strongly-connected-components
+ * algorithm is in section 23.5.
+ * </p>
+ *
+ * @param projects a list of projects (element type:
+ * <code>IProject</code>)
+ * @param references a list of project references [A,B] meaning that A
+ * references B (element type: <code>IProject[]</code>)
+ * @return an object describing the resulting project order
+ */
+ static IWorkspace.ProjectOrder computeProjectOrder(SortedSet projects, List references) {
+
+ // Step 1: Create the graph object.
+ final Digraph g1 = new Digraph();
+ // add vertexes
+ for (Iterator it = projects.iterator(); it.hasNext();) {
+ IProject project = (IProject) it.next();
+ g1.addVertex(project);
+ }
+ // add edges
+ for (Iterator it = references.iterator(); it.hasNext();) {
+ IProject[] ref = (IProject[]) it.next();
+ IProject p = ref[0];
+ IProject q = ref[1];
+ // p has a project reference to q
+ // therefore create an edge from q to p
+ // to cause q to come before p in eventual result
+ g1.addEdge(q, p);
+ }
+ g1.freeze();
+
+ // Step 2: Create the transposed graph. This time, define the vertexes
+ // in decreasing order of depth-first finish time in g1
+ // interchange "to" and "from" to reverse edges from g1
+ final Digraph g2 = new Digraph();
+ // add vertexes
+ List resortedVertexes = g1.idsByDFSFinishTime(false);
+ for (Iterator it = resortedVertexes.iterator(); it.hasNext();) {
+ final IProject project = (IProject) it.next();
+ g2.addVertex(project);
+ }
+ // add edges
+ for (Iterator it = references.iterator(); it.hasNext();) {
+ IProject[] ref = (IProject[]) it.next();
+ IProject p = ref[0];
+ IProject q = ref[1];
+ // p has a project reference to q
+ // therefore create an edge from p to q
+ // N.B. this is the reverse of step 1
+ g2.addEdge(p, q);
+ }
+ g2.freeze();
+
+ // Step 3: Return the vertexes in increasing order of depth-first finish
+ // time in g2
+ List sortedProjectList = g2.idsByDFSFinishTime(true);
+ IProject[] orderedProjects = new IProject[sortedProjectList.size()];
+ sortedProjectList.toArray(orderedProjects);
+ IProject[][] knots;
+ boolean hasCycles = g2.containsCycles();
+ if (hasCycles) {
+ List knotList = g2.nonTrivialComponents();
+ knots = new IProject[knotList.size()][];
+ // cannot use knotList.toArray(knots) because each knot is Object[]
+ // and we need each to be an IProject[]
+ int k = 0;
+ for (Iterator it = knotList.iterator(); it.hasNext();) {
+ Object[] knot = (Object[]) it.next();
+ IProject[] knotCopy = new IProject[knot.length];
+ for (int i = 0; i < knot.length; i++) {
+ knotCopy[i] = (IProject) knot[i];
+ }
+ knots[k] = knotCopy;
+ k++;
+ }
+ } else {
+ knots = new IProject[][] {};
+ }
+ return new IWorkspace.ProjectOrder(orderedProjects, hasCycles, knots);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Container.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,298 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.localstore.IHistoryStore;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+
+public abstract class Container extends Resource implements IContainer {
+ protected Container(IPath path, Workspace container) {
+ super(path, container);
+ }
+
+ /**
+ * Converts this resource and all its children into phantoms by modifying
+ * their resource infos in-place.
+ */
+ public void convertToPhantom() throws CoreException {
+ if (isPhantom())
+ return;
+ super.convertToPhantom();
+ IResource[] members = members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < members.length; i++)
+ ((Resource) members[i]).convertToPhantom();
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#exists(IPath)
+ */
+ public boolean exists(IPath childPath) {
+ return workspace.getResourceInfo(getFullPath().append(childPath), false, false) != null;
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#findMember(String)
+ */
+ public IResource findMember(String name) {
+ return findMember(name, false);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#findMember(String, boolean)
+ */
+ public IResource findMember(String name, boolean phantom) {
+ IPath childPath = getFullPath().append(name);
+ ResourceInfo info = workspace.getResourceInfo(childPath, phantom, false);
+ return info == null ? null : workspace.newResource(childPath, info.getType());
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#findMember(IPath)
+ */
+ public IResource findMember(IPath childPath) {
+ return findMember(childPath, false);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#findMember(IPath)
+ */
+ public IResource findMember(IPath childPath, boolean phantom) {
+ childPath = getFullPath().append(childPath);
+ ResourceInfo info = workspace.getResourceInfo(childPath, phantom, false);
+ return (info == null) ? null : workspace.newResource(childPath, info.getType());
+ }
+
+ protected void fixupAfterMoveSource() throws CoreException {
+ super.fixupAfterMoveSource();
+ if (!synchronizing(getResourceInfo(true, false)))
+ return;
+ IResource[] members = members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < members.length; i++)
+ ((Resource) members[i]).fixupAfterMoveSource();
+ }
+
+ protected IResource[] getChildren(int memberFlags) {
+ IPath[] children = null;
+ try {
+ children = workspace.tree.getChildren(path);
+ } catch (IllegalArgumentException e) {
+ //concurrency problem: the container has been deleted by another
+ //thread during this call. Just return empty children set
+ }
+ if (children == null || children.length == 0)
+ return ICoreConstants.EMPTY_RESOURCE_ARRAY;
+ Resource[] result = new Resource[children.length];
+ int found = 0;
+ for (int i = 0; i < children.length; i++) {
+ ResourceInfo info = workspace.getResourceInfo(children[i], true, false);
+ if (info != null && isMember(info.getFlags(), memberFlags))
+ result[found++] = workspace.newResource(children[i], info.getType());
+ }
+ if (found == result.length)
+ return result;
+ Resource[] trimmedResult = new Resource[found];
+ System.arraycopy(result, 0, trimmedResult, 0, found);
+ return trimmedResult;
+ }
+
+ /* (non-Javadoc)
+ * @see IFolder#getFile(String) and IProject#getFile(String)
+ */
+ public IFile getFile(String name) {
+ return (IFile) workspace.newResource(getFullPath().append(name), FILE);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#getFile(IPath)
+ */
+ public IFile getFile(IPath childPath) {
+ return (IFile) workspace.newResource(getFullPath().append(childPath), FILE);
+ }
+
+ /* (non-Javadoc)
+ * @see IFolder#getFolder(String) and IProject#getFolder(String)
+ */
+ public IFolder getFolder(String name) {
+ return (IFolder) workspace.newResource(getFullPath().append(name), FOLDER);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#getFolder(IPath)
+ */
+ public IFolder getFolder(IPath childPath) {
+ return (IFolder) workspace.newResource(getFullPath().append(childPath), FOLDER);
+ }
+
+ /**
+ * @deprecated
+ */
+ public boolean isLocal(int flags, int depth) {
+ if (!super.isLocal(flags, depth))
+ return false;
+ if (depth == DEPTH_ZERO)
+ return true;
+ if (depth == DEPTH_ONE)
+ depth = DEPTH_ZERO;
+ // get the children via the workspace since we know that this
+ // resource exists (it is local).
+ IResource[] children = getChildren(IResource.NONE);
+ for (int i = 0; i < children.length; i++)
+ if (!children[i].isLocal(depth))
+ return false;
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#members()
+ */
+ public IResource[] members() throws CoreException {
+ // forward to central method
+ return members(IResource.NONE);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#members(boolean)
+ */
+ public IResource[] members(boolean phantom) throws CoreException {
+ // forward to central method
+ return members(phantom ? INCLUDE_PHANTOMS : IResource.NONE);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#members(int)
+ */
+ public IResource[] members(int memberFlags) throws CoreException {
+ final boolean phantom = (memberFlags & INCLUDE_PHANTOMS) != 0;
+ ResourceInfo info = getResourceInfo(phantom, false);
+ checkAccessible(getFlags(info));
+ //if children are currently unknown, ask for immediate refresh
+ if (info.isSet(ICoreConstants.M_CHILDREN_UNKNOWN))
+ workspace.refreshManager.refresh(this);
+ return getChildren(memberFlags);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#getDefaultCharset()
+ */
+ public String getDefaultCharset() throws CoreException {
+ return getDefaultCharset(true);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#findDeletedMembersWithHistory(int, IProgressMonitor)
+ */
+ public IFile[] findDeletedMembersWithHistory(int depth, IProgressMonitor monitor) {
+ IHistoryStore historyStore = getLocalManager().getHistoryStore();
+ IPath basePath = getFullPath();
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ Set deletedFiles = new HashSet();
+
+ if (depth == IResource.DEPTH_ZERO) {
+ // this folder might have been a file in a past life
+ if (historyStore.getStates(basePath, monitor).length > 0) {
+ IFile file = root.getFile(basePath);
+ if (!file.exists()) {
+ deletedFiles.add(file);
+ }
+ }
+ } else {
+ Set allFilePaths = historyStore.allFiles(basePath, depth, monitor);
+ // convert IPaths to IFiles keeping only files that no longer exist
+ for (Iterator it = allFilePaths.iterator(); it.hasNext();) {
+ IPath filePath = (IPath) it.next();
+ IFile file = root.getFile(filePath);
+ if (!file.exists()) {
+ deletedFiles.add(file);
+ }
+ }
+ }
+ return (IFile[]) deletedFiles.toArray(new IFile[deletedFiles.size()]);
+ }
+
+ /** (non-Javadoc)
+ * @see IContainer#setDefaultCharset(String)
+ * @deprecated Replaced by {@link #setDefaultCharset(String, IProgressMonitor)} which
+ * is a workspace operation and reports changes in resource deltas.
+ */
+ public void setDefaultCharset(String charset) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ workspace.getCharsetManager().setCharsetFor(getFullPath(), charset);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#setDefaultCharset(String, IProgressMonitor)
+ */
+ public void setDefaultCharset(String newCharset, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_settingDefaultCharsetContainer, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ // need to get the project as a scheduling rule because we might be
+ // creating a new folder/file to hold the project settings
+ final ISchedulingRule rule = workspace.getRuleFactory().charsetRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ checkAccessible(getFlags(getResourceInfo(false, false)));
+ workspace.beginOperation(true);
+ workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
+ // now propagate the changes to all children inheriting their setting from this container
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ boolean visitedRoot = false;
+
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ if (elementContents == null)
+ return false;
+ IPath nodePath = requestor.requestPath();
+ // we will always generate an event at least for the root of the sub tree
+ // (skip visiting the root because we already have set the charset above and
+ // that is the condition we are checking later)
+ if (!visitedRoot) {
+ visitedRoot = true;
+ ResourceInfo info = workspace.getResourceInfo(nodePath, false, true);
+ if (info == null)
+ return false;
+ info.incrementCharsetGenerationCount();
+ return true;
+ }
+ // does it already have an encoding explicitly set?
+ if (workspace.getCharsetManager().getCharsetFor(nodePath, false) != null)
+ return false;
+ ResourceInfo info = workspace.getResourceInfo(nodePath, false, true);
+ if (info == null)
+ return false;
+ info.incrementCharsetGenerationCount();
+ return true;
+ }
+ };
+ try {
+ new ElementTreeIterator(workspace.getElementTree(), getFullPath()).iterate(visitor);
+ } catch (WrappedRuntimeException e) {
+ throw (CoreException) e.getTargetException();
+ }
+ monitor.worked(Policy.opWork);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ContentDescriptionManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,520 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.FileUtil;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.events.ILifecycleListener;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.*;
+import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+
+/**
+ * Keeps a cache of recently read content descriptions.
+ *
+ * @since 3.0
+ * @see IFile#getContentDescription()
+ */
+public class ContentDescriptionManager implements IManager, IRegistryChangeListener, IContentTypeManager.IContentTypeChangeListener, ILifecycleListener {
+ /**
+ * This job causes the content description cache and the related flags
+ * in the resource tree to be flushed.
+ */
+ private class FlushJob extends WorkspaceJob {
+ private final List toFlush;
+ private boolean fullFlush;
+
+ public FlushJob() {
+ super(Messages.resources_flushingContentDescriptionCache);
+ setSystem(true);
+ setUser(false);
+ setPriority(LONG);
+ setRule(workspace.getRoot());
+ toFlush = new ArrayList(5);
+ }
+
+ /* (non-Javadoc)
+ * See Job#belongsTo(Object)
+ */
+ public boolean belongsTo(Object family) {
+ return FAMILY_DESCRIPTION_CACHE_FLUSH.equals(family);
+ }
+
+ /* (non-Javadoc)
+ * See WorkspaceJob#runInWorkspace(IProgressMonitor)
+ */
+ public IStatus runInWorkspace(final IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ return Status.CANCEL_STATUS;
+ try {
+ monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
+ //note that even though we are running in a workspace job, we
+ //must do a begin/endOperation to re-acquire the workspace lock
+ final ISchedulingRule rule = workspace.getRoot();
+ try {
+ workspace.prepareOperation(rule, monitor);
+ workspace.beginOperation(true);
+ //don't do anything if the system is shutting down or has been shut down
+ //it is too late to change the workspace at this point anyway
+ if (systemBundle.getState() != Bundle.STOPPING)
+ doFlushCache(monitor, getPathsToFlush());
+ } finally {
+ workspace.endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } catch (OperationCanceledException e) {
+ return Status.CANCEL_STATUS;
+ } catch (CoreException e) {
+ return e.getStatus();
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+
+ private IPath[] getPathsToFlush() {
+ synchronized (toFlush) {
+ try {
+ if (fullFlush)
+ return null;
+ int size = toFlush.size();
+ return (size == 0) ? null : (IPath[]) toFlush.toArray(new IPath[size]);
+ } finally {
+ fullFlush = false;
+ toFlush.clear();
+ }
+ }
+ }
+
+ /**
+ * @param project project to flush, or null for a full flush
+ */
+ void flush(IProject project) {
+ if (Policy.DEBUG_CONTENT_TYPE_CACHE)
+ Policy.debug("Scheduling flushing of content type cache for " + (project == null ? Path.ROOT : project.getFullPath())); //$NON-NLS-1$
+ synchronized (toFlush) {
+ if (!fullFlush)
+ if (project == null)
+ fullFlush = true;
+ else
+ toFlush.add(project.getFullPath());
+ }
+ schedule(1000);
+ }
+
+ }
+
+ /**
+ * An input stream that only opens the file if bytes are actually requested.
+ * @see #readDescription(File)
+ */
+ class LazyFileInputStream extends InputStream {
+ private InputStream actual;
+ private IFileStore target;
+
+ LazyFileInputStream(IFileStore target) {
+ this.target = target;
+ }
+
+ public int available() throws IOException {
+ if (actual == null)
+ return 0;
+ return actual.available();
+ }
+
+ public void close() throws IOException {
+ if (actual == null)
+ return;
+ actual.close();
+ }
+
+ private void ensureOpened() throws IOException {
+ if (actual != null)
+ return;
+ if (target == null)
+ throw new FileNotFoundException();
+ try {
+ actual = target.openInputStream(EFS.NONE, null);
+ } catch (CoreException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public int read() throws IOException {
+ ensureOpened();
+ return actual.read();
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ ensureOpened();
+ return actual.read(b, off, len);
+ }
+
+ public long skip(long n) throws IOException {
+ ensureOpened();
+ return actual.skip(n);
+ }
+ }
+
+ private static final QualifiedName CACHE_STATE = new QualifiedName(ResourcesPlugin.PI_RESOURCES, "contentCacheState"); //$NON-NLS-1$
+ private static final QualifiedName CACHE_TIMESTAMP = new QualifiedName(ResourcesPlugin.PI_RESOURCES, "contentCacheTimestamp"); //$NON-NLS-1$\
+
+ public static final String FAMILY_DESCRIPTION_CACHE_FLUSH = ResourcesPlugin.PI_RESOURCES + ".contentDescriptionCacheFamily"; //$NON-NLS-1$
+
+ //possible values for the CACHE_STATE property
+ public static final byte EMPTY_CACHE = 1;
+ public static final byte USED_CACHE = 2;
+ public static final byte INVALID_CACHE = 3;
+ public static final byte FLUSHING_CACHE = 4;
+
+ // This state indicates that FlushJob is scheduled and full flush is going to be performed.
+ // In the meantime the cache was discarded. It is used as a temporary cache till the FlushJob start.
+ public static final byte ABOUT_TO_FLUSH = 5;
+
+ private static final String PT_CONTENTTYPES = "contentTypes"; //$NON-NLS-1$
+
+ private Cache cache;
+
+ private byte cacheState;
+
+ private FlushJob flushJob;
+ private ProjectContentTypes projectContentTypes;
+
+ Workspace workspace;
+ protected final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
+
+ /**
+ * @see IContentTypeManager.IContentTypeChangeListener#contentTypeChanged(IContentTypeManager.ContentTypeChangeEvent)
+ */
+ public void contentTypeChanged(ContentTypeChangeEvent event) {
+ if (Policy.DEBUG_CONTENT_TYPE)
+ Policy.debug("Content type settings changed for " + event.getContentType()); //$NON-NLS-1$
+ invalidateCache(true, null);
+ }
+
+ synchronized void doFlushCache(final IProgressMonitor monitor, IPath[] toClean) throws CoreException {
+ // nothing to be done if no information cached
+ if (getCacheState() != INVALID_CACHE && getCacheState() != ABOUT_TO_FLUSH) {
+ if (Policy.DEBUG_CONTENT_TYPE_CACHE)
+ Policy.debug("Content type cache flush not performed"); //$NON-NLS-1$
+ return;
+ }
+ try {
+ setCacheState(FLUSHING_CACHE);
+ // flush the MRU cache
+ cache.discardAll();
+ if (toClean == null || toClean.length == 0)
+ // no project was added, must be a global flush
+ clearContentFlags(Path.ROOT, monitor);
+ else {
+ // flush a project at a time
+ for (int i = 0; i < toClean.length; i++)
+ clearContentFlags(toClean[i], monitor);
+ }
+ } catch (CoreException ce) {
+ setCacheState(INVALID_CACHE);
+ throw ce;
+ }
+ // done cleaning (only if we didn't fail)
+ setCacheState(EMPTY_CACHE);
+ }
+
+ /**
+ * Clears the content related flags for every file under the given root.
+ */
+ private void clearContentFlags(IPath root, final IProgressMonitor monitor) {
+ long flushStart = System.currentTimeMillis();
+ if (Policy.DEBUG_CONTENT_TYPE_CACHE)
+ Policy.debug("Flushing content type cache for " + root); //$NON-NLS-1$
+ // discard content type related flags for all files in the tree
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ if (elementContents == null)
+ return false;
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (info.getType() != IResource.FILE)
+ return true;
+ info = workspace.getResourceInfo(requestor.requestPath(), false, true);
+ if (info == null)
+ return false;
+ info.clear(ICoreConstants.M_CONTENT_CACHE);
+ return true;
+ }
+ };
+ new ElementTreeIterator(workspace.getElementTree(), root).iterate(visitor);
+ if (Policy.DEBUG_CONTENT_TYPE_CACHE)
+ Policy.debug("Content type cache for " + root + " flushed in " + (System.currentTimeMillis() - flushStart) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ Cache getCache() {
+ return cache;
+ }
+
+ /** Public so tests can examine it. */
+ public synchronized byte getCacheState() {
+ if (cacheState != 0)
+ // we have read/set it before, no nead to read property
+ return cacheState;
+ String persisted;
+ try {
+ persisted = workspace.getRoot().getPersistentProperty(CACHE_STATE);
+ cacheState = persisted != null ? Byte.parseByte(persisted) : INVALID_CACHE;
+ } catch (NumberFormatException e) {
+ cacheState = INVALID_CACHE;
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ cacheState = INVALID_CACHE;
+ }
+ return cacheState;
+ }
+
+ public long getCacheTimestamp() throws CoreException {
+ try {
+ return Long.parseLong(workspace.getRoot().getPersistentProperty(CACHE_TIMESTAMP));
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ public IContentTypeMatcher getContentTypeMatcher(Project project) throws CoreException {
+ return projectContentTypes.getMatcherFor(project);
+ }
+
+ public IContentDescription getDescriptionFor(File file, ResourceInfo info) throws CoreException {
+ if (ProjectContentTypes.usesContentTypePreferences(file.getFullPath().segment(0)))
+ // caching for project containing project specific settings is not supported
+ return readDescription(file);
+ if (getCacheState() == INVALID_CACHE) {
+ // discard the cache, so it can be used before the flush job starts
+ setCacheState(ABOUT_TO_FLUSH);
+ cache.discardAll();
+ // the cache is not good, flush it
+ flushJob.schedule(1000);
+ }
+ if (getCacheState() != ABOUT_TO_FLUSH) {
+ // first look for the flags in the resource info to avoid looking in the cache
+ // don't need to copy the info because the modified bits are not in the deltas
+ if (info == null)
+ return null;
+ if (info.isSet(ICoreConstants.M_NO_CONTENT_DESCRIPTION))
+ // presumably, this file has no known content type
+ return null;
+ if (info.isSet(ICoreConstants.M_DEFAULT_CONTENT_DESCRIPTION)) {
+ // this file supposedly has a default content description for an "obvious" content type
+ IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+ // try to find the obvious content type matching its name
+ IContentType type = contentTypeManager.findContentTypeFor(file.getName());
+ if (type != null)
+ // we found it, we are done
+ return type.getDefaultDescription();
+ // for some reason, there was no content type for this file name
+ // fix this and keep going
+ info.clear(ICoreConstants.M_CONTENT_CACHE);
+ }
+ }
+ synchronized (this) {
+ // tries to get a description from the cache
+ Cache.Entry entry = cache.getEntry(file.getFullPath());
+ if (entry != null && entry.getTimestamp() == getTimestamp(info))
+ // there was a description in the cache, and it was up to date
+ return (IContentDescription) entry.getCached();
+ }
+
+ // either we didn't find a description in the cache, or it was not up-to-date - has to be read again
+ // reading description can call 3rd party code, so don't synchronize it
+ IContentDescription newDescription = readDescription(file);
+
+ synchronized (this) {
+ // tries to get a description from the cache
+ Cache.Entry entry = cache.getEntry(file.getFullPath());
+ if (entry != null && entry.getTimestamp() == getTimestamp(info))
+ // there was a description in the cache, and it was up to date
+ return (IContentDescription) entry.getCached();
+
+ if (getCacheState() != ABOUT_TO_FLUSH) {
+ // we are going to add an entry to the cache or update the resource info - remember that
+ setCacheState(USED_CACHE);
+ if (newDescription == null) {
+ // no content type exists for this file name/contents - remember this
+ info.set(ICoreConstants.M_NO_CONTENT_DESCRIPTION);
+ return null;
+ }
+ if (newDescription.getContentType().getDefaultDescription().equals(newDescription)) {
+ // we got a default description
+ IContentType defaultForName = Platform.getContentTypeManager().findContentTypeFor(file.getName());
+ if (newDescription.getContentType().equals(defaultForName)) {
+ // it is a default description for the obvious content type given its file name, we don't have to cache
+ info.set(ICoreConstants.M_DEFAULT_CONTENT_DESCRIPTION);
+ return newDescription;
+ }
+ }
+ }
+ // we actually got a description filled by a describer (or a default description for a non-obvious type)
+ if (entry == null)
+ // there was no entry before - create one
+ entry = cache.addEntry(file.getFullPath(), newDescription, getTimestamp(info));
+ else {
+ // just update the existing entry
+ entry.setTimestamp(info.getContentId());
+ entry.setCached(newDescription);
+ }
+ return newDescription;
+ }
+ }
+
+ /**
+ * Returns a timestamp that uniquely identifies a particular content state
+ * of a particular resource. For use as a key in a content type cache.
+ */
+ private long getTimestamp(ResourceInfo info) {
+ return info.getContentId() + info.getNodeId();
+ }
+
+ /**
+ * Marks the cache as invalid. Does not do anything if the cache is new.
+ * Optionally causes the cached information to be actually flushed.
+ *
+ * @param flush whether the cached information should be flushed
+ * @see #doFlushCache(IProgressMonitor, IPath[])
+ */
+ public synchronized void invalidateCache(boolean flush, IProject project) {
+ if (getCacheState() == EMPTY_CACHE)
+ // cache has not been touched, nothing to do
+ return;
+ // mark the cache as invalid
+ try {
+ setCacheState(INVALID_CACHE);
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ }
+ if (Policy.DEBUG_CONTENT_TYPE_CACHE)
+ Policy.debug("Invalidated cache for " + (project == null ? Path.ROOT : project.getFullPath())); //$NON-NLS-1$
+ if (flush) {
+ try {
+ // discard the cache, so it can be used before the flush job starts
+ setCacheState(ABOUT_TO_FLUSH);
+ cache.discardAll();
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ }
+ // the cache is not good, flush it
+ flushJob.flush(project);
+ }
+ }
+
+ /**
+ * Tries to obtain a content description for the given file.
+ */
+ private IContentDescription readDescription(File file) throws CoreException {
+ if (Policy.DEBUG_CONTENT_TYPE)
+ Policy.debug("reading contents of " + file); //$NON-NLS-1$
+ // tries to obtain a description for this file contents
+ InputStream contents = new LazyFileInputStream(file.getStore());
+ try {
+ IContentTypeMatcher matcher = getContentTypeMatcher((Project) file.getProject());
+ return matcher.getDescriptionFor(contents, file.getName(), IContentDescription.ALL);
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.resources_errorContentDescription, file.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, file.getFullPath(), message, e);
+ } finally {
+ FileUtil.safeClose(contents);
+ }
+ }
+
+ /**
+ * @see IRegistryChangeListener#registryChanged(IRegistryChangeEvent)
+ */
+ public void registryChanged(IRegistryChangeEvent event) {
+ // no changes related to the content type registry
+ if (event.getExtensionDeltas(Platform.PI_RUNTIME, PT_CONTENTTYPES).length == 0)
+ return;
+ invalidateCache(true, null);
+ }
+
+ /**
+ * @see ILifecycleListener#handleEvent(LifecycleEvent)
+ */
+ public void handleEvent(LifecycleEvent event) {
+ //TODO are these the only events we care about?
+ switch (event.kind) {
+ case LifecycleEvent.PRE_PROJECT_CHANGE :
+ // if the project changes, its natures may have changed as well (content types may be associated to natures)
+ case LifecycleEvent.PRE_PROJECT_DELETE :
+ // if the project gets deleted, we may get confused if it is recreated again (content ids might match)
+ case LifecycleEvent.PRE_PROJECT_MOVE :
+ // if the project moves, resource paths (used as keys in the in-memory cache) will have changed
+ invalidateCache(true, (IProject) event.resource);
+ }
+ }
+
+ synchronized void setCacheState(byte newCacheState) throws CoreException {
+ if (cacheState == newCacheState)
+ return;
+ workspace.getRoot().setPersistentProperty(CACHE_STATE, Byte.toString(newCacheState));
+ cacheState = newCacheState;
+ }
+
+ private void setCacheTimeStamp(long timeStamp) throws CoreException {
+ workspace.getRoot().setPersistentProperty(CACHE_TIMESTAMP, Long.toString(timeStamp));
+ }
+
+ public void shutdown(IProgressMonitor monitor) throws CoreException {
+ if (getCacheState() != INVALID_CACHE)
+ // remember the platform timestamp for which we have a valid cache
+ setCacheTimeStamp(Platform.getStateStamp());
+ Platform.getContentTypeManager().removeContentTypeChangeListener(this);
+ Platform.getExtensionRegistry().removeRegistryChangeListener(this);
+ cache.dispose();
+ cache = null;
+ flushJob.cancel();
+ flushJob = null;
+ projectContentTypes = null;
+ }
+
+ public void startup(IProgressMonitor monitor) throws CoreException {
+ workspace = (Workspace) ResourcesPlugin.getWorkspace();
+ cache = new Cache(100, 1000, 0.1);
+ projectContentTypes = new ProjectContentTypes(workspace);
+ getCacheState();
+ if (cacheState == FLUSHING_CACHE || cacheState == ABOUT_TO_FLUSH)
+ // in case we died before completing the last flushing
+ setCacheState(INVALID_CACHE);
+ flushJob = new FlushJob();
+ // the cache is stale (plug-ins that might be contributing content types were added/removed)
+ if (getCacheTimestamp() != Platform.getStateStamp())
+ invalidateCache(false, null);
+ // register a lifecycle listener
+ workspace.addLifecycleListener(this);
+ // register a content type change listener
+ Platform.getContentTypeManager().addContentTypeChangeListener(this);
+ // register a registry change listener
+ Platform.getExtensionRegistry().addRegistryChangeListener(this, Platform.PI_RUNTIME);
+ }
+
+ public void projectPreferencesChanged(IProject project) {
+ if (Policy.DEBUG_CONTENT_TYPE)
+ Policy.debug("Project preferences changed for " + project); //$NON-NLS-1$
+ projectContentTypes.contentTypePreferencesChanged(project);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/DelayedSnapshotJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.ISaveContext;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * Performs periodic saving (snapshot) of the workspace.
+ */
+public class DelayedSnapshotJob extends Job {
+
+ private static final String MSG_SNAPSHOT = Messages.resources_snapshot;
+ private SaveManager saveManager;
+
+ public DelayedSnapshotJob(SaveManager manager) {
+ super(MSG_SNAPSHOT);
+ this.saveManager = manager;
+ setRule(ResourcesPlugin.getWorkspace().getRoot());
+ setSystem(true);
+ }
+
+ /*
+ * @see Job#run()
+ */
+ public IStatus run(IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ return Status.CANCEL_STATUS;
+ if (ResourcesPlugin.getWorkspace() == null)
+ return Status.OK_STATUS;
+ try {
+ return saveManager.save(ISaveContext.SNAPSHOT, null, Policy.monitorFor(null));
+ } catch (CoreException e) {
+ return e.getStatus();
+ } finally {
+ saveManager.operationCount = 0;
+ saveManager.snapshotRequested = false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,472 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.FileUtil;
+
+import java.io.*;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.internal.preferences.EclipsePreferences;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The standard implementation of {@link IFile}.
+ */
+public class File extends Resource implements IFile {
+
+ protected File(IPath path, Workspace container) {
+ super(path, container);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#appendContents(InputStream, int, IProgressMonitor)
+ */
+ public void appendContents(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_settingContents, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ Assert.isNotNull(content, "Content cannot be null."); //$NON-NLS-1$
+ if (workspace.shouldValidate)
+ workspace.validateSave(this);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ workspace.beginOperation(true);
+ IFileInfo fileInfo = getStore().fetchInfo();
+ internalSetContents(content, fileInfo, updateFlags, true, Policy.subMonitorFor(monitor, Policy.opWork));
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#appendContents(InputStream, boolean, boolean, IProgressMonitor)
+ */
+ public void appendContents(InputStream content, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ // funnel all operations to central method
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+ appendContents(content, updateFlags, monitor);
+ }
+
+ /**
+ * Changes this file to be a folder in the resource tree and returns
+ * the newly created folder. All related
+ * properties are deleted. It is assumed that on disk the resource is
+ * already a folder/directory so no action is taken to delete the disk
+ * contents.
+ * <p>
+ * <b>This method is for the exclusive use of the local resource manager</b>
+ */
+ public IFolder changeToFolder() throws CoreException {
+ getPropertyManager().deleteProperties(this, IResource.DEPTH_ZERO);
+ IFolder result = workspace.getRoot().getFolder(path);
+ if (isLinked()) {
+ IPath location = getRawLocation();
+ delete(IResource.NONE, null);
+ result.createLink(location, IResource.ALLOW_MISSING_LOCAL, null);
+ } else {
+ workspace.deleteResource(this);
+ workspace.createResource(result, false);
+ }
+ return result;
+ }
+
+ /**
+ * Checks that this resource is synchronized with the local file system.
+ */
+ private void checkSynchronized() throws CoreException {
+ if (!isSynchronized(IResource.DEPTH_ZERO)) {
+ String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, getFullPath());
+ throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, getFullPath(), message, null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#create(InputStream, int, IProgressMonitor)
+ */
+ public void create(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ final boolean monitorNull = monitor == null;
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = monitorNull ? "" : NLS.bind(Messages.resources_creating, getFullPath()); //$NON-NLS-1$
+ monitor.beginTask(message, Policy.totalWork);
+ checkValidPath(path, FILE, true);
+ final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ checkDoesNotExist();
+ Container parent = (Container) getParent();
+ ResourceInfo info = parent.getResourceInfo(false, false);
+ parent.checkAccessible(getFlags(info));
+
+ workspace.beginOperation(true);
+ IFileStore store = getStore();
+ IFileInfo localInfo = store.fetchInfo();
+ if (BitMask.isSet(updateFlags, IResource.FORCE)) {
+ if (!Workspace.caseSensitive) {
+ if (localInfo.exists()) {
+ String name = getLocalManager().getLocalName(store);
+ if (name == null || localInfo.getName().equals(name)) {
+ delete(true, null);
+ } else {
+ // The file system is not case sensitive and there is already a file
+ // under this location.
+ message = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+ throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), message, null);
+ }
+ }
+ }
+ } else {
+ if (localInfo.exists()) {
+ //return an appropriate error message for case variant collisions
+ if (!Workspace.caseSensitive) {
+ String name = getLocalManager().getLocalName(store);
+ if (name != null && !localInfo.getName().equals(name)) {
+ message = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+ throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), message, null);
+ }
+ }
+ message = NLS.bind(Messages.resources_fileExists, store.toString());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, getFullPath(), message, null);
+ }
+ }
+ monitor.worked(Policy.opWork * 40 / 100);
+
+ info = workspace.createResource(this, updateFlags);
+ boolean local = content != null;
+ if (local) {
+ try {
+ internalSetContents(content, localInfo, updateFlags, false, Policy.subMonitorFor(monitor, Policy.opWork * 60 / 100));
+ } catch (CoreException e) {
+ // a problem happened creating the file on disk, so delete from the workspace and disk
+ workspace.deleteResource(this);
+ store.delete(EFS.NONE, null);
+ throw e; // rethrow
+ }
+ }
+ internalSetLocal(local, DEPTH_ZERO);
+ if (!local)
+ getResourceInfo(true, true).clearModificationStamp();
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ FileUtil.safeClose(content);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#create(InputStream, boolean, IProgressMonitor)
+ */
+ public void create(InputStream content, boolean force, IProgressMonitor monitor) throws CoreException {
+ // funnel all operations to central method
+ create(content, (force ? IResource.FORCE : IResource.NONE), monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#getCharset()
+ */
+ public String getCharset() throws CoreException {
+ return getCharset(true);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#getCharset(boolean)
+ */
+ public String getCharset(boolean checkImplicit) throws CoreException {
+ // non-existing resources default to parent's charset
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (!exists(flags, false))
+ return checkImplicit ? workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true) : null;
+ checkLocal(flags, DEPTH_ZERO);
+ return internalGetCharset(checkImplicit, info);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#getCharsetFor(Reader)
+ */
+ public String getCharsetFor(Reader contents) throws CoreException {
+ String charset;
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (exists(flags, true))
+ // the file exists, look for user setting
+ if ((charset = workspace.getCharsetManager().getCharsetFor(getFullPath(), false)) != null)
+ // if there is a file-specific user setting, use it
+ return charset;
+ // tries to obtain a description from the contents provided
+ IContentDescription description;
+ try {
+ // TODO need to take project specific settings into account
+ IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+ description = contentTypeManager.getDescriptionFor(contents, getName(), new QualifiedName[] {IContentDescription.CHARSET});
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.resources_errorContentDescription, getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, getFullPath(), message, e);
+ }
+ if (description != null)
+ if ((charset = description.getCharset()) != null)
+ // the description contained charset info, we are done
+ return charset;
+ // could not find out the encoding based on the contents... default to parent's
+ return workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true);
+ }
+
+ private String internalGetCharset(boolean checkImplicit, ResourceInfo info) throws CoreException {
+ // if there is a file-specific user setting, use it
+ String charset = workspace.getCharsetManager().getCharsetFor(getFullPath(), false);
+ if (charset != null || !checkImplicit)
+ return charset;
+ // tries to obtain a description for the file contents
+ IContentDescription description = workspace.getContentDescriptionManager().getDescriptionFor(this, info);
+ if (description != null) {
+ String contentCharset = description.getCharset();
+ if (contentCharset != null)
+ return contentCharset;
+ }
+ // could not find out the encoding based on the contents... default to parent's
+ return workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.resources.IFile#getContentDescription()
+ */
+ public IContentDescription getContentDescription() throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ checkSynchronized();
+ checkLocal(flags, DEPTH_ZERO);
+ return workspace.getContentDescriptionManager().getDescriptionFor(this, info);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#getContents()
+ */
+ public InputStream getContents() throws CoreException {
+ return getContents(false);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#getContents(boolean)
+ */
+ public InputStream getContents(boolean force) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ checkLocal(flags, DEPTH_ZERO);
+ return getLocalManager().read(this, force, null);
+ }
+
+ /**
+ * @see IFile#getEncoding()
+ * @deprecated
+ */
+ public int getEncoding() throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ checkLocal(flags, DEPTH_ZERO);
+ return getLocalManager().getEncoding(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#getHistory(IProgressMonitor)
+ */
+ public IFileState[] getHistory(IProgressMonitor monitor) {
+ return getLocalManager().getHistoryStore().getStates(getFullPath(), monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getType()
+ */
+ public int getType() {
+ return FILE;
+ }
+
+ protected void internalSetContents(InputStream content, IFileInfo fileInfo, int updateFlags, boolean append, IProgressMonitor monitor) throws CoreException {
+ if (content == null)
+ content = new ByteArrayInputStream(new byte[0]);
+ getLocalManager().write(this, content, fileInfo, updateFlags, append, monitor);
+ updateMetadataFiles();
+ workspace.getAliasManager().updateAliases(this, getStore(), IResource.DEPTH_ZERO, monitor);
+ }
+
+ /**
+ * Optimized refreshLocal for files. This implementation does not block the workspace
+ * for the common case where the file exists both locally and on the file system, and
+ * is in sync. For all other cases, it defers to the super implementation.
+ */
+ public void refreshLocal(int depth, IProgressMonitor monitor) throws CoreException {
+ if (!getLocalManager().fastIsSynchronized(this))
+ super.refreshLocal(IResource.DEPTH_ZERO, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#setContents(IFileState, int, IProgressMonitor)
+ */
+ public void setContents(IFileState content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ setContents(content.getContents(), updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#setContents(InputStream, int, IProgressMonitor)
+ */
+ public void setContents(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_settingContents, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ if (workspace.shouldValidate)
+ workspace.validateSave(this);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ workspace.beginOperation(true);
+ IFileInfo fileInfo = getStore().fetchInfo();
+ internalSetContents(content, fileInfo, updateFlags, false, Policy.subMonitorFor(monitor, Policy.opWork));
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ FileUtil.safeClose(content);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setLocalTimeStamp(long)
+ */
+ public long setLocalTimeStamp(long value) throws CoreException {
+ //override to handle changing timestamp on project description file
+ long result = super.setLocalTimeStamp(value);
+ if (path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
+ //handle concurrent project deletion
+ ResourceInfo projectInfo = ((Project) getProject()).getResourceInfo(false, false);
+ if (projectInfo != null)
+ getLocalManager().updateLocalSync(projectInfo, result);
+ }
+ return result;
+ }
+
+ /**
+ * Treat the file specially if it represents a metadata file, which includes:
+ * - project description file (.project)
+ * - project preferences files (*.prefs)
+ *
+ * This method is called whenever it is discovered that a file has
+ * been modified (added, removed, or changed).
+ */
+ public void updateMetadataFiles() throws CoreException {
+ int count = path.segmentCount();
+ String name = path.segment(1);
+ // is this a project description file?
+ if (count == 2 && name.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
+ ((Project) getProject()).updateDescription();
+ return;
+ }
+ // check to see if we are in the .settings directory
+ if (count == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(name)) {
+ ProjectPreferences.updatePreferences(this);
+ return;
+ }
+ }
+
+ /** (non-Javadoc)
+ * @see IFile#setCharset(String)
+ * @deprecated Replaced by {@link #setCharset(String, IProgressMonitor)} which
+ * is a workspace operation and reports changes in resource deltas.
+ */
+ public void setCharset(String newCharset) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#setCharset(String, IProgressMonitor)
+ */
+ public void setCharset(String newCharset, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_settingCharset, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ // need to get the project as a scheduling rule because we might be creating a new folder/file to
+ // hold the project settings
+ final ISchedulingRule rule = workspace.getRuleFactory().charsetRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ workspace.beginOperation(true);
+ workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
+ info = getResourceInfo(false, true);
+ info.incrementCharsetGenerationCount();
+ monitor.worked(Policy.opWork);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#setContents(InputStream, boolean, boolean, IProgressMonitor)
+ */
+ public void setContents(InputStream content, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ // funnel all operations to central method
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+ setContents(content, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#setContents(IFileState, boolean, boolean, IProgressMonitor)
+ */
+ public void setContents(IFileState source, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ // funnel all operations to central method
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+ setContents(source.getContents(), updateFlags, monitor);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/FileState.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import org.eclipse.core.internal.localstore.IHistoryStore;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.UniversalUniqueIdentifier;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.osgi.util.NLS;
+
+public class FileState extends PlatformObject implements IFileState {
+ private static final IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ protected long lastModified;
+ protected UniversalUniqueIdentifier uuid;
+ protected IHistoryStore store;
+ protected IPath fullPath;
+
+ public FileState(IHistoryStore store, IPath fullPath, long lastModified, UniversalUniqueIdentifier uuid) {
+ this.store = store;
+ this.lastModified = lastModified;
+ this.uuid = uuid;
+ this.fullPath = fullPath;
+ }
+
+ /* (non-Javadoc)
+ * @see IFileState#exists()
+ */
+ public boolean exists() {
+ return store.exists(this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IEncodedStorage#getCharset()
+ */
+ public String getCharset() throws CoreException {
+ // if there is an existing file at this state's path, use the encoding of that file
+ IResource file = workspace.getRoot().findMember(fullPath);
+ if (file != null && file.getType() == IResource.FILE)
+ return ((IFile)file).getCharset();
+
+ // tries to obtain a description for the file contents
+ IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+ InputStream contents = new BufferedInputStream(getContents());
+ boolean failed = false;
+ try {
+ IContentDescription description = contentTypeManager.getDescriptionFor(contents, getName(), new QualifiedName[] {IContentDescription.CHARSET});
+ return description == null ? null : description.getCharset();
+ } catch (IOException e) {
+ failed = true;
+ String message = NLS.bind(Messages.history_errorContentDescription, getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, getFullPath(), message, e);
+ } finally {
+ try {
+ contents.close();
+ } catch (IOException e) {
+ if (!failed) {
+ String message = NLS.bind(Messages.history_errorContentDescription, getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, getFullPath(), message, e);
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IFileState#getContents()
+ */
+ public InputStream getContents() throws CoreException {
+ return store.getContents(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IFileState#getFullPath()
+ */
+ public IPath getFullPath() {
+ return fullPath;
+ }
+
+ /* (non-Javadoc)
+ * @see IFileState#getModificationTime()
+ */
+ public long getModificationTime() {
+ return lastModified;
+ }
+
+ /* (non-Javadoc)
+ * @see IFileState#getName()
+ */
+ public String getName() {
+ return fullPath.lastSegment();
+ }
+
+ public UniversalUniqueIdentifier getUUID() {
+ return uuid;
+ }
+
+ /* (non-Javadoc)
+ * @see IFileState#isReadOnly()
+ */
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this object. Used for debug only.
+ */
+ public String toString() {
+ StringBuffer s = new StringBuffer();
+ s.append("FileState(uuid: "); //$NON-NLS-1$
+ s.append(uuid.toString());
+ s.append(", lastModified: "); //$NON-NLS-1$
+ s.append(lastModified);
+ s.append(", path: "); //$NON-NLS-1$
+ s.append(fullPath);
+ s.append(')');
+ return s.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Folder.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+
+public class Folder extends Container implements IFolder {
+ protected Folder(IPath path, Workspace container) {
+ super(path, container);
+ }
+
+ protected void assertCreateRequirements(IFileStore store, IFileInfo localInfo, int updateFlags) throws CoreException {
+ checkDoesNotExist();
+ Container parent = (Container) getParent();
+ ResourceInfo info = parent.getResourceInfo(false, false);
+ parent.checkAccessible(getFlags(info));
+
+ final boolean force = (updateFlags & IResource.FORCE) != 0;
+ if (!force && localInfo.exists()) {
+ //return an appropriate error message for case variant collisions
+ if (!Workspace.caseSensitive) {
+ String name = getLocalManager().getLocalName(store);
+ if (name != null && !store.getName().equals(name)) {
+ String msg = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+ throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), msg, null);
+ }
+ }
+ String msg = NLS.bind(Messages.resources_fileExists, store.toString());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, getFullPath(), msg, null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Changes this folder to be a file in the resource tree and returns the newly
+ * created file. All related properties are deleted. It is assumed that on
+ * disk the resource is already a file so no action is taken to delete the disk
+ * contents.
+ * <p>
+ * <b>This method is for the exclusive use of the local refresh mechanism</b>
+ *
+ * @see org.eclipse.core.internal.localstore.RefreshLocalVisitor#folderToFile(UnifiedTreeNode, Resource)
+ */
+ public IFile changeToFile() throws CoreException {
+ getPropertyManager().deleteProperties(this, IResource.DEPTH_INFINITE);
+ IFile result = workspace.getRoot().getFile(path);
+ if (isLinked()) {
+ URI location = getRawLocationURI();
+ delete(IResource.NONE, null);
+ result.createLink(location, IResource.ALLOW_MISSING_LOCAL, null);
+ } else {
+ workspace.deleteResource(this);
+ workspace.createResource(result, false);
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IFolder#create(int, boolean, IProgressMonitor)
+ */
+ public void create(int updateFlags, boolean local, IProgressMonitor monitor) throws CoreException {
+ final boolean force = (updateFlags & IResource.FORCE) != 0;
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_creating, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ checkValidPath(path, FOLDER, true);
+ final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ IFileStore store = getStore();
+ IFileInfo localInfo = store.fetchInfo();
+ assertCreateRequirements(store, localInfo, updateFlags);
+ workspace.beginOperation(true);
+ if (force && !Workspace.caseSensitive && localInfo.exists()) {
+ String name = getLocalManager().getLocalName(store);
+ if (name == null || localInfo.getName().equals(name)) {
+ delete(true, null);
+ } else {
+ // The file system is not case sensitive and a case variant exists at this location
+ String msg = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+ throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), msg, null);
+ }
+ }
+ internalCreate(updateFlags, local, Policy.subMonitorFor(monitor, Policy.opWork));
+ workspace.getAliasManager().updateAliases(this, getStore(), IResource.DEPTH_ZERO, monitor);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IFolder#create(boolean, boolean, IProgressMonitor)
+ */
+ public void create(boolean force, boolean local, IProgressMonitor monitor) throws CoreException {
+ // funnel all operations to central method
+ create((force ? IResource.FORCE : IResource.NONE), local, monitor);
+ }
+
+ /**
+ * Ensures that this folder exists in the workspace. This is similar in
+ * concept to mkdirs but it does not work on projects.
+ * If this folder is created, it will be marked as being local.
+ */
+ public void ensureExists(IProgressMonitor monitor) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (exists(flags, true))
+ return;
+ if (exists(flags, false)) {
+ String message = NLS.bind(Messages.resources_folderOverFile, getFullPath());
+ throw new ResourceException(IResourceStatus.RESOURCE_WRONG_TYPE, getFullPath(), message, null);
+ }
+ Container parent = (Container) getParent();
+ if (parent.getType() == PROJECT) {
+ info = parent.getResourceInfo(false, false);
+ parent.checkExists(getFlags(info), true);
+ } else
+ ((Folder) parent).ensureExists(monitor);
+ internalCreate(IResource.FORCE, true, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#getDefaultCharset(boolean)
+ */
+ public String getDefaultCharset(boolean checkImplicit) {
+ // non-existing resources default to parent's charset
+ if (!exists())
+ return checkImplicit ? workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true) : null;
+ return workspace.getCharsetManager().getCharsetFor(getFullPath(), checkImplicit);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getType()
+ */
+ public int getType() {
+ return FOLDER;
+ }
+
+ public void internalCreate(int updateFlags, boolean local, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_creating, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ workspace.createResource(this, updateFlags);
+ if (local) {
+ try {
+ final boolean force = (updateFlags & IResource.FORCE) != 0;
+ getLocalManager().write(this, force, Policy.subMonitorFor(monitor, Policy.totalWork));
+ } catch (CoreException e) {
+ // a problem happened creating the folder on disk, so delete from the workspace
+ workspace.deleteResource(this);
+ throw e; // rethrow
+ }
+ }
+ internalSetLocal(local, DEPTH_ZERO);
+ if (!local)
+ getResourceInfo(true, true).clearModificationStamp();
+ } finally {
+ monitor.done();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ICoreConstants.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.QualifiedName;
+
+public interface ICoreConstants {
+
+ // Standard resource properties
+ /** map of builders to their last built state. */
+ public static final QualifiedName K_BUILD_LIST = new QualifiedName(ResourcesPlugin.PI_RESOURCES, "BuildMap"); //$NON-NLS-1$
+
+ /**
+ * Command line argument indicating a workspace refresh on startup is requested.
+ */
+ public static final String REFRESH_ON_STARTUP = "-refresh"; //$NON-NLS-1$
+
+ // resource info constants
+ static final long I_NULL_SYNC_INFO = -1;
+
+ // Useful flag masks for resource info states
+ static final int M_OPEN = 0x1;
+ static final int M_LOCAL_EXISTS = 0x2;
+ static final int M_PHANTOM = 0x8;
+ static final int M_USED = 0x10;
+ static final int M_TYPE = 0xF00;
+ static final int M_TYPE_START = 8;
+ static final int M_MARKERS_SNAP_DIRTY = 0x1000;
+ static final int M_SYNCINFO_SNAP_DIRTY = 0x2000;
+ /**
+ * Marks this resource as derived.
+ * @since 2.0
+ */
+ static final int M_DERIVED = 0x4000;
+ /**
+ * Marks this resource as a team-private member of its container.
+ * @since 2.0
+ */
+ static final int M_TEAM_PRIVATE_MEMBER = 0x8000;
+ /**
+ * Marks this resource as a hidden resource.
+ * @since 3.4
+ */
+ static final int M_HIDDEN = 0x200000;
+
+ /**
+ * Marks this resource as a linked resource.
+ * @since 2.1
+ */
+ static final int M_LINK = 0x10000;
+ /**
+ * The file has no content description.
+ * @since 3.0
+ */
+ static final int M_NO_CONTENT_DESCRIPTION = 0x20000;
+ /**
+ * The file has a default content description.
+ * @since 3.0
+ */
+ static final int M_DEFAULT_CONTENT_DESCRIPTION = 0x40000;
+
+ /**
+ * Marks this resource as having undiscovered children
+ * @since 3.1
+ */
+ static final int M_CHILDREN_UNKNOWN = 0x100000;
+
+ /**
+ * Set of flags that should be cleared when the contents for a file change.
+ * @since 3.0
+ */
+ static final int M_CONTENT_CACHE = M_NO_CONTENT_DESCRIPTION | M_DEFAULT_CONTENT_DESCRIPTION;
+
+ static final int NULL_FLAG = -1;
+
+ /**
+ * A private preference stored in a preference node to indicate the preference
+ * version that is used. This version identifier is used to handle preference
+ * migration when old preferences are loaded.
+ */
+ public static final String PREF_VERSION_KEY = "version"; //$NON-NLS-1$
+
+ /**
+ * A private preference stored in a preference node to indicate the preference
+ * version that is used. This version identifier is used to handle preference
+ * migration when old preferences are loaded.
+ */
+ public static final String PREF_VERSION = "1"; //$NON-NLS-1$
+
+ // Internal status codes
+ // Information Only [00-24]
+ // Warnings [25-74]
+ public static final int CRASH_DETECTED = 10035;
+
+ // Errors [75-99]
+
+ public static final int PROJECT_SEGMENT_LENGTH = 1;
+ public static final int MINIMUM_FOLDER_SEGMENT_LENGTH = 2;
+ public static final int MINIMUM_FILE_SEGMENT_LENGTH = 2;
+
+ public static final int WORKSPACE_TREE_VERSION_1 = 67305985;
+ public static final int WORKSPACE_TREE_VERSION_2 = 67305986;
+
+ // helper constants for empty structures
+ public static final IProject[] EMPTY_PROJECT_ARRAY = new IProject[0];
+ public static final IResource[] EMPTY_RESOURCE_ARRAY = new IResource[0];
+ public static final IFileState[] EMPTY_FILE_STATES = new IFileState[0];
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/IManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public interface IManager {
+ public void shutdown(IProgressMonitor monitor) throws CoreException;
+
+ public void startup(IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/IMarkerSetElement.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+public interface IMarkerSetElement {
+ public long getId();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/IModelObjectConstants.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+public interface IModelObjectConstants {
+ public static final String ARGUMENTS = "arguments"; //$NON-NLS-1$
+ public static final String AUTOBUILD = "autobuild"; //$NON-NLS-1$
+ public static final String BUILD_COMMAND = "buildCommand"; //$NON-NLS-1$
+ public static final String BUILD_ORDER = "buildOrder"; //$NON-NLS-1$
+ public static final String BUILD_SPEC = "buildSpec"; //$NON-NLS-1$
+ public static final String BUILD_TRIGGERS = "triggers"; //$NON-NLS-1$
+ public static final String TRIGGER_AUTO = "auto"; //$NON-NLS-1$
+ public static final String TRIGGER_CLEAN = "clean"; //$NON-NLS-1$
+ public static final String TRIGGER_FULL = "full"; //$NON-NLS-1$
+ public static final String TRIGGER_INCREMENTAL = "incremental"; //$NON-NLS-1$
+ public static final String COMMENT = "comment"; //$NON-NLS-1$
+ public static final String DICTIONARY = "dictionary"; //$NON-NLS-1$
+ public static final String FILE_STATE_LONGEVITY = "fileStateLongevity"; //$NON-NLS-1$
+ public static final String KEY = "key"; //$NON-NLS-1$
+ public static final String LOCATION = "location"; //$NON-NLS-1$
+ public static final String LOCATION_URI = "locationURI"; //$NON-NLS-1$
+ public static final String MAX_FILE_STATE_SIZE = "maxFileStateSize"; //$NON-NLS-1$
+ public static final String MAX_FILE_STATES = "maxFileStates"; //$NON-NLS-1$
+ /**
+ * The project relative path is called the link name for backwards compatibility
+ */
+ public static final String NAME = "name"; //$NON-NLS-1$
+ public static final String NATURE = "nature"; //$NON-NLS-1$
+ public static final String NATURES = "natures"; //$NON-NLS-1$
+ public static final String SNAPSHOT_INTERVAL = "snapshotInterval"; //$NON-NLS-1$
+ public static final String PROJECT = "project"; //$NON-NLS-1$
+ public static final String PROJECT_DESCRIPTION = "projectDescription"; //$NON-NLS-1$
+ public static final String PROJECTS = "projects"; //$NON-NLS-1$
+ public static final String TYPE = "type"; //$NON-NLS-1$
+ public static final String VALUE = "value"; //$NON-NLS-1$
+ public static final String WORKSPACE_DESCRIPTION = "workspaceDescription"; //$NON-NLS-1$
+ public static final String LINKED_RESOURCES = "linkedResources"; //$NON-NLS-1$
+ public static final String LINK = "link"; //$NON-NLS-1$
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/InternalTeamHook.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceRuleFactory;
+import org.eclipse.core.resources.team.TeamHook;
+
+/**
+ * The internal abstract superclass of all TeamHook implementations. This superclass
+ * provides access to internal non-API methods that are not available from the API
+ * package. Plugin developers should not subclass this class.
+ *
+ * @see TeamHook
+ */
+public class InternalTeamHook {
+ /* (non-Javadoc)
+ * Internal implementation of TeamHook#setRulesFor(IProject,IResourceRuleFactory)
+ */
+ protected void setRuleFactory(IProject project, IResourceRuleFactory factory) {
+ Workspace workspace = ((Workspace) project.getWorkspace());
+ ((Rules) workspace.getRuleFactory()).setRuleFactory(project, factory);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/InternalWorkspaceJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * Batches the activity of a job as a single operation, without obtaining the workspace
+ * lock.
+ */
+public abstract class InternalWorkspaceJob extends Job {
+ private Workspace workspace;
+
+ public InternalWorkspaceJob(String name) {
+ super(name);
+ this.workspace = (Workspace) ResourcesPlugin.getWorkspace();
+ }
+
+ public final IStatus run(IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ int depth = -1;
+ try {
+ workspace.prepareOperation(null, monitor);
+ workspace.beginOperation(true);
+ depth = workspace.getWorkManager().beginUnprotected();
+ return runInWorkspace(monitor);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ return Status.CANCEL_STATUS;
+ } finally {
+ if (depth >= 0)
+ workspace.getWorkManager().endUnprotected(depth);
+ workspace.endOperation(null, false, monitor);
+ }
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ }
+
+ protected abstract IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/LinkDescription.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Object for describing the characteristics of linked resources that are stored
+ * in the project description.
+ */
+public class LinkDescription implements Comparable {
+
+ private URI localLocation;
+
+ /**
+ * The project relative path.
+ */
+ private IPath path;
+ /**
+ * The resource type (IResource.FILE or IResoruce.FOLDER)
+ */
+ private int type;
+
+ public LinkDescription() {
+ this.path = Path.EMPTY;
+ this.type = -1;
+ }
+
+ public LinkDescription(IResource linkedResource, URI location) {
+ super();
+ Assert.isNotNull(linkedResource);
+ Assert.isNotNull(location);
+ this.type = linkedResource.getType();
+ this.path = linkedResource.getProjectRelativePath();
+ this.localLocation = location;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o.getClass() == LinkDescription.class))
+ return false;
+ LinkDescription other = (LinkDescription) o;
+ return localLocation.equals(other.localLocation) && type == other.type;
+ }
+
+ public URI getLocationURI() {
+ return localLocation;
+ }
+
+ /**
+ * Returns the project relative path of the resource that is linked.
+ * @return the project relative path of the resource that is linked.
+ */
+ public IPath getProjectRelativePath() {
+ return path;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public int hashCode() {
+ return type + localLocation.hashCode();
+ }
+
+ public void setLocationURI(URI location) {
+ this.localLocation = location;
+ }
+
+ public void setPath(IPath path) {
+ this.path = path;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ /**
+ * Compare link descriptions in a way that sorts them topologically by path.
+ * This is important to ensure we process links in topological (breadth-first) order when reconciling
+ * links. See {@link Project#reconcileLinks(ProjectDescription)}.
+ */
+ public int compareTo(Object o) {
+ LinkDescription that = (LinkDescription) o;
+ IPath path1 = this.getProjectRelativePath();
+ IPath path2 = that.getProjectRelativePath();
+ int count1 = path1.segmentCount();
+ int compare = count1 - path2.segmentCount();
+ if (compare != 0)
+ return compare;
+ for (int i = 0; i < count1; i++) {
+ compare = path1.segment(i).compareTo(path2.segment(i));
+ if (compare != 0)
+ return compare;
+ }
+ return 0;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/LocalMetaArea.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.net.URI;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.localstore.SafeChunkyInputStream;
+import org.eclipse.core.internal.localstore.SafeChunkyOutputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+public class LocalMetaArea implements ICoreConstants {
+ /* package */static final String F_BACKUP_FILE_EXTENSION = ".bak"; //$NON-NLS-1$
+ /* package */static final String F_DESCRIPTION = ".workspace"; //$NON-NLS-1$
+
+ /* package */static final String F_HISTORY_STORE = ".history"; //$NON-NLS-1$
+ /* package */static final String F_MARKERS = ".markers"; //$NON-NLS-1$
+ /* package */static final String F_OLD_PROJECT = ".prj"; //$NON-NLS-1$
+ /* package */static final String F_PROJECT_LOCATION = ".location"; //$NON-NLS-1$
+ /* package */static final String F_PROJECTS = ".projects"; //$NON-NLS-1$
+ /* package */static final String F_PROPERTIES = ".properties"; //$NON-NLS-1$
+ /* package */static final String F_ROOT = ".root"; //$NON-NLS-1$
+ /* package */static final String F_SAFE_TABLE = ".safetable"; //$NON-NLS-1$
+ /* package */static final String F_SNAP = ".snap"; //$NON-NLS-1$
+ /* package */static final String F_SNAP_EXTENSION = "snap"; //$NON-NLS-1$
+ /* package */static final String F_SYNCINFO = ".syncinfo"; //$NON-NLS-1$
+ /* package */static final String F_TREE = ".tree"; //$NON-NLS-1$
+ /* package */static final String URI_PREFIX = "URI//"; //$NON-NLS-1$
+ /* package */static final String F_METADATA = ".metadata"; //$NON-NLS-1$
+
+
+ protected final IPath metaAreaLocation;
+
+ /**
+ * The project location is just stored as an optimization, to avoid recomputing it.
+ */
+ protected final IPath projectMetaLocation;
+
+ public LocalMetaArea() {
+ super();
+ metaAreaLocation = ResourcesPlugin.getPlugin().getStateLocation();
+ projectMetaLocation = metaAreaLocation.append(F_PROJECTS);
+ }
+
+ /**
+ * For backwards compatibility, if there is a project at the old project
+ * description location, delete it.
+ */
+ public void clearOldDescription(IProject target) {
+ Workspace.clear(getOldDescriptionLocationFor(target).toFile());
+ }
+
+ public void create(IProject target) {
+ java.io.File file = locationFor(target).toFile();
+ //make sure area is empty
+ Workspace.clear(file);
+ file.mkdirs();
+ }
+
+ /**
+ * Creates the meta area root directory.
+ */
+ public synchronized void createMetaArea() throws CoreException {
+ java.io.File workspaceLocation = metaAreaLocation.toFile();
+ Workspace.clear(workspaceLocation);
+ if (!workspaceLocation.mkdirs()) {
+ String message = NLS.bind(Messages.resources_writeWorkspaceMeta, workspaceLocation);
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, null, message, null);
+ }
+ }
+
+ /**
+ * The project is being deleted. Delete all meta-data associated with the
+ * project.
+ */
+ public void delete(IProject target) throws CoreException {
+ IPath path = locationFor(target);
+ if (!Workspace.clear(path.toFile()) && path.toFile().exists()) {
+ String message = NLS.bind(Messages.resources_deleteMeta, target.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, target.getFullPath(), message, null);
+ }
+ }
+
+ public IPath getBackupLocationFor(IPath file) {
+ return file.removeLastSegments(1).append(file.lastSegment() + F_BACKUP_FILE_EXTENSION);
+ }
+
+ public IPath getHistoryStoreLocation() {
+ return metaAreaLocation.append(F_HISTORY_STORE);
+ }
+
+ /**
+ * Returns the local file system location which contains the META data for
+ * the resources plugin (i.e., the entire workspace).
+ */
+ public IPath getLocation() {
+ return metaAreaLocation;
+ }
+
+ /**
+ * Returns the path of the file in which to save markers for the given
+ * resource. Should only be called for the workspace root and projects.
+ */
+ public IPath getMarkersLocationFor(IResource resource) {
+ Assert.isNotNull(resource);
+ Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
+ return locationFor(resource).append(F_MARKERS);
+ }
+
+ /**
+ * Returns the path of the file in which to snapshot markers for the given
+ * resource. Should only be called for the workspace root and projects.
+ */
+ public IPath getMarkersSnapshotLocationFor(IResource resource) {
+ return getMarkersLocationFor(resource).addFileExtension(F_SNAP_EXTENSION);
+ }
+
+ /**
+ * The project description file is the only metadata file stored outside
+ * the metadata area. It is stored as a file directly under the project
+ * location. For backwards compatibility, we also have to check for a
+ * project file at the old location in the metadata area.
+ */
+ public IPath getOldDescriptionLocationFor(IProject target) {
+ return locationFor(target).append(F_OLD_PROJECT);
+ }
+
+ public IPath getOldWorkspaceDescriptionLocation() {
+ return metaAreaLocation.append(F_DESCRIPTION);
+ }
+
+ public IPath getPropertyStoreLocation(IResource resource) {
+ int type = resource.getType();
+ Assert.isTrue(type != IResource.FILE && type != IResource.FOLDER);
+ return locationFor(resource).append(F_PROPERTIES);
+ }
+
+ public IPath getSafeTableLocationFor(String pluginId) {
+ IPath prefix = metaAreaLocation.append(F_SAFE_TABLE);
+ // if the plugin is the resources plugin, we return the master table
+ // location
+ if (pluginId.equals(ResourcesPlugin.PI_RESOURCES))
+ return prefix.append(pluginId); // master table
+ int saveNumber = getWorkspace().getSaveManager().getSaveNumber(pluginId);
+ return prefix.append(pluginId + "." + saveNumber); //$NON-NLS-1$
+ }
+
+ public IPath getSnapshotLocationFor(IResource resource) {
+ return metaAreaLocation.append(F_SNAP);
+ }
+
+ /**
+ * Returns the path of the file in which to save the sync information for
+ * the given resource. Should only be called for the workspace root and
+ * projects.
+ */
+ public IPath getSyncInfoLocationFor(IResource resource) {
+ Assert.isNotNull(resource);
+ Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
+ return locationFor(resource).append(F_SYNCINFO);
+ }
+
+ /**
+ * Returns the path of the file in which to snapshot the sync information
+ * for the given resource. Should only be called for the workspace root and
+ * projects.
+ */
+ public IPath getSyncInfoSnapshotLocationFor(IResource resource) {
+ return getSyncInfoLocationFor(resource).addFileExtension(F_SNAP_EXTENSION);
+ }
+
+ /**
+ * Returns the local file system location of the tree file for the given
+ * resource. This file does not follow the same save number as its plug-in.
+ * So, the number here is called "sequence number" and not "save number" to
+ * avoid confusion.
+ */
+ public IPath getTreeLocationFor(IResource target, boolean updateSequenceNumber) {
+ IPath key = target.getFullPath().append(F_TREE);
+ String sequenceNumber = getWorkspace().getSaveManager().getMasterTable().getProperty(key.toString());
+ if (sequenceNumber == null)
+ sequenceNumber = "0"; //$NON-NLS-1$
+ if (updateSequenceNumber) {
+ int n = new Integer(sequenceNumber).intValue() + 1;
+ n = n < 0 ? 1 : n;
+ sequenceNumber = new Integer(n).toString();
+ getWorkspace().getSaveManager().getMasterTable().setProperty(key.toString(), new Integer(sequenceNumber).toString());
+ }
+ return locationFor(target).append(sequenceNumber + F_TREE);
+ }
+
+ public IPath getWorkingLocation(IResource resource, String id) {
+ return locationFor(resource).append(id);
+ }
+
+ protected Workspace getWorkspace() {
+ return (Workspace) ResourcesPlugin.getWorkspace();
+ }
+
+ public boolean hasSavedProject(IProject project) {
+ //if there is a location file, then the project exists
+ return getOldDescriptionLocationFor(project).toFile().exists() || locationFor(project).append(F_PROJECT_LOCATION).toFile().exists();
+ }
+
+ public boolean hasSavedWorkspace() {
+ return metaAreaLocation.toFile().exists() || getBackupLocationFor(metaAreaLocation).toFile().exists();
+ }
+
+ /**
+ * Returns the local file system location in which the meta data for the
+ * resource with the given path is stored.
+ */
+ public IPath locationFor(IPath resourcePath) {
+ if (Path.ROOT.equals(resourcePath))
+ return metaAreaLocation.append(F_ROOT);
+ return projectMetaLocation.append(resourcePath.segment(0));
+ }
+
+ /**
+ * Returns the local file system location in which the meta data for the
+ * given resource is stored.
+ */
+ public IPath locationFor(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return metaAreaLocation.append(F_ROOT);
+ return projectMetaLocation.append(resource.getProject().getName());
+ }
+
+ /**
+ * Reads and returns the project description for the given project. Returns
+ * null if there was no project description file on disk. Throws an
+ * exception if there was any failure to read the project.
+ */
+ public ProjectDescription readOldDescription(IProject project) throws CoreException {
+ IPath path = getOldDescriptionLocationFor(project);
+ if (!path.toFile().exists())
+ return null;
+ IPath tempPath = getBackupLocationFor(path);
+ ProjectDescription description = null;
+ try {
+ description = new ProjectDescriptionReader(project).read(path, tempPath);
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.resources_readMeta, project.getName());
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, project.getFullPath(), msg, e);
+ }
+ if (description == null) {
+ String msg = NLS.bind(Messages.resources_readMeta, project.getName());
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, project.getFullPath(), msg, null);
+ }
+ return description;
+ }
+
+ /**
+ * Provides backward compatibility with existing workspaces based on
+ * descriptions.
+ */
+ public WorkspaceDescription readOldWorkspace() {
+ IPath path = getOldWorkspaceDescriptionLocation();
+ IPath tempPath = getBackupLocationFor(path);
+ try {
+ WorkspaceDescription oldDescription = (WorkspaceDescription) new WorkspaceDescriptionReader().read(path, tempPath);
+ // if one of those files exist, get rid of them
+ Workspace.clear(path.toFile());
+ Workspace.clear(tempPath.toFile());
+ return oldDescription;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the portions of the project description that are private, and
+ * adds them to the supplied project description. In particular, the
+ * project location and the project's dynamic references are stored here.
+ * The project location will be set to <code>null</code> if the default
+ * location should be used. In the case of failure, log the exception and
+ * return silently, thus reverting to using the default location and no
+ * dynamic references. The current format of the location file is:
+ * UTF - project location
+ * int - number of dynamic project references
+ * UTF - project reference 1
+ * ... repeat for remaining references
+ */
+ public void readPrivateDescription(IProject target, IProjectDescription description) {
+ IPath locationFile = locationFor(target).append(F_PROJECT_LOCATION);
+ java.io.File file = locationFile.toFile();
+ if (!file.exists()) {
+ locationFile = getBackupLocationFor(locationFile);
+ file = locationFile.toFile();
+ if (!file.exists())
+ return;
+ }
+ try {
+ SafeChunkyInputStream input = new SafeChunkyInputStream(file, 500);
+ DataInputStream dataIn = new DataInputStream(input);
+ try {
+ try {
+ String location = dataIn.readUTF();
+ if (location.length() > 0) {
+ //location format < 3.2 was a local file system OS path
+ //location format >= 3.2 is: URI_PREFIX + uri.toString()
+ if (location.startsWith(URI_PREFIX))
+ description.setLocationURI(URI.create(location.substring(URI_PREFIX.length())));
+ else
+ description.setLocationURI(URIUtil.toURI(Path.fromOSString(location)));
+ }
+ } catch (Exception e) {
+ //don't allow failure to read the location to propagate
+ String msg = NLS.bind(Messages.resources_exReadProjectLocation, target.getName());
+ Policy.log(new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_READ_METADATA, target.getFullPath(), msg, e));
+ }
+ //try to read the dynamic references - will fail for old location files
+ int numRefs = dataIn.readInt();
+ IProject[] references = new IProject[numRefs];
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ for (int i = 0; i < numRefs; i++)
+ references[i] = root.getProject(dataIn.readUTF());
+ description.setDynamicReferences(references);
+ } finally {
+ dataIn.close();
+ }
+ } catch (IOException e) {
+ //ignore - this is an old location file or an exception occurred
+ // closing the stream
+ }
+ }
+
+ /**
+ * Writes the workspace description to the local meta area. This method is
+ * synchronized to prevent multiple current write attempts.
+ *
+ * @deprecated should not be called any more - workspace preferences are
+ * now maintained in the plug-in's preferences
+ */
+ public synchronized void write(WorkspaceDescription description) throws CoreException {
+ IPath path = getOldWorkspaceDescriptionLocation();
+ path.toFile().getParentFile().mkdirs();
+ IPath tempPath = getBackupLocationFor(path);
+ try {
+ new ModelObjectWriter().write(description, path, tempPath);
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.resources_writeWorkspaceMeta, path);
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, null, message, e);
+ }
+ }
+
+ /**
+ * Write the private project description information, including the location
+ * and the dynamic project references. See <tt>readPrivateDescription</tt>
+ * for details on the file format.
+ */
+ public void writePrivateDescription(IProject target) throws CoreException {
+ IPath location = locationFor(target).append(F_PROJECT_LOCATION);
+ java.io.File file = location.toFile();
+ //delete any old location file
+ Workspace.clear(file);
+ //don't write anything if there is no interesting private metadata
+ ProjectDescription desc = ((Project) target).internalGetDescription();
+ if (desc == null)
+ return;
+ final URI projectLocation = desc.getLocationURI();
+ final IProject[] references = desc.getDynamicReferences(false);
+ final int numRefs = references.length;
+ if (projectLocation == null && numRefs == 0)
+ return;
+ //write the private metadata file
+ try {
+ SafeChunkyOutputStream output = new SafeChunkyOutputStream(file);
+ DataOutputStream dataOut = new DataOutputStream(output);
+ try {
+ if (projectLocation == null)
+ dataOut.writeUTF(""); //$NON-NLS-1$
+ else
+ dataOut.writeUTF(URI_PREFIX + projectLocation.toString());
+ dataOut.writeInt(numRefs);
+ for (int i = 0; i < numRefs; i++)
+ dataOut.writeUTF(references[i].getName());
+ output.succeed();
+ } finally {
+ dataOut.close();
+ }
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.resources_exSaveProjectLocation, target.getName());
+ throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/LocationValidator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,430 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class implements the various path, URI, and name validation methods
+ * in the workspace API
+ */
+public class LocationValidator {
+ private final Workspace workspace;
+
+ public LocationValidator(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ /**
+ * Returns a string representation of a URI suitable for displaying to an end user.
+ */
+ private String toString(URI uri) {
+ try {
+ return EFS.getStore(uri).toString();
+ } catch (CoreException e) {
+ //there is no store defined, so the best we can do is the URI toString.
+ return uri.toString();
+ }
+ }
+
+ /**
+ * Check that the location is absolute
+ */
+ private IStatus validateAbsolute(URI location, boolean error) {
+ if (!location.isAbsolute()) {
+ IPath pathPart = new Path(location.getSchemeSpecificPart());
+ String message;
+ if (pathPart.segmentCount() > 0)
+ message = NLS.bind(Messages.pathvar_undefined, location.toString(), pathPart.segment(0));
+ else
+ message = Messages.links_noPath;
+ int code = error ? IResourceStatus.VARIABLE_NOT_DEFINED : IResourceStatus.VARIABLE_NOT_DEFINED_WARNING;
+ return new ResourceStatus(code, null, message);
+ }
+ return Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateLinkLocation(IResource, IPath)
+ */
+ public IStatus validateLinkLocation(IResource resource, IPath unresolvedLocation) {
+ IPath location = workspace.getPathVariableManager().resolvePath(unresolvedLocation);
+ if (location.isEmpty())
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), Messages.links_noPath);
+ //check that the location is absolute
+ if (!location.isAbsolute()) {
+ //we know there is at least one segment, because of previous isEmpty check
+ String message = NLS.bind(Messages.pathvar_undefined, location.toOSString(), location.segment(0));
+ return new ResourceStatus(IResourceStatus.VARIABLE_NOT_DEFINED_WARNING, resource.getFullPath(), message);
+ }
+ //if the location doesn't have a device, see if the OS will assign one
+ if (location.getDevice() == null)
+ location = new Path(location.toFile().getAbsolutePath());
+ return validateLinkLocationURI(resource, URIUtil.toURI(location));
+ }
+
+ public IStatus validateLinkLocationURI(IResource resource, URI unresolvedLocation) {
+ String message;
+ //check if resource linking is disabled
+ if (ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(ResourcesPlugin.PREF_DISABLE_LINKING)) {
+ message = NLS.bind(Messages.links_workspaceVeto, resource.getName());
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message);
+ }
+ //check that the resource is the right type
+ int type = resource.getType();
+ if (type != IResource.FOLDER && type != IResource.FILE) {
+ message = NLS.bind(Messages.links_notFileFolder, resource.getName());
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message);
+ }
+ IContainer parent = resource.getParent();
+ if (!parent.isAccessible()) {
+ message = NLS.bind(Messages.links_parentNotAccessible, resource.getFullPath());
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message);
+ }
+ URI location = workspace.getPathVariableManager().resolveURI(unresolvedLocation);
+ //check nature veto
+ String[] natureIds = ((Project) resource.getProject()).internalGetDescription().getNatureIds();
+
+ IStatus result = workspace.getNatureManager().validateLinkCreation(natureIds);
+ if (!result.isOK())
+ return result;
+ //check team provider veto
+ if (resource.getType() == IResource.FILE)
+ result = workspace.getTeamHook().validateCreateLink((IFile) resource, IResource.NONE, location);
+ else
+ result = workspace.getTeamHook().validateCreateLink((IFolder) resource, IResource.NONE, location);
+ if (!result.isOK())
+ return result;
+ //check the standard path name restrictions
+ result = validateSegments(location);
+ if (!result.isOK())
+ return result;
+ //check if the location is based on an undefined variable
+ result = validateAbsolute(location, false);
+ if (!result.isOK())
+ return result;
+ // test if the given location overlaps the platform metadata location
+ URI testLocation = workspace.getMetaArea().getLocation().toFile().toURI();
+ if (FileUtil.isOverlapping(location, testLocation)) {
+ message = NLS.bind(Messages.links_invalidLocation, toString(location));
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message);
+ }
+ //test if the given path overlaps the location of the given project
+ testLocation = resource.getProject().getLocationURI();
+ if (testLocation != null && FileUtil.isPrefixOf(location, testLocation)) {
+ message = NLS.bind(Messages.links_locationOverlapsProject, toString(location));
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message);
+ }
+ //warnings (all errors must be checked before all warnings)
+
+ // Iterate over each known project and ensure that the location does not
+ // conflict with any project locations or linked resource locations
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ // since we are iterating over the project in the workspace, we
+ // know that they have been created before and must have a description
+ IProjectDescription desc = ((Project) project).internalGetDescription();
+ testLocation = desc.getLocationURI();
+ if (testLocation != null && FileUtil.isOverlapping(location, testLocation)) {
+ message = NLS.bind(Messages.links_overlappingResource, toString(location));
+ return new ResourceStatus(IResourceStatus.OVERLAPPING_LOCATION, resource.getFullPath(), message);
+ }
+ //iterate over linked resources and check for overlap
+ if (!project.isOpen())
+ continue;
+ IResource[] children = null;
+ try {
+ children = project.members();
+ } catch (CoreException e) {
+ //ignore projects that cannot be accessed
+ }
+ if (children == null)
+ continue;
+ for (int j = 0; j < children.length; j++) {
+ if (children[j].isLinked()) {
+ testLocation = children[j].getLocationURI();
+ if (testLocation != null && FileUtil.isOverlapping(location, testLocation)) {
+ message = NLS.bind(Messages.links_overlappingResource, toString(location));
+ return new ResourceStatus(IResourceStatus.OVERLAPPING_LOCATION, resource.getFullPath(), message);
+ }
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateName(String, int)
+ */
+ public IStatus validateName(String segment, int type) {
+ String message;
+
+ /* segment must not be null */
+ if (segment == null) {
+ message = Messages.resources_nameNull;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ // cannot be an empty string
+ if (segment.length() == 0) {
+ message = Messages.resources_nameEmpty;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* test invalid characters */
+ char[] chars = OS.INVALID_RESOURCE_CHARACTERS;
+ for (int i = 0; i < chars.length; i++)
+ if (segment.indexOf(chars[i]) != -1) {
+ message = NLS.bind(Messages.resources_invalidCharInName, String.valueOf(chars[i]), segment);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* test invalid OS names */
+ if (!OS.isNameValid(segment)) {
+ message = NLS.bind(Messages.resources_invalidName, segment);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates that the given workspace path is valid for the given type. If
+ * <code>lastSegmentOnly</code> is true, it is assumed that all segments except
+ * the last one have previously been validated. This is an optimization for validating
+ * a leaf resource when it is known that the parent exists (and thus its parent path
+ * must already be valid).
+ */
+ public IStatus validatePath(IPath path, int type, boolean lastSegmentOnly) {
+ String message;
+
+ /* path must not be null */
+ if (path == null) {
+ message = Messages.resources_pathNull;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* path must not have a device separator */
+ if (path.getDevice() != null) {
+ message = NLS.bind(Messages.resources_invalidCharInPath, String.valueOf(IPath.DEVICE_SEPARATOR), path);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* path must not be the root path */
+ if (path.isRoot()) {
+ message = Messages.resources_invalidRoot;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* path must be absolute */
+ if (!path.isAbsolute()) {
+ message = NLS.bind(Messages.resources_mustBeAbsolute, path);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* validate segments */
+ int numberOfSegments = path.segmentCount();
+ if ((type & IResource.PROJECT) != 0) {
+ if (numberOfSegments == ICoreConstants.PROJECT_SEGMENT_LENGTH) {
+ return validateName(path.segment(0), IResource.PROJECT);
+ } else if (type == IResource.PROJECT) {
+ message = NLS.bind(Messages.resources_projectPath, path);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ }
+ if ((type & (IResource.FILE | IResource.FOLDER)) != 0) {
+ if (numberOfSegments < ICoreConstants.MINIMUM_FILE_SEGMENT_LENGTH) {
+ message = NLS.bind(Messages.resources_resourcePath, path);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ int fileFolderType = type &= ~IResource.PROJECT;
+ int segmentCount = path.segmentCount();
+ if (lastSegmentOnly)
+ return validateName(path.segment(segmentCount - 1), fileFolderType);
+ IStatus status = validateName(path.segment(0), IResource.PROJECT);
+ if (!status.isOK())
+ return status;
+ // ignore first segment (the project)
+ for (int i = 1; i < segmentCount; i++) {
+ status = validateName(path.segment(i), fileFolderType);
+ if (!status.isOK())
+ return status;
+ }
+ return Status.OK_STATUS;
+ }
+ message = NLS.bind(Messages.resources_invalidPath, path);
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validatePath(String, int)
+ */
+ public IStatus validatePath(String path, int type) {
+ /* path must not be null */
+ if (path == null) {
+ String message = Messages.resources_pathNull;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ return validatePath(Path.fromOSString(path), type, false);
+ }
+
+ public IStatus validateProjectLocation(IProject context, IPath unresolvedLocation) {
+ if (unresolvedLocation == null)
+ return validateProjectLocationURI(context, null);
+
+ IPath location = workspace.getPathVariableManager().resolvePath(unresolvedLocation);
+ //check that the location is absolute
+ if (!location.isAbsolute()) {
+ String message;
+ if (location.segmentCount() > 0)
+ message = NLS.bind(Messages.pathvar_undefined, location.toString(), location.segment(0));
+ else
+ message = Messages.links_noPath;
+ return new ResourceStatus(IResourceStatus.VARIABLE_NOT_DEFINED, null, message);
+ }
+ return validateProjectLocationURI(context, URIUtil.toURI(location));
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateProjectLocation(IProject, URI)
+ */
+ public IStatus validateProjectLocationURI(IProject context, URI unresolvedLocation) {
+ if (context == null && unresolvedLocation == null)
+ throw new IllegalArgumentException("Either a project or a location must be provided"); //$NON-NLS-1$
+
+ // Checks if the new location overlaps the workspace metadata location
+ boolean isMetadataLocation = false;
+
+ if (unresolvedLocation != null) {
+ if (URIUtil.equals(unresolvedLocation, URIUtil.toURI(Platform.getLocation().addTrailingSeparator().append(LocalMetaArea.F_METADATA)))) {
+ isMetadataLocation = true;
+ }
+ } else if (context != null && context.getName().equals(LocalMetaArea.F_METADATA)) {
+ isMetadataLocation = true;
+ }
+
+ String message;
+ if (isMetadataLocation) {
+ message = NLS.bind(Messages.resources_invalidPath, toString(URIUtil.toURI(Platform.getLocation().addTrailingSeparator().append(LocalMetaArea.F_METADATA))));
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ // the default is ok for all other projects
+ if (unresolvedLocation == null)
+ return Status.OK_STATUS;
+
+ URI location = workspace.getPathVariableManager().resolveURI(unresolvedLocation);
+ //check the standard path name restrictions
+ IStatus result = validateSegments(location);
+ if (!result.isOK())
+ return result;
+ result = validateAbsolute(location, true);
+ if (!result.isOK())
+ return result;
+ //check that the URI has a legal scheme
+ try {
+ EFS.getFileSystem(location.getScheme());
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ //overlaps with default location can only occur with file URIs
+ if (location.getScheme().equals(EFS.SCHEME_FILE)) {
+ IPath locationPath = URIUtil.toPath(location);
+ // test if the given location overlaps the default default location
+ IPath defaultDefaultLocation = workspace.getRoot().getLocation();
+ if (FileUtil.isPrefixOf(locationPath, defaultDefaultLocation)) {
+ message = NLS.bind(Messages.resources_overlapWorkspace, toString(location), defaultDefaultLocation.toOSString());
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ // test if the given location is the default location for any potential project
+ IPath parentPath = locationPath.removeLastSegments(1);
+ if (FileUtil.isPrefixOf(parentPath, defaultDefaultLocation) && FileUtil.isPrefixOf(defaultDefaultLocation, parentPath)) {
+ message = NLS.bind(Messages.resources_overlapProject, toString(location), locationPath.lastSegment());
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ }
+
+ // Iterate over each known project and ensure that the location does not
+ // conflict with any of their already defined locations.
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int j = 0; j < projects.length; j++) {
+ IProject project = projects[j];
+ // since we are iterating over the project in the workspace, we
+ // know that they have been created before and must have a description
+ IProjectDescription desc = ((Project) project).internalGetDescription();
+ URI testLocation = desc.getLocationURI();
+ // if the project uses the default location then continue
+ if (testLocation == null)
+ continue;
+ if (context != null && project.equals(context)) {
+ //tolerate locations being the same if this is the project being tested
+ if (URIUtil.equals(testLocation, location))
+ continue;
+ //a project cannot be moved inside of its current location
+ if (!FileUtil.isPrefixOf(testLocation, location))
+ continue;
+ } else if (!URIUtil.equals(testLocation, location)) {
+ // a project cannot have the same location as another existing project
+ continue;
+ }
+ //in all other cases there is illegal overlap
+ message = NLS.bind(Messages.resources_overlapProject, toString(location), project.getName());
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ //if this project exists and has linked resources, the project location cannot overlap
+ //the locations of any linked resources in that project
+ if (context != null && context.exists() && context.isOpen()) {
+ IResource[] children = null;
+ try {
+ children = context.members();
+ } catch (CoreException e) {
+ //ignore projects that cannot be accessed
+ }
+ if (children != null) {
+ for (int i = 0; i < children.length; i++) {
+ if (children[i].isLinked()) {
+ URI testLocation = children[i].getLocationURI();
+ if (testLocation != null && FileUtil.isPrefixOf(testLocation, location)) {
+ message = NLS.bind(Messages.links_locationOverlapsLink, toString(location));
+ return new ResourceStatus(IResourceStatus.OVERLAPPING_LOCATION, context.getFullPath(), message);
+ }
+ }
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates the standard path name restrictions on the segments of the provided URI.
+ * @param location The URI to validate
+ * @return A status indicating if the segments of the provided URI are valid
+ */
+ private IStatus validateSegments(URI location) {
+ if (EFS.SCHEME_FILE.equals(location.getScheme())) {
+ IPath pathPart = new Path(location.getSchemeSpecificPart());
+ int segmentCount = pathPart.segmentCount();
+ for (int i = 0; i < segmentCount; i++) {
+ IStatus result = validateName(pathPart.segment(i), IResource.PROJECT);
+ if (!result.isOK())
+ return result;
+ }
+ }
+ return Status.OK_STATUS;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Marker.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.Map;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * An abstract marker implementation.
+ * Subclasses must implement the <code>clone</code> method, and
+ * are free to declare additional field and method members.
+ * <p>
+ * Note: Marker objects do not store whether they are "standalone"
+ * vs. "attached" to the workspace. This information is maintained
+ * by the workspace.
+ * </p>
+ *
+ * @see IMarker
+ */
+public class Marker extends PlatformObject implements IMarker {
+
+ /** Marker identifier. */
+ protected long id;
+
+ /** Resource with which this marker is associated. */
+ protected IResource resource;
+
+ /**
+ * Constructs a new marker object.
+ */
+ Marker(IResource resource, long id) {
+ Assert.isLegal(resource != null);
+ this.resource = resource;
+ this.id = id;
+ }
+
+ /**
+ * Checks the given marker info to ensure that it is not null.
+ * Throws an exception if it is.
+ */
+ private void checkInfo(MarkerInfo info) throws CoreException {
+ if (info == null) {
+ String message = NLS.bind(Messages.resources_markerNotFound, Long.toString(id));
+ throw new ResourceException(new ResourceStatus(IResourceStatus.MARKER_NOT_FOUND, resource.getFullPath(), message));
+ }
+ }
+
+ /**
+ * @see IMarker#delete()
+ */
+ public void delete() throws CoreException {
+ final ISchedulingRule rule = getWorkspace().getRuleFactory().markerRule(resource);
+ try {
+ getWorkspace().prepareOperation(rule, null);
+ getWorkspace().beginOperation(true);
+ getWorkspace().getMarkerManager().removeMarker(getResource(), getId());
+ } finally {
+ getWorkspace().endOperation(rule, false, null);
+ }
+ }
+
+ /**
+ * @see IMarker#equals(Object)
+ */
+ public boolean equals(Object object) {
+ if (!(object instanceof IMarker))
+ return false;
+ IMarker other = (IMarker) object;
+ return (id == other.getId() && resource.equals(other.getResource()));
+ }
+
+ /**
+ * @see IMarker#exists()
+ */
+ public boolean exists() {
+ return getInfo() != null;
+ }
+
+ /**
+ * @see IMarker#getAttribute(String)
+ */
+ public Object getAttribute(String attributeName) throws CoreException {
+ Assert.isNotNull(attributeName);
+ MarkerInfo info = getInfo();
+ checkInfo(info);
+ return info.getAttribute(attributeName);
+ }
+
+ /**
+ * @see IMarker#getAttribute(String, int)
+ */
+ public int getAttribute(String attributeName, int defaultValue) {
+ Assert.isNotNull(attributeName);
+ MarkerInfo info = getInfo();
+ if (info == null)
+ return defaultValue;
+ Object value = info.getAttribute(attributeName);
+ if (value instanceof Integer)
+ return ((Integer) value).intValue();
+ return defaultValue;
+ }
+
+ /**
+ * @see IMarker#getAttribute(String, String)
+ */
+ public String getAttribute(String attributeName, String defaultValue) {
+ Assert.isNotNull(attributeName);
+ MarkerInfo info = getInfo();
+ if (info == null)
+ return defaultValue;
+ Object value = info.getAttribute(attributeName);
+ if (value instanceof String)
+ return (String) value;
+ return defaultValue;
+ }
+
+ /**
+ * @see IMarker#getAttribute(String, boolean)
+ */
+ public boolean getAttribute(String attributeName, boolean defaultValue) {
+ Assert.isNotNull(attributeName);
+ MarkerInfo info = getInfo();
+ if (info == null)
+ return defaultValue;
+ Object value = info.getAttribute(attributeName);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ return defaultValue;
+ }
+
+ /**
+ * @see IMarker#getAttributes()
+ */
+ public Map getAttributes() throws CoreException {
+ MarkerInfo info = getInfo();
+ checkInfo(info);
+ return info.getAttributes();
+ }
+
+ /**
+ * @see IMarker#getAttributes(String[])
+ */
+ public Object[] getAttributes(String[] attributeNames) throws CoreException {
+ Assert.isNotNull(attributeNames);
+ MarkerInfo info = getInfo();
+ checkInfo(info);
+ return info.getAttributes(attributeNames);
+ }
+
+ /**
+ * @see IMarker#getCreationTime()
+ */
+ public long getCreationTime() throws CoreException {
+ MarkerInfo info = getInfo();
+ checkInfo(info);
+ return info.getCreationTime();
+ }
+
+ /**
+ * @see IMarker#getId()
+ */
+ public long getId() {
+ return id;
+ }
+
+ protected MarkerInfo getInfo() {
+ return getWorkspace().getMarkerManager().findMarkerInfo(resource, id);
+ }
+
+ /**
+ * @see IMarker#getResource()
+ */
+ public IResource getResource() {
+ return resource;
+ }
+
+ /**
+ * @see IMarker#getType()
+ */
+ public String getType() throws CoreException {
+ MarkerInfo info = getInfo();
+ checkInfo(info);
+ return info.getType();
+ }
+
+ /**
+ * Returns the workspace which manages this marker. Returns
+ * <code>null</code> if this resource does not have an associated
+ * resource.
+ */
+ private Workspace getWorkspace() {
+ return resource == null ? null : (Workspace) resource.getWorkspace();
+ }
+
+ public int hashCode() {
+ return (int) id + resource.hashCode();
+ }
+
+ /**
+ * @see IMarker#isSubtypeOf(String)
+ */
+ public boolean isSubtypeOf(String type) throws CoreException {
+ return getWorkspace().getMarkerManager().isSubtype(getType(), type);
+ }
+
+ /**
+ * @see IMarker#setAttribute(String, int)
+ */
+ public void setAttribute(String attributeName, int value) throws CoreException {
+ setAttribute(attributeName, new Integer(value));
+ }
+
+ /**
+ * @see IMarker#setAttribute(String, Object)
+ */
+ public void setAttribute(String attributeName, Object value) throws CoreException {
+ Assert.isNotNull(attributeName);
+ Workspace workspace = getWorkspace();
+ MarkerManager manager = workspace.getMarkerManager();
+ try {
+ workspace.prepareOperation(null, null);
+ workspace.beginOperation(true);
+ MarkerInfo markerInfo = getInfo();
+ checkInfo(markerInfo);
+
+ //only need to generate delta info if none already
+ boolean needDelta = !manager.hasDelta(resource.getFullPath(), id);
+ MarkerInfo oldInfo = needDelta ? (MarkerInfo) markerInfo.clone() : null;
+ markerInfo.setAttribute(attributeName, value);
+ if (manager.isPersistent(markerInfo))
+ ((Resource) resource).getResourceInfo(false, true).set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ if (needDelta) {
+ MarkerDelta delta = new MarkerDelta(IResourceDelta.CHANGED, resource, oldInfo);
+ manager.changedMarkers(resource, new MarkerDelta[] {delta});
+ }
+ } finally {
+ workspace.endOperation(null, false, null);
+ }
+ }
+
+ /**
+ * @see IMarker#setAttribute(String, boolean)
+ */
+ public void setAttribute(String attributeName, boolean value) throws CoreException {
+ setAttribute(attributeName, value ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * @see IMarker#setAttributes(String[], Object[])
+ */
+ public void setAttributes(String[] attributeNames, Object[] values) throws CoreException {
+ Assert.isNotNull(attributeNames);
+ Assert.isNotNull(values);
+ Workspace workspace = getWorkspace();
+ MarkerManager manager = workspace.getMarkerManager();
+ try {
+ workspace.prepareOperation(null, null);
+ workspace.beginOperation(true);
+ MarkerInfo markerInfo = getInfo();
+ checkInfo(markerInfo);
+
+ //only need to generate delta info if none already
+ boolean needDelta = !manager.hasDelta(resource.getFullPath(), id);
+ MarkerInfo oldInfo = needDelta ? (MarkerInfo) markerInfo.clone() : null;
+ markerInfo.setAttributes(attributeNames, values);
+ if (manager.isPersistent(markerInfo))
+ ((Resource) resource).getResourceInfo(false, true).set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ if (needDelta) {
+ MarkerDelta delta = new MarkerDelta(IResourceDelta.CHANGED, resource, oldInfo);
+ manager.changedMarkers(resource, new MarkerDelta[] {delta});
+ }
+ } finally {
+ workspace.endOperation(null, false, null);
+ }
+ }
+
+ /**
+ * @see IMarker#setAttributes(Map)
+ */
+ public void setAttributes(Map values) throws CoreException {
+ Workspace workspace = getWorkspace();
+ MarkerManager manager = workspace.getMarkerManager();
+ try {
+ workspace.prepareOperation(null, null);
+ workspace.beginOperation(true);
+ MarkerInfo markerInfo = getInfo();
+ checkInfo(markerInfo);
+
+ //only need to generate delta info if none already
+ boolean needDelta = !manager.hasDelta(resource.getFullPath(), id);
+ MarkerInfo oldInfo = needDelta ? (MarkerInfo) markerInfo.clone() : null;
+ markerInfo.setAttributes(values);
+ if (manager.isPersistent(markerInfo))
+ ((Resource) resource).getResourceInfo(false, true).set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ if (needDelta) {
+ MarkerDelta delta = new MarkerDelta(IResourceDelta.CHANGED, resource, oldInfo);
+ manager.changedMarkers(resource, new MarkerDelta[] {delta});
+ }
+ } finally {
+ workspace.endOperation(null, false, null);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerAttributeMap.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.utils.IStringPoolParticipant;
+import org.eclipse.core.internal.utils.StringPool;
+
+/**
+ * A specialized map implementation that is optimized for a
+ * small set of interned strings as keys. The provided keys
+ * MUST be instances of java.lang.String.
+ *
+ * Implemented as a single array that alternates keys and values.
+ */
+public class MarkerAttributeMap implements Map, IStringPoolParticipant {
+ protected Object[] elements = null;
+ protected int count = 0;
+
+ // 8 attribute keys, 8 attribute values
+ protected static final int DEFAULT_SIZE = 16;
+ protected static final int GROW_SIZE = 10;
+
+ /**
+ * Creates a new marker attribute map of default size
+ */
+ public MarkerAttributeMap() {
+ super();
+ }
+
+ /**
+ * Creates a new marker attribute map.
+ * @param initialCapacity The initial number of elements that will fit in the map.
+ */
+ public MarkerAttributeMap(int initialCapacity) {
+ elements = new Object[Math.max(initialCapacity * 2, 0)];
+ }
+
+ /**
+ * Creates a new marker attribute map of default size
+ * @param map The entries in the given map will be added to the new map.
+ */
+ public MarkerAttributeMap(Map map) {
+ this(map.size());
+ putAll(map);
+ }
+
+ /* (non-Javadoc)
+ * @see Map#clear()
+ */
+ public void clear() {
+ elements = null;
+ count = 0;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#containsKey(java.lang.Object)
+ */
+ public boolean containsKey(Object key) {
+ key = ((String) key).intern();
+ if (elements == null || count == 0)
+ return false;
+ for (int i = 0; i < elements.length; i = i + 2)
+ if (elements[i] == key)
+ return true;
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#containsValue(java.lang.Object)
+ */
+ public boolean containsValue(Object value) {
+ if (elements == null || count == 0)
+ return false;
+ for (int i = 1; i < elements.length; i = i + 2)
+ if (elements[i] != null && elements[i].equals(value))
+ return true;
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#entrySet()
+ * This implementation does not conform properly to the specification
+ * in the Map interface. The returned collection will not be bound to
+ * this map and will not remain in sync with this map.
+ */
+ public Set entrySet() {
+ return toHashMap().entrySet();
+ }
+
+ /* (non-Javadoc)
+ * @see Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof Map))
+ return false;
+ Map other = (Map) o;
+ //must be same size
+ if (count != other.size())
+ return false;
+
+ //keysets must be equal
+ if (!keySet().equals(other.keySet()))
+ return false;
+
+ //values for each key must be equal
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null && (!elements[i + 1].equals(other.get(elements[i]))))
+ return false;
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#get(java.lang.Object)
+ */
+ public Object get(Object key) {
+ key = ((String) key).intern();
+ if (elements == null || count == 0)
+ return null;
+ for (int i = 0; i < elements.length; i = i + 2)
+ if (elements[i] == key)
+ return elements[i + 1];
+ return null;
+ }
+
+ /**
+ * The capacity of the map has been exceeded, grow the array by
+ * GROW_SIZE to accomodate more entries.
+ */
+ protected void grow() {
+ Object[] expanded = new Object[elements.length + GROW_SIZE];
+ System.arraycopy(elements, 0, expanded, 0, elements.length);
+ elements = expanded;
+ }
+
+ /* (non-Javadoc)
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ int hash = 0;
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ hash += elements[i].hashCode();
+ }
+ }
+ return hash;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#isEmpty()
+ */
+ public boolean isEmpty() {
+ return count == 0;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#keySet()
+ * This implementation does not conform properly to the specification
+ * in the Map interface. The returned collection will not be bound to
+ * this map and will not remain in sync with this map.
+ */
+ public Set keySet() {
+ Set result = new HashSet(size());
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ result.add(elements[i]);
+ }
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#put(java.lang.Object, java.lang.Object)
+ */
+ public Object put(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ if (value == null)
+ return remove(key);
+ key = ((String) key).intern();
+
+ // handle the case where we don't have any attributes yet
+ if (elements == null)
+ elements = new Object[DEFAULT_SIZE];
+ if (count == 0) {
+ elements[0] = key;
+ elements[1] = value;
+ count++;
+ return null;
+ }
+
+ // replace existing value if it exists
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] == key) {
+ Object oldValue = elements[i + 1];
+ elements[i + 1] = value;
+ return oldValue;
+ }
+ }
+
+ // otherwise add it to the list of elements.
+ // grow if necessary
+ if (elements.length <= (count * 2))
+ grow();
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] == null) {
+ elements[i] = key;
+ elements[i + 1] = value;
+ count++;
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#putAll(java.util.Map)
+ */
+ public void putAll(Map map) {
+ for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ Object value = map.get(key);
+ put(key, value);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see Map#remove(java.lang.Object)
+ */
+ public Object remove(Object key) {
+ key = ((String) key).intern();
+ if (elements == null || count == 0)
+ return null;
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] == key) {
+ elements[i] = null;
+ Object result = elements[i + 1];
+ elements[i + 1] = null;
+ count--;
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#size()
+ */
+ public int size() {
+ return count;
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool set) {
+ //copy elements for thread safety
+ Object[] array = elements;
+ if (array == null)
+ return;
+ //don't share keys because they are already interned
+ for (int i = 1; i < array.length; i = i + 2) {
+ Object o = array[i];
+ if (o instanceof String)
+ array[i] = set.add((String)o);
+ else if (o instanceof IStringPoolParticipant)
+ ((IStringPoolParticipant)o).shareStrings(set);
+ }
+ }
+
+ /**
+ * Creates a new hash map with the same contents as this map.
+ */
+ private HashMap toHashMap() {
+ HashMap result = new HashMap(size());
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ result.put(elements[i], elements[i + 1]);
+ }
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see Map#values()
+ * This implementation does not conform properly to the specification
+ * in the Map interface. The returned collection will not be bound to
+ * this map and will not remain in sync with this map.
+ */
+ public Collection values() {
+ Set result = new HashSet(size());
+ for (int i = 1; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ result.add(elements[i]);
+ }
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerDelta.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.Iterator;
+import java.util.Map;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * @see IMarkerDelta
+ */
+public class MarkerDelta implements IMarkerDelta, IMarkerSetElement {
+ protected int kind;
+ protected IResource resource;
+ protected MarkerInfo info;
+
+ /**
+ * Creates a new marker delta.
+ */
+ public MarkerDelta(int kind, IResource resource, MarkerInfo info) {
+ this.kind = kind;
+ this.resource = resource;
+ this.info = info;
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getAttribute(String)
+ */
+ public Object getAttribute(String attributeName) {
+ return info.getAttribute(attributeName);
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getAttribute(String, int)
+ */
+ public int getAttribute(String attributeName, int defaultValue) {
+ Object value = info.getAttribute(attributeName);
+ if (value instanceof Integer)
+ return ((Integer) value).intValue();
+ return defaultValue;
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getAttribute(String, String)
+ */
+ public String getAttribute(String attributeName, String defaultValue) {
+ Object value = info.getAttribute(attributeName);
+ if (value instanceof String)
+ return (String) value;
+ return defaultValue;
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getAttribute(String, boolean)
+ */
+ public boolean getAttribute(String attributeName, boolean defaultValue) {
+ Object value = info.getAttribute(attributeName);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ return defaultValue;
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getAttributes()
+ */
+ public Map getAttributes() {
+ return info.getAttributes();
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getAttributes(String[])
+ */
+ public Object[] getAttributes(String[] attributeNames) {
+ return info.getAttributes(attributeNames);
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getId()
+ */
+ public long getId() {
+ return info.getId();
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getKind()
+ */
+ public int getKind() {
+ return kind;
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getMarker()
+ */
+ public IMarker getMarker() {
+ return new Marker(resource, getId());
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getResource()
+ */
+ public IResource getResource() {
+ return resource;
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#getType()
+ */
+ public String getType() {
+ return info.getType();
+ }
+
+ /* (non-Javadoc)
+ * @see IMarkerDelta#isSubtypeOf(String)
+ */
+ public boolean isSubtypeOf(String superType) {
+ return ((Workspace) getResource().getWorkspace()).getMarkerManager().isSubtype(getType(), superType);
+ }
+
+ /**
+ * Merge two Maps of (IPath->MarkerSet) representing changes. Use the old
+ * map to store the result so we don't have to build a new map to return.
+ */
+ public static Map merge(Map oldChanges, Map newChanges) {
+ if (oldChanges == null)
+ //don't worry about copying since the new changes are no longer used
+ return newChanges;
+ if (newChanges == null)
+ return oldChanges;
+ for (Iterator it = newChanges.keySet().iterator(); it.hasNext();) {
+ IPath key = (IPath) it.next();
+ MarkerSet oldSet = (MarkerSet) oldChanges.get(key);
+ MarkerSet newSet = (MarkerSet) newChanges.get(key);
+ if (oldSet == null)
+ oldChanges.put(key, newSet);
+ else
+ merge(oldSet, newSet.elements());
+ }
+ return oldChanges;
+ }
+
+ /**
+ * Merge two sets of marker changes. Both sets must be on the same resource. Use the original set
+ * of changes to store the result so we don't have to build a completely different set to return.
+ *
+ * add + add = N/A
+ * add + remove = nothing (no delta)
+ * add + change = add
+ * remove + add = N/A
+ * remove + remove = N/A
+ * remove + change = N/A
+ * change + add = N/A
+ * change + change = change (note: info held onto by the marker delta should be that of the oldest change, and not replaced when composed)
+ * change + remove = remove (note: info held onto by the marker delta should be that of the oldest change, and not replaced when changed to a remove)
+ */
+ protected static MarkerSet merge(MarkerSet oldChanges, IMarkerSetElement[] newChanges) {
+ if (oldChanges == null) {
+ MarkerSet result = new MarkerSet(newChanges.length);
+ for (int i = 0; i < newChanges.length; i++)
+ result.add(newChanges[i]);
+ return result;
+ }
+ if (newChanges == null)
+ return oldChanges;
+
+ for (int i = 0; i < newChanges.length; i++) {
+ MarkerDelta newDelta = (MarkerDelta) newChanges[i];
+ MarkerDelta oldDelta = (MarkerDelta) oldChanges.get(newDelta.getId());
+ if (oldDelta == null) {
+ oldChanges.add(newDelta);
+ continue;
+ }
+ switch (oldDelta.getKind()) {
+ case IResourceDelta.ADDED :
+ switch (newDelta.getKind()) {
+ case IResourceDelta.ADDED :
+ // add + add = N/A
+ break;
+ case IResourceDelta.REMOVED :
+ // add + remove = nothing
+ // Remove the original ADD delta.
+ oldChanges.remove(oldDelta);
+ break;
+ case IResourceDelta.CHANGED :
+ // add + change = add
+ break;
+ }
+ break;
+ case IResourceDelta.REMOVED :
+ switch (newDelta.getKind()) {
+ case IResourceDelta.ADDED :
+ // remove + add = N/A
+ break;
+ case IResourceDelta.REMOVED :
+ // remove + remove = N/A
+ break;
+ case IResourceDelta.CHANGED :
+ // remove + change = N/A
+ break;
+ }
+ break;
+ case IResourceDelta.CHANGED :
+ switch (newDelta.getKind()) {
+ case IResourceDelta.ADDED :
+ // change + add = N/A
+ break;
+ case IResourceDelta.REMOVED :
+ // change + remove = remove
+ // Change the delta kind.
+ oldDelta.setKind(IResourceDelta.REMOVED);
+ break;
+ case IResourceDelta.CHANGED :
+ // change + change = change
+ break;
+ }
+ break;
+ }
+ }
+ return oldChanges;
+ }
+
+ private void setKind(int kind) {
+ this.kind = kind;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerDeltaManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+
+/**
+ * The notification mechanism can request marker deltas for several overlapping intervals
+ * of time. This class maintains a history of marker deltas, and upon request can
+ * generate a map of marker deltas for any interval. This is done by maintaining
+ * batches of marker deltas keyed by the change Id at the start of that batch.
+ * When the delta factory requests a delta, it specifies the start generation, and
+ * this class assembles the deltas for all generations between then and the most
+ * recent delta.
+ */
+class MarkerDeltaManager {
+ private static final int DEFAULT_SIZE = 10;
+ private long[] startIds = new long[DEFAULT_SIZE];
+ private Map[] batches = new Map[DEFAULT_SIZE];
+ private int nextFree = 0;
+
+ /**
+ * Returns the deltas from the given start id up until the present. Returns null
+ * if there are no deltas for that interval.
+ */
+ protected Map assembleDeltas(long start) {
+ Map result = null;
+ for (int i = 0; i < nextFree; i++)
+ if (startIds[i] >= start)
+ result = MarkerDelta.merge(result, batches[i]);
+ return result;
+ }
+
+ /**
+ * Flushes all delta batches up to but not including the given start Id.
+ */
+ protected void resetDeltas(long startId) {
+ //find offset of first batch to keep
+ int startOffset = 0;
+ for (; startOffset < nextFree; startOffset++)
+ if (startIds[startOffset] >= startId)
+ break;
+ if (startOffset == 0)
+ return;
+ long[] newIds = startIds;
+ Map[] newBatches = batches;
+ //shrink the arrays if it has grown too large
+ if (startIds.length > DEFAULT_SIZE && (nextFree - startOffset < DEFAULT_SIZE)) {
+ newIds = new long[DEFAULT_SIZE];
+ newBatches = new Map[DEFAULT_SIZE];
+ }
+ //copy and compact into the new array
+ int remaining = nextFree - startOffset;
+ System.arraycopy(startIds, startOffset, newIds, 0, remaining);
+ System.arraycopy(batches, startOffset, newBatches, 0, remaining);
+ //clear the end of the array
+ Arrays.fill(startIds, remaining, startIds.length, 0);
+ Arrays.fill(batches, remaining, startIds.length, null);
+ startIds = newIds;
+ batches = newBatches;
+ nextFree = remaining;
+ }
+
+ protected Map newGeneration(long start) {
+ int len = startIds.length;
+ if (nextFree >= len) {
+ long[] newIds = new long[len * 2];
+ Map[] newBatches = new Map[len * 2];
+ System.arraycopy(startIds, 0, newIds, 0, len);
+ System.arraycopy(batches, 0, newBatches, 0, len);
+ startIds = newIds;
+ batches = newBatches;
+ }
+ startIds[nextFree] = start;
+ batches[nextFree] = new HashMap(11);
+ return batches[nextFree++];
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerInfo.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.Map;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.osgi.util.NLS;
+
+public class MarkerInfo implements IMarkerSetElement, Cloneable, IStringPoolParticipant {
+
+ // well known Integer values
+ protected static final Integer INTEGER_ONE = new Integer(1);
+ protected static final Integer INTEGER_TWO = new Integer(2);
+ protected static final Integer INTEGER_ZERO = new Integer(0);
+
+ //
+ protected static final long UNDEFINED_ID = -1;
+ /** The store of attributes for this marker. */
+ protected Map attributes = null;
+
+ /** The creation time for this marker. */
+ protected long creationTime = 0;
+
+ /** Marker identifier. */
+ protected long id = UNDEFINED_ID;
+
+ /** The type of this marker. */
+ protected String type = null;
+
+ /**
+ * Returns whether the given object is a valid attribute value. Returns
+ * either the attribute or an equal canonical substitute.
+ */
+ protected static Object checkValidAttribute(Object value) {
+ if (value == null)
+ return null;
+ if (value instanceof String) {
+ //we cannot write attributes whose UTF encoding exceeds 65535 bytes.
+ String valueString = (String) value;
+ //optimized test based on maximum 3 bytes per character
+ if (valueString.length() < 21000)
+ return value;
+ byte[] bytes;
+ try {
+ bytes = valueString.getBytes(("UTF-8"));//$NON-NLS-1$
+ } catch (UnsupportedEncodingException uee) {
+ //cannot validate further
+ return value;
+ }
+ if (bytes.length > 65535) {
+ String msg = "Marker property value is too long: " + valueString.substring(0, 10000); //$NON-NLS-1$
+ Assert.isTrue(false, msg);
+ }
+ return value;
+ }
+ if (value instanceof Boolean) {
+ //return canonical boolean
+ return ((Boolean) value).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
+ }
+ if (value instanceof Integer) {
+ //replace common integers with canonical values
+ switch (((Integer) value).intValue()) {
+ case 0 :
+ return INTEGER_ZERO;
+ case 1 :
+ return INTEGER_ONE;
+ case 2 :
+ return INTEGER_TWO;
+ }
+ return value;
+ }
+ //if we got here, it's an invalid attribute value type
+ throw new IllegalArgumentException(NLS.bind(Messages.resources_wrongMarkerAttributeValueType, value.getClass().getName()));
+ }
+
+ public MarkerInfo() {
+ super();
+ }
+
+ /**
+ * See Object#clone.
+ */
+ public Object clone() {
+ try {
+ MarkerInfo copy = (MarkerInfo) super.clone();
+ //copy the attribute table contents
+ copy.attributes = getAttributes(true);
+ return copy;
+ } catch (CloneNotSupportedException e) {
+ //cannot happen because this class implements Cloneable
+ return null;
+ }
+ }
+
+ public Object getAttribute(String attributeName) {
+ return attributes == null ? null : attributes.get(attributeName);
+ }
+
+ public Map getAttributes() {
+ return getAttributes(true);
+ }
+
+ public Map getAttributes(boolean makeCopy) {
+ if (attributes == null)
+ return null;
+ return makeCopy ? new MarkerAttributeMap(attributes) : attributes;
+ }
+
+ public Object[] getAttributes(String[] attributeNames) {
+ Object[] result = new Object[attributeNames.length];
+ for (int i = 0; i < attributeNames.length; i++)
+ result[i] = getAttribute(attributeNames[i]);
+ return result;
+ }
+
+ public long getCreationTime() {
+ return creationTime;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void internalSetAttributes(Map map) {
+ //the cast effectively acts as an assertion to make sure
+ //the right kind of map is being used
+ attributes = map;
+ }
+
+ public void setAttribute(String attributeName, Object value) {
+ value = checkValidAttribute(value);
+ if (attributes == null) {
+ if (value == null)
+ return;
+ attributes = new MarkerAttributeMap();
+ attributes.put(attributeName, value);
+ } else {
+ if (value == null) {
+ attributes.remove(attributeName);
+ if (attributes.isEmpty())
+ attributes = null;
+ } else {
+ attributes.put(attributeName, value);
+ }
+ }
+ }
+
+ public void setAttributes(Map map) {
+ if (map == null)
+ attributes = null;
+ else {
+ attributes = new MarkerAttributeMap(map.size());
+ for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ Assert.isTrue(key instanceof String);
+ Object value = map.get(key);
+ setAttribute((String) key, MarkerInfo.checkValidAttribute(value));
+ }
+ }
+ }
+
+ public void setAttributes(String[] attributeNames, Object[] values) {
+ Assert.isTrue(attributeNames.length == values.length);
+ for (int i = 0; i < attributeNames.length; i++)
+ setAttribute(attributeNames[i], values[i]);
+ }
+
+ public void setCreationTime(long value) {
+ creationTime = value;
+ }
+
+ public void setId(long value) {
+ id = value;
+ }
+
+ public void setType(String value) {
+ type = value;
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool set) {
+ type = set.add(type);
+ Map map = attributes;
+ if (map instanceof IStringPoolParticipant)
+ ((IStringPoolParticipant) map).shareStrings(set);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,653 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.localstore.SafeChunkyInputStream;
+import org.eclipse.core.internal.localstore.SafeFileInputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * A marker manager stores and retrieves markers on resources in the workspace.
+ */
+public class MarkerManager implements IManager {
+
+ //singletons
+ private static final MarkerInfo[] NO_MARKER_INFO = new MarkerInfo[0];
+ private static final IMarker[] NO_MARKERS = new IMarker[0];
+ protected MarkerTypeDefinitionCache cache = new MarkerTypeDefinitionCache();
+ private long changeId = 0;
+ protected Map currentDeltas = null;
+ protected final MarkerDeltaManager deltaManager = new MarkerDeltaManager();
+
+ protected Workspace workspace;
+ protected MarkerWriter writer = new MarkerWriter(this);
+
+ /**
+ * Creates a new marker manager
+ */
+ public MarkerManager(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ /* (non-Javadoc)
+ * Adds the given markers to the given resource.
+ *
+ * @see IResource#createMarker(String)
+ */
+ public void add(IResource resource, MarkerInfo newMarker) throws CoreException {
+ Resource target = (Resource) resource;
+ ResourceInfo info = workspace.getResourceInfo(target.getFullPath(), false, false);
+ target.checkExists(target.getFlags(info), false);
+ info = workspace.getResourceInfo(resource.getFullPath(), false, true);
+ //resource may have been deleted concurrently -- just bail out if this happens
+ if (info == null)
+ return;
+ // set the M_MARKERS_SNAP_DIRTY flag to indicate that this
+ // resource's markers have changed since the last snapshot
+ if (isPersistent(newMarker))
+ info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ //Concurrency: copy the marker set on modify
+ MarkerSet markers = info.getMarkers(true);
+ if (markers == null)
+ markers = new MarkerSet(1);
+ basicAdd(resource, markers, newMarker);
+ if (!markers.isEmpty())
+ info.setMarkers(markers);
+ }
+
+ /**
+ * Adds the new markers to the given set of markers. If added, the markers
+ * are associated with the specified resource.IMarkerDeltas for Added markers
+ * are generated.
+ */
+ private void basicAdd(IResource resource, MarkerSet markers, MarkerInfo newMarker) throws CoreException {
+ // should always be a new marker.
+ if (newMarker.getId() != MarkerInfo.UNDEFINED_ID) {
+ String message = Messages.resources_changeInAdd;
+ throw new ResourceException(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, resource.getFullPath(), message));
+ }
+ newMarker.setId(workspace.nextMarkerId());
+ markers.add(newMarker);
+ IMarkerSetElement[] changes = new IMarkerSetElement[1];
+ changes[0] = new MarkerDelta(IResourceDelta.ADDED, resource, newMarker);
+ changedMarkers(resource, changes);
+ }
+
+ /**
+ * Returns the markers in the given set of markers which match the given type.
+ */
+ protected MarkerInfo[] basicFindMatching(MarkerSet markers, String type, boolean includeSubtypes) {
+ int size = markers.size();
+ if (size <= 0)
+ return NO_MARKER_INFO;
+ List result = new ArrayList(size);
+ IMarkerSetElement[] elements = markers.elements();
+ for (int i = 0; i < elements.length; i++) {
+ MarkerInfo marker = (MarkerInfo) elements[i];
+ // if the type is null then we are looking for all types of markers
+ if (type == null)
+ result.add(marker);
+ else {
+ if (includeSubtypes) {
+ if (cache.isSubtype(marker.getType(), type))
+ result.add(marker);
+ } else {
+ if (marker.getType().equals(type))
+ result.add(marker);
+ }
+ }
+ }
+ size = result.size();
+ if (size <= 0)
+ return NO_MARKER_INFO;
+ return (MarkerInfo[]) result.toArray(new MarkerInfo[size]);
+ }
+
+ protected int basicFindMaxSeverity(MarkerSet markers, String type, boolean includeSubtypes) {
+ int max = -1;
+ int size = markers.size();
+ if (size <= 0)
+ return max;
+ IMarkerSetElement[] elements = markers.elements();
+ for (int i = 0; i < elements.length; i++) {
+ MarkerInfo marker = (MarkerInfo) elements[i];
+ // if the type is null then we are looking for all types of markers
+ if (type == null)
+ max = Math.max(max, getSeverity(marker));
+ else {
+ if (includeSubtypes) {
+ if (cache.isSubtype(marker.getType(), type))
+ max = Math.max(max, getSeverity(marker));
+ } else {
+ if (marker.getType().equals(type))
+ max = Math.max(max, getSeverity(marker));
+ }
+ }
+ if (max >= IMarker.SEVERITY_ERROR) {
+ break;
+ }
+ }
+ return max;
+ }
+
+ private int getSeverity(MarkerInfo marker) {
+ Object o = marker.getAttribute(IMarker.SEVERITY);
+ if (o instanceof Integer) {
+ Integer i = (Integer) o;
+ return i.intValue();
+ }
+ return -1;
+ }
+
+ /**
+ * Removes markers of the specified type from the given resource.
+ * Note: this method is protected to avoid creation of a synthetic accessor (it
+ * is called from an anonymous inner class).
+ */
+ protected void basicRemoveMarkers(ResourceInfo info, IPathRequestor requestor, String type, boolean includeSubtypes) {
+ MarkerSet markers = info.getMarkers(false);
+ if (markers == null)
+ return;
+ IMarkerSetElement[] matching;
+ IPath path;
+ if (type == null) {
+ // if the type is null, all markers are to be removed.
+ //now we need to crack open the tree
+ path = requestor.requestPath();
+ info = workspace.getResourceInfo(path, false, true);
+ info.setMarkers(null);
+ matching = markers.elements();
+ } else {
+ matching = basicFindMatching(markers, type, includeSubtypes);
+ // if none match, there is nothing to remove
+ if (matching.length == 0)
+ return;
+ //now we need to crack open the tree
+ path = requestor.requestPath();
+ info = workspace.getResourceInfo(path, false, true);
+ //Concurrency: copy the marker set on modify
+ markers = info.getMarkers(true);
+ // remove all the matching markers and also the whole
+ // set if there are no remaining markers
+ markers.removeAll(matching);
+ info.setMarkers(markers.size() == 0 ? null : markers);
+ }
+ info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ IMarkerSetElement[] changes = new IMarkerSetElement[matching.length];
+ IResource resource = workspace.getRoot().findMember(path);
+ for (int i = 0; i < matching.length; i++)
+ changes[i] = new MarkerDelta(IResourceDelta.REMOVED, resource, (MarkerInfo) matching[i]);
+ changedMarkers(resource, changes);
+ return;
+ }
+
+ /**
+ * Adds the markers on the given target which match the specified type to the list.
+ */
+ protected void buildMarkers(IMarkerSetElement[] markers, IPath path, int type, ArrayList list) {
+ if (markers.length == 0)
+ return;
+ IResource resource = workspace.newResource(path, type);
+ list.ensureCapacity(list.size() + markers.length);
+ for (int i = 0; i < markers.length; i++) {
+ list.add(new Marker(resource, ((MarkerInfo) markers[i]).getId()));
+ }
+ }
+
+ /**
+ * Markers have changed on the given resource. Remember the changes for subsequent notification.
+ */
+ protected void changedMarkers(IResource resource, IMarkerSetElement[] changes) {
+ if (changes == null || changes.length == 0)
+ return;
+ changeId++;
+ if (currentDeltas == null)
+ currentDeltas = deltaManager.newGeneration(changeId);
+ IPath path = resource.getFullPath();
+ MarkerSet previousChanges = (MarkerSet) currentDeltas.get(path);
+ MarkerSet result = MarkerDelta.merge(previousChanges, changes);
+ if (result.size() == 0)
+ currentDeltas.remove(path);
+ else
+ currentDeltas.put(path, result);
+ ResourceInfo info = workspace.getResourceInfo(path, false, true);
+ if (info != null)
+ info.incrementMarkerGenerationCount();
+ }
+
+ /**
+ * Returns the marker with the given id or <code>null</code> if none is found.
+ */
+ public IMarker findMarker(IResource resource, long id) {
+ MarkerInfo info = findMarkerInfo(resource, id);
+ return info == null ? null : new Marker(resource, info.getId());
+ }
+
+ /**
+ * Returns the marker with the given id or <code>null</code> if none is found.
+ */
+ public MarkerInfo findMarkerInfo(IResource resource, long id) {
+ ResourceInfo info = workspace.getResourceInfo(resource.getFullPath(), false, false);
+ if (info == null)
+ return null;
+ MarkerSet markers = info.getMarkers(false);
+ if (markers == null)
+ return null;
+ return (MarkerInfo) markers.get(id);
+ }
+
+ /**
+ * Returns all markers of the specified type on the given target, with option
+ * to search the target's children.
+ * Passing <code>null</code> for the type specifies a match
+ * for all types (i.e., <code>null</code> is a wildcard.
+ */
+ public IMarker[] findMarkers(IResource target, final String type, final boolean includeSubtypes, int depth) {
+ ArrayList result = new ArrayList();
+ doFindMarkers(target, result, type, includeSubtypes, depth);
+ if (result.size() == 0)
+ return NO_MARKERS;
+ return (IMarker[]) result.toArray(new IMarker[result.size()]);
+ }
+
+ /**
+ * Fills the provided list with all markers of the specified type on the given target,
+ * with option to search the target's children.
+ * Passing <code>null</code> for the type specifies a match
+ * for all types (i.e., <code>null</code> is a wildcard.
+ */
+ public void doFindMarkers(IResource target, ArrayList result, final String type, final boolean includeSubtypes, int depth) {
+ //optimize the deep searches with an element tree visitor
+ if (depth == IResource.DEPTH_INFINITE && target.getType() != IResource.FILE)
+ visitorFindMarkers(target.getFullPath(), result, type, includeSubtypes);
+ else
+ recursiveFindMarkers(target.getFullPath(), result, type, includeSubtypes, depth);
+ }
+
+ /**
+ * Finds the max severity across all problem markers on the given target,
+ * with option to search the target's children.
+ */
+ public int findMaxProblemSeverity(IResource target, String type, boolean includeSubtypes, int depth) {
+ //optimize the deep searches with an element tree visitor
+ if (depth == IResource.DEPTH_INFINITE && target.getType() != IResource.FILE)
+ return visitorFindMaxSeverity(target.getFullPath(), type, includeSubtypes);
+ return recursiveFindMaxSeverity(target.getFullPath(), type, includeSubtypes, depth);
+ }
+
+ public long getChangeId() {
+ return changeId;
+ }
+
+ /**
+ * Returns the map of all marker deltas since the given change Id.
+ */
+ public Map getMarkerDeltas(long startChangeId) {
+ return deltaManager.assembleDeltas(startChangeId);
+ }
+
+ /**
+ * Returns true if this manager has a marker delta record
+ * for the given marker id, and false otherwise.
+ */
+ boolean hasDelta(IPath path, long id) {
+ if (currentDeltas == null)
+ return false;
+ MarkerSet set = (MarkerSet) currentDeltas.get(path);
+ if (set == null)
+ return false;
+ return set.get(id) != null;
+ }
+
+ /**
+ * Returns true if the given marker is persistent, and false
+ * otherwise.
+ */
+ public boolean isPersistent(MarkerInfo info) {
+ if (!cache.isPersistent(info.getType()))
+ return false;
+ Object isTransient = info.getAttribute(IMarker.TRANSIENT);
+ return isTransient == null || !(isTransient instanceof Boolean) || !((Boolean) isTransient).booleanValue();
+ }
+
+ /**
+ * Returns true if <code>type</code> is a sub type of <code>superType</code>.
+ */
+ public boolean isSubtype(String type, String superType) {
+ return cache.isSubtype(type, superType);
+ }
+
+ public void moved(final IResource source, final IResource destination, int depth) throws CoreException {
+ final int count = destination.getFullPath().segmentCount();
+
+ // we removed from the source and added to the destination
+ IResourceVisitor visitor = new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ Resource r = (Resource) resource;
+ ResourceInfo info = r.getResourceInfo(false, true);
+ MarkerSet markers = info.getMarkers(false);
+ if (markers == null)
+ return true;
+ info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ IMarkerSetElement[] removed = new IMarkerSetElement[markers.size()];
+ IMarkerSetElement[] added = new IMarkerSetElement[markers.size()];
+ IPath path = resource.getFullPath().removeFirstSegments(count);
+ path = source.getFullPath().append(path);
+ IResource sourceChild = workspace.newResource(path, resource.getType());
+ IMarkerSetElement[] elements = markers.elements();
+ for (int i = 0; i < elements.length; i++) {
+ // calculate the ADDED delta
+ MarkerInfo markerInfo = (MarkerInfo) elements[i];
+ MarkerDelta delta = new MarkerDelta(IResourceDelta.ADDED, resource, markerInfo);
+ added[i] = delta;
+ // calculate the REMOVED delta
+ delta = new MarkerDelta(IResourceDelta.REMOVED, sourceChild, markerInfo);
+ removed[i] = delta;
+ }
+ changedMarkers(resource, added);
+ changedMarkers(sourceChild, removed);
+ return true;
+ }
+ };
+ destination.accept(visitor, depth, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ }
+
+ /**
+ * Adds the markers for a subtree of resources to the list.
+ */
+ private void recursiveFindMarkers(IPath path, ArrayList list, String type, boolean includeSubtypes, int depth) {
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ return;
+ MarkerSet markers = info.getMarkers(false);
+
+ //add the matching markers for this resource
+ if (markers != null) {
+ IMarkerSetElement[] matching;
+ if (type == null)
+ matching = markers.elements();
+ else
+ matching = basicFindMatching(markers, type, includeSubtypes);
+ buildMarkers(matching, path, info.getType(), list);
+ }
+
+ //recurse
+ if (depth == IResource.DEPTH_ZERO || info.getType() == IResource.FILE)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ IPath[] children = workspace.getElementTree().getChildren(path);
+ for (int i = 0; i < children.length; i++) {
+ recursiveFindMarkers(children[i], list, type, includeSubtypes, depth);
+ }
+ }
+
+ /**
+ * Finds the max severity across problem markers for a subtree of resources.
+ */
+ private int recursiveFindMaxSeverity(IPath path, String type, boolean includeSubtypes, int depth) {
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ return -1;
+ MarkerSet markers = info.getMarkers(false);
+
+ //add the matching markers for this resource
+ int max = -1;
+ if (markers != null) {
+ max = basicFindMaxSeverity(markers, type, includeSubtypes);
+ if (max >= IMarker.SEVERITY_ERROR) {
+ return max;
+ }
+ }
+
+ //recurse
+ if (depth == IResource.DEPTH_ZERO || info.getType() == IResource.FILE)
+ return max;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ IPath[] children = workspace.getElementTree().getChildren(path);
+ for (int i = 0; i < children.length; i++) {
+ max = Math.max(max, recursiveFindMaxSeverity(children[i], type, includeSubtypes, depth));
+ if (max >= IMarker.SEVERITY_ERROR) {
+ break;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * Adds the markers for a subtree of resources to the list.
+ */
+ private void recursiveRemoveMarkers(final IPath path, String type, boolean includeSubtypes, int depth) {
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)//phantoms don't have markers
+ return;
+ IPathRequestor requestor = new IPathRequestor() {
+ public String requestName() {
+ return path.lastSegment();
+ }
+
+ public IPath requestPath() {
+ return path;
+ }
+ };
+ basicRemoveMarkers(info, requestor, type, includeSubtypes);
+ //recurse
+ if (depth == IResource.DEPTH_ZERO || info.getType() == IResource.FILE)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ IPath[] children = workspace.getElementTree().getChildren(path);
+ for (int i = 0; i < children.length; i++) {
+ recursiveRemoveMarkers(children[i], type, includeSubtypes, depth);
+ }
+ }
+
+ /**
+ * Removes the specified marker
+ */
+ public void removeMarker(IResource resource, long id) {
+ MarkerInfo markerInfo = findMarkerInfo(resource, id);
+ if (markerInfo == null)
+ return;
+ ResourceInfo info = ((Workspace) resource.getWorkspace()).getResourceInfo(resource.getFullPath(), false, true);
+ //Concurrency: copy the marker set on modify
+ MarkerSet markers = info.getMarkers(true);
+ int size = markers.size();
+ markers.remove(markerInfo);
+ // if that was the last marker remove the set to save space.
+ info.setMarkers(markers.size() == 0 ? null : markers);
+ // if we actually did remove a marker, post a delta for the change.
+ if (markers.size() != size) {
+ if (isPersistent(markerInfo))
+ info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ IMarkerSetElement[] change = new IMarkerSetElement[] {new MarkerDelta(IResourceDelta.REMOVED, resource, markerInfo)};
+ changedMarkers(resource, change);
+ }
+ }
+
+ /**
+ * Remove all markers for the given resource to the specified depth.
+ */
+ public void removeMarkers(IResource resource, int depth) {
+ removeMarkers(resource, null, false, depth);
+ }
+
+ /**
+ * Remove all markers with the given type from the node at the given path.
+ * Passing <code>null</code> for the type specifies a match
+ * for all types (i.e., <code>null</code> is a wildcard.
+ */
+ public void removeMarkers(IResource target, final String type, final boolean includeSubtypes, int depth) {
+ if (depth == IResource.DEPTH_INFINITE && target.getType() != IResource.FILE)
+ visitorRemoveMarkers(target.getFullPath(), type, includeSubtypes);
+ else
+ recursiveRemoveMarkers(target.getFullPath(), type, includeSubtypes, depth);
+ }
+
+ /**
+ * Reset the marker deltas up to but not including the given start Id.
+ */
+ public void resetMarkerDeltas(long startId) {
+ currentDeltas = null;
+ deltaManager.resetDeltas(startId);
+ }
+
+ public void restore(IResource resource, boolean generateDeltas, IProgressMonitor monitor) throws CoreException {
+ // first try and load the last saved file, then apply the snapshots
+ restoreFromSave(resource, generateDeltas);
+ restoreFromSnap(resource);
+ }
+
+ protected void restoreFromSave(IResource resource, boolean generateDeltas) throws CoreException {
+ IPath sourceLocation = workspace.getMetaArea().getMarkersLocationFor(resource);
+ IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(sourceLocation);
+ java.io.File sourceFile = new java.io.File(sourceLocation.toOSString());
+ java.io.File tempFile = new java.io.File(tempLocation.toOSString());
+ if (!sourceFile.exists() && !tempFile.exists())
+ return;
+ try {
+ DataInputStream input = new DataInputStream(new SafeFileInputStream(sourceLocation.toOSString(), tempLocation.toOSString()));
+ try {
+ MarkerReader reader = new MarkerReader(workspace);
+ reader.read(input, generateDeltas);
+ } finally {
+ input.close();
+ }
+ } catch (Exception e) {
+ //don't let runtime exceptions such as ArrayIndexOutOfBounds prevent startup
+ String msg = NLS.bind(Messages.resources_readMeta, sourceLocation);
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, sourceLocation, msg, e);
+ }
+ }
+
+ protected void restoreFromSnap(IResource resource) {
+ IPath sourceLocation = workspace.getMetaArea().getMarkersSnapshotLocationFor(resource);
+ if (!sourceLocation.toFile().exists())
+ return;
+ try {
+ DataInputStream input = new DataInputStream(new SafeChunkyInputStream(sourceLocation.toFile()));
+ try {
+ MarkerSnapshotReader reader = new MarkerSnapshotReader(workspace);
+ while (true)
+ reader.read(input);
+ } catch (EOFException eof) {
+ // ignore end of file
+ } finally {
+ input.close();
+ }
+ } catch (Exception e) {
+ // only log the exception, we should not fail restoring the snapshot
+ String msg = NLS.bind(Messages.resources_readMeta, sourceLocation);
+ Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, sourceLocation, msg, e));
+ }
+ }
+
+ public void save(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List list) throws IOException {
+ writer.save(info, requestor, output, list);
+ }
+
+ /* (non-Javadoc)
+ * @see IManager#shutdown(IProgressMonitor)
+ */
+ public void shutdown(IProgressMonitor monitor) {
+ // do nothing
+ }
+
+ public void snap(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException {
+ writer.snap(info, requestor, output);
+ }
+
+ /* (non-Javadoc)
+ * @see IManager#startup(IProgressMonitor)
+ */
+ public void startup(IProgressMonitor monitor) {
+ // do nothing
+ }
+
+ /**
+ * Adds the markers for a subtree of resources to the list.
+ */
+ private void visitorFindMarkers(IPath path, final ArrayList list, final String type, final boolean includeSubtypes) {
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (info == null)
+ return false;
+ MarkerSet markers = info.getMarkers(false);
+
+ //add the matching markers for this resource
+ if (markers != null) {
+ IMarkerSetElement[] matching;
+ if (type == null)
+ matching = markers.elements();
+ else
+ matching = basicFindMatching(markers, type, includeSubtypes);
+ buildMarkers(matching, requestor.requestPath(), info.getType(), list);
+ }
+ return true;
+ }
+ };
+ new ElementTreeIterator(workspace.getElementTree(), path).iterate(visitor);
+ }
+
+ /**
+ * Finds the max severity across problem markers for a subtree of resources.
+ */
+ private int visitorFindMaxSeverity(IPath path, final String type, final boolean includeSubtypes) {
+ class MaxSeverityVisitor implements IElementContentVisitor {
+ int max = -1;
+
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ // bail if an earlier sibling already hit the max
+ if (max >= IMarker.SEVERITY_ERROR) {
+ return false;
+ }
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (info == null)
+ return false;
+ MarkerSet markers = info.getMarkers(false);
+
+ //add the matching markers for this resource
+ if (markers != null) {
+ max = Math.max(max, basicFindMaxSeverity(markers, type, includeSubtypes));
+ }
+ return max < IMarker.SEVERITY_ERROR;
+ }
+ }
+ MaxSeverityVisitor visitor = new MaxSeverityVisitor();
+ new ElementTreeIterator(workspace.getElementTree(), path).iterate(visitor);
+ return visitor.max;
+ }
+
+ /**
+ * Adds the markers for a subtree of resources to the list.
+ */
+ private void visitorRemoveMarkers(IPath path, final String type, final boolean includeSubtypes) {
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (info == null)
+ return false;
+ basicRemoveMarkers(info, requestor, type, includeSubtypes);
+ return true;
+ }
+ };
+ new ElementTreeIterator(workspace.getElementTree(), path).iterate(visitor);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is used to read markers from disk. Subclasses implement
+ * version specific reading code.
+ */
+public class MarkerReader {
+ protected Workspace workspace;
+
+ public MarkerReader(Workspace workspace) {
+ super();
+ this.workspace = workspace;
+ }
+
+ /**
+ * Returns the appropriate reader for the given version.
+ */
+ protected MarkerReader getReader(int formatVersion) throws IOException {
+ switch (formatVersion) {
+ case 1 :
+ return new MarkerReader_1(workspace);
+ case 2 :
+ return new MarkerReader_2(workspace);
+ case 3 :
+ return new MarkerReader_3(workspace);
+ default :
+ throw new IOException(NLS.bind(Messages.resources_format, new Integer(formatVersion)));
+ }
+ }
+
+ public void read(DataInputStream input, boolean generateDeltas) throws IOException, CoreException {
+ int formatVersion = readVersionNumber(input);
+ MarkerReader reader = getReader(formatVersion);
+ reader.read(input, generateDeltas);
+ }
+
+ protected static int readVersionNumber(DataInputStream input) throws IOException {
+ return input.readInt();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_1.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+/**
+ * This class is used to read markers from disk. This is for version 1.
+ */
+public class MarkerReader_1 extends MarkerReader {
+
+ // type constants
+ public static final int INDEX = 1;
+ public static final int QNAME = 2;
+
+ // marker attribute types
+ public static final int ATTRIBUTE_NULL = -1;
+ public static final int ATTRIBUTE_BOOLEAN = 0;
+ public static final int ATTRIBUTE_INTEGER = 1;
+ public static final int ATTRIBUTE_STRING = 2;
+
+ public MarkerReader_1(Workspace workspace) {
+ super(workspace);
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE+
+ * VERSION_ID ->
+ * RESOURCE -> RESOURCE_PATH MARKERS_SIZE MARKER*
+ * RESOURCE_PATH -> String
+ * MARKERS_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE*
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> int int
+ * QNAME -> int String
+ * ATTRIBUTES_SIZE -> int
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> INTEGER_VALUE | BOOLEAN_VALUE | STRING_VALUE | NULL_VALUE
+ * INTEGER_VALUE -> int int
+ * BOOLEAN_VALUE -> int boolean
+ * STRING_VALUE -> int String
+ * NULL_VALUE -> int
+ */
+ public void read(DataInputStream input, boolean generateDeltas) throws IOException, CoreException {
+ try {
+ List readTypes = new ArrayList(5);
+ while (true) {
+ IPath path = new Path(input.readUTF());
+ int markersSize = input.readInt();
+ MarkerSet markers = new MarkerSet(markersSize);
+ for (int i = 0; i < markersSize; i++)
+ markers.add(readMarkerInfo(input, readTypes));
+ // if the resource doesn't exist then return. ensure we do this after
+ // reading the markers from the file so we don't get into an
+ // inconsistent state.
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ continue;
+ info.setMarkers(markers);
+ if (generateDeltas) {
+ Resource resource = workspace.newResource(path, info.getType());
+ // Iterate over all elements and add not null ones. This saves us from copying
+ // and shrinking the array.
+ IMarkerSetElement[] infos = markers.elements;
+ ArrayList deltas = new ArrayList(infos.length);
+ for (int i = 0; i < infos.length; i++)
+ if (infos[i] != null)
+ deltas.add(new MarkerDelta(IResourceDelta.ADDED, resource, (MarkerInfo) infos[i]));
+ workspace.getMarkerManager().changedMarkers(resource, (IMarkerSetElement[]) deltas.toArray(new IMarkerSetElement[deltas.size()]));
+ }
+ }
+ } catch (EOFException e) {
+ // ignore end of file
+ }
+ }
+
+ private Map readAttributes(DataInputStream input) throws IOException {
+ int attributesSize = input.readInt();
+ if (attributesSize == 0)
+ return null;
+ Map result = new MarkerAttributeMap(attributesSize);
+ for (int j = 0; j < attributesSize; j++) {
+ String key = input.readUTF();
+ int type = input.readInt();
+ Object value = null;
+ switch (type) {
+ case ATTRIBUTE_INTEGER :
+ value = new Integer(input.readInt());
+ break;
+ case ATTRIBUTE_BOOLEAN :
+ value = input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ break;
+ case ATTRIBUTE_STRING :
+ value = input.readUTF();
+ break;
+ case ATTRIBUTE_NULL :
+ // do nothing
+ break;
+ }
+ if (value != null)
+ result.put(key, value);
+ }
+ return result.isEmpty() ? null : result;
+ }
+
+ private MarkerInfo readMarkerInfo(DataInputStream input, List readTypes) throws IOException, CoreException {
+ MarkerInfo info = new MarkerInfo();
+ info.setId(input.readLong());
+ int constant = input.readInt();
+ switch (constant) {
+ case QNAME :
+ String type = input.readUTF();
+ info.setType(type);
+ readTypes.add(type);
+ break;
+ case INDEX :
+ info.setType((String) readTypes.get(input.readInt()));
+ break;
+ default :
+ //if we get here the marker file is corrupt
+ String msg = Messages.resources_readMarkers;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ info.internalSetAttributes(readAttributes(input));
+ return info;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+/**
+ * This class is used to read markers from disk. This is for version 2. Here
+ * is the file format:
+ */
+public class MarkerReader_2 extends MarkerReader {
+
+ // type constants
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ // marker attribute types
+ public static final byte ATTRIBUTE_NULL = 0;
+ public static final byte ATTRIBUTE_BOOLEAN = 1;
+ public static final byte ATTRIBUTE_INTEGER = 2;
+ public static final byte ATTRIBUTE_STRING = 3;
+
+ public MarkerReader_2(Workspace workspace) {
+ super(workspace);
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE+
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH MARKERS_SIZE MARKER+
+ * RESOURCE_PATH -> String
+ * MARKERS_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE*
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * ATTRIBUTES_SIZE -> short
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> INTEGER_VALUE | BOOLEAN_VALUE | STRING_VALUE | NULL_VALUE
+ * INTEGER_VALUE -> byte int
+ * BOOLEAN_VALUE -> byte boolean
+ * STRING_VALUE -> byte String
+ * NULL_VALUE -> byte
+ */
+ public void read(DataInputStream input, boolean generateDeltas) throws IOException, CoreException {
+ try {
+ List readTypes = new ArrayList(5);
+ while (true) {
+ IPath path = new Path(input.readUTF());
+ int markersSize = input.readInt();
+ MarkerSet markers = new MarkerSet(markersSize);
+ for (int i = 0; i < markersSize; i++)
+ markers.add(readMarkerInfo(input, readTypes));
+ // if the resource doesn't exist then return. ensure we do this after
+ // reading the markers from the file so we don't get into an
+ // inconsistent state.
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ continue;
+ info.setMarkers(markers);
+ if (generateDeltas) {
+ // Iterate over all elements and add not null ones. This saves us from copying
+ // and shrinking the array.
+ Resource resource = workspace.newResource(path, info.getType());
+ IMarkerSetElement[] infos = markers.elements;
+ ArrayList deltas = new ArrayList(infos.length);
+ for (int i = 0; i < infos.length; i++)
+ if (infos[i] != null)
+ deltas.add(new MarkerDelta(IResourceDelta.ADDED, resource, (MarkerInfo) infos[i]));
+ workspace.getMarkerManager().changedMarkers(resource, (IMarkerSetElement[]) deltas.toArray(new IMarkerSetElement[deltas.size()]));
+ }
+ }
+ } catch (EOFException e) {
+ // ignore end of file
+ }
+ }
+
+ private Map readAttributes(DataInputStream input) throws IOException {
+ int attributesSize = input.readShort();
+ if (attributesSize == 0)
+ return null;
+ Map result = new MarkerAttributeMap(attributesSize);
+ for (int j = 0; j < attributesSize; j++) {
+ String key = input.readUTF();
+ byte type = input.readByte();
+ Object value = null;
+ switch (type) {
+ case ATTRIBUTE_INTEGER :
+ value = new Integer(input.readInt());
+ break;
+ case ATTRIBUTE_BOOLEAN :
+ value = input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ break;
+ case ATTRIBUTE_STRING :
+ value = input.readUTF();
+ break;
+ case ATTRIBUTE_NULL :
+ // do nothing
+ break;
+ }
+ if (value != null)
+ result.put(key, value);
+ }
+ return result.isEmpty() ? null : result;
+ }
+
+ private MarkerInfo readMarkerInfo(DataInputStream input, List readTypes) throws IOException, CoreException {
+ MarkerInfo info = new MarkerInfo();
+ info.setId(input.readLong());
+ byte constant = input.readByte();
+ switch (constant) {
+ case QNAME :
+ String type = input.readUTF();
+ info.setType(type);
+ readTypes.add(type);
+ break;
+ case INDEX :
+ info.setType((String) readTypes.get(input.readInt()));
+ break;
+ default :
+ //if we get here the marker file is corrupt
+ String msg = Messages.resources_readMarkers;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ info.internalSetAttributes(readAttributes(input));
+ return info;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_3.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+/**
+ * This class is used to read markers from disk. This is for version 2. Here
+ * is the file format:
+ */
+public class MarkerReader_3 extends MarkerReader {
+
+ // type constants
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ // marker attribute types
+ public static final byte ATTRIBUTE_NULL = 0;
+ public static final byte ATTRIBUTE_BOOLEAN = 1;
+ public static final byte ATTRIBUTE_INTEGER = 2;
+ public static final byte ATTRIBUTE_STRING = 3;
+
+ public MarkerReader_3(Workspace workspace) {
+ super(workspace);
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE+
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH MARKERS_SIZE MARKER+
+ * RESOURCE_PATH -> String
+ * MARKERS_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * ATTRIBUTES_SIZE -> short
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> INTEGER_VALUE | BOOLEAN_VALUE | STRING_VALUE | NULL_VALUE
+ * INTEGER_VALUE -> byte int
+ * BOOLEAN_VALUE -> byte boolean
+ * STRING_VALUE -> byte String
+ * NULL_VALUE -> byte
+ * CREATION_TIME -> long
+ */
+ public void read(DataInputStream input, boolean generateDeltas) throws IOException, CoreException {
+ try {
+ List readTypes = new ArrayList(5);
+ while (true) {
+ IPath path = new Path(input.readUTF());
+ int markersSize = input.readInt();
+ MarkerSet markers = new MarkerSet(markersSize);
+ for (int i = 0; i < markersSize; i++)
+ markers.add(readMarkerInfo(input, readTypes));
+ // if the resource doesn't exist then return. ensure we do this after
+ // reading the markers from the file so we don't get into an
+ // inconsistent state.
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ continue;
+ info.setMarkers(markers);
+ if (generateDeltas) {
+ // Iterate over all elements and add not null ones. This saves us from copying
+ // and shrinking the array.
+ Resource resource = workspace.newResource(path, info.getType());
+ IMarkerSetElement[] infos = markers.elements;
+ ArrayList deltas = new ArrayList(infos.length);
+ for (int i = 0; i < infos.length; i++)
+ if (infos[i] != null)
+ deltas.add(new MarkerDelta(IResourceDelta.ADDED, resource, (MarkerInfo) infos[i]));
+ workspace.getMarkerManager().changedMarkers(resource, (IMarkerSetElement[]) deltas.toArray(new IMarkerSetElement[deltas.size()]));
+ }
+ }
+ } catch (EOFException e) {
+ // ignore end of file
+ }
+ }
+
+ private Map readAttributes(DataInputStream input) throws IOException {
+ int attributesSize = input.readShort();
+ if (attributesSize == 0)
+ return null;
+ Map result = new MarkerAttributeMap(attributesSize);
+ for (int j = 0; j < attributesSize; j++) {
+ String key = input.readUTF();
+ byte type = input.readByte();
+ Object value = null;
+ switch (type) {
+ case ATTRIBUTE_INTEGER :
+ int intValue = input.readInt();
+ //canonicalize well known values (marker severity, task priority)
+ switch (intValue) {
+ case 0:
+ value = MarkerInfo.INTEGER_ZERO;
+ break;
+ case 1:
+ value = MarkerInfo.INTEGER_ONE;
+ break;
+ case 2:
+ value = MarkerInfo.INTEGER_TWO;
+ break;
+ default:
+ value = new Integer(intValue);
+ }
+ break;
+ case ATTRIBUTE_BOOLEAN :
+ value = input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ break;
+ case ATTRIBUTE_STRING :
+ value = input.readUTF();
+ break;
+ case ATTRIBUTE_NULL :
+ // do nothing
+ break;
+ }
+ if (value != null)
+ result.put(key, value);
+ }
+ return result.isEmpty() ? null : result;
+ }
+
+ private MarkerInfo readMarkerInfo(DataInputStream input, List readTypes) throws IOException, CoreException {
+ MarkerInfo info = new MarkerInfo();
+ info.setId(input.readLong());
+ byte constant = input.readByte();
+ switch (constant) {
+ case QNAME :
+ String type = input.readUTF();
+ info.setType(type);
+ readTypes.add(type);
+ break;
+ case INDEX :
+ info.setType((String) readTypes.get(input.readInt()));
+ break;
+ default :
+ //if we get here the marker file is corrupt
+ String msg = Messages.resources_readMarkers;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ info.internalSetAttributes(readAttributes(input));
+ info.setCreationTime(input.readLong());
+ return info;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSet.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.IStringPoolParticipant;
+import org.eclipse.core.internal.utils.StringPool;
+
+public class MarkerSet implements Cloneable, IStringPoolParticipant {
+ protected static final int MINIMUM_SIZE = 5;
+ protected int elementCount = 0;
+ protected IMarkerSetElement[] elements;
+
+ public MarkerSet() {
+ this(MINIMUM_SIZE);
+ }
+
+ public MarkerSet(int capacity) {
+ super();
+ this.elements = new IMarkerSetElement[Math.max(MINIMUM_SIZE, capacity * 2)];
+ }
+
+ public void add(IMarkerSetElement element) {
+ if (element == null)
+ return;
+ int hash = hashFor(element.getId()) % elements.length;
+
+ // search for an empty slot at the end of the array
+ for (int i = hash; i < elements.length; i++) {
+ if (elements[i] == null) {
+ elements[i] = element;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return;
+ }
+ }
+
+ // search for an empty slot at the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ if (elements[i] == null) {
+ elements[i] = element;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return;
+ }
+ }
+
+ // if we didn't find a free slot, then try again with the expanded set
+ expand();
+ add(element);
+ }
+
+ public void addAll(IMarkerSetElement[] toAdd) {
+ for (int i = 0; i < toAdd.length; i++)
+ add(toAdd[i]);
+ }
+
+ protected Object clone() {
+ try {
+ MarkerSet copy = (MarkerSet) super.clone();
+ //copy the attribute array
+ copy.elements = (IMarkerSetElement[]) elements.clone();
+ return copy;
+ } catch (CloneNotSupportedException e) {
+ //cannot happen because this class implements Cloneable
+ return null;
+ }
+ }
+
+ public boolean contains(long id) {
+ return get(id) != null;
+ }
+
+ public IMarkerSetElement[] elements() {
+ IMarkerSetElement[] result = new IMarkerSetElement[elementCount];
+ int j = 0;
+ for (int i = 0; i < elements.length; i++) {
+ IMarkerSetElement element = elements[i];
+ if (element != null)
+ result[j++] = element;
+ }
+ return result;
+ }
+
+ /**
+ * The array isn't large enough so double its size and rehash
+ * all its current values.
+ */
+ protected void expand() {
+ IMarkerSetElement[] array = new IMarkerSetElement[elements.length * 2];
+ int maxArrayIndex = array.length - 1;
+ for (int i = 0; i < elements.length; i++) {
+ IMarkerSetElement element = elements[i];
+ if (element != null) {
+ int hash = hashFor(element.getId()) % array.length;
+ while (array[hash] != null) {
+ hash++;
+ if (hash > maxArrayIndex)
+ hash = 0;
+ }
+ array[hash] = element;
+ }
+ }
+ elements = array;
+ }
+
+ /**
+ * Returns the set element with the given id, or null
+ * if not found.
+ */
+ public IMarkerSetElement get(long id) {
+ if (elementCount == 0)
+ return null;
+ int hash = hashFor(id) % elements.length;
+
+ // search the last half of the array
+ for (int i = hash; i < elements.length; i++) {
+ IMarkerSetElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.getId() == id)
+ return element;
+ }
+
+ // search the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ IMarkerSetElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.getId() == id)
+ return element;
+ }
+
+ // marker info not found so return null
+ return null;
+ }
+
+ private int hashFor(long id) {
+ return Math.abs((int) id);
+ }
+
+ public boolean isEmpty() {
+ return elementCount == 0;
+ }
+
+ /**
+ * The element at the given index has been removed so move
+ * elements to keep the set properly hashed.
+ */
+ protected void rehashTo(int anIndex) {
+
+ int target = anIndex;
+ int index = anIndex + 1;
+ if (index >= elements.length)
+ index = 0;
+ IMarkerSetElement element = elements[index];
+ while (element != null) {
+ int hashIndex = hashFor(element.getId()) % elements.length;
+ boolean match;
+ if (index < target)
+ match = !(hashIndex > target || hashIndex <= index);
+ else
+ match = !(hashIndex > target && hashIndex <= index);
+ if (match) {
+ elements[target] = element;
+ target = index;
+ }
+ index++;
+ if (index >= elements.length)
+ index = 0;
+ element = elements[index];
+ }
+ elements[target] = null;
+ }
+
+ public void remove(long id) {
+ int hash = hashFor(id) % elements.length;
+
+ for (int i = hash; i < elements.length; i++) {
+ IMarkerSetElement element = elements[i];
+ if (element == null)
+ return;
+ if (element.getId() == id) {
+ rehashTo(i);
+ elementCount--;
+ }
+ }
+
+ for (int i = 0; i < hash - 1; i++) {
+ IMarkerSetElement element = elements[i];
+ if (element == null)
+ return;
+ if (element.getId() == id) {
+ rehashTo(i);
+ elementCount--;
+ }
+ }
+ }
+
+ public void remove(IMarkerSetElement element) {
+ remove(element.getId());
+ }
+
+ public void removeAll(IMarkerSetElement[] toRemove) {
+ for (int i = 0; i < toRemove.length; i++)
+ remove(toRemove[i]);
+ }
+
+ private boolean shouldGrow() {
+ return elementCount > elements.length * 0.75;
+ }
+
+ public int size() {
+ return elementCount;
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool set) {
+ //copy elements for thread safety
+ Object[] array = elements;
+ if (array == null)
+ return;
+ for (int i = 0; i < array.length; i++) {
+ Object o = array[i];
+ if (o instanceof String)
+ array[i] = set.add((String) o);
+ if (o instanceof IStringPoolParticipant)
+ ((IStringPoolParticipant) o).shareStrings(set);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.osgi.util.NLS;
+
+public class MarkerSnapshotReader {
+ protected Workspace workspace;
+
+ public MarkerSnapshotReader(Workspace workspace) {
+ super();
+ this.workspace = workspace;
+ }
+
+ /**
+ * Returns the appropriate reader for the given version.
+ */
+ protected MarkerSnapshotReader getReader(int formatVersion) throws IOException {
+ switch (formatVersion) {
+ case 1 :
+ return new MarkerSnapshotReader_1(workspace);
+ case 2 :
+ return new MarkerSnapshotReader_2(workspace);
+ default :
+ throw new IOException(NLS.bind(Messages.resources_format, new Integer(formatVersion)));
+ }
+ }
+
+ public void read(DataInputStream input) throws IOException, CoreException {
+ int formatVersion = readVersionNumber(input);
+ MarkerSnapshotReader reader = getReader(formatVersion);
+ reader.read(input);
+ }
+
+ protected static int readVersionNumber(DataInputStream input) throws IOException {
+ return input.readInt();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader_1.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+//
+/**
+ * Snapshot the markers for the specified resource to the given output stream.
+ */
+public class MarkerSnapshotReader_1 extends MarkerSnapshotReader {
+
+ // type constants
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ // marker attribute types
+ public static final byte ATTRIBUTE_NULL = 0;
+ public static final byte ATTRIBUTE_BOOLEAN = 1;
+ public static final byte ATTRIBUTE_INTEGER = 2;
+ public static final byte ATTRIBUTE_STRING = 3;
+
+ public MarkerSnapshotReader_1(Workspace workspace) {
+ super(workspace);
+ }
+
+ /**
+ * SNAP_FILE -> [VERSION_ID RESOURCE]*
+ * VERSION_ID -> int (used for backwards compatibiliy)
+ * RESOURCE -> RESOURCE_PATH MARKER_SIZE MARKER+
+ * RESOURCE_PATH -> String
+ * MARKER_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE*
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * ATTRIBUTES_SIZE -> short
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> BOOLEAN_VALUE | INTEGER_VALUE | STRING_VALUE | NULL_VALUE
+ * BOOLEAN_VALUE -> byte boolean
+ * INTEGER_VALUE -> byte int
+ * STRING_VALUE -> byte String
+ * NULL_VALUE -> byte
+ */
+ public void read(DataInputStream input) throws IOException, CoreException {
+ IPath path = new Path(input.readUTF());
+ int markersSize = input.readInt();
+ MarkerSet markers = new MarkerSet(markersSize);
+ ArrayList readTypes = new ArrayList();
+ for (int i = 0; i < markersSize; i++)
+ markers.add(readMarkerInfo(input, readTypes));
+ // we've read all the markers from the file for this snap. if the resource
+ // doesn't exist in the workspace then consider this a delete and return
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ return;
+ info.setMarkers(markers);
+ info.clear(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ }
+
+ private Map readAttributes(DataInputStream input) throws IOException {
+ short attributesSize = input.readShort();
+ if (attributesSize == 0)
+ return null;
+ Map result = new MarkerAttributeMap(attributesSize);
+ for (int j = 0; j < attributesSize; j++) {
+ String key = input.readUTF();
+ byte type = input.readByte();
+ Object value = null;
+ switch (type) {
+ case ATTRIBUTE_INTEGER :
+ value = new Integer(input.readInt());
+ break;
+ case ATTRIBUTE_BOOLEAN :
+ value = input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ break;
+ case ATTRIBUTE_STRING :
+ value = input.readUTF();
+ break;
+ case ATTRIBUTE_NULL :
+ // do nothing
+ break;
+ }
+ if (value != null)
+ result.put(key, value);
+ }
+ return result.isEmpty() ? null : result;
+ }
+
+ private MarkerInfo readMarkerInfo(DataInputStream input, List readTypes) throws IOException, CoreException {
+ MarkerInfo info = new MarkerInfo();
+ info.setId(input.readLong());
+ byte constant = input.readByte();
+ switch (constant) {
+ case QNAME :
+ String type = input.readUTF();
+ info.setType(type);
+ readTypes.add(type);
+ break;
+ case INDEX :
+ info.setType((String) readTypes.get(input.readInt()));
+ break;
+ default :
+ //if we get here the marker file is corrupt
+ String msg = Messages.resources_readMarkers;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ info.internalSetAttributes(readAttributes(input));
+ return info;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader_2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+//
+/**
+ * Snapshot the markers for the specified resource to the given output stream.
+ */
+public class MarkerSnapshotReader_2 extends MarkerSnapshotReader {
+
+ // type constants
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ // marker attribute types
+ public static final byte ATTRIBUTE_NULL = 0;
+ public static final byte ATTRIBUTE_BOOLEAN = 1;
+ public static final byte ATTRIBUTE_INTEGER = 2;
+ public static final byte ATTRIBUTE_STRING = 3;
+
+ public MarkerSnapshotReader_2(Workspace workspace) {
+ super(workspace);
+ }
+
+ /**
+ * SNAP_FILE -> [VERSION_ID RESOURCE]*
+ * VERSION_ID -> int (used for backwards compatibiliy)
+ * RESOURCE -> RESOURCE_PATH MARKER_SIZE MARKER+
+ * RESOURCE_PATH -> String
+ * MARKER_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * ATTRIBUTES_SIZE -> short
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> BOOLEAN_VALUE | INTEGER_VALUE | STRING_VALUE | NULL_VALUE
+ * BOOLEAN_VALUE -> byte boolean
+ * INTEGER_VALUE -> byte int
+ * STRING_VALUE -> byte String
+ * NULL_VALUE -> byte
+ * CREATION_TIME -> long
+ */
+ public void read(DataInputStream input) throws IOException, CoreException {
+ IPath path = new Path(input.readUTF());
+ int markersSize = input.readInt();
+ MarkerSet markers = new MarkerSet(markersSize);
+ ArrayList readTypes = new ArrayList();
+ for (int i = 0; i < markersSize; i++)
+ markers.add(readMarkerInfo(input, readTypes));
+ // we've read all the markers from the file for this snap. if the resource
+ // doesn't exist in the workspace then consider this a delete and return
+ ResourceInfo info = workspace.getResourceInfo(path, false, false);
+ if (info == null)
+ return;
+ info.setMarkers(markers);
+ info.clear(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ }
+
+ private Map readAttributes(DataInputStream input) throws IOException {
+ short attributesSize = input.readShort();
+ if (attributesSize == 0)
+ return null;
+ Map result = new MarkerAttributeMap(attributesSize);
+ for (int j = 0; j < attributesSize; j++) {
+ String key = input.readUTF();
+ byte type = input.readByte();
+ Object value = null;
+ switch (type) {
+ case ATTRIBUTE_INTEGER :
+ int intValue = input.readInt();
+ switch (intValue) {
+ case 0:
+ value = MarkerInfo.INTEGER_ZERO;
+ break;
+ case 1:
+ value = MarkerInfo.INTEGER_ONE;
+ break;
+ case 2:
+ value = MarkerInfo.INTEGER_TWO;
+ break;
+ default:
+ value = new Integer(intValue);
+ }
+ break;
+ case ATTRIBUTE_BOOLEAN :
+ value = input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ break;
+ case ATTRIBUTE_STRING :
+ value = input.readUTF();
+ break;
+ case ATTRIBUTE_NULL :
+ // do nothing
+ break;
+ }
+ if (value != null)
+ result.put(key, value);
+ }
+ return result.isEmpty() ? null : result;
+ }
+
+ private MarkerInfo readMarkerInfo(DataInputStream input, List readTypes) throws IOException, CoreException {
+ MarkerInfo info = new MarkerInfo();
+ info.setId(input.readLong());
+ byte constant = input.readByte();
+ switch (constant) {
+ case QNAME :
+ String type = input.readUTF();
+ info.setType(type);
+ readTypes.add(type);
+ break;
+ case INDEX :
+ info.setType((String) readTypes.get(input.readInt()));
+ break;
+ default :
+ //if we get here the marker file is corrupt
+ String msg = Messages.resources_readMarkers;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ info.internalSetAttributes(readAttributes(input));
+ info.setCreationTime(input.readLong());
+ return info;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerTypeDefinitionCache.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+
+public class MarkerTypeDefinitionCache {
+ static class MarkerTypeDefinition {
+ boolean isPersistent = false;
+ Set superTypes;
+
+ MarkerTypeDefinition(IExtension ext) {
+ IConfigurationElement[] elements = ext.getConfigurationElements();
+ for (int i = 0; i < elements.length; i++) {
+ IConfigurationElement element = elements[i];
+ // supertype
+ final String elementName = element.getName();
+ if (elementName.equalsIgnoreCase("super")) { //$NON-NLS-1$
+ String aType = element.getAttribute("type"); //$NON-NLS-1$
+ if (aType != null) {
+ if (superTypes == null)
+ superTypes = new HashSet(8);
+ //note that all marker type names will be in the intern table
+ //already because there is invariably a constant to describe
+ //the type name
+ superTypes.add(aType.intern());
+ }
+ }
+ // persistent
+ if (elementName.equalsIgnoreCase("persistent")) { //$NON-NLS-1$
+ String bool = element.getAttribute("value"); //$NON-NLS-1$
+ if (bool != null)
+ this.isPersistent = Boolean.valueOf(bool).booleanValue();
+ }
+ // XXX: legacy code for support of <transient> tag. remove later.
+ if (elementName.equalsIgnoreCase("transient")) { //$NON-NLS-1$
+ String bool = element.getAttribute("value"); //$NON-NLS-1$
+ if (bool != null)
+ this.isPersistent = !Boolean.valueOf(bool).booleanValue();
+ }
+ }
+ }
+ }
+
+ /**
+ * The marker type definitions. Maps String (markerId) -> MarkerTypeDefinition
+ */
+ protected HashMap definitions;
+
+ /** Constructs a new type cache.
+ */
+ public MarkerTypeDefinitionCache() {
+ loadDefinitions();
+ HashSet toCompute = new HashSet(definitions.keySet());
+ for (Iterator i = definitions.keySet().iterator(); i.hasNext();) {
+ String markerId = (String) i.next();
+ if (toCompute.contains(markerId))
+ computeSuperTypes(markerId, toCompute);
+ }
+ }
+
+ /**
+ * Computes the transitive set of super types of the given marker type.
+ * @param markerId The type to compute super types for
+ * @param toCompute The set of types that have not yet had their
+ * supertypes computed.
+ * @return The transitive set of super types for this marker, or null
+ * if this marker is not defined or has no super types.
+ */
+ private Set computeSuperTypes(String markerId, Set toCompute) {
+ MarkerTypeDefinition def = (MarkerTypeDefinition) definitions.get(markerId);
+ if (def == null || def.superTypes == null) {
+ //nothing to do if there are no supertypes
+ toCompute.remove(markerId);
+ return null;
+ }
+ Set transitiveSuperTypes = new HashSet(def.superTypes);
+ for (Iterator it = def.superTypes.iterator(); it.hasNext();) {
+ String superId = (String) it.next();
+ Set toAdd = null;
+ if (toCompute.contains(superId)) {
+ //this type's super types have not been compute yet. Do recursive call
+ toAdd = computeSuperTypes(superId, toCompute);
+ } else {
+ // we have already computed this super type's super types (or it doesn't exist)
+ MarkerTypeDefinition parentDef = (MarkerTypeDefinition) definitions.get(superId);
+ if (parentDef != null)
+ toAdd = parentDef.superTypes;
+ }
+ if (toAdd != null)
+ transitiveSuperTypes.addAll(toAdd);
+ }
+ def.superTypes = transitiveSuperTypes;
+ toCompute.remove(markerId);
+ return transitiveSuperTypes;
+ }
+
+ /**
+ * Returns true if the given marker type is defined to be persistent.
+ */
+ public boolean isPersistent(String type) {
+ MarkerTypeDefinition def = (MarkerTypeDefinition) definitions.get(type);
+ return def != null && def.isPersistent;
+ }
+
+ /**
+ * Returns true if the given target class has the specified type as a super type.
+ */
+ public boolean isSubtype(String type, String superType) {
+ //types are considered super types of themselves
+ if (type.equals(superType))
+ return true;
+ MarkerTypeDefinition def = (MarkerTypeDefinition) definitions.get(type);
+ return def != null && def.superTypes != null && def.superTypes.contains(superType);
+ }
+
+ private void loadDefinitions() {
+ IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_MARKERS);
+ IExtension[] types = point.getExtensions();
+ definitions = new HashMap(types.length);
+ for (int i = 0; i < types.length; i++) {
+ String markerId = types[i].getUniqueIdentifier();
+ if (markerId != null)
+ definitions.put(markerId.intern(), new MarkerTypeDefinition(types[i]));
+ else
+ Policy.log(IStatus.WARNING, "Missing marker id from plugin: " + types[i].getContributor().getName(), null); //$NON-NLS-1$
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerWriter.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.*;
+import org.eclipse.core.internal.watson.IPathRequestor;
+
+//
+public class MarkerWriter {
+
+ protected MarkerManager manager;
+
+ // version numbers
+ public static final int MARKERS_SAVE_VERSION = 3;
+ public static final int MARKERS_SNAP_VERSION = 2;
+
+ // type constants
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ // marker attribute types
+ public static final byte ATTRIBUTE_NULL = 0;
+ public static final byte ATTRIBUTE_BOOLEAN = 1;
+ public static final byte ATTRIBUTE_INTEGER = 2;
+ public static final byte ATTRIBUTE_STRING = 3;
+
+ public MarkerWriter(MarkerManager manager) {
+ super();
+ this.manager = manager;
+ }
+
+ /**
+ * Returns an Object array of length 2. The first element is an Integer which is the number
+ * of persistent markers found. The second element is an array of boolean values, with a
+ * value of true meaning that the marker at that index is to be persisted.
+ */
+ private Object[] filterMarkers(IMarkerSetElement[] markers) {
+ Object[] result = new Object[2];
+ boolean[] isPersistent = new boolean[markers.length];
+ int count = 0;
+ for (int i = 0; i < markers.length; i++) {
+ MarkerInfo info = (MarkerInfo) markers[i];
+ if (manager.isPersistent(info)) {
+ isPersistent[i] = true;
+ count++;
+ }
+ }
+ result[0] = new Integer(count);
+ result[1] = isPersistent;
+ return result;
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE+
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH MARKERS_SIZE MARKER+
+ * RESOURCE_PATH -> String
+ * MARKERS_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * ATTRIBUTES_SIZE -> short
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> INTEGER_VALUE | BOOLEAN_VALUE | STRING_VALUE | NULL_VALUE
+ * INTEGER_VALUE -> byte int
+ * BOOLEAN_VALUE -> byte boolean
+ * STRING_VALUE -> byte String
+ * NULL_VALUE -> byte
+ * CREATION_TIME -> long
+ *
+ */
+ public void save(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List writtenTypes) throws IOException {
+ // phantom resources don't have markers
+ if (info.isSet(ICoreConstants.M_PHANTOM))
+ return;
+ MarkerSet markers = info.getMarkers(false);
+ if (markers == null)
+ return;
+ IMarkerSetElement[] elements = markers.elements();
+ // filter out the markers...determine if there are any persistent ones
+ Object[] result = filterMarkers(elements);
+ int count = ((Integer) result[0]).intValue();
+ if (count == 0)
+ return;
+ // if this is the first set of markers that we have written, then
+ // write the version id for the file.
+ if (output.size() == 0)
+ output.writeInt(MARKERS_SAVE_VERSION);
+ boolean[] isPersistent = (boolean[]) result[1];
+ output.writeUTF(requestor.requestPath().toString());
+ output.writeInt(count);
+ for (int i = 0; i < elements.length; i++)
+ if (isPersistent[i])
+ write((MarkerInfo) elements[i], output, writtenTypes);
+ }
+
+ /**
+ * Snapshot the markers for the specified resource to the given output stream.
+ *
+ * SNAP_FILE -> [VERSION_ID RESOURCE]*
+ * VERSION_ID -> int (used for backwards compatibiliy)
+ * RESOURCE -> RESOURCE_PATH MARKER_SIZE MARKER+
+ * RESOURCE_PATH -> String
+ * MARKER_SIZE -> int
+ * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME
+ * MARKER_ID -> long
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * ATTRIBUTES_SIZE -> short
+ * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
+ * ATTRIBUTE_KEY -> String
+ * ATTRIBUTE_VALUE -> BOOLEAN_VALUE | INTEGER_VALUE | STRING_VALUE | NULL_VALUE
+ * BOOLEAN_VALUE -> byte boolean
+ * INTEGER_VALUE -> byte int
+ * STRING_VALUE -> byte String
+ * NULL_VALUE -> byte
+ * CREATION_TIME -> long
+ */
+ public void snap(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException {
+ // phantom resources don't have markers
+ if (info.isSet(ICoreConstants.M_PHANTOM))
+ return;
+ if (!info.isSet(ICoreConstants.M_MARKERS_SNAP_DIRTY))
+ return;
+ MarkerSet markers = info.getMarkers(false);
+ if (markers == null)
+ return;
+ IMarkerSetElement[] elements = markers.elements();
+ // filter out the markers...determine if there are any persistent ones
+ Object[] result = filterMarkers(elements);
+ int count = ((Integer) result[0]).intValue();
+ // write the version id for the snapshot.
+ output.writeInt(MARKERS_SNAP_VERSION);
+ boolean[] isPersistent = (boolean[]) result[1];
+ output.writeUTF(requestor.requestPath().toString());
+ // always write out the count...even if its zero. this will help
+ // use pick up marker deletions from our snapshot.
+ output.writeInt(count);
+ List writtenTypes = new ArrayList();
+ for (int i = 0; i < elements.length; i++)
+ if (isPersistent[i])
+ write((MarkerInfo) elements[i], output, writtenTypes);
+ info.clear(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ }
+
+ /*
+ * Write out the given marker attributes to the given output stream.
+ */
+ private void write(Map attributes, DataOutputStream output) throws IOException {
+ output.writeShort(attributes.size());
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ output.writeUTF(key);
+ Object value = attributes.get(key);
+ if (value instanceof Integer) {
+ output.writeByte(ATTRIBUTE_INTEGER);
+ output.writeInt(((Integer) value).intValue());
+ continue;
+ }
+ if (value instanceof Boolean) {
+ output.writeByte(ATTRIBUTE_BOOLEAN);
+ output.writeBoolean(((Boolean) value).booleanValue());
+ continue;
+ }
+ if (value instanceof String) {
+ output.writeByte(ATTRIBUTE_STRING);
+ output.writeUTF((String) value);
+ continue;
+ }
+ // otherwise we came across an attribute of an unknown type
+ // so just write out null since we don't know how to marshal it.
+ output.writeByte(ATTRIBUTE_NULL);
+ }
+ }
+
+ private void write(MarkerInfo info, DataOutputStream output, List writtenTypes) throws IOException {
+ output.writeLong(info.getId());
+ // if we have already written the type once, then write an integer
+ // constant to represent it instead to remove duplication
+ String type = info.getType();
+ int index = writtenTypes.indexOf(type);
+ if (index == -1) {
+ output.writeByte(QNAME);
+ output.writeUTF(type);
+ writtenTypes.add(type);
+ } else {
+ output.writeByte(INDEX);
+ output.writeInt(index);
+ }
+
+ // write out the size of the attribute table and
+ // then each attribute.
+ if (info.getAttributes(false) == null) {
+ output.writeShort(0);
+ } else
+ write(info.getAttributes(false), output);
+
+ // write out the creation time
+ output.writeLong(info.getCreationTime());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ModelObject.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+public abstract class ModelObject implements Cloneable {
+ protected String name;
+
+ public ModelObject() {
+ super();
+ }
+
+ public ModelObject(String name) {
+ setName(name);
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null; // won't happen
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String value) {
+ name = value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ModelObjectWriter.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.internal.events.BuildCommand;
+import org.eclipse.core.internal.localstore.SafeFileOutputStream;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.IPath;
+
+//
+public class ModelObjectWriter implements IModelObjectConstants {
+
+ /**
+ * Returns the string representing the serialized set of build triggers for
+ * the given command
+ */
+ private static String triggerString(BuildCommand command) {
+ StringBuffer buf = new StringBuffer();
+ if (command.isBuilding(IncrementalProjectBuilder.AUTO_BUILD))
+ buf.append(TRIGGER_AUTO).append(',');
+ if (command.isBuilding(IncrementalProjectBuilder.CLEAN_BUILD))
+ buf.append(TRIGGER_CLEAN).append(',');
+ if (command.isBuilding(IncrementalProjectBuilder.FULL_BUILD))
+ buf.append(TRIGGER_FULL).append(',');
+ if (command.isBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD))
+ buf.append(TRIGGER_INCREMENTAL).append(',');
+ return buf.toString();
+ }
+
+ public ModelObjectWriter() {
+ super();
+ }
+
+ protected String[] getReferencedProjects(ProjectDescription description) {
+ IProject[] projects = description.getReferencedProjects();
+ String[] result = new String[projects.length];
+ for (int i = 0; i < projects.length; i++)
+ result[i] = projects[i].getName();
+ return result;
+ }
+
+ protected void write(BuildCommand command, XMLWriter writer) {
+ writer.startTag(BUILD_COMMAND, null);
+ if (command != null) {
+ writer.printSimpleTag(NAME, command.getName());
+ if (shouldWriteTriggers(command))
+ writer.printSimpleTag(BUILD_TRIGGERS, triggerString(command));
+ write(ARGUMENTS, command.getArguments(false), writer);
+ }
+ writer.endTag(BUILD_COMMAND);
+ }
+
+ /**
+ * Returns whether the build triggers for this command should be written.
+ */
+ private boolean shouldWriteTriggers(BuildCommand command) {
+ //only write triggers if command is configurable and there exists a trigger
+ //that the builder does NOT respond to. I.e., don't write out on the default
+ //cases to avoid dirtying .project files unnecessarily.
+ if (!command.isConfigurable())
+ return false;
+ return !command.isBuilding(IncrementalProjectBuilder.AUTO_BUILD) ||
+ !command.isBuilding(IncrementalProjectBuilder.CLEAN_BUILD) ||
+ !command.isBuilding(IncrementalProjectBuilder.FULL_BUILD) ||
+ !command.isBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD);
+ }
+
+ protected void write(LinkDescription description, XMLWriter writer) {
+ writer.startTag(LINK, null);
+ if (description != null) {
+ writer.printSimpleTag(NAME, description.getProjectRelativePath());
+ writer.printSimpleTag(TYPE, Integer.toString(description.getType()));
+ //use ASCII string of URI to ensure spaces are encoded
+ writeLocation(description.getLocationURI(), writer);
+ }
+ writer.endTag(LINK);
+ }
+
+ /**
+ * Writes a location to the XML writer. For backwards compatibility,
+ * local file system locations are written and read using a different tag
+ * from non-local file systems.
+ * @param location
+ * @param writer
+ */
+ private void writeLocation(URI location, XMLWriter writer) {
+ if (EFS.SCHEME_FILE.equals(location.getScheme())) {
+ writer.printSimpleTag(LOCATION, FileUtil.toPath(location).toPortableString());
+ } else {
+ writer.printSimpleTag(LOCATION_URI, location.toASCIIString());
+ }
+ }
+
+ /**
+ * The parameter tempLocation is a location to place our temp file (copy of the target one)
+ * to be used in case we could not successfully write the new file.
+ */
+ public void write(Object object, IPath location, IPath tempLocation) throws IOException {
+ SafeFileOutputStream file = null;
+ String tempPath = tempLocation == null ? null : tempLocation.toOSString();
+ try {
+ file = new SafeFileOutputStream(location.toOSString(), tempPath);
+ write(object, file);
+ } finally {
+ if (file != null)
+ file.close();
+ }
+ }
+
+ /**
+ * The OutputStream is closed in this method.
+ */
+ public void write(Object object, OutputStream output) throws IOException {
+ try {
+ XMLWriter writer = new XMLWriter(output);
+ write(object, writer);
+ writer.flush();
+ writer.close();
+ } finally {
+ output.close();
+ }
+ }
+
+ protected void write(Object obj, XMLWriter writer) throws IOException {
+ if (obj instanceof BuildCommand) {
+ write((BuildCommand) obj, writer);
+ return;
+ }
+ if (obj instanceof ProjectDescription) {
+ write((ProjectDescription) obj, writer);
+ return;
+ }
+ if (obj instanceof WorkspaceDescription) {
+ write((WorkspaceDescription) obj, writer);
+ return;
+ }
+ if (obj instanceof LinkDescription) {
+ write((LinkDescription) obj, writer);
+ return;
+ }
+ writer.printTabulation();
+ writer.println(obj.toString());
+ }
+
+ protected void write(ProjectDescription description, XMLWriter writer) throws IOException {
+ writer.startTag(PROJECT_DESCRIPTION, null);
+ if (description != null) {
+ writer.printSimpleTag(NAME, description.getName());
+ String comment = description.getComment();
+ writer.printSimpleTag(COMMENT, comment == null ? "" : comment); //$NON-NLS-1$
+ write(PROJECTS, PROJECT, getReferencedProjects(description), writer);
+ write(BUILD_SPEC, Arrays.asList(description.getBuildSpec(false)), writer);
+ write(NATURES, NATURE, description.getNatureIds(false), writer);
+ HashMap links = description.getLinks();
+ if (links != null) {
+ // ensure consistent order of map elements
+ List sorted = new ArrayList(links.values());
+ Collections.sort(sorted);
+ write(LINKED_RESOURCES, sorted, writer);
+ }
+ }
+ writer.endTag(PROJECT_DESCRIPTION);
+ }
+
+ protected void write(String name, Collection collection, XMLWriter writer) throws IOException {
+ writer.startTag(name, null);
+ for (Iterator it = collection.iterator(); it.hasNext();)
+ write(it.next(), writer);
+ writer.endTag(name);
+ }
+
+ /**
+ * Write maps of (String, String).
+ */
+ protected void write(String name, Map table, XMLWriter writer) {
+ writer.startTag(name, null);
+ if (table != null) {
+ // ensure consistent order of map elements
+ List sorted = new ArrayList(table.keySet());
+ Collections.sort(sorted);
+
+ for (Iterator it = sorted.iterator(); it.hasNext();) {
+ String key = (String) it.next();
+ Object value = table.get(key);
+ writer.startTag(DICTIONARY, null);
+ {
+ writer.printSimpleTag(KEY, key);
+ writer.printSimpleTag(VALUE, value);
+ }
+ writer.endTag(DICTIONARY);
+ }
+ }
+ writer.endTag(name);
+ }
+
+ protected void write(String name, String elementTagName, String[] array, XMLWriter writer) {
+ writer.startTag(name, null);
+ for (int i = 0; i < array.length; i++)
+ writer.printSimpleTag(elementTagName, array[i]);
+ writer.endTag(name);
+ }
+
+ protected void write(WorkspaceDescription description, XMLWriter writer) {
+ writer.startTag(WORKSPACE_DESCRIPTION, null);
+ if (description != null) {
+ writer.printSimpleTag(NAME, description.getName());
+ writer.printSimpleTag(AUTOBUILD, description.isAutoBuilding() ? "1" : "0"); //$NON-NLS-1$ //$NON-NLS-2$
+ writer.printSimpleTag(SNAPSHOT_INTERVAL, new Long(description.getSnapshotInterval()));
+ writer.printSimpleTag(FILE_STATE_LONGEVITY, new Long(description.getFileStateLongevity()));
+ writer.printSimpleTag(MAX_FILE_STATE_SIZE, new Long(description.getMaxFileStateSize()));
+ writer.printSimpleTag(MAX_FILE_STATES, new Integer(description.getMaxFileStates()));
+ String[] order = description.getBuildOrder(false);
+ if (order != null)
+ write(BUILD_ORDER, PROJECT, order, writer);
+ }
+ writer.endTag(WORKSPACE_DESCRIPTION);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MoveDeleteHook.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.IMoveDeleteHook;
+import org.eclipse.core.resources.team.IResourceTree;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * @since 2.0
+ */
+public class MoveDeleteHook implements IMoveDeleteHook {
+
+ /**
+ * @see IMoveDeleteHook#deleteFile(IResourceTree, IFile, int, IProgressMonitor)
+ */
+ public boolean deleteFile(IResourceTree tree, IFile file, int updateFlags, IProgressMonitor monitor) {
+ // Let someone else do the work.
+ return false;
+ }
+
+ /**
+ * @see IMoveDeleteHook#deleteFolder(IResourceTree, IFolder, int, IProgressMonitor)
+ */
+ public boolean deleteFolder(IResourceTree tree, IFolder folder, int updateFlags, IProgressMonitor monitor) {
+ // Let someone else do the work.
+ return false;
+ }
+
+ /**
+ * @see IMoveDeleteHook#deleteProject(IResourceTree, IProject, int, IProgressMonitor)
+ */
+ public boolean deleteProject(IResourceTree tree, IProject project, int updateFlags, IProgressMonitor monitor) {
+ // Let someone else do the work.
+ return false;
+ }
+
+ /**
+ * @see IMoveDeleteHook#moveFile(IResourceTree, IFile, IFile, int, IProgressMonitor)
+ */
+ public boolean moveFile(IResourceTree tree, IFile source, IFile destination, int updateFlags, IProgressMonitor monitor) {
+ // Let someone else do the work.
+ return false;
+ }
+
+ /**
+ * @see IMoveDeleteHook#moveFolder(IResourceTree, IFolder, IFolder, int, IProgressMonitor)
+ */
+ public boolean moveFolder(final IResourceTree tree, IFolder source, IFolder destination, int updateFlags, IProgressMonitor monitor) {
+ // Let someone else do the work.
+ return false;
+ }
+
+ /**
+ * @see IMoveDeleteHook#moveProject(IResourceTree, IProject, IProjectDescription, int, IProgressMonitor)
+ */
+ public boolean moveProject(IResourceTree tree, IProject source, IProjectDescription description, int updateFlags, IProgressMonitor monitor) {
+ // Let someone else do the work.
+ return false;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/NatureManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,650 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.events.ILifecycleListener;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Maintains collection of known nature descriptors, and implements
+ * nature-related algorithms provided by the workspace.
+ */
+public class NatureManager implements ILifecycleListener, IManager {
+ //maps String (nature ID) -> descriptor objects
+ protected Map descriptors;
+
+ //maps IProject -> String[] of enabled natures for that project
+ protected Map natureEnablements;
+
+ //maps String (builder ID) -> String (nature ID)
+ protected Map buildersToNatures = null;
+ //colour constants used in cycle detection algorithm
+ private static final byte WHITE = 0;
+ private static final byte GREY = 1;
+ private static final byte BLACK = 2;
+
+ protected NatureManager() {
+ super();
+ }
+
+ /**
+ * Computes the list of natures that are enabled for the given project.
+ * Enablement computation is subtlely different from nature set
+ * validation, because it must find and remove all inconsistencies.
+ */
+ protected String[] computeNatureEnablements(Project project) {
+ final ProjectDescription description = project.internalGetDescription();
+ if (description == null)
+ return new String[0];//project deleted concurrently
+ String[] natureIds = description.getNatureIds();
+ int count = natureIds.length;
+ if (count == 0)
+ return natureIds;
+
+ //set of the nature ids being validated (String (id))
+ HashSet candidates = new HashSet(count * 2);
+ //table of String(set ID) -> ArrayList (nature IDs that belong to that set)
+ HashMap setsToNatures = new HashMap(count);
+ for (int i = 0; i < count; i++) {
+ String id = natureIds[i];
+ ProjectNatureDescriptor desc = (ProjectNatureDescriptor) getNatureDescriptor(id);
+ if (desc == null)
+ continue;
+ if (!desc.hasCycle)
+ candidates.add(id);
+ //build set to nature map
+ String[] setIds = desc.getNatureSetIds();
+ for (int j = 0; j < setIds.length; j++) {
+ String set = setIds[j];
+ ArrayList current = (ArrayList) setsToNatures.get(set);
+ if (current == null) {
+ current = new ArrayList(5);
+ setsToNatures.put(set, current);
+ }
+ current.add(id);
+ }
+ }
+ //now remove all natures that belong to sets with more than one member
+ for (Iterator it = setsToNatures.values().iterator(); it.hasNext();) {
+ ArrayList setMembers = (ArrayList) it.next();
+ if (setMembers.size() > 1) {
+ candidates.removeAll(setMembers);
+ }
+ }
+ //now walk over the set and ensure all pre-requisite natures are present
+ //need to walk in prereq order because if A requires B and B requires C, and C is
+ //disabled for some other reason, we must ensure both A and B are disabled
+ String[] orderedCandidates = (String[]) candidates.toArray(new String[candidates.size()]);
+ orderedCandidates = sortNatureSet(orderedCandidates);
+ for (int i = 0; i < orderedCandidates.length; i++) {
+ String id = orderedCandidates[i];
+ IProjectNatureDescriptor desc = getNatureDescriptor(id);
+ String[] required = desc.getRequiredNatureIds();
+ for (int j = 0; j < required.length; j++) {
+ if (!candidates.contains(required[j])) {
+ candidates.remove(id);
+ break;
+ }
+ }
+ }
+ //remaining candidates are enabled
+ return (String[]) candidates.toArray(new String[candidates.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getNatureDescriptor(String)
+ */
+ public IProjectNatureDescriptor getNatureDescriptor(String natureId) {
+ lazyInitialize();
+ return (IProjectNatureDescriptor) descriptors.get(natureId);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getNatureDescriptors()
+ */
+ public IProjectNatureDescriptor[] getNatureDescriptors() {
+ lazyInitialize();
+ Collection values = descriptors.values();
+ return (IProjectNatureDescriptor[]) values.toArray(new IProjectNatureDescriptor[values.size()]);
+ }
+
+ public void handleEvent(LifecycleEvent event) {
+ switch (event.kind) {
+ case LifecycleEvent.PRE_PROJECT_CHANGE :
+ case LifecycleEvent.PRE_PROJECT_CLOSE :
+ case LifecycleEvent.PRE_PROJECT_DELETE :
+ case LifecycleEvent.PRE_PROJECT_MOVE :
+ case LifecycleEvent.PRE_PROJECT_OPEN :
+ flushEnablements((IProject) event.resource);
+ }
+ }
+
+ /**
+ * Configures the nature with the given ID for the given project.
+ */
+ protected void configureNature(final Project project, final String natureID, final MultiStatus errors) {
+ ISafeRunnable code = new ISafeRunnable() {
+ public void run() throws Exception {
+ IProjectNature nature = createNature(project, natureID);
+ nature.configure();
+ ProjectInfo info = (ProjectInfo) project.getResourceInfo(false, true);
+ info.setNature(natureID, nature);
+ }
+
+ public void handleException(Throwable exception) {
+ if (exception instanceof CoreException)
+ errors.add(((CoreException) exception).getStatus());
+ else
+ errors.add(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, project.getFullPath(), NLS.bind(Messages.resources_errorNature, natureID), exception));
+ }
+ };
+ if (Policy.DEBUG_NATURES) {
+ System.out.println("Configuring nature: " + natureID + " on project: " + project.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ SafeRunner.run(code);
+ }
+
+ /**
+ * Configures the natures for the given project. Natures found in the new description
+ * that weren't present in the old description are added, and natures missing from the
+ * new description are removed. Updates the old description so that it reflects
+ * the new set of the natures. Errors are added to the given multi-status.
+ */
+ public void configureNatures(Project project, ProjectDescription oldDescription, ProjectDescription newDescription, MultiStatus status) {
+ // Be careful not to rely on much state because (de)configuring a nature
+ // may well result in recursive calls to this method.
+ HashSet oldNatures = new HashSet(Arrays.asList(oldDescription.getNatureIds(false)));
+ HashSet newNatures = new HashSet(Arrays.asList(newDescription.getNatureIds(false)));
+ if (oldNatures.equals(newNatures))
+ return;
+ HashSet deletions = (HashSet) oldNatures.clone();
+ HashSet additions = (HashSet) newNatures.clone();
+ additions.removeAll(oldNatures);
+ deletions.removeAll(newNatures);
+ //do validation of the changes. If any single change is invalid, fail the whole operation
+ IStatus result = validateAdditions(newNatures, additions, project);
+ if (!result.isOK()) {
+ status.merge(result);
+ return;
+ }
+ result = validateRemovals(newNatures, deletions);
+ if (!result.isOK()) {
+ status.merge(result);
+ return;
+ }
+ // set the list of nature ids BEFORE (de)configuration so recursive calls will
+ // not try to do the same work.
+ oldDescription.setNatureIds(newDescription.getNatureIds(true));
+ flushEnablements(project);
+ //(de)configure in topological order to maintain consistency of configured set
+ String[] ordered = null;
+ if (deletions.size() > 0) {
+ ordered = sortNatureSet((String[]) deletions.toArray(new String[deletions.size()]));
+ for (int i = ordered.length; --i >= 0;)
+ deconfigureNature(project, ordered[i], status);
+ }
+ if (additions.size() > 0) {
+ ordered = sortNatureSet((String[]) additions.toArray(new String[additions.size()]));
+ for (int i = 0; i < ordered.length; i++)
+ configureNature(project, ordered[i], status);
+ }
+ }
+
+ /**
+ * Finds the nature extension, and initializes and returns an instance.
+ */
+ protected IProjectNature createNature(Project project, String natureID) throws CoreException {
+ IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_NATURES, natureID);
+ if (extension == null) {
+ String message = NLS.bind(Messages.resources_natureExtension, natureID);
+ throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, null);
+ }
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length < 1) {
+ String message = NLS.bind(Messages.resources_natureClass, natureID);
+ throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, null);
+ }
+ //find the runtime configuration element
+ IConfigurationElement config = null;
+ for (int i = 0; config == null && i < configs.length; i++)
+ if ("runtime".equalsIgnoreCase(configs[i].getName())) //$NON-NLS-1$
+ config = configs[i];
+ if (config == null) {
+ String message = NLS.bind(Messages.resources_natureFormat, natureID);
+ throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, null);
+ }
+ try {
+ IProjectNature nature = (IProjectNature) config.createExecutableExtension("run"); //$NON-NLS-1$
+ nature.setProject(project);
+ return nature;
+ } catch (ClassCastException e) {
+ String message = NLS.bind(Messages.resources_natureImplement, natureID);
+ throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, e);
+ }
+ }
+
+ /**
+ * Deconfigures the nature with the given ID for the given project.
+ */
+ protected void deconfigureNature(final Project project, final String natureID, final MultiStatus status) {
+ final ProjectInfo info = (ProjectInfo) project.getResourceInfo(false, true);
+ IProjectNature existingNature = info.getNature(natureID);
+ if (existingNature == null) {
+ // if there isn't a nature then create one so we can deconfig it.
+ try {
+ existingNature = createNature(project, natureID);
+ } catch (CoreException e) {
+ // Ignore - we are removing a nature that no longer exists in the install
+ return;
+ }
+ }
+ final IProjectNature nature = existingNature;
+ ISafeRunnable code = new ISafeRunnable() {
+ public void run() throws Exception {
+ nature.deconfigure();
+ info.setNature(natureID, null);
+ }
+
+ public void handleException(Throwable exception) {
+ if (exception instanceof CoreException)
+ status.add(((CoreException) exception).getStatus());
+ else
+ status.add(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, project.getFullPath(), NLS.bind(Messages.resources_natureDeconfig, natureID), exception));
+ }
+ };
+ if (Policy.DEBUG_NATURES) {
+ System.out.println("Deconfiguring nature: " + natureID + " on project: " + project.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ SafeRunner.run(code);
+ }
+
+ /**
+ * Marks all nature descriptors that are involved in cycles
+ */
+ protected void detectCycles() {
+ Collection values = descriptors.values();
+ ProjectNatureDescriptor[] natures = (ProjectNatureDescriptor[]) values.toArray(new ProjectNatureDescriptor[values.size()]);
+ for (int i = 0; i < natures.length; i++)
+ if (natures[i].colour == WHITE)
+ hasCycles(natures[i]);
+ }
+
+ /**
+ * Returns a status indicating failure to configure natures.
+ */
+ protected IStatus failure(String reason) {
+ return new ResourceStatus(IResourceStatus.INVALID_NATURE_SET, reason);
+ }
+
+ /**
+ * Returns the ID of the project nature that claims ownership of the
+ * builder with the given ID. Returns null if no nature owns that builder.
+ */
+ public String findNatureForBuilder(String builderID) {
+ if (buildersToNatures == null) {
+ buildersToNatures = new HashMap(10);
+ IProjectNatureDescriptor[] descs = getNatureDescriptors();
+ for (int i = 0; i < descs.length; i++) {
+ String natureId = descs[i].getNatureId();
+ String[] builders = ((ProjectNatureDescriptor) descs[i]).getBuilderIds();
+ for (int j = 0; j < builders.length; j++) {
+ //FIXME: how to handle multiple natures specifying same builder
+ buildersToNatures.put(builders[j], natureId);
+ }
+ }
+ }
+ return (String) buildersToNatures.get(builderID);
+ }
+
+ protected void flushEnablements(IProject project) {
+ if (natureEnablements != null) {
+ natureEnablements.remove(project);
+ if (natureEnablements.size() == 0) {
+ natureEnablements = null;
+ }
+ }
+ }
+
+ /**
+ * Returns the cached array of enabled natures for this project,
+ * or null if there is nothing in the cache.
+ */
+ protected String[] getEnabledNatures(Project project) {
+ String[] enabled;
+ if (natureEnablements != null) {
+ enabled = (String[]) natureEnablements.get(project);
+ if (enabled != null)
+ return enabled;
+ }
+ enabled = computeNatureEnablements(project);
+ setEnabledNatures(project, enabled);
+ return enabled;
+ }
+
+ /**
+ * Returns true if there are cycles in the graph of nature
+ * dependencies starting at root i. Returns false otherwise.
+ * Marks all descriptors that are involved in the cycle as invalid.
+ */
+ protected boolean hasCycles(ProjectNatureDescriptor desc) {
+ if (desc.colour == BLACK) {
+ //this subgraph has already been traversed, so we know the answer
+ return desc.hasCycle;
+ }
+ //if we are already grey, then we have found a cycle
+ if (desc.colour == GREY) {
+ desc.hasCycle = true;
+ desc.colour = BLACK;
+ return true;
+ }
+ //colour current descriptor GREY to indicate it is being visited
+ desc.colour = GREY;
+
+ //visit all dependents of nature i
+ String[] required = desc.getRequiredNatureIds();
+ for (int i = 0; i < required.length; i++) {
+ ProjectNatureDescriptor dependency = (ProjectNatureDescriptor) getNatureDescriptor(required[i]);
+ //missing dependencies cannot create cycles
+ if (dependency != null && hasCycles(dependency)) {
+ desc.hasCycle = true;
+ desc.colour = BLACK;
+ return true;
+ }
+ }
+ desc.hasCycle = false;
+ desc.colour = BLACK;
+ return false;
+ }
+
+ /**
+ * Returns true if the given project has linked resources, and false otherwise.
+ */
+ protected boolean hasLinks(IProject project) {
+ try {
+ IResource[] children = project.members();
+ for (int i = 0; i < children.length; i++)
+ if (children[i].isLinked())
+ return true;
+ } catch (CoreException e) {
+ //not possible for project to be inaccessible
+ Policy.log(e.getStatus());
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the two natures have overlapping "one-of-nature" set
+ * memberships. Returns the name of one such overlap, or null if
+ * there is no set overlap.
+ */
+ protected String hasSetOverlap(IProjectNatureDescriptor one, IProjectNatureDescriptor two) {
+ if (one == null || two == null) {
+ return null;
+ }
+ //efficiency not so important because these sets are very small
+ String[] setsOne = one.getNatureSetIds();
+ String[] setsTwo = two.getNatureSetIds();
+ for (int iOne = 0; iOne < setsOne.length; iOne++) {
+ for (int iTwo = 0; iTwo < setsTwo.length; iTwo++) {
+ if (setsOne[iOne].equals(setsTwo[iTwo])) {
+ return setsOne[iOne];
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Perform depth-first insertion of the given nature ID into the result list.
+ */
+ protected void insert(ArrayList list, Set seen, String id) {
+ if (seen.contains(id))
+ return;
+ seen.add(id);
+ //insert prerequisite natures
+ IProjectNatureDescriptor desc = getNatureDescriptor(id);
+ if (desc != null) {
+ String[] prereqs = desc.getRequiredNatureIds();
+ for (int i = 0; i < prereqs.length; i++)
+ insert(list, seen, prereqs[i]);
+ }
+ list.add(id);
+ }
+
+ /* (non-Javadoc)
+ * Returns true if the given nature is enabled for the given project.
+ *
+ * @see IProject#isNatureEnabled(String)
+ */
+ public boolean isNatureEnabled(Project project, String id) {
+ String[] enabled = getEnabledNatures(project);
+ for (int i = 0; i < enabled.length; i++) {
+ if (enabled[i].equals(id))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Only initialize the descriptor cache when we know it is actually needed.
+ * Running programs may never need to refer to this cache.
+ */
+ protected void lazyInitialize() {
+ if (descriptors != null)
+ return;
+ IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_NATURES);
+ IExtension[] extensions = point.getExtensions();
+ descriptors = new HashMap(extensions.length * 2 + 1);
+ for (int i = 0, imax = extensions.length; i < imax; i++) {
+ IProjectNatureDescriptor desc = null;
+ try {
+ desc = new ProjectNatureDescriptor(extensions[i]);
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ }
+ if (desc != null)
+ descriptors.put(desc.getNatureId(), desc);
+ }
+ //do cycle detection now so it only has to be done once
+ //cycle detection on a graph subset is a pain
+ detectCycles();
+ }
+
+ /**
+ * Sets the cached array of enabled natures for this project.
+ */
+ protected void setEnabledNatures(IProject project, String[] enablements) {
+ if (natureEnablements == null)
+ natureEnablements = Collections.synchronizedMap(new HashMap(20));
+ natureEnablements.put(project, enablements);
+ }
+
+ public void shutdown(IProgressMonitor monitor) {
+ // do nothing
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#sortNatureSet(String[])
+ */
+ public String[] sortNatureSet(String[] natureIds) {
+ int count = natureIds.length;
+ if (count == 0)
+ return natureIds;
+ ArrayList result = new ArrayList(count);
+ HashSet seen = new HashSet(count);//for cycle and duplicate detection
+ for (int i = 0; i < count; i++)
+ insert(result, seen, natureIds[i]);
+ //remove added prerequisites that didn't exist in original list
+ seen.clear();
+ seen.addAll(Arrays.asList(natureIds));
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Object id = it.next();
+ if (!seen.contains(id))
+ it.remove();
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ ((Workspace) ResourcesPlugin.getWorkspace()).addLifecycleListener(this);
+ }
+
+ /**
+ * Validates the given nature additions in the nature set for this
+ * project. Tolerates existing inconsistencies in the nature set.
+ * @param newNatures the complete new set of nature IDs for the project,
+ * including additions
+ * @param additions the subset of newNatures that represents natures
+ * being added
+ * @return An OK status if all additions are valid, and an error status
+ * if any of the additions introduce new inconsistencies.
+ */
+ protected IStatus validateAdditions(HashSet newNatures, HashSet additions, IProject project) {
+ Boolean hasLinks = null;//three states: true, false, null (not yet computed)
+ //perform checks in order from least expensive to most expensive
+ for (Iterator added = additions.iterator(); added.hasNext();) {
+ String id = (String) added.next();
+ // check for adding a nature that is not available.
+ IProjectNatureDescriptor desc = getNatureDescriptor(id);
+ if (desc == null) {
+ return failure(NLS.bind(Messages.natures_missingNature, id));
+ }
+ // check for adding a nature that creates a circular dependency
+ if (((ProjectNatureDescriptor) desc).hasCycle) {
+ return failure(NLS.bind(Messages.natures_hasCycle, id));
+ }
+ // check for adding a nature that has a missing prerequisite.
+ String[] required = desc.getRequiredNatureIds();
+ for (int i = 0; i < required.length; i++) {
+ if (!newNatures.contains(required[i])) {
+ return failure(NLS.bind(Messages.natures_missingPrerequisite, id, required[i]));
+ }
+ }
+ // check for adding a nature that creates a duplicated set member.
+ for (Iterator all = newNatures.iterator(); all.hasNext();) {
+ String current = (String) all.next();
+ if (!current.equals(id)) {
+ String overlap = hasSetOverlap(desc, getNatureDescriptor(current));
+ if (overlap != null) {
+ return failure(NLS.bind(Messages.natures_multipleSetMembers, overlap));
+ }
+ }
+ }
+ //check for adding a nature that has linked resource veto
+ if (!desc.isLinkingAllowed()) {
+ if (hasLinks == null) {
+ hasLinks = hasLinks(project) ? Boolean.TRUE : Boolean.FALSE;
+ }
+ if (hasLinks.booleanValue())
+ return failure(NLS.bind(Messages.links_vetoNature, project.getName(), id));
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates whether a project with the given set of natures should allow
+ * linked resources. Returns an OK status if linking is allowed,
+ * otherwise a non-OK status indicating why linking is not allowed.
+ * Linking is allowed if there is no project nature that explicitly disallows it.
+ * No validation is done on the nature ids themselves (ids that don't have
+ * a corresponding nature definition will be ignored).
+ */
+ public IStatus validateLinkCreation(String[] natureIds) {
+ for (int i = 0; i < natureIds.length; i++) {
+ IProjectNatureDescriptor desc = getNatureDescriptor(natureIds[i]);
+ if (desc != null && !desc.isLinkingAllowed()) {
+ String msg = NLS.bind(Messages.links_natureVeto, desc.getLabel());
+ return new ResourceStatus(IResourceStatus.LINKING_NOT_ALLOWED, msg);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates the given nature removals in the nature set for this
+ * project. Tolerates existing inconsistencies in the nature set.
+ *
+ * @param newNatures the complete new set of nature IDs for the project,
+ * excluding deletions
+ * @param deletions the nature IDs that are being removed from the set.
+ * @return An OK status if all removals are valid, and a not OK status
+ * if any of the deletions introduce new inconsistencies.
+ */
+ protected IStatus validateRemovals(HashSet newNatures, HashSet deletions) {
+ //iterate over new nature set, and ensure that none of their prerequisites are being deleted
+ for (Iterator it = newNatures.iterator(); it.hasNext();) {
+ String currentID = (String) it.next();
+ IProjectNatureDescriptor desc = getNatureDescriptor(currentID);
+ if (desc != null) {
+ String[] required = desc.getRequiredNatureIds();
+ for (int i = 0; i < required.length; i++) {
+ if (deletions.contains(required[i])) {
+ return failure(NLS.bind(Messages.natures_invalidRemoval, required[i], currentID));
+ }
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateNatureSet(String[])
+ */
+ public IStatus validateNatureSet(String[] natureIds) {
+ int count = natureIds.length;
+ if (count == 0)
+ return Status.OK_STATUS;
+ String msg = Messages.natures_invalidSet;
+ MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_NATURE_SET, msg, null);
+
+ //set of the nature ids being validated (String (id))
+ HashSet natures = new HashSet(count * 2);
+ //set of nature sets for which a member nature has been found (String (id))
+ HashSet sets = new HashSet(count);
+ for (int i = 0; i < count; i++) {
+ String id = natureIds[i];
+ ProjectNatureDescriptor desc = (ProjectNatureDescriptor) getNatureDescriptor(id);
+ if (desc == null) {
+ result.add(failure(NLS.bind(Messages.natures_missingNature, id)));
+ continue;
+ }
+ if (desc.hasCycle)
+ result.add(failure(NLS.bind(Messages.natures_hasCycle, id)));
+ if (!natures.add(id))
+ result.add(failure(NLS.bind(Messages.natures_duplicateNature, id)));
+ //validate nature set one-of constraint
+ String[] setIds = desc.getNatureSetIds();
+ for (int j = 0; j < setIds.length; j++) {
+ if (!sets.add(setIds[j]))
+ result.add(failure(NLS.bind(Messages.natures_multipleSetMembers, setIds[j])));
+ }
+ }
+ //now walk over the set and ensure all pre-requisite natures are present
+ for (int i = 0; i < count; i++) {
+ IProjectNatureDescriptor desc = getNatureDescriptor(natureIds[i]);
+ if (desc == null)
+ continue;
+ String[] required = desc.getRequiredNatureIds();
+ for (int j = 0; j < required.length; j++)
+ if (!natures.contains(required[j]))
+ result.add(failure(NLS.bind(Messages.natures_missingPrerequisite, natureIds[i], required[j])));
+ }
+ //if there are no problems we must return a status whose code is OK
+ return result.isOK() ? Status.OK_STATUS : (IStatus) result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/OS.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.Arrays;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Captures platform specific attributes relevant to the core resources plugin. This
+ * class is not intended to be instantiated.
+ */
+public abstract class OS {
+ private static final String INSTALLED_PLATFORM;
+
+ public static final char[] INVALID_RESOURCE_CHARACTERS;
+ public static final String[] INVALID_RESOURCE_NAMES;
+
+ static {
+ //find out the OS being used
+ //setup the invalid names
+ INSTALLED_PLATFORM = Platform.getOS();
+ if (INSTALLED_PLATFORM.equals(Platform.OS_WIN32)) {
+ //valid names and characters taken from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
+ INVALID_RESOURCE_CHARACTERS = new char[] {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
+ INVALID_RESOURCE_NAMES = new String[] {"aux", "clock$", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+ "com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
+ "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
+ Arrays.sort(INVALID_RESOURCE_NAMES);
+ } else {
+ //only front slash and null char are invalid on UNIXes
+ //taken from http://www.faqs.org/faqs/unix-faq/faq/part2/section-2.html
+ INVALID_RESOURCE_CHARACTERS = new char[] {'/', '\0',};
+ INVALID_RESOURCE_NAMES = new String[] {};
+ }
+ }
+
+ /**
+ * Returns true if the given name is a valid resource name on this operating system,
+ * and false otherwise.
+ */
+ public static boolean isNameValid(String name) {
+ //. and .. have special meaning on all platforms
+ if (name.equals(".") || name.equals("..")) //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ if (INSTALLED_PLATFORM.equals(Platform.OS_WIN32)) {
+ //empty names are not valid
+ final int length = name.length();
+ if (length == 0)
+ return false;
+ final char lastChar = name.charAt(length-1);
+ // filenames ending in dot are not valid
+ if (lastChar == '.')
+ return false;
+ // file names ending with whitespace are truncated (bug 118997)
+ if (Character.isWhitespace(lastChar))
+ return false;
+ int dot = name.indexOf('.');
+ //on windows, filename suffixes are not relevant to name validity
+ name = dot == -1 ? name : name.substring(0, dot);
+ }
+ return Arrays.binarySearch(INVALID_RESOURCE_NAMES, name.toLowerCase()) < 0;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PathVariableManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.events.PathVariableChangeEvent;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Core's implementation of IPathVariableManager.
+ */
+public class PathVariableManager implements IPathVariableManager, IManager {
+
+ static final String VARIABLE_PREFIX = "pathvariable."; //$NON-NLS-1$
+ private Set listeners;
+
+ private Preferences preferences;
+
+ /**
+ * Constructor for the class.
+ */
+ public PathVariableManager() {
+ this.listeners = Collections.synchronizedSet(new HashSet());
+ this.preferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+ }
+
+ /**
+ * @see org.eclipse.core.resources.
+ * IPathVariableManager#addChangeListener(IPathVariableChangeListener)
+ */
+ public void addChangeListener(IPathVariableChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Throws a runtime exception if the given name is not valid as a path
+ * variable name.
+ */
+ private void checkIsValidName(String name) throws CoreException {
+ IStatus status = validateName(name);
+ if (!status.isOK())
+ throw new CoreException(status);
+ }
+
+ /**
+ * Throws an exception if the given path is not valid as a path variable
+ * value.
+ */
+ private void checkIsValidValue(IPath newValue) throws CoreException {
+ IStatus status = validateValue(newValue);
+ if (!status.isOK())
+ throw new CoreException(status);
+ }
+
+ /**
+ * Fires a property change event corresponding to a change to the
+ * current value of the variable with the given name.
+ *
+ * @param name the name of the variable, to be used as the variable
+ * in the event object
+ * @param value the current value of the path variable or <code>null</code> if
+ * the variable was deleted
+ * @param type one of <code>IPathVariableChangeEvent.VARIABLE_CREATED</code>,
+ * <code>IPathVariableChangeEvent.VARIABLE_CHANGED</code>, or
+ * <code>IPathVariableChangeEvent.VARIABLE_DELETED</code>
+ * @see IPathVariableChangeEvent
+ * @see IPathVariableChangeEvent#VARIABLE_CREATED
+ * @see IPathVariableChangeEvent#VARIABLE_CHANGED
+ * @see IPathVariableChangeEvent#VARIABLE_DELETED
+ */
+ private void fireVariableChangeEvent(String name, IPath value, int type) {
+ if (this.listeners.size() == 0)
+ return;
+ // use a separate collection to avoid interference of simultaneous additions/removals
+ Object[] listenerArray = this.listeners.toArray();
+ final PathVariableChangeEvent pve = new PathVariableChangeEvent(this, name, value, type);
+ for (int i = 0; i < listenerArray.length; ++i) {
+ final IPathVariableChangeListener l = (IPathVariableChangeListener) listenerArray[i];
+ ISafeRunnable job = new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // already being logged in SafeRunner#run()
+ }
+
+ public void run() throws Exception {
+ l.pathVariableChanged(pve);
+ }
+ };
+ SafeRunner.run(job);
+ }
+ }
+
+ /**
+ * Return a key to use in the Preferences.
+ */
+ private String getKeyForName(String varName) {
+ return VARIABLE_PREFIX + varName;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableManager#getPathVariableNames()
+ */
+ public String[] getPathVariableNames() {
+ List result = new LinkedList();
+ String[] names = preferences.propertyNames();
+ for (int i = 0; i < names.length; i++) {
+ if (names[i].startsWith(VARIABLE_PREFIX)) {
+ String key = names[i].substring(VARIABLE_PREFIX.length());
+ // filter out names for preferences which might be valid in the
+ // preference store but does not have valid path variable names
+ // and/or values. We can get in this state if the user has
+ // edited the file on disk or set a preference using the prefix
+ // reserved to path variables (#VARIABLE_PREFIX).
+ // TODO: we may want to look at removing these keys from the
+ // preference store as a garbage collection means
+ if (validateName(key).isOK() && validateValue(getValue(key)).isOK())
+ result.add(key);
+ }
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Note that if a user changes the key in the preferences file to be invalid
+ * and then calls #getValue using that key, they will get the value back for
+ * that. But then if they try and call #setValue using the same key it will throw
+ * an exception. We may want to revisit this behaviour in the future.
+ *
+ * @see org.eclipse.core.resources.IPathVariableManager#getValue(String)
+ */
+ public IPath getValue(String varName) {
+ String key = getKeyForName(varName);
+ String value = preferences.getString(key);
+ return value.length() == 0 ? null : Path.fromPortableString(value);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableManager#isDefined(String)
+ */
+ public boolean isDefined(String varName) {
+ return getValue(varName) != null;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.
+ * IPathVariableManager#removeChangeListener(IPathVariableChangeListener)
+ */
+ public void removeChangeListener(IPathVariableChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableManager#resolvePath(IPath)
+ */
+ public IPath resolvePath(IPath path) {
+ if (path == null || path.segmentCount() == 0 || path.isAbsolute() || path.getDevice() != null)
+ return path;
+ IPath value = getValue(path.segment(0));
+ return value == null ? path : value.append(path.removeFirstSegments(1));
+ }
+
+ public URI resolveURI(URI uri) {
+ if (uri == null || uri.isAbsolute())
+ return uri;
+ IPath raw = new Path(uri.getSchemeSpecificPart());
+ IPath resolved = resolvePath(raw);
+ return raw == resolved ? uri : URIUtil.toURI(resolved);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableManager#setValue(String, IPath)
+ */
+ public void setValue(String varName, IPath newValue) throws CoreException {
+ checkIsValidName(varName);
+ //convert path value to canonical form
+ if (newValue != null && newValue.isAbsolute())
+ newValue = FileUtil.canonicalPath(newValue);
+ checkIsValidValue(newValue);
+ int eventType;
+ // read previous value and set new value atomically in order to generate the right event
+ synchronized (this) {
+ IPath currentValue = getValue(varName);
+ boolean variableExists = currentValue != null;
+ if (!variableExists && newValue == null)
+ return;
+ if (variableExists && currentValue.equals(newValue))
+ return;
+ if (newValue == null) {
+ preferences.setToDefault(getKeyForName(varName));
+ eventType = IPathVariableChangeEvent.VARIABLE_DELETED;
+ } else {
+ preferences.setValue(getKeyForName(varName), newValue.toPortableString());
+ eventType = variableExists ? IPathVariableChangeEvent.VARIABLE_CHANGED : IPathVariableChangeEvent.VARIABLE_CREATED;
+ }
+ }
+ // notify listeners from outside the synchronized block to avoid deadlocks
+ fireVariableChangeEvent(varName, newValue, eventType);
+ }
+
+ /**
+ * @see org.eclipse.core.internal.resources.IManager#shutdown(IProgressMonitor)
+ */
+ public void shutdown(IProgressMonitor monitor) {
+ // The preferences for this plug-in are saved in the Plugin.shutdown
+ // method so we don't have to do it here.
+ }
+
+ /**
+ * @see org.eclipse.core.internal.resources.IManager#startup(IProgressMonitor)
+ */
+ public void startup(IProgressMonitor monitor) {
+ // since we are accessing the preference store directly, we don't
+ // need to do any setup here.
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IPathVariableManager#validateName(String)
+ */
+ public IStatus validateName(String name) {
+ String message = null;
+ if (name.length() == 0) {
+ message = Messages.pathvar_length;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ char first = name.charAt(0);
+ if (!Character.isLetter(first) && first != '_') {
+ message = NLS.bind(Messages.pathvar_beginLetter, String.valueOf(first));
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+
+ for (int i = 1; i < name.length(); i++) {
+ char following = name.charAt(i);
+ if (Character.isWhitespace(following))
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, Messages.pathvar_whitespace);
+ if (!Character.isLetter(following) && !Character.isDigit(following) && following != '_') {
+ message = NLS.bind(Messages.pathvar_invalidChar, String.valueOf(following));
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * @see IPathVariableManager#validateValue(IPath)
+ */
+ public IStatus validateValue(IPath value) {
+ if (value != null && (!value.isValidPath(value.toString()) || !value.isAbsolute())) {
+ String message = Messages.pathvar_invalidValue;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
+ }
+ return Status.OK_STATUS;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PlatformURLResourceConnection.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.IOException;
+import java.net.*;
+import org.eclipse.core.internal.boot.PlatformURLConnection;
+import org.eclipse.core.internal.boot.PlatformURLHandler;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Platform URL support
+ * platform:/resource/<path>/<resource> maps to resource in current workspace
+ */
+public class PlatformURLResourceConnection extends PlatformURLConnection {
+
+ // resource/ protocol
+ public static final String RESOURCE = "resource"; //$NON-NLS-1$
+ public static final String RESOURCE_URL_STRING = PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + '/' + RESOURCE + '/';
+ private static URL rootURL;
+
+ public PlatformURLResourceConnection(URL url) {
+ super(url);
+ }
+
+ protected boolean allowCaching() {
+ return false; // don't cache, workspace is local
+ }
+
+ protected URL resolve() throws IOException {
+ String filePath = url.getFile().trim();
+ filePath = URLDecoder.decode(filePath, "UTF-8"); //$NON-NLS-1$
+ IPath spec = new Path(filePath).makeRelative();
+ if (!spec.segment(0).equals(RESOURCE))
+ throw new IOException(NLS.bind(Messages.url_badVariant, url));
+ int count = spec.segmentCount();
+ // if there is only one segment then we are talking about the workspace root.
+ if (count == 1)
+ return rootURL;
+ // if there are two segments then the second is a project name.
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(spec.segment(1));
+ if (!project.exists()) {
+ String message = NLS.bind(Messages.url_couldNotResolve_projectDoesNotExist, project.getName(), url.toExternalForm());
+ throw new IOException(message);
+ }
+
+ IResource resource = null;
+ IPath result = null;
+
+ if (count == 2) {
+ resource = project;
+ } else {
+ spec = spec.removeFirstSegments(2);
+ resource = project.getFile(spec);
+ }
+
+ result = resource.getLocation();
+
+ if (result == null) {
+ URI uri = resource.getLocationURI();
+ if (uri != null) {
+ try {
+ URL url2 = uri.toURL();
+ if (url2.getProtocol().equals("file")) //$NON-NLS-1$
+ return url2;
+ } catch (MalformedURLException e) {
+ String message = NLS.bind(Messages.url_couldNotResolve_URLProtocolHandlerCanNotResolveURL, uri.toString(), url.toExternalForm());
+ throw new IOException(message);
+ }
+ }
+ String message = NLS.bind(Messages.url_couldNotResolve_resourceLocationCanNotBeDetermined, resource.getFullPath(), url.toExternalForm());
+ throw new IOException(message);
+ }
+ return new URL("file", "", result.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * This method is called during resource plugin startup() initialization.
+ *
+ * @param root URL to the root of the current workspace.
+ */
+ public static void startup(IPath root) {
+ // register connection type for platform:/resource/ handling
+ if (rootURL != null)
+ return;
+ try {
+ rootURL = root.toFile().toURL();
+ } catch (MalformedURLException e) {
+ // should never happen but if it does, the resource URL cannot be supported.
+ return;
+ }
+ PlatformURLHandler.register(RESOURCE, PlatformURLResourceConnection.class);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PreferenceInitializer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.preferences.*;
+
+/**
+ * @since 3.1
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+
+ // internal preference keys
+ public static final String PREF_OPERATIONS_PER_SNAPSHOT = "snapshots.operations"; //$NON-NLS-1$
+ public static final String PREF_DELTA_EXPIRATION = "delta.expiration"; //$NON-NLS-1$
+
+ // DEFAULTS
+ public static final boolean PREF_AUTO_REFRESH_DEFAULT = false;
+ public static final boolean PREF_DISABLE_LINKING_DEFAULT = false;
+ public static final String PREF_ENCODING_DEFAULT = ""; //$NON-NLS-1$
+ public static final boolean PREF_AUTO_BUILDING_DEFAULT = true;
+ public static final String PREF_BUILD_ORDER_DEFAULT = ""; //$NON-NLS-1$
+ public static final int PREF_MAX_BUILD_ITERATIONS_DEFAULT = 10;
+ public static final boolean PREF_DEFAULT_BUILD_ORDER_DEFAULT = true;
+ public final static long PREF_SNAPSHOT_INTERVAL_DEFAULT = 5 * 60 * 1000l; // 5 min
+ public static final int PREF_OPERATIONS_PER_SNAPSHOT_DEFAULT = 100;
+ public static final long PREF_FILE_STATE_LONGEVITY_DEFAULT = 7 * 24 * 3600 * 1000l; // 7 days
+ public static final long PREF_MAX_FILE_STATE_SIZE_DEFAULT = 1024 * 1024l; // 1 MB
+ public static final int PREF_MAX_FILE_STATES_DEFAULT = 50;
+ public static final long PREF_DELTA_EXPIRATION_DEFAULT = 30 * 24 * 3600 * 1000l; // 30 days
+
+ public PreferenceInitializer() {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences()
+ */
+ public void initializeDefaultPreferences() {
+ IEclipsePreferences node = new DefaultScope().getNode(ResourcesPlugin.PI_RESOURCES);
+ // auto-refresh default
+ node.putBoolean(ResourcesPlugin.PREF_AUTO_REFRESH, PREF_AUTO_REFRESH_DEFAULT);
+
+ // linked resources default
+ node.putBoolean(ResourcesPlugin.PREF_DISABLE_LINKING, PREF_DISABLE_LINKING_DEFAULT);
+
+ // build manager defaults
+ node.putBoolean(ResourcesPlugin.PREF_AUTO_BUILDING, PREF_AUTO_BUILDING_DEFAULT);
+ node.put(ResourcesPlugin.PREF_BUILD_ORDER, PREF_BUILD_ORDER_DEFAULT);
+ node.putInt(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS, PREF_MAX_BUILD_ITERATIONS_DEFAULT);
+ node.putBoolean(ResourcesPlugin.PREF_DEFAULT_BUILD_ORDER, PREF_DEFAULT_BUILD_ORDER_DEFAULT);
+
+ // history store defaults
+ node.putLong(ResourcesPlugin.PREF_FILE_STATE_LONGEVITY, PREF_FILE_STATE_LONGEVITY_DEFAULT);
+ node.putLong(ResourcesPlugin.PREF_MAX_FILE_STATE_SIZE, PREF_MAX_FILE_STATE_SIZE_DEFAULT);
+ node.putInt(ResourcesPlugin.PREF_MAX_FILE_STATES, PREF_MAX_FILE_STATES_DEFAULT);
+
+ // save manager defaults
+ node.putLong(ResourcesPlugin.PREF_SNAPSHOT_INTERVAL, PREF_SNAPSHOT_INTERVAL_DEFAULT);
+ node.putInt(PREF_OPERATIONS_PER_SNAPSHOT, PREF_OPERATIONS_PER_SNAPSHOT_DEFAULT);
+ node.putLong(PREF_DELTA_EXPIRATION, PREF_DELTA_EXPIRATION_DEFAULT);
+
+ // encoding defaults
+ node.put(ResourcesPlugin.PREF_ENCODING, PREF_ENCODING_DEFAULT);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Project.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1172 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.IMoveDeleteHook;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentTypeMatcher;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+
+public class Project extends Container implements IProject {
+
+ protected Project(IPath path, Workspace container) {
+ super(path, container);
+ }
+
+ protected void assertCreateRequirements(IProjectDescription description) throws CoreException {
+ checkDoesNotExist();
+ checkDescription(this, description, false);
+ URI location = description.getLocationURI();
+ if (location != null)
+ return;
+ //if the project is in the default location, need to check for collision with existing folder of different case
+ if (!Workspace.caseSensitive) {
+ IFileStore store = getStore();
+ IFileInfo localInfo = store.fetchInfo();
+ if (localInfo.exists()) {
+ String name = getLocalManager().getLocalName(store);
+ if (name != null && !store.getName().equals(name)) {
+ String msg = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+ throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), msg, null);
+ }
+ }
+ }
+ }
+
+ /*
+ * If the creation boolean is true then this method is being called on project creation.
+ * Otherwise it is being called via #setDescription. The difference is that we don't allow
+ * some description fields to change value after project creation. (e.g. project location)
+ */
+ protected MultiStatus basicSetDescription(ProjectDescription description, int updateFlags) {
+ String message = Messages.resources_projectDesc;
+ MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_WRITE_METADATA, message, null);
+ ProjectDescription current = internalGetDescription();
+ current.setComment(description.getComment());
+ // set the build order before setting the references or the natures
+ current.setBuildSpec(description.getBuildSpec(true));
+
+ // set the references before the natures
+ boolean flushOrder = false;
+ IProject[] oldReferences = current.getReferencedProjects();
+ IProject[] newReferences = description.getReferencedProjects();
+ if (!Arrays.equals(oldReferences, newReferences)) {
+ current.setReferencedProjects(newReferences);
+ flushOrder = true;
+ }
+ oldReferences = current.getDynamicReferences();
+ newReferences = description.getDynamicReferences();
+ if (!Arrays.equals(oldReferences, newReferences)) {
+ current.setDynamicReferences(newReferences);
+ flushOrder = true;
+ }
+
+ if (flushOrder)
+ workspace.flushBuildOrder();
+
+ // the natures last as this may cause recursive calls to setDescription.
+ if ((updateFlags & IResource.AVOID_NATURE_CONFIG) == 0)
+ workspace.getNatureManager().configureNatures(this, current, description, result);
+ else
+ current.setNatureIds(description.getNatureIds(false));
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#build(int, IProgressMonitor)
+ */
+ public void build(int trigger, IProgressMonitor monitor) throws CoreException {
+ internalBuild(trigger, null, null, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#build(int, String, Map, IProgressMonitor)
+ */
+ public void build(int trigger, String builderName, Map args, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(builderName);
+ internalBuild(trigger, builderName, args, monitor);
+ }
+
+ /**
+ * Checks that this resource is accessible. Typically this means that it
+ * exists. In the case of projects, they must also be open.
+ * If phantom is true, phantom resources are considered.
+ *
+ * @exception CoreException if this resource is not accessible
+ */
+ public void checkAccessible(int flags) throws CoreException {
+ super.checkAccessible(flags);
+ if (!isOpen(flags)) {
+ String message = NLS.bind(Messages.resources_mustBeOpen, getFullPath());
+ throw new ResourceException(IResourceStatus.PROJECT_NOT_OPEN, getFullPath(), message, null);
+ }
+ }
+
+ /**
+ * Checks validity of the given project description.
+ */
+ protected void checkDescription(IProject project, IProjectDescription desc, boolean moving) throws CoreException {
+ URI location = desc.getLocationURI();
+ String message = Messages.resources_invalidProjDesc;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_VALUE, message, null);
+ status.merge(workspace.validateName(desc.getName(), IResource.PROJECT));
+ if (moving) {
+ // if we got here from a move call then we should check the location in the description since
+ // its possible that we want to do a rename without moving the contents. (and we shouldn't
+ // throw an Overlapping mapping exception in this case) So if the source description's location
+ // is null (we are using the default) or if the locations aren't equal, then validate the location
+ // of the new description. Otherwise both locations aren't null and they are equal so ignore validation.
+ URI sourceLocation = internalGetDescription().getLocationURI();
+ if (sourceLocation == null || !sourceLocation.equals(location))
+ status.merge(workspace.validateProjectLocationURI(project, location));
+ } else
+ // otherwise continue on like before
+ status.merge(workspace.validateProjectLocationURI(project, location));
+ if (!status.isOK())
+ throw new ResourceException(status);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#close(IProgressMonitor)
+ */
+ public void close(IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String msg = NLS.bind(Messages.resources_closing_1, getName());
+ monitor.beginTask(msg, Policy.totalWork);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkExists(flags, true);
+ monitor.subTask(msg);
+ if (!isOpen(flags))
+ return;
+ // Signal that this resource is about to be closed. Do this at the very
+ // beginning so that infrastructure pieces have a chance to do clean up
+ // while the resources still exist.
+ workspace.beginOperation(true);
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, this));
+ // flush the build order early in case there is a problem
+ workspace.flushBuildOrder();
+ IProgressMonitor sub = Policy.subMonitorFor(monitor, Policy.opWork / 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL);
+ IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, this, sub);
+ internalClose();
+ monitor.worked(Policy.opWork / 2);
+ if (saveStatus != null && !saveStatus.isOK())
+ throw new ResourceException(saveStatus);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#copy(IPath, int, IProgressMonitor)
+ */
+ public void copy(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ // FIXME - the logic here for copying projects needs to be moved to Resource.copy
+ // so that IResource.copy(IPath,int,IProgressMonitor) works properly for
+ // projects and honours all update flags
+ monitor = Policy.monitorFor(monitor);
+ if (destination.segmentCount() == 1) {
+ // copy project to project
+ String projectName = destination.segment(0);
+ IProjectDescription desc = getDescription();
+ desc.setName(projectName);
+ desc.setLocation(null);
+ internalCopy(desc, updateFlags, monitor);
+ } else {
+ // will fail since we're trying to copy a project to a non-project
+ checkCopyRequirements(destination, IResource.PROJECT, updateFlags);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#copy(IProjectDescription, int, IProgressMonitor)
+ */
+ public void copy(IProjectDescription destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ // FIXME - the logic here for copying projects needs to be moved to Resource.copy
+ // so that IResource.copy(IProjectDescription,int,IProgressMonitor) works properly for
+ // projects and honours all update flags
+ Assert.isNotNull(destination);
+ internalCopy(destination, updateFlags, monitor);
+ }
+
+ protected void copyMetaArea(IProject source, IProject destination, IProgressMonitor monitor) throws CoreException {
+ IFileStore oldMetaArea = EFS.getFileSystem(EFS.SCHEME_FILE).getStore(workspace.getMetaArea().locationFor(source));
+ IFileStore newMetaArea = EFS.getFileSystem(EFS.SCHEME_FILE).getStore(workspace.getMetaArea().locationFor(destination));
+ oldMetaArea.copy(newMetaArea, EFS.NONE, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#create(IProgressMonitor)
+ */
+ public void create(IProgressMonitor monitor) throws CoreException {
+ create(null, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#create(IProjectDescription, IProgressMonitor)
+ */
+ public void create(IProjectDescription description, IProgressMonitor monitor) throws CoreException {
+ create(description, IResource.NONE, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#create(IProjectDescription, IProgressMonitor)
+ */
+ public void create(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask(Messages.resources_create, Policy.totalWork);
+ checkValidPath(path, PROJECT, false);
+ final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ if (description == null) {
+ description = new ProjectDescription();
+ description.setName(getName());
+ }
+ assertCreateRequirements(description);
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CREATE, this));
+ workspace.beginOperation(true);
+ workspace.createResource(this, updateFlags);
+ workspace.getMetaArea().create(this);
+ ProjectInfo info = (ProjectInfo) getResourceInfo(false, true);
+
+ // setup description to obtain project location
+ ProjectDescription desc = (ProjectDescription) ((ProjectDescription) description).clone();
+ desc.setLocationURI(FileUtil.canonicalURI(description.getLocationURI()));
+ desc.setName(getName());
+ internalSetDescription(desc, false);
+ // see if there potentially are already contents on disk
+ final boolean hasSavedDescription = getLocalManager().hasSavedDescription(this);
+ boolean hasContent = hasSavedDescription;
+ //if there is no project description, there might still be content on disk
+ if (!hasSavedDescription)
+ hasContent = getLocalManager().hasSavedContent(this);
+ try {
+ // look for a description on disk
+ if (hasSavedDescription) {
+ updateDescription();
+ //make sure the .location file is written
+ workspace.getMetaArea().writePrivateDescription(this);
+ } else {
+ //write out the project
+ writeDescription(IResource.FORCE);
+ }
+ } catch (CoreException e) {
+ workspace.deleteResource(this);
+ throw e;
+ }
+ // inaccessible projects have a null modification stamp.
+ // set this after setting the description as #setDescription
+ // updates the stamp
+ info.clearModificationStamp();
+ //if a project already had content on disk, mark the project as having unknown children
+ if (hasContent)
+ info.set(ICoreConstants.M_CHILDREN_UNKNOWN);
+ workspace.getSaveManager().requestSnapshot();
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#delete(boolean, boolean, IProgressMonitor)
+ */
+ public void delete(boolean deleteContent, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= deleteContent ? IResource.ALWAYS_DELETE_PROJECT_CONTENT : IResource.NEVER_DELETE_PROJECT_CONTENT;
+ delete(updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#delete(boolean, IProgressMonitor)
+ */
+ public void delete(boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ delete(updateFlags, monitor);
+ }
+
+ public void deleteResource(boolean convertToPhantom, MultiStatus status) throws CoreException {
+ super.deleteResource(convertToPhantom, status);
+ // Delete the project metadata.
+ workspace.getMetaArea().delete(this);
+ // Clear the history store.
+ clearHistory(null);
+ }
+
+ protected void fixupAfterMoveSource() throws CoreException {
+ workspace.deleteResource(this);
+ // check if we deleted a preferences file
+ ProjectPreferences.deleted(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see IProject#getContentTypeMatcher
+ */
+ public IContentTypeMatcher getContentTypeMatcher() throws CoreException {
+ return workspace.getContentDescriptionManager().getContentTypeMatcher(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IContainer#getDefaultCharset(boolean)
+ */
+ public String getDefaultCharset(boolean checkImplicit) {
+ // non-existing resources default to parent's charset
+ if (!exists())
+ return checkImplicit ? ResourcesPlugin.getEncoding() : null;
+ return workspace.getCharsetManager().getCharsetFor(getFullPath(), checkImplicit);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#getDescription()
+ */
+ public IProjectDescription getDescription() throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ ProjectDescription description = ((ProjectInfo) info).getDescription();
+ //if the project is currently in the middle of being created, the description might not be available yet
+ if (description == null)
+ checkAccessible(NULL_FLAG);
+ return (IProjectDescription) description.clone();
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#getNature(String)
+ */
+ public IProjectNature getNature(String natureID) throws CoreException {
+ // Has it already been initialized?
+ ProjectInfo info = (ProjectInfo) getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ IProjectNature nature = info.getNature(natureID);
+ if (nature == null) {
+ // Not initialized yet. Does this project have the nature?
+ if (!hasNature(natureID))
+ return null;
+ nature = workspace.getNatureManager().createNature(this, natureID);
+ info.setNature(natureID, nature);
+ }
+ return nature;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getParent()
+ */
+ public IContainer getParent() {
+ return workspace.getRoot();
+ }
+
+ /** (non-Javadoc)
+ * @see IProject#getPluginWorkingLocation(IPluginDescriptor)
+ * @deprecated
+ */
+ public IPath getPluginWorkingLocation(IPluginDescriptor plugin) {
+ if (plugin == null)
+ return null;
+ return getWorkingLocation(plugin.getUniqueIdentifier());
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getProject()
+ */
+ public IProject getProject() {
+ return this;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getProjectRelativePath()
+ */
+ public IPath getProjectRelativePath() {
+ return Path.EMPTY;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getRawLocation()
+ */
+ public IPath getRawLocation() {
+ ProjectDescription description = internalGetDescription();
+ return description == null ? null : description.getLocation();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getRawLocation()
+ */
+ public URI getRawLocationURI() {
+ ProjectDescription description = internalGetDescription();
+ return description == null ? null : description.getLocationURI();
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#getReferencedProjects()
+ */
+ public IProject[] getReferencedProjects() throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ ProjectDescription description = ((ProjectInfo) info).getDescription();
+ //if the project is currently in the middle of being created, the description might not be available yet
+ if (description == null)
+ checkAccessible(NULL_FLAG);
+ return description.getAllReferences(true);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#getReferencingProjects()
+ */
+ public IProject[] getReferencingProjects() {
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ List result = new ArrayList(projects.length);
+ for (int i = 0; i < projects.length; i++) {
+ Project project = (Project) projects[i];
+ if (!project.isAccessible())
+ continue;
+ ProjectDescription description = project.internalGetDescription();
+ if (description == null)
+ continue;
+ IProject[] references = description.getAllReferences(false);
+ for (int j = 0; j < references.length; j++)
+ if (references[j].equals(this)) {
+ result.add(projects[i]);
+ break;
+ }
+ }
+ return (IProject[]) result.toArray(new IProject[result.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getType()
+ */
+ public int getType() {
+ return PROJECT;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see IProject#getWorkingLocation(String)
+ */
+ public IPath getWorkingLocation(String id) {
+ if (id == null || !exists())
+ return null;
+ IPath result = workspace.getMetaArea().getWorkingLocation(this, id);
+ result.toFile().mkdirs();
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#hasNature(String)
+ */
+ public boolean hasNature(String natureID) throws CoreException {
+ checkAccessible(getFlags(getResourceInfo(false, false)));
+ // use #internal method to avoid copy but still throw an
+ // exception if the resource doesn't exist.
+ IProjectDescription desc = internalGetDescription();
+ if (desc == null)
+ checkAccessible(NULL_FLAG);
+ return desc.hasNature(natureID);
+ }
+
+ /**
+ * Implements all build methods on IProject.
+ */
+ protected void internalBuild(final int trigger, final String builderName, final Map args, IProgressMonitor monitor) throws CoreException {
+ workspace.run(new IWorkspaceRunnable() {
+ public void run(IProgressMonitor innerMonitor) throws CoreException {
+ innerMonitor = Policy.monitorFor(innerMonitor);
+ final ISchedulingRule rule = workspace.getRoot();
+ try {
+ innerMonitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ try {
+ workspace.prepareOperation(rule, innerMonitor);
+ if (!shouldBuild())
+ return;
+ workspace.beginOperation(true);
+ workspace.aboutToBuild(Project.this, trigger);
+ } finally {
+ workspace.endOperation(rule, false, innerMonitor);
+ }
+ final ISchedulingRule buildRule = workspace.getBuildManager().getRule(Project.this, trigger, builderName, args);
+ try {
+ IStatus result;
+ workspace.prepareOperation(buildRule, innerMonitor);
+ workspace.beginOperation(true);
+ result = workspace.getBuildManager().build(Project.this, trigger, builderName, args, Policy.subMonitorFor(innerMonitor, Policy.opWork));
+ if (!result.isOK())
+ throw new ResourceException(result);
+ } finally {
+ workspace.endOperation(buildRule, false, innerMonitor);
+ try {
+ workspace.prepareOperation(rule, innerMonitor);
+ workspace.beginOperation(true);
+ workspace.broadcastBuildEvent(Project.this, IResourceChangeEvent.POST_BUILD, trigger);
+ //building may close the tree, so open it
+ if (workspace.getElementTree().isImmutable())
+ workspace.newWorkingTree();
+ } finally {
+ workspace.endOperation(rule, false, Policy.subMonitorFor(innerMonitor, Policy.endOpWork));
+ }
+ }
+ } finally {
+ innerMonitor.done();
+ }
+ }
+ /**
+ * Returns whether this project should be built for a given trigger.
+ * @return <code>true</code> if the build should proceed, and <code>false</code> otherwise.
+ */
+ private boolean shouldBuild() {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (!exists(flags, true) || !isOpen(flags))
+ return false;
+ return true;
+ }
+
+ }, monitor);
+ }
+
+
+ /**
+ * Closes the project. This is called during restore when there is a failure
+ * to read the project description. Since it is called during workspace restore,
+ * it cannot start any operations.
+ */
+ protected void internalClose() throws CoreException {
+ workspace.flushBuildOrder();
+ getMarkerManager().removeMarkers(this, IResource.DEPTH_INFINITE);
+ // remove each member from the resource tree.
+ // DO NOT use resource.delete() as this will delete it from disk as well.
+ IResource[] members = members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < members.length; i++) {
+ Resource member = (Resource) members[i];
+ workspace.deleteResource(member);
+ }
+ // finally mark the project as closed.
+ ResourceInfo info = getResourceInfo(false, true);
+ info.clear(M_OPEN);
+ info.clearSessionProperties();
+ info.clearModificationStamp();
+ info.setSyncInfo(null);
+ }
+
+ protected void internalCopy(IProjectDescription destDesc, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_copying, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ String destName = destDesc.getName();
+ IPath destPath = new Path(destName).makeAbsolute();
+ Project destination = (Project) workspace.getRoot().getProject(destName);
+ final ISchedulingRule rule = workspace.getRuleFactory().copyRule(this, destination);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ // The following assert method throws CoreExceptions as stated in the IProject.copy API
+ // and assert for programming errors. See checkCopyRequirements for more information.
+ assertCopyRequirements(destPath, IResource.PROJECT, updateFlags);
+ checkDescription(destination, destDesc, false);
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_COPY, this, destination, updateFlags));
+
+ workspace.beginOperation(true);
+ getLocalManager().refresh(this, DEPTH_INFINITE, true, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
+
+ // close the property store so incorrect info is not copied to the destination
+ getPropertyManager().closePropertyStore(this);
+ getLocalManager().getHistoryStore().closeHistoryStore(this);
+
+ // copy the meta area for the project
+ copyMetaArea(this, destination, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
+
+ // copy just the project and not its children yet (tree node, properties)
+ internalCopyProjectOnly(destination, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
+
+ // set the description
+ destination.internalSetDescription(destDesc, false);
+
+ //create the directory for the new project
+ destination.getStore().mkdir(EFS.NONE, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
+
+ // call super.copy for each child (excluding project description file)
+ //make it a best effort copy
+ message = Messages.resources_copyProblem;
+ MultiStatus problems = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
+
+ IResource[] children = members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ final int childCount = children.length;
+ final int childWork = childCount > 1 ? Policy.opWork * 50 / 100 / (childCount - 1) : 0;
+ for (int i = 0; i < childCount; i++) {
+ IResource child = children[i];
+ if (!isProjectDescriptionFile(child)) {
+ try {
+ child.copy(destPath.append(child.getName()), updateFlags, Policy.subMonitorFor(monitor, childWork));
+ } catch (CoreException e) {
+ problems.merge(e.getStatus());
+ }
+ }
+ }
+
+ // write out the new project description to the meta area
+ try {
+ destination.writeDescription(IResource.FORCE);
+ } catch (CoreException e) {
+ try {
+ destination.delete((updateFlags & IResource.FORCE) != 0, null);
+ } catch (CoreException e2) {
+ // ignore and rethrow the exception that got us here
+ }
+ throw e;
+ }
+ monitor.worked(Policy.opWork * 5 / 100);
+
+ // refresh local
+ monitor.subTask(Messages.resources_updating);
+ getLocalManager().refresh(destination, DEPTH_INFINITE, true, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
+ if (!problems.isOK())
+ throw new ResourceException(problems);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /*
+ * Copies just the project and no children. Does NOT copy the meta area.
+ */
+ protected void internalCopyProjectOnly(IResource destination, IProgressMonitor monitor) throws CoreException {
+ // close the property store so bogus values aren't copied to the destination
+ getPropertyManager().closePropertyStore(this);
+ getLocalManager().getHistoryStore().closeHistoryStore(this);
+ // copy the tree and properties
+ workspace.copyTree(this, destination.getFullPath(), IResource.DEPTH_ZERO, IResource.NONE, false);
+ getPropertyManager().copy(this, destination, IResource.DEPTH_ZERO);
+
+ ProjectInfo info = (ProjectInfo) ((Resource) destination).getResourceInfo(false, true);
+
+ //clear properties, markers, and description for the new project, because they shouldn't be copied.
+ info.description = null;
+ info.natures = null;
+ info.setMarkers(null);
+ info.clearSessionProperties();
+ }
+
+ /**
+ * This is an internal helper method. This implementation is different from the API
+ * method getDescription(). This one does not check the project accessibility. It exists
+ * in order to prevent "chicken and egg" problems in places like the project creation.
+ * It may return null.
+ */
+ public ProjectDescription internalGetDescription() {
+ ProjectInfo info = (ProjectInfo) getResourceInfo(false, false);
+ if (info == null)
+ return null;
+ return info.getDescription();
+ }
+
+ /**
+ * Sets this project's description to the given value. This is the body of the
+ * corresponding API method but is needed separately since it is used
+ * during workspace restore (i.e., when you cannot do an operation)
+ */
+ void internalSetDescription(IProjectDescription value, boolean incrementContentId) {
+ ProjectInfo info = (ProjectInfo) getResourceInfo(false, true);
+ info.setDescription((ProjectDescription) value);
+ getLocalManager().setLocation(this, info, value.getLocationURI());
+ if (incrementContentId) {
+ info.incrementContentId();
+ //if the project is not accessible, stamp will be null and should remain null
+ if (info.getModificationStamp() != NULL_STAMP)
+ workspace.updateModificationStamp(info);
+ }
+ }
+
+ public void internalSetLocal(boolean flag, int depth) throws CoreException {
+ // do nothing for projects, but call for its children
+ if (depth == IResource.DEPTH_ZERO)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ // get the children via the workspace since we know that this
+ // resource exists (it is local).
+ IResource[] children = getChildren(IResource.NONE);
+ for (int i = 0; i < children.length; i++)
+ ((Resource) children[i]).internalSetLocal(flag, depth);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isAccessible()
+ */
+ public boolean isAccessible() {
+ return isOpen();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.Resource#isDerived(int)
+ */
+ public boolean isDerived(int options) {
+ //projects are never derived
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isLinked(int)
+ */
+ public boolean isLinked(int options) {
+ return false;//projects are never linked
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isTeamPrivateMember(int)
+ */
+ public boolean isTeamPrivateMember(int options) {
+ return false;//projects are never team private members
+ }
+
+ /**
+ * @see IResource#isLocal(int)
+ * @deprecated
+ */
+ public boolean isLocal(int depth) {
+ // the flags parameter is ignored for projects so pass anything
+ return isLocal(-1, depth);
+ }
+
+ /**
+ * @see IResource#isLocal(int)
+ * @deprecated
+ */
+ public boolean isLocal(int flags, int depth) {
+ // don't check the flags....projects are always local
+ if (depth == DEPTH_ZERO)
+ return true;
+ if (depth == DEPTH_ONE)
+ depth = DEPTH_ZERO;
+ // get the children via the workspace since we know that this
+ // resource exists (it is local).
+ IResource[] children = getChildren(IResource.NONE);
+ for (int i = 0; i < children.length; i++)
+ if (!children[i].isLocal(depth))
+ return false;
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#isNatureEnabled(String)
+ */
+ public boolean isNatureEnabled(String natureId) throws CoreException {
+ checkAccessible(getFlags(getResourceInfo(false, false)));
+ return workspace.getNatureManager().isNatureEnabled(this, natureId);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#isOpen()
+ */
+ public boolean isOpen() {
+ ResourceInfo info = getResourceInfo(false, false);
+ return isOpen(getFlags(info));
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#isOpen()
+ */
+ public boolean isOpen(int flags) {
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_OPEN);
+ }
+
+ /**
+ * Returns true if this resource represents the project description file, and
+ * false otherwise.
+ */
+ protected boolean isProjectDescriptionFile(IResource resource) {
+ return resource.getType() == IResource.FILE && resource.getFullPath().segmentCount() == 2 && resource.getName().equals(IProjectDescription.DESCRIPTION_FILE_NAME);
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#move(IProjectDescription, boolean, IProgressMonitor)
+ */
+ public void move(IProjectDescription destination, boolean force, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(destination);
+ move(destination, force ? IResource.FORCE : IResource.NONE, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#move(IProjectDescription, int, IProgressMonitor)
+ */
+ public void move(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(description);
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_moving, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ IProject destination = workspace.getRoot().getProject(description.getName());
+ final ISchedulingRule rule = workspace.getRuleFactory().moveRule(this, destination);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ // The following assert method throws CoreExceptions as stated in the IResource.move API
+ // and assert for programming errors. See checkMoveRequirements for more information.
+ if (!getName().equals(description.getName())) {
+ IPath destPath = Path.ROOT.append(description.getName());
+ assertMoveRequirements(destPath, IResource.PROJECT, updateFlags);
+ }
+ checkDescription(destination, description, true);
+ workspace.beginOperation(true);
+ message = Messages.resources_moveProblem;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
+ WorkManager workManager = workspace.getWorkManager();
+ ResourceTree tree = new ResourceTree(getLocalManager(), workManager.getLock(), status, updateFlags);
+ IMoveDeleteHook hook = workspace.getMoveDeleteHook();
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_MOVE, this, destination, updateFlags));
+ int depth = 0;
+ try {
+ depth = workManager.beginUnprotected();
+ if (!hook.moveProject(tree, this, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2)))
+ tree.standardMoveProject(this, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2));
+ } finally {
+ workManager.endUnprotected(depth);
+ }
+ // Invalidate the tree for further use by clients.
+ tree.makeInvalid();
+ if (!tree.getStatus().isOK())
+ throw new ResourceException(tree.getStatus());
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#open(IProgressMonitor)
+ */
+ public void open(int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String msg = NLS.bind(Messages.resources_opening_1, getName());
+ monitor.beginTask(msg, Policy.totalWork);
+ monitor.subTask(msg);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ ProjectInfo info = (ProjectInfo) getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkExists(flags, true);
+ if (isOpen(flags))
+ return;
+
+ workspace.beginOperation(true);
+ // flush the build order early in case there is a problem
+ workspace.flushBuildOrder();
+ info = (ProjectInfo) getResourceInfo(false, true);
+ info.set(M_OPEN);
+ //clear the unknown children immediately to avoid background refresh
+ boolean unknownChildren = info.isSet(M_CHILDREN_UNKNOWN);
+ if (unknownChildren)
+ info.clear(M_CHILDREN_UNKNOWN);
+ // the M_USED flag is used to indicate the difference between opening a project
+ // for the first time and opening it from a previous close (restoring it from disk)
+ boolean used = info.isSet(M_USED);
+ boolean minorIssuesDuringRestore = false;
+ if (used) {
+ minorIssuesDuringRestore = workspace.getSaveManager().restore(this, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
+ } else {
+ info.set(M_USED);
+ //reconcile any links in the project description
+ IStatus result = reconcileLinks(info.getDescription());
+ if (!result.isOK())
+ throw new CoreException(result);
+ workspace.updateModificationStamp(info);
+ monitor.worked(Policy.opWork * 20 / 100);
+ }
+ startup();
+ //request a refresh if the project is new and has unknown members on disk
+ // or restore of the project is not fully succesfull
+ if ((!used && unknownChildren) || !minorIssuesDuringRestore) {
+ //refresh either in background or foreground
+ if ((updateFlags & IResource.BACKGROUND_REFRESH) != 0) {
+ workspace.refreshManager.refresh(this);
+ monitor.worked(Policy.opWork * 80 / 100);
+ } else {
+ refreshLocal(IResource.DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 80 / 100));
+ }
+ }
+ //creation of this project may affect overlapping resources
+ workspace.getAliasManager().updateAliases(this, getStore(), IResource.DEPTH_INFINITE, monitor);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#open(IProgressMonitor)
+ */
+ public void open(IProgressMonitor monitor) throws CoreException {
+ open(IResource.NONE, monitor);
+ }
+
+ /**
+ * The project description file has changed on disk, resulting in a changed
+ * set of linked resources. Perform the necessary creations and deletions of
+ * links to bring the links in sync with those described in the project description.
+ * @param newDescription the new project description that may have
+ * changed link descriptions.
+ * @return status ok if everything went well, otherwise an ERROR multi-status
+ * describing the problems encountered.
+ */
+ public IStatus reconcileLinks(ProjectDescription newDescription) {
+ HashMap newLinks = newDescription.getLinks();
+ String msg = Messages.links_errorLinkReconcile;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.OPERATION_FAILED, msg, null);
+ //walk over old linked resources and remove those that are no longer defined
+ ProjectDescription oldDescription = internalGetDescription();
+ if (oldDescription != null) {
+ HashMap oldLinks = oldDescription.getLinks();
+ if (oldLinks != null) {
+ for (Iterator it = oldLinks.values().iterator(); it.hasNext();) {
+ LinkDescription oldLink = (LinkDescription) it.next();
+ Resource oldLinkResource = (Resource) findMember(oldLink.getProjectRelativePath());
+ if (oldLinkResource == null || !oldLinkResource.isLinked())
+ continue;
+ LinkDescription newLink = null;
+ if (newLinks != null)
+ newLink = (LinkDescription) newLinks.get(oldLink.getProjectRelativePath());
+ //if the new link is missing, or has different (raw) location or gender, then remove old link
+ if (newLink == null || !newLink.getLocationURI().equals(oldLinkResource.getRawLocationURI()) || newLink.getType() != oldLinkResource.getType()) {
+ try {
+ oldLinkResource.delete(IResource.NONE, null);
+ //refresh the resource, because removing a link can reveal a previously hidden resource in parent
+ oldLinkResource.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e) {
+ status.merge(e.getStatus());
+ }
+ }
+ }
+ }
+ }
+ //walk over new links and create if necessary
+ if (newLinks == null)
+ return status;
+ //sort links to avoid creating nested links before their parents
+ List sortedLinks = new ArrayList(newLinks.values());
+ Collections.sort(sortedLinks);
+ for (Iterator it = sortedLinks.iterator(); it.hasNext();) {
+ LinkDescription newLink = (LinkDescription) it.next();
+ try {
+ Resource toLink = workspace.newResource(getFullPath().append(newLink.getProjectRelativePath()), newLink.getType());
+ IContainer parent = toLink.getParent();
+ if (parent != null && !parent.exists() && parent.getType() == FOLDER)
+ ((Folder) parent).ensureExists(Policy.monitorFor(null));
+ if (!toLink.exists() || !toLink.isLinked())
+ toLink.createLink(newLink.getLocationURI(), IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null);
+ } catch (CoreException e) {
+ status.merge(e.getStatus());
+ }
+ }
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ */
+ public void setDescription(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ // FIXME - update flags should be honored:
+ // KEEP_HISTORY means capture .project file in local history
+ // FORCE means overwrite any existing .project file
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask(Messages.resources_setDesc, Policy.totalWork);
+ ISchedulingRule rule = null;
+ if ((updateFlags & IResource.AVOID_NATURE_CONFIG) != 0)
+ rule = workspace.getRuleFactory().modifyRule(this);
+ else
+ rule = workspace.getRoot();
+ try {
+ //need to use root rule because nature configuration calls third party code
+ workspace.prepareOperation(rule, monitor);
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ //if nothing has changed, we don't need to do anything
+ ProjectDescription oldDescription = internalGetDescription();
+ ProjectDescription newDescription = (ProjectDescription) description;
+ boolean hasPublicChanges = oldDescription.hasPublicChanges(newDescription);
+ boolean hasPrivateChanges = oldDescription.hasPrivateChanges(newDescription);
+ if (!hasPublicChanges && !hasPrivateChanges)
+ return;
+ checkDescription(this, newDescription, false);
+ //If we're out of sync and !FORCE, then fail.
+ //If the file is missing, we want to write the new description then throw an exception.
+ boolean hadSavedDescription = true;
+ if (((updateFlags & IResource.FORCE) == 0)) {
+ hadSavedDescription = getLocalManager().hasSavedDescription(this);
+ if (hadSavedDescription && !getLocalManager().isDescriptionSynchronized(this)) {
+ String message = NLS.bind(Messages.resources_projectDescSync, getName());
+ throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, getFullPath(), message, null);
+ }
+ }
+ //see if we have an old .prj file
+ if (!hadSavedDescription)
+ hadSavedDescription = workspace.getMetaArea().hasSavedProject(this);
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CHANGE, this));
+ workspace.beginOperation(true);
+ MultiStatus status = basicSetDescription(newDescription, updateFlags);
+ if (hadSavedDescription && !status.isOK())
+ throw new CoreException(status);
+ //write the new description to the .project file
+ writeDescription(oldDescription, updateFlags, hasPublicChanges, hasPrivateChanges);
+ //increment the content id even for private changes
+ info = getResourceInfo(false, true);
+ info.incrementContentId();
+ workspace.updateModificationStamp(info);
+ if (!hadSavedDescription) {
+ String msg = NLS.bind(Messages.resources_missingProjectMetaRepaired, getName());
+ status.merge(new ResourceStatus(IResourceStatus.MISSING_DESCRIPTION_REPAIRED, getFullPath(), msg));
+ }
+ if (!status.isOK())
+ throw new CoreException(status);
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#setDescription(IProjectDescription, IProgressMonitor)
+ */
+ public void setDescription(IProjectDescription description, IProgressMonitor monitor) throws CoreException {
+ // funnel all operations to central method
+ setDescription(description, IResource.KEEP_HISTORY, monitor);
+ }
+
+ /**
+ * Restore the non-persisted state for the project. For example, read and set
+ * the description from the local meta area. Also, open the property store etc.
+ * This method is used when an open project is restored and so emulates
+ * the behaviour of open().
+ */
+ protected void startup() throws CoreException {
+ if (!isOpen())
+ return;
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_OPEN, this));
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#touch(IProgressMonitor)
+ */
+ public void touch(IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_touch, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CHANGE, this));
+ workspace.beginOperation(true);
+ super.touch(Policy.subMonitorFor(monitor, Policy.opWork));
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * The project description file on disk is better than the description in memory.
+ * Make sure the project description in memory is synchronized with the
+ * description file contents.
+ */
+ protected void updateDescription() throws CoreException {
+ if (ProjectDescription.isWriting)
+ return;
+ ProjectDescription.isReading = true;
+ try {
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CHANGE, this));
+ ProjectDescription description = getLocalManager().read(this, false);
+ //links can only be created if the project is open
+ IStatus result = null;
+ if (isOpen())
+ result = reconcileLinks(description);
+ internalSetDescription(description, true);
+ if (result != null && !result.isOK())
+ throw new CoreException(result);
+ } finally {
+ ProjectDescription.isReading = false;
+ }
+ }
+
+ /**
+ * Writes the project's current description file to disk.
+ */
+ public void writeDescription(int updateFlags) throws CoreException {
+ writeDescription(internalGetDescription(), updateFlags, true, true);
+ }
+
+ /**
+ * Writes the project description file to disk. This is the only method
+ * that should ever be writing the description, because it ensures that
+ * the description isn't then immediately discovered as an incoming
+ * change and read back from disk.
+ * @param description The description to write
+ * @param updateFlags The write operation update flags
+ * @param hasPublicChanges Whether the public sections of the description have changed
+ * @param hasPrivateChanges Whether the private sections of the description have changed
+ * @exception CoreException On failure to write the description
+ */
+ public void writeDescription(IProjectDescription description, int updateFlags, boolean hasPublicChanges, boolean hasPrivateChanges) throws CoreException {
+ if (ProjectDescription.isReading)
+ return;
+ ProjectDescription.isWriting = true;
+ try {
+ getLocalManager().internalWrite(this, description, updateFlags, hasPublicChanges, hasPrivateChanges);
+ } finally {
+ ProjectDescription.isWriting = false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectContentTypes.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.utils.Cache;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.*;
+import org.eclipse.core.runtime.content.IContentTypeManager.ISelectionPolicy;
+import org.eclipse.core.runtime.preferences.*;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * Manages project-specific content type behavior.
+ *
+ * @see ContentDescriptionManager
+ * @see IContentTypeManager.ISelectionPolicy
+ * @since 3.1
+ */
+public class ProjectContentTypes {
+
+ /**
+ * A project-aware content type selection policy.
+ * This class is also a dynamic scope context that will delegate to either
+ * project or instance scope depending on whether project specific settings were enabled
+ * for the project in question.
+ */
+ private class ProjectContentTypeSelectionPolicy implements ISelectionPolicy, IScopeContext {
+ // corresponding project
+ private Project project;
+ // cached project scope
+ private IScopeContext projectScope;
+
+ public ProjectContentTypeSelectionPolicy(Project project) {
+ this.project = project;
+ this.projectScope = new ProjectScope(project);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof IScopeContext))
+ return false;
+ IScopeContext other = (IScopeContext) obj;
+ if (!getName().equals(other.getName()))
+ return false;
+ IPath location = getLocation();
+ return location == null ? other.getLocation() == null : location.equals(other.getLocation());
+ }
+
+ private IScopeContext getDelegate() {
+ if (!usesContentTypePreferences(project.getName()))
+ return ProjectContentTypes.INSTANCE_SCOPE;
+ return projectScope;
+ }
+
+ public IPath getLocation() {
+ return getDelegate().getLocation();
+ }
+
+ public String getName() {
+ return getDelegate().getName();
+ }
+
+ public IEclipsePreferences getNode(String qualifier) {
+ return getDelegate().getNode(qualifier);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ public IContentType[] select(IContentType[] candidates, boolean fileName, boolean content) {
+ return ProjectContentTypes.this.select(project, candidates, fileName, content);
+ }
+ }
+
+ private static final String CONTENT_TYPE_PREF_NODE = "content-types"; //$NON-NLS-1$
+
+ static final InstanceScope INSTANCE_SCOPE = new InstanceScope();
+
+ private static final String PREF_LOCAL_CONTENT_TYPE_SETTINGS = "enabled"; //$NON-NLS-1$
+ private static final Preferences PROJECT_SCOPE = Platform.getPreferencesService().getRootNode().node(ProjectScope.SCOPE);
+ private Cache contentTypesPerProject;
+ private Workspace workspace;
+
+ static boolean usesContentTypePreferences(String projectName) {
+ try {
+ // be careful looking up for our node so not to create any nodes as side effect
+ Preferences node = PROJECT_SCOPE;
+ //TODO once bug 90500 is fixed, should be simpler
+ // for now, take the long way
+ if (!node.nodeExists(projectName))
+ return false;
+ node = node.node(projectName);
+ if (!node.nodeExists(Platform.PI_RUNTIME))
+ return false;
+ node = node.node(Platform.PI_RUNTIME);
+ if (!node.nodeExists(CONTENT_TYPE_PREF_NODE))
+ return false;
+ node = node.node(CONTENT_TYPE_PREF_NODE);
+ return node.getBoolean(PREF_LOCAL_CONTENT_TYPE_SETTINGS, false);
+ } catch (BackingStoreException e) {
+ // exception treated when retrieving the project preferences
+ }
+ return false;
+ }
+
+ public ProjectContentTypes(Workspace workspace) {
+ this.workspace = workspace;
+ // keep cache small
+ this.contentTypesPerProject = new Cache(5, 30, 0.4);
+ }
+
+ /**
+ * Collect content types associated to the natures configured for the given project.
+ */
+ private Set collectAssociatedContentTypes(Project project) {
+ String[] enabledNatures = workspace.getNatureManager().getEnabledNatures(project);
+ if (enabledNatures.length == 0)
+ return Collections.EMPTY_SET;
+ Set related = new HashSet(enabledNatures.length);
+ for (int i = 0; i < enabledNatures.length; i++) {
+ ProjectNatureDescriptor descriptor = (ProjectNatureDescriptor) workspace.getNatureDescriptor(enabledNatures[i]);
+ if (descriptor == null)
+ // no descriptor found for the nature, skip it
+ continue;
+ String[] natureContentTypes = descriptor.getContentTypeIds();
+ for (int j = 0; j < natureContentTypes.length; j++)
+ // collect associate content types
+ related.add(natureContentTypes[j]);
+ }
+ return related;
+ }
+
+ public void contentTypePreferencesChanged(IProject project) {
+ final ProjectInfo info = (ProjectInfo) ((Project) project).getResourceInfo(false, false);
+ if (info != null)
+ info.setMatcher(null);
+ }
+
+ /**
+ * Creates a content type matcher for the given project. Takes natures and user settings into account.
+ */
+ private IContentTypeMatcher createMatcher(Project project) {
+ ProjectContentTypeSelectionPolicy projectContentTypeSelectionPolicy = new ProjectContentTypeSelectionPolicy(project);
+ return Platform.getContentTypeManager().getMatcher(projectContentTypeSelectionPolicy, projectContentTypeSelectionPolicy);
+ }
+
+ private Set getAssociatedContentTypes(Project project) {
+ final ResourceInfo info = project.getResourceInfo(false, false);
+ if (info == null)
+ // the project has been deleted
+ return null;
+ final String projectName = project.getName();
+ synchronized (contentTypesPerProject) {
+ Cache.Entry entry = contentTypesPerProject.getEntry(projectName);
+ if (entry != null)
+ // we have an entry...
+ if (entry.getTimestamp() == info.getContentId())
+ // ...and it is not stale, so just return it
+ return (Set) entry.getCached();
+ // no cached information found, have to collect associated content types
+ Set result = collectAssociatedContentTypes(project);
+ if (entry == null)
+ // there was no entry before - create one
+ entry = contentTypesPerProject.addEntry(projectName, result, info.getContentId());
+ else {
+ // just update the existing entry
+ entry.setTimestamp(info.getContentId());
+ entry.setCached(result);
+ }
+ return result;
+ }
+ }
+
+ public IContentTypeMatcher getMatcherFor(Project project) throws CoreException {
+ ProjectInfo info = (ProjectInfo) project.getResourceInfo(false, false);
+ //fail if project has been deleted concurrently
+ if (info == null)
+ project.checkAccessible(project.getFlags(info));
+ IContentTypeMatcher matcher = info.getMatcher();
+ if (matcher != null)
+ return matcher;
+ matcher = createMatcher(project);
+ info.setMatcher(matcher);
+ return matcher;
+ }
+
+ /**
+ * Implements project specific, nature-based selection policy. No content types are vetoed.
+ *
+ * The criteria for this policy is as follows:
+ * <ol>
+ * <li>associated content types should appear before non-associated content types</li>
+ * <li>otherwise, relative ordering should be preserved.</li>
+ * </ol>
+ *
+ * @see IContentTypeManager.ISelectionPolicy
+ */
+ final IContentType[] select(Project project, IContentType[] candidates, boolean fileName, boolean content) {
+ // since no vetoing is done here, don't go further if there is nothing to sort
+ if (candidates.length < 2)
+ return candidates;
+ final Set associated = getAssociatedContentTypes(project);
+ if (associated == null || associated.isEmpty())
+ // project has no content types associated
+ return candidates;
+ int associatedCount = 0;
+ for (int i = 0; i < candidates.length; i++)
+ // is it an associated content type?
+ if (associated.contains(candidates[i].getId())) {
+ // need to move it to the right spot (unless all types visited so far are associated as well)
+ if (associatedCount < i) {
+ final IContentType promoted = candidates[i];
+ // move all non-associated content types before it one one position up...
+ for (int j = i; j > associatedCount; j--)
+ candidates[j] = candidates[j - 1];
+ // ...so there is an empty spot for the content type we are promoting
+ candidates[associatedCount] = promoted;
+ }
+ associatedCount++;
+ }
+ return candidates;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectDescription.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,389 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Martin Oberhuber (Wind River) - [245937] setLinkLocation() detects non-change
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.events.BuildCommand;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IPath;
+
+public class ProjectDescription extends ModelObject implements IProjectDescription {
+ private static final ICommand[] EMPTY_COMMAND_ARRAY = new ICommand[0];
+ // constants
+ private static final IProject[] EMPTY_PROJECT_ARRAY = new IProject[0];
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ protected static boolean isReading = false;
+
+ //flags to indicate when we are in the middle of reading or writing a
+ // workspace description
+ //these can be static because only one description can be read at once.
+ protected static boolean isWriting = false;
+ protected ICommand[] buildSpec = EMPTY_COMMAND_ARRAY;
+ /*
+ * Cached union of static and dynamic references (duplicates omitted).
+ * This cache is not persisted.
+ */
+ protected IProject[] cachedRefs = null;
+ protected String comment = ""; //$NON-NLS-1$
+ protected IProject[] dynamicRefs = EMPTY_PROJECT_ARRAY;
+
+ /**
+ * Map of (IPath -> LinkDescription) pairs for each linked resource
+ * in this project, where IPath is the project relative path of the resource.
+ */
+ protected HashMap linkDescriptions = null;
+
+ // fields
+ protected URI location = null;
+ protected String[] natures = EMPTY_STRING_ARRAY;
+ protected IProject[] staticRefs = EMPTY_PROJECT_ARRAY;
+
+ public ProjectDescription() {
+ super();
+ }
+
+ public Object clone() {
+ ProjectDescription clone = (ProjectDescription) super.clone();
+ //don't want the clone to have access to our internal link locations table or builders
+ clone.linkDescriptions = null;
+ clone.buildSpec = getBuildSpec(true);
+ return clone;
+ }
+
+ /**
+ * Returns a copy of the given array with all duplicates removed
+ */
+ private IProject[] copyAndRemoveDuplicates(IProject[] projects) {
+ IProject[] result = new IProject[projects.length];
+ int count = 0;
+ next: for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ // scan to see if there are any other projects by the same name
+ for (int j = 0; j < count; j++)
+ if (project.equals(result[j]))
+ continue next;
+ // not found
+ result[count++] = project;
+ }
+ if (count < projects.length) {
+ //shrink array
+ IProject[] reduced = new IProject[count];
+ System.arraycopy(result, 0, reduced, 0, count);
+ return reduced;
+ }
+ return result;
+}
+ /**
+ * Returns the union of the description's static and dynamic project references,
+ * with duplicates omitted. The calculation is optimized by caching the result
+ */
+ public IProject[] getAllReferences(boolean makeCopy) {
+ if (cachedRefs == null) {
+ IProject[] statik = getReferencedProjects(false);
+ IProject[] dynamic = getDynamicReferences(false);
+ if (dynamic.length == 0) {
+ cachedRefs = statik;
+ } else if (statik.length == 0) {
+ cachedRefs = dynamic;
+ } else {
+ //combine all references
+ IProject[] result = new IProject[dynamic.length + statik.length];
+ System.arraycopy(statik, 0, result, 0, statik.length);
+ System.arraycopy(dynamic, 0, result, statik.length, dynamic.length);
+ cachedRefs = copyAndRemoveDuplicates(result);
+ }
+ }
+ //still need to copy the result to prevent tampering with the cache
+ return makeCopy ? (IProject[]) cachedRefs.clone() : cachedRefs;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#getBuildSpec()
+ */
+ public ICommand[] getBuildSpec() {
+ return getBuildSpec(true);
+ }
+
+ public ICommand[] getBuildSpec(boolean makeCopy) {
+ //thread safety: copy reference in case of concurrent write
+ ICommand[] oldCommands = this.buildSpec;
+ if (oldCommands == null)
+ return EMPTY_COMMAND_ARRAY;
+ if (!makeCopy)
+ return oldCommands;
+ ICommand[] result = new ICommand[oldCommands.length];
+ for (int i = 0; i < result.length; i++)
+ result[i] = (ICommand) ((BuildCommand) oldCommands[i]).clone();
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#getComment()
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#getDynamicReferences()
+ */
+ public IProject[] getDynamicReferences() {
+ return getDynamicReferences(true);
+ }
+
+ public IProject[] getDynamicReferences(boolean makeCopy) {
+ if (dynamicRefs == null)
+ return EMPTY_PROJECT_ARRAY;
+ return makeCopy ? (IProject[]) dynamicRefs.clone() : dynamicRefs;
+ }
+
+ /**
+ * Returns the link location for the given resource name. Returns null if
+ * no such link exists.
+ */
+ public URI getLinkLocationURI(IPath aPath) {
+ if (linkDescriptions == null)
+ return null;
+ LinkDescription desc = (LinkDescription) linkDescriptions.get(aPath);
+ return desc == null ? null : desc.getLocationURI();
+ }
+
+ /**
+ * Returns the map of link descriptions (IPath (project relative path) -> LinkDescription).
+ * Since this method is only used internally, it never creates a copy.
+ * Returns null if the project does not have any linked resources.
+ */
+ public HashMap getLinks() {
+ return linkDescriptions;
+ }
+
+ /**
+ * @see IProjectDescription#getLocation()
+ * @deprecated
+ */
+ public IPath getLocation() {
+ if (location == null)
+ return null;
+ return FileUtil.toPath(location);
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#getLocationURI()
+ */
+ public URI getLocationURI() {
+ return location;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#getNatureIds()
+ */
+ public String[] getNatureIds() {
+ return getNatureIds(true);
+ }
+
+ public String[] getNatureIds(boolean makeCopy) {
+ if (natures == null)
+ return EMPTY_STRING_ARRAY;
+ return makeCopy ? (String[]) natures.clone() : natures;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#getReferencedProjects()
+ */
+ public IProject[] getReferencedProjects() {
+ return getReferencedProjects(true);
+ }
+
+ public IProject[] getReferencedProjects(boolean makeCopy) {
+ if (staticRefs == null)
+ return EMPTY_PROJECT_ARRAY;
+ return makeCopy ? (IProject[]) staticRefs.clone() : staticRefs;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#hasNature(String)
+ */
+ public boolean hasNature(String natureID) {
+ String[] natureIDs = getNatureIds(false);
+ for (int i = 0; i < natureIDs.length; ++i)
+ if (natureIDs[i].equals(natureID))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns true if any private attributes of the description have changed.
+ * Private attributes are those that are not stored in the project description
+ * file (.project).
+ */
+ public boolean hasPrivateChanges(ProjectDescription description) {
+ if (!Arrays.equals(dynamicRefs, description.getDynamicReferences(false)))
+ return true;
+ IPath otherLocation = description.getLocation();
+ if (location == null)
+ return otherLocation != null;
+ return !location.equals(otherLocation);
+ }
+
+ /**
+ * Returns true if any public attributes of the description have changed.
+ * Public attributes are those that are stored in the project description
+ * file (.project).
+ */
+ public boolean hasPublicChanges(ProjectDescription description) {
+ if (!getName().equals(description.getName()))
+ return true;
+ if (!comment.equals(description.getComment()))
+ return true;
+ //don't bother optimizing if the order has changed
+ if (!Arrays.equals(buildSpec, description.getBuildSpec(false)))
+ return true;
+ if (!Arrays.equals(staticRefs, description.getReferencedProjects(false)))
+ return true;
+ if (!Arrays.equals(natures, description.getNatureIds(false)))
+ return true;
+ HashMap otherLinks = description.getLinks();
+ if (linkDescriptions == null)
+ return otherLinks != null;
+ return !linkDescriptions.equals(otherLinks);
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#newCommand()
+ */
+ public ICommand newCommand() {
+ return new BuildCommand();
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setBuildSpec(ICommand[])
+ */
+ public void setBuildSpec(ICommand[] value) {
+ Assert.isLegal(value != null);
+ //perform a deep copy in case clients perform further changes to the command
+ ICommand[] result = new ICommand[value.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (ICommand) ((BuildCommand) value[i]).clone();
+ //copy the reference to any builder instance from the old build spec
+ //to preserve builder states if possible.
+ for (int j = 0; j < buildSpec.length; j++) {
+ if (result[i].equals(buildSpec[j])) {
+ ((BuildCommand) result[i]).setBuilder(((BuildCommand) buildSpec[j]).getBuilder());
+ break;
+ }
+ }
+ }
+ buildSpec = result;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setComment(String)
+ */
+ public void setComment(String value) {
+ comment = value;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setDynamicReferences(IProject[])
+ */
+ public void setDynamicReferences(IProject[] value) {
+ Assert.isLegal(value != null);
+ dynamicRefs = copyAndRemoveDuplicates(value);
+ cachedRefs = null;
+ }
+
+ /**
+ * Sets the map of link descriptions (String name -> LinkDescription).
+ * Since this method is only used internally, it never creates a copy. May
+ * pass null if this project does not have any linked resources
+ */
+ public void setLinkDescriptions(HashMap linkDescriptions) {
+ this.linkDescriptions = linkDescriptions;
+ }
+
+ /**
+ * Sets the description of a link. Setting to a description of null will
+ * remove the link from the project description.
+ * @return <code>true</code> if the description was actually changed,
+ * <code>false</code> otherwise.
+ * @since 3.5 returns boolean (was void before)
+ */
+ public boolean setLinkLocation(IPath path, LinkDescription description) {
+ HashMap tempMap = linkDescriptions;
+ if (description != null) {
+ //addition or modification
+ if (tempMap == null)
+ tempMap = new HashMap(10);
+ else
+ //copy on write to protect against concurrent read
+ tempMap = (HashMap) tempMap.clone();
+ Object oldValue = tempMap.put(path, description);
+ if (oldValue!=null && description.equals(oldValue)) {
+ //not actually changed anything
+ return false;
+ }
+ linkDescriptions = tempMap;
+ } else {
+ //removal
+ if (tempMap == null)
+ return false;
+ //copy on write to protect against concurrent access
+ HashMap newMap = (HashMap) tempMap.clone();
+ Object oldValue = newMap.remove(path);
+ if (oldValue == null) {
+ //not actually changed anything
+ return false;
+ }
+ linkDescriptions = newMap.size() == 0 ? null : newMap;
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setLocation(IPath)
+ */
+ public void setLocation(IPath path) {
+ this.location = path == null ? null : URIUtil.toURI(path);
+ }
+
+ public void setLocationURI(URI location) {
+ this.location = location;
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setName(String)
+ */
+ public void setName(String value) {
+ super.setName(value);
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setNatureIds(String[])
+ */
+ public void setNatureIds(String[] value) {
+ natures = (String[]) value.clone();
+ }
+
+ /* (non-Javadoc)
+ * @see IProjectDescription#setReferencedProjects(IProject[])
+ */
+ public void setReferencedProjects(IProject[] value) {
+ Assert.isLegal(value != null);
+ staticRefs = copyAndRemoveDuplicates(value);
+ cachedRefs = null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectDescriptionReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,736 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+import javax.xml.parsers.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.events.BuildCommand;
+import org.eclipse.core.internal.localstore.SafeFileInputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.xml.sax.*;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class ProjectDescriptionReader extends DefaultHandler implements IModelObjectConstants {
+
+ //states
+ protected static final int S_BUILD_COMMAND = 0;
+ protected static final int S_BUILD_COMMAND_ARGUMENTS = 1;
+ protected static final int S_BUILD_COMMAND_NAME = 2;
+ protected static final int S_BUILD_COMMAND_TRIGGERS = 3;
+ protected static final int S_BUILD_SPEC = 4;
+ protected static final int S_DICTIONARY = 5;
+ protected static final int S_DICTIONARY_KEY = 6;
+ protected static final int S_DICTIONARY_VALUE = 7;
+ protected static final int S_INITIAL = 8;
+ protected static final int S_LINK = 9;
+ protected static final int S_LINK_LOCATION = 10;
+ protected static final int S_LINK_LOCATION_URI = 11;
+ protected static final int S_LINK_PATH = 12;
+ protected static final int S_LINK_TYPE = 13;
+ protected static final int S_LINKED_RESOURCES = 14;
+ protected static final int S_NATURE_NAME = 15;
+ protected static final int S_NATURES = 16;
+ protected static final int S_PROJECT_COMMENT = 17;
+ protected static final int S_PROJECT_DESC = 18;
+ protected static final int S_PROJECT_NAME = 19;
+ protected static final int S_PROJECTS = 20;
+ protected static final int S_REFERENCED_PROJECT_NAME = 21;
+
+ /**
+ * Singleton sax parser factory
+ */
+ private static SAXParserFactory singletonParserFactory;
+
+ /**
+ * Singleton sax parser
+ */
+ private static SAXParser singletonParser;
+
+ protected final StringBuffer charBuffer = new StringBuffer();
+
+ protected Stack objectStack;
+ protected MultiStatus problems;
+
+ /**
+ * The project we are reading the description for, or null if unknown.
+ */
+ private final IProject project;
+ // The project description we are creating.
+ ProjectDescription projectDescription = null;
+
+ protected int state = S_INITIAL;
+
+
+ /**
+ * Returns the SAXParser to use when parsing project description files.
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ */
+ private static synchronized SAXParser createParser() throws ParserConfigurationException, SAXException{
+ //the parser can't be used concurrently, so only use singleton when workspace is locked
+ if (!isWorkspaceLocked())
+ return createParserFactory().newSAXParser();
+ if (singletonParser == null) {
+ singletonParser = createParserFactory().newSAXParser();
+ }
+ return singletonParser;
+ }
+
+ /**
+ * Returns the SAXParserFactory to use when parsing project description files.
+ * @throws ParserConfigurationException
+ */
+ private static synchronized SAXParserFactory createParserFactory() throws ParserConfigurationException{
+ if (singletonParserFactory == null) {
+ singletonParserFactory = SAXParserFactory.newInstance();
+ singletonParserFactory.setNamespaceAware(true);
+ try {
+ singletonParserFactory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
+ } catch (SAXException e) {
+ // In case support for this feature is removed
+ }
+ }
+ return singletonParserFactory;
+ }
+
+ private static boolean isWorkspaceLocked() {
+ try {
+ return ((Workspace) ResourcesPlugin.getWorkspace()).getWorkManager().isLockAlreadyAcquired();
+ } catch (CoreException e) {
+ return false;
+ }
+ }
+
+
+ public ProjectDescriptionReader() {
+ this.project = null;
+ }
+
+ public ProjectDescriptionReader(IProject project) {
+ this.project = project;
+ }
+
+ /**
+ * @see ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] chars, int offset, int length) {
+ //accumulate characters and process them when endElement is reached
+ charBuffer.append(chars, offset, length);
+ }
+
+ /**
+ * End of an element that is part of a build command
+ */
+ private void endBuildCommandElement(String elementName) {
+ if (elementName.equals(BUILD_COMMAND)) {
+ // Pop this BuildCommand off the stack.
+ BuildCommand command = (BuildCommand) objectStack.pop();
+ // Add this BuildCommand to a array list of BuildCommands.
+ ArrayList commandList = (ArrayList) objectStack.peek();
+ commandList.add(command);
+ state = S_BUILD_SPEC;
+ }
+ }
+
+ /**
+ * End of an element that is part of a build spec
+ */
+ private void endBuildSpecElement(String elementName) {
+ if (elementName.equals(BUILD_SPEC)) {
+ // Pop off the array list of BuildCommands and add them to the
+ // ProjectDescription which is the next thing on the stack.
+ ArrayList commands = (ArrayList) objectStack.pop();
+ state = S_PROJECT_DESC;
+ if (commands.isEmpty())
+ return;
+ ICommand[] commandArray = ((ICommand[]) commands.toArray(new ICommand[commands.size()]));
+ projectDescription.setBuildSpec(commandArray);
+ }
+ }
+
+ /**
+ * End a build triggers element and set the triggers for the current
+ * build command element.
+ */
+ private void endBuildTriggersElement(String elementName) {
+ if (elementName.equals(BUILD_TRIGGERS)) {
+ state = S_BUILD_COMMAND;
+ BuildCommand command = (BuildCommand) objectStack.peek();
+ //presence of this element indicates the builder is configurable
+ command.setConfigurable(true);
+ //clear all existing values
+ command.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, false);
+ command.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, false);
+ command.setBuilding(IncrementalProjectBuilder.FULL_BUILD, false);
+ command.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, false);
+
+ //set new values according to value in the triggers element
+ StringTokenizer tokens = new StringTokenizer(charBuffer.toString(), ","); //$NON-NLS-1$
+ while (tokens.hasMoreTokens()) {
+ String next = tokens.nextToken();
+ if (next.toLowerCase().equals(TRIGGER_AUTO)) {
+ command.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, true);
+ } else if (next.toLowerCase().equals(TRIGGER_CLEAN)) {
+ command.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, true);
+ } else if (next.toLowerCase().equals(TRIGGER_FULL)) {
+ command.setBuilding(IncrementalProjectBuilder.FULL_BUILD, true);
+ } else if (next.toLowerCase().equals(TRIGGER_INCREMENTAL)) {
+ command.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, true);
+ }
+ }
+ }
+ }
+
+ /**
+ * End of a dictionary element
+ */
+ private void endDictionary(String elementName) {
+ if (elementName.equals(DICTIONARY)) {
+ // Pick up the value and then key off the stack and add them
+ // to the HashMap which is just below them on the stack.
+ // Leave the HashMap on the stack to pick up more key/value
+ // pairs if they exist.
+ String value = (String) objectStack.pop();
+ String key = (String) objectStack.pop();
+ ((HashMap) objectStack.peek()).put(key, value);
+ state = S_BUILD_COMMAND_ARGUMENTS;
+ }
+ }
+
+ private void endDictionaryKey(String elementName) {
+ if (elementName.equals(KEY)) {
+ // There is a value place holder on the top of the stack and
+ // a key place holder just below it.
+ String value = (String) objectStack.pop();
+ String oldKey = (String) objectStack.pop();
+ String newKey = charBuffer.toString();
+ if (oldKey != null && oldKey.length() != 0) {
+ parseProblem(NLS.bind(Messages.projRead_whichKey, oldKey, newKey));
+ objectStack.push(oldKey);
+ } else {
+ objectStack.push(newKey);
+ }
+ //push back the dictionary value
+ objectStack.push(value);
+ state = S_DICTIONARY;
+ }
+ }
+
+ private void endDictionaryValue(String elementName) {
+ if (elementName.equals(VALUE)) {
+ String newValue = charBuffer.toString();
+ // There is a value place holder on the top of the stack
+ String oldValue = (String) objectStack.pop();
+ if (oldValue != null && oldValue.length() != 0) {
+ parseProblem(NLS.bind(Messages.projRead_whichValue, oldValue, newValue));
+ objectStack.push(oldValue);
+ } else {
+ objectStack.push(newValue);
+ }
+ state = S_DICTIONARY;
+ }
+ }
+
+ /**
+ * @see ContentHandler#endElement(String, String, String)
+ */
+ public void endElement(String uri, String elementName, String qname) {
+ switch (state) {
+ case S_PROJECT_DESC :
+ // Don't think we need to do anything here.
+ break;
+ case S_PROJECT_NAME :
+ if (elementName.equals(NAME)) {
+ // Project names cannot have leading/trailing whitespace
+ // as they are IResource names.
+ projectDescription.setName(charBuffer.toString().trim());
+ state = S_PROJECT_DESC;
+ }
+ break;
+ case S_PROJECTS :
+ if (elementName.equals(PROJECTS)) {
+ endProjectsElement(elementName);
+ state = S_PROJECT_DESC;
+ }
+ break;
+ case S_DICTIONARY :
+ endDictionary(elementName);
+ break;
+ case S_BUILD_COMMAND_ARGUMENTS :
+ if (elementName.equals(ARGUMENTS)) {
+ // There is a hashmap on the top of the stack with the
+ // arguments (if any).
+ HashMap dictionaryArgs = (HashMap) objectStack.pop();
+ state = S_BUILD_COMMAND;
+ if (dictionaryArgs.isEmpty())
+ break;
+ // Below the hashMap on the stack, there is a BuildCommand.
+ ((BuildCommand) objectStack.peek()).setArguments(dictionaryArgs);
+ }
+ break;
+ case S_BUILD_COMMAND :
+ endBuildCommandElement(elementName);
+ break;
+ case S_BUILD_SPEC :
+ endBuildSpecElement(elementName);
+ break;
+ case S_BUILD_COMMAND_TRIGGERS :
+ endBuildTriggersElement(elementName);
+ break;
+ case S_NATURES :
+ endNaturesElement(elementName);
+ break;
+ case S_LINK :
+ endLinkElement(elementName);
+ break;
+ case S_LINKED_RESOURCES :
+ endLinkedResourcesElement(elementName);
+ return;
+ case S_PROJECT_COMMENT :
+ if (elementName.equals(COMMENT)) {
+ projectDescription.setComment(charBuffer.toString());
+ state = S_PROJECT_DESC;
+ }
+ break;
+ case S_REFERENCED_PROJECT_NAME :
+ if (elementName.equals(PROJECT)) {
+ //top of stack is list of project references
+ // Referenced projects are just project names and, therefore,
+ // are also IResource names and cannot have leading/trailing
+ // whitespace.
+ ((ArrayList) objectStack.peek()).add(charBuffer.toString().trim());
+ state = S_PROJECTS;
+ }
+ break;
+ case S_BUILD_COMMAND_NAME :
+ if (elementName.equals(NAME)) {
+ //top of stack is the build command
+ // A build command name is an extension id and
+ // cannot have leading/trailing whitespace.
+ ((BuildCommand) objectStack.peek()).setName(charBuffer.toString().trim());
+ state = S_BUILD_COMMAND;
+ }
+ break;
+ case S_DICTIONARY_KEY :
+ endDictionaryKey(elementName);
+ break;
+ case S_DICTIONARY_VALUE :
+ endDictionaryValue(elementName);
+ break;
+ case S_NATURE_NAME :
+ if (elementName.equals(NATURE)) {
+ //top of stack is list of nature names
+ // A nature name is an extension id and cannot
+ // have leading/trailing whitespace.
+ ((ArrayList) objectStack.peek()).add(charBuffer.toString().trim());
+ state = S_NATURES;
+ }
+ break;
+ case S_LINK_PATH :
+ endLinkPath(elementName);
+ break;
+ case S_LINK_TYPE :
+ endLinkType(elementName);
+ break;
+ case S_LINK_LOCATION :
+ endLinkLocation(elementName);
+ break;
+ case S_LINK_LOCATION_URI :
+ endLinkLocationURI(elementName);
+ break;
+ }
+ charBuffer.setLength(0);
+ }
+
+ /**
+ * End this group of linked resources and add them to the project description.
+ */
+ private void endLinkedResourcesElement(String elementName) {
+ if (elementName.equals(LINKED_RESOURCES)) {
+ HashMap linkedResources = (HashMap) objectStack.pop();
+ state = S_PROJECT_DESC;
+ if (linkedResources.isEmpty())
+ return;
+ projectDescription.setLinkDescriptions(linkedResources);
+ }
+ }
+
+ /**
+ * End a single linked resource and add it to the HashMap.
+ */
+ private void endLinkElement(String elementName) {
+ if (elementName.equals(LINK)) {
+ state = S_LINKED_RESOURCES;
+ // Pop off the link description
+ LinkDescription link = (LinkDescription) objectStack.pop();
+ // Make sure that you have something reasonable
+ IPath path = link.getProjectRelativePath();
+ int type = link.getType();
+ URI location = link.getLocationURI();
+ if (location == null) {
+ parseProblem(NLS.bind(Messages.projRead_badLinkLocation, path, Integer.toString(type)));
+ return;
+ }
+ if ((path == null) || path.segmentCount() == 0) {
+ parseProblem(NLS.bind(Messages.projRead_emptyLinkName, Integer.toString(type), location));
+ return;
+ }
+ if (type == -1) {
+ parseProblem(NLS.bind(Messages.projRead_badLinkType, path, location));
+ return;
+ }
+
+ // The HashMap of linked resources is the next thing on the stack
+ ((HashMap) objectStack.peek()).put(link.getProjectRelativePath(), link);
+ }
+ }
+
+ /**
+ * For backwards compatibility, link locations in the local file system are represented
+ * in the project description under the "location" tag.
+ * @param elementName
+ */
+ private void endLinkLocation(String elementName) {
+ if (elementName.equals(LOCATION)) {
+ // A link location is an URI. URIs cannot have leading/trailing whitespace
+ String newLocation = charBuffer.toString().trim();
+ // objectStack has a LinkDescription on it. Set the type on this LinkDescription.
+ URI oldLocation = ((LinkDescription) objectStack.peek()).getLocationURI();
+ if (oldLocation != null) {
+ parseProblem(NLS.bind(Messages.projRead_badLocation, oldLocation, newLocation));
+ } else {
+ ((LinkDescription) objectStack.peek()).setLocationURI(URIUtil.toURI(Path.fromPortableString(newLocation)));
+ }
+ state = S_LINK;
+ }
+ }
+
+ /**
+ * Link locations that are not stored in the local file system are represented
+ * in the project description under the "locationURI" tag.
+ * @param elementName
+ */
+ private void endLinkLocationURI(String elementName) {
+ if (elementName.equals(LOCATION_URI)) {
+ // A link location is an URI. URIs cannot have leading/trailing whitespace
+ String newLocation = charBuffer.toString().trim();
+ // objectStack has a LinkDescription on it. Set the type on this LinkDescription.
+ URI oldLocation = ((LinkDescription) objectStack.peek()).getLocationURI();
+ if (oldLocation != null) {
+ parseProblem(NLS.bind(Messages.projRead_badLocation, oldLocation, newLocation));
+ } else {
+ try {
+ ((LinkDescription) objectStack.peek()).setLocationURI(new URI(newLocation));
+ } catch (URISyntaxException e) {
+ String msg = Messages.projRead_failureReadingProjectDesc;
+ problems.add(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, msg, e));
+ }
+ }
+ state = S_LINK;
+ }
+ }
+
+ private void endLinkPath(String elementName) {
+ if (elementName.equals(NAME)) {
+ IPath newPath = new Path(charBuffer.toString());
+ // objectStack has a LinkDescription on it. Set the name
+ // on this LinkDescription.
+ IPath oldPath = ((LinkDescription) objectStack.peek()).getProjectRelativePath();
+ if (oldPath.segmentCount() != 0) {
+ parseProblem(NLS.bind(Messages.projRead_badLinkName, oldPath, newPath));
+ } else {
+ ((LinkDescription) objectStack.peek()).setPath(newPath);
+ }
+ state = S_LINK;
+ }
+ }
+
+ private void endLinkType(String elementName) {
+ if (elementName.equals(TYPE)) {
+ //FIXME we should handle this case by removing the entire link
+ //for now we default to a file link
+ int newType = IResource.FILE;
+ try {
+ // parseInt expects a string containing only numerics
+ // or a leading '-'. Ensure there is no leading/trailing
+ // whitespace.
+ newType = Integer.parseInt(charBuffer.toString().trim());
+ } catch (NumberFormatException e) {
+ log(e);
+ }
+ // objectStack has a LinkDescription on it. Set the type
+ // on this LinkDescription.
+ int oldType = ((LinkDescription) objectStack.peek()).getType();
+ if (oldType != -1) {
+ parseProblem(NLS.bind(Messages.projRead_badLinkType2, Integer.toString(oldType), Integer.toString(newType)));
+ } else {
+ ((LinkDescription) objectStack.peek()).setType(newType);
+ }
+ state = S_LINK;
+ }
+ }
+
+ /**
+ * End of an element that is part of a nature list
+ */
+ private void endNaturesElement(String elementName) {
+ if (elementName.equals(NATURES)) {
+ // Pop the array list of natures off the stack
+ ArrayList natures = (ArrayList) objectStack.pop();
+ state = S_PROJECT_DESC;
+ if (natures.size() == 0)
+ return;
+ String[] natureNames = (String[]) natures.toArray(new String[natures.size()]);
+ projectDescription.setNatureIds(natureNames);
+ }
+ }
+
+ /**
+ * End of an element that is part of a project references list
+ */
+ private void endProjectsElement(String elementName) {
+ // Pop the array list that contains all the referenced project names
+ ArrayList referencedProjects = (ArrayList) objectStack.pop();
+ if (referencedProjects.size() == 0)
+ // Don't bother adding an empty group of referenced projects to the
+ // project descriptor.
+ return;
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IProject[] projects = new IProject[referencedProjects.size()];
+ for (int i = 0; i < projects.length; i++) {
+ projects[i] = root.getProject((String) referencedProjects.get(i));
+ }
+ projectDescription.setReferencedProjects(projects);
+ }
+
+ /**
+ * @see ErrorHandler#error(SAXParseException)
+ */
+ public void error(SAXParseException error) {
+ log(error);
+ }
+
+ /**
+ * @see ErrorHandler#fatalError(SAXParseException)
+ */
+ public void fatalError(SAXParseException error) throws SAXException {
+ // ensure a null value is not passed as message to Status constructor (bug 42782)
+ String message = error.getMessage();
+ if (project != null)
+ message = NLS.bind(Messages.resources_readMeta, project.getName());
+ problems.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message == null ? "" : message, error)); //$NON-NLS-1$
+ throw error;
+ }
+
+ protected void log(Exception ex) {
+ String message = ex.getMessage();
+ if (project != null)
+ message = NLS.bind(Messages.resources_readMeta, project.getName());
+ problems.add(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message == null ? "" : message, ex)); //$NON-NLS-1$
+ }
+
+ private void parseProblem(String errorMessage) {
+ problems.add(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, errorMessage, null));
+ }
+
+ private void parseProjectDescription(String elementName) {
+ if (elementName.equals(NAME)) {
+ state = S_PROJECT_NAME;
+ return;
+ }
+ if (elementName.equals(COMMENT)) {
+ state = S_PROJECT_COMMENT;
+ return;
+ }
+ if (elementName.equals(PROJECTS)) {
+ state = S_PROJECTS;
+ // Push an array list on the object stack to hold the name
+ // of all the referenced projects. This array list will be
+ // popped off the stack, massaged into the right format
+ // and added to the project description when we hit the
+ // end element for PROJECTS.
+ objectStack.push(new ArrayList());
+ return;
+ }
+ if (elementName.equals(BUILD_SPEC)) {
+ state = S_BUILD_SPEC;
+ // Push an array list on the object stack to hold the build commands
+ // for this build spec. This array list will be popped off the stack,
+ // massaged into the right format and added to the project's build
+ // spec when we hit the end element for BUILD_SPEC.
+ objectStack.push(new ArrayList());
+ return;
+ }
+ if (elementName.equals(NATURES)) {
+ state = S_NATURES;
+ // Push an array list to hold all the nature names.
+ objectStack.push(new ArrayList());
+ return;
+ }
+ if (elementName.equals(LINKED_RESOURCES)) {
+ // Push a HashMap to collect all the links.
+ objectStack.push(new HashMap());
+ state = S_LINKED_RESOURCES;
+ return;
+ }
+ }
+
+ public ProjectDescription read(InputSource input) {
+ problems = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, Messages.projRead_failureReadingProjectDesc, null);
+ objectStack = new Stack();
+ state = S_INITIAL;
+ try {
+ createParser().parse(input, this);
+ } catch (ParserConfigurationException e) {
+ log(e);
+ } catch (IOException e) {
+ log(e);
+ } catch (SAXException e) {
+ log(e);
+ }
+ switch (problems.getSeverity()) {
+ case IStatus.ERROR :
+ Policy.log(problems);
+ return null;
+ case IStatus.WARNING :
+ case IStatus.INFO :
+ Policy.log(problems);
+ case IStatus.OK :
+ default :
+ return projectDescription;
+ }
+ }
+
+ /**
+ * Reads and returns a project description stored at the given location
+ */
+ public ProjectDescription read(IPath location) throws IOException {
+ BufferedInputStream file = null;
+ try {
+ file = new BufferedInputStream(new FileInputStream(location.toFile()));
+ return read(new InputSource(file));
+ } finally {
+ if (file != null)
+ file.close();
+ }
+ }
+
+ /**
+ * Reads and returns a project description stored at the given location, or
+ * temporary location.
+ */
+ public ProjectDescription read(IPath location, IPath tempLocation) throws IOException {
+ SafeFileInputStream file = new SafeFileInputStream(location.toOSString(), tempLocation.toOSString());
+ try {
+ return read(new InputSource(file));
+ } finally {
+ file.close();
+ }
+ }
+
+ /**
+ * @see ContentHandler#startElement(String, String, String, Attributes)
+ */
+ public void startElement(String uri, String elementName, String qname, Attributes attributes) throws SAXException {
+ //clear the character buffer at the start of every element
+ charBuffer.setLength(0);
+ switch (state) {
+ case S_INITIAL :
+ if (elementName.equals(PROJECT_DESCRIPTION)) {
+ state = S_PROJECT_DESC;
+ projectDescription = new ProjectDescription();
+ } else {
+ throw (new SAXException(NLS.bind(Messages.projRead_notProjectDescription, elementName)));
+ }
+ break;
+ case S_PROJECT_DESC :
+ parseProjectDescription(elementName);
+ break;
+ case S_PROJECTS :
+ if (elementName.equals(PROJECT)) {
+ state = S_REFERENCED_PROJECT_NAME;
+ }
+ break;
+ case S_BUILD_SPEC :
+ if (elementName.equals(BUILD_COMMAND)) {
+ state = S_BUILD_COMMAND;
+ objectStack.push(new BuildCommand());
+ }
+ break;
+ case S_BUILD_COMMAND :
+ if (elementName.equals(NAME)) {
+ state = S_BUILD_COMMAND_NAME;
+ } else if (elementName.equals(BUILD_TRIGGERS)) {
+ state = S_BUILD_COMMAND_TRIGGERS;
+ } else if (elementName.equals(ARGUMENTS)) {
+ state = S_BUILD_COMMAND_ARGUMENTS;
+ // Push a HashMap to hold all the key/value pairs which
+ // will become the argument list.
+ objectStack.push(new HashMap());
+ }
+ break;
+ case S_BUILD_COMMAND_ARGUMENTS :
+ if (elementName.equals(DICTIONARY)) {
+ state = S_DICTIONARY;
+ // Push 2 strings for the key/value pair to be read
+ objectStack.push(new String()); // key
+ objectStack.push(new String()); // value
+ }
+ break;
+ case S_DICTIONARY :
+ if (elementName.equals(KEY)) {
+ state = S_DICTIONARY_KEY;
+ } else if (elementName.equals(VALUE)) {
+ state = S_DICTIONARY_VALUE;
+ }
+ break;
+ case S_NATURES :
+ if (elementName.equals(NATURE)) {
+ state = S_NATURE_NAME;
+ }
+ break;
+ case S_LINKED_RESOURCES :
+ if (elementName.equals(LINK)) {
+ state = S_LINK;
+ // Push place holders for the name, type and location of
+ // this link.
+ objectStack.push(new LinkDescription());
+ }
+ break;
+ case S_LINK :
+ if (elementName.equals(NAME)) {
+ state = S_LINK_PATH;
+ } else if (elementName.equals(TYPE)) {
+ state = S_LINK_TYPE;
+ } else if (elementName.equals(LOCATION)) {
+ state = S_LINK_LOCATION;
+ } else if (elementName.equals(LOCATION_URI)) {
+ state = S_LINK_LOCATION_URI;
+ }
+ break;
+ }
+ }
+
+ /**
+ * @see ErrorHandler#warning(SAXParseException)
+ */
+ public void warning(SAXParseException error) {
+ log(error);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectInfo.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.HashMap;
+import org.eclipse.core.internal.events.BuildCommand;
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.content.IContentTypeMatcher;
+
+public class ProjectInfo extends ResourceInfo {
+
+ /** The description of this object */
+ protected ProjectDescription description = null;
+
+ /** The list of natures for this project */
+ protected HashMap natures = null;
+
+ /** The property store for this resource (used only by the compatibility fragment) */
+ protected Object propertyStore = null;
+
+ /** The content type matcher for this project. */
+ protected IContentTypeMatcher matcher = null;
+
+ /**
+ * Discards any stale state on this project after it has been moved. Builder
+ * instances must be cleared because they reference the old project handle.
+ */
+ public synchronized void fixupAfterMove() {
+ natures = null;
+ // note that the property store instance will be recreated lazily
+ propertyStore = null;
+ if (description != null) {
+ ICommand[] buildSpec = description.getBuildSpec(false);
+ for (int i = 0; i < buildSpec.length; i++)
+ ((BuildCommand) buildSpec[i]).setBuilder(null);
+ }
+ }
+
+ /**
+ * Returns the description associated with this info. The return value may be null.
+ */
+ public ProjectDescription getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns the content type matcher associated with this info. The return value may be null.
+ */
+ public IContentTypeMatcher getMatcher() {
+ return matcher;
+ }
+
+ public IProjectNature getNature(String natureId) {
+ // thread safety: (Concurrency001)
+ HashMap temp = natures;
+ if (temp == null)
+ return null;
+ return (IProjectNature) temp.get(natureId);
+ }
+
+ /**
+ * Returns the property store associated with this info. The return value may be null.
+ */
+ public Object getPropertyStore() {
+ return propertyStore;
+ }
+
+ /**
+ * Sets the description associated with this info. The value may be null.
+ */
+ public void setDescription(ProjectDescription value) {
+ if (description != null) {
+ //if we already have a description, assign the new
+ //build spec on top of the old one to ensure we maintain
+ //any existing builder instances in the old build commands
+ ICommand[] oldSpec = description.buildSpec;
+ ICommand[] newSpec = value.buildSpec;
+ value.buildSpec = oldSpec;
+ value.setBuildSpec(newSpec);
+ }
+ description = value;
+ }
+
+ /**
+ * Sets the content type matcher to be associated with this info. The value may be null.
+ */
+ public void setMatcher(IContentTypeMatcher matcher) {
+ this.matcher = matcher;
+ }
+
+ public synchronized void setNature(String natureId, IProjectNature value) {
+ // thread safety: (Concurrency001)
+ if (value == null) {
+ if (natures == null)
+ return;
+ HashMap temp = (HashMap) natures.clone();
+ temp.remove(natureId);
+ if (temp.isEmpty())
+ natures = null;
+ else
+ natures = temp;
+ } else {
+ HashMap temp = natures;
+ if (temp == null)
+ temp = new HashMap(5);
+ else
+ temp = (HashMap) natures.clone();
+ temp.put(natureId, value);
+ natures = temp;
+ }
+ }
+
+ /**
+ * Sets the property store associated with this info. The value may be null.
+ */
+ public void setPropertyStore(Object value) {
+ propertyStore = value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectNatureDescriptor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.ArrayList;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IProjectNatureDescriptor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ */
+public class ProjectNatureDescriptor implements IProjectNatureDescriptor {
+ protected String id;
+ protected String label;
+ protected String[] requiredNatures;
+ protected String[] natureSets;
+ protected String[] builderIds;
+ protected String[] contentTypeIds;
+ protected boolean allowLinking = true;
+
+ //descriptors that are in a dependency cycle are never valid
+ protected boolean hasCycle = false;
+ //colours used by cycle detection algorithm
+ protected byte colour = 0;
+
+ /**
+ * Creates a new descriptor based on the given extension markup.
+ * @exception CoreException if the given nature extension is not correctly formed.
+ */
+ protected ProjectNatureDescriptor(IExtension natureExtension) throws CoreException {
+ readExtension(natureExtension);
+ }
+
+ protected void fail() throws CoreException {
+ fail(NLS.bind(Messages.natures_invalidDefinition, id));
+ }
+
+ protected void fail(String reason) throws CoreException {
+ throw new ResourceException(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, reason, null));
+ }
+
+ /**
+ * Returns the IDs of the incremental builders that this nature claims to
+ * own. These builders do not necessarily exist in the registry.
+ */
+ public String[] getBuilderIds() {
+ return builderIds;
+ }
+
+ /**
+ * Returns the IDs of the content types this nature declares to
+ * have affinity with. These content types do not necessarily exist in the registry.
+ */
+ public String[] getContentTypeIds() {
+ return contentTypeIds;
+ }
+
+ /**
+ * @see IProjectNatureDescriptor#getNatureId()
+ */
+ public String getNatureId() {
+ return id;
+ }
+
+ /**
+ * @see IProjectNatureDescriptor#getLabel()
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @see IProjectNatureDescriptor#getRequiredNatureIds()
+ */
+ public String[] getRequiredNatureIds() {
+ return requiredNatures;
+ }
+
+ /**
+ * @see IProjectNatureDescriptor#getNatureSetIds()
+ */
+ public String[] getNatureSetIds() {
+ return natureSets;
+ }
+
+ /**
+ * @see IProjectNatureDescriptor#isLinkingAllowed()
+ */
+ public boolean isLinkingAllowed() {
+ return allowLinking;
+ }
+
+ /**
+ * Initialize this nature descriptor based on the provided extension point.
+ */
+ protected void readExtension(IExtension natureExtension) throws CoreException {
+ //read the extension
+ id = natureExtension.getUniqueIdentifier();
+ if (id == null) {
+ fail(Messages.natures_missingIdentifier);
+ }
+ label = natureExtension.getLabel();
+ IConfigurationElement[] elements = natureExtension.getConfigurationElements();
+ int count = elements.length;
+ ArrayList requiredList = new ArrayList(count);
+ ArrayList setList = new ArrayList(count);
+ ArrayList builderList = new ArrayList(count);
+ ArrayList contentTypeList = new ArrayList(count);
+ for (int i = 0; i < count; i++) {
+ IConfigurationElement element = elements[i];
+ String name = element.getName();
+ if (name.equalsIgnoreCase("requires-nature")) { //$NON-NLS-1$
+ String attribute = element.getAttribute("id"); //$NON-NLS-1$
+ if (attribute == null)
+ fail();
+ requiredList.add(attribute);
+ } else if (name.equalsIgnoreCase("one-of-nature")) { //$NON-NLS-1$
+ String attribute = element.getAttribute("id"); //$NON-NLS-1$
+ if (attribute == null)
+ fail();
+ setList.add(attribute);
+ } else if (name.equalsIgnoreCase("builder")) { //$NON-NLS-1$
+ String attribute = element.getAttribute("id"); //$NON-NLS-1$
+ if (attribute == null)
+ fail();
+ builderList.add(attribute);
+ } else if (name.equalsIgnoreCase("content-type")) { //$NON-NLS-1$
+ String attribute = element.getAttribute("id"); //$NON-NLS-1$
+ if (attribute == null)
+ fail();
+ contentTypeList.add(attribute);
+ } else if (name.equalsIgnoreCase("options")) { //$NON-NLS-1$
+ String attribute = element.getAttribute("allowLinking"); //$NON-NLS-1$
+ //when in doubt (missing attribute, wrong value) default to allow linking
+ allowLinking = !Boolean.FALSE.toString().equalsIgnoreCase(attribute);
+ }
+ }
+ requiredNatures = (String[]) requiredList.toArray(new String[requiredList.size()]);
+ natureSets = (String[]) setList.toArray(new String[setList.size()]);
+ builderIds = (String[]) builderList.toArray(new String[builderList.size()]);
+ contentTypeIds = (String[]) contentTypeList.toArray(new String[contentTypeList.size()]);
+ }
+
+ /**
+ * Prints out a string representation for debugging purposes only.
+ */
+ public String toString() {
+ return "ProjectNatureDescriptor(" + id + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectPreferences.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,596 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.preferences.EclipsePreferences;
+import org.eclipse.core.internal.preferences.ExportedPreferences;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IExportedPreferences;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * Represents a node in the Eclipse preference hierarchy which stores preference
+ * values for projects.
+ *
+ * @since 3.0
+ */
+public class ProjectPreferences extends EclipsePreferences {
+
+ class SortedProperties extends Properties {
+
+ class IteratorWrapper implements Enumeration {
+ Iterator iterator;
+
+ public IteratorWrapper(Iterator iterator) {
+ this.iterator = iterator;
+ }
+
+ public boolean hasMoreElements() {
+ return iterator.hasNext();
+ }
+
+ public Object nextElement() {
+ return iterator.next();
+ }
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ /* (non-Javadoc)
+ * @see java.util.Hashtable#keys()
+ */
+ public synchronized Enumeration keys() {
+ TreeSet set = new TreeSet();
+ for (Enumeration e = super.keys(); e.hasMoreElements();)
+ set.add(e.nextElement());
+ return new IteratorWrapper(set.iterator());
+ }
+ }
+
+ /**
+ * Cache which nodes have been loaded from disk
+ */
+ protected static Set loadedNodes = new HashSet();
+ private IFile file;
+ private boolean initialized = false;
+ /**
+ * Flag indicating that this node is currently reading values from disk,
+ * to avoid flushing during a read.
+ */
+ private boolean isReading;
+ /**
+ * Flag indicating that this node is currently writing values to disk,
+ * to avoid re-reading after the write completes.
+ */
+ private boolean isWriting;
+ private IEclipsePreferences loadLevel;
+ private IProject project;
+ private String qualifier;
+
+ // cache
+ private int segmentCount;
+
+ static void deleted(IFile file) throws CoreException {
+ IPath path = file.getFullPath();
+ int count = path.segmentCount();
+ if (count != 3)
+ return;
+ // check if we are in the .settings directory
+ if (!EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1)))
+ return;
+ Preferences root = Platform.getPreferencesService().getRootNode();
+ String project = path.segment(0);
+ String qualifier = path.removeFileExtension().lastSegment();
+ ProjectPreferences projectNode = (ProjectPreferences) root.node(ProjectScope.SCOPE).node(project);
+ // if the node isn't known then just return
+ try {
+ if (!projectNode.nodeExists(qualifier))
+ return;
+ } catch (BackingStoreException e) {
+ // ignore
+ }
+
+ // clear the preferences
+ clearNode(projectNode.node(qualifier));
+
+ // notifies the CharsetManager if needed
+ if (qualifier.equals(ResourcesPlugin.PI_RESOURCES))
+ preferencesChanged(file.getProject());
+ }
+
+ static void deleted(IFolder folder) throws CoreException {
+ IPath path = folder.getFullPath();
+ int count = path.segmentCount();
+ if (count != 2)
+ return;
+ // check if we are the .settings directory
+ if (!EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1)))
+ return;
+ Preferences root = Platform.getPreferencesService().getRootNode();
+ // The settings dir has been removed/moved so remove all project prefs
+ // for the resource.
+ String project = path.segment(0);
+ Preferences projectNode = root.node(ProjectScope.SCOPE).node(project);
+ // check if we need to notify the charset manager
+ boolean hasResourcesSettings = getFile(folder, ResourcesPlugin.PI_RESOURCES).exists();
+ // remove the preferences
+ removeNode(projectNode);
+ // notifies the CharsetManager
+ if (hasResourcesSettings)
+ preferencesChanged(folder.getProject());
+ }
+
+ /*
+ * The whole project has been removed so delete all of the project settings
+ */
+ static void deleted(IProject project) throws CoreException {
+ // The settings dir has been removed/moved so remove all project prefs
+ // for the resource. We have to do this now because (since we aren't
+ // synchronizing) there is short-circuit code that doesn't visit the
+ // children.
+ Preferences root = Platform.getPreferencesService().getRootNode();
+ Preferences projectNode = root.node(ProjectScope.SCOPE).node(project.getName());
+ // check if we need to notify the charset manager
+ boolean hasResourcesSettings = getFile(project, ResourcesPlugin.PI_RESOURCES).exists();
+ // remove the preferences
+ removeNode(projectNode);
+ // notifies the CharsetManager
+ if (hasResourcesSettings)
+ preferencesChanged(project);
+ }
+
+ static void deleted(IResource resource) throws CoreException {
+ switch (resource.getType()) {
+ case IResource.FILE :
+ deleted((IFile) resource);
+ return;
+ case IResource.FOLDER :
+ deleted((IFolder) resource);
+ return;
+ case IResource.PROJECT :
+ deleted((IProject) resource);
+ return;
+ }
+ }
+
+ /*
+ * Return the preferences file for the given folder and qualifier.
+ */
+ static IFile getFile(IFolder folder, String qualifier) {
+ Assert.isLegal(folder.getName().equals(DEFAULT_PREFERENCES_DIRNAME));
+ return folder.getFile(new Path(qualifier).addFileExtension(PREFS_FILE_EXTENSION));
+ }
+
+ /*
+ * Return the preferences file for the given project and qualifier.
+ */
+ static IFile getFile(IProject project, String qualifier) {
+ return project.getFile(new Path(DEFAULT_PREFERENCES_DIRNAME).append(qualifier).addFileExtension(PREFS_FILE_EXTENSION));
+ }
+
+ private static Properties loadProperties(IFile file) throws BackingStoreException {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Loading preferences from file: " + file.getFullPath()); //$NON-NLS-1$
+ Properties result = new Properties();
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(file.getContents(true));
+ result.load(input);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.preferences_loadException, file.getFullPath());
+ log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.preferences_loadException, file.getFullPath());
+ log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ } finally {
+ FileUtil.safeClose(input);
+ }
+ return result;
+ }
+
+ private static void preferencesChanged(IProject project) {
+ Workspace workspace = ((Workspace) ResourcesPlugin.getWorkspace());
+ workspace.getCharsetManager().projectPreferencesChanged(project);
+ workspace.getContentDescriptionManager().projectPreferencesChanged(project);
+ }
+
+ private static void read(ProjectPreferences node, IFile file) throws BackingStoreException, CoreException {
+ if (file == null || !file.exists()) {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Unable to determine preference file or file does not exist for node: " + node.absolutePath()); //$NON-NLS-1$
+ return;
+ }
+ Properties fromDisk = loadProperties(file);
+ // no work to do
+ if (fromDisk.isEmpty())
+ return;
+ // create a new node to store the preferences in.
+ IExportedPreferences myNode = (IExportedPreferences) ExportedPreferences.newRoot().node(node.absolutePath());
+ convertFromProperties((EclipsePreferences) myNode, fromDisk, false);
+ //flag that we are currently reading, to avoid unnecessary writing
+ boolean oldIsReading = node.isReading;
+ node.isReading = true;
+ try {
+ Platform.getPreferencesService().applyPreferences(myNode);
+ } finally {
+ node.isReading = oldIsReading;
+ }
+ }
+
+ static void removeNode(Preferences node) throws CoreException {
+ String message = NLS.bind(Messages.preferences_removeNodeException, node.absolutePath());
+ try {
+ node.removeNode();
+ } catch (BackingStoreException e) {
+ IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e);
+ throw new CoreException(status);
+ }
+ removeLoadedNodes(node);
+ }
+
+ static void clearNode(Preferences node) throws CoreException {
+ // if the underlying properties file was deleted, clear the values and remove
+ // it from the list of loaded nodes, keep the node as it might still be referenced
+ try {
+ clearAll(node);
+ } catch (BackingStoreException e) {
+ String message = NLS.bind(Messages.preferences_clearNodeException, node.absolutePath());
+ IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e);
+ throw new CoreException(status);
+ }
+ removeLoadedNodes(node);
+ }
+
+ private static void clearAll(Preferences node) throws BackingStoreException {
+ node.clear();
+ String[] names = node.childrenNames();
+ for (int i = 0; i < names.length; i++) {
+ clearAll(node.node(names[i]));
+ }
+ }
+
+ private static synchronized void removeLoadedNodes(Preferences node){
+ String path = node.absolutePath();
+ for (Iterator i = loadedNodes.iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ if (key.startsWith(path))
+ i.remove();
+ }
+ }
+
+ public static void updatePreferences(IFile file) throws CoreException {
+ IPath path = file.getFullPath();
+ // if we made it this far we are inside /project/.settings and might
+ // have a change to a preference file
+ if (!PREFS_FILE_EXTENSION.equals(path.getFileExtension()))
+ return;
+
+ String project = path.segment(0);
+ String qualifier = path.removeFileExtension().lastSegment();
+ Preferences root = Platform.getPreferencesService().getRootNode();
+ Preferences node = root.node(ProjectScope.SCOPE).node(project).node(qualifier);
+ String message = null;
+ try {
+ message = NLS.bind(Messages.preferences_syncException, node.absolutePath());
+ if (!(node instanceof ProjectPreferences))
+ return;
+ ProjectPreferences projectPrefs = (ProjectPreferences) node;
+ if (projectPrefs.isWriting)
+ return;
+ read(projectPrefs, file);
+ // make sure that we generate the appropriate resource change events
+ // if encoding settings have changed
+ if (ResourcesPlugin.PI_RESOURCES.equals(qualifier))
+ preferencesChanged(file.getProject());
+ } catch (BackingStoreException e) {
+ IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e);
+ throw new CoreException(status);
+ }
+ }
+
+ /**
+ * Default constructor. Should only be called by #createExecutableExtension.
+ */
+ public ProjectPreferences() {
+ super(null, null);
+ }
+
+ private ProjectPreferences(EclipsePreferences parent, String name) {
+ super(parent, name);
+
+ // cache the segment count
+ String path = absolutePath();
+ segmentCount = getSegmentCount(path);
+
+ if (segmentCount == 1)
+ return;
+
+ // cache the project name
+ String projectName = getSegment(path, 1);
+ if (projectName != null)
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+
+ // cache the qualifier
+ if (segmentCount > 2)
+ qualifier = getSegment(path, 2);
+
+ if (segmentCount != 2)
+ return;
+
+ // else segmentCount == 2 so we initialize the children
+ if (initialized)
+ return;
+ try {
+ synchronized (this) {
+ String[] names = computeChildren();
+ for (int i = 0; i < names.length; i++)
+ addChild(names[i], null);
+ }
+ } finally {
+ initialized = true;
+ }
+ }
+
+ /*
+ * Figure out what the children of this node are based on the resources
+ * that are in the workspace.
+ */
+ private String[] computeChildren() {
+ if (project == null)
+ return EMPTY_STRING_ARRAY;
+ IFolder folder = project.getFolder(DEFAULT_PREFERENCES_DIRNAME);
+ if (!folder.exists())
+ return EMPTY_STRING_ARRAY;
+ IResource[] members = null;
+ try {
+ members = folder.members();
+ } catch (CoreException e) {
+ return EMPTY_STRING_ARRAY;
+ }
+ ArrayList result = new ArrayList();
+ for (int i = 0; i < members.length; i++) {
+ IResource resource = members[i];
+ if (resource.getType() == IResource.FILE && PREFS_FILE_EXTENSION.equals(resource.getFullPath().getFileExtension()))
+ result.add(resource.getFullPath().removeFileExtension().lastSegment());
+ }
+ return (String[]) result.toArray(EMPTY_STRING_ARRAY);
+ }
+
+ public void flush() throws BackingStoreException {
+ if (isReading)
+ return;
+ isWriting = true;
+ try {
+ super.flush();
+ } finally {
+ isWriting = false;
+ }
+ }
+
+ private IFile getFile() {
+ if (file == null) {
+ if (project == null || qualifier == null)
+ return null;
+ file = getFile(project, qualifier);
+ }
+ return file;
+ }
+
+ /*
+ * Return the node at which these preferences are loaded/saved.
+ */
+ protected IEclipsePreferences getLoadLevel() {
+ if (loadLevel == null) {
+ if (project == null || qualifier == null)
+ return null;
+ // Make it relative to this node rather than navigating to it from the root.
+ // Walk backwards up the tree starting at this node.
+ // This is important to avoid a chicken/egg thing on startup.
+ EclipsePreferences node = this;
+ for (int i = 3; i < segmentCount; i++)
+ node = (EclipsePreferences) node.parent();
+ loadLevel = node;
+ }
+ return loadLevel;
+ }
+
+ /*
+ * Calculate and return the file system location for this preference node.
+ * Use the absolute path of the node to find out the project name so
+ * we can get its location on disk.
+ *
+ * NOTE: we cannot cache the location since it may change over the course
+ * of the project life-cycle.
+ */
+ protected IPath getLocation() {
+ if (project == null || qualifier == null)
+ return null;
+ IPath path = project.getLocation();
+ return computeLocation(path, qualifier);
+ }
+
+ protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
+ return new ProjectPreferences(nodeParent, nodeName);
+ }
+
+ protected synchronized boolean isAlreadyLoaded(IEclipsePreferences node) {
+ return loadedNodes.contains(node.absolutePath());
+ }
+
+ protected synchronized boolean isAlreadyLoaded(String path) {
+ return loadedNodes.contains(path);
+ }
+
+ protected void load() throws BackingStoreException {
+ IFile localFile = getFile();
+ if (localFile == null || !localFile.exists()) {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Unable to determine preference file or file does not exist for node: " + absolutePath()); //$NON-NLS-1$
+ return;
+ }
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Loading preferences from file: " + localFile.getFullPath()); //$NON-NLS-1$
+ Properties fromDisk = new Properties();
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(localFile.getContents(true));
+ fromDisk.load(input);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.preferences_loadException, localFile.getFullPath());
+ log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.preferences_loadException, localFile.getFullPath());
+ log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ } finally {
+ FileUtil.safeClose(input);
+ }
+ convertFromProperties(this, fromDisk, true);
+ }
+
+ protected synchronized void loaded() {
+ loadedNodes.add(absolutePath());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.preferences.EclipsePreferences#nodeExists(java.lang.String)
+ *
+ * If we are at the /project node and we are checking for the existance of a child, we
+ * want special behaviour. If the child is a single segment name, then we want to
+ * return true if the node exists OR if a project with that name exists in the workspace.
+ */
+ public boolean nodeExists(String path) throws BackingStoreException {
+ if (segmentCount != 1)
+ return super.nodeExists(path);
+ if (path.length() == 0)
+ return super.nodeExists(path);
+ if (path.charAt(0) == IPath.SEPARATOR)
+ return super.nodeExists(path);
+ if (path.indexOf(IPath.SEPARATOR) != -1)
+ return super.nodeExists(path);
+ // if we are checking existance of a single segment child of /project, base the answer on
+ // whether or not it exists in the workspace.
+ return ResourcesPlugin.getWorkspace().getRoot().getProject(path).exists() || super.nodeExists(path);
+ }
+
+ protected void save() throws BackingStoreException {
+ final IFile fileInWorkspace = getFile();
+ if (fileInWorkspace == null) {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Not saving preferences since there is no file for node: " + absolutePath()); //$NON-NLS-1$
+ return;
+ }
+ Properties table = convertToProperties(new SortedProperties(), ""); //$NON-NLS-1$
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IResourceRuleFactory factory = workspace.getRuleFactory();
+ try {
+ if (table.isEmpty()) {
+ IWorkspaceRunnable operation = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ // nothing to save. delete existing file if one exists.
+ if (fileInWorkspace.exists()) {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Deleting preference file: " + fileInWorkspace.getFullPath()); //$NON-NLS-1$
+ if (fileInWorkspace.isReadOnly()) {
+ IStatus status = fileInWorkspace.getWorkspace().validateEdit(new IFile[] {fileInWorkspace}, IWorkspace.VALIDATE_PROMPT);
+ if (!status.isOK())
+ throw new CoreException(status);
+ }
+ try {
+ fileInWorkspace.delete(true, null);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.preferences_deleteException, fileInWorkspace.getFullPath());
+ log(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IStatus.WARNING, message, null));
+ }
+ }
+ }
+ };
+ ISchedulingRule rule = factory.deleteRule(fileInWorkspace);
+ try {
+ ResourcesPlugin.getWorkspace().run(operation, rule, IResource.NONE, null);
+ } catch (OperationCanceledException e) {
+ throw new BackingStoreException(Messages.preferences_operationCanceled);
+ }
+ return;
+ }
+ table.put(VERSION_KEY, VERSION_VALUE);
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ try {
+ table.store(output, null);
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.preferences_saveProblems, absolutePath());
+ log(new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ } finally {
+ try {
+ output.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ final InputStream input = new BufferedInputStream(new ByteArrayInputStream(output.toByteArray()));
+ IWorkspaceRunnable operation = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ if (fileInWorkspace.exists()) {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Setting preference file contents for: " + fileInWorkspace.getFullPath()); //$NON-NLS-1$
+ if (fileInWorkspace.isReadOnly()) {
+ IStatus status = fileInWorkspace.getWorkspace().validateEdit(new IFile[] {fileInWorkspace}, IWorkspace.VALIDATE_PROMPT);
+ if (!status.isOK())
+ throw new CoreException(status);
+ }
+ // set the contents
+ fileInWorkspace.setContents(input, IResource.KEEP_HISTORY, null);
+ } else {
+ // create the file
+ IFolder folder = (IFolder) fileInWorkspace.getParent();
+ if (!folder.exists()) {
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Creating parent preference directory: " + folder.getFullPath()); //$NON-NLS-1$
+ folder.create(IResource.NONE, true, null);
+ }
+ if (Policy.DEBUG_PREFERENCES)
+ Policy.debug("Creating preference file: " + fileInWorkspace.getLocation()); //$NON-NLS-1$
+ fileInWorkspace.create(input, IResource.NONE, null);
+ }
+ }
+ };
+ //don't bother with scheduling rules if we are already inside an operation
+ try {
+ if (((Workspace) workspace).getWorkManager().isLockAlreadyAcquired()) {
+ operation.run(null);
+ } else {
+ // we might: create the .settings folder, create the file, or modify the file.
+ ISchedulingRule rule = MultiRule.combine(factory.createRule(fileInWorkspace.getParent()), factory.modifyRule(fileInWorkspace));
+ workspace.run(operation, rule, IResource.NONE, null);
+ }
+ } catch (OperationCanceledException e) {
+ throw new BackingStoreException(Messages.preferences_operationCanceled);
+ }
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.preferences_saveProblems, fileInWorkspace.getFullPath());
+ log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Resource.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1841 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dan Rubel <dan_rubel@instantiations.com> - Implementation of getLocalTimeStamp
+ * Red Hat Incorporated - get/setResourceAttribute code
+ * Oakland Software Incorporated - added getSessionProperties and getPersistentProperties
+ * Holger Oehm <holger.oehm@sap.com> - [226264] race condition in Workspace.isTreeLocked()/setTreeLocked()
+ * Martin Oberhuber (Wind River) - [245937] ProjectDescription#setLinkLocation() detects non-change
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.internal.localstore.FileSystemResourceManager;
+import org.eclipse.core.internal.properties.IPropertyManager;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.IMoveDeleteHook;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+import org.eclipse.osgi.util.NLS;
+
+public abstract class Resource extends PlatformObject implements IResource, ICoreConstants, Cloneable, IPathRequestor {
+ /* package */IPath path;
+ /* package */Workspace workspace;
+
+ protected Resource(IPath path, Workspace workspace) {
+ this.path = path.removeTrailingSeparator();
+ this.workspace = workspace;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#accept(IResourceProxyVisitor, int)
+ */
+ public void accept(final IResourceProxyVisitor visitor, final int memberFlags) throws CoreException {
+ // it is invalid to call accept on a phantom when INCLUDE_PHANTOMS is not specified
+ final boolean includePhantoms = (memberFlags & IContainer.INCLUDE_PHANTOMS) != 0;
+ checkAccessible(getFlags(getResourceInfo(includePhantoms, false)));
+
+ final ResourceProxy proxy = new ResourceProxy();
+ IElementContentVisitor elementVisitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object contents) {
+ ResourceInfo info = (ResourceInfo) contents;
+ if (!isMember(getFlags(info), memberFlags))
+ return false;
+ proxy.requestor = requestor;
+ proxy.info = info;
+ try {
+ return visitor.visit(proxy);
+ } catch (CoreException e) {
+ //throw an exception to bail out of the traversal
+ throw new WrappedRuntimeException(e);
+ } finally {
+ proxy.reset();
+ }
+ }
+ };
+ try {
+ new ElementTreeIterator(workspace.getElementTree(), getFullPath()).iterate(elementVisitor);
+ } catch (WrappedRuntimeException e) {
+ throw (CoreException) e.getTargetException();
+ } catch (OperationCanceledException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ String msg = Messages.resources_errorVisiting;
+ IResourceStatus errorStatus = new ResourceStatus(IResourceStatus.INTERNAL_ERROR, getFullPath(), msg, e);
+ Policy.log(errorStatus);
+ throw new ResourceException(errorStatus);
+ } finally {
+ proxy.requestor = null;
+ proxy.info = null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#accept(IResourceVisitor)
+ */
+ public void accept(IResourceVisitor visitor) throws CoreException {
+ accept(visitor, IResource.DEPTH_INFINITE, 0);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#accept(IResourceVisitor, int, boolean)
+ */
+ public void accept(IResourceVisitor visitor, int depth, boolean includePhantoms) throws CoreException {
+ accept(visitor, depth, includePhantoms ? IContainer.INCLUDE_PHANTOMS : 0);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#accept(IResourceVisitor, int, int)
+ */
+ public void accept(final IResourceVisitor visitor, int depth, int memberFlags) throws CoreException {
+ //use the fast visitor if visiting to infinite depth
+ if (depth == IResource.DEPTH_INFINITE) {
+ accept(new IResourceProxyVisitor() {
+ public boolean visit(IResourceProxy proxy) throws CoreException {
+ return visitor.visit(proxy.requestResource());
+ }
+ }, memberFlags);
+ return;
+ }
+ // it is invalid to call accept on a phantom when INCLUDE_PHANTOMS is not specified
+ final boolean includePhantoms = (memberFlags & IContainer.INCLUDE_PHANTOMS) != 0;
+ ResourceInfo info = getResourceInfo(includePhantoms, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+
+ //check that this resource matches the member flags
+ if (!isMember(flags, memberFlags))
+ return;
+ // visit this resource
+ if (!visitor.visit(this) || depth == DEPTH_ZERO)
+ return;
+ // get the info again because it might have been changed by the visitor
+ info = getResourceInfo(includePhantoms, false);
+ if (info == null)
+ return;
+ // thread safety: (cache the type to avoid changes -- we might not be inside an operation)
+ int type = info.getType();
+ if (type == FILE)
+ return;
+ // if we had a gender change we need to fix up the resource before asking for its members
+ IContainer resource = getType() != type ? (IContainer) workspace.newResource(getFullPath(), type) : (IContainer) this;
+ IResource[] members = resource.members(memberFlags);
+ for (int i = 0; i < members.length; i++)
+ members[i].accept(visitor, DEPTH_ZERO, memberFlags);
+ }
+
+ protected void assertCopyRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
+ IStatus status = checkCopyRequirements(destination, destinationType, updateFlags);
+ if (!status.isOK()) {
+ // this assert is ok because the error cases generated by the
+ // check method above indicate assertion conditions.
+ Assert.isTrue(false, status.getChildren()[0].getMessage());
+ }
+ }
+
+ /**
+ * Throws an exception if the link preconditions are not met. Returns the file info
+ * for the file being linked to, or <code>null</code> if not available.
+ * @throws CoreException
+ */
+ protected IFileInfo assertLinkRequirements(URI localLocation, int updateFlags) throws CoreException {
+ boolean allowMissingLocal = (updateFlags & IResource.ALLOW_MISSING_LOCAL) != 0;
+ if ((updateFlags & IResource.REPLACE) == 0)
+ checkDoesNotExist(getFlags(getResourceInfo(false, false)), true);
+ IStatus locationStatus = workspace.validateLinkLocationURI(this, localLocation);
+ //we only tolerate an undefined path variable in the allow missing local case
+ final boolean variableUndefined = locationStatus.getCode() == IResourceStatus.VARIABLE_NOT_DEFINED_WARNING;
+ if (locationStatus.getSeverity() == IStatus.ERROR || (variableUndefined && !allowMissingLocal))
+ throw new ResourceException(locationStatus);
+ //check that the parent exists and is open
+ Container parent = (Container) getParent();
+ parent.checkAccessible(getFlags(parent.getResourceInfo(false, false)));
+ //if the variable is undefined we can't do any further checks
+ if (variableUndefined)
+ return null;
+ //check if the file exists
+ URI resolved = workspace.getPathVariableManager().resolveURI(localLocation);
+ IFileStore store = EFS.getStore(resolved);
+ IFileInfo fileInfo = store.fetchInfo();
+ boolean localExists = fileInfo.exists();
+ if (!allowMissingLocal && !localExists) {
+ String msg = NLS.bind(Messages.links_localDoesNotExist, store.toString());
+ throw new ResourceException(IResourceStatus.NOT_FOUND_LOCAL, getFullPath(), msg, null);
+ }
+ //resource type and file system type must match
+ if (localExists && ((getType() == IResource.FOLDER) != fileInfo.isDirectory())) {
+ String msg = NLS.bind(Messages.links_wrongLocalType, getFullPath());
+ throw new ResourceException(IResourceStatus.WRONG_TYPE_LOCAL, getFullPath(), msg, null);
+ }
+ return fileInfo;
+ }
+
+ protected void assertMoveRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
+ IStatus status = checkMoveRequirements(destination, destinationType, updateFlags);
+ if (!status.isOK()) {
+ // this assert is ok because the error cases generated by the
+ // check method above indicate assertion conditions.
+ Assert.isTrue(false, status.getChildren()[0].getMessage());
+ }
+ }
+
+ public void checkAccessible(int flags) throws CoreException {
+ checkExists(flags, true);
+ }
+
+ private ResourceInfo checkAccessibleAndLocal(int depth) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ checkLocal(flags, depth);
+ return info;
+ }
+
+ /**
+ * This method reports errors in two different ways. It can throw a
+ * CoreException or return a status. CoreExceptions are used according to the
+ * specification of the copy method. Programming errors, that would usually be
+ * prevented by using an "Assert" code, are reported as an IStatus. We're doing
+ * this way because we have two different methods to copy resources:
+ * IResource#copy and IWorkspace#copy. The first one gets the error and throws
+ * its message in an AssertionFailureException. The second one just throws a
+ * CoreException using the status returned by this method.
+ *
+ * @see IResource#copy(IPath, int, IProgressMonitor)
+ */
+ public IStatus checkCopyRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
+ String message = Messages.resources_copyNotMet;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_VALUE, message, null);
+ if (destination == null) {
+ message = Messages.resources_destNotNull;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message);
+ }
+ destination = makePathAbsolute(destination);
+ if (getFullPath().isPrefixOf(destination)) {
+ message = NLS.bind(Messages.resources_copyDestNotSub, getFullPath());
+ status.add(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
+ }
+ checkValidPath(destination, destinationType, false);
+
+ ResourceInfo info;
+ checkAccessibleAndLocal(DEPTH_INFINITE);
+
+ Resource dest = workspace.newResource(destination, destinationType);
+ dest.checkDoesNotExist();
+
+ // ensure we aren't trying to copy a file to a project
+ if (getType() == IResource.FILE && destinationType == IResource.PROJECT) {
+ message = Messages.resources_fileToProj;
+ throw new ResourceException(IResourceStatus.INVALID_VALUE, getFullPath(), message, null);
+ }
+
+ // we can't copy into a closed project
+ if (destinationType != IResource.PROJECT) {
+ Project project = (Project) dest.getProject();
+ info = project.getResourceInfo(false, false);
+ project.checkAccessible(getFlags(info));
+ Container parent = (Container) dest.getParent();
+ if (!parent.equals(project)) {
+ info = parent.getResourceInfo(false, false);
+ parent.checkExists(getFlags(info), true);
+ }
+ }
+ if (isUnderLink() || dest.isUnderLink()) {
+ //make sure location is not null. This can occur with linked resources relative to
+ //undefined path variables
+ URI sourceLocation = getLocationURI();
+ if (sourceLocation == null) {
+ message = NLS.bind(Messages.localstore_locationUndefined, getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, getFullPath(), message, null);
+ }
+ URI destLocation = dest.getLocationURI();
+ if (destLocation == null) {
+ message = NLS.bind(Messages.localstore_locationUndefined, dest.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, dest.getFullPath(), message, null);
+ }
+ //make sure location of source is not a prefix of the location of the destination
+ //this can occur if the source and/or destination is a linked resource
+ if (getStore().isParentOf(dest.getStore())) {
+ message = NLS.bind(Messages.resources_copyDestNotSub, getFullPath());
+ throw new ResourceException(IResourceStatus.INVALID_VALUE, getFullPath(), message, null);
+ }
+ }
+
+ return status.isOK() ? Status.OK_STATUS : (IStatus) status;
+ }
+
+ /**
+ * Checks that this resource does not exist. If the file system is not case
+ * sensitive, this method also checks for a case variant.
+ */
+ protected void checkDoesNotExist() throws CoreException {
+ checkDoesNotExist(getFlags(getResourceInfo(false, false)), false);
+ }
+
+ /**
+ * Checks that this resource does not exist. If the file system is not case
+ * sensitive, this method also checks for a case variant.
+ *
+ * @exception CoreException if this resource exists
+ */
+ public void checkDoesNotExist(int flags, boolean checkType) throws CoreException {
+ //if this exact resource exists we are done
+ if (exists(flags, checkType)) {
+ String message = NLS.bind(Messages.resources_mustNotExist, getFullPath());
+ throw new ResourceException(checkType ? IResourceStatus.RESOURCE_EXISTS : IResourceStatus.PATH_OCCUPIED, getFullPath(), message, null);
+ }
+ if (Workspace.caseSensitive)
+ return;
+ //now look for a matching case variant in the tree
+ IResource variant = findExistingResourceVariant(getFullPath());
+ if (variant == null)
+ return;
+ String msg = NLS.bind(Messages.resources_existsDifferentCase, variant.getFullPath());
+ throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, variant.getFullPath(), msg, null);
+ }
+
+ /**
+ * Checks that this resource exists.
+ * If checkType is true, the type of this resource and the one in the tree must match.
+ *
+ * @exception CoreException if this resource does not exist
+ */
+ public void checkExists(int flags, boolean checkType) throws CoreException {
+ if (!exists(flags, checkType)) {
+ String message = NLS.bind(Messages.resources_mustExist, getFullPath());
+ throw new ResourceException(IResourceStatus.RESOURCE_NOT_FOUND, getFullPath(), message, null);
+ }
+ }
+
+ /**
+ * Checks that this resource is local to the given depth.
+ *
+ * @exception CoreException if this resource is not local
+ */
+ public void checkLocal(int flags, int depth) throws CoreException {
+ if (!isLocal(flags, depth)) {
+ String message = NLS.bind(Messages.resources_mustBeLocal, getFullPath());
+ throw new ResourceException(IResourceStatus.RESOURCE_NOT_LOCAL, getFullPath(), message, null);
+ }
+ }
+
+ /**
+ * This method reports errors in two different ways. It can throw a
+ * CoreException or log a status. CoreExceptions are used according
+ * to the specification of the move method. Programming errors, that
+ * would usually be prevented by using an "Assert" code, are reported as
+ * an IStatus.
+ * We're doing this way because we have two different methods to move
+ * resources: IResource#move and IWorkspace#move. The first one gets
+ * the error and throws its message in an AssertionFailureException. The
+ * second one just throws a CoreException using the status returned
+ * by this method.
+ *
+ * @see IResource#move(IPath, int, IProgressMonitor)
+ */
+ protected IStatus checkMoveRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
+ String message = Messages.resources_moveNotMet;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_VALUE, message, null);
+ if (destination == null) {
+ message = Messages.resources_destNotNull;
+ return new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message);
+ }
+ destination = makePathAbsolute(destination);
+ if (getFullPath().isPrefixOf(destination)) {
+ message = NLS.bind(Messages.resources_moveDestNotSub, getFullPath());
+ status.add(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
+ }
+ checkValidPath(destination, destinationType, false);
+
+ ResourceInfo info;
+ checkAccessibleAndLocal(DEPTH_INFINITE);
+
+ Resource dest = workspace.newResource(destination, destinationType);
+
+ // check if we are only changing case
+ IResource variant = Workspace.caseSensitive ? null : findExistingResourceVariant(destination);
+ if (variant == null || !this.equals(variant))
+ dest.checkDoesNotExist();
+
+ // ensure we aren't trying to move a file to a project
+ if (getType() == IResource.FILE && dest.getType() == IResource.PROJECT) {
+ message = Messages.resources_fileToProj;
+ throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
+ }
+
+ // we can't move into a closed project
+ if (destinationType != IResource.PROJECT) {
+ Project project = (Project) dest.getProject();
+ info = project.getResourceInfo(false, false);
+ project.checkAccessible(getFlags(info));
+ Container parent = (Container) dest.getParent();
+ if (!parent.equals(project)) {
+ info = parent.getResourceInfo(false, false);
+ parent.checkExists(getFlags(info), true);
+ }
+ }
+ if (isUnderLink() || dest.isUnderLink()) {
+ //make sure location is not null. This can occur with linked resources relative to
+ //undefined path variables
+ URI sourceLocation = getLocationURI();
+ if (sourceLocation == null) {
+ message = NLS.bind(Messages.localstore_locationUndefined, getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, getFullPath(), message, null);
+ }
+ URI destLocation = dest.getLocationURI();
+ if (destLocation == null) {
+ message = NLS.bind(Messages.localstore_locationUndefined, dest.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, dest.getFullPath(), message, null);
+ }
+ //make sure location of source is not a prefix of the location of the destination
+ //this can occur if the source and/or destination is a linked resource
+ if (getStore().isParentOf(dest.getStore())) {
+ message = NLS.bind(Messages.resources_moveDestNotSub, getFullPath());
+ throw new ResourceException(IResourceStatus.INVALID_VALUE, getFullPath(), message, null);
+ }
+ }
+
+ return status.isOK() ? Status.OK_STATUS : (IStatus) status;
+ }
+
+ /**
+ * Checks that the supplied path is valid according to Workspace.validatePath().
+ *
+ * @exception CoreException if the path is not valid
+ */
+ public void checkValidPath(IPath toValidate, int type, boolean lastSegmentOnly) throws CoreException {
+ IStatus result = workspace.locationValidator.validatePath(toValidate, type, lastSegmentOnly);
+ if (!result.isOK())
+ throw new ResourceException(result);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#clearHistory(IProgressMonitor)
+ */
+ public void clearHistory(IProgressMonitor monitor) {
+ getLocalManager().getHistoryStore().remove(getFullPath(), monitor);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see ISchedulingRule#contains(ISchedulingRule)
+ */
+ public boolean contains(ISchedulingRule rule) {
+ if (this == rule)
+ return true;
+ //must allow notifications to nest in all resource rules
+ if (rule.getClass().equals(WorkManager.NotifyRule.class))
+ return true;
+ if (rule instanceof MultiRule) {
+ MultiRule multi = (MultiRule) rule;
+ ISchedulingRule[] children = multi.getChildren();
+ for (int i = 0; i < children.length; i++)
+ if (!contains(children[i]))
+ return false;
+ return true;
+ }
+ if (!(rule instanceof IResource))
+ return false;
+ return path.isPrefixOf(((IResource) rule).getFullPath());
+ }
+
+ public void convertToPhantom() throws CoreException {
+ ResourceInfo info = getResourceInfo(false, true);
+ if (info == null || isPhantom(getFlags(info)))
+ return;
+ info.clearSessionProperties();
+ info.set(M_PHANTOM);
+ getLocalManager().updateLocalSync(info, I_NULL_SYNC_INFO);
+ info.clearModificationStamp();
+ // should already be done by the #deleteResource call but left in
+ // just to be safe and for code clarity.
+ info.setMarkers(null);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#copy(IPath, boolean, IProgressMonitor)
+ */
+ public void copy(IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ copy(destination, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#copy(IPath, int, IProgressMonitor)
+ */
+ public void copy(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ try {
+ monitor = Policy.monitorFor(monitor);
+ String message = NLS.bind(Messages.resources_copying, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ Policy.checkCanceled(monitor);
+ destination = makePathAbsolute(destination);
+ checkValidPath(destination, getType(), false);
+ Resource destResource = workspace.newResource(destination, getType());
+ final ISchedulingRule rule = workspace.getRuleFactory().copyRule(this, destResource);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ // The following assert method throws CoreExceptions as stated in the IResource.copy API
+ // and assert for programming errors. See checkCopyRequirements for more information.
+ assertCopyRequirements(destination, getType(), updateFlags);
+ workspace.beginOperation(true);
+ getLocalManager().copy(this, destResource, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork));
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#copy(IProjectDescription, boolean, IProgressMonitor)
+ */
+ public void copy(IProjectDescription destDesc, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ copy(destDesc, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * Used when a folder is to be copied to a project.
+ * @see IResource#copy(IProjectDescription, int, IProgressMonitor)
+ */
+ public void copy(IProjectDescription destDesc, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(destDesc);
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_copying, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ try {
+ workspace.prepareOperation(workspace.getRoot(), monitor);
+ // The following assert method throws CoreExceptions as stated in the IResource.copy API
+ // and assert for programming errors. See checkCopyRequirements for more information.
+ IPath destPath = new Path(destDesc.getName()).makeAbsolute();
+ assertCopyRequirements(destPath, getType(), updateFlags);
+ Project destProject = (Project) workspace.getRoot().getProject(destPath.lastSegment());
+ workspace.beginOperation(true);
+
+ // create and open the new project
+ destProject.create(destDesc, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
+ destProject.open(Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
+
+ // copy the children
+ // FIXME: fix the progress monitor here...create a sub monitor and do a worked(1) after each child instead
+ IResource[] children = ((IContainer) this).members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < children.length; i++) {
+ Resource child = (Resource) children[i];
+ child.copy(destPath.append(child.getName()), updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 60 / 100 / children.length));
+ }
+
+ // copy over the properties
+ getPropertyManager().copy(this, destProject, DEPTH_ZERO);
+ monitor.worked(Policy.opWork * 15 / 100);
+
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(workspace.getRoot(), true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Count the number of resources in the tree from this container to the
+ * specified depth. Include this resource. Include phantoms if
+ * the phantom boolean is true.
+ */
+ public int countResources(int depth, boolean phantom) {
+ return workspace.countResources(path, depth, phantom);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IFolder#createLink(IPath, int, IProgressMonitor)
+ * @see org.eclipse.core.resources.IFile#createLink(IPath, int, IProgressMonitor)
+ */
+ public void createLink(IPath localLocation, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(localLocation);
+ createLink(URIUtil.toURI(localLocation), updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IFolder#createLink(URI, int, IProgressMonitor)
+ * @see org.eclipse.core.resources.IFile#createLink(URI, int, IProgressMonitor)
+ */
+ public void createLink(URI localLocation, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(localLocation);
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.links_creating, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ Policy.checkCanceled(monitor);
+ checkValidPath(path, FOLDER, true);
+ final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ IFileInfo fileInfo = assertLinkRequirements(localLocation, updateFlags);
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_LINK_CREATE, this));
+ workspace.beginOperation(true);
+ //replace existing resource, if applicable
+ if ((updateFlags & REPLACE) != 0) {
+ IResource existing = workspace.getRoot().findMember(getFullPath());
+ if (existing != null)
+ workspace.deleteResource(existing);
+ }
+ ResourceInfo info = workspace.createResource(this, false);
+ if ((updateFlags & IResource.HIDDEN) != 0)
+ info.set(M_HIDDEN);
+ info.set(M_LINK);
+ localLocation = FileUtil.canonicalURI(localLocation);
+ getLocalManager().link(this, localLocation, fileInfo);
+ monitor.worked(Policy.opWork * 5 / 100);
+ //save the location in the project description
+ Project project = (Project) getProject();
+ boolean changed = project.internalGetDescription().setLinkLocation(getProjectRelativePath(), new LinkDescription(this, localLocation));
+ if (changed)
+ project.writeDescription(IResource.NONE);
+ monitor.worked(Policy.opWork * 5 / 100);
+
+ //refresh to discover any new resources below this linked location
+ if (getType() != IResource.FILE) {
+ //refresh either in background or foreground
+ if ((updateFlags & IResource.BACKGROUND_REFRESH) != 0) {
+ workspace.refreshManager.refresh(this);
+ monitor.worked(Policy.opWork * 90 / 100);
+ } else {
+ refreshLocal(DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 90 / 100));
+ }
+ } else
+ monitor.worked(Policy.opWork * 90 / 100);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#createMarker(String)
+ */
+ public IMarker createMarker(String type) throws CoreException {
+ Assert.isNotNull(type);
+ final ISchedulingRule rule = workspace.getRuleFactory().markerRule(this);
+ try {
+ workspace.prepareOperation(rule, null);
+ checkAccessible(getFlags(getResourceInfo(false, false)));
+ workspace.beginOperation(true);
+ MarkerInfo info = new MarkerInfo();
+ info.setType(type);
+ info.setCreationTime(System.currentTimeMillis());
+ workspace.getMarkerManager().add(this, info);
+ return new Marker(this, info.getId());
+ } finally {
+ workspace.endOperation(rule, false, null);
+ }
+ }
+
+ public IResourceProxy createProxy() {
+ ResourceProxy result = new ResourceProxy();
+ result.info = getResourceInfo(false, false);
+ result.requestor = this;
+ result.resource = this;
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IProject#delete(boolean, boolean, IProgressMonitor)
+ * @see IWorkspaceRoot#delete(boolean, boolean, IProgressMonitor)
+ * N.B. This is not an IResource method!
+ */
+ public void delete(boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+ delete(updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#delete(boolean, IProgressMonitor)
+ */
+ public void delete(boolean force, IProgressMonitor monitor) throws CoreException {
+ delete(force ? IResource.FORCE : IResource.NONE, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#delete(int, IProgressMonitor)
+ */
+ public void delete(int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_deleting, getFullPath());
+ monitor.beginTask("", Policy.totalWork * 1000); //$NON-NLS-1$
+ monitor.subTask(message);
+ final ISchedulingRule rule = workspace.getRuleFactory().deleteRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ // if there is no resource then there is nothing to delete so just return
+ if (!exists())
+ return;
+ workspace.beginOperation(true);
+ broadcastPreDeleteEvent();
+
+ // when a project is being deleted, flush the build order in case there is a problem
+ if (this.getType() == IResource.PROJECT)
+ workspace.flushBuildOrder();
+
+ final IFileStore originalStore = getStore();
+ boolean wasLinked = isLinked();
+ message = Messages.resources_deleteProblem;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_DELETE_LOCAL, message, null);
+ WorkManager workManager = workspace.getWorkManager();
+ ResourceTree tree = new ResourceTree(workspace.getFileSystemManager(), workManager.getLock(), status, updateFlags);
+ int depth = 0;
+ try {
+ depth = workManager.beginUnprotected();
+ unprotectedDelete(tree, updateFlags, monitor);
+ } finally {
+ workManager.endUnprotected(depth);
+ }
+ if (getType() == ROOT) {
+ // need to clear out the root info
+ workspace.getMarkerManager().removeMarkers(this, IResource.DEPTH_ZERO);
+ getPropertyManager().deleteProperties(this, IResource.DEPTH_ZERO);
+ getResourceInfo(false, false).clearSessionProperties();
+ }
+ // Invalidate the tree for further use by clients.
+ tree.makeInvalid();
+ if (!tree.getStatus().isOK())
+ throw new ResourceException(tree.getStatus());
+ //update any aliases of this resource
+ //note that deletion of a linked resource cannot affect other resources
+ if (!wasLinked)
+ workspace.getAliasManager().updateAliases(this, originalStore, IResource.DEPTH_INFINITE, monitor);
+ //make sure the rule factory is cleared on project deletion
+ if (getType() == PROJECT)
+ ((Rules) workspace.getRuleFactory()).setRuleFactory((IProject) this, null);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork * 1000));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#deleteMarkers(String, boolean, int)
+ */
+ public void deleteMarkers(String type, boolean includeSubtypes, int depth) throws CoreException {
+ final ISchedulingRule rule = workspace.getRuleFactory().markerRule(this);
+ try {
+ workspace.prepareOperation(rule, null);
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+
+ workspace.beginOperation(true);
+ workspace.getMarkerManager().removeMarkers(this, type, includeSubtypes, depth);
+ } finally {
+ workspace.endOperation(rule, false, null);
+ }
+ }
+
+ /**
+ * This method should be called to delete a resource from the tree because it will also
+ * delete its properties and markers. If a status object is provided, minor exceptions are
+ * added, otherwise they are thrown. If major exceptions occur, they are always thrown.
+ */
+ public void deleteResource(boolean convertToPhantom, MultiStatus status) throws CoreException {
+ // remove markers on this resource and its descendents
+ if (exists())
+ getMarkerManager().removeMarkers(this, IResource.DEPTH_INFINITE);
+ // if this is a linked resource or contains linked resources , remove their entries from the project description
+ List links = findLinks();
+ //pre-delete notification to internal infrastructure
+ if (links != null)
+ for (Iterator it = links.iterator(); it.hasNext();)
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_LINK_DELETE, (IResource) it.next()));
+
+ // check if we deleted a preferences file
+ ProjectPreferences.deleted(this);
+
+ /* if we are synchronizing, do not delete the resource. Convert it
+ into a phantom. Actual deletion will happen when we refresh or push. */
+ if (convertToPhantom && getType() != PROJECT && synchronizing(getResourceInfo(true, false)))
+ convertToPhantom();
+ else
+ workspace.deleteResource(this);
+
+ //remove all deleted linked resources from the project description
+ if (getType() != IResource.PROJECT && links != null) {
+ Project project = (Project) getProject();
+ ProjectDescription description = project.internalGetDescription();
+ boolean wasChanged = false;
+ for (Iterator it = links.iterator(); it.hasNext();)
+ wasChanged |= description.setLinkLocation(((IResource) it.next()).getProjectRelativePath(), null);
+ if (wasChanged) {
+ project.internalSetDescription(description, true);
+ project.writeDescription(IResource.FORCE);
+ }
+ }
+
+ // Delete properties after the resource is deleted from the tree. See bug 84584.
+ CoreException err = null;
+ try {
+ getPropertyManager().deleteResource(this);
+ } catch (CoreException e) {
+ if (status != null)
+ status.add(e.getStatus());
+ else
+ err = e;
+ }
+ if (err != null)
+ throw err;
+ }
+
+ /*
+ * Returns a list of all linked resources at or below this resource, or null if there
+ * are no links.
+ */
+ private List findLinks() {
+ Project project = (Project) getProject();
+ ProjectDescription description = project.internalGetDescription();
+ HashMap linkMap = description.getLinks();
+ if (linkMap == null)
+ return null;
+ List links = null;
+ IPath myPath = getProjectRelativePath();
+ for (Iterator it = linkMap.values().iterator(); it.hasNext();) {
+ LinkDescription link = (LinkDescription) it.next();
+ IPath linkPath = link.getProjectRelativePath();
+ if (myPath.isPrefixOf(linkPath)) {
+ if (links == null)
+ links = new ArrayList();
+ links.add(workspace.newResource(project.getFullPath().append(linkPath), link.getType()));
+ }
+ }
+ return links;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#equals(Object)
+ */
+ public boolean equals(Object target) {
+ if (this == target)
+ return true;
+ if (!(target instanceof Resource))
+ return false;
+ Resource resource = (Resource) target;
+ return getType() == resource.getType() && path.equals(resource.path) && workspace.equals(resource.workspace);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#exists()
+ */
+ public boolean exists() {
+ ResourceInfo info = getResourceInfo(false, false);
+ return exists(getFlags(info), true);
+ }
+
+ public boolean exists(int flags, boolean checkType) {
+ return flags != NULL_FLAG && !(checkType && ResourceInfo.getType(flags) != getType());
+ }
+
+ /**
+ * Helper method for case insensitive file systems. Returns
+ * an existing resource whose path differs only in case from
+ * the given path, or null if no such resource exists.
+ */
+ public IResource findExistingResourceVariant(IPath target) {
+ if (!workspace.tree.includesIgnoreCase(target))
+ return null;
+ //ignore phantoms
+ ResourceInfo info = (ResourceInfo) workspace.tree.getElementDataIgnoreCase(target);
+ if (info != null && info.isSet(M_PHANTOM))
+ return null;
+ //resort to slow lookup to find exact case variant
+ IPath result = Path.ROOT;
+ int segmentCount = target.segmentCount();
+ for (int i = 0; i < segmentCount; i++) {
+ String[] childNames = workspace.tree.getNamesOfChildren(result);
+ String name = findVariant(target.segment(i), childNames);
+ if (name == null)
+ return null;
+ result = result.append(name);
+ }
+ return workspace.getRoot().findMember(result);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#findMarker(long)
+ */
+ public IMarker findMarker(long id) {
+ return workspace.getMarkerManager().findMarker(this, id);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#findMarkers(String, boolean, int)
+ */
+ public IMarker[] findMarkers(String type, boolean includeSubtypes, int depth) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ // It might happen that from this point the resource is not accessible anymore.
+ // But markers have the #exists method that callers can use to check if it is
+ // still valid.
+ return workspace.getMarkerManager().findMarkers(this, type, includeSubtypes, depth);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#findMaxProblemSeverity(String, boolean, int)
+ */
+ public int findMaxProblemSeverity(String type, boolean includeSubtypes, int depth) throws CoreException {
+ ResourceInfo info = getResourceInfo(false, false);
+ checkAccessible(getFlags(info));
+ // It might happen that from this point the resource is not accessible anymore.
+ // But markers have the #exists method that callers can use to check if it is
+ // still valid.
+ return workspace.getMarkerManager().findMaxProblemSeverity(this, type, includeSubtypes, depth);
+ }
+
+ /**
+ * Searches for a variant of the given target in the list,
+ * that differs only in case. Returns the variant from
+ * the list if one is found, otherwise returns null.
+ */
+ private String findVariant(String target, String[] list) {
+ for (int i = 0; i < list.length; i++) {
+ if (target.equalsIgnoreCase(list[i]))
+ return list[i];
+ }
+ return null;
+ }
+
+ protected void fixupAfterMoveSource() throws CoreException {
+ ResourceInfo info = getResourceInfo(true, true);
+ //if a linked resource is moved, we need to remove the location info from the .project
+ if (isLinked()) {
+ Project project = (Project) getProject();
+ if (project.internalGetDescription().setLinkLocation(getProjectRelativePath(), null))
+ project.writeDescription(IResource.NONE);
+ }
+
+ // check if we deleted a preferences file
+ ProjectPreferences.deleted(this);
+
+ if (!synchronizing(info)) {
+ workspace.deleteResource(this);
+ return;
+ }
+ info.clearSessionProperties();
+ info.clear(M_LOCAL_EXISTS);
+ info.setLocalSyncInfo(I_NULL_SYNC_INFO);
+ info.set(M_PHANTOM);
+ info.clearModificationStamp();
+ info.setMarkers(null);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getFileExtension()
+ */
+ public String getFileExtension() {
+ String name = getName();
+ int index = name.lastIndexOf('.');
+ if (index == -1)
+ return null;
+ if (index == (name.length() - 1))
+ return ""; //$NON-NLS-1$
+ return name.substring(index + 1);
+ }
+
+ public int getFlags(ResourceInfo info) {
+ return (info == null) ? NULL_FLAG : info.getFlags();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getFullPath()
+ */
+ public IPath getFullPath() {
+ return path;
+ }
+
+ public FileSystemResourceManager getLocalManager() {
+ return workspace.getFileSystemManager();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getLocalTimeStamp()
+ */
+ public long getLocalTimeStamp() {
+ ResourceInfo info = getResourceInfo(false, false);
+ return info == null ? IResource.NULL_STAMP : info.getLocalSyncInfo();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getLocation()
+ */
+ public IPath getLocation() {
+ IProject project = getProject();
+ if (project != null && !project.exists())
+ return null;
+ return getLocalManager().locationFor(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getLocation()
+ */
+ public URI getLocationURI() {
+ IProject project = getProject();
+ if (project != null && !project.exists())
+ return null;
+ return getLocalManager().locationURIFor(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getMarker(long)
+ */
+ public IMarker getMarker(long id) {
+ return new Marker(this, id);
+ }
+
+ protected MarkerManager getMarkerManager() {
+ return workspace.getMarkerManager();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getModificationStamp()
+ */
+ public long getModificationStamp() {
+ ResourceInfo info = getResourceInfo(false, false);
+ return info == null ? IResource.NULL_STAMP : info.getModificationStamp();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getName()
+ */
+ public String getName() {
+ return path.lastSegment();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getParent()
+ */
+ public IContainer getParent() {
+ int segments = path.segmentCount();
+ //zero and one segments handled by subclasses
+ if (segments < 2)
+ Assert.isLegal(false, path.toString());
+ if (segments == 2)
+ return workspace.getRoot().getProject(path.segment(0));
+ return (IFolder) workspace.newResource(path.removeLastSegments(1), IResource.FOLDER);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getPersistentProperty(QualifiedName)
+ */
+ public String getPersistentProperty(QualifiedName key) throws CoreException {
+ checkAccessibleAndLocal(DEPTH_ZERO);
+ return getPropertyManager().getProperty(this, key);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getPersistentProperties()
+ */
+ public Map getPersistentProperties() throws CoreException {
+ checkAccessibleAndLocal(DEPTH_ZERO);
+ return getPropertyManager().getProperties(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getProject()
+ */
+ public IProject getProject() {
+ return workspace.getRoot().getProject(path.segment(0));
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getProjectRelativePath()
+ */
+ public IPath getProjectRelativePath() {
+ return getFullPath().removeFirstSegments(ICoreConstants.PROJECT_SEGMENT_LENGTH);
+ }
+
+ public IPropertyManager getPropertyManager() {
+ return workspace.getPropertyManager();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getRawLocation()
+ */
+ public IPath getRawLocation() {
+ if (isLinked())
+ return FileUtil.toPath(((Project) getProject()).internalGetDescription().getLinkLocationURI(getProjectRelativePath()));
+ return getLocation();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getRawLocation()
+ */
+ public URI getRawLocationURI() {
+ if (isLinked())
+ return ((Project) getProject()).internalGetDescription().getLinkLocationURI(getProjectRelativePath());
+ return getLocationURI();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getResourceAttributes()
+ */
+ public ResourceAttributes getResourceAttributes() {
+ if (!isAccessible())
+ return null;
+ return getLocalManager().attributes(this);
+ }
+
+ /**
+ * Returns the resource info. Returns null if the resource doesn't exist.
+ * If the phantom flag is true, phantom resources are considered.
+ * If the mutable flag is true, a mutable info is returned.
+ */
+ public ResourceInfo getResourceInfo(boolean phantom, boolean mutable) {
+ return workspace.getResourceInfo(getFullPath(), phantom, mutable);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getSessionProperty(QualifiedName)
+ */
+ public Object getSessionProperty(QualifiedName key) throws CoreException {
+ ResourceInfo info = checkAccessibleAndLocal(DEPTH_ZERO);
+ return info.getSessionProperty(key);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getSessionProperties()
+ */
+ public Map getSessionProperties() throws CoreException {
+ ResourceInfo info = checkAccessibleAndLocal(DEPTH_ZERO);
+ return info.getSessionProperties();
+ }
+
+ public IFileStore getStore() {
+ return getLocalManager().getStore(this);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getType()
+ */
+ public abstract int getType();
+
+ public String getTypeString() {
+ switch (getType()) {
+ case FILE :
+ return "L"; //$NON-NLS-1$
+ case FOLDER :
+ return "F"; //$NON-NLS-1$
+ case PROJECT :
+ return "P"; //$NON-NLS-1$
+ case ROOT :
+ return "R"; //$NON-NLS-1$
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#getWorkspace()
+ */
+ public IWorkspace getWorkspace() {
+ return workspace;
+ }
+
+ public int hashCode() {
+ // the container may be null if the identified resource
+ // does not exist so don't bother with it in the hash
+ return getFullPath().hashCode();
+ }
+
+ /**
+ * Sets the M_LOCAL_EXISTS flag. Is internal so we don't have
+ * to begin an operation.
+ */
+ protected void internalSetLocal(boolean flag, int depth) throws CoreException {
+ ResourceInfo info = getResourceInfo(true, true);
+ //only make the change if it's not already in desired state
+ if (info.isSet(M_LOCAL_EXISTS) != flag) {
+ if (flag && !isPhantom(getFlags(info))) {
+ info.set(M_LOCAL_EXISTS);
+ workspace.updateModificationStamp(info);
+ } else {
+ info.clear(M_LOCAL_EXISTS);
+ info.clearModificationStamp();
+ }
+ }
+ if (getType() == IResource.FILE || depth == IResource.DEPTH_ZERO)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ IResource[] children = ((IContainer) this).members();
+ for (int i = 0; i < children.length; i++)
+ ((Resource) children[i]).internalSetLocal(flag, depth);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isAccessible()
+ */
+ public boolean isAccessible() {
+ return exists();
+ }
+
+ /* (non-Javadoc)
+ * @see ISchedulingRule#isConflicting(ISchedulingRule)
+ */
+ public boolean isConflicting(ISchedulingRule rule) {
+ //must not schedule at same time as notification
+ if (rule.getClass().equals(WorkManager.NotifyRule.class))
+ return true;
+ if (!(rule instanceof IResource))
+ return false;
+ IPath otherPath = ((IResource) rule).getFullPath();
+ return path.isPrefixOf(otherPath) || otherPath.isPrefixOf(path);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isDerived()
+ */
+ public boolean isDerived() {
+ return isDerived(IResource.NONE);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isDerived(int)
+ */
+ public boolean isDerived(int options) {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_DERIVED))
+ return true;
+ // check ancestors if the appropriate option is set
+ if ((options & CHECK_ANCESTORS) != 0)
+ return getParent().isDerived(options);
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isHidden()
+ */
+ public boolean isHidden() {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_HIDDEN);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isHidden(int)
+ */
+ public boolean isHidden(int options) {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_HIDDEN))
+ return true;
+ // check ancestors if the appropriate option is set
+ if ((options & CHECK_ANCESTORS) != 0)
+ return getParent().isHidden(options);
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isLinked()
+ */
+ public boolean isLinked() {
+ return isLinked(NONE);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isLinked()
+ */
+ public boolean isLinked(int options) {
+ if ((options & CHECK_ANCESTORS) != 0) {
+ IProject project = getProject();
+ if (project == null)
+ return false;
+ ProjectDescription desc = ((Project) project).internalGetDescription();
+ if (desc == null)
+ return false;
+ HashMap links = desc.getLinks();
+ if (links == null)
+ return false;
+ IPath myPath = getProjectRelativePath();
+ for (Iterator it = links.values().iterator(); it.hasNext();) {
+ if (((LinkDescription) it.next()).getProjectRelativePath().isPrefixOf(myPath))
+ return true;
+ }
+ return false;
+ }
+ //the no ancestor checking case
+ ResourceInfo info = getResourceInfo(false, false);
+ return info != null && info.isSet(M_LINK);
+ }
+
+ /**
+ * @see IResource#isLocal(int)
+ * @deprecated
+ */
+ public boolean isLocal(int depth) {
+ ResourceInfo info = getResourceInfo(false, false);
+ return isLocal(getFlags(info), depth);
+ }
+
+ /**
+ * Note the depth parameter is intentionally ignored because
+ * this method is over-ridden by Container.isLocal().
+ * @deprecated
+ */
+ public boolean isLocal(int flags, int depth) {
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_LOCAL_EXISTS);
+ }
+
+ /**
+ * Returns whether a resource should be included in a traversal
+ * based on the provided member flags.
+ *
+ * @param flags The resource info flags
+ * @param memberFlags The member flag mask
+ * @return Whether the resource is included
+ */
+ protected boolean isMember(int flags, int memberFlags) {
+ int excludeMask = 0;
+ if ((memberFlags & IContainer.INCLUDE_PHANTOMS) == 0)
+ excludeMask |= M_PHANTOM;
+ if ((memberFlags & IContainer.INCLUDE_HIDDEN) == 0)
+ excludeMask |= M_HIDDEN;
+ if ((memberFlags & IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS) == 0)
+ excludeMask |= M_TEAM_PRIVATE_MEMBER;
+ if ((memberFlags & IContainer.EXCLUDE_DERIVED) != 0)
+ excludeMask |= M_DERIVED;
+ //the resource is a matching member if it matches none of the exclude flags
+ return flags != NULL_FLAG && (flags & excludeMask) == 0;
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isPhantom()
+ */
+ public boolean isPhantom() {
+ ResourceInfo info = getResourceInfo(true, false);
+ return isPhantom(getFlags(info));
+ }
+
+ public boolean isPhantom(int flags) {
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_PHANTOM);
+ }
+
+ /** (non-Javadoc)
+ * @see IResource#isReadOnly()
+ * @deprecated
+ */
+ public boolean isReadOnly() {
+ final ResourceAttributes attributes = getResourceAttributes();
+ return attributes == null ? false : attributes.isReadOnly();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isSynchronized(int)
+ */
+ public boolean isSynchronized(int depth) {
+ return getLocalManager().isSynchronized(this, depth);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isTeamPrivateMember()
+ */
+ public boolean isTeamPrivateMember() {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_TEAM_PRIVATE_MEMBER);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#isTeamPrivateMember(int)
+ */
+ public boolean isTeamPrivateMember(int options) {
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ if (flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_TEAM_PRIVATE_MEMBER))
+ return true;
+ // check ancestors if the appropriate option is set
+ if ((options & CHECK_ANCESTORS) != 0)
+ return getParent().isTeamPrivateMember(options);
+ return false;
+ }
+
+ /**
+ * Returns true if this resource is a linked resource, or a child of a linked
+ * resource, and false otherwise.
+ */
+ public boolean isUnderLink() {
+ int depth = path.segmentCount();
+ if (depth < 2)
+ return false;
+ if (depth == 2)
+ return isLinked();
+ //check if parent at depth two is a link
+ IPath linkParent = path.removeLastSegments(depth - 2);
+ return workspace.getResourceInfo(linkParent, false, false).isSet(ICoreConstants.M_LINK);
+ }
+
+ protected IPath makePathAbsolute(IPath target) {
+ if (target.isAbsolute())
+ return target;
+ return getParent().getFullPath().append(target);
+ }
+
+ /* (non-Javadoc)
+ * @see IFile#move(IPath, boolean, boolean, IProgressMonitor)
+ * @see IFolder#move(IPath, boolean, boolean, IProgressMonitor)
+ */
+ public void move(IPath destination, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+ move(destination, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#move(IPath, boolean, IProgressMonitor)
+ */
+ public void move(IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
+ move(destination, force ? IResource.FORCE : IResource.NONE, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#move(IPath, int, IProgressMonitor)
+ */
+ public void move(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_moving, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ Policy.checkCanceled(monitor);
+ destination = makePathAbsolute(destination);
+ checkValidPath(destination, getType(), false);
+ Resource destResource = workspace.newResource(destination, getType());
+ final ISchedulingRule rule = workspace.getRuleFactory().moveRule(this, destResource);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ // The following assert method throws CoreExceptions as stated in the IResource.move API
+ // and assert for programming errors. See checkMoveRequirements for more information.
+ assertMoveRequirements(destination, getType(), updateFlags);
+ workspace.beginOperation(true);
+ broadcastPreMoveEvent(destResource, updateFlags);
+ IFileStore originalStore = getStore();
+ message = Messages.resources_moveProblem;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
+ WorkManager workManager = workspace.getWorkManager();
+ ResourceTree tree = new ResourceTree(workspace.getFileSystemManager(), workManager.getLock(), status, updateFlags);
+ boolean success = false;
+ int depth = 0;
+ try {
+ depth = workManager.beginUnprotected();
+ success = unprotectedMove(tree, destResource, updateFlags, monitor);
+ } finally {
+ workManager.endUnprotected(depth);
+ }
+ // Invalidate the tree for further use by clients.
+ tree.makeInvalid();
+ //update any aliases of this resource and the destination
+ if (success) {
+ workspace.getAliasManager().updateAliases(this, originalStore, IResource.DEPTH_INFINITE, monitor);
+ workspace.getAliasManager().updateAliases(destResource, destResource.getStore(), IResource.DEPTH_INFINITE, monitor);
+ }
+ if (!tree.getStatus().isOK())
+ throw new ResourceException(tree.getStatus());
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#move(IProjectDescription, boolean, IProgressMonitor)
+ */
+ public void move(IProjectDescription description, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+ move(description, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#move(IPath, int, IProgressMonitor)
+ */
+ public void move(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ Assert.isNotNull(description);
+ if (getType() != IResource.PROJECT) {
+ String message = NLS.bind(Messages.resources_moveNotProject, getFullPath(), description.getName());
+ throw new ResourceException(IResourceStatus.INVALID_VALUE, getFullPath(), message, null);
+ }
+ ((Project) this).move(description, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#refreshLocal(int, IProgressMonitor)
+ */
+ public void refreshLocal(int depth, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ boolean isRoot = getType() == ROOT;
+ String message = isRoot ? Messages.resources_refreshingRoot : NLS.bind(Messages.resources_refreshing, getFullPath());
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ monitor.subTask(message);
+ boolean build = false;
+ final ISchedulingRule rule = workspace.getRuleFactory().refreshRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ if (!isRoot && !getProject().isAccessible())
+ return;
+ workspace.beginOperation(true);
+ if (getType() == IResource.PROJECT || getType() == IResource.ROOT)
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_REFRESH, this));
+ build = getLocalManager().refresh(this, depth, true, Policy.subMonitorFor(monitor, Policy.opWork));
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } catch (Error e) {
+ //support to track down Bug 95089
+ Policy.log(e);
+ throw e;
+ } catch (RuntimeException e) {
+ //support to track down Bug 95089
+ Policy.log(e);
+ throw e;
+ } finally {
+ workspace.endOperation(rule, build, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on {@link IPathRequestor}.
+ */
+ public String requestName() {
+ return getName();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on {@link IPathRequestor}.
+ */
+ public IPath requestPath() {
+ return getFullPath();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#revertModificationStamp
+ */
+ public void revertModificationStamp(long value) throws CoreException {
+ if (value < 0)
+ throw new IllegalArgumentException("Illegal value: " + value); //$NON-NLS-1$
+ // fetch the info but don't bother making it mutable even though we are going
+ // to modify it. It really doesn't matter as the change we are doing does not show up in deltas.
+ ResourceInfo info = checkAccessibleAndLocal(DEPTH_ZERO);
+ info.setModificationStamp(value);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setDerived(boolean)
+ */
+ public void setDerived(boolean isDerived) throws CoreException {
+ // fetch the info but don't bother making it mutable even though we are going
+ // to modify it. We don't know whether or not the tree is open and it really doesn't
+ // matter as the change we are doing does not show up in deltas.
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ // ignore attempts to set derived flag on anything except files and folders
+ if (info.getType() == FILE || info.getType() == FOLDER) {
+ if (isDerived) {
+ info.set(ICoreConstants.M_DERIVED);
+ } else {
+ info.clear(ICoreConstants.M_DERIVED);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setHidden(boolean)
+ */
+ public void setHidden(boolean isHidden) throws CoreException {
+ // fetch the info but don't bother making it mutable even though we are going
+ // to modify it. We don't know whether or not the tree is open and it really doesn't
+ // matter as the change we are doing does not show up in deltas.
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ if (isHidden) {
+ info.set(ICoreConstants.M_HIDDEN);
+ } else {
+ info.clear(ICoreConstants.M_HIDDEN);
+ }
+ }
+
+ /**
+ * @see IResource#setLocal(boolean, int, IProgressMonitor)
+ * @deprecated
+ */
+ public void setLocal(boolean flag, int depth, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = Messages.resources_setLocal;
+ monitor.beginTask(message, Policy.totalWork);
+ try {
+ workspace.prepareOperation(null, monitor);
+ workspace.beginOperation(true);
+ internalSetLocal(flag, depth);
+ monitor.worked(Policy.opWork);
+ } finally {
+ workspace.endOperation(null, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setLocalTimeStamp(long)
+ */
+ public long setLocalTimeStamp(long value) throws CoreException {
+ if (value < 0)
+ throw new IllegalArgumentException("Illegal value: " + value); //$NON-NLS-1$
+ // fetch the info but don't bother making it mutable even though we are going
+ // to modify it. It really doesn't matter as the change we are doing does not show up in deltas.
+ ResourceInfo info = checkAccessibleAndLocal(DEPTH_ZERO);
+ return getLocalManager().setLocalTimeStamp(this, info, value);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setPersistentProperty(QualifiedName, String)
+ */
+ public void setPersistentProperty(QualifiedName key, String value) throws CoreException {
+ checkAccessibleAndLocal(DEPTH_ZERO);
+ getPropertyManager().setProperty(this, key, value);
+ }
+
+ /** (non-Javadoc)
+ * @see IResource#setReadOnly(boolean)
+ * @deprecated
+ */
+ public void setReadOnly(boolean readonly) {
+ ResourceAttributes attributes = getResourceAttributes();
+ if (attributes == null)
+ return;
+ attributes.setReadOnly(readonly);
+ try {
+ setResourceAttributes(attributes);
+ } catch (CoreException e) {
+ //failure is not an option
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResource#setResourceAttributes(org.eclipse.core.resources.ResourceAttributes)
+ */
+ public void setResourceAttributes(ResourceAttributes attributes) throws CoreException {
+ checkAccessibleAndLocal(DEPTH_ZERO);
+ getLocalManager().setResourceAttributes(this, attributes);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setSessionProperty(QualifiedName, Object)
+ */
+ public void setSessionProperty(QualifiedName key, Object value) throws CoreException {
+ // fetch the info but don't bother making it mutable even though we are going
+ // to modify it. We don't know whether or not the tree is open and it really doesn't
+ // matter as the change we are doing does not show up in deltas.
+ ResourceInfo info = checkAccessibleAndLocal(DEPTH_ZERO);
+ info.setSessionProperty(key, value);
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#setTeamPrivateMember(boolean)
+ */
+ public void setTeamPrivateMember(boolean isTeamPrivate) throws CoreException {
+ // fetch the info but don't bother making it mutable even though we are going
+ // to modify it. We don't know whether or not the tree is open and it really doesn't
+ // matter as the change we are doing does not show up in deltas.
+ ResourceInfo info = getResourceInfo(false, false);
+ int flags = getFlags(info);
+ checkAccessible(flags);
+ // ignore attempts to set team private member flag on anything except files and folders
+ if (info.getType() == FILE || info.getType() == FOLDER) {
+ if (isTeamPrivate) {
+ info.set(ICoreConstants.M_TEAM_PRIVATE_MEMBER);
+ } else {
+ info.clear(ICoreConstants.M_TEAM_PRIVATE_MEMBER);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this resource has the potential to be
+ * (or have been) synchronized.
+ */
+ public boolean synchronizing(ResourceInfo info) {
+ return info != null && info.getSyncInfo(false) != null;
+ }
+
+ /* (non-Javadoc)
+ * @see Object#toString()
+ */
+ public String toString() {
+ return getTypeString() + getFullPath().toString();
+ }
+
+ /* (non-Javadoc)
+ * @see IResource#touch(IProgressMonitor)
+ */
+ public void touch(IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = NLS.bind(Messages.resources_touch, getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+ try {
+ workspace.prepareOperation(rule, monitor);
+ ResourceInfo info = checkAccessibleAndLocal(DEPTH_ZERO);
+
+ workspace.beginOperation(true);
+ // fake a change by incrementing the content ID
+ info = getResourceInfo(false, true);
+ info.incrementContentId();
+ // forget content-related caching flags
+ info.clear(M_CONTENT_CACHE);
+ workspace.updateModificationStamp(info);
+ monitor.worked(Policy.opWork);
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Calls the move/delete hook to perform the deletion. Since this method calls
+ * client code, it is run "unprotected", so the workspace lock is not held.
+ */
+ private void unprotectedDelete(ResourceTree tree, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ IMoveDeleteHook hook = workspace.getMoveDeleteHook();
+ switch (getType()) {
+ case IResource.FILE :
+ if (!hook.deleteFile(tree, (IFile) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000 / 2)))
+ tree.standardDeleteFile((IFile) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000));
+ break;
+ case IResource.FOLDER :
+ if (!hook.deleteFolder(tree, (IFolder) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000 / 2)))
+ tree.standardDeleteFolder((IFolder) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000));
+ break;
+ case IResource.PROJECT :
+ if (!hook.deleteProject(tree, (IProject) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000 / 2)))
+ tree.standardDeleteProject((IProject) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000));
+ break;
+ case IResource.ROOT :
+ // when the root is deleted, all its children including hidden projects
+ // have to be deleted
+ IProject[] projects = ((IWorkspaceRoot) this).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ if (!hook.deleteProject(tree, projects[i], updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000 / projects.length / 2)))
+ tree.standardDeleteProject(projects[i], updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 1000 / projects.length));
+ }
+ }
+ }
+
+ /**
+ * Calls the move/delete hook to perform the move. Since this method calls
+ * client code, it is run "unprotected", so the workspace lock is not held.
+ * Returns true if resources were actually moved, and false otherwise.
+ */
+ private boolean unprotectedMove(ResourceTree tree, final IResource destination, int updateFlags, IProgressMonitor monitor) throws CoreException, ResourceException {
+ IMoveDeleteHook hook = workspace.getMoveDeleteHook();
+ switch (getType()) {
+ case IResource.FILE :
+ if (!hook.moveFile(tree, (IFile) this, (IFile) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2)))
+ tree.standardMoveFile((IFile) this, (IFile) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork));
+ break;
+ case IResource.FOLDER :
+ if (!hook.moveFolder(tree, (IFolder) this, (IFolder) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2)))
+ tree.standardMoveFolder((IFolder) this, (IFolder) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork));
+ break;
+ case IResource.PROJECT :
+ IProject project = (IProject) this;
+ // if there is no change in name, there is nothing to do so return.
+ if (getName().equals(destination.getName()))
+ return false;
+ IProjectDescription description = project.getDescription();
+ description.setName(destination.getName());
+ if (!hook.moveProject(tree, project, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2)))
+ tree.standardMoveProject(project, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork));
+ break;
+ case IResource.ROOT :
+ String msg = Messages.resources_moveRoot;
+ throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), msg));
+ }
+ return true;
+ }
+
+ private void broadcastPreDeleteEvent() throws CoreException {
+ switch (getType()) {
+ case IResource.PROJECT :
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_DELETE, this));
+ break;
+ case IResource.ROOT :
+ // all root children including hidden projects will be deleted so notify
+ IResource[] projects = ((Container) this).getChildren(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_DELETE, projects[i]));
+ }
+ }
+
+ private void broadcastPreMoveEvent(final IResource destination, int updateFlags) throws CoreException {
+ switch (getType()) {
+ case IResource.FILE :
+ if (isLinked())
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_LINK_MOVE, this, destination, updateFlags));
+ break;
+ case IResource.FOLDER :
+ if (isLinked())
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_LINK_MOVE, this, destination, updateFlags));
+ break;
+ case IResource.PROJECT :
+ if (!getName().equals(destination.getName())) {
+ // if there is a change in name, we are deleting the source project so notify.
+ workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_MOVE, this, destination, updateFlags));
+ }
+ break;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceException.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A checked exception representing a failure.
+ * <p>
+ * Resource exceptions contain a status object describing the cause of the
+ * exception, and optionally the path of the resource where the failure
+ * occurred.
+ * </p>
+ *
+ * @see IStatus
+ */
+public class ResourceException extends CoreException {
+ /**
+ * All serializable objects should have a stable serialVersionUID
+ */
+ private static final long serialVersionUID = 1L;
+
+ public ResourceException(int code, IPath path, String message, Throwable exception) {
+ super(new ResourceStatus(code, path, message, exception));
+ }
+
+ /**
+ * Constructs a new exception with the given status object.
+ *
+ * @param status the status object to be associated with this exception
+ * @see IStatus
+ */
+ public ResourceException(IStatus status) {
+ super(status);
+ }
+
+ /**
+ * Prints a stack trace out for the exception, and
+ * any nested exception that it may have embedded in
+ * its Status object.
+ */
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Prints a stack trace out for the exception, and
+ * any nested exception that it may have embedded in
+ * its Status object.
+ */
+ public void printStackTrace(PrintStream output) {
+ synchronized (output) {
+ IStatus status = getStatus();
+ if (status.getException() != null) {
+ String path = "()"; //$NON-NLS-1$
+ if (status instanceof IResourceStatus)
+ path = "(" + ((IResourceStatus) status).getPath() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ output.print(getClass().getName() + path + "[" + status.getCode() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$
+ status.getException().printStackTrace(output);
+ } else
+ super.printStackTrace(output);
+ }
+ }
+
+ /**
+ * Prints a stack trace out for the exception, and
+ * any nested exception that it may have embedded in
+ * its Status object.
+ */
+ public void printStackTrace(PrintWriter output) {
+ synchronized (output) {
+ IStatus status = getStatus();
+ if (status.getException() != null) {
+ String path = "()"; //$NON-NLS-1$
+ if (status instanceof IResourceStatus)
+ path = "(" + ((IResourceStatus) status).getPath() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ output.print(getClass().getName() + path + "[" + status.getCode() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$
+ status.getException().printStackTrace(output);
+ } else
+ super.printStackTrace(output);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceInfo.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Oakland Software Incorporated - added getSessionProperties and getPersistentProperties
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.Map;
+import org.eclipse.core.internal.localstore.FileStoreRoot;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.IElementTreeData;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * A data structure containing the in-memory state of a resource in the workspace.
+ */
+public class ResourceInfo implements IElementTreeData, ICoreConstants, IStringPoolParticipant {
+ protected static final int LOWER = 0xFFFF;
+ protected static final int UPPER = 0xFFFF0000;
+
+ /**
+ * This field stores the resource modification stamp in the lower two bytes,
+ * and the character set generation count in the higher two bytes.
+ */
+ protected volatile int charsetAndContentId = 0;
+
+ /**
+ * The file system root that this resource is stored in
+ */
+ protected FileStoreRoot fileStoreRoot;
+
+ /** Set of flags which reflect various states of the info (used, derived, ...). */
+ protected int flags = 0;
+
+ /** Local sync info */
+ // thread safety: (Concurrency004)
+ protected volatile long localInfo = I_NULL_SYNC_INFO;
+
+ /**
+ * This field stores the sync info generation in the lower two bytes, and
+ * the marker generation count in the upper two bytes.
+ */
+ protected volatile int markerAndSyncStamp;
+
+ /** The collection of markers for this resource. */
+ protected MarkerSet markers = null;
+
+ /** Modification stamp */
+ protected long modStamp = 0;
+
+ /** Unique node identifier */
+ // thread safety: (Concurrency004)
+ protected volatile long nodeId = 0;
+
+ /**
+ * The properties which are maintained for the lifecycle of the workspace.
+ * <p>
+ * This field is declared as the implementing class rather than the
+ * interface so we ensure that we get it right since we are making certain
+ * assumptions about the object type w.r.t. casting.
+ */
+ protected ObjectMap sessionProperties = null;
+
+ /**
+ * The table of sync information.
+ * <p>
+ * This field is declared as the implementing class rather than the
+ * interface so we ensure that we get it right since we are making certain
+ * assumptions about the object type w.r.t. casting.
+ */
+ protected ObjectMap syncInfo = null;
+
+ /**
+ * Returns the integer value stored in the indicated part of this info's flags.
+ */
+ protected static int getBits(int flags, int mask, int start) {
+ return (flags & mask) >> start;
+ }
+
+ /**
+ * Returns the type setting for this info. Valid values are
+ * FILE, FOLDER, PROJECT,
+ */
+ public static int getType(int flags) {
+ return getBits(flags, M_TYPE, M_TYPE_START);
+ }
+
+ /**
+ * Returns true if all of the bits indicated by the mask are set.
+ */
+ public static boolean isSet(int flags, int mask) {
+ return (flags & mask) == mask;
+ }
+
+ /**
+ * Clears all of the bits indicated by the mask.
+ */
+ public void clear(int mask) {
+ flags &= ~mask;
+ }
+
+ public void clearModificationStamp() {
+ modStamp = IResource.NULL_STAMP;
+ }
+
+ public synchronized void clearSessionProperties() {
+ sessionProperties = null;
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null; // never gets here.
+ }
+ }
+
+ public int getCharsetGenerationCount() {
+ return charsetAndContentId >> 16;
+ }
+
+ public int getContentId() {
+ return charsetAndContentId & LOWER;
+ }
+
+ public FileStoreRoot getFileStoreRoot() {
+ return fileStoreRoot;
+ }
+
+ /**
+ * Returns the set of flags for this info.
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Gets the local-relative sync information.
+ */
+ public long getLocalSyncInfo() {
+ return localInfo;
+ }
+
+ /**
+ * Returns the marker generation count.
+ * The count is incremented whenever markers on the resource change.
+ */
+ public int getMarkerGenerationCount() {
+ return markerAndSyncStamp >> 16;
+ }
+
+ /**
+ * Returns a copy of the collection of makers on this resource.
+ * <code>null</code> is returned if there are none.
+ */
+ public MarkerSet getMarkers() {
+ return getMarkers(true);
+ }
+
+ /**
+ * Returns the collection of makers on this resource.
+ * <code>null</code> is returned if there are none.
+ */
+ public MarkerSet getMarkers(boolean makeCopy) {
+ if (markers == null)
+ return null;
+ return makeCopy ? (MarkerSet) markers.clone() : markers;
+ }
+
+ public long getModificationStamp() {
+ return modStamp;
+ }
+
+ public long getNodeId() {
+ return nodeId;
+ }
+
+ /**
+ * Returns the property store associated with this info. The return value may be null.
+ */
+ public Object getPropertyStore() {
+ return null;
+ }
+
+ /**
+ * Returns a copy of the map of this resource session properties.
+ * An empty map is returned if there are none.
+ */
+ public Map getSessionProperties() {
+ // thread safety: (Concurrency001)
+ ObjectMap temp = sessionProperties;
+ if (temp == null)
+ temp = new ObjectMap(5);
+ else
+ temp = (ObjectMap) sessionProperties.clone();
+ return temp;
+ }
+
+ /**
+ * Returns the value of the identified session property
+ */
+ public Object getSessionProperty(QualifiedName name) {
+ // thread safety: (Concurrency001)
+ Map temp = sessionProperties;
+ if (temp == null)
+ return null;
+ return temp.get(name);
+ }
+
+ /**
+ * The parameter to this method is the implementing class rather than the
+ * interface so we ensure that we get it right since we are making certain
+ * assumptions about the object type w.r.t. casting.
+ */
+ public synchronized ObjectMap getSyncInfo(boolean makeCopy) {
+ if (syncInfo == null)
+ return null;
+ return makeCopy ? (ObjectMap) syncInfo.clone() : syncInfo;
+ }
+
+ public synchronized byte[] getSyncInfo(QualifiedName id, boolean makeCopy) {
+ // thread safety: (Concurrency001)
+ byte[] b;
+ if (syncInfo == null)
+ return null;
+ b = (byte[]) syncInfo.get(id);
+ return b == null ? null : (makeCopy ? (byte[]) b.clone() : b);
+ }
+
+ /**
+ * Returns the sync information generation count.
+ * The count is incremented whenever sync info on the resource changes.
+ */
+ public int getSyncInfoGenerationCount() {
+ return markerAndSyncStamp & LOWER;
+ }
+
+ /**
+ * Returns the type setting for this info. Valid values are
+ * FILE, FOLDER, PROJECT,
+ */
+ public int getType() {
+ return getType(flags);
+ }
+
+ /**
+ * Increments the charset generation count.
+ * The count is incremented whenever the encoding on the resource changes.
+ */
+ public void incrementCharsetGenerationCount() {
+ //increment high order bits
+ charsetAndContentId = ((charsetAndContentId + LOWER + 1) & UPPER) + (charsetAndContentId & LOWER);
+ }
+
+ /**
+ * Mark this resource info as having changed content
+ */
+ public void incrementContentId() {
+ //increment low order bits
+ charsetAndContentId = (charsetAndContentId & UPPER) + ((charsetAndContentId + 1) & LOWER);
+ }
+
+ /**
+ * Increments the marker generation count.
+ * The count is incremented whenever markers on the resource change.
+ */
+ public void incrementMarkerGenerationCount() {
+ //increment high order bits
+ markerAndSyncStamp = ((markerAndSyncStamp + LOWER + 1) & UPPER) + (markerAndSyncStamp & LOWER);
+ }
+
+ /**
+ * Change the modification stamp to indicate that this resource has changed.
+ * The exact value of the stamp doesn't matter, as long as it can be used to
+ * distinguish two arbitrary resource generations.
+ */
+ public void incrementModificationStamp() {
+ modStamp++;
+ }
+
+ /**
+ * Increments the sync information generation count.
+ * The count is incremented whenever sync info on the resource changes.
+ */
+ public void incrementSyncInfoGenerationCount() {
+ //increment low order bits
+ markerAndSyncStamp = (markerAndSyncStamp & UPPER) + ((markerAndSyncStamp + 1) & LOWER);
+ }
+
+ /**
+ * Returns true if all of the bits indicated by the mask are set.
+ */
+ public boolean isSet(int mask) {
+ return (flags & mask) == mask;
+ }
+
+ public void readFrom(int newFlags, DataInput input) throws IOException {
+ // The flags for this info are read by the visitor (flattener).
+ // See Workspace.readElement(). This allows the reader to look ahead
+ // and see what type of info is being loaded.
+ this.flags = newFlags;
+ localInfo = input.readLong();
+ nodeId = input.readLong();
+ charsetAndContentId = input.readInt() & LOWER;
+ modStamp = input.readLong();
+ }
+
+ /**
+ * Sets all of the bits indicated by the mask.
+ */
+ public void set(int mask) {
+ flags |= mask;
+ }
+
+ /**
+ * Sets the value of the indicated bits to be the given value.
+ */
+ protected void setBits(int mask, int start, int value) {
+ int baseMask = mask >> start;
+ int newValue = (value & baseMask) << start;
+ // thread safety: (guarantee atomic assignment)
+ int temp = flags;
+ temp &= ~mask;
+ temp |= newValue;
+ flags = temp;
+ }
+
+ public void setFileStoreRoot(FileStoreRoot fileStoreRoot) {
+ this.fileStoreRoot = fileStoreRoot;
+ }
+
+ /**
+ * Sets the flags for this info.
+ */
+ protected void setFlags(int value) {
+ flags = value;
+ }
+
+ /**
+ * Sets the local-relative sync information.
+ */
+ public void setLocalSyncInfo(long info) {
+ localInfo = info;
+ }
+
+ /**
+ * Sets the collection of makers for this resource.
+ * <code>null</code> is passed in if there are no markers.
+ */
+ public void setMarkers(MarkerSet value) {
+ markers = value;
+ }
+
+ /**
+ * Sets the resource modification stamp.
+ */
+ public void setModificationStamp(long value) {
+ this.modStamp = value;
+ }
+
+ /**
+ *
+ */
+ public void setNodeId(long id) {
+ nodeId = id;
+ }
+
+ /**
+ * Sets the property store associated with this info. The value may be null.
+ */
+ public void setPropertyStore(Object value) {
+ // needs to be implemented on subclasses
+ }
+
+ /**
+ * Sets the identified session property to the given value. If
+ * the value is null, the property is removed.
+ */
+ public synchronized void setSessionProperty(QualifiedName name, Object value) {
+ // thread safety: (Concurrency001)
+ if (value == null) {
+ if (sessionProperties == null)
+ return;
+ ObjectMap temp = (ObjectMap) sessionProperties.clone();
+ temp.remove(name);
+ if (temp.isEmpty())
+ sessionProperties = null;
+ else
+ sessionProperties = temp;
+ } else {
+ ObjectMap temp = sessionProperties;
+ if (temp == null)
+ temp = new ObjectMap(5);
+ else
+ temp = (ObjectMap) sessionProperties.clone();
+ temp.put(name, value);
+ sessionProperties = temp;
+ }
+ }
+
+ /**
+ * The parameter to this method is the implementing class rather than the
+ * interface so we ensure that we get it right since we are making certain
+ * assumptions about the object type w.r.t. casting.
+ */
+ protected void setSyncInfo(ObjectMap syncInfo) {
+ this.syncInfo = syncInfo;
+ }
+
+ public synchronized void setSyncInfo(QualifiedName id, byte[] value) {
+ if (value == null) {
+ //delete sync info
+ if (syncInfo == null)
+ return;
+ syncInfo.remove(id);
+ if (syncInfo.isEmpty())
+ syncInfo = null;
+ } else {
+ //add sync info
+ if (syncInfo == null)
+ syncInfo = new ObjectMap(5);
+ syncInfo.put(id, value.clone());
+ }
+ }
+
+ /**
+ * Sets the type for this info to the given value. Valid values are
+ * FILE, FOLDER, PROJECT
+ */
+ public void setType(int value) {
+ setBits(M_TYPE, M_TYPE_START, value);
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool set) {
+ ObjectMap map = syncInfo;
+ if (map != null)
+ map.shareStrings(set);
+ map = sessionProperties;
+ if (map != null)
+ map.shareStrings(set);
+ MarkerSet markerSet = markers;
+ if (markerSet != null)
+ markerSet.shareStrings(set);
+ }
+
+ public void writeTo(DataOutput output) throws IOException {
+ // The flags for this info are written by the visitor (flattener).
+ // See SaveManager.writeElement(). This allows the reader to look ahead
+ // and see what type of info is being loaded.
+ output.writeLong(localInfo);
+ output.writeLong(nodeId);
+ output.writeInt(getContentId());
+ output.writeLong(modStamp);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceProxy.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.watson.IPathRequestor;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * Implements a resource proxy given a path requestor and the resource
+ * info of the resource currently being visited.
+ */
+public class ResourceProxy implements IResourceProxy, ICoreConstants {
+ protected final Workspace workspace = (Workspace) ResourcesPlugin.getWorkspace();
+ protected IPathRequestor requestor;
+ protected ResourceInfo info;
+
+ //cached info
+ protected IPath fullPath;
+ protected IResource resource;
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#getModificationStamp()
+ */
+ public long getModificationStamp() {
+ return info.getModificationStamp();
+ }
+
+ public String getName() {
+ return requestor.requestName();
+ }
+
+ public Object getSessionProperty(QualifiedName key) {
+ return info.getSessionProperty(key);
+ }
+
+ public int getType() {
+ return info.getType();
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#isAccessible()
+ */
+ public boolean isAccessible() {
+ int flags = info.getFlags();
+ if (info.getType() == IResource.PROJECT)
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_OPEN);
+ return flags != NULL_FLAG;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#isDerived()
+ */
+ public boolean isDerived() {
+ int flags = info.getFlags();
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_DERIVED);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#isLinked()
+ */
+ public boolean isLinked() {
+ int flags = info.getFlags();
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_LINK);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#isPhantom()
+ */
+ public boolean isPhantom() {
+ int flags = info.getFlags();
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_PHANTOM);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#isTeamPrivateMember()
+ */
+ public boolean isTeamPrivateMember() {
+ int flags = info.getFlags();
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_TEAM_PRIVATE_MEMBER);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#isHidden()
+ */
+ public boolean isHidden() {
+ int flags = info.getFlags();
+ return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_HIDDEN);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#requestFullPath()
+ */
+ public IPath requestFullPath() {
+ if (fullPath == null)
+ fullPath = requestor.requestPath();
+ return fullPath;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IResourceProxy#requestResource()
+ */
+ public IResource requestResource() {
+ if (resource == null)
+ resource = workspace.newResource(requestFullPath(), info.getType());
+ return resource;
+ }
+
+ protected void reset() {
+ fullPath = null;
+ resource = null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceStatus.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+
+/**
+ *
+ */
+public class ResourceStatus extends Status implements IResourceStatus {
+ IPath path;
+
+ public ResourceStatus(int type, int code, IPath path, String message, Throwable exception) {
+ super(type, ResourcesPlugin.PI_RESOURCES, code, message, exception);
+ this.path = path;
+ }
+
+ public ResourceStatus(int code, String message) {
+ this(getSeverity(code), code, null, message, null);
+ }
+
+ public ResourceStatus(int code, IPath path, String message) {
+ this(getSeverity(code), code, path, message, null);
+ }
+
+ public ResourceStatus(int code, IPath path, String message, Throwable exception) {
+ this(getSeverity(code), code, path, message, exception);
+ }
+
+ /**
+ * @see IResourceStatus#getPath()
+ */
+ public IPath getPath() {
+ return path;
+ }
+
+ protected static int getSeverity(int code) {
+ return code == 0 ? 0 : 1 << (code % 100 / 33);
+ }
+
+ // for debug only
+ private String getTypeName() {
+ switch (getSeverity()) {
+ case IStatus.OK :
+ return "OK"; //$NON-NLS-1$
+ case IStatus.ERROR :
+ return "ERROR"; //$NON-NLS-1$
+ case IStatus.INFO :
+ return "INFO"; //$NON-NLS-1$
+ case IStatus.WARNING :
+ return "WARNING"; //$NON-NLS-1$
+ default :
+ return String.valueOf(getSeverity());
+ }
+ }
+
+ // for debug only
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[type: "); //$NON-NLS-1$
+ sb.append(getTypeName());
+ sb.append("], [path: "); //$NON-NLS-1$
+ sb.append(getPath());
+ sb.append("], [message: "); //$NON-NLS-1$
+ sb.append(getMessage());
+ sb.append("], [plugin: "); //$NON-NLS-1$
+ sb.append(getPlugin());
+ sb.append("], [exception: "); //$NON-NLS-1$
+ sb.append(getException());
+ sb.append("]\n"); //$NON-NLS-1$
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1142 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.net.URI;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.localstore.FileSystemResourceManager;
+import org.eclipse.core.internal.properties.IPropertyManager;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.IResourceTree;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @since 2.0
+ *
+ * Implementation note: Since the move/delete hook involves running third
+ * party code, the workspace lock is not held. This means the workspace
+ * lock must be re-acquired whenever we need to manipulate the workspace
+ * in any way. All entry points from third party code back into the tree must
+ * be done in an acquire/release pair.
+ */
+class ResourceTree implements IResourceTree {
+
+ private boolean isValid = true;
+ private final FileSystemResourceManager localManager;
+ /**
+ * The lock to acquire when the workspace needs to be manipulated
+ */
+ private ILock lock;
+ private MultiStatus multistatus;
+ private int updateFlags;
+
+ /**
+ * Constructor for this class.
+ */
+ public ResourceTree(FileSystemResourceManager localManager, ILock lock, MultiStatus status, int updateFlags) {
+ super();
+ this.localManager = localManager;
+ this.lock = lock;
+ this.multistatus = status;
+ this.updateFlags = updateFlags;
+ }
+
+ /**
+ * @see IResourceTree#addToLocalHistory(IFile)
+ */
+ public void addToLocalHistory(IFile file) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ if (!file.exists())
+ return;
+ IFileStore store = localManager.getStore(file);
+ final IFileInfo fileInfo = store.fetchInfo();
+ if (!fileInfo.exists())
+ return;
+ localManager.getHistoryStore().addState(file.getFullPath(), store, fileInfo, false);
+ } finally {
+ lock.release();
+ }
+ }
+
+ private IFileStore computeDestinationStore(IProjectDescription destDescription) throws CoreException {
+ URI destLocation = destDescription.getLocationURI();
+ // Use the default area if necessary for the destination.
+ if (destLocation == null) {
+ IPath rootLocation = ResourcesPlugin.getWorkspace().getRoot().getLocation();
+ destLocation = rootLocation.append(destDescription.getName()).toFile().toURI();
+ }
+ return EFS.getStore(destLocation);
+ }
+
+ /**
+ * @see IResourceTree#computeTimestamp(IFile)
+ */
+ public long computeTimestamp(IFile file) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ if (!file.getProject().exists())
+ return NULL_TIMESTAMP;
+ return internalComputeTimestamp(file);
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * Copies the local history of source to destination. Note that if source
+ * is an IFolder, it is assumed that the same structure exists under destination
+ * and the local history of any IFile under source will be copied to the
+ * associated IFile under destination.
+ */
+ private void copyLocalHistory(IResource source, IResource destination) {
+ localManager.getHistoryStore().copyHistory(source, destination, true);
+ }
+
+ /**
+ * @see IResourceTree#deletedFile(IFile)
+ */
+ public void deletedFile(IFile file) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the resource doesn't exist.
+ if (!file.exists())
+ return;
+ try {
+ // Delete properties, generate marker deltas, and remove the node from the workspace tree.
+ ((Resource) file).deleteResource(true, null);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorDeleting, file.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, file.getFullPath(), message, e);
+ failed(status);
+ }
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * @see IResourceTree#deletedFolder(IFolder)
+ */
+ public void deletedFolder(IFolder folder) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the resource doesn't exist.
+ if (!folder.exists())
+ return;
+ try {
+ // Delete properties, generate marker deltas, and remove the node from the workspace tree.
+ ((Resource) folder).deleteResource(true, null);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorDeleting, folder.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, folder.getFullPath(), message, e);
+ failed(status);
+ }
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * @see IResourceTree#deletedProject(IProject)
+ */
+ public void deletedProject(IProject target) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the resource doesn't exist.
+ if (!target.exists())
+ return;
+ // Delete properties, generate marker deltas, and remove the node from the workspace tree.
+ try {
+ ((Project) target).deleteResource(false, null);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorDeleting, target.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, target.getFullPath(), message, e);
+ // log the status but don't return until we try and delete the rest of the project info
+ failed(status);
+ }
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * Makes sure that the destination directory for a project move is unoccupied.
+ * Returns true if successful, and false if the move should be aborted
+ */
+ private boolean ensureDestinationEmpty(IProject source, IFileStore destinationStore, IProgressMonitor monitor) throws CoreException {
+ String message;
+ //Make sure the destination location is unoccupied
+ if (!destinationStore.fetchInfo().exists())
+ return true;
+ //check for existing children
+ if (destinationStore.childNames(EFS.NONE, Policy.subMonitorFor(monitor, 0)).length > 0) {
+ //allow case rename to proceed
+ if (((Resource) source).getStore().equals(destinationStore))
+ return true;
+ //fail because the destination is occupied
+ message = NLS.bind(Messages.localstore_resourceExists, destinationStore);
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, null);
+ failed(status);
+ return false;
+ }
+ //delete the destination directory to allow for efficient renaming
+ destinationStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, 0));
+ return true;
+ }
+
+ /**
+ * This operation has failed for the given reason. Add it to this
+ * resource tree's status.
+ */
+ public void failed(IStatus reason) {
+ Assert.isLegal(isValid);
+ multistatus.add(reason);
+ }
+
+ /**
+ * Returns the status object held onto by this resource tree.
+ */
+ protected IStatus getStatus() {
+ return multistatus;
+ }
+
+ /**
+ * @see IResourceTree#getTimestamp(IFile)
+ */
+ public long getTimestamp(IFile file) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ if (!file.exists())
+ return NULL_TIMESTAMP;
+ ResourceInfo info = ((File) file).getResourceInfo(false, false);
+ return info == null ? NULL_TIMESTAMP : info.getLocalSyncInfo();
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * Returns the local timestamp for a file.
+ *
+ * @param file
+ * @return The local file system timestamp
+ */
+ private long internalComputeTimestamp(IFile file) {
+ IFileInfo fileInfo = localManager.getStore(file).fetchInfo();
+ return fileInfo.exists() ? fileInfo.getLastModified() : NULL_TIMESTAMP;
+ }
+
+ /**
+ * Helper method for #standardDeleteFile. Returns a boolean indicating whether or
+ * not the delete was successful.
+ */
+ private boolean internalDeleteFile(IFile file, int flags, IProgressMonitor monitor) {
+ try {
+ String message = NLS.bind(Messages.resources_deleting, file.getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ Policy.checkCanceled(monitor);
+
+ // Do nothing if the file doesn't exist in the workspace.
+ if (!file.exists()) {
+ // Indicate that the delete was successful.
+ return true;
+ }
+ // Don't delete contents if this is a linked resource
+ if (file.isLinked()) {
+ deletedFile(file);
+ return true;
+ }
+ // If the file doesn't exist on disk then signal to the workspace to delete the
+ // file and return.
+ IFileStore fileStore = localManager.getStore(file);
+ boolean localExists = fileStore.fetchInfo().exists();
+ if (!localExists) {
+ deletedFile(file);
+ // Indicate that the delete was successful.
+ return true;
+ }
+
+ boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0;
+ boolean force = (flags & IResource.FORCE) != 0;
+
+ // Add the file to the local history if requested by the user.
+ if (keepHistory)
+ addToLocalHistory(file);
+ monitor.worked(Policy.totalWork / 4);
+
+ // We want to fail if force is false and the file is not synchronized with the
+ // local file system.
+ if (!force) {
+ boolean inSync = isSynchronized(file, IResource.DEPTH_ZERO);
+ // only want to fail if the file still exists.
+ if (!inSync && localExists) {
+ message = NLS.bind(Messages.localstore_resourceIsOutOfSync, file.getFullPath());
+ IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, file.getFullPath(), message);
+ failed(status);
+ // Indicate that the delete was unsuccessful.
+ return false;
+ }
+ }
+ monitor.worked(Policy.totalWork / 4);
+
+ // Try to delete the file from the file system.
+ try {
+ fileStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork / 4));
+ // If the file was successfully deleted from the file system the
+ // workspace tree should be updated accordingly.
+ deletedFile(file);
+ // Indicate that the delete was successful.
+ return true;
+ } catch (CoreException e) {
+ message = NLS.bind(Messages.resources_couldnotDelete, fileStore.toString());
+ IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message, e);
+ failed(status);
+ }
+ // Indicate that the delete was unsuccessful.
+ return false;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Helper method for #standardDeleteFolder. Returns a boolean indicating
+ * whether or not the deletion of this folder was successful. Does a best effort
+ * delete of this resource and its children.
+ */
+ private boolean internalDeleteFolder(IFolder folder, int flags, IProgressMonitor monitor) {
+ String message = NLS.bind(Messages.resources_deleting, folder.getFullPath());
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ monitor.subTask(message);
+ Policy.checkCanceled(monitor);
+
+ // Do nothing if the folder doesn't exist in the workspace.
+ if (!folder.exists())
+ return true;
+
+ // Don't delete contents if this is a linked resource
+ if (folder.isLinked()) {
+ deletedFolder(folder);
+ return true;
+ }
+
+ // If the folder doesn't exist on disk then update the tree and return.
+ IFileStore fileStore = localManager.getStore(folder);
+ if (!fileStore.fetchInfo().exists()) {
+ deletedFolder(folder);
+ return true;
+ }
+
+ try {
+ //this will delete local and workspace
+ localManager.delete(folder, flags, Policy.subMonitorFor(monitor, Policy.totalWork));
+ } catch (CoreException ce) {
+ message = NLS.bind(Messages.localstore_couldnotDelete, folder.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_DELETE_LOCAL, folder.getFullPath(), message, ce);
+ failed(status);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does a best-effort delete on this resource and all its children.
+ */
+ private boolean internalDeleteProject(IProject project, int flags, IProgressMonitor monitor) {
+ // Recursively delete each member of the project.
+ IResource[] members = null;
+ try {
+ members = project.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMembers, project.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, project.getFullPath(), message, e);
+ failed(status);
+ // Indicate that the delete was unsuccessful.
+ return false;
+ }
+ boolean deletedChildren = true;
+ for (int i = 0; i < members.length; i++) {
+ IResource child = members[i];
+ switch (child.getType()) {
+ case IResource.FILE :
+ // ignore the .project file for now and delete it last
+ if (!IProjectDescription.DESCRIPTION_FILE_NAME.equals(child.getName()))
+ deletedChildren &= internalDeleteFile((IFile) child, flags, Policy.subMonitorFor(monitor, Policy.totalWork / members.length));
+ break;
+ case IResource.FOLDER :
+ deletedChildren &= internalDeleteFolder((IFolder) child, flags, Policy.subMonitorFor(monitor, Policy.totalWork / members.length));
+ break;
+ }
+ }
+ IFileStore projectStore = localManager.getStore(project);
+ // Check to see if the children were deleted ok. If there was a problem
+ // just return as the problem should have been logged by the recursive
+ // call to the child.
+ if (!deletedChildren)
+ // Indicate that the delete was unsuccessful.
+ return false;
+
+ //Check if there are any undiscovered children of the project on disk other than description file
+ String[] children;
+ try {
+ children = projectStore.childNames(EFS.NONE, null);
+ } catch (CoreException e) {
+ //treat failure to access the directory as a non-existent directory
+ children = new String[0];
+ }
+ boolean force = BitMask.isSet(flags, IResource.FORCE);
+ if (!force && (children.length != 1 || !IProjectDescription.DESCRIPTION_FILE_NAME.equals(children[0]))) {
+ String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, project.getName());
+ failed(new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, project.getFullPath(), message));
+ return false;
+ }
+
+ //Now delete the project description file
+ IResource file = project.findMember(IProjectDescription.DESCRIPTION_FILE_NAME);
+ if (file == null) {
+ //the .project have may have been recreated on disk automatically by snapshot
+ IFileStore dotProject = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME);
+ try {
+ dotProject.delete(EFS.NONE, null);
+ } catch (CoreException e) {
+ failed(e.getStatus());
+ }
+ } else {
+ boolean deletedProjectFile = internalDeleteFile((IFile) file, flags, Policy.monitorFor(null));
+ if (!deletedProjectFile) {
+ String message = NLS.bind(Messages.resources_couldnotDelete, file.getFullPath());
+ IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message);
+ failed(status);
+ // Indicate that the delete was unsuccessful.
+ return false;
+ }
+ }
+
+ //children are deleted, so now delete the parent
+ try {
+ projectStore.delete(EFS.NONE, null);
+ deletedProject(project);
+ // Indicate that the delete was successful.
+ return true;
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_couldnotDelete, projectStore.toString());
+ IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message, e);
+ failed(status);
+ // Indicate that the delete was unsuccessful.
+ return false;
+ }
+ }
+
+ /**
+ * Return <code>true</code> if there is a change in the content area for the project.
+ */
+ private boolean isContentChange(IProject project, IProjectDescription destDescription) {
+ IProjectDescription srcDescription = ((Project) project).internalGetDescription();
+ URI srcLocation = srcDescription.getLocationURI();
+ URI destLocation = destDescription.getLocationURI();
+ if (srcLocation == null || destLocation == null)
+ return true;
+ //don't use URIUtil because we want to treat case rename as a content change
+ return !srcLocation.equals(destLocation);
+ }
+
+ /**
+ * Return <code>true</code> if there is a change in the name of the project.
+ */
+ private boolean isNameChange(IProject project, IProjectDescription description) {
+ return !project.getName().equals(description.getName());
+ }
+
+ /**
+ * Refreshes the resource hierarchy with its children. In case of failure
+ * adds an appropriate status to the resource tree's status.
+ */
+ private void safeRefresh(IResource resource) {
+ try {
+ resource.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException ce) {
+ IStatus status = new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_DELETE_LOCAL, resource.getFullPath(), Messages.refresh_refreshErr, ce);
+ failed(status);
+ }
+ }
+
+ /**
+ * @see IResourceTree#isSynchronized(IResource, int)
+ */
+ public boolean isSynchronized(IResource resource, int depth) {
+ try {
+ lock.acquire();
+ return localManager.isSynchronized(resource, depth);
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * The specific operation for which this tree was created has completed and this tree
+ * should not be used anymore. Ensure that this is the case by making it invalid. This
+ * is checked by all API methods.
+ */
+ void makeInvalid() {
+ this.isValid = false;
+ }
+
+ /**
+ * @see IResourceTree#movedFile(IFile, IFile)
+ */
+ public void movedFile(IFile source, IFile destination) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the resource doesn't exist.
+ if (!source.exists())
+ return;
+ // If the destination already exists then we have a problem.
+ if (destination.exists()) {
+ String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
+ // log the status but don't return until we try and move the rest of the resource information.
+ failed(status);
+ }
+
+ // Move the resource's persistent properties.
+ IPropertyManager propertyManager = ((Resource) source).getPropertyManager();
+ try {
+ propertyManager.copy(source, destination, IResource.DEPTH_ZERO);
+ propertyManager.deleteProperties(source, IResource.DEPTH_ZERO);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorPropertiesMove, source.getFullPath(), destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource information.
+ failed(status);
+ }
+
+ // Move the node in the workspace tree.
+ Workspace workspace = (Workspace) source.getWorkspace();
+ try {
+ workspace.move((Resource) source, destination.getFullPath(), IResource.DEPTH_ZERO, updateFlags, false);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource information.
+ failed(status);
+ }
+
+ // Generate the marker deltas.
+ try {
+ workspace.getMarkerManager().moved(source, destination, IResource.DEPTH_ZERO);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMarkersDelete, source.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ failed(status);
+ }
+
+ // Copy the local history information
+ copyLocalHistory(source, destination);
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * @see IResourceTree#movedFolderSubtree(IFolder, IFolder)
+ */
+ public void movedFolderSubtree(IFolder source, IFolder destination) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the source resource doesn't exist.
+ if (!source.exists())
+ return;
+ // If the destination already exists then we have an error.
+ if (destination.exists()) {
+ String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
+ failed(status);
+ return;
+ }
+
+ // Move the folder properties.
+ int depth = IResource.DEPTH_INFINITE;
+ IPropertyManager propertyManager = ((Resource) source).getPropertyManager();
+ try {
+ propertyManager.copy(source, destination, depth);
+ propertyManager.deleteProperties(source, depth);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorPropertiesMove, source.getFullPath(), destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource info
+ failed(status);
+ }
+
+ // Create the destination node in the tree.
+ Workspace workspace = (Workspace) source.getWorkspace();
+ try {
+ workspace.move((Resource) source, destination.getFullPath(), depth, updateFlags, false);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource info
+ failed(status);
+ }
+
+ // Generate the marker deltas.
+ try {
+ workspace.getMarkerManager().moved(source, destination, depth);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMarkersDelete, source.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ failed(status);
+ }
+
+ // Copy the local history for this folder
+ copyLocalHistory(source, destination);
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * @see IResourceTree#movedProjectSubtree(IProject, IProjectDescription)
+ */
+ public boolean movedProjectSubtree(IProject project, IProjectDescription destDescription) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the source resource doesn't exist.
+ if (!project.exists())
+ return true;
+
+ Project source = (Project) project;
+ Project destination = (Project) source.getWorkspace().getRoot().getProject(destDescription.getName());
+ Workspace workspace = (Workspace) source.getWorkspace();
+ int depth = IResource.DEPTH_INFINITE;
+
+ // If the name of the source and destination projects are not the same then
+ // rename the meta area and make changes in the tree.
+ if (isNameChange(source, destDescription)) {
+ if (destination.exists()) {
+ String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
+ failed(status);
+ return false;
+ }
+
+ // Rename the project metadata area. Close the property store to flush everything to disk
+ try {
+ source.getPropertyManager().closePropertyStore(source);
+ localManager.getHistoryStore().closeHistoryStore(source);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.properties_couldNotClose, source.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource info
+ failed(status);
+ }
+ final IFileSystem fileSystem = EFS.getLocalFileSystem();
+ IFileStore oldMetaArea = fileSystem.getStore(workspace.getMetaArea().locationFor(source));
+ IFileStore newMetaArea = fileSystem.getStore(workspace.getMetaArea().locationFor(destination));
+ try {
+ oldMetaArea.move(newMetaArea, EFS.NONE, new NullProgressMonitor());
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_moveMeta, oldMetaArea, newMetaArea);
+ IStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, destination.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource info
+ failed(status);
+ }
+
+ // Move the workspace tree.
+ try {
+ workspace.move(source, destination.getFullPath(), depth, updateFlags, true);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource info
+ failed(status);
+ }
+
+ // Clear stale state on the destination project.
+ ((ProjectInfo) destination.getResourceInfo(false, true)).fixupAfterMove();
+
+ // Generate marker deltas.
+ try {
+ workspace.getMarkerManager().moved(source, destination, depth);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorMarkersMove, source.getFullPath(), destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ // log the status but don't return until we try and move the rest of the resource info
+ failed(status);
+ }
+ // Copy the local history
+ copyLocalHistory(source, destination);
+ }
+
+ // Write the new project description on the destination project.
+ try {
+ //moving linked resources may have modified the description in memory
+ ((ProjectDescription) destDescription).setLinkDescriptions(destination.internalGetDescription().getLinks());
+ destination.internalSetDescription(destDescription, true);
+ destination.writeDescription(IResource.FORCE);
+ } catch (CoreException e) {
+ String message = Messages.resources_projectDesc;
+ IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e);
+ failed(status);
+ }
+
+ // write the private project description, including the project location
+ try {
+ workspace.getMetaArea().writePrivateDescription(destination);
+ } catch (CoreException e) {
+ failed(e.getStatus());
+ }
+
+ // Do a refresh on the destination project to pick up any newly discovered resources
+ try {
+ destination.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_errorRefresh, destination.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e);
+ failed(status);
+ return false;
+ }
+ return true;
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * Helper method for moving the project content. Determines the content location
+ * based on the project description. (default location or user defined?)
+ */
+ private void moveProjectContent(IProject source, IFileStore destStore, int flags, IProgressMonitor monitor) throws CoreException {
+ try {
+ String message = NLS.bind(Messages.resources_moving, source.getFullPath());
+ monitor.beginTask(message, 10);
+ IProjectDescription srcDescription = source.getDescription();
+ URI srcLocation = srcDescription.getLocationURI();
+ // If the locations are the same (and non-default) then there is nothing to do.
+ if (srcLocation != null && URIUtil.equals(srcLocation, destStore.toURI()))
+ return;
+
+ //If this is a replace, just make sure the destination location exists, and return
+ boolean replace = (flags & IResource.REPLACE) != 0;
+ if (replace) {
+ destStore.mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 10));
+ return;
+ }
+
+ // Move the contents on disk.
+ localManager.move(source, destStore, flags, Policy.subMonitorFor(monitor, 9));
+
+ //if this is a deep move, move the contents of any linked resources
+ if ((flags & IResource.SHALLOW) == 0) {
+ IResource[] children = source.members();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i].isLinked()) {
+ message = NLS.bind(Messages.resources_moving, children[i].getFullPath());
+ monitor.subTask(message);
+ IFileStore linkDestination = destStore.getChild(children[i].getName());
+ try {
+ localManager.move(children[i], linkDestination, flags, Policy.monitorFor(null));
+ } catch (CoreException ce) {
+ //log the failure, but keep trying on remaining links
+ failed(ce.getStatus());
+ }
+ }
+ }
+ }
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IResourceTree#standardDeleteFile(IFile, int, IProgressMonitor)
+ */
+ public void standardDeleteFile(IFile file, int flags, IProgressMonitor monitor) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ internalDeleteFile(file, flags, monitor);
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * @see IResourceTree#standardDeleteFolder(IFolder, int, IProgressMonitor)
+ */
+ public void standardDeleteFolder(IFolder folder, int flags, IProgressMonitor monitor) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ internalDeleteFolder(folder, flags, monitor);
+ } catch (OperationCanceledException oce) {
+ safeRefresh(folder);
+ throw oce;
+ } finally {
+ lock.release();
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IResourceTree#standardDeleteProject(IProject, int, IProgressMonitor)
+ */
+ public void standardDeleteProject(IProject project, int flags, IProgressMonitor monitor) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ String message = NLS.bind(Messages.resources_deleting, project.getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+ // Do nothing if the project doesn't exist in the workspace tree.
+ if (!project.exists())
+ return;
+
+ boolean alwaysDeleteContent = (flags & IResource.ALWAYS_DELETE_PROJECT_CONTENT) != 0;
+ boolean neverDeleteContent = (flags & IResource.NEVER_DELETE_PROJECT_CONTENT) != 0;
+ boolean success = true;
+
+ // Delete project content. Don't do anything if the user specified explicitly asked
+ // not to delete the project content or if the project is closed and
+ // ALWAYS_DELETE_PROJECT_CONTENT was not specified.
+ if (alwaysDeleteContent || (project.isOpen() && !neverDeleteContent)) {
+ // Force is implied if alwaysDeleteContent is true or if the project is in sync
+ // with the local file system.
+ if (alwaysDeleteContent || isSynchronized(project, IResource.DEPTH_INFINITE)) {
+ flags |= IResource.FORCE;
+ }
+
+ // If the project is open we have to recursively try and delete all the files doing best-effort.
+ if (project.isOpen()) {
+ success = internalDeleteProject(project, flags, monitor);
+ if (!success) {
+ IFileStore store = localManager.getStore(project);
+ message = NLS.bind(Messages.resources_couldnotDelete, store.toString());
+ IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
+ failed(status);
+ }
+ return;
+ }
+
+ // If the project is closed we can short circuit this operation and delete all the files on disk.
+ // The .project file is deleted at the end of the operation.
+ try {
+ IFileStore projectStore = localManager.getStore(project);
+ IFileStore members[] = projectStore.childStores(EFS.NONE, null);
+ for (int i = 0; i < members.length; i++) {
+ if (!IProjectDescription.DESCRIPTION_FILE_NAME.equals(members[i].getName()))
+ members[i].delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork * 7 / 8 / members.length));
+ }
+ projectStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork * 7 / 8 / (members.length > 0 ? members.length : 1)));
+ } catch (OperationCanceledException oce) {
+ safeRefresh(project);
+ throw oce;
+ } catch (CoreException ce) {
+ message = NLS.bind(Messages.localstore_couldnotDelete, project.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message, ce);
+ failed(status);
+ return;
+ }
+ }
+
+ // Signal that the workspace tree should be updated that the project has been deleted.
+ if (success)
+ deletedProject(project);
+ else {
+ message = NLS.bind(Messages.localstore_couldnotDelete, project.getFullPath());
+ IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
+ failed(status);
+ }
+ } finally {
+ lock.release();
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IResourceTree#standardMoveFile(IFile, IFile, int, IProgressMonitor)
+ */
+ public void standardMoveFile(IFile source, IFile destination, int flags, IProgressMonitor monitor) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ String message = NLS.bind(Messages.resources_moving, source.getFullPath());
+ monitor.subTask(message);
+
+ // These pre-conditions should all be ok but just in case...
+ if (!source.exists() || destination.exists() || !destination.getParent().isAccessible())
+ throw new IllegalArgumentException();
+
+ boolean force = (flags & IResource.FORCE) != 0;
+ boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0;
+ boolean isDeep = (flags & IResource.SHALLOW) == 0;
+
+ // If the file is not in sync with the local file system and force is false,
+ // then signal that we have an error.
+ if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
+ message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath());
+ IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message);
+ failed(status);
+ return;
+ }
+ monitor.worked(Policy.totalWork / 4);
+
+ // Add the file contents to the local history if requested by the user.
+ if (keepHistory)
+ addToLocalHistory(source);
+ monitor.worked(Policy.totalWork / 4);
+
+ //for shallow move of linked resources, nothing needs to be moved in the file system
+ if (!isDeep && source.isLinked()) {
+ movedFile(source, destination);
+ return;
+ }
+
+ // If the file was successfully moved in the file system then the workspace
+ // tree needs to be updated accordingly. Otherwise signal that we have an error.
+ IFileStore destStore = null;
+ boolean failedDeletingSource = false;
+ try {
+ destStore = localManager.getStore(destination);
+ //ensure parent of destination exists
+ destStore.getParent().mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 0));
+ localManager.move(source, destStore, flags, monitor);
+ } catch (CoreException e) {
+ failed(e.getStatus());
+ // did the fail occur after copying to the destination?
+ failedDeletingSource = destStore != null && destStore.fetchInfo().exists();
+ // if so, we should proceed
+ if (!failedDeletingSource)
+ return;
+ }
+ movedFile(source, destination);
+ updateMovedFileTimestamp(destination, internalComputeTimestamp(destination));
+ if (failedDeletingSource) {
+ //recreate source file to ensure we are not out of sync
+ try {
+ source.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e) {
+ //ignore secondary failure - we have already logged the main failure
+ }
+ }
+ monitor.worked(Policy.totalWork / 4);
+ return;
+ } finally {
+ lock.release();
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IResourceTree#standardMoveFolder(IFolder, IFolder, int, IProgressMonitor)
+ */
+ public void standardMoveFolder(IFolder source, IFolder destination, int flags, IProgressMonitor monitor) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ String message = NLS.bind(Messages.resources_moving, source.getFullPath());
+ monitor.beginTask(message, 100);
+
+ // These pre-conditions should all be ok but just in case...
+ if (!source.exists() || destination.exists() || !destination.getParent().isAccessible())
+ throw new IllegalArgumentException();
+
+ // Check to see if we are synchronized with the local file system. If we are in sync then we can
+ // short circuit this method and do a file system only move. Otherwise we have to recursively
+ // try and move all resources, doing it in a best-effort manner.
+ boolean force = (flags & IResource.FORCE) != 0;
+ if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
+ message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message);
+ failed(status);
+ return;
+ }
+ monitor.worked(20);
+
+ //for linked resources, nothing needs to be moved in the file system
+ boolean isDeep = (flags & IResource.SHALLOW) == 0;
+ if (!isDeep && source.isLinked()) {
+ movedFolderSubtree(source, destination);
+ return;
+ }
+
+ // Move the resources in the file system. Only the FORCE flag is valid here so don't
+ // have to worry about clearing the KEEP_HISTORY flag.
+ IFileStore destStore = null;
+ boolean failedDeletingSource = false;
+ try {
+ destStore = localManager.getStore(destination);
+ localManager.move(source, destStore, flags, Policy.subMonitorFor(monitor, 60));
+ } catch (CoreException e) {
+ failed(e.getStatus());
+ // did the fail occur after copying to the destination?
+ failedDeletingSource = destStore != null && destStore.fetchInfo().exists();
+ // if so, we should proceed
+ if (!failedDeletingSource)
+ return;
+ }
+ movedFolderSubtree(source, destination);
+ monitor.worked(20);
+ updateTimestamps(destination, isDeep);
+ if (failedDeletingSource) {
+ //the move could have been partially successful, so refresh to ensure we are in sync
+ try {
+ source.refreshLocal(IResource.DEPTH_INFINITE, null);
+ destination.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e) {
+ //ignore secondary failures -we have already logged main failure
+ }
+ }
+ } finally {
+ lock.release();
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IResourceTree#standardMoveProject(IProject, IProjectDescription, int, IProgressMonitor)
+ */
+ public void standardMoveProject(IProject source, IProjectDescription description, int flags, IProgressMonitor monitor) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ String message = NLS.bind(Messages.resources_moving, source.getFullPath());
+ monitor.beginTask(message, Policy.totalWork);
+
+ // Double-check this pre-condition.
+ if (!source.isAccessible())
+ throw new IllegalArgumentException();
+
+ // If there is nothing to do on disk then signal to make the workspace tree
+ // changes.
+ if (!isContentChange(source, description)) {
+ movedProjectSubtree(source, description);
+ return;
+ }
+
+ // Check to see if we are synchronized with the local file system.
+ boolean force = (flags & IResource.FORCE) != 0;
+ if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
+ // FIXME: make this a best effort move?
+ message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath());
+ IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message);
+ failed(status);
+ return;
+ }
+
+ IFileStore destinationStore;
+ try {
+ destinationStore = computeDestinationStore(description);
+ //destination can be non-empty on replace
+ if ((flags & IResource.REPLACE) == 0)
+ if (!ensureDestinationEmpty(source, destinationStore, monitor))
+ return;
+ } catch (CoreException e) {
+ //must fail if the destination location cannot be accessd (undefined file system)
+ message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ failed(status);
+ return;
+ }
+
+ // Move the project content in the local file system.
+ try {
+ moveProjectContent(source, destinationStore, flags, Policy.subMonitorFor(monitor, Policy.totalWork * 3 / 4));
+ } catch (CoreException e) {
+ message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath());
+ IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
+ failed(status);
+ //refresh the project because it might have been partially moved
+ try {
+ source.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e2) {
+ //ignore secondary failures
+ }
+ }
+
+ // If we got this far the project content has been moved on disk (if necessary)
+ // and we need to update the workspace tree.
+ movedProjectSubtree(source, description);
+ monitor.worked(Policy.totalWork * 1 / 8);
+
+ boolean isDeep = (flags & IResource.SHALLOW) == 0;
+ updateTimestamps(source.getWorkspace().getRoot().getProject(description.getName()), isDeep);
+ monitor.worked(Policy.totalWork * 1 / 8);
+ } finally {
+ lock.release();
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IResourceTree#updateMovedFileTimestamp(IFile, long)
+ */
+ public void updateMovedFileTimestamp(IFile file, long timestamp) {
+ Assert.isLegal(isValid);
+ try {
+ lock.acquire();
+ // Do nothing if the file doesn't exist in the workspace tree.
+ if (!file.exists())
+ return;
+ // Update the timestamp in the tree.
+ ResourceInfo info = ((Resource) file).getResourceInfo(false, true);
+ // The info should never be null since we just checked that the resource exists in the tree.
+ localManager.updateLocalSync(info, timestamp);
+ //remove the linked bit since this resource has been moved in the file system
+ info.clear(ICoreConstants.M_LINK);
+ } finally {
+ lock.release();
+ }
+ }
+
+ /**
+ * Helper method to update all the timestamps in the tree to match
+ * those in the file system. Used after a #move.
+ */
+ private void updateTimestamps(IResource root, final boolean isDeep) {
+ IResourceVisitor visitor = new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ if (resource.isLinked()) {
+ if (isDeep) {
+ //clear the linked resource bit, if any
+ ResourceInfo info = ((Resource) resource).getResourceInfo(false, true);
+ info.clear(ICoreConstants.M_LINK);
+ }
+ return true;
+ }
+ //only needed if underlying file system does not preserve timestamps
+ // if (resource.getType() == IResource.FILE) {
+ // IFile file = (IFile) resource;
+ // updateMovedFileTimestamp(file, computeTimestamp(file));
+ // }
+ return true;
+ }
+ };
+ try {
+ root.accept(visitor, IResource.DEPTH_INFINITE, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ } catch (CoreException e) {
+ // No exception should be thrown.
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourcesCompatibilityHelper.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.localstore.HistoryStore2;
+import org.eclipse.core.internal.localstore.IHistoryStore;
+import org.eclipse.core.internal.properties.IPropertyManager;
+import org.eclipse.core.internal.properties.PropertyManager2;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This is mostly a convenience class for accessing the ResourcesCompatibility class from the
+ * compatibility fragment using reflection.
+ *
+ * See the ResourcesCompatibility class in the compatibility fragment.
+ */
+public class ResourcesCompatibilityHelper {
+ private static final String COMPATIBILITY_CLASS = "org.eclipse.core.internal.resources.ResourcesCompatibility"; //$NON-NLS-1$
+ private static final String CONVERT_HISTORY_STORE = ResourcesPlugin.PI_RESOURCES + ".convertHistory"; //$NON-NLS-1$
+ private static final String CONVERT_PROPERTY_STORE = ResourcesPlugin.PI_RESOURCES + ".convertProperties"; //$NON-NLS-1$
+ private static final String ENABLE_NEW_HISTORY_STORE = ResourcesPlugin.PI_RESOURCES + ".newHistory"; //$NON-NLS-1$
+ private static final String ENABLE_NEW_PROPERTY_STORE = ResourcesPlugin.PI_RESOURCES + ".newProperties"; //$NON-NLS-1$
+
+ /**
+ * Creates a history store. Decides which implementation of history store should be chosen, and whether
+ * conversion from the existing state should be performed by looking at some system properties.
+ */
+ public static IHistoryStore createHistoryStore(IPath location, int limit) {
+ // the default is to use new implementation
+ boolean newImpl = !Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty(ENABLE_NEW_HISTORY_STORE));
+ // the default is to convert existing state to the new implementation
+ boolean convert = !Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty(CONVERT_HISTORY_STORE));
+ try {
+ return createHistoryStore(location, limit, newImpl, convert, true);
+ } catch (ClassNotFoundException e) {
+ // fragment not available
+ } catch (NoSuchMethodException e) {
+ // unlikely
+ if (Workspace.DEBUG)
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // unlikely
+ if (Workspace.DEBUG)
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // got a runtime exception/error
+ Throwable target = e.getTargetException();
+ if (target instanceof RuntimeException)
+ throw (RuntimeException) target;
+ throw (Error) target;
+ }
+ // default to new version
+ IFileStore store = EFS.getLocalFileSystem().getStore(location);
+ return new HistoryStore2((Workspace) ResourcesPlugin.getWorkspace(), store, limit);
+ }
+
+ public static IHistoryStore createHistoryStore(IPath location, int limit, boolean newImpl, boolean convert, boolean rename) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ Class clazz = Class.forName(COMPATIBILITY_CLASS);
+ Method createMethod = clazz.getDeclaredMethod("createHistoryStore", new Class[] {IPath.class, int.class, boolean.class, boolean.class, boolean.class}); //$NON-NLS-1$
+ return (IHistoryStore) createMethod.invoke(null, new Object[] {location, new Integer(limit), Boolean.valueOf(newImpl), Boolean.valueOf(convert), Boolean.valueOf(rename)});
+ }
+
+ public static IPropertyManager createPropertyManager(boolean newImpl, boolean convert) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ Class clazz = Class.forName(COMPATIBILITY_CLASS);
+ Method createMethod = clazz.getDeclaredMethod("createPropertyManager", new Class[] {boolean.class, boolean.class}); //$NON-NLS-1$
+ return (IPropertyManager) createMethod.invoke(null, new Object[] {Boolean.valueOf(newImpl), Boolean.valueOf(convert)});
+ }
+
+ /**
+ * Creates a property manager. Decides which implementation of property manager should be chosen, and whether
+ * conversion from the existing state should be performed by looking at some system properties.
+ */
+ public static IPropertyManager createPropertyManager() {
+ // the default is to use new implementation
+ boolean newImpl = !Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty(ENABLE_NEW_PROPERTY_STORE));
+ // the default is to convert existing state to the new implementation
+ boolean convert = !Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty(CONVERT_PROPERTY_STORE));
+ try {
+ return createPropertyManager(newImpl, convert);
+ } catch (ClassNotFoundException e) {
+ // fragment not available
+ } catch (NoSuchMethodException e) {
+ // unlikely
+ if (Workspace.DEBUG)
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // unlikely
+ if (Workspace.DEBUG)
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ // got a runtime exception/error
+ Throwable target = e.getTargetException();
+ if (target instanceof RuntimeException)
+ throw (RuntimeException) target;
+ throw (Error) target;
+ }
+ // default to new version
+ return new PropertyManager2((Workspace) ResourcesPlugin.getWorkspace());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/RootInfo.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.runtime.QualifiedName;
+
+public class RootInfo extends ResourceInfo {
+ /** The property store for this resource */
+ protected Object propertyStore = null;
+
+ /**
+ * Returns the property store associated with this info. The return value may be null.
+ */
+ public Object getPropertyStore() {
+ return propertyStore;
+ }
+
+ /**
+ * Override parent's behaviour and do nothing. Sync information
+ * cannot be stored on the workspace root so we don't need to
+ * update this counter which is used for deltas.
+ */
+ public void incrementSyncInfoGenerationCount() {
+ // do nothing
+ }
+
+ /**
+ * Sets the property store associated with this info. The value may be null.
+ */
+ public void setPropertyStore(Object value) {
+ propertyStore = value;
+ }
+
+ /**
+ * Overrides parent's behaviour since sync information is not
+ * stored on the workspace root.
+ */
+ public void setSyncInfo(QualifiedName id, byte[] value) {
+ // do nothing
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Rules.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.internal.events.ILifecycleListener;
+import org.eclipse.core.internal.events.LifecycleEvent;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.ResourceRuleFactory;
+import org.eclipse.core.resources.team.TeamHook;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+
+/**
+ * Class for calculating scheduling rules for resource changing operations.
+ * This factory delegates to the TeamHook to obtain an appropriate factory
+ * for the resource that the operation is proposing to modify.
+ */
+class Rules implements IResourceRuleFactory, ILifecycleListener {
+ private final ResourceRuleFactory defaultFactory = new ResourceRuleFactory() {/**/};
+ /**
+ * Map of project names to the factory for that project.
+ */
+ private final Map projectsToRules = Collections.synchronizedMap(new HashMap());
+ private final TeamHook teamHook;
+ private final IWorkspaceRoot root;
+
+ /**
+ * Creates a new scheduling rule factory for the given workspace
+ * @param workspace
+ */
+ Rules(Workspace workspace) {
+ this.root = workspace.getRoot();
+ this.teamHook = workspace.getTeamHook();
+ workspace.addLifecycleListener(this);
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a build operation.
+ */
+ public ISchedulingRule buildRule() {
+ //team hook currently cannot change this rule
+ return root;
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factories for a copy operation.
+ */
+ public ISchedulingRule copyRule(IResource source, IResource destination) {
+ if (source.getType() == IResource.ROOT || destination.getType() == IResource.ROOT)
+ return root;
+ //source is not modified, destination is created
+ return factoryFor(destination).copyRule(source, destination);
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a create operation.
+ */
+ public ISchedulingRule createRule(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return root;
+ return factoryFor(resource).createRule(resource);
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a delete operation.
+ */
+ public ISchedulingRule deleteRule(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return root;
+ return factoryFor(resource).deleteRule(resource);
+ }
+
+ /**
+ * Returns the scheduling rule factory for the given resource
+ */
+ private IResourceRuleFactory factoryFor(IResource destination) {
+ IResourceRuleFactory fac = (IResourceRuleFactory) projectsToRules.get(destination.getFullPath().segment(0));
+ if (fac == null) {
+ //use the default factory if the project is not yet accessible
+ if (!destination.getProject().isAccessible())
+ return defaultFactory;
+ //ask the team hook to supply one
+ fac = teamHook.getRuleFactory(destination.getProject());
+ projectsToRules.put(destination.getFullPath().segment(0), fac);
+ }
+ return fac;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.events.ILifecycleListener#handleEvent(org.eclipse.core.internal.events.LifecycleEvent)
+ */
+ public void handleEvent(LifecycleEvent event) {
+ //clear resource rule factory for projects that are about to be closed
+ //or deleted. It is ok to do this during a PRE event because the rule
+ //has already been obtained at this point.
+ switch (event.kind) {
+ case LifecycleEvent.PRE_PROJECT_CLOSE :
+ case LifecycleEvent.PRE_PROJECT_DELETE :
+ case LifecycleEvent.PRE_PROJECT_MOVE :
+ setRuleFactory((IProject) event.resource, null);
+ }
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a charset change operation.
+ */
+ public ISchedulingRule charsetRule(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return null;
+ return factoryFor(resource).charsetRule(resource);
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a marker change operation.
+ */
+ public ISchedulingRule markerRule(IResource resource) {
+ //team hook currently cannot change this rule
+ return null;
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a modify operation.
+ */
+ public ISchedulingRule modifyRule(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return root;
+ return factoryFor(resource).modifyRule(resource);
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factories for a move operation.
+ */
+ public ISchedulingRule moveRule(IResource source, IResource destination) {
+ if (source.getType() == IResource.ROOT || destination.getType() == IResource.ROOT)
+ return root;
+ //treat a move across projects as a create on the destination and a delete on the source
+ if (!source.getFullPath().segment(0).equals(destination.getFullPath().segment(0)))
+ return MultiRule.combine(deleteRule(source), createRule(destination));
+ return factoryFor(source).moveRule(source, destination);
+ }
+
+ /**
+ * Obtains the scheduling rule from the appropriate factory for a refresh operation.
+ */
+ public ISchedulingRule refreshRule(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return root;
+ return factoryFor(resource).refreshRule(resource);
+ }
+
+ /* (non-javadoc)
+ * Implements TeamHook#setRuleFactory
+ */
+ void setRuleFactory(IProject project, IResourceRuleFactory factory) {
+ if (factory == null)
+ projectsToRules.remove(project.getName());
+ else
+ projectsToRules.put(project.getName(), factory);
+ }
+
+ /**
+ * Combines rules for each parameter to validateEdit from the corresponding
+ * rule factories.
+ */
+ public ISchedulingRule validateEditRule(IResource[] resources) {
+ if (resources.length == 0)
+ return null;
+ //optimize rule for single file
+ if (resources.length == 1) {
+ if (resources[0].getType() == IResource.ROOT)
+ return root;
+ return factoryFor(resources[0]).validateEditRule(resources);
+ }
+ //gather rules for each resource from appropriate factory
+ HashSet rules = new HashSet();
+ IResource[] oneResource = new IResource[1];
+ for (int i = 0; i < resources.length; i++) {
+ if (resources[i].getType() == IResource.ROOT)
+ return root;
+ oneResource[0] = resources[i];
+ ISchedulingRule rule = factoryFor(resources[i]).validateEditRule(oneResource);
+ if (rule != null)
+ rules.add(rule);
+ }
+ if (rules.isEmpty())
+ return null;
+ if (rules.size() == 1)
+ return (ISchedulingRule) rules.iterator().next();
+ ISchedulingRule[] ruleArray = (ISchedulingRule[]) rules.toArray(new ISchedulingRule[rules.size()]);
+ return new MultiRule(ruleArray);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SafeFileTable.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.Properties;
+import java.util.Set;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Represents a table of keys and paths used by a plugin to maintain its
+ * configuration files' names.
+ */
+public class SafeFileTable {
+ protected IPath location;
+ protected Properties table;
+
+ public SafeFileTable(String pluginId) throws CoreException {
+ location = getWorkspace().getMetaArea().getSafeTableLocationFor(pluginId);
+ restore();
+ }
+
+ public IPath[] getFiles() {
+ Set set = table.keySet();
+ String[] keys = (String[]) set.toArray(new String[set.size()]);
+ IPath[] files = new IPath[keys.length];
+ for (int i = 0; i < keys.length; i++)
+ files[i] = new Path(keys[i]);
+ return files;
+ }
+
+ protected Workspace getWorkspace() {
+ return (Workspace) ResourcesPlugin.getWorkspace();
+ }
+
+ public IPath lookup(IPath file) {
+ String result = table.getProperty(file.toOSString());
+ return result == null ? null : new Path(result);
+ }
+
+ public void map(IPath file, IPath aLocation) {
+ if (aLocation == null)
+ table.remove(file);
+ else
+ table.setProperty(file.toOSString(), aLocation.toOSString());
+ }
+
+ public void restore() throws CoreException {
+ java.io.File target = location.toFile();
+ table = new Properties();
+ if (!target.exists())
+ return;
+ try {
+ FileInputStream input = new FileInputStream(target);
+ try {
+ table.load(input);
+ } finally {
+ input.close();
+ }
+ } catch (IOException e) {
+ String message = Messages.resources_exSafeRead;
+ throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
+ }
+ }
+
+ public void save() throws CoreException {
+ java.io.File target = location.toFile();
+ try {
+ FileOutputStream output = new FileOutputStream(target);
+ try {
+ table.store(output, "safe table"); //$NON-NLS-1$
+ } finally {
+ output.close();
+ }
+ } catch (IOException e) {
+ String message = Messages.resources_exSafeSave;
+ throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
+ }
+ }
+
+ public void setLocation(IPath location) {
+ if (location != null)
+ this.location = location;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SaveContext.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+public class SaveContext implements ISaveContext {
+ protected Plugin plugin;
+ protected int kind;
+ protected boolean needDelta;
+ protected boolean needSaveNumber;
+ protected SafeFileTable fileTable;
+ protected int previousSaveNumber;
+ protected IProject project;
+
+ protected SaveContext(Plugin plugin, int kind, IProject project) throws CoreException {
+ this.plugin = plugin;
+ this.kind = kind;
+ this.project = project;
+ needDelta = false;
+ needSaveNumber = false;
+ String pluginId = plugin.getBundle().getSymbolicName();
+ fileTable = new SafeFileTable(pluginId);
+ previousSaveNumber = getWorkspace().getSaveManager().getSaveNumber(pluginId);
+ }
+
+ public void commit() throws CoreException {
+ if (needSaveNumber) {
+ String pluginId = plugin.getBundle().getSymbolicName();
+ IPath oldLocation = getWorkspace().getMetaArea().getSafeTableLocationFor(pluginId);
+ getWorkspace().getSaveManager().setSaveNumber(pluginId, getSaveNumber());
+ fileTable.setLocation(getWorkspace().getMetaArea().getSafeTableLocationFor(pluginId));
+ fileTable.save();
+ oldLocation.toFile().delete();
+ }
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public IPath[] getFiles() {
+ return getFileTable().getFiles();
+ }
+
+ protected SafeFileTable getFileTable() {
+ return fileTable;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public int getKind() {
+ return kind;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public Plugin getPlugin() {
+ return plugin;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public int getPreviousSaveNumber() {
+ return previousSaveNumber;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public IProject getProject() {
+ return project;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public int getSaveNumber() {
+ int result = getPreviousSaveNumber() + 1;
+ return result > 0 ? result : 1;
+ }
+
+ protected Workspace getWorkspace() {
+ return (Workspace) ResourcesPlugin.getWorkspace();
+ }
+
+ public boolean isDeltaNeeded() {
+ return needDelta;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public IPath lookup(IPath file) {
+ return getFileTable().lookup(file);
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public void map(IPath file, IPath location) {
+ getFileTable().map(file, location);
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public void needDelta() {
+ needDelta = true;
+ }
+
+ /**
+ * @see ISaveContext
+ */
+ public void needSaveNumber() {
+ needSaveNumber = true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SaveManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1785 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.events.*;
+import org.eclipse.core.internal.localstore.*;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.osgi.util.NLS;
+
+public class SaveManager implements IElementInfoFlattener, IManager, IStringPoolParticipant {
+ protected static final String CLEAR_DELTA_PREFIX = "clearDelta_"; //$NON-NLS-1$
+ protected static final String DELTA_EXPIRATION_PREFIX = "deltaExpiration_"; //$NON-NLS-1$
+ protected static final int DONE_SAVING = 3;
+
+ /**
+ * The minimum delay, in milliseconds, between workspace snapshots
+ */
+ private static final long MIN_SNAPSHOT_DELAY = 1000 * 30L; //30 seconds
+
+ /**
+ * The number of empty operations that are equivalent to a single non-
+ * trivial operation.
+ */
+ protected static final int NO_OP_THRESHOLD = 20;
+
+ /** constants */
+ protected static final int PREPARE_TO_SAVE = 1;
+ protected static final int ROLLBACK = 4;
+ protected static final String SAVE_NUMBER_PREFIX = "saveNumber_"; //$NON-NLS-1$
+ protected static final int SAVING = 2;
+ protected ElementTree lastSnap;
+ protected Properties masterTable;
+
+ /**
+ * A flag indicating that a save operation is occurring. This is a signal
+ * that snapshot should not be scheduled if a nested operation occurs during
+ * save.
+ */
+ private boolean isSaving = false;
+
+ /**
+ * The number of empty (non-changing) operations since the last snapshot.
+ */
+ protected int noopCount = 0;
+ /**
+ * The number of non-trivial operations since the last snapshot.
+ */
+ protected int operationCount = 0;
+
+ // Count up the time taken for all saves/snaps on markers and sync info
+ protected long persistMarkers = 0l;
+ protected long persistSyncInfo = 0l;
+
+ /**
+ * In-memory representation of plugins saved state. Maps String (plugin id)-> SavedState.
+ * This map is accessed from API that is not synchronized, so it requires
+ * independent synchronization. This is accomplished using a synchronized
+ * wrapper map.
+ */
+ protected Map savedStates;
+
+ /**
+ * Plugins that participate on a workspace save. Maps (Plugin -> ISaveParticipant).
+ * This map is accessed from API that is not synchronized, so it requires
+ * independent synchronization. This is accomplished using a synchronized
+ * wrapper map.
+ */
+ protected Map saveParticipants;
+
+ protected final DelayedSnapshotJob snapshotJob;
+
+ protected boolean snapshotRequested;
+ protected Workspace workspace;
+ //declare debug messages as fields to get sharing
+ private static final String DEBUG_START = " starting..."; //$NON-NLS-1$
+ private static final String DEBUG_FULL_SAVE = "Full save on workspace: "; //$NON-NLS-1$
+ private static final String DEBUG_PROJECT_SAVE = "Save on project "; //$NON-NLS-1$
+ private static final String DEBUG_SNAPSHOT = "Snapshot: "; //$NON-NLS-1$
+ private static final int TREE_BUFFER_SIZE = 1024 * 64;//64KB buffer
+
+ public SaveManager(Workspace workspace) {
+ this.workspace = workspace;
+ this.snapshotJob = new DelayedSnapshotJob(this);
+ snapshotRequested = false;
+ saveParticipants = Collections.synchronizedMap(new HashMap(10));
+ }
+
+ public ISavedState addParticipant(Plugin plugin, ISaveParticipant participant) throws CoreException {
+ // If the plugin was already registered as a save participant we return null
+ if (saveParticipants.put(plugin, participant) != null)
+ return null;
+ String id = plugin.getBundle().getSymbolicName();
+ SavedState state = (SavedState) savedStates.get(id);
+ if (state != null) {
+ if (isDeltaCleared(id)) {
+ // this plugin was marked not to receive deltas
+ state.forgetTrees();
+ removeClearDeltaMarks(id);
+ } else {
+ try {
+ // thread safety: (we need to guarantee that the tree is immutable when computing deltas)
+ // so, the tree inside the saved state needs to be immutable
+ workspace.prepareOperation(null, null);
+ workspace.beginOperation(true);
+ state.newTree = workspace.getElementTree();
+ } finally {
+ workspace.endOperation(null, false, null);
+ }
+ return state;
+ }
+ }
+ // if the plug-in has a previous save number, we return a state, otherwise we return null
+ if (getSaveNumber(id) > 0)
+ return new SavedState(workspace, id, null, null);
+ return null;
+ }
+
+ protected void broadcastLifecycle(final int lifecycle, Map contexts, final MultiStatus warnings, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", contexts.size()); //$NON-NLS-1$
+ for (final Iterator it = contexts.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ Plugin plugin = (Plugin) entry.getKey();
+ final ISaveParticipant participant = (ISaveParticipant) saveParticipants.get(plugin);
+ //save participants can be removed concurrently
+ if (participant == null) {
+ monitor.worked(1);
+ continue;
+ }
+ final SaveContext context = (SaveContext) entry.getValue();
+ /* Be extra careful when calling lifecycle method on arbitrary plugin */
+ ISafeRunnable code = new ISafeRunnable() {
+
+ public void handleException(Throwable e) {
+ String message = Messages.resources_saveProblem;
+ IStatus status = new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, e);
+ warnings.add(status);
+
+ /* Remove entry for defective plug-in from this save operation */
+ it.remove();
+ }
+
+ public void run() throws Exception {
+ executeLifecycle(lifecycle, participant, context);
+ }
+ };
+ SafeRunner.run(code);
+ monitor.worked(1);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Remove the delta expiration timestamp from the master table, either
+ * because the saved state has been processed, or the delta has expired.
+ */
+ protected void clearDeltaExpiration(String pluginId) {
+ masterTable.remove(DELTA_EXPIRATION_PREFIX + pluginId);
+ }
+
+ protected void cleanMasterTable() {
+ //remove tree file entries for everything except closed projects
+ for (Iterator it = masterTable.keySet().iterator(); it.hasNext();) {
+ String key = (String) it.next();
+ if (!key.endsWith(LocalMetaArea.F_TREE))
+ continue;
+ String prefix = key.substring(0, key.length() - LocalMetaArea.F_TREE.length());
+ //always save the root tree entry
+ if (prefix.equals(Path.ROOT.toString()))
+ continue;
+ IProject project = workspace.getRoot().getProject(prefix);
+ if (!project.exists() || project.isOpen())
+ it.remove();
+ }
+ IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
+ IPath backup = workspace.getMetaArea().getBackupLocationFor(location);
+ try {
+ saveMasterTable(backup);
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ backup.toFile().delete();
+ return;
+ }
+ if (location.toFile().exists() && !location.toFile().delete())
+ return;
+ try {
+ saveMasterTable(location);
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ location.toFile().delete();
+ return;
+ }
+ backup.toFile().delete();
+ }
+
+ /**
+ * Marks the current participants to not receive deltas next time they are registered
+ * as save participants. This is done in order to maintain consistency if we crash
+ * after a snapshot. It would force plug-ins to rebuild their state.
+ */
+ protected void clearSavedDelta() {
+ synchronized (saveParticipants) {
+ for (Iterator i = saveParticipants.keySet().iterator(); i.hasNext();) {
+ String pluginId = ((Plugin) i.next()).getBundle().getSymbolicName();
+ masterTable.setProperty(CLEAR_DELTA_PREFIX + pluginId, "true"); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Collects the set of ElementTrees we are still interested in,
+ * and removes references to any other trees.
+ */
+ protected void collapseTrees() throws CoreException {
+ //collect trees we're interested in
+
+ //trees for plugin saved states
+ ArrayList trees = new ArrayList();
+ synchronized (savedStates) {
+ for (Iterator i = savedStates.values().iterator(); i.hasNext();) {
+ SavedState state = (SavedState) i.next();
+ if (state.oldTree != null) {
+ trees.add(state.oldTree);
+ }
+ }
+ }
+
+ //trees for builders
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ if (project.isOpen()) {
+ ArrayList builderInfos = workspace.getBuildManager().createBuildersPersistentInfo(project);
+ if (builderInfos != null) {
+ for (Iterator it = builderInfos.iterator(); it.hasNext();) {
+ BuilderPersistentInfo info = (BuilderPersistentInfo) it.next();
+ trees.add(info.getLastBuiltTree());
+ }
+ }
+ }
+ }
+
+ //no need to collapse if there are no trees at this point
+ if (trees.isEmpty())
+ return;
+
+ //the complete tree
+ trees.add(workspace.getElementTree());
+
+ //collapse the trees
+ //sort trees in topological order, and set the parent of each
+ //tree to its parent in the topological ordering.
+ ElementTree[] treeArray = new ElementTree[trees.size()];
+ trees.toArray(treeArray);
+ ElementTree[] sorted = sortTrees(treeArray);
+ // if there was a problem sorting the tree, bail on trying to collapse.
+ // We will be able to GC the layers at a later time.
+ if (sorted == null)
+ return;
+ for (int i = 1; i < sorted.length; i++)
+ sorted[i].collapseTo(sorted[i - 1]);
+ }
+
+ protected void commit(Map contexts) throws CoreException {
+ for (Iterator i = contexts.values().iterator(); i.hasNext();)
+ ((SaveContext) i.next()).commit();
+ }
+
+ /**
+ * Given a collection of save participants, compute the collection of
+ * <code>SaveContexts</code> to use during the save lifecycle.
+ * The keys are plugins and values are SaveContext objects.
+ */
+ protected Map computeSaveContexts(Plugin[] plugins, int kind, IProject project) {
+ HashMap result = new HashMap(plugins.length);
+ for (int i = 0; i < plugins.length; i++) {
+ Plugin plugin = plugins[i];
+ try {
+ SaveContext context = new SaveContext(plugin, kind, project);
+ result.put(plugin, context);
+ } catch (CoreException e) {
+ // FIXME: should return a status to the user and not just log it
+ Policy.log(e.getStatus());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a table mapping having the plug-in id as the key and the old tree
+ * as the value.
+ * This table is based on the union of the current savedStates</code>
+ * and the given table of contexts. The specified tree is used as the tree for
+ * any newly created saved states. This method is used to compute the set of
+ * saved states to be written out.
+ */
+ protected Map computeStatesToSave(Map contexts, ElementTree current) {
+ HashMap result = new HashMap(savedStates.size() * 2);
+ synchronized (savedStates) {
+ for (Iterator i = savedStates.values().iterator(); i.hasNext();) {
+ SavedState state = (SavedState) i.next();
+ if (state.oldTree != null)
+ result.put(state.pluginId, state.oldTree);
+ }
+ }
+ for (Iterator i = contexts.values().iterator(); i.hasNext();) {
+ SaveContext context = (SaveContext) i.next();
+ if (!context.isDeltaNeeded())
+ continue;
+ String pluginId = context.getPlugin().getBundle().getSymbolicName();
+ result.put(pluginId, current);
+ }
+ return result;
+ }
+
+ protected void executeLifecycle(int lifecycle, ISaveParticipant participant, SaveContext context) throws CoreException {
+ switch (lifecycle) {
+ case PREPARE_TO_SAVE :
+ participant.prepareToSave(context);
+ break;
+ case SAVING :
+ try {
+ if (ResourceStats.TRACE_SAVE_PARTICIPANTS)
+ ResourceStats.startSave(participant);
+ participant.saving(context);
+ } finally {
+ if (ResourceStats.TRACE_SAVE_PARTICIPANTS)
+ ResourceStats.endSave();
+ }
+ break;
+ case DONE_SAVING :
+ participant.doneSaving(context);
+ break;
+ case ROLLBACK :
+ participant.rollback(context);
+ break;
+ default :
+ Assert.isTrue(false, "Invalid save lifecycle code"); //$NON-NLS-1$
+ }
+ }
+
+ public void forgetSavedTree(String pluginId) {
+ if (pluginId == null) {
+ synchronized (savedStates) {
+ for (Iterator i = savedStates.values().iterator(); i.hasNext();)
+ ((SavedState) i.next()).forgetTrees();
+ }
+ } else {
+ SavedState state = (SavedState) savedStates.get(pluginId);
+ if (state != null)
+ state.forgetTrees();
+ }
+ }
+
+ /**
+ * Used in the policy for cleaning up tree's of plug-ins that are not often activated.
+ */
+ protected long getDeltaExpiration(String pluginId) {
+ String result = masterTable.getProperty(DELTA_EXPIRATION_PREFIX + pluginId);
+ return (result == null) ? System.currentTimeMillis() : new Long(result).longValue();
+ }
+
+ protected Properties getMasterTable() {
+ return masterTable;
+ }
+
+ public int getSaveNumber(String pluginId) {
+ String value = masterTable.getProperty(SAVE_NUMBER_PREFIX + pluginId);
+ return (value == null) ? 0 : new Integer(value).intValue();
+ }
+
+ protected Plugin[] getSaveParticipantPlugins() {
+ synchronized (saveParticipants) {
+ return (Plugin[]) saveParticipants.keySet().toArray(new Plugin[saveParticipants.size()]);
+ }
+ }
+
+ /**
+ * Hooks the end of a save operation, for debugging and performance
+ * monitoring purposes.
+ */
+ private void hookEndSave(int kind, IProject project, long start) {
+ if (ResourceStats.TRACE_SNAPSHOT && kind == ISaveContext.SNAPSHOT)
+ ResourceStats.endSnapshot();
+ if (Policy.DEBUG_SAVE) {
+ String endMessage = null;
+ switch (kind) {
+ case ISaveContext.FULL_SAVE :
+ endMessage = DEBUG_FULL_SAVE;
+ break;
+ case ISaveContext.SNAPSHOT :
+ endMessage = DEBUG_SNAPSHOT;
+ break;
+ case ISaveContext.PROJECT_SAVE :
+ endMessage = DEBUG_PROJECT_SAVE + project.getFullPath() + ": "; //$NON-NLS-1$
+ break;
+ }
+ if (endMessage != null)
+ System.out.println(endMessage + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Hooks the start of a save operation, for debugging and performance
+ * monitoring purposes.
+ */
+ private void hookStartSave(int kind, Project project) {
+ if (ResourceStats.TRACE_SNAPSHOT && kind == ISaveContext.SNAPSHOT)
+ ResourceStats.startSnapshot();
+ if (Policy.DEBUG_SAVE) {
+ switch (kind) {
+ case ISaveContext.FULL_SAVE :
+ System.out.println(DEBUG_FULL_SAVE + DEBUG_START);
+ break;
+ case ISaveContext.SNAPSHOT :
+ System.out.println(DEBUG_SNAPSHOT + DEBUG_START);
+ break;
+ case ISaveContext.PROJECT_SAVE :
+ System.out.println(DEBUG_PROJECT_SAVE + project.getFullPath() + DEBUG_START);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Initializes the snapshot mechanism for this workspace.
+ */
+ protected void initSnap(IProgressMonitor monitor) throws CoreException {
+ //discard any pending snapshot request
+ snapshotJob.cancel();
+ //the "lastSnap" tree must be frozen as the exact tree obtained from startup,
+ // otherwise ensuing snapshot deltas may be based on an incorrect tree (see bug 12575)
+ lastSnap = workspace.getElementTree();
+ lastSnap.immutable();
+ workspace.newWorkingTree();
+ operationCount = 0;
+ // delete the snapshot file, if any
+ IPath snapPath = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
+ java.io.File file = snapPath.toFile();
+ if (file.exists())
+ file.delete();
+ if (file.exists()) {
+ String message = Messages.resources_snapInit;
+ throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, null, message, null);
+ }
+ }
+
+ protected boolean isDeltaCleared(String pluginId) {
+ String clearDelta = masterTable.getProperty(CLEAR_DELTA_PREFIX + pluginId);
+ return clearDelta != null && clearDelta.equals("true"); //$NON-NLS-1$
+ }
+
+ protected boolean isOldPluginTree(String pluginId) {
+ // first, check if this plug-ins was marked not to receive a delta
+ if (isDeltaCleared(pluginId))
+ return false;
+ //see if the plugin is still installed
+ if (Platform.getBundle(pluginId) == null)
+ return true;
+
+ //finally see if the delta has past its expiry date
+ long deltaAge = System.currentTimeMillis() - getDeltaExpiration(pluginId);
+ return deltaAge > workspace.internalGetDescription().getDeltaExpiration();
+ }
+
+ /**
+ * @see IElementInfoFlattener#readElement(IPath, DataInput)
+ */
+ public Object readElement(IPath path, DataInput input) throws IOException {
+ Assert.isNotNull(path);
+ Assert.isNotNull(input);
+ // read the flags and pull out the type.
+ int flags = input.readInt();
+ int type = (flags & ICoreConstants.M_TYPE) >> ICoreConstants.M_TYPE_START;
+ ResourceInfo info = workspace.newElement(type);
+ info.readFrom(flags, input);
+ return info;
+ }
+
+ /**
+ * Remove marks from current save participants. This marks prevent them to receive their
+ * deltas when they register themselves as save participants.
+ */
+ protected void removeClearDeltaMarks() {
+ synchronized (saveParticipants) {
+ for (Iterator i = saveParticipants.keySet().iterator(); i.hasNext();) {
+ String pluginId = ((Plugin) i.next()).getBundle().getSymbolicName();
+ removeClearDeltaMarks(pluginId);
+ }
+ }
+ }
+
+ protected void removeClearDeltaMarks(String pluginId) {
+ masterTable.setProperty(CLEAR_DELTA_PREFIX + pluginId, "false"); //$NON-NLS-1$
+ }
+
+ protected void removeFiles(java.io.File root, String[] candidates, List exclude) {
+ for (int i = 0; i < candidates.length; i++) {
+ boolean delete = true;
+ for (ListIterator it = exclude.listIterator(); it.hasNext();) {
+ String s = (String) it.next();
+ if (s.equals(candidates[i])) {
+ it.remove();
+ delete = false;
+ break;
+ }
+ }
+ if (delete)
+ new java.io.File(root, candidates[i]).delete();
+ }
+ }
+
+ private void removeGarbage(DataOutputStream output, IPath location, IPath tempLocation) throws IOException {
+ if (output.size() == 0) {
+ output.close();
+ location.toFile().delete();
+ tempLocation.toFile().delete();
+ }
+ }
+
+ public void removeParticipant(Plugin plugin) {
+ saveParticipants.remove(plugin);
+ }
+
+ protected void removeUnusedSafeTables() {
+ List valuables = new ArrayList(10);
+ IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
+ valuables.add(location.lastSegment()); // add master table
+ for (Enumeration e = masterTable.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ if (key.startsWith(SAVE_NUMBER_PREFIX)) {
+ String pluginId = key.substring(SAVE_NUMBER_PREFIX.length());
+ valuables.add(workspace.getMetaArea().getSafeTableLocationFor(pluginId).lastSegment());
+ }
+ }
+ java.io.File target = location.toFile().getParentFile();
+ String[] candidates = target.list();
+ if (candidates == null)
+ return;
+ removeFiles(target, candidates, valuables);
+ }
+
+ protected void removeUnusedTreeFiles() {
+ // root resource
+ List valuables = new ArrayList(10);
+ IPath location = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
+ valuables.add(location.lastSegment());
+ java.io.File target = location.toFile().getParentFile();
+ FilenameFilter filter = new FilenameFilter() {
+ public boolean accept(java.io.File dir, String name) {
+ return name.endsWith(LocalMetaArea.F_TREE);
+ }
+ };
+ String[] candidates = target.list(filter);
+ if (candidates != null)
+ removeFiles(target, candidates, valuables);
+
+ // projects
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ location = workspace.getMetaArea().getTreeLocationFor(projects[i], false);
+ valuables.add(location.lastSegment());
+ target = location.toFile().getParentFile();
+ candidates = target.list(filter);
+ if (candidates != null)
+ removeFiles(target, candidates, valuables);
+ }
+ }
+
+ public void requestSnapshot() {
+ snapshotRequested = true;
+ }
+
+ /**
+ * Reset the snapshot mechanism for the non-workspace files. This
+ * includes the markers and sync info.
+ */
+ protected void resetSnapshots(IResource resource) throws CoreException {
+ Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
+ String message;
+
+ // delete the snapshot file, if any
+ java.io.File file = workspace.getMetaArea().getMarkersSnapshotLocationFor(resource).toFile();
+ if (file.exists())
+ file.delete();
+ if (file.exists()) {
+ message = Messages.resources_resetMarkers;
+ throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, resource.getFullPath(), message, null);
+ }
+
+ // delete the snapshot file, if any
+ file = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(resource).toFile();
+ if (file.exists())
+ file.delete();
+ if (file.exists()) {
+ message = Messages.resources_resetSync;
+ throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, resource.getFullPath(), message, null);
+ }
+
+ // if we have the workspace root then recursive over the projects.
+ // only do open projects since closed ones are saved elsewhere
+ if (resource.getType() == IResource.PROJECT)
+ return;
+ IProject[] projects = ((IWorkspaceRoot) resource).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ resetSnapshots(projects[i]);
+ }
+
+ /**
+ * Restores the state of this workspace by opening the projects
+ * which were open when it was last saved.
+ */
+ protected void restore(IProgressMonitor monitor) throws CoreException {
+ if (Policy.DEBUG_RESTORE)
+ System.out.println("Restore workspace: starting..."); //$NON-NLS-1$
+ long start = System.currentTimeMillis();
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", 50); //$NON-NLS-1$
+ // need to open the tree to restore, but since we're not
+ // inside an operation, be sure to close it afterwards
+ workspace.newWorkingTree();
+ try {
+ String msg = Messages.resources_startupProblems;
+ MultiStatus problems = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, msg, null);
+
+ restoreMasterTable();
+ // restore the saved tree and overlay the snapshots if any
+ restoreTree(Policy.subMonitorFor(monitor, 10));
+ restoreSnapshots(Policy.subMonitorFor(monitor, 10));
+
+ // tolerate failure for non-critical information
+ // if startup fails, the entire workspace is shot
+ try {
+ restoreMarkers(workspace.getRoot(), false, Policy.subMonitorFor(monitor, 10));
+ } catch (CoreException e) {
+ problems.merge(e.getStatus());
+ }
+ try {
+ restoreSyncInfo(workspace.getRoot(), Policy.subMonitorFor(monitor, 10));
+ } catch (CoreException e) {
+ problems.merge(e.getStatus());
+ }
+ // restore meta info last because it might close a project if its description is not readable
+ restoreMetaInfo(problems, Policy.subMonitorFor(monitor, 10));
+ IProject[] roots = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < roots.length; i++)
+ ((Project) roots[i]).startup();
+ if (!problems.isOK())
+ Policy.log(problems);
+ } finally {
+ workspace.getElementTree().immutable();
+ }
+ } finally {
+ monitor.done();
+ }
+ if (Policy.DEBUG_RESTORE)
+ System.out.println("Restore workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Restores the contents of this project. Throw
+ * an exception if the project could not be restored.
+ * @return <code><code>true</code> if the project data was restored successfully,
+ * and <code>false</code> if non-critical problems occurred while restoring.
+ * @exception CoreException if the project could not be restored.
+ */
+ protected boolean restore(Project project, IProgressMonitor monitor) throws CoreException {
+ boolean status = true;
+ if (Policy.DEBUG_RESTORE)
+ System.out.println("Restore project " + project.getFullPath() + ": starting..."); //$NON-NLS-1$ //$NON-NLS-2$
+ long start = System.currentTimeMillis();
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", 40); //$NON-NLS-1$
+ if (project.isOpen()) {
+ status = restoreTree(project, Policy.subMonitorFor(monitor, 10));
+ } else {
+ monitor.worked(10);
+ }
+ restoreMarkers(project, true, Policy.subMonitorFor(monitor, 10));
+ restoreSyncInfo(project, Policy.subMonitorFor(monitor, 10));
+ // restore meta info last because it might close a project if its description is not found
+ restoreMetaInfo(project, Policy.subMonitorFor(monitor, 10));
+ } finally {
+ monitor.done();
+ }
+ if (Policy.DEBUG_RESTORE)
+ System.out.println("Restore project " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return status;
+ }
+
+ /**
+ * Reads the markers which were originally saved
+ * for the tree rooted by the given resource.
+ */
+ protected void restoreMarkers(IResource resource, boolean generateDeltas, IProgressMonitor monitor) throws CoreException {
+ Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
+ long start = System.currentTimeMillis();
+ MarkerManager markerManager = workspace.getMarkerManager();
+ // when restoring a project, only load markers if it is open
+ if (resource.isAccessible())
+ markerManager.restore(resource, generateDeltas, monitor);
+
+ // if we have the workspace root then restore markers for its projects
+ if (resource.getType() == IResource.PROJECT) {
+ if (Policy.DEBUG_RESTORE_MARKERS) {
+ System.out.println("Restore Markers for " + resource.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return;
+ }
+ IProject[] projects = ((IWorkspaceRoot) resource).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ if (projects[i].isAccessible())
+ markerManager.restore(projects[i], generateDeltas, monitor);
+ if (Policy.DEBUG_RESTORE_MARKERS) {
+ System.out.println("Restore Markers for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ protected void restoreMasterTable() throws CoreException {
+ long start = System.currentTimeMillis();
+ masterTable = new Properties();
+ IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
+ java.io.File target = location.toFile();
+ if (!target.exists()) {
+ location = workspace.getMetaArea().getBackupLocationFor(location);
+ target = location.toFile();
+ if (!target.exists())
+ return;
+ }
+ try {
+ SafeChunkyInputStream input = new SafeChunkyInputStream(target);
+ try {
+ masterTable.load(input);
+ } finally {
+ input.close();
+ }
+ } catch (IOException e) {
+ String message = Messages.resources_exMasterTable;
+ throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
+ }
+ if (Policy.DEBUG_RESTORE_MASTERTABLE)
+ System.out.println("Restore master table for " + location + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Restores the state of this workspace by opening the projects
+ * which were open when it was last saved.
+ */
+ protected void restoreMetaInfo(MultiStatus problems, IProgressMonitor monitor) {
+ if (Policy.DEBUG_RESTORE_METAINFO)
+ System.out.println("Restore workspace metainfo: starting..."); //$NON-NLS-1$
+ long start = System.currentTimeMillis();
+ IProject[] roots = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < roots.length; i++) {
+ //fatal to throw exceptions during startup
+ try {
+ restoreMetaInfo((Project) roots[i], monitor);
+ } catch (CoreException e) {
+ String message = NLS.bind(Messages.resources_readMeta, roots[i].getName());
+ problems.merge(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, roots[i].getFullPath(), message, e));
+ }
+ }
+ if (Policy.DEBUG_RESTORE_METAINFO)
+ System.out.println("Restore workspace metainfo: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Restores the contents of this project. Throw an exception if the
+ * project description could not be restored.
+ */
+ protected void restoreMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ ProjectDescription description = null;
+ CoreException failure = null;
+ try {
+ if (project.isOpen())
+ description = workspace.getFileSystemManager().read(project, true);
+ else
+ //for closed projects, just try to read the legacy .prj file,
+ //because the project location is stored there.
+ description = workspace.getMetaArea().readOldDescription(project);
+ } catch (CoreException e) {
+ failure = e;
+ }
+ // If we had an open project and there was an error reading the description
+ // from disk, close the project and give it a default description. If the project
+ // was already closed then just set a default description.
+ if (description == null) {
+ description = new ProjectDescription();
+ description.setName(project.getName());
+ //try to read private metadata and add to the description
+ workspace.getMetaArea().readPrivateDescription(project, description);
+ }
+ project.internalSetDescription(description, false);
+ if (failure != null) {
+ // write the project tree ...
+ writeTree(project, IResource.DEPTH_INFINITE);
+ // ... and close the project
+ project.internalClose();
+ throw failure;
+ }
+ if (Policy.DEBUG_RESTORE_METAINFO)
+ System.out.println("Restore metainfo for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Restores the workspace tree from snapshot files in the event
+ * of a crash. The workspace tree must be open when this method
+ * is called, and will be open at the end of this method. In the
+ * event of a crash recovery, the snapshot file is not deleted until
+ * the next successful save.
+ */
+ protected void restoreSnapshots(IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ IPath snapLocation = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
+ java.io.File localFile = snapLocation.toFile();
+
+ // If the snapshot file doesn't exist, there was no crash.
+ // Just initialize the snapshot file and return.
+ if (!localFile.exists()) {
+ initSnap(Policy.subMonitorFor(monitor, Policy.totalWork / 2));
+ return;
+ }
+ // If we have a snapshot file, the workspace was shutdown without being saved or crashed.
+ workspace.setCrashed(true);
+ try {
+ /* Read each of the snapshots and lay them on top of the current tree.*/
+ ElementTree complete = workspace.getElementTree();
+ complete.immutable();
+ DataInputStream input = new DataInputStream(new SafeChunkyInputStream(localFile));
+ try {
+ WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt());
+ complete = reader.readSnapshotTree(input, complete, monitor);
+ } finally {
+ FileUtil.safeClose(input);
+ //reader returned an immutable tree, but since we're inside
+ //an operation, we must return an open tree
+ lastSnap = complete;
+ complete = complete.newEmptyDelta();
+ workspace.tree = complete;
+ }
+ } catch (Exception e) {
+ // only log the exception, we should not fail restoring the snapshot
+ message = Messages.resources_snapRead;
+ Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, null, message, e));
+ }
+ } finally {
+ monitor.done();
+ }
+ if (Policy.DEBUG_RESTORE_SNAPSHOTS)
+ System.out.println("Restore snapshots for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Reads the sync info which was originally saved
+ * for the tree rooted by the given resource.
+ */
+ protected void restoreSyncInfo(IResource resource, IProgressMonitor monitor) throws CoreException {
+ Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
+ long start = System.currentTimeMillis();
+ Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
+ // when restoring a project, only load sync info if it is open
+ if (resource.isAccessible())
+ synchronizer.restore(resource, monitor);
+
+ // restore sync info for all projects if we were given the workspace root.
+ if (resource.getType() == IResource.PROJECT) {
+ if (Policy.DEBUG_RESTORE_SYNCINFO) {
+ System.out.println("Restore SyncInfo for " + resource.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return;
+ }
+ IProject[] projects = ((IWorkspaceRoot) resource).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ if (projects[i].isAccessible())
+ synchronizer.restore(projects[i], monitor);
+ if (Policy.DEBUG_RESTORE_SYNCINFO) {
+ System.out.println("Restore SyncInfo for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Reads the contents of the tree rooted by the given resource from the
+ * file system. This method is used when restoring a complete workspace
+ * after workspace save/shutdown.
+ * @exception CoreException if the workspace could not be restored.
+ */
+ protected void restoreTree(IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
+ IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
+ if (!treeLocation.toFile().exists() && !tempLocation.toFile().exists()) {
+ savedStates = Collections.synchronizedMap(new HashMap(10));
+ return;
+ }
+ try {
+ DataInputStream input = new DataInputStream(new SafeFileInputStream(treeLocation.toOSString(), tempLocation.toOSString(), TREE_BUFFER_SIZE));
+ try {
+ WorkspaceTreeReader.getReader(workspace, input.readInt()).readTree(input, monitor);
+ } finally {
+ input.close();
+ }
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.resources_readMeta, treeLocation.toOSString());
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, treeLocation, msg, e);
+ }
+ if (Policy.DEBUG_RESTORE_TREE) {
+ System.out.println("Restore Tree for workspace: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Restores the trees for the builders of this project from the local disk.
+ * Does nothing if the tree file does not exist (this means the
+ * project has never been saved). This method is
+ * used when restoring a saved/closed project. restoreTree(Workspace) is
+ * used when restoring a complete workspace after workspace save/shutdown.
+ * @return <code>true</code> if the tree file exists, <code>false</code> otherwise.
+ * @exception CoreException if the project could not be restored.
+ */
+ protected boolean restoreTree(Project project, IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(project, false);
+ IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
+ if (!treeLocation.toFile().exists() && !tempLocation.toFile().exists())
+ return false;
+ DataInputStream input = new DataInputStream(new SafeFileInputStream(treeLocation.toOSString(), tempLocation.toOSString()));
+ try {
+ WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt());
+ reader.readTree(project, input, Policy.subMonitorFor(monitor, Policy.totalWork));
+ } finally {
+ input.close();
+ }
+ } catch (IOException e) {
+ message = NLS.bind(Messages.resources_readMeta, project.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, project.getFullPath(), message, e);
+ } finally {
+ monitor.done();
+ }
+ if (Policy.DEBUG_RESTORE_TREE) {
+ System.out.println("Restore Tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return true;
+ }
+
+ class InternalMonitorWrapper extends ProgressMonitorWrapper{
+ private boolean ignoreCancel;
+
+ public InternalMonitorWrapper(IProgressMonitor monitor){
+ super(Policy.monitorFor(monitor));
+ }
+
+ public void ignoreCancelState(boolean ignore) {
+ this.ignoreCancel = ignore;
+ }
+
+ public boolean isCanceled() {
+ return ignoreCancel ? false : super.isCanceled();
+ }
+ };
+
+ public IStatus save(int kind, Project project, IProgressMonitor monitor) throws CoreException {
+ return save(kind, false, project, monitor);
+ }
+
+ public IStatus save(int kind, boolean keepConsistencyWhenCanceled, Project project, IProgressMonitor parentMonitor) throws CoreException {
+ InternalMonitorWrapper monitor = new InternalMonitorWrapper(parentMonitor);
+ monitor.ignoreCancelState(keepConsistencyWhenCanceled);
+ try {
+ isSaving = true;
+ String message = Messages.resources_saving_0;
+ monitor.beginTask(message, 7);
+ message = Messages.resources_saveWarnings;
+ MultiStatus warnings = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.WARNING, message, null);
+ ISchedulingRule rule = project != null ? (IResource) project : workspace.getRoot();
+ try {
+ workspace.prepareOperation(rule, monitor);
+ workspace.beginOperation(false);
+ hookStartSave(kind, project);
+ long start = System.currentTimeMillis();
+ Map contexts = computeSaveContexts(getSaveParticipantPlugins(), kind, project);
+ broadcastLifecycle(PREPARE_TO_SAVE, contexts, warnings, Policy.subMonitorFor(monitor, 1));
+ try {
+ broadcastLifecycle(SAVING, contexts, warnings, Policy.subMonitorFor(monitor, 1));
+ switch (kind) {
+ case ISaveContext.FULL_SAVE :
+ // save the complete tree and remember all of the required saved states
+ saveTree(contexts, Policy.subMonitorFor(monitor, 1));
+ // reset the snapshot state.
+ initSnap(null);
+ //save master table right after saving tree to ensure correct tree number is saved
+ cleanMasterTable();
+ // save all of the markers and all sync info in the workspace
+ persistMarkers = 0l;
+ persistSyncInfo = 0l;
+ visitAndSave(workspace.getRoot());
+ monitor.worked(1);
+ if (Policy.DEBUG_SAVE) {
+ Policy.debug("Total Save Markers: " + persistMarkers + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ Policy.debug("Total Save Sync Info: " + persistSyncInfo + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ // reset the snap shot files
+ resetSnapshots(workspace.getRoot());
+ //remove unused files
+ removeUnusedSafeTables();
+ removeUnusedTreeFiles();
+
+ // history pruning can be always canceled
+ monitor.ignoreCancelState(false);
+ workspace.getFileSystemManager().getHistoryStore().clean(Policy.subMonitorFor(monitor, 1));
+ monitor.ignoreCancelState(keepConsistencyWhenCanceled);
+
+ // write out all metainfo (e.g., workspace/project descriptions)
+ saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
+ break;
+ case ISaveContext.SNAPSHOT :
+ snapTree(workspace.getElementTree(), Policy.subMonitorFor(monitor, 1));
+ // snapshot the markers and sync info for the workspace
+ persistMarkers = 0l;
+ persistSyncInfo = 0l;
+ visitAndSnap(workspace.getRoot());
+ monitor.worked(1);
+ if (Policy.DEBUG_SAVE) {
+ Policy.debug("Total Snap Markers: " + persistMarkers + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ Policy.debug("Total Snap Sync Info: " + persistSyncInfo + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ collapseTrees();
+ clearSavedDelta();
+ // write out all metainfo (e.g., workspace/project descriptions)
+ saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
+ break;
+ case ISaveContext.PROJECT_SAVE :
+ writeTree(project, IResource.DEPTH_INFINITE);
+ monitor.worked(1);
+ // save markers and sync info
+ visitAndSave(project);
+ monitor.worked(1);
+ // reset the snapshot file
+ resetSnapshots(project);
+ IStatus result = saveMetaInfo(project, null);
+ if (!result.isOK())
+ warnings.merge(result);
+ monitor.worked(1);
+ break;
+ }
+ // save contexts
+ commit(contexts);
+ if (kind == ISaveContext.FULL_SAVE)
+ removeClearDeltaMarks();
+ //this must be done after committing save contexts to update participant save numbers
+ saveMasterTable();
+ broadcastLifecycle(DONE_SAVING, contexts, warnings, Policy.subMonitorFor(monitor, 1));
+ hookEndSave(kind, project, start);
+ return warnings;
+ } catch (CoreException e) {
+ broadcastLifecycle(ROLLBACK, contexts, warnings, Policy.subMonitorFor(monitor, 1));
+ // rollback ResourcesPlugin master table
+ restoreMasterTable();
+ throw e; // re-throw
+ }
+ } catch (OperationCanceledException e) {
+ workspace.getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ workspace.endOperation(rule, false, Policy.monitorFor(null));
+ }
+ } finally {
+ isSaving = false;
+ monitor.done();
+ }
+ }
+
+ protected void saveMasterTable() throws CoreException {
+ saveMasterTable(workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES));
+ }
+
+ protected void saveMasterTable(IPath location) throws CoreException {
+ long start = System.currentTimeMillis();
+ java.io.File target = location.toFile();
+ try {
+ SafeChunkyOutputStream output = new SafeChunkyOutputStream(target);
+ try {
+ masterTable.store(output, "master table"); //$NON-NLS-1$
+ output.succeed();
+ } finally {
+ output.close();
+ }
+ } catch (IOException e) {
+ throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, NLS.bind(Messages.resources_exSaveMaster, location.toOSString()), e);
+ }
+ if (Policy.DEBUG_SAVE_MASTERTABLE)
+ System.out.println("Save master table for " + location + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Writes the metainfo (e.g. descriptions) of the given workspace and
+ * all projects to the local disk.
+ */
+ protected void saveMetaInfo(MultiStatus problems, IProgressMonitor monitor) throws CoreException {
+ if (Policy.DEBUG_SAVE_METAINFO)
+ System.out.println("Save workspace metainfo: starting..."); //$NON-NLS-1$
+ long start = System.currentTimeMillis();
+ // save preferences (workspace description, path variables, etc)
+ ResourcesPlugin.getPlugin().savePluginPreferences();
+ // save projects' meta info
+ IProject[] roots = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < roots.length; i++)
+ if (roots[i].isAccessible()) {
+ IStatus result = saveMetaInfo((Project) roots[i], null);
+ if (!result.isOK())
+ problems.merge(result);
+ }
+ if (Policy.DEBUG_SAVE_METAINFO)
+ System.out.println("Save workspace metainfo: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Ensures that the project meta-info is saved. The project meta-info
+ * is usually saved as soon as it changes, so this is just a sanity check
+ * to make sure there is something on disk before we shutdown.
+ *
+ * @return Status object containing non-critical warnings, or an OK status.
+ */
+ protected IStatus saveMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ //if there is nothing on disk, write the description
+ if (!workspace.getFileSystemManager().hasSavedDescription(project)) {
+ workspace.getFileSystemManager().writeSilently(project);
+ String msg = NLS.bind(Messages.resources_missingProjectMetaRepaired, project.getName());
+ return new ResourceStatus(IResourceStatus.MISSING_DESCRIPTION_REPAIRED, project.getFullPath(), msg);
+ }
+ if (Policy.DEBUG_SAVE_METAINFO)
+ System.out.println("Save metainfo for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Writes the current state of the entire workspace tree to disk.
+ * This is used during workspace save. saveTree(Project)
+ * is used to save the state of an individual project.
+ * @exception CoreException if there is a problem writing the tree to disk.
+ */
+ protected void saveTree(Map contexts, IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), true);
+ try {
+ IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
+ DataOutputStream output = new DataOutputStream(new SafeFileOutputStream(treeLocation.toOSString(), tempLocation.toOSString()));
+ try {
+ output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
+ writeTree(computeStatesToSave(contexts, workspace.getElementTree()), output, monitor);
+ } finally {
+ output.close();
+ }
+ } catch (Exception e) {
+ String msg = NLS.bind(Messages.resources_writeWorkspaceMeta, treeLocation);
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, Path.ROOT, msg, e);
+ }
+ if (Policy.DEBUG_SAVE_TREE)
+ System.out.println("Save Workspace Tree: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Should only be used for read purposes.
+ */
+ void setPluginsSavedState(HashMap savedStates) {
+ this.savedStates = Collections.synchronizedMap(savedStates);
+ }
+
+ protected void setSaveNumber(String pluginId, int number) {
+ masterTable.setProperty(SAVE_NUMBER_PREFIX + pluginId, new Integer(number).toString());
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool pool) {
+ lastSnap.shareStrings(pool);
+ }
+
+ public void shutdown(final IProgressMonitor monitor) {
+ // do a last snapshot if it was scheduled
+ // we force it in the same thread because it would not
+ // help if the job runs after we close the workspace
+ int state = snapshotJob.getState();
+ if (state == Job.WAITING || state == Job.SLEEPING)
+ // we cannot pass null to Job#run
+ snapshotJob.run(Policy.monitorFor(monitor));
+ // cancel the snapshot job
+ snapshotJob.cancel();
+ }
+
+ /**
+ * Performs a snapshot if one is deemed necessary.
+ * Encapsulates rules for determining when a snapshot is needed.
+ * This should be called at the end of every top level operation.
+ */
+ public void snapshotIfNeeded(boolean hasTreeChanges) {
+ // never schedule a snapshot while save is occurring.
+ if (isSaving)
+ return;
+ if (snapshotRequested || operationCount >= workspace.internalGetDescription().getOperationsPerSnapshot()) {
+ if (snapshotJob.getState() == Job.NONE)
+ snapshotJob.schedule();
+ else
+ snapshotJob.wakeUp();
+ } else {
+ if (hasTreeChanges) {
+ operationCount++;
+ if (snapshotJob.getState() == Job.NONE) {
+ if (Policy.DEBUG_SAVE)
+ System.out.println("Scheduling workspace snapshot"); //$NON-NLS-1$
+ long interval = workspace.internalGetDescription().getSnapshotInterval();
+ snapshotJob.schedule(Math.max(interval, MIN_SNAPSHOT_DELAY));
+ }
+ } else {
+ //only increment the operation count if we've had a sufficient number of no-ops
+ if (++noopCount > NO_OP_THRESHOLD) {
+ operationCount++;
+ noopCount = 0;
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs a snapshot of the workspace tree.
+ */
+ protected void snapTree(ElementTree tree, IProgressMonitor monitor) throws CoreException {
+ long start = System.currentTimeMillis();
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ //the tree must be immutable
+ tree.immutable();
+ // don't need to snapshot if there are no changes
+ if (tree == lastSnap)
+ return;
+ operationCount = 0;
+ IPath snapPath = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
+ ElementTreeWriter writer = new ElementTreeWriter(this);
+ java.io.File localFile = snapPath.toFile();
+ try {
+ SafeChunkyOutputStream safeStream = new SafeChunkyOutputStream(localFile);
+ DataOutputStream out = new DataOutputStream(safeStream);
+ try {
+ out.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
+ writeWorkspaceFields(out, monitor);
+ writer.writeDelta(tree, lastSnap, Path.ROOT, ElementTreeWriter.D_INFINITE, out, ResourceComparator.getSaveComparator());
+ safeStream.succeed();
+ } finally {
+ out.close();
+ }
+ } catch (IOException e) {
+ message = NLS.bind(Messages.resources_writeWorkspaceMeta, localFile.getAbsolutePath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, Path.ROOT, message, e);
+ }
+ lastSnap = tree;
+ } finally {
+ monitor.done();
+ }
+ if (Policy.DEBUG_SAVE_TREE)
+ System.out.println("Snapshot Workspace Tree: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Sorts the given array of trees so that the following rules are true:
+ * - The first tree has no parent
+ * - No tree has an ancestor with a greater index in the array.
+ * If there are no missing parents in the given trees array, this means
+ * that in the resulting array, the i'th tree's parent will be tree i-1.
+ * The input tree array may contain duplicate trees.
+ */
+ protected ElementTree[] sortTrees(ElementTree[] trees) {
+ /* the sorted list */
+ int numTrees = trees.length;
+ ElementTree[] sorted = new ElementTree[numTrees];
+
+ /* first build a table of ElementTree -> List of Integers(indices in trees array) */
+ Map table = new HashMap(numTrees * 2 + 1);
+ for (int i = 0; i < trees.length; i++) {
+ List indices = (List) table.get(trees[i]);
+ if (indices == null) {
+ indices = new ArrayList(10);
+ table.put(trees[i], indices);
+ }
+ indices.add(new Integer(i));
+ }
+
+ /* find the oldest tree (a descendent of all other trees) */
+ ElementTree oldest = trees[ElementTree.findOldest(trees)];
+
+ /**
+ * Walk through the chain of trees from oldest to newest,
+ * adding them to the sorted list as we go.
+ */
+ int i = numTrees - 1;
+ while (i >= 0) {
+ /* add all instances of the current oldest tree to the sorted list */
+ List indices = (List) table.remove(oldest);
+ for (Enumeration e = Collections.enumeration(indices); e.hasMoreElements();) {
+ e.nextElement();
+ sorted[i] = oldest;
+ i--;
+ }
+ if (i >= 0) {
+ /* find the next tree in the list */
+ ElementTree parent = oldest.getParent();
+ while (parent != null && table.get(parent) == null) {
+ parent = parent.getParent();
+ }
+ if (parent == null) {
+ IStatus status = new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, "null parent found while collapsing trees", null); //$NON-NLS-1$
+ Policy.log(status);
+ return null;
+ }
+ oldest = parent;
+ }
+ }
+ return sorted;
+ }
+
+ public void startup(IProgressMonitor monitor) throws CoreException {
+ restore(monitor);
+ java.io.File table = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES).toFile();
+ if (!table.exists())
+ table.getParentFile().mkdirs();
+ }
+
+ /**
+ * Update the expiration time for the given plug-in's tree. If the tree was never
+ * loaded, use the current value in the master table. If the tree has been loaded,
+ * use the provided new timestamp.
+ *
+ * The timestamp is used in the policy for cleaning up tree's of plug-ins that are
+ * not often activated.
+ */
+ protected void updateDeltaExpiration(String pluginId) {
+ String key = DELTA_EXPIRATION_PREFIX + pluginId;
+ if (!masterTable.containsKey(key))
+ masterTable.setProperty(key, Long.toString(System.currentTimeMillis()));
+ }
+
+ /**
+ * Visit the given resource (to depth infinite) and write out extra information
+ * like markers and sync info. To be called during a full save and project save.
+ *
+ * FIXME: This method is ugly. Fix it up and look at merging with #visitAndSnap
+ */
+ public void visitAndSave(final IResource root) throws CoreException {
+ // Ensure we have either a project or the workspace root
+ Assert.isLegal(root.getType() == IResource.ROOT || root.getType() == IResource.PROJECT);
+ // only write out info for accessible resources
+ if (!root.isAccessible())
+ return;
+
+ // Setup variables
+ final Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
+ final MarkerManager markerManager = workspace.getMarkerManager();
+ IPath markersLocation = workspace.getMetaArea().getMarkersLocationFor(root);
+ IPath markersTempLocation = workspace.getMetaArea().getBackupLocationFor(markersLocation);
+ IPath syncInfoLocation = workspace.getMetaArea().getSyncInfoLocationFor(root);
+ IPath syncInfoTempLocation = workspace.getMetaArea().getBackupLocationFor(syncInfoLocation);
+ final List writtenTypes = new ArrayList(5);
+ final List writtenPartners = new ArrayList(synchronizer.registry.size());
+ DataOutputStream o1 = null;
+ DataOutputStream o2 = null;
+ String message;
+
+ // Create the output streams
+ try {
+ o1 = new DataOutputStream(new SafeFileOutputStream(markersLocation.toOSString(), markersTempLocation.toOSString()));
+ // we don't store the sync info for the workspace root so don't create
+ // an empty file
+ if (root.getType() != IResource.ROOT)
+ o2 = new DataOutputStream(new SafeFileOutputStream(syncInfoLocation.toOSString(), syncInfoTempLocation.toOSString()));
+ } catch (IOException e) {
+ if (o1 != null)
+ try {
+ o1.close();
+ } catch (IOException e2) {
+ // ignore
+ }
+ message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
+ }
+
+ final DataOutputStream markersOutput = o1;
+ final DataOutputStream syncInfoOutput = o2;
+ // The following 2 piece array will hold a running total of the times
+ // taken to save markers and syncInfo respectively. This will cut down
+ // on the number of statements printed out as we would get 2 statements
+ // for each resource otherwise.
+ final long[] saveTimes = new long[2];
+
+ // Create the visitor
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (info != null) {
+ try {
+ // save the markers
+ long start = System.currentTimeMillis();
+ markerManager.save(info, requestor, markersOutput, writtenTypes);
+ long markerSaveTime = System.currentTimeMillis() - start;
+ saveTimes[0] += markerSaveTime;
+ persistMarkers += markerSaveTime;
+ // save the sync info - if we have the workspace root then the output stream will be null
+ if (syncInfoOutput != null) {
+ start = System.currentTimeMillis();
+ synchronizer.saveSyncInfo(info, requestor, syncInfoOutput, writtenPartners);
+ long syncInfoSaveTime = System.currentTimeMillis() - start;
+ saveTimes[1] += syncInfoSaveTime;
+ persistSyncInfo += syncInfoSaveTime;
+ }
+ } catch (IOException e) {
+ throw new WrappedRuntimeException(e);
+ }
+ }
+ // don't continue if the current resource is the workspace root, only continue for projects
+ return root.getType() != IResource.ROOT;
+ }
+ };
+
+ // Call the visitor
+ try {
+ try {
+ new ElementTreeIterator(workspace.getElementTree(), root.getFullPath()).iterate(visitor);
+ } catch (WrappedRuntimeException e) {
+ throw (IOException) e.getTargetException();
+ }
+ if (Policy.DEBUG_SAVE_MARKERS)
+ System.out.println("Save Markers for " + root.getFullPath() + ": " + saveTimes[0] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (Policy.DEBUG_SAVE_SYNCINFO)
+ System.out.println("Save SyncInfo for " + root.getFullPath() + ": " + saveTimes[1] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ removeGarbage(markersOutput, markersLocation, markersTempLocation);
+ // if we have the workspace root the output stream will be null and we
+ // don't have to perform cleanup code
+ if (syncInfoOutput != null)
+ removeGarbage(syncInfoOutput, syncInfoLocation, syncInfoTempLocation);
+ } catch (IOException e) {
+ message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
+ } finally {
+ FileUtil.safeClose(markersOutput);
+ FileUtil.safeClose(syncInfoOutput);
+ }
+
+ // recurse over the projects in the workspace if we were given the workspace root
+ if (root.getType() == IResource.PROJECT)
+ return;
+ IProject[] projects = ((IWorkspaceRoot) root).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ visitAndSave(projects[i]);
+ }
+
+ /**
+ * Visit the given resource (to depth infinite) and write out extra information
+ * like markers and sync info. To be called during a snapshot
+ *
+ * FIXME: This method is ugly. Fix it up and look at merging with #visitAndSnap
+ */
+ public void visitAndSnap(final IResource root) throws CoreException {
+ // Ensure we have either a project or the workspace root
+ Assert.isLegal(root.getType() == IResource.ROOT || root.getType() == IResource.PROJECT);
+ // only write out info for accessible resources
+ if (!root.isAccessible())
+ return;
+
+ // Setup variables
+ final Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
+ final MarkerManager markerManager = workspace.getMarkerManager();
+ IPath markersLocation = workspace.getMetaArea().getMarkersSnapshotLocationFor(root);
+ IPath syncInfoLocation = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(root);
+ SafeChunkyOutputStream safeMarkerStream = null;
+ SafeChunkyOutputStream safeSyncInfoStream = null;
+ DataOutputStream o1 = null;
+ DataOutputStream o2 = null;
+ String message;
+
+ // Create the output streams
+ try {
+ safeMarkerStream = new SafeChunkyOutputStream(markersLocation.toFile());
+ o1 = new DataOutputStream(safeMarkerStream);
+ // we don't store the sync info for the workspace root so don't create
+ // an empty file
+ if (root.getType() != IResource.ROOT) {
+ safeSyncInfoStream = new SafeChunkyOutputStream(syncInfoLocation.toFile());
+ o2 = new DataOutputStream(safeSyncInfoStream);
+ }
+ } catch (IOException e) {
+ FileUtil.safeClose(o1);
+ message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
+ }
+
+ final DataOutputStream markersOutput = o1;
+ final DataOutputStream syncInfoOutput = o2;
+ int markerFileSize = markersOutput.size();
+ int syncInfoFileSize = safeSyncInfoStream == null ? -1 : syncInfoOutput.size();
+ // The following 2 piece array will hold a running total of the times
+ // taken to save markers and syncInfo respectively. This will cut down
+ // on the number of statements printed out as we would get 2 statements
+ // for each resource otherwise.
+ final long[] snapTimes = new long[2];
+
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ ResourceInfo info = (ResourceInfo) elementContents;
+ if (info != null) {
+ try {
+ // save the markers
+ long start = System.currentTimeMillis();
+ markerManager.snap(info, requestor, markersOutput);
+ long markerSnapTime = System.currentTimeMillis() - start;
+ snapTimes[0] += markerSnapTime;
+ persistMarkers += markerSnapTime;
+ // save the sync info - if we have the workspace root then the output stream will be null
+ if (syncInfoOutput != null) {
+ start = System.currentTimeMillis();
+ synchronizer.snapSyncInfo(info, requestor, syncInfoOutput);
+ long syncInfoSnapTime = System.currentTimeMillis() - start;
+ snapTimes[1] += syncInfoSnapTime;
+ persistSyncInfo += syncInfoSnapTime;
+ }
+ } catch (IOException e) {
+ throw new WrappedRuntimeException(e);
+ }
+ }
+ // don't continue if the current resource is the workspace root, only continue for projects
+ return root.getType() != IResource.ROOT;
+ }
+ };
+
+ try {
+ // Call the visitor
+ try {
+ new ElementTreeIterator(workspace.getElementTree(), root.getFullPath()).iterate(visitor);
+ } catch (WrappedRuntimeException e) {
+ throw (IOException) e.getTargetException();
+ }
+ if (Policy.DEBUG_SAVE_MARKERS)
+ System.out.println("Snap Markers for " + root.getFullPath() + ": " + snapTimes[0] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (Policy.DEBUG_SAVE_SYNCINFO)
+ System.out.println("Snap SyncInfo for " + root.getFullPath() + ": " + snapTimes[1] + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (markerFileSize != markersOutput.size())
+ safeMarkerStream.succeed();
+ if (safeSyncInfoStream != null && syncInfoFileSize != syncInfoOutput.size())
+ safeSyncInfoStream.succeed();
+ } catch (IOException e) {
+ message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
+ } finally {
+ FileUtil.safeClose(markersOutput);
+ FileUtil.safeClose(syncInfoOutput);
+ }
+
+ // recurse over the projects in the workspace if we were given the workspace root
+ if (root.getType() == IResource.PROJECT)
+ return;
+ IProject[] projects = ((IWorkspaceRoot) root).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ visitAndSnap(projects[i]);
+ }
+
+ /**
+ * Writes out persistent information about all builders for which a last built
+ * tree is available. File format is:
+ * int - number of builders
+ * for each builder:
+ * UTF - project name
+ * UTF - fully qualified builder extension name
+ * int - number of interesting projects for builder
+ * For each interesting project:
+ * UTF - interesting project name
+ */
+ protected void writeBuilderPersistentInfo(DataOutputStream output, List builders, List trees, IProgressMonitor monitor) throws IOException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ // write the number of builders we are saving
+ int numBuilders = builders.size();
+ output.writeInt(numBuilders);
+ for (int i = 0; i < numBuilders; i++) {
+ BuilderPersistentInfo info = (BuilderPersistentInfo) builders.get(i);
+ output.writeUTF(info.getProjectName());
+ output.writeUTF(info.getBuilderName());
+ // write interesting projects
+ IProject[] interestingProjects = info.getInterestingProjects();
+ output.writeInt(interestingProjects.length);
+ for (int j = 0; j < interestingProjects.length; j++)
+ output.writeUTF(interestingProjects[j].getName());
+ ElementTree last = info.getLastBuiltTree();
+ //it is not unusual for a builder to have no last built tree (for example after a clean)
+ if (last == null)
+ last = workspace.getElementTree();
+ trees.add(last);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * @see IElementInfoFlattener#writeElement(IPath, Object, DataOutput)
+ */
+ public void writeElement(IPath path, Object element, DataOutput output) throws IOException {
+ Assert.isNotNull(path);
+ Assert.isNotNull(element);
+ Assert.isNotNull(output);
+ ResourceInfo info = (ResourceInfo) element;
+ output.writeInt(info.getFlags());
+ info.writeTo(output);
+ }
+
+ protected void writeTree(Map statesToSave, DataOutputStream output, IProgressMonitor monitor) throws IOException, CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ boolean wasImmutable = false;
+ try {
+ // Create an array of trees to save. Ensure that the current one is in the list
+ ElementTree current = workspace.getElementTree();
+ wasImmutable = current.isImmutable();
+ current.immutable();
+ ArrayList trees = new ArrayList(statesToSave.size() * 2); // pick a number
+ monitor.worked(Policy.totalWork * 10 / 100);
+
+ // write out the workspace fields
+ writeWorkspaceFields(output, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
+
+ // save plugin info
+ output.writeInt(statesToSave.size()); // write the number of plugins we are saving
+ for (Iterator i = statesToSave.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String pluginId = (String) entry.getKey();
+ output.writeUTF(pluginId);
+ trees.add(entry.getValue()); // tree
+ updateDeltaExpiration(pluginId);
+ }
+ monitor.worked(Policy.totalWork * 10 / 100);
+
+ // add builders' trees
+ IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ List builders = new ArrayList(projects.length * 2);
+ for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ if (project.isOpen()) {
+ ArrayList infos = workspace.getBuildManager().createBuildersPersistentInfo(project);
+ if (infos != null)
+ builders.addAll(infos);
+ }
+ }
+ writeBuilderPersistentInfo(output, builders, trees, Policy.subMonitorFor(monitor, Policy.totalWork * 10 / 100));
+
+ // add the current tree in the list as the last element
+ trees.add(current);
+
+ /* save the forest! */
+ ElementTreeWriter writer = new ElementTreeWriter(this);
+ ElementTree[] treesToSave = (ElementTree[]) trees.toArray(new ElementTree[trees.size()]);
+ writer.writeDeltaChain(treesToSave, Path.ROOT, ElementTreeWriter.D_INFINITE, output, ResourceComparator.getSaveComparator());
+ monitor.worked(Policy.totalWork * 50 / 100);
+ } finally {
+ if (!wasImmutable)
+ workspace.newWorkingTree();
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Attempts to save all the trees for this project (the current tree
+ * plus a tree for each builder with a previously built state). Throws
+ * an IOException if anything went wrong during save. Attempts to close
+ * the provided stream at all costs.
+ */
+ protected void writeTree(Project project, DataOutputStream output, IProgressMonitor monitor) throws IOException, CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", 10); //$NON-NLS-1$
+ boolean wasImmutable = false;
+ try {
+ /**
+ * Obtain a list of BuilderPersistentInfo.
+ * This list includes builders that have never been instantiated
+ * but already had a last built state.
+ */
+ ArrayList builderInfos = workspace.getBuildManager().createBuildersPersistentInfo(project);
+ if (builderInfos == null)
+ builderInfos = new ArrayList(5);
+ List trees = new ArrayList(builderInfos.size() + 1);
+ monitor.worked(1);
+
+ /* Make sure the most recent tree is in the array */
+ ElementTree current = workspace.getElementTree();
+ wasImmutable = current.isImmutable();
+ current.immutable();
+
+ /* add the tree for each builder to the array */
+ writeBuilderPersistentInfo(output, builderInfos, trees, Policy.subMonitorFor(monitor, 1));
+ trees.add(current);
+
+ /* save the forest! */
+ ElementTreeWriter writer = new ElementTreeWriter(this);
+ ElementTree[] treesToSave = (ElementTree[]) trees.toArray(new ElementTree[trees.size()]);
+ writer.writeDeltaChain(treesToSave, project.getFullPath(), ElementTreeWriter.D_INFINITE, output, ResourceComparator.getSaveComparator());
+ monitor.worked(8);
+ } finally {
+ if (output != null)
+ output.close();
+ if (!wasImmutable)
+ workspace.newWorkingTree();
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected void writeTree(Project project, int depth) throws CoreException {
+ long start = System.currentTimeMillis();
+ IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(project, true);
+ IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
+ try {
+ SafeFileOutputStream safe = new SafeFileOutputStream(treeLocation.toOSString(), tempLocation.toOSString());
+ try {
+ DataOutputStream output = new DataOutputStream(safe);
+ output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
+ writeTree(project, output, null);
+ } finally {
+ safe.close();
+ }
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.resources_writeMeta, project.getFullPath());
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, treeLocation, msg, e);
+ }
+ if (Policy.DEBUG_SAVE_TREE)
+ System.out.println("Save tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ protected void writeWorkspaceFields(DataOutputStream output, IProgressMonitor monitor) throws IOException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ // save the next node id
+ output.writeLong(workspace.nextNodeId);
+ // save the modification stamp (no longer used)
+ output.writeLong(0L);
+ // save the marker id counter
+ output.writeLong(workspace.nextMarkerId);
+ // save the registered sync partners in the synchronizer
+ ((Synchronizer) workspace.getSynchronizer()).savePartners(output);
+ } finally {
+ monitor.done();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SavedState.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.events.ResourceDelta;
+import org.eclipse.core.internal.events.ResourceDeltaFactory;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * Standard implementation of the ISavedState interface.
+ */
+public class SavedState implements ISavedState {
+ ElementTree oldTree;
+ ElementTree newTree;
+ SafeFileTable fileTable;
+ String pluginId;
+ Workspace workspace;
+
+ SavedState(Workspace workspace, String pluginId, ElementTree oldTree, ElementTree newTree) throws CoreException {
+ this.workspace = workspace;
+ this.pluginId = pluginId;
+ this.newTree = newTree;
+ this.oldTree = oldTree;
+ this.fileTable = restoreFileTable();
+ }
+
+ void forgetTrees() {
+ newTree = null;
+ oldTree = null;
+ workspace.saveManager.clearDeltaExpiration(pluginId);
+ }
+
+ public int getSaveNumber() {
+ return workspace.getSaveManager().getSaveNumber(pluginId);
+ }
+
+ protected SafeFileTable getFileTable() {
+ return fileTable;
+ }
+
+ protected SafeFileTable restoreFileTable() throws CoreException {
+ if (fileTable == null)
+ fileTable = new SafeFileTable(pluginId);
+ return fileTable;
+ }
+
+ public IPath lookup(IPath file) {
+ return getFileTable().lookup(file);
+ }
+
+ public IPath[] getFiles() {
+ return getFileTable().getFiles();
+ }
+
+ public void processResourceChangeEvents(IResourceChangeListener listener) {
+ try {
+ final ISchedulingRule rule = workspace.getRoot();
+ try {
+ workspace.prepareOperation(rule, null);
+ if (oldTree == null || newTree == null)
+ return;
+ workspace.beginOperation(true);
+ ResourceDelta delta = ResourceDeltaFactory.computeDelta(workspace, oldTree, newTree, Path.ROOT, -1);
+ forgetTrees(); // free trees to prevent memory leak
+ workspace.getNotificationManager().broadcastChanges(listener, IResourceChangeEvent.POST_BUILD, delta);
+ } finally {
+ workspace.endOperation(rule, false, null);
+ }
+ } catch (CoreException e) {
+ // this is unlikely to happen, so, just log it
+ Policy.log(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SyncInfoReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is used to read sync info from disk. Subclasses implement
+ * version specific reading code.
+ */
+public class SyncInfoReader {
+ protected Workspace workspace;
+ protected Synchronizer synchronizer;
+
+ public SyncInfoReader(Workspace workspace, Synchronizer synchronizer) {
+ super();
+ this.workspace = workspace;
+ this.synchronizer = synchronizer;
+ }
+
+ /**
+ * Returns the appropriate reader for the given version.
+ */
+ protected SyncInfoReader getReader(int formatVersion) throws IOException {
+ switch (formatVersion) {
+ case 2 :
+ return new SyncInfoReader_2(workspace, synchronizer);
+ case 3 :
+ return new SyncInfoReader_3(workspace, synchronizer);
+ default :
+ throw new IOException(NLS.bind(Messages.resources_format, new Integer(formatVersion)));
+ }
+ }
+
+ public void readPartners(DataInputStream input) throws CoreException {
+ try {
+ int size = input.readInt();
+ Set registry = new HashSet(size);
+ for (int i = 0; i < size; i++) {
+ String qualifier = input.readUTF();
+ String local = input.readUTF();
+ registry.add(new QualifiedName(qualifier, local));
+ }
+ synchronizer.setRegistry(registry);
+ } catch (IOException e) {
+ String message = NLS.bind(Messages.resources_readSync, e);
+ throw new ResourceException(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, message));
+ }
+ }
+
+ public void readSyncInfo(DataInputStream input) throws IOException, CoreException {
+ // dispatch to the appropriate reader depending
+ // on the version of the file
+ int formatVersion = readVersionNumber(input);
+ SyncInfoReader reader = getReader(formatVersion);
+ reader.readSyncInfo(input);
+ }
+
+ protected static int readVersionNumber(DataInputStream input) throws IOException {
+ return input.readInt();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SyncInfoReader_2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.ObjectMap;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is used to read sync info from disk. This is the implementation
+ * for reading files with version number 2.
+ */
+public class SyncInfoReader_2 extends SyncInfoReader {
+
+ // for sync info
+ public static final int INDEX = 1;
+ public static final int QNAME = 2;
+
+ public SyncInfoReader_2(Workspace workspace, Synchronizer synchronizer) {
+ super(workspace, synchronizer);
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE*
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH SIZE SYNCINFO*
+ * RESOURCE_PATH -> String
+ * SIZE -> int
+ * SYNCINFO -> TYPE BYTES
+ * TYPE -> INDEX | QNAME
+ * INDEX -> int int
+ * QNAME -> int String
+ * BYTES -> byte[]
+ *
+ */
+ public void readSyncInfo(DataInputStream input) throws IOException, CoreException {
+ try {
+ List readPartners = new ArrayList(5);
+ while (true) {
+ IPath path = new Path(input.readUTF());
+ readSyncInfo(path, input, readPartners);
+ }
+ } catch (EOFException e) {
+ // ignore end of file
+ }
+ }
+
+ private void readSyncInfo(IPath path, DataInputStream input, List readPartners) throws IOException, CoreException {
+ int size = input.readInt();
+ ObjectMap table = new ObjectMap(size);
+ for (int i = 0; i < size; i++) {
+ QualifiedName name = null;
+ int type = input.readInt();
+ switch (type) {
+ case QNAME :
+ String qualifier = input.readUTF();
+ String local = input.readUTF();
+ name = new QualifiedName(qualifier, local);
+ readPartners.add(name);
+ break;
+ case INDEX :
+ name = (QualifiedName) readPartners.get(input.readInt());
+ break;
+ default :
+ //if we get here then the sync info file is corrupt
+ String msg = NLS.bind(Messages.resources_readSync, path == null ? "" : path.toString()); //$NON-NLS-1$
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, path, msg, null);
+ }
+ // read the bytes
+ int length = input.readInt();
+ byte[] bytes = new byte[length];
+ input.readFully(bytes);
+ // put them in the table
+ table.put(name, bytes);
+ }
+ // set the table on the resource info
+ ResourceInfo info = workspace.getResourceInfo(path, true, false);
+ if (info == null)
+ return;
+ info.setSyncInfo(table);
+ info.clear(ICoreConstants.M_SYNCINFO_SNAP_DIRTY);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SyncInfoReader_3.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.ObjectMap;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is used to read sync info from disk. This is the implementation
+ * for reading files with version number 3.
+ */
+public class SyncInfoReader_3 extends SyncInfoReader {
+
+ // for sync info
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ public SyncInfoReader_3(Workspace workspace, Synchronizer synchronizer) {
+ super(workspace, synchronizer);
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE+
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH SIZE SYNCINFO*
+ * RESOURCE_PATH -> String
+ * SIZE -> int
+ * SYNCINFO -> TYPE BYTES
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * BYTES -> byte[]
+ *
+ */
+ public void readSyncInfo(DataInputStream input) throws IOException, CoreException {
+ try {
+ List readPartners = new ArrayList(5);
+ while (true) {
+ IPath path = new Path(input.readUTF());
+ readSyncInfo(path, input, readPartners);
+ }
+ } catch (EOFException e) {
+ // ignore end of file
+ }
+ }
+
+ private void readSyncInfo(IPath path, DataInputStream input, List readPartners) throws IOException, CoreException {
+ int size = input.readInt();
+ ObjectMap table = new ObjectMap(size);
+ for (int i = 0; i < size; i++) {
+ QualifiedName name = null;
+ byte type = input.readByte();
+ switch (type) {
+ case QNAME :
+ String qualifier = input.readUTF();
+ String local = input.readUTF();
+ name = new QualifiedName(qualifier, local);
+ readPartners.add(name);
+ break;
+ case INDEX :
+ name = (QualifiedName) readPartners.get(input.readInt());
+ break;
+ default :
+ //if we get here then the sync info file is corrupt
+ String msg = NLS.bind(Messages.resources_readSync, path == null ? "" : path.toString()); //$NON-NLS-1$
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, path, msg, null);
+ }
+ // read the bytes
+ int length = input.readInt();
+ byte[] bytes = new byte[length];
+ input.readFully(bytes);
+ // put them in the table
+ table.put(name, bytes);
+ }
+ // set the table on the resource info
+ ResourceInfo info = workspace.getResourceInfo(path, true, false);
+ if (info == null)
+ return;
+ info.setSyncInfo(table);
+ info.clear(ICoreConstants.M_SYNCINFO_SNAP_DIRTY);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SyncInfoSnapReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.osgi.util.NLS;
+
+public class SyncInfoSnapReader {
+ protected Workspace workspace;
+ protected Synchronizer synchronizer;
+
+ public SyncInfoSnapReader(Workspace workspace, Synchronizer synchronizer) {
+ super();
+ this.workspace = workspace;
+ this.synchronizer = synchronizer;
+ }
+
+ /**
+ * Returns the appropriate reader for the given version.
+ */
+ protected SyncInfoSnapReader getReader(int formatVersion) throws IOException {
+ switch (formatVersion) {
+ case 3 :
+ return new SyncInfoSnapReader_3(workspace, synchronizer);
+ default :
+ throw new IOException(NLS.bind(Messages.resources_format, new Integer(formatVersion)));
+ }
+ }
+
+ public void readSyncInfo(DataInputStream input) throws IOException {
+ // dispatch to the appropriate reader depending
+ // on the version of the file
+ int formatVersion = readVersionNumber(input);
+ SyncInfoSnapReader reader = getReader(formatVersion);
+ reader.readSyncInfo(input);
+ }
+
+ protected static int readVersionNumber(DataInputStream input) throws IOException {
+ return input.readInt();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SyncInfoSnapReader_3.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import org.eclipse.core.internal.utils.ObjectMap;
+import org.eclipse.core.runtime.*;
+
+public class SyncInfoSnapReader_3 extends SyncInfoSnapReader {
+
+ public SyncInfoSnapReader_3(Workspace workspace, Synchronizer synchronizer) {
+ super(workspace, synchronizer);
+ }
+
+ private ObjectMap internalReadSyncInfo(DataInputStream input) throws IOException {
+ int size = input.readInt();
+ ObjectMap map = new ObjectMap(size);
+ for (int i = 0; i < size; i++) {
+ // read the qualified name
+ String qualifier = input.readUTF();
+ String local = input.readUTF();
+ QualifiedName name = new QualifiedName(qualifier, local);
+ // read the bytes
+ int length = input.readInt();
+ byte[] bytes = new byte[length];
+ input.readFully(bytes);
+ // put them in the table
+ map.put(name, bytes);
+ }
+ return map;
+ }
+
+ /**
+ * SNAP_FILE -> [VERSION_ID RESOURCE]*
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH SIZE SYNCINFO*
+ * RESOURCE_PATH -> String
+ * SIZE -> int
+ * SYNCINFO -> QNAME BYTES
+ * QNAME -> String String
+ * BYTES -> byte[]
+ */
+ public void readSyncInfo(DataInputStream input) throws IOException {
+ IPath path = new Path(input.readUTF());
+ ObjectMap map = internalReadSyncInfo(input);
+ // set the table on the resource info
+ ResourceInfo info = workspace.getResourceInfo(path, true, false);
+ if (info == null)
+ return;
+ info.setSyncInfo(map);
+ info.clear(ICoreConstants.M_SYNCINFO_SNAP_DIRTY);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SyncInfoWriter.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.*;
+import org.eclipse.core.internal.watson.IPathRequestor;
+import org.eclipse.core.runtime.QualifiedName;
+
+public class SyncInfoWriter {
+ protected Synchronizer synchronizer;
+ protected Workspace workspace;
+
+ // version number
+ public static final int SYNCINFO_SAVE_VERSION = 3;
+ public static final int SYNCINFO_SNAP_VERSION = 3;
+
+ // for sync info
+ public static final byte INDEX = 1;
+ public static final byte QNAME = 2;
+
+ public SyncInfoWriter(Workspace workspace, Synchronizer synchronizer) {
+ super();
+ this.workspace = workspace;
+ this.synchronizer = synchronizer;
+ }
+
+ public void savePartners(DataOutputStream output) throws IOException {
+ Set registry = synchronizer.getRegistry();
+ output.writeInt(registry.size());
+ for (Iterator i = registry.iterator(); i.hasNext();) {
+ QualifiedName qname = (QualifiedName) i.next();
+ output.writeUTF(qname.getQualifier());
+ output.writeUTF(qname.getLocalName());
+ }
+ }
+
+ /**
+ * SAVE_FILE -> VERSION_ID RESOURCE+
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH SIZE SYNCINFO*
+ * RESOURCE_PATH -> String
+ * SIZE -> int
+ * SYNCINFO -> TYPE BYTES
+ * TYPE -> INDEX | QNAME
+ * INDEX -> byte int
+ * QNAME -> byte String
+ * BYTES -> byte[]
+ */
+ public void saveSyncInfo(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List writtenPartners) throws IOException {
+ Map table = info.getSyncInfo(false);
+ if (table == null)
+ return;
+ // if this is the first sync info that we have written, then
+ // write the version id for the file.
+ if (output.size() == 0)
+ output.writeInt(SYNCINFO_SAVE_VERSION);
+ output.writeUTF(requestor.requestPath().toString());
+ output.writeInt(table.size());
+ for (Iterator i = table.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ QualifiedName name = (QualifiedName) entry.getKey();
+ // if we have already written the partner name once, then write an integer
+ // constant to represent it instead to remove duplication
+ int index = writtenPartners.indexOf(name);
+ if (index == -1) {
+ // FIXME: what to do about null qualifier?
+ output.writeByte(QNAME);
+ output.writeUTF(name.getQualifier());
+ output.writeUTF(name.getLocalName());
+ writtenPartners.add(name);
+ } else {
+ output.writeByte(INDEX);
+ output.writeInt(index);
+ }
+ byte[] bytes = (byte[]) entry.getValue();
+ output.writeInt(bytes.length);
+ output.write(bytes);
+ }
+ }
+
+ /**
+ * SNAP_FILE -> [VERSION_ID RESOURCE]*
+ * VERSION_ID -> int
+ * RESOURCE -> RESOURCE_PATH SIZE SYNCINFO*
+ * RESOURCE_PATH -> String
+ * SIZE -> int
+ * SYNCINFO -> QNAME BYTES
+ * QNAME -> String String
+ * BYTES -> byte[]
+ */
+ public void snapSyncInfo(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException {
+ if (!info.isSet(ICoreConstants.M_SYNCINFO_SNAP_DIRTY))
+ return;
+ Map table = info.getSyncInfo(false);
+ if (table == null)
+ return;
+ // write the version id for the snapshot.
+ output.writeInt(SYNCINFO_SNAP_VERSION);
+ output.writeUTF(requestor.requestPath().toString());
+ output.writeInt(table.size());
+ for (Iterator i = table.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ QualifiedName name = (QualifiedName) entry.getKey();
+ output.writeUTF(name.getQualifier());
+ output.writeUTF(name.getLocalName());
+ byte[] bytes = (byte[]) entry.getValue();
+ output.writeInt(bytes.length);
+ output.write(bytes);
+ }
+ info.clear(ICoreConstants.M_SYNCINFO_SNAP_DIRTY);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Synchronizer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.localstore.SafeChunkyInputStream;
+import org.eclipse.core.internal.localstore.SafeFileInputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.IPathRequestor;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+//
+public class Synchronizer implements ISynchronizer {
+ protected Workspace workspace;
+ protected SyncInfoWriter writer;
+
+ // Registry of sync partners. Set of qualified names.
+ protected Set registry = new HashSet(5);
+
+ public Synchronizer(Workspace workspace) {
+ super();
+ this.workspace = workspace;
+ this.writer = new SyncInfoWriter(workspace, this);
+ }
+
+ /**
+ * @see ISynchronizer#accept(QualifiedName, IResource, IResourceVisitor, int)
+ */
+ public void accept(QualifiedName partner, IResource resource, IResourceVisitor visitor, int depth) throws CoreException {
+ Assert.isLegal(partner != null);
+ Assert.isLegal(resource != null);
+ Assert.isLegal(visitor != null);
+
+ // if we don't have sync info for the given identifier, then skip it
+ if (getSyncInfo(partner, resource) != null) {
+ // visit the resource and if the visitor says to stop the recursion then return
+ if (!visitor.visit(resource))
+ return;
+ }
+
+ // adjust depth if necessary
+ if (depth == IResource.DEPTH_ZERO || resource.getType() == IResource.FILE)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+
+ // otherwise recurse over the children
+ IResource[] children = ((IContainer) resource).members();
+ for (int i = 0; i < children.length; i++)
+ accept(partner, children[i], visitor, depth);
+ }
+
+ /**
+ * @see ISynchronizer#add(QualifiedName)
+ */
+ public void add(QualifiedName partner) {
+ Assert.isLegal(partner != null);
+ registry.add(partner);
+ }
+
+ /**
+ * @see ISynchronizer#flushSyncInfo(QualifiedName, IResource, int)
+ */
+ public void flushSyncInfo(final QualifiedName partner, final IResource root, final int depth) throws CoreException {
+ Assert.isLegal(partner != null);
+ Assert.isLegal(root != null);
+
+ IWorkspaceRunnable body = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ IResourceVisitor visitor = new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ //only need to flush sync info if there is sync info
+ if (getSyncInfo(partner, resource) != null)
+ setSyncInfo(partner, resource, null);
+ return true;
+ }
+ };
+ root.accept(visitor, depth, true);
+ }
+ };
+ workspace.run(body, root, IResource.NONE, null);
+ }
+
+ /**
+ * @see ISynchronizer#getPartners()
+ */
+ public QualifiedName[] getPartners() {
+ return (QualifiedName[]) registry.toArray(new QualifiedName[registry.size()]);
+ }
+
+ /**
+ * For use by the serialization code.
+ */
+ protected Set getRegistry() {
+ return registry;
+ }
+
+ /**
+ * @see ISynchronizer#getSyncInfo(QualifiedName, IResource)
+ */
+ public byte[] getSyncInfo(QualifiedName partner, IResource resource) throws CoreException {
+ Assert.isLegal(partner != null);
+ Assert.isLegal(resource != null);
+
+ if (!isRegistered(partner)) {
+ String message = NLS.bind(Messages.synchronizer_partnerNotRegistered, partner);
+ throw new ResourceException(new ResourceStatus(IResourceStatus.PARTNER_NOT_REGISTERED, message));
+ }
+
+ // namespace check, if the resource doesn't exist then return null
+ ResourceInfo info = workspace.getResourceInfo(resource.getFullPath(), true, false);
+ return (info == null) ? null : info.getSyncInfo(partner, true);
+ }
+
+ protected boolean isRegistered(QualifiedName partner) {
+ Assert.isLegal(partner != null);
+ return registry.contains(partner);
+ }
+
+ /**
+ * @see #savePartners(DataOutputStream)
+ */
+ public void readPartners(DataInputStream input) throws CoreException {
+ SyncInfoReader reader = new SyncInfoReader(workspace, this);
+ reader.readPartners(input);
+ }
+
+ public void restore(IResource resource, IProgressMonitor monitor) throws CoreException {
+ // first restore from the last save and then apply any snapshots
+ restoreFromSave(resource);
+ restoreFromSnap(resource);
+ }
+
+ protected void restoreFromSave(IResource resource) throws CoreException {
+ IPath sourceLocation = workspace.getMetaArea().getSyncInfoLocationFor(resource);
+ IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(sourceLocation);
+ if (!sourceLocation.toFile().exists() && !tempLocation.toFile().exists())
+ return;
+ try {
+ DataInputStream input = new DataInputStream(new SafeFileInputStream(sourceLocation.toOSString(), tempLocation.toOSString()));
+ try {
+ SyncInfoReader reader = new SyncInfoReader(workspace, this);
+ reader.readSyncInfo(input);
+ } finally {
+ input.close();
+ }
+ } catch (Exception e) {
+ //don't let runtime exceptions such as ArrayIndexOutOfBounds prevent startup
+ String msg = NLS.bind(Messages.resources_readMeta, sourceLocation);
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, sourceLocation, msg, e);
+ }
+ }
+
+ protected void restoreFromSnap(IResource resource) {
+ IPath sourceLocation = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(resource);
+ if (!sourceLocation.toFile().exists())
+ return;
+ try {
+ DataInputStream input = new DataInputStream(new SafeChunkyInputStream(sourceLocation.toFile()));
+ try {
+ SyncInfoSnapReader reader = new SyncInfoSnapReader(workspace, this);
+ while (true)
+ reader.readSyncInfo(input);
+ } catch (EOFException eof) {
+ // ignore end of file -- proceed with what we successfully read
+ } finally {
+ input.close();
+ }
+ } catch (Exception e) {
+ // only log the exception, we should not fail restoring the snapshot
+ String msg = NLS.bind(Messages.resources_readMeta, sourceLocation);
+ Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, sourceLocation, msg, e));
+ }
+ }
+
+ /**
+ * @see ISynchronizer#remove(QualifiedName)
+ */
+ public void remove(QualifiedName partner) {
+ Assert.isLegal(partner != null);
+ if (isRegistered(partner)) {
+ // remove all sync info for this partner
+ try {
+ flushSyncInfo(partner, workspace.getRoot(), IResource.DEPTH_INFINITE);
+ registry.remove(partner);
+ } catch (CoreException e) {
+ // XXX: flush needs to be more resilient and not throw exceptions all the time
+ Policy.log(e);
+ }
+ }
+ }
+
+ public void savePartners(DataOutputStream output) throws IOException {
+ writer.savePartners(output);
+ }
+
+ public void saveSyncInfo(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List writtenPartners) throws IOException {
+ writer.saveSyncInfo(info, requestor, output, writtenPartners);
+ }
+
+ protected void setRegistry(Set registry) {
+ this.registry = registry;
+ }
+
+ /**
+ * @see ISynchronizer#setSyncInfo(QualifiedName, IResource, byte[])
+ */
+ public void setSyncInfo(QualifiedName partner, IResource resource, byte[] info) throws CoreException {
+ Assert.isLegal(partner != null);
+ Assert.isLegal(resource != null);
+ try {
+ workspace.prepareOperation(resource, null);
+ workspace.beginOperation(true);
+ if (!isRegistered(partner)) {
+ String message = NLS.bind(Messages.synchronizer_partnerNotRegistered, partner);
+ throw new ResourceException(new ResourceStatus(IResourceStatus.PARTNER_NOT_REGISTERED, message));
+ }
+ // we do not store sync info on the workspace root
+ if (resource.getType() == IResource.ROOT)
+ return;
+ // if the resource doesn't yet exist then create a phantom so we can set the sync info on it
+ Resource target = (Resource) resource;
+ ResourceInfo resourceInfo = workspace.getResourceInfo(target.getFullPath(), true, false);
+ int flags = target.getFlags(resourceInfo);
+ if (!target.exists(flags, false)) {
+ if (info == null)
+ return;
+ //ensure it is possible to create this resource
+ target.checkValidPath(target.getFullPath(), target.getType(), false);
+ Container parent = (Container)target.getParent();
+ parent.checkAccessible(parent.getFlags(parent.getResourceInfo(true, false)));
+ workspace.createResource(target, true);
+ }
+ resourceInfo = target.getResourceInfo(true, true);
+ resourceInfo.setSyncInfo(partner, info);
+ resourceInfo.incrementSyncInfoGenerationCount();
+ resourceInfo.set(ICoreConstants.M_SYNCINFO_SNAP_DIRTY);
+ flags = target.getFlags(resourceInfo);
+ if (target.isPhantom(flags) && resourceInfo.getSyncInfo(false) == null) {
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.resources_deleteProblem, null);
+ ((Resource) resource).deleteResource(false, status);
+ if (!status.isOK())
+ throw new ResourceException(status);
+ }
+ } finally {
+ workspace.endOperation(resource, false, null);
+ }
+ }
+
+ public void snapSyncInfo(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException {
+ writer.snapSyncInfo(info, requestor, output);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/TestingSupport.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.Properties;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+/**
+ * Provides special internal access to the workspace resource implementation.
+ * This class is to be used for testing purposes only.
+ *
+ * @since 2.0
+ */
+public class TestingSupport {
+ /**
+ * Returns the save manager's master table.
+ */
+ public static Properties getMasterTable() {
+ return ((Workspace) ResourcesPlugin.getWorkspace()).getSaveManager().getMasterTable();
+ }
+
+ /**
+ * Blocks the calling thread until background snapshot completes.
+ * @since 3.0
+ */
+ public static void waitForSnapshot() {
+ try {
+ ((Workspace) ResourcesPlugin.getWorkspace()).getSaveManager().snapshotJob.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Interrupted while waiting for snapshot"); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * Class cannot be instantiated.
+ */
+ private TestingSupport() {
+ // not allowed
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,317 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.jobs.*;
+
+/**
+ * The work manager governs concurrent access to the workspace tree. The {@link #lock}
+ * field is used to protect the workspace tree data structure from concurrent
+ * write attempts. This is an internal lock that is generally not held while
+ * client code is running. Scheduling rules are used by client code to obtain
+ * exclusive write access to a portion of the workspace.
+ *
+ * This class also tracks operation state for each thread that is involved in an
+ * operation. This includes prepared and running operation depth, auto-build
+ * strategy and cancel state.
+ */
+public class WorkManager implements IManager {
+ /**
+ * Scheduling rule for use during resource change notification. This rule
+ * must always be allowed to nest within a resource rule of any granularity
+ * since it is used from within the scope of all resource changing
+ * operations. The purpose of this rule is two-fold: 1. To prevent other
+ * resource changing jobs from being scheduled while the notification is
+ * running 2. To cause an exception if a resource change listener tries to
+ * begin a resource rule during a notification. This also prevents
+ * deadlock, because the notification thread owns the workspace lock, and
+ * threads that own the workspace lock must never block trying to acquire a
+ * resource rule.
+ */
+ class NotifyRule implements ISchedulingRule {
+ public boolean contains(ISchedulingRule rule) {
+ return (rule instanceof IResource) || rule.getClass().equals(NotifyRule.class);
+ }
+
+ public boolean isConflicting(ISchedulingRule rule) {
+ return contains(rule);
+ }
+ }
+
+ /**
+ * Indicates that the last checkIn failed, either due to cancelation or due to the
+ * workspace tree being locked for modifications (during resource change events).
+ */
+ private final ThreadLocal checkInFailed = new ThreadLocal();
+ /**
+ * Indicates whether any operations have run that may require a build.
+ */
+ private boolean hasBuildChanges = false;
+ private IJobManager jobManager;
+ /**
+ * The primary workspace lock. This lock must be held by any thread
+ * modifying the workspace tree.
+ */
+ private final ILock lock;
+
+ /**
+ * The current depth of running nested operations.
+ */
+ private int nestedOperations = 0;
+
+ private NotifyRule notifyRule = new NotifyRule();
+
+ private boolean operationCanceled = false;
+
+ /**
+ * The current depth of prepared operations.
+ */
+ private int preparedOperations = 0;
+ private Workspace workspace;
+
+ public WorkManager(Workspace workspace) {
+ this.workspace = workspace;
+ this.jobManager = Job.getJobManager();
+ this.lock = jobManager.newLock();
+ }
+
+ /**
+ * Releases the workspace lock without changing the nested operation depth.
+ * Must be followed eventually by endUnprotected. Any
+ * beginUnprotected/endUnprotected pair must be done entirely within the
+ * scope of a checkIn/checkOut pair. Returns the old lock depth.
+ * @see #endUnprotected(int)
+ */
+ public int beginUnprotected() {
+ int depth = lock.getDepth();
+ for (int i = 0; i < depth; i++)
+ lock.release();
+ return depth;
+ }
+
+ /**
+ * An operation calls this method and it only returns when the operation is
+ * free to run.
+ */
+ public void checkIn(ISchedulingRule rule, IProgressMonitor monitor) throws CoreException {
+ boolean success = false;
+ try {
+ if (workspace.isTreeLocked()) {
+ String msg = Messages.resources_cannotModify;
+ throw new ResourceException(IResourceStatus.WORKSPACE_LOCKED, null, msg, null);
+ }
+ jobManager.beginRule(rule, monitor);
+ lock.acquire();
+ incrementPreparedOperations();
+ success = true;
+ } finally {
+ //remember if we failed to check in, so we can avoid check out
+ if (!success)
+ checkInFailed.set(Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Returns true if the check in for this thread failed, in which case the
+ * check out and other end of operation code should not run.
+ * <p>
+ * The failure flag is reset immediately after calling this method. Subsequent
+ * calls to this method will indicate no failure (unless a new failure has occurred).
+ * @return <code>true</code> if the checkIn failed, and <code>false</code> otherwise.
+ */
+ public boolean checkInFailed(ISchedulingRule rule) {
+ if (checkInFailed.get() != null) {
+ //clear the failure flag for this thread
+ checkInFailed.set(null);
+ //must still end the rule even in the case of failure
+ if (!workspace.isTreeLocked())
+ jobManager.endRule(rule);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Inform that an operation has finished.
+ */
+ public synchronized void checkOut(ISchedulingRule rule) {
+ decrementPreparedOperations();
+ rebalanceNestedOperations();
+ //reset state if this is the end of a top level operation
+ if (preparedOperations == 0)
+ operationCanceled = hasBuildChanges = false;
+ try {
+ lock.release();
+ } finally {
+ //end rule in finally in case lock.release throws an exception
+ jobManager.endRule(rule);
+ }
+ }
+
+ /**
+ * This method can only be safely called from inside a workspace
+ * operation. Should NOT be called from outside a
+ * prepareOperation/endOperation block.
+ */
+ private void decrementPreparedOperations() {
+ preparedOperations--;
+ }
+
+ /**
+ * Re-acquires the workspace lock that was temporarily released during an
+ * operation, and restores the old lock depth.
+ * @see #beginUnprotected()
+ */
+ public void endUnprotected(int depth) {
+ for (int i = 0; i < depth; i++)
+ lock.acquire();
+ }
+
+ /**
+ * Returns the work manager's lock
+ */
+ ILock getLock() {
+ return lock;
+ }
+
+ /**
+ * Returns the scheduling rule used during resource change notifications.
+ */
+ public ISchedulingRule getNotifyRule() {
+ return notifyRule;
+ }
+
+ /**
+ * This method can only be safely called from inside a workspace
+ * operation. Should NOT be called from outside a
+ * prepareOperation/endOperation block.
+ */
+ public synchronized int getPreparedOperationDepth() {
+ return preparedOperations;
+ }
+
+ /**
+ * This method can only be safely called from inside a workspace
+ * operation. Should NOT be called from outside a
+ * prepareOperation/endOperation block.
+ */
+ void incrementNestedOperations() {
+ nestedOperations++;
+ }
+
+ /**
+ * This method can only be safely called from inside a workspace
+ * operation. Should NOT be called from outside a
+ * prepareOperation/endOperation block.
+ */
+ private void incrementPreparedOperations() {
+ preparedOperations++;
+ }
+
+ /**
+ * Returns true if the nested operation depth is the same as the prepared
+ * operation depth, and false otherwise. This method can only be safely
+ * called from inside a workspace operation. Should NOT be called from
+ * outside a prepareOperation/endOperation block.
+ */
+ boolean isBalanced() {
+ return nestedOperations == preparedOperations;
+ }
+
+ /**
+ * Returns true if the workspace lock has already been acquired by this
+ * thread, and false otherwise.
+ */
+ public boolean isLockAlreadyAcquired() {
+ boolean result = false;
+ try {
+ boolean success = lock.acquire(0L);
+ if (success) {
+ //if lock depth is greater than one, then we already owned it
+ // before
+ result = lock.getDepth() > 1;
+ lock.release();
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ return result;
+ }
+
+ /**
+ * This method can only be safely called from inside a workspace
+ * operation. Should NOT be called from outside a
+ * prepareOperation/endOperation block.
+ */
+ public void operationCanceled() {
+ operationCanceled = true;
+ }
+
+ /**
+ * Used to make things stable again after an operation has failed between a
+ * workspace.prepareOperation() and workspace.beginOperation(). This method
+ * can only be safely called from inside a workspace operation. Should NOT
+ * be called from outside a prepareOperation/endOperation block.
+ */
+ public void rebalanceNestedOperations() {
+ nestedOperations = preparedOperations;
+ }
+
+ /**
+ * Indicates if the operation that has just completed may potentially
+ * require a build.
+ */
+ public void setBuild(boolean hasChanges) {
+ hasBuildChanges = hasBuildChanges || hasChanges;
+ }
+
+ /**
+ * This method can only be safely called from inside a workspace operation.
+ * Should NOT be called from outside a prepareOperation/endOperation block.
+ */
+ public boolean shouldBuild() {
+ if (hasBuildChanges) {
+ if (operationCanceled)
+ return Policy.buildOnCancel;
+ return true;
+ }
+ return false;
+ }
+
+ public void shutdown(IProgressMonitor monitor) {
+ // do nothing
+ }
+
+ public void startup(IProgressMonitor monitor) {
+ jobManager.beginRule(workspace.getRoot(), monitor);
+ lock.acquire();
+ }
+
+ /**
+ * This method should be called at the end of the workspace startup, even if the startup failed.
+ * It must be preceded by a call to <code>startup</code>. It releases the primary workspace lock
+ * and ends applying the workspace rule to this thread.
+ */
+ void postWorkspaceStartup() {
+ try {
+ lock.release();
+ } finally {
+ //end rule in finally in case lock.release throws an exception
+ jobManager.endRule(workspace.getRoot());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,2127 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Incorporated - loadProjectDescription(InputStream)
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.internal.events.*;
+import org.eclipse.core.internal.localstore.FileSystemResourceManager;
+import org.eclipse.core.internal.properties.IPropertyManager;
+import org.eclipse.core.internal.refresh.RefreshManager;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+import org.xml.sax.InputSource;
+
+/**
+ * The workspace class is the monolithic nerve center of the resources plugin.
+ * All interesting functionality stems from this class.
+ * </p>
+ * <p>
+ * The lifecycle of the resources plugin is encapsulated by the {@link #open(IProgressMonitor)}
+ * and {@link #close(IProgressMonitor)} methods. A closed workspace is completely
+ * unusable - any attempt to access or modify interesting workspace state on a closed
+ * workspace will fail.
+ * </p>
+ * <p>
+ * All modifications to the workspace occur within the context of a workspace operation.
+ * A workspace operation is implemented using the following sequence:
+ * <pre>
+ * try {
+ * prepareOperation(...);
+ * //check preconditions
+ * beginOperation(...);
+ * //perform changes
+ * } finally {
+ * endOperation(...);
+ * }
+ * </pre>
+ * Workspace operations can be nested arbitrarily. A "top level" workspace operation
+ * is an operation that is not nested within another workspace operation in the current
+ * thread.
+ * See the javadoc of {@link #prepareOperation(ISchedulingRule, IProgressMonitor)},
+ * {@link #beginOperation(boolean)}, and {@link #endOperation(ISchedulingRule, boolean, IProgressMonitor)}
+ * for more details.
+ * </p>
+ * <p>
+ * Major areas of functionality are farmed off to various manager classes. Open a
+ * type hierarchy on {@link IManager} to see all the different managers. Each
+ * manager is typically referenced three times in this class: Once in {@link #startup(IProgressMonitor)}
+ * when it is instantiated, once in {@link #shutdown(IProgressMonitor)} when it
+ * is destroyed, and once in a manager accessor method.
+ * </p>
+ */
+public class Workspace extends PlatformObject implements IWorkspace, ICoreConstants {
+ public static final boolean caseSensitive = Platform.OS_MACOSX.equals(Platform.getOS()) ? false : new java.io.File("a").compareTo(new java.io.File("A")) != 0; //$NON-NLS-1$ //$NON-NLS-2$
+ // whether the resources plugin is in debug mode.
+ public static boolean DEBUG = false;
+
+ /**
+ * Work manager should never be accessed directly because accessor
+ * asserts that workspace is still open.
+ */
+ protected WorkManager _workManager;
+ protected AliasManager aliasManager;
+ protected BuildManager buildManager;
+ protected IProject[] buildOrder = null;
+ protected CharsetManager charsetManager;
+ protected ContentDescriptionManager contentDescriptionManager;
+ /** indicates if the workspace crashed in a previous session */
+ protected boolean crashed = false;
+ protected final IWorkspaceRoot defaultRoot = new WorkspaceRoot(Path.ROOT, this);
+ protected WorkspacePreferences description;
+ protected FileSystemResourceManager fileSystemManager;
+ protected final HashSet lifecycleListeners = new HashSet(10);
+ protected LocalMetaArea localMetaArea;
+ /**
+ * Helper class for performing validation of resource names and locations.
+ */
+ protected final LocationValidator locationValidator = new LocationValidator(this);
+ protected MarkerManager markerManager;
+ /**
+ * The currently installed Move/Delete hook.
+ */
+ protected IMoveDeleteHook moveDeleteHook = null;
+ protected NatureManager natureManager;
+ protected long nextMarkerId = 0;
+ protected long nextNodeId = 1;
+
+ protected NotificationManager notificationManager;
+ protected boolean openFlag = false;
+ protected ElementTree operationTree; // tree at the start of the current operation
+ protected PathVariableManager pathVariableManager;
+ protected IPropertyManager propertyManager;
+
+ protected RefreshManager refreshManager;
+
+ /**
+ * Scheduling rule factory. This field is null if the factory has not been used
+ * yet. The accessor method should be used rather than accessing this field
+ * directly.
+ */
+ private IResourceRuleFactory ruleFactory;
+
+ protected SaveManager saveManager;
+ /**
+ * File modification validation. If it is true and validator is null, we try/initialize
+ * validator first time through. If false, there is no validator.
+ */
+ protected boolean shouldValidate = true;
+
+ /**
+ * Job that performs periodic string pool canonicalization.
+ */
+ private StringPoolJob stringPoolJob;
+
+ /**
+ * The synchronizer
+ */
+ protected Synchronizer synchronizer;
+
+ /**
+ * The currently installed team hook.
+ */
+ protected TeamHook teamHook = null;
+
+ /**
+ * The workspace tree. The tree is an in-memory representation
+ * of the resources that make up the workspace. The tree caches
+ * the structure and state of files and directories on disk (their existence
+ * and last modified times). When external parties make changes to
+ * the files on disk, this representation becomes out of sync. A local refresh
+ * reconciles the state of the files on disk with this tree (@link {@link IResource#refreshLocal(int, IProgressMonitor)}).
+ * The tree is also used to store metadata associated with resources in
+ * the workspace (markers, properties, etc).
+ *
+ * While the ElementTree data structure can handle both concurrent
+ * reads and concurrent writes, write access to the tree is governed
+ * by {@link WorkManager}.
+ */
+ protected ElementTree tree;
+
+ /**
+ * This field is used to control access to the workspace tree during
+ * resource change notifications. It tracks which thread, if any, is
+ * in the middle of a resource change notification. This is used to cause
+ * attempts to modify the workspace during notifications to fail.
+ */
+ protected Thread treeLocked = null;
+
+ /**
+ * The currently installed file modification validator.
+ */
+ protected IFileModificationValidator validator = null;
+
+ /**
+ * Deletes all the files and directories from the given root down (inclusive).
+ * Returns false if we could not delete some file or an exception occurred
+ * at any point in the deletion.
+ * Even if an exception occurs, a best effort is made to continue deleting.
+ */
+ public static boolean clear(java.io.File root) {
+ boolean result = clearChildren(root);
+ try {
+ if (root.exists())
+ result &= root.delete();
+ } catch (Exception e) {
+ result = false;
+ }
+ return result;
+ }
+
+ /**
+ * Deletes all the files and directories from the given root down, except for
+ * the root itself.
+ * Returns false if we could not delete some file or an exception occurred
+ * at any point in the deletion.
+ * Even if an exception occurs, a best effort is made to continue deleting.
+ */
+ public static boolean clearChildren(java.io.File root) {
+ boolean result = true;
+ if (root.isDirectory()) {
+ String[] list = root.list();
+ // for some unknown reason, list() can return null.
+ // Just skip the children If it does.
+ if (list != null)
+ for (int i = 0; i < list.length; i++)
+ result &= clear(new java.io.File(root, list[i]));
+ }
+ return result;
+ }
+
+ public static WorkspaceDescription defaultWorkspaceDescription() {
+ return new WorkspaceDescription("Workspace"); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns true if the object at the specified position has any
+ * other copy in the given array.
+ */
+ private static boolean isDuplicate(Object[] array, int position) {
+ if (array == null || position >= array.length)
+ return false;
+ for (int j = position - 1; j >= 0; j--)
+ if (array[j].equals(array[position]))
+ return true;
+ return false;
+ }
+
+ public Workspace() {
+ super();
+ localMetaArea = new LocalMetaArea();
+ tree = new ElementTree();
+ /* tree should only be modified during operations */
+ tree.immutable();
+ treeLocked = Thread.currentThread();
+ tree.setTreeData(newElement(IResource.ROOT));
+ }
+
+ /**
+ * Indicates that a build is about to occur. Broadcasts the necessary
+ * deltas before the build starts. Note that this will cause POST_BUILD
+ * to be automatically done at the end of the operation in which
+ * the build occurs.
+ */
+ protected void aboutToBuild(Object source, int trigger) {
+ //fire a POST_CHANGE first to ensure everyone is up to date before firing PRE_BUILD
+ broadcastPostChange();
+ broadcastBuildEvent(source, IResourceChangeEvent.PRE_BUILD, trigger);
+ }
+
+ /**
+ * Adds a listener for internal workspace lifecycle events. There is no way to
+ * remove lifecycle listeners.
+ */
+ public void addLifecycleListener(ILifecycleListener listener) {
+ lifecycleListeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#addResourceChangeListener(IResourceChangeListener)
+ */
+ public void addResourceChangeListener(IResourceChangeListener listener) {
+ notificationManager.addListener(listener, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#addResourceChangeListener(IResourceChangeListener, int)
+ */
+ public void addResourceChangeListener(IResourceChangeListener listener, int eventMask) {
+ notificationManager.addListener(listener, eventMask);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#addSaveParticipant(Plugin, ISaveParticipant)
+ */
+ public ISavedState addSaveParticipant(Plugin plugin, ISaveParticipant participant) throws CoreException {
+ Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$
+ Assert.isNotNull(participant, "Participant must not be null"); //$NON-NLS-1$
+ return saveManager.addParticipant(plugin, participant);
+ }
+
+ public void beginOperation(boolean createNewTree) throws CoreException {
+ WorkManager workManager = getWorkManager();
+ workManager.incrementNestedOperations();
+ if (!workManager.isBalanced())
+ Assert.isTrue(false, "Operation was not prepared."); //$NON-NLS-1$
+ if (workManager.getPreparedOperationDepth() > 1) {
+ if (createNewTree && tree.isImmutable())
+ newWorkingTree();
+ return;
+ }
+ // stash the current tree as the basis for this operation.
+ operationTree = tree;
+ if (createNewTree && tree.isImmutable())
+ newWorkingTree();
+ }
+
+ public void broadcastBuildEvent(Object source, int type, int buildTrigger) {
+ ResourceChangeEvent event = new ResourceChangeEvent(source, type, buildTrigger, null);
+ notificationManager.broadcastChanges(tree, event, false);
+ }
+
+ /**
+ * Broadcasts an internal workspace lifecycle event to interested
+ * internal listeners.
+ */
+ protected void broadcastEvent(LifecycleEvent event) throws CoreException {
+ for (Iterator it = lifecycleListeners.iterator(); it.hasNext();) {
+ ILifecycleListener listener = (ILifecycleListener) it.next();
+ listener.handleEvent(event);
+ }
+ }
+
+ public void broadcastPostChange() {
+ ResourceChangeEvent event = new ResourceChangeEvent(this, IResourceChangeEvent.POST_CHANGE, 0, null);
+ notificationManager.broadcastChanges(tree, event, true);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#build(int, IProgressMonitor)
+ */
+ public void build(int trigger, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ final ISchedulingRule rule = getRuleFactory().buildRule();
+ try {
+ monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
+ try {
+ prepareOperation(rule, monitor);
+ beginOperation(true);
+ aboutToBuild(this, trigger);
+ IStatus result;
+ try {
+ result = getBuildManager().build(trigger, Policy.subMonitorFor(monitor, Policy.opWork));
+ } finally {
+ //must fire POST_BUILD if PRE_BUILD has occurred
+ broadcastBuildEvent(this, IResourceChangeEvent.POST_BUILD, trigger);
+ }
+ if (!result.isOK())
+ throw new ResourceException(result);
+ } finally {
+ //building may close the tree, but we are still inside an operation so open it
+ if (tree.isImmutable())
+ newWorkingTree();
+ endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Returns whether creating executable extensions is acceptable
+ * at this point in time. In particular, returns <code>false</code>
+ * when the system bundle is shutting down, which only occurs
+ * when the entire framework is exiting.
+ */
+ private boolean canCreateExtensions() {
+ return Platform.getBundle("org.eclipse.osgi").getState() != Bundle.STOPPING; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#checkpoint(boolean)
+ */
+ public void checkpoint(boolean build) {
+ try {
+ final ISchedulingRule rule = getWorkManager().getNotifyRule();
+ try {
+ prepareOperation(rule, null);
+ beginOperation(true);
+ broadcastPostChange();
+ } finally {
+ endOperation(rule, build, null);
+ }
+ } catch (CoreException e) {
+ Policy.log(e.getStatus());
+ }
+ }
+
+ /**
+ * Closes this workspace; ignored if this workspace is not open.
+ * The state of this workspace is not saved before the workspace
+ * is shut down.
+ * <p>
+ * If the workspace was saved immediately prior to closing,
+ * it will have the same set of projects
+ * (open or closed) when reopened for a subsequent session.
+ * Otherwise, closing a workspace may lose some or all of the
+ * changes made since the last save or snapshot.
+ * </p>
+ * <p>
+ * Note that session properties are discarded when a workspace is closed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ * @exception CoreException if the workspace could not be shutdown.
+ */
+ public void close(IProgressMonitor monitor) throws CoreException {
+ //nothing to do if the workspace failed to open
+ if (!isOpen())
+ return;
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String msg = Messages.resources_closing_0;
+ int rootCount = tree.getChildCount(Path.ROOT);
+ monitor.beginTask(msg, rootCount + 2);
+ monitor.subTask(msg);
+ //this operation will never end because the world is going away
+ try {
+ stringPoolJob.cancel();
+ //shutdown save manager now so a last snapshot can be taken before we close
+ //note: you can't call #save() from within a nested operation
+ saveManager.shutdown(null);
+ prepareOperation(getRoot(), monitor);
+ //shutdown notification first to avoid calling third parties during shutdown
+ notificationManager.shutdown(null);
+ beginOperation(true);
+ IProject[] projects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++) {
+ //notify managers of closing so they can cleanup
+ broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, projects[i]));
+ monitor.worked(1);
+ }
+ //empty the workspace tree so we leave in a clean state
+ deleteResource(getRoot());
+ openFlag = false;
+ // endOperation not needed here
+ } finally {
+ // Shutdown needs to be executed regardless of failures
+ shutdown(Policy.subMonitorFor(monitor, 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
+ }
+ } finally {
+ //release the scheduling rule to be a good job citizen
+ Job.getJobManager().endRule(getRoot());
+ monitor.done();
+ }
+ }
+
+ /**
+ * Computes the global total ordering of all open projects in the
+ * workspace based on project references. If an existing and open project P
+ * references another existing and open project Q also included in the list,
+ * then Q should come before P in the resulting ordering. Closed and non-
+ * existent projects are ignored, and will not appear in the result. References
+ * to non-existent or closed projects are also ignored, as are any self-
+ * references.
+ * <p>
+ * When there are choices, the choice is made in a reasonably stable way. For
+ * example, given an arbitrary choice between two projects, the one with the
+ * lower collating project name is usually selected.
+ * </p>
+ * <p>
+ * When the project reference graph contains cyclic references, it is
+ * impossible to honor all of the relationships. In this case, the result
+ * ignores as few relationships as possible. For example, if P2 references P1,
+ * P4 references P3, and P2 and P3 reference each other, then exactly one of the
+ * relationships between P2 and P3 will have to be ignored. The outcome will be
+ * either [P1, P2, P3, P4] or [P1, P3, P2, P4]. The result also contains
+ * complete details of any cycles present.
+ * </p>
+ *
+ * @return result describing the global project order
+ * @since 2.1
+ */
+ private ProjectOrder computeFullProjectOrder() {
+
+ // determine the full set of accessible projects in the workspace
+ // order the set in descending alphabetical order of project name
+ SortedSet allAccessibleProjects = new TreeSet(new Comparator() {
+ public int compare(Object x, Object y) {
+ IProject px = (IProject) x;
+ IProject py = (IProject) y;
+ return py.getName().compareTo(px.getName());
+ }
+ });
+ IProject[] allProjects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ // List<IProject[]> edges
+ List edges = new ArrayList(allProjects.length);
+ for (int i = 0; i < allProjects.length; i++) {
+ Project project = (Project) allProjects[i];
+ // ignore projects that are not accessible
+ if (!project.isAccessible())
+ continue;
+ ProjectDescription desc = project.internalGetDescription();
+ if (desc == null)
+ continue;
+ //obtain both static and dynamic project references
+ IProject[] refs = desc.getAllReferences(false);
+ allAccessibleProjects.add(project);
+ for (int j = 0; j < refs.length; j++) {
+ IProject ref = refs[j];
+ // ignore self references and references to projects that are not accessible
+ if (ref.isAccessible() && !ref.equals(project))
+ edges.add(new IProject[] {project, ref});
+ }
+ }
+
+ ProjectOrder fullProjectOrder = ComputeProjectOrder.computeProjectOrder(allAccessibleProjects, edges);
+ return fullProjectOrder;
+ }
+
+ /**
+ * Implementation of API method declared on IWorkspace.
+ *
+ * @see IWorkspace#computePrerequisiteOrder(IProject[])
+ * @deprecated Replaced by <code>IWorkspace.computeProjectOrder</code>, which
+ * produces a more usable result when there are cycles in project reference
+ * graph.
+ */
+ public IProject[][] computePrerequisiteOrder(IProject[] targets) {
+ return computePrerequisiteOrder1(targets);
+ }
+
+ /*
+ * Compatible reimplementation of
+ * <code>IWorkspace.computePrerequisiteOrder</code> using
+ * <code>IWorkspace.computeProjectOrder</code>.
+ *
+ * @since 2.1
+ */
+ private IProject[][] computePrerequisiteOrder1(IProject[] projects) {
+ IWorkspace.ProjectOrder r = computeProjectOrder(projects);
+ if (!r.hasCycles) {
+ return new IProject[][] {r.projects, new IProject[0]};
+ }
+ // when there are cycles, we need to remove all knotted projects from
+ // r.projects to form result[0] and merge all knots to form result[1]
+ // Set<IProject> bad
+ Set bad = new HashSet();
+ // Set<IProject> bad
+ Set keepers = new HashSet(Arrays.asList(r.projects));
+ for (int i = 0; i < r.knots.length; i++) {
+ IProject[] knot = r.knots[i];
+ for (int j = 0; j < knot.length; j++) {
+ IProject project = knot[j];
+ // keep only selected projects in knot
+ if (keepers.contains(project)) {
+ bad.add(project);
+ }
+ }
+ }
+ IProject[] result2 = new IProject[bad.size()];
+ bad.toArray(result2);
+ // List<IProject> p
+ List p = new LinkedList();
+ p.addAll(Arrays.asList(r.projects));
+ for (Iterator it = p.listIterator(); it.hasNext();) {
+ IProject project = (IProject) it.next();
+ if (bad.contains(project)) {
+ // remove knotted projects from the main answer
+ it.remove();
+ }
+ }
+ IProject[] result1 = new IProject[p.size()];
+ p.toArray(result1);
+ return new IProject[][] {result1, result2};
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#computeProjectOrder(IProject[])
+ * @since 2.1
+ */
+ public ProjectOrder computeProjectOrder(IProject[] projects) {
+
+ // compute the full project order for all accessible projects
+ ProjectOrder fullProjectOrder = computeFullProjectOrder();
+
+ // "fullProjectOrder.projects" contains no inaccessible projects
+ // but might contain accessible projects omitted from "projects"
+ // optimize common case where "projects" includes everything
+ int accessibleCount = 0;
+ for (int i = 0; i < projects.length; i++) {
+ if (projects[i].isAccessible()) {
+ accessibleCount++;
+ }
+ }
+ // no filtering required if the subset accounts for the full list
+ if (accessibleCount == fullProjectOrder.projects.length) {
+ return fullProjectOrder;
+ }
+
+ // otherwise we need to eliminate mention of other projects...
+ // ... from "fullProjectOrder.projects"...
+ // Set<IProject> keepers
+ Set keepers = new HashSet(Arrays.asList(projects));
+ // List<IProject> projects
+ List reducedProjects = new ArrayList(fullProjectOrder.projects.length);
+ for (int i = 0; i < fullProjectOrder.projects.length; i++) {
+ IProject project = fullProjectOrder.projects[i];
+ if (keepers.contains(project)) {
+ // remove projects not in the initial subset
+ reducedProjects.add(project);
+ }
+ }
+ IProject[] p1 = new IProject[reducedProjects.size()];
+ reducedProjects.toArray(p1);
+
+ // ... and from "fullProjectOrder.knots"
+ // List<IProject[]> knots
+ List reducedKnots = new ArrayList(fullProjectOrder.knots.length);
+ for (int i = 0; i < fullProjectOrder.knots.length; i++) {
+ IProject[] knot = fullProjectOrder.knots[i];
+ List x = new ArrayList(knot.length);
+ for (int j = 0; j < knot.length; j++) {
+ IProject project = knot[j];
+ if (keepers.contains(project)) {
+ x.add(project);
+ }
+ }
+ // keep knots containing 2 or more projects in the specified subset
+ if (x.size() > 1) {
+ reducedKnots.add(x.toArray(new IProject[x.size()]));
+ }
+ }
+ IProject[][] k1 = new IProject[reducedKnots.size()][];
+ // okay to use toArray here because reducedKnots elements are IProject[]
+ reducedKnots.toArray(k1);
+ return new ProjectOrder(p1, (k1.length > 0), k1);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#copy(IResource[], IPath, boolean, IProgressMonitor)
+ */
+ public IStatus copy(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ return copy(resources, destination, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#copy(IResource[], IPath, int, IProgressMonitor)
+ */
+ public IStatus copy(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ Assert.isLegal(resources != null);
+ int opWork = Math.max(resources.length, 1);
+ int totalWork = Policy.totalWork * opWork / Policy.opWork;
+ String message = Messages.resources_copying_0;
+ monitor.beginTask(message, totalWork);
+ if (resources.length == 0)
+ return Status.OK_STATUS;
+ // to avoid concurrent changes to this array
+ resources = (IResource[]) resources.clone();
+ IPath parentPath = null;
+ message = Messages.resources_copyProblem;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
+ try {
+ prepareOperation(getRoot(), monitor);
+ beginOperation(true);
+ for (int i = 0; i < resources.length; i++) {
+ Policy.checkCanceled(monitor);
+ IResource resource = resources[i];
+ if (resource == null || isDuplicate(resources, i)) {
+ monitor.worked(1);
+ continue;
+ }
+ // test siblings
+ if (parentPath == null)
+ parentPath = resource.getFullPath().removeLastSegments(1);
+ if (parentPath.equals(resource.getFullPath().removeLastSegments(1))) {
+ // test copy requirements
+ try {
+ IPath destinationPath = destination.append(resource.getName());
+ IStatus requirements = ((Resource) resource).checkCopyRequirements(destinationPath, resource.getType(), updateFlags);
+ if (requirements.isOK()) {
+ try {
+ resource.copy(destinationPath, updateFlags, Policy.subMonitorFor(monitor, 1));
+ } catch (CoreException e) {
+ status.merge(e.getStatus());
+ }
+ } else {
+ monitor.worked(1);
+ status.merge(requirements);
+ }
+ } catch (CoreException e) {
+ monitor.worked(1);
+ status.merge(e.getStatus());
+ }
+ } else {
+ monitor.worked(1);
+ message = NLS.bind(Messages.resources_notChild, resources[i].getFullPath(), parentPath);
+ status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resources[i].getFullPath(), message));
+ }
+ }
+ } catch (OperationCanceledException e) {
+ getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
+ }
+ if (status.matches(IStatus.ERROR))
+ throw new ResourceException(status);
+ return status.isOK() ? Status.OK_STATUS : (IStatus) status;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected void copyTree(IResource source, IPath destination, int depth, int updateFlags, boolean keepSyncInfo) throws CoreException {
+ // retrieve the resource at the destination if there is one (phantoms included).
+ // if there isn't one, then create a new handle based on the type that we are
+ // trying to copy
+ IResource destinationResource = getRoot().findMember(destination, true);
+ int destinationType;
+ if (destinationResource == null) {
+ if (source.getType() == IResource.FILE)
+ destinationType = IResource.FILE;
+ else if (destination.segmentCount() == 1)
+ destinationType = IResource.PROJECT;
+ else
+ destinationType = IResource.FOLDER;
+ destinationResource = newResource(destination, destinationType);
+ } else
+ destinationType = destinationResource.getType();
+
+ // create the resource at the destination
+ ResourceInfo sourceInfo = ((Resource) source).getResourceInfo(true, false);
+ if (destinationType != source.getType()) {
+ sourceInfo = (ResourceInfo) sourceInfo.clone();
+ sourceInfo.setType(destinationType);
+ }
+ ResourceInfo newInfo = createResource(destinationResource, sourceInfo, false, false, keepSyncInfo);
+ // get/set the node id from the source's resource info so we can later put it in the
+ // info for the destination resource. This will help us generate the proper deltas,
+ // indicating a move rather than a add/delete
+ newInfo.setNodeId(sourceInfo.getNodeId());
+
+ // preserve local sync info but not location info
+ newInfo.setFlags(newInfo.getFlags() | (sourceInfo.getFlags() & M_LOCAL_EXISTS));
+ newInfo.setFileStoreRoot(null);
+
+ // forget content-related caching flags
+ newInfo.clear(M_CONTENT_CACHE);
+
+ // update link locations in project descriptions
+ if (source.isLinked()) {
+ LinkDescription linkDescription;
+ if ((updateFlags & IResource.SHALLOW) != 0) {
+ //for shallow move the destination is a linked resource with the same location
+ newInfo.set(ICoreConstants.M_LINK);
+ linkDescription = new LinkDescription(destinationResource, source.getLocationURI());
+ } else {
+ //for deep move the destination is not a linked resource
+ newInfo.clear(ICoreConstants.M_LINK);
+ linkDescription = null;
+ }
+ Project project = (Project) destinationResource.getProject();
+ project.internalGetDescription().setLinkLocation(destinationResource.getProjectRelativePath(), linkDescription);
+ project.writeDescription(updateFlags);
+ }
+
+ // do the recursion. if we have a file then it has no members so return. otherwise
+ // recursively call this method on the container's members if the depth tells us to
+ if (depth == IResource.DEPTH_ZERO || source.getType() == IResource.FILE)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ //copy .project file first if project is being copied, otherwise links won't be able to update description
+ boolean projectCopy = source.getType() == IResource.PROJECT && destinationType == IResource.PROJECT;
+ if (projectCopy) {
+ IResource dotProject = ((Project) source).findMember(IProjectDescription.DESCRIPTION_FILE_NAME);
+ if (dotProject != null)
+ copyTree(dotProject, destination.append(dotProject.getName()), depth, updateFlags, keepSyncInfo);
+ }
+ IResource[] children = ((IContainer) source).members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+ for (int i = 0, imax = children.length; i < imax; i++) {
+ String childName = children[i].getName();
+ if (!projectCopy || !childName.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
+ IPath childPath = destination.append(childName);
+ copyTree(children[i], childPath, depth, updateFlags, keepSyncInfo);
+ }
+ }
+ }
+
+ /**
+ * Returns the number of resources in a subtree of the resource tree.
+ *
+ * @param root The subtree to count resources for
+ * @param depth The depth of the subtree to count
+ * @param phantom If true, phantoms are included, otherwise they are ignored.
+ */
+ public int countResources(IPath root, int depth, final boolean phantom) {
+ if (!tree.includes(root))
+ return 0;
+ switch (depth) {
+ case IResource.DEPTH_ZERO :
+ return 1;
+ case IResource.DEPTH_ONE :
+ return 1 + tree.getChildCount(root);
+ case IResource.DEPTH_INFINITE :
+ final int[] count = new int[1];
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree aTree, IPathRequestor requestor, Object elementContents) {
+ if (phantom || !((ResourceInfo) elementContents).isSet(M_PHANTOM))
+ count[0]++;
+ return true;
+ }
+ };
+ new ElementTreeIterator(tree, root).iterate(visitor);
+ return count[0];
+ }
+ return 0;
+ }
+
+ /*
+ * Creates the given resource in the tree and returns the new resource info object.
+ * If phantom is true, the created element is marked as a phantom.
+ * If there is already be an element in the tree for the given resource
+ * in the given state (i.e., phantom), a CoreException is thrown.
+ * If there is already a phantom in the tree and the phantom flag is false,
+ * the element is overwritten with the new element. (but the synchronization
+ * information is preserved)
+ */
+ public ResourceInfo createResource(IResource resource, boolean phantom) throws CoreException {
+ return createResource(resource, null, phantom, false, false);
+ }
+
+ /**
+ * Creates a resource, honoring update flags requesting that the resource
+ * be immediately made derived, hidden and/or team private
+ */
+ public ResourceInfo createResource(IResource resource, int updateFlags) throws CoreException {
+ ResourceInfo info = createResource(resource, null, false, false, false);
+ if ((updateFlags & IResource.DERIVED) != 0)
+ info.set(M_DERIVED);
+ if ((updateFlags & IResource.TEAM_PRIVATE) != 0)
+ info.set(M_TEAM_PRIVATE_MEMBER);
+ if ((updateFlags & IResource.HIDDEN) != 0)
+ info.set(M_HIDDEN);
+ return info;
+ }
+
+ /*
+ * Creates the given resource in the tree and returns the new resource info object.
+ * If phantom is true, the created element is marked as a phantom.
+ * If there is already be an element in the tree for the given resource
+ * in the given state (i.e., phantom), a CoreException is thrown.
+ * If there is already a phantom in the tree and the phantom flag is false,
+ * the element is overwritten with the new element. (but the synchronization
+ * information is preserved) If the specified resource info is null, then create
+ * a new one.
+ *
+ * If keepSyncInfo is set to be true, the sync info in the given ResourceInfo is NOT
+ * cleared before being created and thus any sync info already existing at that namespace
+ * (as indicated by an already existing phantom resource) will be lost.
+ */
+ public ResourceInfo createResource(IResource resource, ResourceInfo info, boolean phantom, boolean overwrite, boolean keepSyncInfo) throws CoreException {
+ info = info == null ? newElement(resource.getType()) : (ResourceInfo) info.clone();
+ ResourceInfo original = getResourceInfo(resource.getFullPath(), true, false);
+ if (phantom) {
+ info.set(M_PHANTOM);
+ info.clearModificationStamp();
+ }
+ // if nothing existed at the destination then just create the resource in the tree
+ if (original == null) {
+ // we got here from a copy/move. we don't want to copy over any sync info
+ // from the source so clear it.
+ if (!keepSyncInfo)
+ info.setSyncInfo(null);
+ tree.createElement(resource.getFullPath(), info);
+ } else {
+ // if overwrite==true then slam the new info into the tree even if one existed before
+ if (overwrite || (!phantom && original.isSet(M_PHANTOM))) {
+ // copy over the sync info and flags from the old resource info
+ // since we are replacing a phantom with a real resource
+ // DO NOT set the sync info dirty flag because we want to
+ // preserve the old sync info so its not dirty
+ // XXX: must copy over the generic sync info from the old info to the new
+ // XXX: do we really need to clone the sync info here?
+ if (!keepSyncInfo)
+ info.setSyncInfo(original.getSyncInfo(true));
+ // mark the markers bit as dirty so we snapshot an empty marker set for
+ // the new resource
+ info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+ tree.setElementData(resource.getFullPath(), info);
+ } else {
+ String message = NLS.bind(Messages.resources_mustNotExist, resource.getFullPath());
+ throw new ResourceException(IResourceStatus.RESOURCE_EXISTS, resource.getFullPath(), message, null);
+ }
+ }
+ return info;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#delete(IResource[], boolean, IProgressMonitor)
+ */
+ public IStatus delete(IResource[] resources, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= IResource.KEEP_HISTORY;
+ return delete(resources, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#delete(IResource[], int, IProgressMonitor)
+ */
+ public IStatus delete(IResource[] resources, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ int opWork = Math.max(resources.length, 1);
+ int totalWork = Policy.totalWork * opWork / Policy.opWork;
+ String message = Messages.resources_deleting_0;
+ monitor.beginTask(message, totalWork);
+ message = Messages.resources_deleteProblem;
+ MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
+ if (resources.length == 0)
+ return result;
+ resources = (IResource[]) resources.clone(); // to avoid concurrent changes to this array
+ try {
+ prepareOperation(getRoot(), monitor);
+ beginOperation(true);
+ for (int i = 0; i < resources.length; i++) {
+ Policy.checkCanceled(monitor);
+ Resource resource = (Resource) resources[i];
+ if (resource == null) {
+ monitor.worked(1);
+ continue;
+ }
+ try {
+ resource.delete(updateFlags, Policy.subMonitorFor(monitor, 1));
+ } catch (CoreException e) {
+ // Don't really care about the exception unless the resource is still around.
+ ResourceInfo info = resource.getResourceInfo(false, false);
+ if (resource.exists(resource.getFlags(info), false)) {
+ message = NLS.bind(Messages.resources_couldnotDelete, resource.getFullPath());
+ result.merge(new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, resource.getFullPath(), message));
+ result.merge(e.getStatus());
+ }
+ }
+ }
+ if (result.matches(IStatus.ERROR))
+ throw new ResourceException(result);
+ return result;
+ } catch (OperationCanceledException e) {
+ getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#deleteMarkers(IMarker[])
+ */
+ public void deleteMarkers(IMarker[] markers) throws CoreException {
+ Assert.isNotNull(markers);
+ if (markers.length == 0)
+ return;
+ // clone to avoid outside changes
+ markers = (IMarker[]) markers.clone();
+ try {
+ prepareOperation(null, null);
+ beginOperation(true);
+ for (int i = 0; i < markers.length; ++i)
+ if (markers[i] != null && markers[i].getResource() != null)
+ markerManager.removeMarker(markers[i].getResource(), markers[i].getId());
+ } finally {
+ endOperation(null, false, null);
+ }
+ }
+
+ /**
+ * Delete the given resource from the current tree of the receiver.
+ * This method simply removes the resource from the tree. No cleanup or
+ * other management is done. Use IResource.delete for proper deletion.
+ * If the given resource is the root, all of its children (i.e., all projects) are
+ * deleted but the root is left.
+ */
+ void deleteResource(IResource resource) {
+ IPath path = resource.getFullPath();
+ if (path.equals(Path.ROOT)) {
+ IProject[] children = getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < children.length; i++)
+ tree.deleteElement(children[i].getFullPath());
+ } else
+ tree.deleteElement(path);
+ }
+
+ /**
+ * End an operation (group of resource changes).
+ * Notify interested parties that resource changes have taken place. All
+ * registered resource change listeners are notified. If autobuilding is
+ * enabled, a build is run.
+ */
+ public void endOperation(ISchedulingRule rule, boolean build, IProgressMonitor monitor) throws CoreException {
+ WorkManager workManager = getWorkManager();
+ //don't do any end operation work if we failed to check in
+ if (workManager.checkInFailed(rule))
+ return;
+ // This is done in a try finally to ensure that we always decrement the operation count
+ // and release the workspace lock. This must be done at the end because snapshot
+ // and "hasChanges" comparison have to happen without interference from other threads.
+ boolean hasTreeChanges = false;
+ boolean depthOne = false;
+ try {
+ workManager.setBuild(build);
+ // if we are not exiting a top level operation then just decrement the count and return
+ depthOne = workManager.getPreparedOperationDepth() == 1;
+ if (!(notificationManager.shouldNotify() || depthOne)) {
+ notificationManager.requestNotify();
+ return;
+ }
+ // do the following in a try/finally to ensure that the operation tree is nulled at the end
+ // as we are completing a top level operation.
+ try {
+ notificationManager.beginNotify();
+ // check for a programming error on using beginOperation/endOperation
+ Assert.isTrue(workManager.getPreparedOperationDepth() > 0, "Mismatched begin/endOperation"); //$NON-NLS-1$
+
+ // At this time we need to re-balance the nested operations. It is necessary because
+ // build() and snapshot() should not fail if they are called.
+ workManager.rebalanceNestedOperations();
+
+ //find out if any operation has potentially modified the tree
+ hasTreeChanges = workManager.shouldBuild();
+ //double check if the tree has actually changed
+ if (hasTreeChanges)
+ hasTreeChanges = operationTree != null && ElementTree.hasChanges(tree, operationTree, ResourceComparator.getBuildComparator(), true);
+ broadcastPostChange();
+ // Request a snapshot if we are sufficiently out of date.
+ saveManager.snapshotIfNeeded(hasTreeChanges);
+ } finally {
+ // make sure the tree is immutable if we are ending a top-level operation.
+ if (depthOne) {
+ tree.immutable();
+ operationTree = null;
+ } else
+ newWorkingTree();
+ }
+ } finally {
+ workManager.checkOut(rule);
+ }
+ if (depthOne)
+ buildManager.endTopLevel(hasTreeChanges);
+ }
+
+ /**
+ * Flush the build order cache for the workspace. Only needed if the
+ * description does not already have a build order. That is, if this
+ * is really a cache.
+ */
+ protected void flushBuildOrder() {
+ if (description.getBuildOrder(false) == null)
+ buildOrder = null;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#forgetSavedTree(String)
+ */
+ public void forgetSavedTree(String pluginId) {
+ saveManager.forgetSavedTree(pluginId);
+ }
+
+ public AliasManager getAliasManager() {
+ return aliasManager;
+ }
+
+ /**
+ * Returns this workspace's build manager
+ */
+ public BuildManager getBuildManager() {
+ return buildManager;
+ }
+
+ /**
+ * Returns the order in which open projects in this workspace will be built.
+ * <p>
+ * The project build order is based on information specified in the workspace
+ * description. The projects are built in the order specified by
+ * <code>IWorkspaceDescription.getBuildOrder</code>; closed or non-existent
+ * projects are ignored and not included in the result. If
+ * <code>IWorkspaceDescription.getBuildOrder</code> is non-null, the default
+ * build order is used; again, only open projects are included in the result.
+ * </p>
+ * <p>
+ * The returned value is cached in the <code>buildOrder</code> field.
+ * </p>
+ *
+ * @return the list of currently open projects in the workspace in the order in
+ * which they would be built by <code>IWorkspace.build</code>.
+ * @see IWorkspace#build(int, IProgressMonitor)
+ * @see IWorkspaceDescription#getBuildOrder()
+ * @since 2.1
+ */
+ public IProject[] getBuildOrder() {
+ if (buildOrder != null) {
+ // return previously-computed and cached project build order
+ return buildOrder;
+ }
+ // see if a particular build order is specified
+ String[] order = description.getBuildOrder(false);
+ if (order != null) {
+ // convert from project names to project handles
+ // and eliminate non-existent and closed projects
+ List projectList = new ArrayList(order.length);
+ for (int i = 0; i < order.length; i++) {
+ IProject project = getRoot().getProject(order[i]);
+ if (project.isAccessible()) {
+ projectList.add(project);
+ }
+ }
+ buildOrder = new IProject[projectList.size()];
+ projectList.toArray(buildOrder);
+ } else {
+ // use default project build order
+ // computed for all accessible projects in workspace
+ buildOrder = computeFullProjectOrder().projects;
+ }
+ return buildOrder;
+ }
+
+ public CharsetManager getCharsetManager() {
+ return charsetManager;
+ }
+
+ public ContentDescriptionManager getContentDescriptionManager() {
+ return contentDescriptionManager;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getDanglingReferences()
+ */
+ public Map getDanglingReferences() {
+ IProject[] projects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
+ Map result = new HashMap(projects.length);
+ for (int i = 0; i < projects.length; i++) {
+ Project project = (Project) projects[i];
+ if (!project.isAccessible())
+ continue;
+ IProject[] refs = project.internalGetDescription().getReferencedProjects(false);
+ List dangling = new ArrayList(refs.length);
+ for (int j = 0; j < refs.length; j++)
+ if (!refs[i].exists())
+ dangling.add(refs[i]);
+ if (!dangling.isEmpty())
+ result.put(projects[i], dangling.toArray(new IProject[dangling.size()]));
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getDescription()
+ */
+ public IWorkspaceDescription getDescription() {
+ WorkspaceDescription workingCopy = defaultWorkspaceDescription();
+ description.copyTo(workingCopy);
+ return workingCopy;
+ }
+
+ /**
+ * Returns the current element tree for this workspace
+ */
+ public ElementTree getElementTree() {
+ return tree;
+ }
+
+ public FileSystemResourceManager getFileSystemManager() {
+ return fileSystemManager;
+ }
+
+ /**
+ * Returns the marker manager for this workspace
+ */
+ public MarkerManager getMarkerManager() {
+ return markerManager;
+ }
+
+ public LocalMetaArea getMetaArea() {
+ return localMetaArea;
+ }
+
+ protected IMoveDeleteHook getMoveDeleteHook() {
+ if (moveDeleteHook == null)
+ initializeMoveDeleteHook();
+ return moveDeleteHook;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getNatureDescriptor(String)
+ */
+ public IProjectNatureDescriptor getNatureDescriptor(String natureId) {
+ return natureManager.getNatureDescriptor(natureId);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getNatureDescriptors()
+ */
+ public IProjectNatureDescriptor[] getNatureDescriptors() {
+ return natureManager.getNatureDescriptors();
+ }
+
+ /**
+ * Returns the nature manager for this workspace.
+ */
+ public NatureManager getNatureManager() {
+ return natureManager;
+ }
+
+ public NotificationManager getNotificationManager() {
+ return notificationManager;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getPathVariableManager()
+ */
+ public IPathVariableManager getPathVariableManager() {
+ return pathVariableManager;
+ }
+
+ public IPropertyManager getPropertyManager() {
+ return propertyManager;
+ }
+
+ /**
+ * Returns the refresh manager for this workspace
+ */
+ public RefreshManager getRefreshManager() {
+ return refreshManager;
+ }
+
+ /**
+ * Returns the resource info for the identified resource.
+ * null is returned if no such resource can be found.
+ * If the phantom flag is true, phantom resources are considered.
+ * If the mutable flag is true, the info is opened for change.
+ *
+ * This method DOES NOT throw an exception if the resource is not found.
+ */
+ public ResourceInfo getResourceInfo(IPath path, boolean phantom, boolean mutable) {
+ try {
+ if (path.segmentCount() == 0) {
+ ResourceInfo info = (ResourceInfo) tree.getTreeData();
+ Assert.isNotNull(info, "Tree root info must never be null"); //$NON-NLS-1$
+ return info;
+ }
+ ResourceInfo result = null;
+ if (!tree.includes(path))
+ return null;
+ if (mutable)
+ result = (ResourceInfo) tree.openElementData(path);
+ else
+ result = (ResourceInfo) tree.getElementData(path);
+ if (result != null && (!phantom && result.isSet(M_PHANTOM)))
+ return null;
+ return result;
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getRoot()
+ */
+ public IWorkspaceRoot getRoot() {
+ return defaultRoot;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getRuleFactory()
+ */
+ public IResourceRuleFactory getRuleFactory() {
+ //note that the rule factory is created lazily because it
+ //requires loading the teamHook extension
+ if (ruleFactory == null)
+ ruleFactory = new Rules(this);
+ return ruleFactory;
+ }
+
+ public SaveManager getSaveManager() {
+ return saveManager;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#getSynchronizer()
+ */
+ public ISynchronizer getSynchronizer() {
+ return synchronizer;
+ }
+
+ /**
+ * Returns the installed team hook. Never returns null.
+ */
+ protected TeamHook getTeamHook() {
+ if (teamHook == null)
+ initializeTeamHook();
+ return teamHook;
+ }
+
+ /**
+ * We should not have direct references to this field. All references should go through
+ * this method.
+ */
+ public WorkManager getWorkManager() throws CoreException {
+ if (_workManager == null) {
+ String message = Messages.resources_shutdown;
+ throw new ResourceException(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, null, message));
+ }
+ return _workManager;
+ }
+
+ /**
+ * A move/delete hook hasn't been initialized. Check the extension point and
+ * try to create a new hook if a user has one defined as an extension. Otherwise
+ * use the Core's implementation as the default.
+ */
+ protected void initializeMoveDeleteHook() {
+ try {
+ if (!canCreateExtensions())
+ return;
+ IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_MOVE_DELETE_HOOK);
+ // no-one is plugged into the extension point so disable validation
+ if (configs == null || configs.length == 0) {
+ return;
+ }
+ // can only have one defined at a time. log a warning
+ if (configs.length > 1) {
+ //XXX: should provide a meaningful status code
+ IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneHook, null);
+ Policy.log(status);
+ return;
+ }
+ // otherwise we have exactly one hook extension. Try to create a new instance
+ // from the user-specified class.
+ try {
+ IConfigurationElement config = configs[0];
+ moveDeleteHook = (IMoveDeleteHook) config.createExecutableExtension("class"); //$NON-NLS-1$
+ } catch (CoreException e) {
+ //ignore the failure if we are shutting down (expected since extension
+ //provider plugin has probably already shut down
+ if (canCreateExtensions()) {
+ IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initHook, e);
+ Policy.log(status);
+ }
+ }
+ } finally {
+ // for now just use Core's implementation
+ if (moveDeleteHook == null)
+ moveDeleteHook = new MoveDeleteHook();
+ }
+ }
+
+ /**
+ * A team hook hasn't been initialized. Check the extension point and
+ * try to create a new hook if a user has one defined as an extension.
+ * Otherwise use the Core's implementation as the default.
+ */
+ protected void initializeTeamHook() {
+ try {
+ if (!canCreateExtensions())
+ return;
+ IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_TEAM_HOOK);
+ // no-one is plugged into the extension point so disable validation
+ if (configs == null || configs.length == 0) {
+ return;
+ }
+ // can only have one defined at a time. log a warning
+ if (configs.length > 1) {
+ //XXX: should provide a meaningful status code
+ IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneTeamHook, null);
+ Policy.log(status);
+ return;
+ }
+ // otherwise we have exactly one hook extension. Try to create a new instance
+ // from the user-specified class.
+ try {
+ IConfigurationElement config = configs[0];
+ teamHook = (TeamHook) config.createExecutableExtension("class"); //$NON-NLS-1$
+ } catch (CoreException e) {
+ //ignore the failure if we are shutting down (expected since extension
+ //provider plugin has probably already shut down
+ if (canCreateExtensions()) {
+ IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initTeamHook, e);
+ Policy.log(status);
+ }
+ }
+ } finally {
+ // default to use Core's implementation
+ //create anonymous subclass because TeamHook is abstract
+ if (teamHook == null)
+ teamHook = new TeamHook() {
+ // empty
+ };
+ }
+ }
+
+ /**
+ * A file modification validator hasn't been initialized. Check the extension point and
+ * try to create a new validator if a user has one defined as an extension.
+ */
+ protected void initializeValidator() {
+ shouldValidate = false;
+ if (!canCreateExtensions())
+ return;
+ IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_FILE_MODIFICATION_VALIDATOR);
+ // no-one is plugged into the extension point so disable validation
+ if (configs == null || configs.length == 0) {
+ return;
+ }
+ // can only have one defined at a time. log a warning, disable validation, but continue with
+ // the #setContents (e.g. don't throw an exception)
+ if (configs.length > 1) {
+ //XXX: should provide a meaningful status code
+ IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneValidator, null);
+ Policy.log(status);
+ return;
+ }
+ // otherwise we have exactly one validator extension. Try to create a new instance
+ // from the user-specified class.
+ try {
+ IConfigurationElement config = configs[0];
+ validator = (IFileModificationValidator) config.createExecutableExtension("class"); //$NON-NLS-1$
+ shouldValidate = true;
+ } catch (CoreException e) {
+ //ignore the failure if we are shutting down (expected since extension
+ //provider plugin has probably already shut down
+ if (canCreateExtensions()) {
+ IStatus status = new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initValidator, e);
+ Policy.log(status);
+ }
+ }
+ }
+
+ public WorkspaceDescription internalGetDescription() {
+ return description;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#isAutoBuilding()
+ */
+ public boolean isAutoBuilding() {
+ return description.isAutoBuilding();
+ }
+
+ public boolean isOpen() {
+ return openFlag;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#isTreeLocked()
+ */
+ public boolean isTreeLocked() {
+ return treeLocked == Thread.currentThread();
+ }
+
+ /**
+ * Link the given tree into the receiver's tree at the specified resource.
+ */
+ protected void linkTrees(IPath path, ElementTree[] newTrees) {
+ tree = tree.mergeDeltaChain(path, newTrees);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#loadProjectDescription(InputStream)
+ * @since 3.1
+ */
+ public IProjectDescription loadProjectDescription(InputStream stream) throws CoreException {
+ IProjectDescription result = null;
+ result = new ProjectDescriptionReader().read(new InputSource(stream));
+ if (result == null) {
+ String message = NLS.bind(Messages.resources_errorReadProject, stream.toString());
+ IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message, null);
+ throw new ResourceException(status);
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#loadProjectDescription(IPath)
+ * @since 2.0
+ */
+ public IProjectDescription loadProjectDescription(IPath path) throws CoreException {
+ IProjectDescription result = null;
+ IOException e = null;
+ try {
+ result = new ProjectDescriptionReader().read(path);
+ if (result != null) {
+ // check to see if we are using in the default area or not. use java.io.File for
+ // testing equality because it knows better w.r.t. drives and case sensitivity
+ IPath user = path.removeLastSegments(1);
+ IPath platform = getRoot().getLocation().append(result.getName());
+ if (!user.toFile().equals(platform.toFile()))
+ result.setLocation(user);
+ }
+ } catch (IOException ex) {
+ e = ex;
+ }
+ if (result == null || e != null) {
+ String message = NLS.bind(Messages.resources_errorReadProject, path.toOSString());
+ IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message, e);
+ throw new ResourceException(status);
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#move(IResource[], IPath, boolean, IProgressMonitor)
+ */
+ public IStatus move(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= IResource.KEEP_HISTORY;
+ return move(resources, destination, updateFlags, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#move(IResource[], IPath, int, IProgressMonitor)
+ */
+ public IStatus move(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ Assert.isLegal(resources != null);
+ int opWork = Math.max(resources.length, 1);
+ int totalWork = Policy.totalWork * opWork / Policy.opWork;
+ String message = Messages.resources_moving_0;
+ monitor.beginTask(message, totalWork);
+ if (resources.length == 0)
+ return Status.OK_STATUS;
+ resources = (IResource[]) resources.clone(); // to avoid concurrent changes to this array
+ IPath parentPath = null;
+ message = Messages.resources_moveProblem;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
+ try {
+ prepareOperation(getRoot(), monitor);
+ beginOperation(true);
+ for (int i = 0; i < resources.length; i++) {
+ Policy.checkCanceled(monitor);
+ Resource resource = (Resource) resources[i];
+ if (resource == null || isDuplicate(resources, i)) {
+ monitor.worked(1);
+ continue;
+ }
+ // test siblings
+ if (parentPath == null)
+ parentPath = resource.getFullPath().removeLastSegments(1);
+ if (parentPath.equals(resource.getFullPath().removeLastSegments(1))) {
+ // test move requirements
+ try {
+ IStatus requirements = resource.checkMoveRequirements(destination.append(resource.getName()), resource.getType(), updateFlags);
+ if (requirements.isOK()) {
+ try {
+ resource.move(destination.append(resource.getName()), updateFlags, Policy.subMonitorFor(monitor, 1));
+ } catch (CoreException e) {
+ status.merge(e.getStatus());
+ }
+ } else {
+ monitor.worked(1);
+ status.merge(requirements);
+ }
+ } catch (CoreException e) {
+ monitor.worked(1);
+ status.merge(e.getStatus());
+ }
+ } else {
+ monitor.worked(1);
+ message = NLS.bind(Messages.resources_notChild, resource.getFullPath(), parentPath);
+ status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resource.getFullPath(), message));
+ }
+ }
+ } catch (OperationCanceledException e) {
+ getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
+ }
+ if (status.matches(IStatus.ERROR))
+ throw new ResourceException(status);
+ return status.isOK() ? (IStatus) Status.OK_STATUS : (IStatus) status;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Moves this resource's subtree to the destination. This operation should only be
+ * used by move methods. Destination must be a valid destination for this resource.
+ * The keepSyncInfo boolean is used to indicated whether or not the sync info should
+ * be moved from the source to the destination.
+ */
+
+ /* package */
+ void move(Resource source, IPath destination, int depth, int updateFlags, boolean keepSyncInfo) throws CoreException {
+ // overlay the tree at the destination path, preserving any important info
+ // in any already existing resource information
+ copyTree(source, destination, depth, updateFlags, keepSyncInfo);
+ source.fixupAfterMoveSource();
+ }
+
+ /**
+ * Create and return a new tree element of the given type.
+ */
+ protected ResourceInfo newElement(int type) {
+ ResourceInfo result = null;
+ switch (type) {
+ case IResource.FILE :
+ case IResource.FOLDER :
+ result = new ResourceInfo();
+ break;
+ case IResource.PROJECT :
+ result = new ProjectInfo();
+ break;
+ case IResource.ROOT :
+ result = new RootInfo();
+ break;
+ }
+ result.setNodeId(nextNodeId());
+ updateModificationStamp(result);
+ result.setType(type);
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#newProjectDescription(String)
+ */
+ public IProjectDescription newProjectDescription(String projectName) {
+ IProjectDescription result = new ProjectDescription();
+ result.setName(projectName);
+ return result;
+ }
+
+ public Resource newResource(IPath path, int type) {
+ String message;
+ switch (type) {
+ case IResource.FOLDER :
+ if (path.segmentCount() < ICoreConstants.MINIMUM_FOLDER_SEGMENT_LENGTH) {
+ message = "Path must include project and resource name: " + path.toString(); //$NON-NLS-1$
+ Assert.isLegal(false, message);
+ }
+ return new Folder(path.makeAbsolute(), this);
+ case IResource.FILE :
+ if (path.segmentCount() < ICoreConstants.MINIMUM_FILE_SEGMENT_LENGTH) {
+ message = "Path must include project and resource name: " + path.toString(); //$NON-NLS-1$
+ Assert.isLegal(false, message);
+ }
+ return new File(path.makeAbsolute(), this);
+ case IResource.PROJECT :
+ return (Resource) getRoot().getProject(path.lastSegment());
+ case IResource.ROOT :
+ return (Resource) getRoot();
+ }
+ Assert.isLegal(false);
+ // will never get here because of assertion.
+ return null;
+ }
+
+ /**
+ * Opens a new mutable element tree layer, thus allowing
+ * modifications to the tree.
+ */
+ public ElementTree newWorkingTree() {
+ tree = tree.newEmptyDelta();
+ return tree;
+ }
+
+ /**
+ * Returns the next, previously unassigned, marker id.
+ */
+ protected long nextMarkerId() {
+ return nextMarkerId++;
+ }
+
+ protected long nextNodeId() {
+ return nextNodeId++;
+ }
+
+ /**
+ * Opens this workspace using the data at its location in the local file system.
+ * This workspace must not be open.
+ * If the operation succeeds, the result will detail any serious
+ * (but non-fatal) problems encountered while opening the workspace.
+ * The status code will be <code>OK</code> if there were no problems.
+ * An exception is thrown if there are fatal problems opening the workspace,
+ * in which case the workspace is left closed.
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ * @return status with code <code>OK</code> if no problems;
+ * otherwise status describing any serious but non-fatal problems.
+ *
+ * @exception CoreException if the workspace could not be opened.
+ * Reasons include:
+ * <ul>
+ * <li> There is no valid workspace structure at the given location
+ * in the local file system.</li>
+ * <li> The workspace structure on disk appears to be hopelessly corrupt.</li>
+ * </ul>
+ * @see ResourcesPlugin#getWorkspace()
+ */
+ public IStatus open(IProgressMonitor monitor) throws CoreException {
+ // This method is not inside an operation because it is the one responsible for
+ // creating the WorkManager object (who takes care of operations).
+ String message = Messages.resources_workspaceOpen;
+ Assert.isTrue(!isOpen(), message);
+ if (!getMetaArea().hasSavedWorkspace()) {
+ message = Messages.resources_readWorkspaceMeta;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, Platform.getLocation(), message, null);
+ }
+ description = new WorkspacePreferences();
+
+ // if we have an old description file, read it (getting rid of it)
+ WorkspaceDescription oldDescription = getMetaArea().readOldWorkspace();
+ if (oldDescription != null) {
+ description.copyFrom(oldDescription);
+ ResourcesPlugin.getPlugin().savePluginPreferences();
+ }
+
+ // create root location
+ localMetaArea.locationFor(getRoot()).toFile().mkdirs();
+
+ IProgressMonitor nullMonitor = Policy.monitorFor(null);
+ startup(nullMonitor);
+ //restart the notification manager so it is initialized with the right tree
+ notificationManager.startup(null);
+ openFlag = true;
+ if (crashed || refreshRequested()) {
+ try {
+ refreshManager.refresh(getRoot());
+ } catch (RuntimeException e) {
+ //don't fail entire open if refresh failed, just report as warning
+ return new ResourceStatus(IResourceStatus.INTERNAL_ERROR, Path.ROOT, Messages.resources_errorMultiRefresh, e);
+ }
+ }
+ //finally register a string pool participant
+ stringPoolJob = new StringPoolJob();
+ stringPoolJob.addStringPoolParticipant(saveManager, getRoot());
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Called before checking the pre-conditions of an operation. Optionally supply
+ * a scheduling rule to determine when the operation is safe to run. If a scheduling
+ * rule is supplied, this method will block until it is safe to run.
+ *
+ * @param rule the scheduling rule that describes what this operation intends to modify.
+ */
+ public void prepareOperation(ISchedulingRule rule, IProgressMonitor monitor) throws CoreException {
+ try {
+ //make sure autobuild is not running if it conflicts with this operation
+ if (rule != null && rule.isConflicting(getRuleFactory().buildRule()))
+ buildManager.interrupt();
+ } finally {
+ getWorkManager().checkIn(rule, monitor);
+ }
+ if (!isOpen()) {
+ String message = Messages.resources_workspaceClosed;
+ throw new ResourceException(IResourceStatus.OPERATION_FAILED, null, message, null);
+ }
+ }
+
+ protected boolean refreshRequested() {
+ String[] args = Platform.getCommandLineArgs();
+ for (int i = 0; i < args.length; i++)
+ if (args[i].equalsIgnoreCase(REFRESH_ON_STARTUP))
+ return true;
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#removeResourceChangeListener(IResourceChangeListener)
+ */
+ public void removeResourceChangeListener(IResourceChangeListener listener) {
+ notificationManager.removeListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#removeSaveParticipant(Plugin)
+ */
+ public void removeSaveParticipant(Plugin plugin) {
+ Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$
+ saveManager.removeParticipant(plugin);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#run(IWorkspaceRunnable, IProgressMonitor)
+ */
+ public void run(IWorkspaceRunnable action, IProgressMonitor monitor) throws CoreException {
+ run(action, defaultRoot, IWorkspace.AVOID_UPDATE, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)
+ */
+ public void run(IWorkspaceRunnable action, ISchedulingRule rule, int options, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
+ int depth = -1;
+ boolean avoidNotification = (options & IWorkspace.AVOID_UPDATE) != 0;
+ try {
+ prepareOperation(rule, monitor);
+ beginOperation(true);
+ if (avoidNotification)
+ avoidNotification = notificationManager.beginAvoidNotify();
+ depth = getWorkManager().beginUnprotected();
+ action.run(Policy.subMonitorFor(monitor, Policy.opWork, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
+ } catch (OperationCanceledException e) {
+ getWorkManager().operationCanceled();
+ throw e;
+ } finally {
+ if (avoidNotification)
+ notificationManager.endAvoidNotify();
+ if (depth >= 0)
+ getWorkManager().endUnprotected(depth);
+ endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#save(boolean, IProgressMonitor)
+ */
+ public IStatus save(boolean full, IProgressMonitor monitor) throws CoreException {
+ return this.save(full, false, monitor);
+ }
+
+ public IStatus save(boolean full, boolean keepConsistencyWhenCanceled, IProgressMonitor monitor) throws CoreException {
+ String message;
+ if (full) {
+ //according to spec it is illegal to start a full save inside another operation
+ if (getWorkManager().isLockAlreadyAcquired()) {
+ message = Messages.resources_saveOp;
+ throw new ResourceException(IResourceStatus.OPERATION_FAILED, null, message, new IllegalStateException());
+ }
+ return saveManager.save(ISaveContext.FULL_SAVE, keepConsistencyWhenCanceled, null, monitor);
+ }
+ // A snapshot was requested. Start an operation (if not already started) and
+ // signal that a snapshot should be done at the end.
+ try {
+ prepareOperation(getRoot(), monitor);
+ beginOperation(false);
+ saveManager.requestSnapshot();
+ message = Messages.resources_snapRequest;
+ return new ResourceStatus(IStatus.OK, message);
+ } finally {
+ endOperation(getRoot(), false, null);
+ }
+ }
+
+ public void setCrashed(boolean value) {
+ crashed = value;
+ if (crashed) {
+ String msg = "The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes."; //$NON-NLS-1$
+ Policy.log(new ResourceStatus(ICoreConstants.CRASH_DETECTED, msg));
+ if (Policy.DEBUG)
+ System.out.println(msg);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ */
+ public void setDescription(IWorkspaceDescription value) {
+ // if both the old and new description's build orders are null, leave the
+ // workspace's build order slot because it is caching the computed order.
+ // Otherwise, set the slot to null to force recomputing or building from the description.
+ WorkspaceDescription newDescription = (WorkspaceDescription) value;
+ String[] newOrder = newDescription.getBuildOrder(false);
+ if (description.getBuildOrder(false) != null || newOrder != null)
+ buildOrder = null;
+ description.copyFrom(newDescription);
+ ResourcesPlugin.getPlugin().savePluginPreferences();
+ }
+
+ public void setTreeLocked(boolean locked) {
+ Assert.isTrue(!locked || treeLocked == null, "The workspace tree is already locked"); //$NON-NLS-1$
+ treeLocked = locked ? Thread.currentThread() : null;
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setWorkspaceLock(WorkspaceLock lock) {
+ // do nothing
+ }
+
+ /**
+ * Shuts down the workspace managers.
+ */
+ protected void shutdown(IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ IManager[] managers = {buildManager, propertyManager, pathVariableManager, charsetManager, fileSystemManager, markerManager, _workManager, aliasManager, refreshManager, contentDescriptionManager};
+ monitor.beginTask("", managers.length); //$NON-NLS-1$
+ String message = Messages.resources_shutdownProblems;
+ MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
+ // best effort to shutdown every object and free resources
+ for (int i = 0; i < managers.length; i++) {
+ IManager manager = managers[i];
+ if (manager == null)
+ monitor.worked(1);
+ else {
+ try {
+ manager.shutdown(Policy.subMonitorFor(monitor, 1));
+ } catch (Exception e) {
+ message = Messages.resources_shutdownProblems;
+ status.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, e));
+ }
+ }
+ }
+ buildManager = null;
+ notificationManager = null;
+ propertyManager = null;
+ pathVariableManager = null;
+ fileSystemManager = null;
+ markerManager = null;
+ synchronizer = null;
+ saveManager = null;
+ _workManager = null;
+ aliasManager = null;
+ refreshManager = null;
+ charsetManager = null;
+ contentDescriptionManager = null;
+ if (!status.isOK())
+ throw new CoreException(status);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#sortNatureSet(String[])
+ */
+ public String[] sortNatureSet(String[] natureIds) {
+ return natureManager.sortNatureSet(natureIds);
+ }
+
+ /**
+ * Starts all the workspace manager classes.
+ */
+ protected void startup(IProgressMonitor monitor) throws CoreException {
+ // ensure the tree is locked during the startup notification
+ try {
+ _workManager = new WorkManager(this);
+ _workManager.startup(null);
+ fileSystemManager = new FileSystemResourceManager(this);
+ fileSystemManager.startup(monitor);
+ pathVariableManager = new PathVariableManager();
+ pathVariableManager.startup(null);
+ natureManager = new NatureManager();
+ natureManager.startup(null);
+ buildManager = new BuildManager(this, getWorkManager().getLock());
+ buildManager.startup(null);
+ notificationManager = new NotificationManager(this);
+ notificationManager.startup(null);
+ markerManager = new MarkerManager(this);
+ markerManager.startup(null);
+ synchronizer = new Synchronizer(this);
+ refreshManager = new RefreshManager(this);
+ saveManager = new SaveManager(this);
+ saveManager.startup(null);
+ //must start after save manager, because (read) access to tree is needed
+ refreshManager.startup(null);
+ aliasManager = new AliasManager(this);
+ aliasManager.startup(null);
+ propertyManager = ResourcesCompatibilityHelper.createPropertyManager();
+ propertyManager.startup(monitor);
+ charsetManager = new CharsetManager(this);
+ charsetManager.startup(null);
+ contentDescriptionManager = new ContentDescriptionManager();
+ contentDescriptionManager.startup(null);
+ } finally {
+ //unlock tree even in case of failure, otherwise shutdown will also fail
+ treeLocked = null;
+ _workManager.postWorkspaceStartup();
+ }
+ }
+
+ /**
+ * Returns a string representation of this working state's
+ * structure suitable for debug purposes.
+ */
+ public String toDebugString() {
+ final StringBuffer buffer = new StringBuffer("\nDump of " + toString() + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ buffer.append(" parent: " + tree.getParent()); //$NON-NLS-1$
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree aTree, IPathRequestor requestor, Object elementContents) {
+ buffer.append("\n " + requestor.requestPath() + ": " + elementContents); //$NON-NLS-1$ //$NON-NLS-2$
+ return true;
+ }
+ };
+ new ElementTreeIterator(tree, Path.ROOT).iterate(visitor);
+ return buffer.toString();
+ }
+
+ public void updateModificationStamp(ResourceInfo info) {
+ info.incrementModificationStamp();
+ }
+
+ /* (non-javadoc)
+ * @see IWorkspace#validateEdit(IFile[], Object)
+ */
+ public IStatus validateEdit(final IFile[] files, final Object context) {
+ // if validation is turned off then just return
+ if (!shouldValidate) {
+ String message = Messages.resources_readOnly2;
+ MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.READ_ONLY_LOCAL, message, null);
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isReadOnly()) {
+ IPath filePath = files[i].getFullPath();
+ message = NLS.bind(Messages.resources_readOnly, filePath);
+ result.add(new ResourceStatus(IResourceStatus.READ_ONLY_LOCAL, filePath, message));
+ }
+ }
+ return result.getChildren().length == 0 ? Status.OK_STATUS : (IStatus) result;
+ }
+ // first time through the validator hasn't been initialized so try and create it
+ if (validator == null)
+ initializeValidator();
+ // we were unable to initialize the validator. Validation has been turned off and
+ // a warning has already been logged so just return.
+ if (validator == null)
+ return Status.OK_STATUS;
+ // otherwise call the API and throw an exception if appropriate
+ final IStatus[] status = new IStatus[1];
+ ISafeRunnable body = new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ status[0] = new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception);
+ }
+
+ public void run() throws Exception {
+ Object c = context;
+ //must null any reference to FileModificationValidationContext for backwards compatibility
+ if (!(validator instanceof FileModificationValidator))
+ if (c instanceof FileModificationValidationContext)
+ c = null;
+ status[0] = validator.validateEdit(files, c);
+ }
+ };
+ SafeRunner.run(body);
+ return status[0];
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateLinkLocation(IResource, IPath)
+ */
+ public IStatus validateLinkLocation(IResource resource, IPath unresolvedLocation) {
+ return locationValidator.validateLinkLocation(resource, unresolvedLocation);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateLinkLocation(IResource, IPath)
+ */
+ public IStatus validateLinkLocationURI(IResource resource, URI unresolvedLocation) {
+ return locationValidator.validateLinkLocationURI(resource, unresolvedLocation);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateName(String, int)
+ */
+ public IStatus validateName(String segment, int type) {
+ return locationValidator.validateName(segment, type);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateNatureSet(String[])
+ */
+ public IStatus validateNatureSet(String[] natureIds) {
+ return natureManager.validateNatureSet(natureIds);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validatePath(String, int)
+ */
+ public IStatus validatePath(String path, int type) {
+ return locationValidator.validatePath(path, type);
+ }
+
+ /* (non-Javadoc)
+ * @see IWorkspace#validateProjectLocation(IProject, IPath)
+ */
+ public IStatus validateProjectLocation(IProject context, IPath location) {
+ return locationValidator.validateProjectLocation(context, location);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.resources.IWorkspace#validateProjectLocation(org.eclipse.core.resources.IProject, java.net.URI)
+ */
+ public IStatus validateProjectLocationURI(IProject project, URI location) {
+ return locationValidator.validateProjectLocationURI(project, location);
+ }
+
+ /**
+ * Internal method. To be called only from the following methods:
+ * <ul>
+ * <li><code>IFile#appendContents</code></li>
+ * <li><code>IFile#setContents(InputStream, boolean, boolean, IProgressMonitor)</code></li>
+ * <li><code>IFile#setContents(IFileState, boolean, boolean, IProgressMonitor)</code></li>
+ * </ul>
+ *
+ * @see IFileModificationValidator#validateSave(IFile)
+ */
+ protected void validateSave(final IFile file) throws CoreException {
+ // if validation is turned off then just return
+ if (!shouldValidate)
+ return;
+ // first time through the validator hasn't been initialized so try and create it
+ if (validator == null)
+ initializeValidator();
+ // we were unable to initialize the validator. Validation has been turned off and
+ // a warning has already been logged so just return.
+ if (validator == null)
+ return;
+ // otherwise call the API and throw an exception if appropriate
+ final IStatus[] status = new IStatus[1];
+ ISafeRunnable body = new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ status[0] = new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception);
+ }
+
+ public void run() throws Exception {
+ status[0] = validator.validateSave(file);
+ }
+ };
+ SafeRunner.run(body);
+ if (!status[0].isOK())
+ throw new ResourceException(status[0]);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceDescription.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.resources.IWorkspaceDescription;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+
+/**
+ * @see IWorkspaceDescription
+ */
+public class WorkspaceDescription extends ModelObject implements IWorkspaceDescription {
+ protected boolean autoBuilding;
+ protected String[] buildOrder;
+ // thread safety: (Concurrency004)
+ protected volatile long fileStateLongevity;
+ protected int maxBuildIterations;
+ protected int maxFileStates;
+ // thread safety: (Concurrency004)
+ protected volatile long maxFileStateSize;
+ // thread safety: (Concurrency004)
+ private volatile long snapshotInterval;
+ protected int operationsPerSnapshot;
+ protected long deltaExpiration;
+
+ public WorkspaceDescription(String name) {
+ super(name);
+ // initialize based on the values in the default preferences
+ IEclipsePreferences node = new DefaultScope().getNode(ResourcesPlugin.PI_RESOURCES);
+ autoBuilding = node.getBoolean(ResourcesPlugin.PREF_AUTO_BUILDING, PreferenceInitializer.PREF_AUTO_BUILDING_DEFAULT);
+ fileStateLongevity = node.getLong(ResourcesPlugin.PREF_FILE_STATE_LONGEVITY, PreferenceInitializer.PREF_FILE_STATE_LONGEVITY_DEFAULT);
+ maxBuildIterations = node.getInt(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS, PreferenceInitializer.PREF_MAX_BUILD_ITERATIONS_DEFAULT);
+ maxFileStates = node.getInt(ResourcesPlugin.PREF_MAX_FILE_STATES, PreferenceInitializer.PREF_MAX_FILE_STATES_DEFAULT);
+ maxFileStateSize = node.getLong(ResourcesPlugin.PREF_MAX_FILE_STATE_SIZE, PreferenceInitializer.PREF_MAX_FILE_STATE_SIZE_DEFAULT);
+ snapshotInterval = node.getLong(ResourcesPlugin.PREF_SNAPSHOT_INTERVAL, PreferenceInitializer.PREF_SNAPSHOT_INTERVAL_DEFAULT);
+ operationsPerSnapshot = node.getInt(PreferenceInitializer.PREF_OPERATIONS_PER_SNAPSHOT, PreferenceInitializer.PREF_OPERATIONS_PER_SNAPSHOT_DEFAULT);
+ deltaExpiration = node.getLong(PreferenceInitializer.PREF_DELTA_EXPIRATION, PreferenceInitializer.PREF_DELTA_EXPIRATION_DEFAULT);
+ }
+
+ /**
+ * @see IWorkspaceDescription#getBuildOrder()
+ */
+ public String[] getBuildOrder() {
+ return getBuildOrder(true);
+ }
+
+ public String[] getBuildOrder(boolean makeCopy) {
+ if (buildOrder == null)
+ return null;
+ return makeCopy ? (String[]) buildOrder.clone() : buildOrder;
+ }
+
+ public long getDeltaExpiration() {
+ return deltaExpiration;
+ }
+
+ public void setDeltaExpiration(long value) {
+ deltaExpiration = value;
+ }
+
+ /**
+ * @see IWorkspaceDescription#getFileStateLongevity()
+ */
+ public long getFileStateLongevity() {
+ return fileStateLongevity;
+ }
+
+ /**
+ * @see IWorkspaceDescription#getMaxBuildIterations()
+ */
+ public int getMaxBuildIterations() {
+ return maxBuildIterations;
+ }
+
+ /**
+ * @see IWorkspaceDescription#getMaxFileStates()
+ */
+ public int getMaxFileStates() {
+ return maxFileStates;
+ }
+
+ /**
+ * @see IWorkspaceDescription#getMaxFileStateSize()
+ */
+ public long getMaxFileStateSize() {
+ return maxFileStateSize;
+ }
+
+ public int getOperationsPerSnapshot() {
+ return operationsPerSnapshot;
+ }
+
+ /**
+ * @see IWorkspaceDescription#getSnapshotInterval()
+ */
+ public long getSnapshotInterval() {
+ return snapshotInterval;
+ }
+
+ public void internalSetBuildOrder(String[] value) {
+ buildOrder = value;
+ }
+
+ /**
+ * @see IWorkspaceDescription#isAutoBuilding()
+ */
+ public boolean isAutoBuilding() {
+ return autoBuilding;
+ }
+
+ public void setOperationsPerSnapshot(int value) {
+ operationsPerSnapshot = value;
+ }
+
+ /**
+ * @see IWorkspaceDescription#setAutoBuilding(boolean)
+ */
+ public void setAutoBuilding(boolean value) {
+ autoBuilding = value;
+ }
+
+ /**
+ * @see IWorkspaceDescription#setBuildOrder(String[])
+ */
+ public void setBuildOrder(String[] value) {
+ buildOrder = (value == null) ? null : (String[]) value.clone();
+ }
+
+ /**
+ * @see IWorkspaceDescription#setFileStateLongevity(long)
+ */
+ public void setFileStateLongevity(long time) {
+ fileStateLongevity = time;
+ }
+
+ /**
+ * @see IWorkspaceDescription#setMaxBuildIterations(int)
+ */
+ public void setMaxBuildIterations(int number) {
+ maxBuildIterations = number;
+ }
+
+ /**
+ * @see IWorkspaceDescription#setMaxFileStates(int)
+ */
+ public void setMaxFileStates(int number) {
+ maxFileStates = number;
+ }
+
+ /**
+ * @see IWorkspaceDescription#setMaxFileStateSize(long)
+ */
+ public void setMaxFileStateSize(long size) {
+ maxFileStateSize = size;
+ }
+
+ /**
+ * @see IWorkspaceDescription#setSnapshotInterval(long)
+ */
+ public void setSnapshotInterval(long snapshotInterval) {
+ this.snapshotInterval = snapshotInterval;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceDescriptionReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.parsers.*;
+import org.eclipse.core.internal.localstore.SafeFileInputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+/**
+ * This class contains legacy code only. It is being used to read workspace
+ * descriptions which are obsolete.
+ */
+public class WorkspaceDescriptionReader implements IModelObjectConstants {
+ /** constants */
+ protected static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ public WorkspaceDescriptionReader() {
+ super();
+ }
+
+ protected String getString(Node target, String tagName) {
+ Node node = searchNode(target, tagName);
+ return node != null ? (node.getFirstChild() == null ? null : node.getFirstChild().getNodeValue()) : null;
+ }
+
+ protected String[] getStrings(Node target) {
+ if (target == null)
+ return null;
+ NodeList list = target.getChildNodes();
+ if (list.getLength() == 0)
+ return EMPTY_STRING_ARRAY;
+ List result = new ArrayList(list.getLength());
+ for (int i = 0; i < list.getLength(); i++) {
+ Node node = list.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ result.add(read(node.getChildNodes().item(0)));
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * A value was discovered in the workspace description file that was not a number.
+ * Log the exception.
+ */
+ private void logNumberFormatException(String value, NumberFormatException e) {
+ String msg = NLS.bind(Messages.resources_readWorkspaceMetaValue, value);
+ Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, null, msg, e));
+ }
+
+ public Object read(InputStream input) {
+ try {
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ Document document = parser.parse(input);
+ return read(document.getFirstChild());
+ } catch (IOException e) {
+ // ignore
+ } catch (SAXException e) {
+ // ignore
+ } catch (ParserConfigurationException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ public Object read(IPath location, IPath tempLocation) throws IOException {
+ SafeFileInputStream file = new SafeFileInputStream(location.toOSString(), tempLocation.toOSString());
+ try {
+ return read(file);
+ } finally {
+ file.close();
+ }
+ }
+
+ protected Object read(Node node) {
+ if (node == null)
+ return null;
+ switch (node.getNodeType()) {
+ case Node.ELEMENT_NODE :
+ if (node.getNodeName().equals(WORKSPACE_DESCRIPTION))
+ return readWorkspaceDescription(node);
+ case Node.TEXT_NODE :
+ String value = node.getNodeValue();
+ return value == null ? null : value.trim();
+ default :
+ return node.toString();
+ }
+ }
+
+ /**
+ * read (String, String) hashtables
+ */
+ protected WorkspaceDescription readWorkspaceDescription(Node node) {
+ // get values
+ String name = getString(node, NAME);
+ String autobuild = getString(node, AUTOBUILD);
+ String snapshotInterval = getString(node, SNAPSHOT_INTERVAL);
+ String fileStateLongevity = getString(node, FILE_STATE_LONGEVITY);
+ String maxFileStateSize = getString(node, MAX_FILE_STATE_SIZE);
+ String maxFileStates = getString(node, MAX_FILE_STATES);
+ String[] buildOrder = getStrings(searchNode(node, BUILD_ORDER));
+
+ // build instance
+ //invalid values are skipped and defaults are used instead
+ WorkspaceDescription description = new WorkspaceDescription(name);
+ if (autobuild != null)
+ //if in doubt (value is corrupt) we want autobuild on
+ description.setAutoBuilding(!autobuild.equals(Integer.toString(0)));
+ try {
+ if (fileStateLongevity != null)
+ description.setFileStateLongevity(Long.parseLong(fileStateLongevity));
+ } catch (NumberFormatException e) {
+ logNumberFormatException(fileStateLongevity, e);
+ }
+ try {
+ if (maxFileStateSize != null)
+ description.setMaxFileStateSize(Long.parseLong(maxFileStateSize));
+ } catch (NumberFormatException e) {
+ logNumberFormatException(maxFileStateSize, e);
+ }
+ try {
+ if (maxFileStates != null)
+ description.setMaxFileStates(Integer.parseInt(maxFileStates));
+ } catch (NumberFormatException e) {
+ logNumberFormatException(maxFileStates, e);
+ }
+ if (buildOrder != null)
+ description.internalSetBuildOrder(buildOrder);
+ try {
+ if (snapshotInterval != null)
+ description.setSnapshotInterval(Long.parseLong(snapshotInterval));
+ } catch (NumberFormatException e) {
+ logNumberFormatException(snapshotInterval, e);
+ }
+ return description;
+ }
+
+ protected Node searchNode(Node target, String tagName) {
+ NodeList list = target.getChildNodes();
+ for (int i = 0; i < list.getLength(); i++) {
+ if (list.item(i).getNodeName().equals(tagName))
+ return list.item(i);
+ }
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspacePreferences.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.*;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+
+/**
+ * This class provides the same interface as <code>WorkspaceDescription</code>
+ * but instead of changing/obtaining values from its internal state, it
+ * changes/obtains properties in/from the workspace plug-in's preferences.
+ *
+ * Note: for performance reasons, some frequently called accessor methods are
+ * reading a cached value from the super class instead of reading the
+ * corresponding property preference store. To keep the cache synchronized with
+ * the preference store, a property change listener is used.
+ */
+
+public class WorkspacePreferences extends WorkspaceDescription {
+
+ public final static String PROJECT_SEPARATOR = "/"; //$NON-NLS-1$
+
+ private Preferences preferences;
+
+ /**
+ * Helper method that converts a string string array {"string1","
+ * string2",..."stringN"} to a string in the form "string1,string2,...
+ * stringN".
+ */
+ public static String convertStringArraytoString(String[] array) {
+ if (array == null || array.length == 0)
+ return ""; //$NON-NLS-1$
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < array.length; i++) {
+ sb.append(array[i]);
+ sb.append(PROJECT_SEPARATOR);
+ }
+ sb.deleteCharAt(sb.length() - 1);
+ return sb.toString();
+ }
+
+ /**
+ * Helper method that converts a string in the form "string1,string2,...
+ * stringN" to a string array {"string1","string2",..."stringN"}.
+ */
+ public static String[] convertStringToStringArray(String string, String separator) {
+ List list = new ArrayList();
+ for (StringTokenizer tokenizer = new StringTokenizer(string, separator); tokenizer.hasMoreTokens();)
+ list.add(tokenizer.nextToken());
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Helper method that copies all attributes from a workspace description
+ * object to another.
+ */
+ private static void copyFromTo(WorkspaceDescription source, WorkspaceDescription target) {
+ target.setAutoBuilding(source.isAutoBuilding());
+ target.setBuildOrder(source.getBuildOrder());
+ target.setFileStateLongevity(source.getFileStateLongevity());
+ target.setMaxBuildIterations(source.getMaxBuildIterations());
+ target.setMaxFileStates(source.getMaxFileStates());
+ target.setMaxFileStateSize(source.getMaxFileStateSize());
+ target.setSnapshotInterval(source.getSnapshotInterval());
+ target.setOperationsPerSnapshot(source.getOperationsPerSnapshot());
+ target.setDeltaExpiration(source.getDeltaExpiration());
+ }
+
+ public WorkspacePreferences() {
+ super("Workspace"); //$NON-NLS-1$
+ this.preferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+
+ final String version = preferences.getString(ICoreConstants.PREF_VERSION_KEY);
+ if (!ICoreConstants.PREF_VERSION.equals(version))
+ upgradeVersion(version);
+
+ // initialize cached preferences (for better performance)
+ super.setAutoBuilding(preferences.getBoolean(ResourcesPlugin.PREF_AUTO_BUILDING));
+ super.setSnapshotInterval(preferences.getInt(ResourcesPlugin.PREF_SNAPSHOT_INTERVAL));
+ super.setMaxBuildIterations(preferences.getInt(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS));
+ super.setMaxFileStates(preferences.getInt(ResourcesPlugin.PREF_MAX_FILE_STATES));
+ super.setMaxFileStateSize(preferences.getLong(ResourcesPlugin.PREF_MAX_FILE_STATE_SIZE));
+ super.setFileStateLongevity(preferences.getLong(ResourcesPlugin.PREF_FILE_STATE_LONGEVITY));
+ super.setOperationsPerSnapshot(preferences.getInt(PreferenceInitializer.PREF_OPERATIONS_PER_SNAPSHOT));
+ super.setDeltaExpiration(preferences.getLong(PreferenceInitializer.PREF_DELTA_EXPIRATION));
+
+ // This property listener ensures we are being updated properly when changes
+ // are done directly to the preference store.
+ preferences.addPropertyChangeListener(new Preferences.IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ synchronizeWithPreferences(event.getProperty());
+ }
+ });
+ }
+
+ public Object clone() {
+ // should never be called - throws an exception to avoid using a
+ // WorkspacePreferences when using WorkspaceDescription was the real
+ // intention (this class offers a different protocol for copying state).
+ throw new UnsupportedOperationException("clone() is not supported in " + getClass().getName()); //$NON-NLS-1$
+ }
+
+ public void copyFrom(WorkspaceDescription source) {
+ copyFromTo(source, this);
+ }
+
+ public void copyTo(WorkspaceDescription target) {
+ copyFromTo(this, target);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#getBuildOrder()
+ */
+ public String[] getBuildOrder() {
+ boolean defaultBuildOrder = preferences.getBoolean(ResourcesPlugin.PREF_DEFAULT_BUILD_ORDER);
+ if (defaultBuildOrder)
+ return null;
+ return convertStringToStringArray(preferences.getString(ResourcesPlugin.PREF_BUILD_ORDER), PROJECT_SEPARATOR);
+ }
+
+ /**
+ * @see org.eclipse.core.internal.resources.
+ * WorkspaceDescription#getBuildOrder(boolean)
+ */
+ public String[] getBuildOrder(boolean makeCopy) {
+ //note that since this is stored in the preference store, we are creating
+ //a new copy of the string array on every access anyway
+ return getBuildOrder();
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setAutoBuilding(boolean)
+ */
+ public void setAutoBuilding(boolean value) {
+ preferences.setValue(ResourcesPlugin.PREF_AUTO_BUILDING, value);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setBuildOrder(String[])
+ */
+ public void setBuildOrder(String[] value) {
+ preferences.setValue(ResourcesPlugin.PREF_DEFAULT_BUILD_ORDER, value == null);
+ preferences.setValue(ResourcesPlugin.PREF_BUILD_ORDER, convertStringArraytoString(value));
+ }
+
+ public void setDeltaExpiration(long value) {
+ preferences.setValue(PreferenceInitializer.PREF_DELTA_EXPIRATION, value);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setFileStateLongevity(long)
+ */
+ public void setFileStateLongevity(long time) {
+ preferences.setValue(ResourcesPlugin.PREF_FILE_STATE_LONGEVITY, time);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setMaxBuildIterations(int)
+ */
+ public void setMaxBuildIterations(int number) {
+ preferences.setValue(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS, number);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setMaxFileStates(int)
+ */
+ public void setMaxFileStates(int number) {
+ preferences.setValue(ResourcesPlugin.PREF_MAX_FILE_STATES, number);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setMaxFileStateSize(long)
+ */
+ public void setMaxFileStateSize(long size) {
+ preferences.setValue(ResourcesPlugin.PREF_MAX_FILE_STATE_SIZE, size);
+ }
+
+ public void setOperationsPerSnapshot(int value) {
+ preferences.setValue(PreferenceInitializer.PREF_OPERATIONS_PER_SNAPSHOT, value);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceDescription#setSnapshotInterval(long)
+ */
+ public void setSnapshotInterval(long delay) {
+ preferences.setValue(ResourcesPlugin.PREF_SNAPSHOT_INTERVAL, delay);
+ }
+
+ protected void synchronizeWithPreferences(String property) {
+ // do not use the value in the event - may be a string instead
+ // of the expected type. Retrieve it from the preferences store
+ // using the type-specific method
+ if (property.equals(ResourcesPlugin.PREF_AUTO_BUILDING))
+ super.setAutoBuilding(preferences.getBoolean(ResourcesPlugin.PREF_AUTO_BUILDING));
+ else if (property.equals(ResourcesPlugin.PREF_SNAPSHOT_INTERVAL))
+ super.setSnapshotInterval(preferences.getLong(ResourcesPlugin.PREF_SNAPSHOT_INTERVAL));
+ else if (property.equals(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS))
+ super.setMaxBuildIterations(preferences.getInt(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS));
+ else if (property.equals(ResourcesPlugin.PREF_MAX_FILE_STATES))
+ super.setMaxFileStates(preferences.getInt(ResourcesPlugin.PREF_MAX_FILE_STATES));
+ else if (property.equals(ResourcesPlugin.PREF_MAX_FILE_STATE_SIZE))
+ super.setMaxFileStateSize(preferences.getLong(ResourcesPlugin.PREF_MAX_FILE_STATE_SIZE));
+ else if (property.equals(ResourcesPlugin.PREF_FILE_STATE_LONGEVITY))
+ super.setFileStateLongevity(preferences.getLong(ResourcesPlugin.PREF_FILE_STATE_LONGEVITY));
+ else if (property.equals(PreferenceInitializer.PREF_OPERATIONS_PER_SNAPSHOT))
+ super.setOperationsPerSnapshot(preferences.getInt(PreferenceInitializer.PREF_OPERATIONS_PER_SNAPSHOT));
+ else if (property.equals(PreferenceInitializer.PREF_DELTA_EXPIRATION))
+ super.setDeltaExpiration(preferences.getLong(PreferenceInitializer.PREF_DELTA_EXPIRATION));
+ }
+
+ private void upgradeVersion(String oldVersion) {
+ if (oldVersion.length() == 0) {
+ //only need to convert the build order if we are not using the default order
+ if (!preferences.getBoolean(ResourcesPlugin.PREF_DEFAULT_BUILD_ORDER)) {
+ String oldOrder = preferences.getString(ResourcesPlugin.PREF_BUILD_ORDER);
+ setBuildOrder(convertStringToStringArray(oldOrder, ":")); //$NON-NLS-1$
+ }
+ }
+ preferences.setValue(ICoreConstants.PREF_VERSION_KEY, ICoreConstants.PREF_VERSION);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceRoot.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+
+import java.net.URI;
+import java.util.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+public class WorkspaceRoot extends Container implements IWorkspaceRoot {
+ /**
+ * As an optimization, we store a table of project handles
+ * that have been requested from this root. This maps project
+ * name strings to project handles.
+ */
+ private final Map projectTable = Collections.synchronizedMap(new HashMap(16));
+
+ /**
+ * Cache of the canonicalized platform location.
+ */
+ private final IPath workspaceLocation;
+
+ protected WorkspaceRoot(IPath path, Workspace container) {
+ super(path, container);
+ Assert.isTrue(path.equals(Path.ROOT));
+ workspaceLocation = FileUtil.canonicalPath(Platform.getLocation());
+ Assert.isNotNull(workspaceLocation);
+ }
+
+ /**
+ * @see IWorkspaceRoot#delete(boolean, boolean, IProgressMonitor)
+ */
+ public void delete(boolean deleteContent, boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ updateFlags |= deleteContent ? IResource.ALWAYS_DELETE_PROJECT_CONTENT : IResource.NEVER_DELETE_PROJECT_CONTENT;
+ delete(updateFlags, monitor);
+ }
+
+ /**
+ * @see org.eclipse.core.internal.resources.Resource#delete(boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void delete(boolean force, IProgressMonitor monitor) throws CoreException {
+ int updateFlags = force ? IResource.FORCE : IResource.NONE;
+ delete(updateFlags, monitor);
+ }
+
+ /**
+ * @see org.eclipse.core.internal.resources.Resource#exists(int, boolean)
+ */
+ public boolean exists(int flags, boolean checkType) {
+ return true;
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceRoot#findContainersForLocation(org.eclipse.core.runtime.IPath)
+ * @deprecated
+ */
+ public IContainer[] findContainersForLocation(IPath location) {
+ return findContainersForLocationURI(URIUtil.toURI(location.makeAbsolute()));
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceRoot#findContainersForLocationURI(java.net.URI)
+ */
+ public IContainer[] findContainersForLocationURI(URI location) {
+ return findContainersForLocationURI(location, NONE);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceRoot#findContainersForLocationURI(java.net.URI, int)
+ */
+ public IContainer[] findContainersForLocationURI(URI location, int memberFlags) {
+ if (!location.isAbsolute())
+ throw new IllegalArgumentException();
+ return (IContainer[]) getLocalManager().allResourcesFor(location, false, memberFlags);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceRoot#findFilesForLocation(org.eclipse.core.runtime.IPath)
+ * @deprecated
+ */
+ public IFile[] findFilesForLocation(IPath location) {
+ return findFilesForLocationURI(URIUtil.toURI(location.makeAbsolute()));
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceRoot#findFilesForLocationURI(java.net.URI)
+ */
+ public IFile[] findFilesForLocationURI(URI location) {
+ return findFilesForLocationURI(location, NONE);
+ }
+
+ /**
+ * @see org.eclipse.core.resources.IWorkspaceRoot#findFilesForLocationURI(java.net.URI, int)
+ */
+ public IFile[] findFilesForLocationURI(URI location, int memberFlags) {
+ if (!location.isAbsolute())
+ throw new IllegalArgumentException();
+ return (IFile[]) getLocalManager().allResourcesFor(location, true, memberFlags);
+ }
+
+ /**
+ * @see IWorkspaceRoot#getContainerForLocation(IPath)
+ */
+ public IContainer getContainerForLocation(IPath location) {
+ return getLocalManager().containerForLocation(location);
+ }
+
+ /**
+ * @see IContainer#getDefaultCharset(boolean)
+ */
+ public String getDefaultCharset(boolean checkImplicit) {
+ if (checkImplicit)
+ return ResourcesPlugin.getEncoding();
+ String enc = ResourcesPlugin.getPlugin().getPluginPreferences().getString(ResourcesPlugin.PREF_ENCODING);
+ return enc == null || enc.length() == 0 ? null : enc;
+ }
+
+ /**
+ * @see IWorkspaceRoot#getFileForLocation(IPath)
+ */
+ public IFile getFileForLocation(IPath location) {
+ return getLocalManager().fileForLocation(location);
+ }
+
+ /**
+ * @see IResource#getLocalTimeStamp()
+ */
+ public long getLocalTimeStamp() {
+ return IResource.NULL_STAMP;
+ }
+
+ /**
+ * @see IResource#getLocation()
+ */
+ public IPath getLocation() {
+ return workspaceLocation;
+ }
+
+ /**
+ * @see IResource#getName()
+ */
+ public String getName() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * @see IResource#getParent()
+ */
+ public IContainer getParent() {
+ return null;
+ }
+
+ /**
+ * @see IResource#getProject()
+ */
+ public IProject getProject() {
+ return null;
+ }
+
+ /**
+ * @see IWorkspaceRoot#getProject(String)
+ */
+ public IProject getProject(String name) {
+ //first check our project cache
+ Project result = (Project) projectTable.get(name);
+ if (result == null) {
+ IPath projectPath = new Path(null, name).makeAbsolute();
+ String message = "Path for project must have only one segment."; //$NON-NLS-1$
+ Assert.isLegal(projectPath.segmentCount() == ICoreConstants.PROJECT_SEGMENT_LENGTH, message);
+ //try to get the project using a canonical name
+ String canonicalName = projectPath.lastSegment();
+ result = (Project) projectTable.get(canonicalName);
+ if (result != null)
+ return result;
+ result = new Project(projectPath, workspace);
+ projectTable.put(canonicalName, result);
+ }
+ return result;
+ }
+
+ /**
+ * @see IResource#getProjectRelativePath()
+ */
+ public IPath getProjectRelativePath() {
+ return Path.EMPTY;
+ }
+
+ /**
+ * @see IWorkspaceRoot#getProjects()
+ */
+ public IProject[] getProjects() {
+ return getProjects(IResource.NONE);
+ }
+
+ /**
+ * @see IWorkspaceRoot#getProjects(int)
+ */
+ public IProject[] getProjects(int memberFlags) {
+ IResource[] roots = getChildren(memberFlags);
+ IProject[] result = new IProject[roots.length];
+ System.arraycopy(roots, 0, result, 0, roots.length);
+ return result;
+ }
+
+ /**
+ * @see IResource#getType()
+ */
+ public int getType() {
+ return IResource.ROOT;
+ }
+
+ public void internalSetLocal(boolean flag, int depth) throws CoreException {
+ // do nothing for the root, but call for its children
+ if (depth == IResource.DEPTH_ZERO)
+ return;
+ if (depth == IResource.DEPTH_ONE)
+ depth = IResource.DEPTH_ZERO;
+ // get the children via the workspace since we know that this
+ // resource exists (it is local).
+ IResource[] children = getChildren(IResource.NONE);
+ for (int i = 0; i < children.length; i++)
+ ((Resource) children[i]).internalSetLocal(flag, depth);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.Resource#isDerived(int)
+ */
+ public boolean isDerived(int options) {
+ return false;//the root is never derived
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.Resource#isHidden()
+ */
+ public boolean isHidden() {
+ return false;//the root is never hidden
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.Resource#isHidden(int)
+ */
+ public boolean isHidden(int options) {
+ return false;//the root is never hidden
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.Resource#isTeamPrivateMember(int)
+ */
+ public boolean isTeamPrivateMember(int options) {
+ return false;//the root is never a team private member
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.Resource#isLinked(int)
+ */
+ public boolean isLinked(int options) {
+ return false;//the root is never linked
+ }
+
+ /**
+ * @see IResource#isLocal(int)
+ * @deprecated
+ */
+ public boolean isLocal(int depth) {
+ // the flags parameter is ignored for the workspace root so pass anything
+ return isLocal(-1, depth);
+ }
+
+ /**
+ * @see IResource#isLocal(int)
+ * @deprecated
+ */
+ public boolean isLocal(int flags, int depth) {
+ // don't check the flags....workspace root is always local
+ if (depth == DEPTH_ZERO)
+ return true;
+ if (depth == DEPTH_ONE)
+ depth = DEPTH_ZERO;
+ // get the children via the workspace since we know that this
+ // resource exists (it is local).
+ IResource[] children = getChildren(IResource.NONE);
+ for (int i = 0; i < children.length; i++)
+ if (!children[i].isLocal(depth))
+ return false;
+ return true;
+ }
+
+ /**
+ * @see IResource#isPhantom()
+ */
+ public boolean isPhantom() {
+ return false;
+ }
+
+ /**
+ * @see IContainer#setDefaultCharset(String)
+ * @deprecated Replaced by {@link #setDefaultCharset(String, IProgressMonitor)} which
+ * is a workspace operation and reports changes in resource deltas.
+ */
+ public void setDefaultCharset(String charset) {
+ // directly change the Resource plugin's preference for encoding
+ Preferences resourcesPreferences = ResourcesPlugin.getPlugin().getPluginPreferences();
+ if (charset != null)
+ resourcesPreferences.setValue(ResourcesPlugin.PREF_ENCODING, charset);
+ else
+ resourcesPreferences.setToDefault(ResourcesPlugin.PREF_ENCODING);
+ }
+
+ public void setHidden(boolean isHidden) {
+ //workspace root cannot be set hidden
+ }
+
+ /**
+ * @see IResource#setLocalTimeStamp(long)
+ */
+ public long setLocalTimeStamp(long value) {
+ if (value < 0)
+ throw new IllegalArgumentException("Illegal time stamp: " + value); //$NON-NLS-1$
+ //can't set local time for root
+ return value;
+ }
+
+ /**
+ * @deprecated
+ * @see IResource#setReadOnly(boolean)
+ */
+ public void setReadOnly(boolean readonly) {
+ //can't set the root read only
+ }
+
+ /**
+ * @see IResource#touch(IProgressMonitor)
+ */
+ public void touch(IProgressMonitor monitor) {
+ // do nothing for the workspace root
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceTreeReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Default tree reader that does not read anything. This is used in cases
+ * where the tree format is unknown (for example when opening a workspace
+ * from a future version).
+ */
+public abstract class WorkspaceTreeReader {
+ /**
+ * Returns the tree reader associated with the given tree version number
+ */
+ public static WorkspaceTreeReader getReader(Workspace workspace, int version) throws CoreException {
+ switch (version) {
+ case ICoreConstants.WORKSPACE_TREE_VERSION_1 :
+ return new WorkspaceTreeReader_1(workspace);
+ case ICoreConstants.WORKSPACE_TREE_VERSION_2 :
+ return new WorkspaceTreeReader_2(workspace);
+ default :
+ // Unknown tree version - fail to read the tree
+ String msg = NLS.bind(Messages.resources_format, new Integer(version));
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
+ }
+ }
+
+ /**
+ * Returns a snapshot from the stream. This default implementation does nothing.
+ */
+ public abstract ElementTree readSnapshotTree(DataInputStream input, ElementTree complete, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Reads all workspace trees from the stream. This default implementation does nothing.
+ */
+ public abstract void readTree(DataInputStream input, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Reads a project's trees from the stream. This default implementation does nothing.
+ */
+ public abstract void readTree(IProject project, DataInputStream input, IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceTreeReader_1.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.events.BuilderPersistentInfo;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.internal.watson.ElementTreeReader;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Reads version 1 of the workspace tree file format.
+ */
+public class WorkspaceTreeReader_1 extends WorkspaceTreeReader {
+ protected Workspace workspace;
+
+ public WorkspaceTreeReader_1(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ protected int getVersion() {
+ return ICoreConstants.WORKSPACE_TREE_VERSION_1;
+ }
+
+ protected void linkBuildersToTrees(List buildersToBeLinked, ElementTree[] trees, int index, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ ArrayList infos = null;
+ String projectName = null;
+ for (int i = 0; i < buildersToBeLinked.size(); i++) {
+ BuilderPersistentInfo info = (BuilderPersistentInfo) buildersToBeLinked.get(i);
+ if (!info.getProjectName().equals(projectName)) {
+ if (infos != null) { // if it is not the first iteration
+ IProject project = workspace.getRoot().getProject(projectName);
+ workspace.getBuildManager().setBuildersPersistentInfo(project, infos);
+ }
+ projectName = info.getProjectName();
+ infos = new ArrayList(5);
+ }
+ info.setLastBuildTree(trees[index++]);
+ infos.add(info);
+ }
+ if (infos != null) {
+ IProject project = workspace.getRoot().getProject(projectName);
+ workspace.getBuildManager().setBuildersPersistentInfo(project, infos);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected void linkPluginsSavedStateToTrees(List states, ElementTree[] trees, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ for (int i = 0; i < states.size(); i++) {
+ SavedState state = (SavedState) states.get(i);
+ // If the tree is too old (depends on the policy), the plug-in should not
+ // get it back as a delta. It is expensive to maintain this information too long.
+ final SaveManager saveManager = workspace.getSaveManager();
+ if (!saveManager.isOldPluginTree(state.pluginId)) {
+ state.oldTree = trees[i];
+ } else {
+ //clear information for this plugin from master table
+ saveManager.clearDeltaExpiration(state.pluginId);
+ }
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected BuilderPersistentInfo readBuilderInfo(IProject project, DataInputStream input, int index) throws IOException {
+ //read the project name
+ String projectName = input.readUTF();
+ //use the name of the project handle if available
+ if (project != null)
+ projectName = project.getName();
+ String builderName = input.readUTF();
+ return new BuilderPersistentInfo(projectName, builderName, index);
+ }
+
+ protected void readBuildersPersistentInfo(IProject project, DataInputStream input, List builders, IProgressMonitor monitor) throws IOException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ int builderCount = input.readInt();
+ for (int i = 0; i < builderCount; i++)
+ builders.add(readBuilderInfo(project, input, i));
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected void readPluginsSavedStates(DataInputStream input, HashMap savedStates, List plugins, IProgressMonitor monitor) throws IOException, CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ int stateCount = input.readInt();
+ for (int i = 0; i < stateCount; i++) {
+ String pluginId = input.readUTF();
+ SavedState state = new SavedState(workspace, pluginId, null, null);
+ savedStates.put(pluginId, state);
+ plugins.add(state);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ public ElementTree readSnapshotTree(DataInputStream input, ElementTree complete, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ message = Messages.resources_readingSnap;
+ monitor.beginTask(message, Policy.totalWork);
+ ElementTreeReader reader = new ElementTreeReader(workspace.getSaveManager());
+ while (input.available() > 0) {
+ readWorkspaceFields(input, Policy.subMonitorFor(monitor, Policy.totalWork / 2));
+ complete = reader.readDelta(complete, input);
+ try {
+ // make sure each snapshot is read by the correct reader
+ int version = input.readInt();
+ if (version != getVersion())
+ return WorkspaceTreeReader.getReader(workspace, version).readSnapshotTree(input, complete, monitor);
+ } catch (EOFException e) {
+ break;
+ }
+ }
+ return complete;
+ } catch (IOException e) {
+ message = Messages.resources_readWorkspaceSnap;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, message, e);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ public void readTree(DataInputStream input, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ message = Messages.resources_reading;
+ monitor.beginTask(message, Policy.totalWork);
+ readWorkspaceFields(input, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
+
+ HashMap savedStates = new HashMap(20);
+ List pluginsToBeLinked = new ArrayList(20);
+ readPluginsSavedStates(input, savedStates, pluginsToBeLinked, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
+ workspace.getSaveManager().setPluginsSavedState(savedStates);
+
+ List buildersToBeLinked = new ArrayList(20);
+ readBuildersPersistentInfo(null, input, buildersToBeLinked, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
+
+ ElementTree[] trees = readTrees(Path.ROOT, input, Policy.subMonitorFor(monitor, Policy.opWork * 40 / 100));
+ linkPluginsSavedStateToTrees(pluginsToBeLinked, trees, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
+ linkBuildersToTrees(buildersToBeLinked, trees, pluginsToBeLinked.size(), Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
+
+ } catch (IOException e) {
+ message = Messages.resources_readWorkspaceTree;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, message, e);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ public void readTree(IProject project, DataInputStream input, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ message = Messages.resources_reading;
+ monitor.beginTask(message, 10);
+ /* read the number of builders */
+ int numBuilders = input.readInt();
+
+ /* read in the list of builder names */
+ String[] builderNames = new String[numBuilders];
+ for (int i = 0; i < numBuilders; i++) {
+ String builderName = input.readUTF();
+ builderNames[i] = builderName;
+ }
+ monitor.worked(1);
+
+ /* read and link the trees */
+ ElementTree[] trees = readTrees(project.getFullPath(), input, Policy.subMonitorFor(monitor, 8));
+
+ /* map builder names to trees */
+ if (numBuilders > 0) {
+ ArrayList infos = new ArrayList(trees.length * 2 + 1);
+ for (int i = 0; i < numBuilders; i++) {
+ BuilderPersistentInfo info = new BuilderPersistentInfo(project.getName(), builderNames[i], -1);
+ info.setLastBuildTree(trees[i]);
+ infos.add(info);
+ }
+ workspace.getBuildManager().setBuildersPersistentInfo(project, infos);
+ }
+ monitor.worked(1);
+
+ } catch (IOException e) {
+ message = Messages.resources_readProjectTree;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, message, e);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Read trees from disk and link them to the workspace tree.
+ */
+ protected ElementTree[] readTrees(IPath root, DataInputStream input, IProgressMonitor monitor) throws IOException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ String message = Messages.resources_reading;
+ monitor.beginTask(message, 4);
+ ElementTreeReader treeReader = new ElementTreeReader(workspace.getSaveManager());
+ ElementTree[] trees = treeReader.readDeltaChain(input);
+ monitor.worked(3);
+ if (root.isRoot()) {
+ //Don't need to link because we're reading the whole workspace.
+ //The last tree in the chain is the complete tree.
+ ElementTree newTree = trees[trees.length - 1];
+ newTree.setTreeData(workspace.tree.getTreeData());
+ workspace.tree = newTree;
+ } else {
+ //splice the restored tree into the current set of trees
+ workspace.linkTrees(root, trees);
+ }
+ monitor.worked(1);
+ return trees;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected void readWorkspaceFields(DataInputStream input, IProgressMonitor monitor) throws IOException, CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ // read the node id
+ workspace.nextNodeId = input.readLong();
+ // read the modification stamp (no longer used)
+ input.readLong();
+ // read the next marker id
+ workspace.nextMarkerId = input.readLong();
+ // read the synchronizer's registered sync partners
+ ((Synchronizer) workspace.getSynchronizer()).readPartners(input);
+ } finally {
+ monitor.done();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceTreeReader_2.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.internal.events.BuilderPersistentInfo;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Reads version 2 of the workspace tree file format.
+ *
+ * This version differs from version 1 in the amount of information that is persisted
+ * for each builder. Version 1 only stored builder names and trees. Version
+ * 2 stores builder names, project names, trees, and interesting projects for
+ * each builder.
+ */
+public class WorkspaceTreeReader_2 extends WorkspaceTreeReader_1 {
+
+ public WorkspaceTreeReader_2(Workspace workspace) {
+ super(workspace);
+ }
+
+ protected int getVersion() {
+ return ICoreConstants.WORKSPACE_TREE_VERSION_2;
+ }
+
+ /*
+ * overwritten from WorkspaceTreeReader_1
+ */
+ protected void readBuildersPersistentInfo(IProject project, DataInputStream input, List builders, IProgressMonitor monitor) throws IOException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ int builderCount = input.readInt();
+ for (int i = 0; i < builderCount; i++) {
+ BuilderPersistentInfo info = readBuilderInfo(project, input, i);
+ // read interesting projects
+ int n = input.readInt();
+ IProject[] projects = new IProject[n];
+ for (int j = 0; j < n; j++)
+ projects[j] = workspace.getRoot().getProject(input.readUTF());
+ info.setInterestingProjects(projects);
+ builders.add(info);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /*
+ * overwritten from WorkspaceTreeReader_1
+ */
+ public void readTree(IProject project, DataInputStream input, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ String message;
+ try {
+ message = Messages.resources_reading;
+ monitor.beginTask(message, 10);
+
+ /* read in the builder infos */
+ List infos = new ArrayList(5);
+ readBuildersPersistentInfo(project, input, infos, Policy.subMonitorFor(monitor, 1));
+
+ /* read and link the trees */
+ ElementTree[] trees = readTrees(project.getFullPath(), input, Policy.subMonitorFor(monitor, 8));
+
+ /* map builder names to trees */
+ linkBuildersToTrees(infos, trees, 0, Policy.subMonitorFor(monitor, 1));
+
+ } catch (IOException e) {
+ message = Messages.resources_readProjectTree;
+ throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, message, e);
+ } finally {
+ monitor.done();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/XMLWriter.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A simple XML writer.
+ */
+public class XMLWriter extends PrintWriter {
+ protected int tab;
+
+ /* constants */
+ protected static final String XML_VERSION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; //$NON-NLS-1$
+
+ public XMLWriter(OutputStream output) throws UnsupportedEncodingException {
+ super(new OutputStreamWriter(output, "UTF8")); //$NON-NLS-1$
+ tab = 0;
+ println(XML_VERSION);
+ }
+
+ public void endTag(String name) {
+ tab--;
+ printTag('/' + name, null);
+ }
+
+ public void printSimpleTag(String name, Object value) {
+ if (value != null) {
+ printTag(name, null, true, false);
+ print(getEscaped(String.valueOf(value)));
+ printTag('/' + name, null, false, true);
+ }
+ }
+
+ public void printTabulation() {
+ for (int i = 0; i < tab; i++)
+ super.print('\t');
+ }
+
+ public void printTag(String name, HashMap parameters) {
+ printTag(name, parameters, true, true);
+ }
+
+ public void printTag(String name, HashMap parameters, boolean shouldTab, boolean newLine) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("<"); //$NON-NLS-1$
+ sb.append(name);
+ if (parameters != null)
+ for (Iterator it = parameters.keySet().iterator(); it.hasNext();) {
+ sb.append(" "); //$NON-NLS-1$
+ String key = (String) it.next();
+ sb.append(key);
+ sb.append("=\""); //$NON-NLS-1$
+ sb.append(getEscaped(String.valueOf(parameters.get(key))));
+ sb.append("\""); //$NON-NLS-1$
+ }
+ sb.append(">"); //$NON-NLS-1$
+ if (shouldTab)
+ printTabulation();
+ if (newLine)
+ println(sb.toString());
+ else
+ print(sb.toString());
+ }
+
+ public void startTag(String name, HashMap parameters) {
+ startTag(name, parameters, true);
+ }
+
+ public void startTag(String name, HashMap parameters, boolean newLine) {
+ printTag(name, parameters, true, newLine);
+ tab++;
+ }
+
+ private static void appendEscapedChar(StringBuffer buffer, char c) {
+ String replacement = getReplacement(c);
+ if (replacement != null) {
+ buffer.append('&');
+ buffer.append(replacement);
+ buffer.append(';');
+ } else {
+ buffer.append(c);
+ }
+ }
+
+ public static String getEscaped(String s) {
+ StringBuffer result = new StringBuffer(s.length() + 10);
+ for (int i = 0; i < s.length(); ++i)
+ appendEscapedChar(result, s.charAt(i));
+ return result.toString();
+ }
+
+ private static String getReplacement(char c) {
+ // Encode special XML characters into the equivalent character references.
+ // These five are defined by default for all XML documents.
+ switch (c) {
+ case '<' :
+ return "lt"; //$NON-NLS-1$
+ case '>' :
+ return "gt"; //$NON-NLS-1$
+ case '"' :
+ return "quot"; //$NON-NLS-1$
+ case '\'' :
+ return "apos"; //$NON-NLS-1$
+ case '&' :
+ return "amp"; //$NON-NLS-1$
+ }
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ChangeDescription.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import java.util.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A description of the changes found in a delta
+ */
+public class ChangeDescription {
+
+ private List addedRoots = new ArrayList();
+ private List changedFiles = new ArrayList();
+ private List closedProjects = new ArrayList();
+ private List copiedRoots = new ArrayList();
+ private List movedRoots = new ArrayList();
+ private List removedRoots = new ArrayList();
+
+ private IResource createSourceResource(IResourceDelta delta) {
+ IPath sourcePath = delta.getMovedFromPath();
+ IResource resource = delta.getResource();
+ IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
+ switch (resource.getType()) {
+ case IResource.PROJECT :
+ return wsRoot.getProject(sourcePath.segment(0));
+ case IResource.FOLDER :
+ return wsRoot.getFolder(sourcePath);
+ case IResource.FILE :
+ return wsRoot.getFile(sourcePath);
+ }
+ return null;
+ }
+
+ private void ensureResourceCovered(IResource resource, List list) {
+ IPath path = resource.getFullPath();
+ for (Iterator iter = list.iterator(); iter.hasNext();) {
+ IResource root = (IResource) iter.next();
+ if (root.getFullPath().isPrefixOf(path)) {
+ return;
+ }
+ }
+ list.add(resource);
+ }
+
+ public IResource[] getRootResources() {
+ Set result = new HashSet();
+ result.addAll(addedRoots);
+ result.addAll(changedFiles);
+ result.addAll(closedProjects);
+ result.addAll(copiedRoots);
+ result.addAll(movedRoots);
+ result.addAll(removedRoots);
+ return (IResource[]) result.toArray(new IResource[result.size()]);
+ }
+
+ private void handleAdded(IResourceDelta delta) {
+ if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
+ handleMove(delta);
+ } else if ((delta.getFlags() & IResourceDelta.COPIED_FROM) != 0) {
+ handleCopy(delta);
+ } else {
+ ensureResourceCovered(delta.getResource(), addedRoots);
+ }
+ }
+
+ private void handleChange(IResourceDelta delta) {
+ if ((delta.getFlags() & IResourceDelta.REPLACED) != 0) {
+ // A replace was added in place of a removed resource
+ handleAdded(delta);
+ } else if (delta.getResource().getType() == IResource.FILE) {
+ ensureResourceCovered(delta.getResource(), changedFiles);
+ }
+ }
+
+ private void handleCopy(IResourceDelta delta) {
+ if ((delta.getFlags() & IResourceDelta.COPIED_FROM) != 0) {
+ IResource source = createSourceResource(delta);
+ ensureResourceCovered(source, copiedRoots);
+ }
+ }
+
+ private void handleMove(IResourceDelta delta) {
+ if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
+ movedRoots.add(delta.getResource());
+ } else if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
+ IResource source = createSourceResource(delta);
+ ensureResourceCovered(source, movedRoots);
+ }
+ }
+
+ private void handleRemoved(IResourceDelta delta) {
+ if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
+ closedProjects.add(delta.getResource());
+ } else if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
+ handleMove(delta);
+ } else {
+ ensureResourceCovered(delta.getResource(), removedRoots);
+ }
+ }
+
+ /**
+ * Record the change and return whether any child changes should be visited.
+ * @param delta the change
+ * @return whether any child changes should be visited
+ */
+ public boolean recordChange(IResourceDelta delta) {
+ switch (delta.getKind()) {
+ case IResourceDelta.ADDED :
+ handleAdded(delta);
+ return true; // Need to traverse children to look for moves or other changes under added roots
+ case IResourceDelta.REMOVED :
+ handleRemoved(delta);
+ // No need to look for further changes under a remove (such as moves).
+ // Changes will be discovered in corresponding destination delta
+ return false;
+ case IResourceDelta.CHANGED :
+ handleChange(delta);
+ return true;
+ }
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ModelProviderDescriptor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import java.util.*;
+import org.eclipse.core.expressions.*;
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.mapping.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+public class ModelProviderDescriptor implements IModelProviderDescriptor {
+
+ private String id;
+ private String[] extendedModels;
+ private String label;
+ private ModelProvider provider;
+ private Expression enablementRule;
+
+ private static EvaluationContext createEvaluationContext(Object element) {
+ EvaluationContext result = new EvaluationContext(null, element);
+ return result;
+ }
+
+ public ModelProviderDescriptor(IExtension extension) throws CoreException {
+ readExtension(extension);
+ }
+
+ private boolean convert(EvaluationResult eval) {
+ if (eval == EvaluationResult.FALSE)
+ return false;
+ return true;
+ }
+
+ protected void fail(String reason) throws CoreException {
+ throw new ResourceException(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, reason, null));
+ }
+
+ public String[] getExtendedModels() {
+ return extendedModels;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public IResource[] getMatchingResources(IResource[] resources) throws CoreException {
+ Set result = new HashSet();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ EvaluationContext evalContext = createEvaluationContext(resource);
+ if (matches(evalContext)) {
+ result.add(resource);
+ }
+ }
+ return (IResource[]) result.toArray(new IResource[result.size()]);
+ }
+
+ public synchronized ModelProvider getModelProvider() throws CoreException {
+ if (provider == null) {
+ IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_MODEL_PROVIDERS, id);
+ IConfigurationElement[] elements = extension.getConfigurationElements();
+ for (int i = 0; i < elements.length; i++) {
+ IConfigurationElement element = elements[i];
+ if (element.getName().equalsIgnoreCase("modelProvider")) { //$NON-NLS-1$
+ try {
+ provider = (ModelProvider) element.createExecutableExtension("class"); //$NON-NLS-1$
+ provider.init(this);
+ } catch (ClassCastException e) {
+ String message = NLS.bind(Messages.mapping_wrongType, id);
+ throw new CoreException(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, Platform.PLUGIN_ERROR, message, e));
+ }
+ }
+ }
+ }
+ return provider;
+ }
+
+ public boolean matches(IEvaluationContext context) throws CoreException {
+ if (enablementRule == null)
+ return false;
+ return convert(enablementRule.evaluate(context));
+ }
+
+ /**
+ * Initialize this descriptor based on the provided extension point.
+ */
+ protected void readExtension(IExtension extension) throws CoreException {
+ //read the extension
+ id = extension.getUniqueIdentifier();
+ if (id == null)
+ fail(Messages.mapping_noIdentifier);
+ label = extension.getLabel();
+ IConfigurationElement[] elements = extension.getConfigurationElements();
+ int count = elements.length;
+ ArrayList extendsList = new ArrayList(count);
+ for (int i = 0; i < count; i++) {
+ IConfigurationElement element = elements[i];
+ String name = element.getName();
+ if (name.equalsIgnoreCase("extends-model")) { //$NON-NLS-1$
+ String attribute = element.getAttribute("id"); //$NON-NLS-1$
+ if (attribute == null)
+ fail(NLS.bind(Messages.mapping_invalidDef, id));
+ extendsList.add(attribute);
+ } else if (name.equalsIgnoreCase(ExpressionTagNames.ENABLEMENT)) {
+ enablementRule = ExpressionConverter.getDefault().perform(element);
+ }
+ }
+ extendedModels = (String[]) extendsList.toArray(new String[extendsList.size()]);
+ }
+
+ public ResourceTraversal[] getMatchingTraversals(ResourceTraversal[] traversals) throws CoreException {
+ List result = new ArrayList();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ if (getMatchingResources(traversal.getResources()).length > 0) {
+ result.add(traversal);
+ }
+ }
+ return (ResourceTraversal[]) result.toArray(new ResourceTraversal[result.size()]);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ModelProviderManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.runtime.*;
+
+public class ModelProviderManager {
+
+ private static Map descriptors;
+ private static ModelProviderManager instance;
+
+ public synchronized static ModelProviderManager getDefault() {
+ if (instance == null) {
+ instance = new ModelProviderManager();
+ }
+ return instance;
+ }
+
+ private void detectCycles() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public IModelProviderDescriptor getDescriptor(String id) {
+ lazyInitialize();
+ return (IModelProviderDescriptor) descriptors.get(id);
+ }
+
+ public IModelProviderDescriptor[] getDescriptors() {
+ lazyInitialize();
+ return (IModelProviderDescriptor[]) descriptors.values().toArray(new IModelProviderDescriptor[descriptors.size()]);
+ }
+
+ public ModelProvider getModelProvider(String modelProviderId) throws CoreException {
+ IModelProviderDescriptor desc = getDescriptor(modelProviderId);
+ if (desc == null)
+ return null;
+ return desc.getModelProvider();
+ }
+
+ protected void lazyInitialize() {
+ if (descriptors != null)
+ return;
+ IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_MODEL_PROVIDERS);
+ IExtension[] extensions = point.getExtensions();
+ descriptors = new HashMap(extensions.length * 2 + 1);
+ for (int i = 0, imax = extensions.length; i < imax; i++) {
+ IModelProviderDescriptor desc = null;
+ try {
+ desc = new ModelProviderDescriptor(extensions[i]);
+ } catch (CoreException e) {
+ Policy.log(e);
+ }
+ if (desc != null)
+ descriptors.put(desc.getId(), desc);
+ }
+ //do cycle detection now so it only has to be done once
+ //cycle detection on a graph subset is a pain
+ detectCycles();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ProposedResourceDelta.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import java.util.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Concrete implementation of IResourceDelta used for operation validation
+ */
+public final class ProposedResourceDelta extends PlatformObject implements IResourceDelta {
+
+ protected static int KIND_MASK = 0xFF;
+
+ private HashMap children = new HashMap(8);
+ private IPath movedFromPath;
+ private IPath movedToPath;
+ private IResource resource;
+ private int status;
+
+ public ProposedResourceDelta(IResource resource) {
+ this.resource = resource;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#accept(org.eclipse.core.resources.IResourceDeltaVisitor)
+ */
+ public void accept(IResourceDeltaVisitor visitor) throws CoreException {
+ accept(visitor, 0);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#accept(org.eclipse.core.resources.IResourceDeltaVisitor, boolean)
+ */
+ public void accept(IResourceDeltaVisitor visitor, boolean includePhantoms) throws CoreException {
+ accept(visitor, includePhantoms ? IContainer.INCLUDE_PHANTOMS : 0);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#accept(org.eclipse.core.resources.IResourceDeltaVisitor, int)
+ */
+ public void accept(IResourceDeltaVisitor visitor, int memberFlags) throws CoreException {
+ if (!visitor.visit(this))
+ return;
+ for (Iterator iter = children.values().iterator(); iter.hasNext();) {
+ ProposedResourceDelta childDelta = (ProposedResourceDelta) iter.next();
+ childDelta.accept(visitor, memberFlags);
+ }
+ }
+
+ /**
+ * Adds a child delta to the list of children for this delta node.
+ * @param delta
+ */
+ protected void add(ProposedResourceDelta delta) {
+ if (children.size() == 0 && status == 0)
+ setKind(IResourceDelta.CHANGED);
+ children.put(delta.getResource().getName(), delta);
+ }
+
+ /**
+ * Adds the given flags to this delta.
+ * @param flags The flags to add
+ */
+ protected void addFlags(int flags) {
+ //make sure the provided flags don't influence the kind
+ this.status |= (flags & ~KIND_MASK);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#findMember(org.eclipse.core.runtime.IPath)
+ */
+ public IResourceDelta findMember(IPath path) {
+ int segmentCount = path.segmentCount();
+ if (segmentCount == 0)
+ return this;
+
+ //iterate over the path and find matching child delta
+ ProposedResourceDelta current = this;
+ for (int i = 0; i < segmentCount; i++) {
+ current = (ProposedResourceDelta) current.children.get(path.segment(i));
+ if (current == null)
+ return null;
+ }
+ return current;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getAffectedChildren()
+ */
+ public IResourceDelta[] getAffectedChildren() {
+ return getAffectedChildren(ADDED | REMOVED | CHANGED, IResource.NONE);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getAffectedChildren(int)
+ */
+ public IResourceDelta[] getAffectedChildren(int kindMask) {
+ return getAffectedChildren(kindMask, IResource.NONE);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getAffectedChildren(int, int)
+ */
+ public IResourceDelta[] getAffectedChildren(int kindMask, int memberFlags) {
+ List result = new ArrayList();
+ for (Iterator iter = children.values().iterator(); iter.hasNext();) {
+ ProposedResourceDelta child = (ProposedResourceDelta) iter.next();
+ if ((child.getKind() & kindMask) != 0)
+ result.add(child);
+ }
+ return (IResourceDelta[]) result.toArray(new IResourceDelta[result.size()]);
+ }
+
+ /**
+ * Returns the child delta corresponding to the given child resource name,
+ * or <code>null</code>.
+ */
+ ProposedResourceDelta getChild(String name) {
+ return (ProposedResourceDelta) children.get(name);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getFlags()
+ */
+ public int getFlags() {
+ return status & ~KIND_MASK;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getFullPath()
+ */
+ public IPath getFullPath() {
+ return getResource().getFullPath();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getKind()
+ */
+ public int getKind() {
+ return status & KIND_MASK;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getMarkerDeltas()
+ */
+ public IMarkerDelta[] getMarkerDeltas() {
+ return new IMarkerDelta[0];
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getMovedFromPath()
+ */
+ public IPath getMovedFromPath() {
+ return movedFromPath;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getMovedToPath()
+ */
+ public IPath getMovedToPath() {
+ return movedToPath;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getProjectRelativePath()
+ */
+ public IPath getProjectRelativePath() {
+ return getResource().getProjectRelativePath();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDelta#getResource()
+ */
+ public IResource getResource() {
+ return resource;
+ }
+
+ public void setFlags(int flags) {
+ status = getKind() | (flags & ~KIND_MASK);
+ }
+
+ protected void setKind(int kind) {
+ status = getFlags() | (kind & KIND_MASK);
+ }
+
+ protected void setMovedFromPath(IPath path) {
+ movedFromPath = path;
+ }
+
+ protected void setMovedToPath(IPath path) {
+ movedToPath = path;
+ }
+
+ /**
+ * For debugging purposes only.
+ */
+ public String toString() {
+ return "ProposedDelta(" + resource + ')'; //$NON-NLS-1$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceAdapterFactory.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.IAdapterFactory;
+
+/**
+ * Adapter factory converting IResource to ResourceMapping
+ *
+ * @since 3.1
+ */
+public class ResourceAdapterFactory implements IAdapterFactory {
+
+ /* (non-Javadoc)
+ * Method declared on IAdapterFactory
+ */
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if (adapterType == ResourceMapping.class && adaptableObject instanceof IResource) {
+ return new SimpleResourceMapping((IResource) adaptableObject);
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IAdapterFactory
+ */
+ public Class[] getAdapterList() {
+ return new Class[] {ResourceMapping.class};
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceChangeDescriptionFactory.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Factory for creating a resource delta that describes a proposed change.
+ */
+public class ResourceChangeDescriptionFactory implements IResourceChangeDescriptionFactory {
+
+ private ProposedResourceDelta root = new ProposedResourceDelta(ResourcesPlugin.getWorkspace().getRoot());
+
+ /**
+ * Creates and a delta representing a deleted resource, and adds it to the provided
+ * parent delta.
+ * @param parentDelta The parent of the deletion delta to create
+ * @param resource The deleted resource to create a delta for
+ */
+ private ProposedResourceDelta buildDeleteDelta(ProposedResourceDelta parentDelta, IResource resource) {
+ //start with the existing delta for this resource, if any, to preserve other flags
+ ProposedResourceDelta delta = parentDelta.getChild(resource.getName());
+ if (delta == null) {
+ delta = new ProposedResourceDelta(resource);
+ parentDelta.add(delta);
+ }
+ delta.setKind(IResourceDelta.REMOVED);
+ if (resource.getType() == IResource.FILE)
+ return delta;
+ //recurse to build deletion deltas for children
+ try {
+ IResource[] members = ((IContainer) resource).members();
+ int childCount = members.length;
+ if (childCount > 0) {
+ ProposedResourceDelta[] childDeltas = new ProposedResourceDelta[childCount];
+ for (int i = 0; i < childCount; i++)
+ childDeltas[i] = buildDeleteDelta(delta, members[i]);
+ }
+ } catch (CoreException e) {
+ //don't need to create deletion deltas for children of inaccessible resources
+ }
+ return delta;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IProposedResourceDeltaFactory#change(org.eclipse.core.resources.IFile)
+ */
+ public void change(IFile file) {
+ ProposedResourceDelta delta = getDelta(file);
+ if (delta.getKind() == 0)
+ delta.setKind(IResourceDelta.CHANGED);
+ //the CONTENT flag only applies to the changed and moved from cases
+ if (delta.getKind() == IResourceDelta.CHANGED
+ || (delta.getFlags() & IResourceDelta.MOVED_FROM) != 0
+ || (delta.getFlags() & IResourceDelta.COPIED_FROM) != 0)
+ delta.addFlags(IResourceDelta.CONTENT);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IProposedResourceDeltaFactory#close(org.eclipse.core.resources.IProject)
+ */
+ public void close(IProject project) {
+ delete(project);
+ ProposedResourceDelta delta = getDelta(project);
+ delta.addFlags(IResourceDelta.OPEN);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IProposedResourceDeltaFactory#copy(org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IPath)
+ */
+ public void copy(IResource resource, IPath destination) {
+ moveOrCopyDeep(resource, destination, false /* copy */);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory#create(org.eclipse.core.resources.IResource)
+ */
+ public void create(IResource resource) {
+ getDelta(resource).setKind(IResourceDelta.ADDED);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IProposedResourceDeltaFactory#delete(org.eclipse.core.resources.IResource)
+ */
+ public void delete(IResource resource) {
+ if (resource.getType() == IResource.ROOT) {
+ //the root itself cannot be deleted, so create deletions for each project
+ IProject[] projects = ((IWorkspaceRoot)resource).getProjects(IContainer.INCLUDE_HIDDEN);
+ for (int i = 0; i < projects.length; i++)
+ buildDeleteDelta(root, projects[i]);
+ } else {
+ buildDeleteDelta(getDelta(resource.getParent()), resource);
+ }
+ }
+
+ private void fail(CoreException e) {
+ Policy.log(e.getStatus().getSeverity(), "An internal error occurred while accumulating a change description.", e); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IProposedResourceDeltaFactory#getDelta()
+ */
+ public IResourceDelta getDelta() {
+ return root;
+ }
+
+ ProposedResourceDelta getDelta(IResource resource) {
+ ProposedResourceDelta delta = (ProposedResourceDelta) root.findMember(resource.getFullPath());
+ if (delta != null) {
+ return delta;
+ }
+ ProposedResourceDelta parent = getDelta(resource.getParent());
+ delta = new ProposedResourceDelta(resource);
+ parent.add(delta);
+ return delta;
+ }
+
+ /*
+ * Return the resource at the destination path that corresponds to the source resource
+ * @param source the source resource
+ * @param sourcePrefix the path of the root of the move or copy
+ * @param destinationPrefix the path of the destination the root was copied to
+ * @return the destination resource
+ */
+ protected IResource getDestinationResource(IResource source, IPath sourcePrefix, IPath destinationPrefix) {
+ IPath relativePath = source.getFullPath().removeFirstSegments(sourcePrefix.segmentCount());
+ IPath destinationPath = destinationPrefix.append(relativePath);
+ IResource destination;
+ IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
+ switch (source.getType()) {
+ case IResource.FILE :
+ destination = wsRoot.getFile(destinationPath);
+ break;
+ case IResource.FOLDER :
+ destination = wsRoot.getFolder(destinationPath);
+ break;
+ case IResource.PROJECT :
+ destination = wsRoot.getProject(destinationPath.segment(0));
+ break;
+ default :
+ // Shouldn't happen
+ destination = null;
+ }
+ return destination;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.IProposedResourceDeltaFactory#move(org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IPath)
+ */
+ public void move(IResource resource, IPath destination) {
+ moveOrCopyDeep(resource, destination, true /* move */);
+ }
+
+ /**
+ * Builds the delta representing a single resource being moved or copied.
+ *
+ * @param resource The resource being moved
+ * @param sourcePrefix The root of the sub-tree being moved
+ * @param destinationPrefix The root of the destination sub-tree
+ * @param move <code>true</code> for a move, <code>false</code> for a copy
+ * @return Whether to move or copy the child
+ */
+ boolean moveOrCopy(IResource resource, final IPath sourcePrefix, final IPath destinationPrefix, final boolean move) {
+ ProposedResourceDelta sourceDelta = getDelta(resource);
+ if (sourceDelta.getKind() == IResourceDelta.REMOVED) {
+ // There is already a removed delta here so there
+ // is nothing to move/copy
+ return false;
+ }
+ IResource destinationResource = getDestinationResource(resource, sourcePrefix, destinationPrefix);
+ ProposedResourceDelta destinationDelta = getDelta(destinationResource);
+ if ((destinationDelta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED)) > 0) {
+ // There is already a resource at the destination
+ // TODO: What do we do
+ return false;
+ }
+ // First, create the delta for the source
+ IPath fromPath = resource.getFullPath();
+ boolean wasAdded = false;
+ final int sourceFlags = sourceDelta.getFlags();
+ if (move) {
+ // We transfer the source flags to the destination
+ if (sourceDelta.getKind() == IResourceDelta.ADDED) {
+ if ((sourceFlags & IResourceDelta.MOVED_FROM) != 0) {
+ // The resource was moved from somewhere else so
+ // we need to transfer the path to the new location
+ fromPath = sourceDelta.getMovedFromPath();
+ sourceDelta.setMovedFromPath(null);
+ }
+ // The source was added and then moved so we'll
+ // make it an add at the destination
+ sourceDelta.setKind(0);
+ wasAdded = true;
+ } else {
+ // We reset the status to be a remove/move_to
+ sourceDelta.setKind(IResourceDelta.REMOVED);
+ sourceDelta.setFlags(IResourceDelta.MOVED_TO);
+ sourceDelta.setMovedToPath(destinationPrefix.append(fromPath.removeFirstSegments(sourcePrefix.segmentCount())));
+ }
+ }
+ // Next, create the delta for the destination
+ if (destinationDelta.getKind() == IResourceDelta.REMOVED) {
+ // The destination was removed and is being re-added
+ destinationDelta.setKind(IResourceDelta.CHANGED);
+ destinationDelta.addFlags(IResourceDelta.REPLACED);
+ } else {
+ destinationDelta.setKind(IResourceDelta.ADDED);
+ }
+ if (!wasAdded || !fromPath.equals(resource.getFullPath())) {
+ // The source wasn't added so it is a move/copy
+ destinationDelta.addFlags(move ? IResourceDelta.MOVED_FROM : IResourceDelta.COPIED_FROM);
+ destinationDelta.setMovedFromPath(fromPath);
+ // Apply the source flags
+ if (move)
+ destinationDelta.addFlags(sourceFlags);
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper method that generate a move or copy delta for a sub-tree
+ * of resources being moved or copied.
+ */
+ private void moveOrCopyDeep(IResource resource, IPath destination, final boolean move) {
+ final IPath sourcePrefix = resource.getFullPath();
+ final IPath destinationPrefix = destination;
+ try {
+ //build delta for the entire sub-tree if available
+ if (resource.isAccessible()) {
+ resource.accept(new IResourceVisitor() {
+ public boolean visit(IResource child) {
+ return moveOrCopy(child, sourcePrefix, destinationPrefix, move);
+ }
+ });
+ } else {
+ //just build a delta for the single resource
+ moveOrCopy(resource, sourcePrefix, destination, move);
+ }
+ } catch (CoreException e) {
+ fail(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceModelProvider.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import java.util.*;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A simple model provider that represents the resource model itself.
+ *
+ * @since 3.2
+ */
+public final class ResourceModelProvider extends ModelProvider {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ModelProvider#getMappings(org.eclipse.core.resources.IResource, org.eclipse.core.resources.mapping.ResourceMappingContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public ResourceMapping[] getMappings(IResource resource, ResourceMappingContext context, IProgressMonitor monitor) {
+ return new ResourceMapping[] {new SimpleResourceMapping(resource)};
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ModelProvider#getMappings(org.eclipse.core.resources.mapping.ResourceTraversal[], org.eclipse.core.resources.mapping.ResourceMappingContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public ResourceMapping[] getMappings(ResourceTraversal[] traversals, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ Set result = new HashSet();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ IResource[] resources = traversal.getResources();
+ int depth = traversal.getDepth();
+ for (int j = 0; j < resources.length; j++) {
+ IResource resource = resources[j];
+ switch (depth) {
+ case IResource.DEPTH_INFINITE :
+ result.add(resource);
+ break;
+ case IResource.DEPTH_ONE :
+ if (resource.getType() == IResource.FILE) {
+ result.add(resource);
+ } else {
+ result.add(new ShallowContainer((IContainer)resource));
+ }
+ break;
+ case IResource.DEPTH_ZERO :
+ if (resource.getType() == IResource.FILE)
+ result.add(resource);
+ break;
+ }
+ }
+ }
+ ResourceMapping[] mappings = new ResourceMapping[result.size()];
+ int i = 0;
+ for (Iterator iter = result.iterator(); iter.hasNext();) {
+ Object element = iter.next();
+ if (element instanceof IResource) {
+ mappings[i++] = new SimpleResourceMapping((IResource) element);
+ } else {
+ mappings[i++] = new ShallowResourceMapping((ShallowContainer)element);
+ }
+ }
+ return mappings;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ShallowContainer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.PlatformObject;
+
+/**
+ * A special model object used to represent shallow folders
+ */
+public class ShallowContainer extends PlatformObject {
+
+ private IContainer container;
+
+ public ShallowContainer(IContainer container) {
+ this.container = container;
+ }
+
+ public IContainer getResource() {
+ return container;
+ }
+
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof ShallowContainer) {
+ ShallowContainer other = (ShallowContainer) obj;
+ return other.getResource().equals(getResource());
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return getResource().hashCode();
+ }
+
+ public Object getAdapter(Class adapter) {
+ if (adapter == IResource.class || adapter == IContainer.class)
+ return container;
+ return super.getAdapter(adapter);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ShallowResourceMapping.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A resource mapping for a shallow container.
+ */
+public class ShallowResourceMapping extends ResourceMapping {
+
+ private final ShallowContainer container;
+
+ public ShallowResourceMapping(ShallowContainer container) {
+ this.container = container;
+ }
+
+ public Object getModelObject() {
+ return container;
+ }
+
+ public String getModelProviderId() {
+ return ModelProvider.RESOURCE_MODEL_PROVIDER_ID;
+ }
+
+ public IProject[] getProjects() {
+ return new IProject[] { container.getResource().getProject() };
+ }
+
+ public ResourceTraversal[] getTraversals(ResourceMappingContext context, IProgressMonitor monitor) {
+ return new ResourceTraversal[] { new ResourceTraversal(new IResource[] { container.getResource() }, IResource.DEPTH_ONE, IResource.NONE)};
+ }
+
+ public boolean contains(ResourceMapping mapping) {
+ if (mapping.getModelProviderId().equals(this.getModelProviderId())) {
+ Object object = mapping.getModelObject();
+ IResource resource = container.getResource();
+ // A shallow mapping only contains direct file children or equal shallow containers
+ if (object instanceof ShallowContainer) {
+ ShallowContainer sc = (ShallowContainer) object;
+ return sc.getResource().equals(resource);
+ }
+ if (object instanceof IResource) {
+ IResource other = (IResource) object;
+ return other.getType() == IResource.FILE
+ && resource.getFullPath().equals(other.getFullPath().removeLastSegments(1));
+ }
+ }
+ return false;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/SimpleResourceMapping.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.mapping;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.mapping.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A simple resource mapping for converting IResource to ResourceMapping.
+ * It uses the resource as the model object and traverses deeply.
+ *
+ * @since 3.1
+ */
+public class SimpleResourceMapping extends ResourceMapping {
+ private final IResource resource;
+
+ public SimpleResourceMapping(IResource resource) {
+ this.resource = resource;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ResourceMapping#contains(org.eclipse.core.resources.mapping.ResourceMapping)
+ */
+ public boolean contains(ResourceMapping mapping) {
+ if (mapping.getModelProviderId().equals(this.getModelProviderId())) {
+ Object object = mapping.getModelObject();
+ if (object instanceof IResource) {
+ IResource other = (IResource) object;
+ return resource.getFullPath().isPrefixOf(other.getFullPath());
+ }
+ if (object instanceof ShallowContainer) {
+ ShallowContainer sc = (ShallowContainer) object;
+ IResource other = sc.getResource();
+ return resource.getFullPath().isPrefixOf(other.getFullPath());
+ }
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on ResourceMapping.
+ */
+ public Object getModelObject() {
+ return resource;
+ }
+
+ public String getModelProviderId() {
+ return ModelProvider.RESOURCE_MODEL_PROVIDER_ID;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on ResourceMapping.
+ */
+ public IProject[] getProjects() {
+ if (resource.getType() == IResource.ROOT)
+ return ((IWorkspaceRoot)resource).getProjects();
+ return new IProject[] {resource.getProject()};
+ }
+
+ /* (non-Javadoc)
+ * Method declared on ResourceMapping.
+ */
+ public ResourceTraversal[] getTraversals(ResourceMappingContext context, IProgressMonitor monitor) {
+ if (resource.getType() == IResource.ROOT) {
+ return new ResourceTraversal[] {new ResourceTraversal(((IWorkspaceRoot)resource).getProjects(), IResource.DEPTH_INFINITE, IResource.NONE)};
+ }
+ return new ResourceTraversal[] {new ResourceTraversal(new IResource[] {resource}, IResource.DEPTH_INFINITE, IResource.NONE)};
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/refresh/win32/Convert.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.refresh.win32;
+
+import java.io.*;
+
+/**
+ * Performs character to byte conversion for passing strings to native win32
+ * methods.
+ */
+public class Convert {
+
+ /*
+ * Obtains the default encoding on this platform
+ */
+ private static String defaultEncoding=
+ new InputStreamReader(new ByteArrayInputStream(new byte[0])).getEncoding();
+
+ /**
+ * Converts the given String to bytes using the platforms default
+ * encoding.
+ *
+ * @param target The String to be converted, can not be <code>null</code>.
+ * @return byte[] The resulting bytes, or <code>null</code>.
+ */
+ /**
+ * Calling String.getBytes() creates a new encoding object and other garbage.
+ * This can be avoided by calling String.getBytes(String encoding) instead.
+ */
+ public static byte[] toPlatformBytes(String target) {
+ if (defaultEncoding == null)
+ return target.getBytes();
+ // try to use the default encoding
+ try {
+ return target.getBytes(defaultEncoding);
+ } catch (UnsupportedEncodingException e) {
+ // null the default encoding so we don't try it again
+ defaultEncoding = null;
+ return target.getBytes();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/refresh/win32/Win32Monitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,589 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.refresh.win32;
+
+import java.io.File;
+import java.util.*;
+import org.eclipse.core.internal.refresh.RefreshManager;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.refresh.IRefreshMonitor;
+import org.eclipse.core.resources.refresh.IRefreshResult;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+
+/**
+ * A monitor that works on Win32 platforms. Provides simple notification of
+ * entire trees by reporting that the root of the tree has changed to depth
+ * DEPTH_INFINITE.
+ */
+class Win32Monitor extends Job implements IRefreshMonitor {
+ private static final long RESCHEDULE_DELAY = 1000;
+
+ /**
+ * A ChainedHandle is a linked list of handles.
+ */
+ protected abstract class ChainedHandle extends Handle {
+ private ChainedHandle next;
+ private ChainedHandle previous;
+
+ public abstract boolean exists();
+
+ public ChainedHandle getNext() {
+ return next;
+ }
+
+ public ChainedHandle getPrevious() {
+ return previous;
+ }
+
+ public void setNext(ChainedHandle next) {
+ this.next = next;
+ }
+
+ public void setPrevious(ChainedHandle previous) {
+ this.previous = previous;
+ }
+ }
+
+ protected class FileHandle extends ChainedHandle {
+ private File file;
+
+ public FileHandle(File file) {
+ this.file = file;
+ }
+
+ public boolean exists() {
+ return file.exists();
+ }
+
+ public void handleNotification() {
+ if (!isOpen())
+ return;
+ ChainedHandle next = getNext();
+ if (next != null) {
+ if (next.isOpen()) {
+ if (!next.exists()) {
+ if (next instanceof LinkedResourceHandle) {
+ next.close();
+ LinkedResourceHandle linkedResourceHandle = (LinkedResourceHandle) next;
+ linkedResourceHandle.postRefreshRequest();
+ } else {
+ next.close();
+ }
+ ChainedHandle previous = getPrevious();
+ if (previous != null)
+ previous.open();
+ }
+ } else {
+ next.open();
+ if (next.isOpen()) {
+ Handle previous = getPrevious();
+ previous.close();
+ if (next instanceof LinkedResourceHandle)
+ ((LinkedResourceHandle) next).postRefreshRequest();
+ }
+ }
+ }
+ findNextChange();
+ }
+
+ public void open() {
+ if (!isOpen()) {
+ Handle next = getNext();
+ if (next != null && next.isOpen()) {
+ openHandleOn(file);
+ } else {
+ if (exists()) {
+ openHandleOn(file);
+ }
+ Handle previous = getPrevious();
+ if (previous != null) {
+ previous.open();
+ }
+ }
+ }
+ }
+ }
+
+ protected abstract class Handle {
+ protected long handleValue;
+
+ public Handle() {
+ handleValue = Win32Natives.INVALID_HANDLE_VALUE;
+ }
+
+ public void close() {
+ if (isOpen()) {
+ if (!Win32Natives.FindCloseChangeNotification(handleValue)) {
+ int error = Win32Natives.GetLastError();
+ if (error != Win32Natives.ERROR_INVALID_HANDLE)
+ addException(NLS.bind(Messages.WM_errCloseHandle, Integer.toString(error)));
+ }
+ if (RefreshManager.DEBUG)
+ System.out.println(DEBUG_PREFIX + "removed handle: " + handleValue); //$NON-NLS-1$
+ handleValue = Win32Natives.INVALID_HANDLE_VALUE;
+ }
+ }
+
+ private long createHandleValue(String path, boolean monitorSubtree, int flags) {
+ long handle = Win32Natives.FindFirstChangeNotification(path, monitorSubtree, flags);
+ if (handle == Win32Natives.INVALID_HANDLE_VALUE) {
+ int error = Win32Natives.GetLastError();
+ addException(NLS.bind(Messages.WM_errCreateHandle, path, Integer.toString(error)));
+ }
+ return handle;
+ }
+
+ public void destroy() {
+ close();
+ }
+
+ protected void findNextChange() {
+ if (!Win32Natives.FindNextChangeNotification(handleValue)) {
+ int error = Win32Natives.GetLastError();
+ if (error != Win32Natives.ERROR_INVALID_HANDLE && error != Win32Natives.ERROR_SUCCESS) {
+ addException(NLS.bind(Messages.WM_errFindChange, Integer.toString(error)));
+ }
+ removeHandle(this);
+ }
+ }
+
+ public long getHandleValue() {
+ return handleValue;
+ }
+
+ public abstract void handleNotification();
+
+ public boolean isOpen() {
+ return handleValue != Win32Natives.INVALID_HANDLE_VALUE;
+ }
+
+ public abstract void open();
+
+ protected void openHandleOn(File file) {
+ openHandleOn(file.getAbsolutePath(), false);
+ }
+
+ protected void openHandleOn(IResource resource) {
+ openHandleOn(resource.getLocation().toOSString(), true);
+ }
+
+ private void openHandleOn(String path, boolean subtree) {
+ setHandleValue(createHandleValue(path, subtree, Win32Natives.FILE_NOTIFY_CHANGE_FILE_NAME | Win32Natives.FILE_NOTIFY_CHANGE_DIR_NAME | Win32Natives.FILE_NOTIFY_CHANGE_LAST_WRITE | Win32Natives.FILE_NOTIFY_CHANGE_SIZE));
+ if (isOpen()) {
+ fHandleValueToHandle.put(new Long(getHandleValue()), this);
+ setHandleValueArrays(createHandleArrays());
+ } else {
+ close();
+ }
+ }
+
+ protected void postRefreshRequest(IResource resource) {
+ //native callback occurs even if resource was changed within workspace
+ if (!resource.isSynchronized(IResource.DEPTH_INFINITE))
+ refreshResult.refresh(resource);
+ }
+
+ public void setHandleValue(long handleValue) {
+ this.handleValue = handleValue;
+ }
+ }
+
+ protected class LinkedResourceHandle extends ChainedHandle {
+ private List fileHandleChain;
+ private IResource resource;
+
+ /**
+ * @param resource
+ */
+ public LinkedResourceHandle(IResource resource) {
+ this.resource = resource;
+ createFileHandleChain();
+ }
+
+ protected void createFileHandleChain() {
+ fileHandleChain = new ArrayList(1);
+ File file = new File(resource.getLocation().toOSString());
+ file = file.getParentFile();
+ while (file != null) {
+ fileHandleChain.add(0, new FileHandle(file));
+ file = file.getParentFile();
+ }
+ int size = fileHandleChain.size();
+ for (int i = 0; i < size; i++) {
+ ChainedHandle handle = (ChainedHandle) fileHandleChain.get(i);
+ handle.setPrevious((i > 0) ? (ChainedHandle) fileHandleChain.get(i - 1) : null);
+ handle.setNext((i + 1 < size) ? (ChainedHandle) fileHandleChain.get(i + 1) : this);
+ }
+ setPrevious((size > 0) ? (ChainedHandle) fileHandleChain.get(size - 1) : null);
+ }
+
+ public void destroy() {
+ super.destroy();
+ for (Iterator i = fileHandleChain.iterator(); i.hasNext();) {
+ Handle handle = (Handle) i.next();
+ handle.destroy();
+ }
+ }
+
+ public boolean exists() {
+ IPath location = resource.getLocation();
+ return location == null ? false : location.toFile().exists();
+ }
+
+ public void handleNotification() {
+ if (isOpen()) {
+ postRefreshRequest(resource);
+ findNextChange();
+ }
+ }
+
+ public void open() {
+ if (!isOpen()) {
+ if (exists()) {
+ openHandleOn(resource);
+ }
+ FileHandle handle = (FileHandle) getPrevious();
+ if (handle != null && !handle.isOpen()) {
+ handle.open();
+ }
+ }
+ }
+
+ public void postRefreshRequest() {
+ postRefreshRequest(resource);
+ }
+ }
+
+ protected class ResourceHandle extends Handle {
+ private IResource resource;
+
+ public ResourceHandle(IResource resource) {
+ super();
+ this.resource = resource;
+ }
+
+ public IResource getResource() {
+ return resource;
+ }
+
+ public void handleNotification() {
+ if (isOpen()) {
+ postRefreshRequest(resource);
+ findNextChange();
+ }
+ }
+
+ public void open() {
+ if (!isOpen()) {
+ openHandleOn(resource);
+ }
+ }
+ }
+
+ private static final String DEBUG_PREFIX = "Win32RefreshMonitor: "; //$NON-NLS-1$
+ private static final int WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT = 300;
+ /**
+ * Any errors that have occurred
+ */
+ protected MultiStatus errors;
+ /**
+ * Arrays of handles, split evenly when the number of handles is larger
+ * than Win32Natives.MAXIMUM_WAIT_OBJECTS
+ */
+ protected long[][] fHandleValueArrays;
+ /**
+ * Mapping of handles (java.lang.Long) to absolute paths
+ * (java.lang.String).
+ */
+ protected Map fHandleValueToHandle;
+ protected IRefreshResult refreshResult;
+
+ /*
+ * Creates a new monitor. @param result A result that will recieve refresh
+ * callbacks and error notifications
+ */
+ public Win32Monitor(IRefreshResult result) {
+ super(Messages.WM_jobName);
+ this.refreshResult = result;
+ setPriority(Job.DECORATE);
+ setSystem(true);
+ fHandleValueToHandle = new HashMap(1);
+ setHandleValueArrays(createHandleArrays());
+ }
+
+ /**
+ * Logs an exception
+ */
+ protected synchronized void addException(String message) {
+ if (errors == null) {
+ String msg = Messages.WM_errors;
+ errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, 1, msg, null);
+ }
+ errors.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, message, null));
+ }
+
+ /*
+ * Splits the given array into arrays of length no greater than <code> max
+ * </code> . The lengths of the sub arrays differ in size by no more than
+ * one element. <p> Examples: <ul><li> If an array of size 11 is split
+ * with a max of 4, the resulting arrays are of size 4, 4, and 3. </li>
+ * <li> If an array of size 18 is split with a max of 5, the resulting
+ * arrays are of size 5, 5, 4, and 4. </li></ul>
+ */
+ private long[][] balancedSplit(final long[] array, final int max) {
+ int elementCount = array.length;
+ // want to handle [1, max] rather than [0, max)
+ int subArrayCount = ((elementCount - 1) / max) + 1;
+ int subArrayBaseLength = elementCount / subArrayCount;
+ int overflow = elementCount % subArrayCount;
+ long[][] result = new long[subArrayCount][];
+ int count = 0;
+ for (int i = 0; i < subArrayCount; i++) {
+ int subArrayLength = subArrayBaseLength + (overflow-- > 0 ? 1 : 0);
+ long[] subArray = new long[subArrayLength];
+ for (int j = 0; j < subArrayLength; j++) {
+ subArray[j] = array[count++];
+ }
+ result[i] = subArray;
+ }
+ return result;
+ }
+
+ private Handle createHandle(IResource resource) {
+ if (resource.isLinked())
+ return new LinkedResourceHandle(resource);
+ return new ResourceHandle(resource);
+ }
+
+ /*
+ * Since the Win32Natives.WaitForMultipleObjects(...) method cannot accept
+ * more than a certain number of objects, we are forced to split the array
+ * of objects to monitor and monitor each one individually. <p> This method
+ * splits the list of handles into arrays no larger than
+ * Win32Natives.MAXIMUM_WAIT_OBJECTS. The arrays are balenced so that they
+ * differ in size by no more than one element.
+ */
+ protected long[][] createHandleArrays() {
+ long[] handles;
+ // synchronized: in order to protect the map during iteration
+ synchronized (fHandleValueToHandle) {
+ Set keys = fHandleValueToHandle.keySet();
+ int size = keys.size();
+ if (size == 0) {
+ return new long[0][0];
+ }
+ handles = new long[size];
+ int count = 0;
+ for (Iterator i = keys.iterator(); i.hasNext();) {
+ handles[count++] = ((Long) i.next()).longValue();
+ }
+ }
+ return balancedSplit(handles, Win32Natives.MAXIMUM_WAIT_OBJECTS);
+ }
+
+ private Handle getHandle(IResource resource) {
+ if (resource == null) {
+ return null;
+ }
+ // synchronized: in order to protect the map during iteration
+ synchronized (fHandleValueToHandle) {
+ for (Iterator i = fHandleValueToHandle.values().iterator(); i.hasNext();) {
+ Handle handle = (Handle) i.next();
+ if (handle instanceof ResourceHandle) {
+ ResourceHandle resourceHandle = (ResourceHandle) handle;
+ if (resourceHandle.getResource().equals(resource)) {
+ return handle;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Answers arrays of handles. The handles are split evenly when the number
+ * of handles becomes larger than Win32Natives.MAXIMUM_WAIT_OBJECTS.
+ * @return long[][]
+ */
+ private long[][] getHandleValueArrays() {
+ return fHandleValueArrays;
+ }
+
+ /**
+ * Adds a resource to be monitored by this native monitor
+ */
+ public boolean monitor(IResource resource) {
+ IPath location = resource.getLocation();
+ if (location == null) {
+ // cannot monitor remotely managed containers
+ return false;
+ }
+ Handle handle = createHandle(resource);
+ // synchronized: handle creation must be atomic
+ synchronized (this) {
+ handle.open();
+ }
+ if (!handle.isOpen()) {
+ //ignore errors if we can't even create a handle on the resource
+ //it will fall back to polling anyway
+ errors = null;
+ return false;
+ }
+ //make sure the job is running
+ schedule(RESCHEDULE_DELAY);
+ if (RefreshManager.DEBUG)
+ System.out.println(DEBUG_PREFIX + " added monitor for: " + resource); //$NON-NLS-1$
+ return true;
+ }
+
+ /**
+ * Removes the handle from the <code>fHandleValueToHandle</code> map.
+ *
+ * @param handle
+ * a handle, not <code>null</code>
+ */
+ protected void removeHandle(Handle handle) {
+ List handles = new ArrayList(1);
+ handles.add(handle);
+ removeHandles(handles);
+ }
+
+ /**
+ * Removes all of the handles in the given collection from the <code>fHandleValueToHandle</code>
+ * map. If collections from the <code>fHandleValueToHandle</code> map are
+ * used, copy them before passing them in as this method modifies the
+ * <code>fHandleValueToHandle</code> map.
+ *
+ * @param handles
+ * a collection of handles, not <code>null</code>
+ */
+ private void removeHandles(Collection handles) {
+ // synchronized: protect the array, removal must be atomic
+ synchronized (this) {
+ for (Iterator i = handles.iterator(); i.hasNext();) {
+ Handle handle = (Handle) i.next();
+ fHandleValueToHandle.remove(new Long(handle.getHandleValue()));
+ handle.destroy();
+ }
+ setHandleValueArrays(createHandleArrays());
+ }
+ }
+
+ /*
+ * @see java.lang.Runnable#run()
+ */
+ protected IStatus run(IProgressMonitor monitor) {
+ long start = -System.currentTimeMillis();
+ if (RefreshManager.DEBUG)
+ System.out.println(DEBUG_PREFIX + "job started."); //$NON-NLS-1$
+ try {
+ long[][] handleArrays = getHandleValueArrays();
+ monitor.beginTask(Messages.WM_beginTask, handleArrays.length);
+ // If changes occur to the list of handles,
+ // ignore them until the next time through the loop.
+ for (int i = 0, length = handleArrays.length; i < length; i++) {
+ if (monitor.isCanceled())
+ return Status.CANCEL_STATUS;
+ waitForNotification(handleArrays[i]);
+ monitor.worked(1);
+ }
+ } finally {
+ monitor.done();
+ start += System.currentTimeMillis();
+ if (RefreshManager.DEBUG)
+ System.out.println(DEBUG_PREFIX + "job finished in: " + start + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ //always reschedule the job - so it will come back after errors or cancelation
+ //make sure it doesn't hog more that 5% of CPU
+ long delay = Math.max(RESCHEDULE_DELAY, start * 30);
+ if (RefreshManager.DEBUG)
+ System.out.println(DEBUG_PREFIX + "rescheduling in: " + delay / 1000 + " seconds"); //$NON-NLS-1$ //$NON-NLS-2$
+ final Bundle bundle = Platform.getBundle(ResourcesPlugin.PI_RESOURCES);
+ //if the bundle is null then the framework has shutdown - just bail out completely (bug 98219)
+ if (bundle == null)
+ return Status.OK_STATUS;
+ //don't reschedule the job if the resources plugin has been shut down
+ if (bundle.getState() == Bundle.ACTIVE)
+ schedule(delay);
+ MultiStatus result = errors;
+ errors = null;
+ //just log native refresh failures
+ if (result != null && !result.isOK())
+ ResourcesPlugin.getPlugin().getLog().log(result);
+ return Status.OK_STATUS;
+ }
+
+ protected void setHandleValueArrays(long[][] arrays) {
+ fHandleValueArrays = arrays;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
+ */
+ public boolean shouldRun() {
+ return !fHandleValueToHandle.isEmpty();
+ }
+
+ /*
+ * @see org.eclipse.core.resources.refresh.IRefreshMonitor#unmonitor(IContainer)
+ */
+ public void unmonitor(IResource resource) {
+ if (resource == null) {
+ // resource == null means stop monitoring all resources
+ synchronized (fHandleValueToHandle) {
+ removeHandles(new ArrayList(fHandleValueToHandle.values()));
+ }
+ } else {
+ Handle handle = getHandle(resource);
+ if (handle != null)
+ removeHandle(handle);
+ }
+ //stop the job if there are no more handles
+ if (fHandleValueToHandle.isEmpty())
+ cancel();
+ }
+
+ /**
+ * Performs the native call to wait for notification on one of the given
+ * handles.
+ *
+ * @param handleValues
+ * an array of handles, it must contain no duplicates.
+ */
+ private void waitForNotification(long[] handleValues) {
+ int handleCount = handleValues.length;
+ int index = Win32Natives.WaitForMultipleObjects(handleCount, handleValues, false, WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT);
+ if (index == Win32Natives.WAIT_TIMEOUT) {
+ // nothing happened.
+ return;
+ }
+ if (index == Win32Natives.WAIT_FAILED) {
+ // we ran into a problem
+ int error = Win32Natives.GetLastError();
+ if (error != Win32Natives.ERROR_INVALID_HANDLE && error != Win32Natives.ERROR_SUCCESS) {
+ addException(NLS.bind(Messages.WM_nativeErr, Integer.toString(error)));
+ refreshResult.monitorFailed(this, null);
+ }
+ return;
+ }
+ // a change occurred
+ // WaitForMultipleObjects returns WAIT_OBJECT_0 + index
+ index -= Win32Natives.WAIT_OBJECT_0;
+ Handle handle = (Handle) fHandleValueToHandle.get(new Long(handleValues[index]));
+ if (handle != null)
+ handle.handleNotification();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/refresh/win32/Win32Natives.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.refresh.win32;
+
+
+/**
+ * Hooks for native methods involved with win32 auto-refresh callbacks.
+ */
+public class Win32Natives {
+ /* general purpose */
+ /**
+ * A general use constant expressing the value of an
+ * invalid handle.
+ */
+ public static final long INVALID_HANDLE_VALUE;
+ /**
+ * An error constant which indicates that the previous function
+ * succeeded.
+ */
+ public static final int ERROR_SUCCESS;
+ /**
+ * An error constant which indicates that a handle is or has become
+ * invalid.
+ */
+ public static final int ERROR_INVALID_HANDLE;
+ /**
+ * The combination of all of the error constants.
+ */
+ public static int FILE_NOTIFY_ALL;
+ /**
+ * A constant which indicates the maximum number of objects
+ * that can be passed into WaitForMultipleObjects.
+ */
+ public static final int MAXIMUM_WAIT_OBJECTS;
+ /**
+ * A constant which indicates the maximum length of a pathname.
+ */
+ public static final int MAX_PATH;
+ /**
+ * A constant which expresses the concept of the infinite.
+ */
+ public static final int INFINITE;
+
+ /* wait return values */
+ /**
+ * A constant used returned WaitForMultipleObjects when the function times out.
+ */
+ public static final int WAIT_TIMEOUT;
+ /**
+ * A constant used by WaitForMultipleObjects to indicate the object which was
+ * signaled.
+ */
+ public static final int WAIT_OBJECT_0;
+ /**
+ * A constant returned by WaitForMultipleObjects which indicates
+ * that the wait failed.
+ */
+ public static final int WAIT_FAILED;
+
+ /* wait notification filter masks */
+ /**
+ * Change filter for monitoring file rename, creation or deletion.
+ */
+ public static final int FILE_NOTIFY_CHANGE_FILE_NAME;
+ /**
+ * Change filter for monitoring directory creation or deletion.
+ */
+ public static final int FILE_NOTIFY_CHANGE_DIR_NAME;
+ /**
+ * Change filter for monitoring file/directory attribute changes.
+ */
+ public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ /**
+ * Change filter for monitoring file size changes.
+ */
+ public static final int FILE_NOTIFY_CHANGE_SIZE;
+ /**
+ * Change filter for monitoring the file write timestamp
+ */
+ public static final int FILE_NOTIFY_CHANGE_LAST_WRITE;
+ /**
+ * Change filter for monitoring the security descriptors
+ * of files.
+ */
+ public static final int FILE_NOTIFY_CHANGE_SECURITY;
+
+ /**
+ * Flag indicating whether or not the OS supports unicode calls.
+ */
+ public static final boolean UNICODE;
+ /*
+ * Make requests to set the constants.
+ */
+ static {
+ System.loadLibrary("win32refresh"); //$NON-NLS-1$
+ UNICODE= IsUnicode();
+ INVALID_HANDLE_VALUE= INVALID_HANDLE_VALUE();
+ ERROR_SUCCESS= ERROR_SUCCESS();
+ ERROR_INVALID_HANDLE= ERROR_INVALID_HANDLE();
+
+ MAXIMUM_WAIT_OBJECTS= MAXIMUM_WAIT_OBJECTS();
+ MAX_PATH= MAX_PATH();
+ INFINITE= INFINITE();
+
+ WAIT_TIMEOUT= WAIT_TIMEOUT();
+ WAIT_OBJECT_0= WAIT_OBJECT_0();
+ WAIT_FAILED= WAIT_FAILED();
+
+ FILE_NOTIFY_CHANGE_FILE_NAME= FILE_NOTIFY_CHANGE_FILE_NAME();
+ FILE_NOTIFY_CHANGE_DIR_NAME= FILE_NOTIFY_CHANGE_DIR_NAME();
+ FILE_NOTIFY_CHANGE_ATTRIBUTES= FILE_NOTIFY_CHANGE_ATTRIBUTES();
+ FILE_NOTIFY_CHANGE_SIZE= FILE_NOTIFY_CHANGE_SIZE();
+ FILE_NOTIFY_CHANGE_LAST_WRITE= FILE_NOTIFY_CHANGE_LAST_WRITE();
+ FILE_NOTIFY_CHANGE_SECURITY= FILE_NOTIFY_CHANGE_SECURITY();
+ FILE_NOTIFY_ALL=
+ FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE |
+ FILE_NOTIFY_CHANGE_LAST_WRITE |
+ FILE_NOTIFY_CHANGE_SECURITY;
+ }
+
+ /**
+ * Creates a change notification object for the given path. The notification
+ * object allows the client to monitor changes to the directory and the
+ * subtree under the directory using FindNextChangeNotification or
+ * WaitForMultipleObjects.
+ * <p>
+ * If the OS supports unicode the path must be no longer than 2^15 - 1 characters.
+ * Otherwise, the path cannot be longer than MAX_PATH. In either case, if the given
+ * path is too long ERROR_INVALID_HANDLE is returned.
+ *
+ * @param lpPathName The path of the file.
+ * @param bWatchSubtree If <code>true</code>, specifies that the entire
+ * tree under the given path should be monitored. If <code>false</code>
+ * specifies that just the named path should be monitored.
+ * @param dwNotifyFilter Any combination of FILE_NOTIFY_CHANGE_FILE_NAME,
+ * FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_ATTRIBUTES,
+ * FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_CHANGE_LAST_WRITE, or
+ * FILE_NOTIFY_CHANGE_SECURITY.
+ * @return long The handle to the find change notification object or
+ * ERROR_INVALID_HANDLE if the attempt fails.
+ */
+ public static long FindFirstChangeNotification(String lpPathName, boolean bWatchSubtree, int dwNotifyFilter) {
+ if (UNICODE)
+ return FindFirstChangeNotificationW(lpPathName, bWatchSubtree, dwNotifyFilter);
+ return FindFirstChangeNotificationA(Convert.toPlatformBytes(lpPathName), bWatchSubtree, dwNotifyFilter);
+ }
+ /**
+ * Creates a change notification object for the given path. This notification object
+ * allows the client to monitor changes to the directory and the subtree
+ * under the directory using FindNextChangeNotification or
+ * WaitForMultipleObjects.
+ *
+ * @param lpPathName The path to the directory to be monitored. Cannot be <code>null</code>,
+ * or longer than 2^15 - 1 characters.
+ * @param bWatchSubtree If <code>true</code>, specifies that the entire
+ * tree under the given path should be monitored. If <code>false</code>
+ * specifies that just the named path should be monitored.
+ * @param dwNotifyFilter Any combination of FILE_NOTIFY_CHANGE_FILE_NAME,
+ * FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_ATTRIBUTES,
+ * FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_CHANGE_LAST_WRITE, or
+ * FILE_NOTIFY_CHANGE_SECURITY.
+ * @return long The handle to the find change notification object or
+ * ERROR_INVALID_HANDLE if the attempt fails.
+ */
+ private static native long FindFirstChangeNotificationW(String lpPathName, boolean bWatchSubtree, int dwNotifyFilter);
+
+ /**
+ * Creates a change notification object for the given path. This notification object
+ * allows the client to monitor changes to the directory and the subtree
+ * under the directory using FindNextChangeNotification or
+ * WaitForMultipleObjects.
+ *
+ * @param lpPathName The path to the directory to be monitored, cannot be <code>null</code>,
+ * or be longer
+ * than MAX_PATH. This path must be in platform bytes converted.
+ * @param bWatchSubtree If <code>true</code>, specifies that the entire
+ * tree under the given path should be monitored. If <code>false</code>
+ * specifies that just the named path should be monitored.
+ * @param dwNotifyFilter Any combination of FILE_NOTIFY_CHANGE_FILE_NAME,
+ * FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_ATTRIBUTES,
+ * FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_CHANGE_LAST_WRITE, or
+ * FILE_NOTIFY_CHANGE_SECURITY.
+ * @return long The handle to the find change notification object or
+ * ERROR_INVALID_HANDLE if the attempt fails.
+ */
+ private static native long FindFirstChangeNotificationA(byte[] lpPathName, boolean bWatchSubtree, int dwNotifyFilter);
+
+
+ /**
+ * Stops and disposes of the change notification object that corresponds to the given
+ * handle. The handle cannot be used in future calls to FindNextChangeNotification or
+ * WaitForMultipleObjects
+ *
+ * @param hChangeHandle a handle which was created with FindFirstChangeNotification
+ * @return boolean <code>true</code> if the method succeeds, <code>false</code>
+ * otherwise.
+ */
+ public static native boolean FindCloseChangeNotification(long hChangeHandle);
+
+ /**
+ * Requests that the next change detected be signaled. This method should only be
+ * called after FindFirstChangeNotification or WaitForMultipleObjects. Once this
+ * method has been called on a given handle, further notification requests can be made
+ * through the WaitForMultipleObjects call.
+ * @param hChangeHandle a handle which was created with FindFirstChangeNotification
+ * @return boolean <code>true</code> if the method succeeds, <code>false</code> otherwise.
+ */
+ public static native boolean FindNextChangeNotification(long hChangeHandle);
+
+ /**
+ * Returns when one of the following occurs.
+ * <ul>
+ * <li>One of the objects is signaled, when bWaitAll is <code>false</code></li>
+ * <li>All of the objects are signaled, when bWaitAll is <code>true</code></li>
+ * <li>The timeout interval of dwMilliseconds elapses.</li>
+ * </ul>
+ * @param nCount The number of handles, cannot be greater than MAXIMUM_WAIT_OBJECTS.
+ * @param lpHandles The array of handles to objects to be waited upon cannot contain
+ * duplicate handles.
+ * @param bWaitAll If <code>true</code> requires all objects to be signaled before this
+ * method returns. If <code>false</code>, indicates that only one object need be
+ * signaled for this method to return.
+ * @param dwMilliseconds A timeout value in milliseconds. If zero, the function tests
+ * the objects and returns immediately. If INFINITE, the function will only return
+ * when the objects have been signaled.
+ * @return int WAIT_TIMEOUT when the function times out before recieving a signal.
+ * WAIT_OBJECT_0 + n when a signal for the handle at index n. WAIT_FAILED when this
+ * function fails.
+ */
+ public static native int WaitForMultipleObjects(int nCount, long[] lpHandles, boolean bWaitAll, int dwMilliseconds);
+
+ /**
+ * Answers <code>true</code> if the operating system supports
+ * long filenames.
+ * @return boolean <code>true</code> if the operating system supports
+ * long filenames, <code>false</code> otherwise.
+ */
+ private static native boolean IsUnicode();
+
+ /**
+ * Answers the last error set in the current thread.
+ * @return int the last error
+ */
+ public static native int GetLastError();
+
+ /**
+ * Returns the constant FILE_NOTIFY_CHANGE_LAST_WRITE.
+ * @return int
+ */
+ private static native int FILE_NOTIFY_CHANGE_LAST_WRITE();
+
+ /**
+ * Returns the constant FILE_NOTIFY_CHANGE_DIR_NAME.
+ * @return int
+ */
+ private static native int FILE_NOTIFY_CHANGE_DIR_NAME();
+
+ /**
+ * Returns the constant FILE_NOTIFY_CHANGE_ATTRIBUTES.
+ * @return int
+ */
+ private static native int FILE_NOTIFY_CHANGE_ATTRIBUTES();
+
+ /**
+ * Returns the constant FILE_NOTIFY_CHANGE_SIZE.
+ * @return int
+ */
+ private static native int FILE_NOTIFY_CHANGE_SIZE();
+
+ /**
+ * Returns the constant FILE_NOTIFY_CHANGE_FILE_NAME.
+ * @return int
+ */
+ private static native int FILE_NOTIFY_CHANGE_FILE_NAME();
+
+ /**
+ * Returns the constant FILE_NOTIFY_CHANGE_SECURITY.
+ * @return int
+ */
+ private static native int FILE_NOTIFY_CHANGE_SECURITY();
+
+ /**
+ * Returns the constant MAXIMUM_WAIT_OBJECTS.
+ * @return int
+ */
+ private static native int MAXIMUM_WAIT_OBJECTS();
+
+ /**
+ * Returns the constant MAX_PATH.
+ * @return int
+ */
+ private static native int MAX_PATH();
+
+ /**
+ * Returns the constant INFINITE.
+ * @return int
+ */
+ private static native int INFINITE();
+
+ /**
+ * Returns the constant WAIT_OBJECT_0.
+ * @return int
+ */
+ private static native int WAIT_OBJECT_0();
+
+ /**
+ * Returns the constant WAIT_FAILED.
+ * @return int
+ */
+ private static native int WAIT_FAILED();
+
+ /**
+ * Returns the constant WAIT_TIMEOUT.
+ * @return int
+ */
+ private static native int WAIT_TIMEOUT();
+
+ /**
+ * Returns the constant ERROR_INVALID_HANDLE.
+ * @return int
+ */
+ private static native int ERROR_INVALID_HANDLE();
+
+ /**
+ * Returns the constant ERROR_SUCCESS.
+ * @return int
+ */
+ private static native int ERROR_SUCCESS();
+
+ /**
+ * Returns the constant INVALID_HANDLE_VALUE.
+ * @return long
+ */
+ private static native long INVALID_HANDLE_VALUE();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/refresh/win32/Win32RefreshProvider.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.refresh.win32;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.refresh.*;
+
+/**
+ * The <code>Win32RefreshProvider</code> creates monitors that
+ * can monitor drives on Win32 platforms.
+ *
+ * @see org.eclipse.core.resources.refresh.RefreshProvider
+ */
+public class Win32RefreshProvider extends RefreshProvider {
+ private Win32Monitor monitor;
+
+ /**
+ * Creates a standard Win32 monitor if the given resource is local.
+ *
+ * @see org.eclipse.core.resources.refresh.RefreshProvider#installMonitor(IResource,IRefreshResult)
+ */
+ public IRefreshMonitor installMonitor(IResource resource, IRefreshResult result) {
+ if (resource.getLocation() == null || !resource.exists() || resource.getType() == IResource.FILE)
+ return null;
+ if (monitor == null)
+ monitor = new Win32Monitor(result);
+ if (monitor.monitor(resource))
+ return monitor;
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/ArrayIterator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * An object that iterates over the elements of an array
+ */
+public class ArrayIterator implements Iterator {
+ Object[] elements;
+ int index;
+ int lastElement;
+
+ /**
+ * Returns new array enumeration over the given object array
+ */
+ public ArrayIterator(Object[] elements) {
+ this(elements, 0, elements.length - 1);
+ }
+
+ /**
+ * Returns new array enumeration over the given object array
+ */
+ public ArrayIterator(Object[] elements, int firstElement, int lastElement) {
+ super();
+ this.elements = elements;
+ index = firstElement;
+ this.lastElement = lastElement;
+ }
+
+ /**
+ * Returns true if this enumeration contains more elements.
+ */
+ public boolean hasNext() {
+ return elements != null && index <= lastElement;
+ }
+
+ /**
+ * Returns the next element of this enumeration.
+ * @exception NoSuchElementException if no more elements exist.
+ */
+ public Object next() throws NoSuchElementException {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ return elements[index++];
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/BitMask.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+/**
+ * Utility methods for manipulating bit masks
+ */
+public class BitMask {
+ /**
+ * Returns true if all of the bits indicated by the mask are set.
+ */
+ public static boolean isSet(int flags, int mask) {
+ return (flags & mask) == mask;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Cache.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A cache that keeps a collection of at most maximumCapacity+threshold entries.
+ * When the number of entries exceeds that limit, least recently used entries are removed
+ * so the current size is the same as the maximum capacity.
+ */
+public class Cache {
+ public class Entry implements KeyedHashSet.KeyedElement {
+ Object cached;
+ Object key;
+ Entry next;
+ Entry previous;
+ long timestamp;
+
+ public Entry(Object key, Object cached, long timestamp) {
+ this.key = key;
+ this.cached = cached;
+ this.timestamp = timestamp;
+ }
+
+ public boolean compare(KeyedHashSet.KeyedElement other) {
+ if (!(other instanceof Entry))
+ return false;
+ Entry otherEntry = (Entry) other;
+ return key.equals(otherEntry.key);
+ }
+
+ /* Removes this entry from the cache */
+ public void discard() {
+ unchain();
+ cached = null;
+ entries.remove(this);
+ }
+
+ public Object getCached() {
+ return cached;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public int getKeyHashCode() {
+ return key.hashCode();
+ }
+
+ public Entry getNext() {
+ return next;
+ }
+
+ public Entry getPrevious() {
+ return previous;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public boolean isHead() {
+ return previous == null;
+ }
+
+ public boolean isTail() {
+ return next == null;
+ }
+
+ /* Inserts into the head of the list */
+ void makeHead() {
+ Entry oldHead = head;
+ head = this;
+ next = oldHead;
+ previous = null;
+ if (oldHead == null)
+ tail = this;
+ else
+ oldHead.previous = this;
+ }
+
+ public void setCached(Object cached) {
+ this.cached = cached;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String toString() {
+ return key + " -> " + cached + " [" + timestamp + ']'; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /* Removes from the linked list, but not from the entries collection */
+ void unchain() {
+ // it may be in the tail
+ if (tail == this)
+ tail = previous;
+ else
+ next.previous = previous;
+ // it may be in the head
+ if (head == this)
+ head = next;
+ else
+ previous.next = next;
+ }
+ }
+
+ KeyedHashSet entries;
+ Entry head;
+ private int maximumCapacity;
+ Entry tail;
+ private double threshold;
+
+ public Cache(int maximumCapacity) {
+ this(Math.min(KeyedHashSet.MINIMUM_SIZE, maximumCapacity), maximumCapacity, 0.25);
+ }
+
+ public Cache(int initialCapacity, int maximumCapacity, double threshold) {
+ Assert.isTrue(maximumCapacity >= initialCapacity, "maximum capacity < initial capacity"); //$NON-NLS-1$
+ Assert.isTrue(threshold >= 0 && threshold <= 1, "threshold should be between 0 and 1"); //$NON-NLS-1$
+ Assert.isTrue(initialCapacity > 0, "initial capacity must be greater than zero"); //$NON-NLS-1$
+ entries = new KeyedHashSet(initialCapacity);
+ this.maximumCapacity = maximumCapacity;
+ this.threshold = threshold;
+ }
+
+ public void addEntry(Object key, Object toCache) {
+ addEntry(key, toCache, 0);
+ }
+
+ public Entry addEntry(Object key, Object toCache, long timestamp) {
+ Entry newHead = (Entry) entries.getByKey(key);
+ if (newHead == null)
+ entries.add(newHead = new Entry(key, toCache, timestamp));
+ newHead.cached = toCache;
+ newHead.timestamp = timestamp;
+ newHead.makeHead();
+ int extraEntries = entries.size() - maximumCapacity;
+ if (extraEntries > maximumCapacity * threshold)
+ // we have reached our limit - ensure we are under the maximum capacity
+ // by discarding older entries
+ packEntries(extraEntries);
+ return newHead;
+ }
+
+ public Entry getEntry(Object key) {
+ return getEntry(key, true);
+ }
+
+ public Entry getEntry(Object key, boolean update) {
+ Entry existing = (Entry) entries.getByKey(key);
+ if (existing == null)
+ return null;
+ if (!update)
+ return existing;
+ existing.unchain();
+ existing.makeHead();
+ return existing;
+ }
+
+ public Entry getHead() {
+ return head;
+ }
+
+ public Entry getTail() {
+ return tail;
+ }
+
+ private void packEntries(int extraEntries) {
+ // should remove in an ad-hoc way to get better performance
+ Entry current = tail;
+ for (; current != null && extraEntries > 0; extraEntries--) {
+ current.discard();
+ current = current.previous;
+ }
+ }
+
+ public long size() {
+ return entries.size();
+ }
+
+ public void discardAll() {
+ entries.clear();
+ }
+
+ public void dispose() {
+ discardAll();
+ entries = null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Convert.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.io.UnsupportedEncodingException;
+
+public class Convert {
+
+ /**
+ * Converts the string argument to a byte array.
+ */
+ public static String fromUTF8(byte[] b) {
+ String result;
+ try {
+ result = new String(b, "UTF8"); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ result = new String(b);
+ }
+ return result;
+ }
+
+ /**
+ * Converts the string argument to a byte array.
+ */
+ public static byte[] toUTF8(String s) {
+ byte[] result;
+ try {
+ result = s.getBytes("UTF8"); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ result = s.getBytes();
+ }
+ return result;
+ }
+
+ /**
+ * Performs conversion of a long value to a byte array representation.
+ *
+ * @see #bytesToLong(byte[])
+ */
+ public static byte[] longToBytes(long value) {
+
+ // A long value is 8 bytes in length.
+ byte[] bytes = new byte[8];
+
+ // Convert and copy value to byte array:
+ // -- Cast long to a byte to retrieve least significant byte;
+ // -- Left shift long value by 8 bits to isolate next byte to be converted;
+ // -- Repeat until all 8 bytes are converted (long = 64 bits).
+ // Note: In the byte array, the least significant byte of the long is held in
+ // the highest indexed array bucket.
+
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[(bytes.length - 1) - i] = (byte) value;
+ value >>>= 8;
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Performs conversion of a byte array to a long representation.
+ *
+ * @see #longToBytes(long)
+ */
+ public static long bytesToLong(byte[] value) {
+
+ long longValue = 0L;
+
+ // See method convertLongToBytes(long) for algorithm details.
+ for (int i = 0; i < value.length; i++) {
+ // Left shift has no effect thru first iteration of loop.
+ longValue <<= 8;
+ longValue ^= value[i] & 0xFF;
+ }
+
+ return longValue;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/FileUtil.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Martin Oberhuber (Wind River) - [44107] Add symbolic links to ResourceAttributes API
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.io.*;
+import java.net.URI;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Static utility methods for manipulating Files and URIs.
+ */
+public class FileUtil {
+ /**
+ * Singleton buffer created to prevent buffer creations in the
+ * transferStreams method. Used as an optimization, based on the assumption
+ * that multiple writes won't happen in a given instance of FileStore.
+ */
+ private static final byte[] buffer = new byte[8192];
+
+ /**
+ * Converts a ResourceAttributes object into an IFileInfo object.
+ * @param attributes The resource attributes
+ * @return The file info
+ */
+ public static IFileInfo attributesToFileInfo(ResourceAttributes attributes) {
+ IFileInfo fileInfo = EFS.createFileInfo();
+ fileInfo.setAttribute(EFS.ATTRIBUTE_READ_ONLY, attributes.isReadOnly());
+ fileInfo.setAttribute(EFS.ATTRIBUTE_EXECUTABLE, attributes.isExecutable());
+ fileInfo.setAttribute(EFS.ATTRIBUTE_ARCHIVE, attributes.isArchive());
+ fileInfo.setAttribute(EFS.ATTRIBUTE_HIDDEN, attributes.isHidden());
+ return fileInfo;
+ }
+
+ /**
+ * Converts an IPath into its canonical form for the local file system.
+ */
+ public static IPath canonicalPath(IPath path) {
+ if (path == null)
+ return null;
+ try {
+ final String pathString = path.toOSString();
+ final String canonicalPath = new java.io.File(pathString).getCanonicalPath();
+ //only create a new path if necessary
+ if (canonicalPath.equals(pathString))
+ return path;
+ return new Path(canonicalPath);
+ } catch (IOException e) {
+ return path;
+ }
+ }
+
+ /**
+ * Converts a URI into its canonical form.
+ */
+ public static URI canonicalURI(URI uri) {
+ if (uri == null)
+ return null;
+ if (EFS.SCHEME_FILE.equals(uri.getScheme())) {
+ //only create a new URI if it is different
+ final IPath inputPath = URIUtil.toPath(uri);
+ final IPath canonicalPath = canonicalPath(inputPath);
+ if (inputPath == canonicalPath)
+ return uri;
+ return URIUtil.toURI(canonicalPath);
+ }
+ return uri;
+ }
+
+ /**
+ * Returns true if the given file system locations overlap. If "bothDirections" is true,
+ * this means they are the same, or one is a proper prefix of the other. If "bothDirections"
+ * is false, this method only returns true if the locations are the same, or the first location
+ * is a prefix of the second. Returns false if the locations do not overlap
+ * Does the right thing with respect to case insensitive platforms.
+ */
+ private static boolean computeOverlap(IPath location1, IPath location2, boolean bothDirections) {
+ IPath one = location1;
+ IPath two = location2;
+ // If we are on a case-insensitive file system then convert to all lower case.
+ if (!Workspace.caseSensitive) {
+ one = new Path(location1.toOSString().toLowerCase());
+ two = new Path(location2.toOSString().toLowerCase());
+ }
+ return one.isPrefixOf(two) || (bothDirections && two.isPrefixOf(one));
+ }
+
+ /**
+ * Returns true if the given file system locations overlap. If "bothDirections" is true,
+ * this means they are the same, or one is a proper prefix of the other. If "bothDirections"
+ * is false, this method only returns true if the locations are the same, or the first location
+ * is a prefix of the second. Returns false if the locations do not overlap
+ */
+ private static boolean computeOverlap(URI location1, URI location2, boolean bothDirections) {
+ if (location1.equals(location2))
+ return true;
+ String scheme1 = location1.getScheme();
+ String scheme2 = location2.getScheme();
+ if (scheme1 == null ? scheme2 != null : !scheme1.equals(scheme2))
+ return false;
+ if (EFS.SCHEME_FILE.equals(scheme1) && EFS.SCHEME_FILE.equals(scheme2))
+ return computeOverlap(URIUtil.toPath(location1), URIUtil.toPath(location2), bothDirections);
+ IFileSystem system = null;
+ try {
+ system = EFS.getFileSystem(scheme1);
+ } catch (CoreException e) {
+ //handled below
+ }
+ if (system == null) {
+ //we are stuck with string comparison
+ String string1 = location1.toString();
+ String string2 = location2.toString();
+ return string1.startsWith(string2) || (bothDirections && string2.startsWith(string1));
+ }
+ IFileStore store1 = system.getStore(location1);
+ IFileStore store2 = system.getStore(location2);
+ return store1.equals(store2) || store1.isParentOf(store2) || (bothDirections && store2.isParentOf(store1));
+ }
+
+ /**
+ * Converts an IFileInfo object into a ResourceAttributes object.
+ * @param fileInfo The file info
+ * @return The resource attributes
+ */
+ public static ResourceAttributes fileInfoToAttributes(IFileInfo fileInfo) {
+ ResourceAttributes attributes = new ResourceAttributes();
+ attributes.setReadOnly(fileInfo.getAttribute(EFS.ATTRIBUTE_READ_ONLY));
+ attributes.setArchive(fileInfo.getAttribute(EFS.ATTRIBUTE_ARCHIVE));
+ attributes.setExecutable(fileInfo.getAttribute(EFS.ATTRIBUTE_EXECUTABLE));
+ attributes.setHidden(fileInfo.getAttribute(EFS.ATTRIBUTE_HIDDEN));
+ attributes.setSymbolicLink(fileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK));
+ return attributes;
+ }
+
+ /**
+ * Returns true if the given file system locations overlap, and false otherwise.
+ * Overlap means the locations are the same, or one is a proper prefix of the other.
+ */
+ public static boolean isOverlapping(URI location1, URI location2) {
+ return computeOverlap(location1, location2, true);
+ }
+
+ /**
+ * Returns true if location1 is the same as, or a proper prefix of, location2.
+ * Returns false otherwise.
+ */
+ public static boolean isPrefixOf(IPath location1, IPath location2) {
+ return computeOverlap(location1, location2, false);
+ }
+
+ /**
+ * Returns true if location1 is the same as, or a proper prefix of, location2.
+ * Returns false otherwise.
+ */
+ public static boolean isPrefixOf(URI location1, URI location2) {
+ return computeOverlap(location1, location2, false);
+ }
+
+ /**
+ * Closes a stream and ignores any resulting exception. This is useful
+ * when doing stream cleanup in a finally block where secondary exceptions
+ * are not worth logging.
+ */
+ public static void safeClose(InputStream in) {
+ try {
+ if (in != null)
+ in.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+
+ /**
+ * Closes a stream and ignores any resulting exception. This is useful
+ * when doing stream cleanup in a finally block where secondary exceptions
+ * are not worth logging.
+ */
+ public static void safeClose(OutputStream out) {
+ try {
+ if (out != null)
+ out.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+
+ /**
+ * Converts a URI to an IPath. Returns null if the URI cannot be represented
+ * as an IPath.
+ * <p>
+ * Note this method differs from URIUtil in its handling of relative URIs
+ * as being relative to path variables.
+ */
+ public static IPath toPath(URI uri) {
+ if (uri == null)
+ return null;
+ final String scheme = uri.getScheme();
+ // null scheme represents path variable
+ if (scheme == null || EFS.SCHEME_FILE.equals(scheme))
+ return new Path(uri.getSchemeSpecificPart());
+ return null;
+ }
+
+ public static final void transferStreams(InputStream source, OutputStream destination, String path, IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ /*
+ * Note: although synchronizing on the buffer is thread-safe,
+ * it may result in slower performance in the future if we want
+ * to allow concurrent writes.
+ */
+ synchronized (buffer) {
+ while (true) {
+ int bytesRead = -1;
+ try {
+ bytesRead = source.read(buffer);
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.localstore_failedReadDuringWrite, path);
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, new Path(path), msg, e);
+ }
+ if (bytesRead == -1)
+ break;
+ try {
+ destination.write(buffer, 0, bytesRead);
+ } catch (IOException e) {
+ String msg = NLS.bind(Messages.localstore_couldNotWrite, path);
+ throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, new Path(path), msg, e);
+ }
+ monitor.worked(1);
+ }
+ }
+ } finally {
+ safeClose(source);
+ safeClose(destination);
+ }
+ }
+
+ /**
+ * Not intended for instantiation.
+ */
+ private FileUtil() {
+ super();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/IStringPoolParticipant.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+/**
+ * A string pool participant is used for sharing strings between several
+ * unrelated parties. Typically a single <code>StringPool</code> instance
+ * will be created, and a group of participants will be asked to store their
+ * strings in the pool. This allows participants to share equal strings
+ * without creating explicit dependencies between each other.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see StringPool
+ * @since 3.1
+ */
+public interface IStringPoolParticipant {
+ /**
+ * Instructs this participant to share its strings in the provided
+ * pool.
+ */
+ public void shareStrings(StringPool pool);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/KeyedHashSet.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+
+/** Adapted from a homonym class in org.eclipse.osgi. A hash table of
+ * <code>KeyedElement</code>s.
+ */
+
+public class KeyedHashSet {
+ public interface KeyedElement {
+
+ public boolean compare(KeyedElement other);
+
+ public Object getKey();
+
+ public int getKeyHashCode();
+ }
+
+ protected static final int MINIMUM_SIZE = 7;
+ private int capacity;
+ protected int elementCount = 0;
+ protected KeyedElement[] elements;
+ protected boolean replace;
+
+ public KeyedHashSet(int capacity) {
+ this(capacity, true);
+ }
+
+ public KeyedHashSet(int capacity, boolean replace) {
+ elements = new KeyedElement[Math.max(MINIMUM_SIZE, capacity * 2)];
+ this.replace = replace;
+ this.capacity = capacity;
+ }
+
+ /**
+ * Adds an element to this set. If an element with the same key already exists,
+ * replaces it depending on the replace flag.
+ * @return true if the element was added/stored, false otherwise
+ */
+ public boolean add(KeyedElement element) {
+ int hash = hash(element);
+
+ // search for an empty slot at the end of the array
+ for (int i = hash; i < elements.length; i++) {
+ if (elements[i] == null) {
+ elements[i] = element;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return true;
+ }
+ if (elements[i].compare(element)) {
+ if (replace)
+ elements[i] = element;
+ return replace;
+ }
+ }
+
+ // search for an empty slot at the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ if (elements[i] == null) {
+ elements[i] = element;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return true;
+ }
+ if (elements[i].compare(element)) {
+ if (replace)
+ elements[i] = element;
+ return replace;
+ }
+ }
+
+ // if we didn't find a free slot, then try again with the expanded set
+ expand();
+ return add(element);
+ }
+
+ public void clear() {
+ elements = new KeyedElement[Math.max(MINIMUM_SIZE, capacity * 2)];
+ elementCount = 0;
+ }
+
+ /**
+ * The array isn't large enough so double its size and rehash
+ * all its current values.
+ */
+ protected void expand() {
+ KeyedElement[] oldElements = elements;
+ elements = new KeyedElement[elements.length * 2];
+
+ int maxArrayIndex = elements.length - 1;
+ for (int i = 0; i < oldElements.length; i++) {
+ KeyedElement element = oldElements[i];
+ if (element != null) {
+ int hash = hash(element);
+ while (elements[hash] != null) {
+ hash++;
+ if (hash > maxArrayIndex)
+ hash = 0;
+ }
+ elements[hash] = element;
+ }
+ }
+ }
+
+ /**
+ * Returns the set element with the given id, or null
+ * if not found.
+ */
+ public KeyedElement getByKey(Object key) {
+ if (elementCount == 0)
+ return null;
+ int hash = keyHash(key);
+
+ // search the last half of the array
+ for (int i = hash; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.getKey().equals(key))
+ return element;
+ }
+
+ // search the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.getKey().equals(key))
+ return element;
+ }
+
+ // nothing found so return null
+ return null;
+ }
+
+ private int hash(KeyedElement key) {
+ return Math.abs(key.getKeyHashCode()) % elements.length;
+ }
+
+ private int keyHash(Object key) {
+ return Math.abs(key.hashCode()) % elements.length;
+ }
+
+ /**
+ * The element at the given index has been removed so move
+ * elements to keep the set properly hashed.
+ */
+ protected void rehashTo(int anIndex) {
+
+ int target = anIndex;
+ int index = anIndex + 1;
+ if (index >= elements.length)
+ index = 0;
+ KeyedElement element = elements[index];
+ while (element != null) {
+ int hashIndex = hash(element);
+ boolean match;
+ if (index < target)
+ match = !(hashIndex > target || hashIndex <= index);
+ else
+ match = !(hashIndex > target && hashIndex <= index);
+ if (match) {
+ elements[target] = element;
+ target = index;
+ }
+ index++;
+ if (index >= elements.length)
+ index = 0;
+ element = elements[index];
+ }
+ elements[target] = null;
+ }
+
+ public boolean remove(KeyedElement toRemove) {
+ if (elementCount == 0)
+ return false;
+
+ int hash = hash(toRemove);
+
+ for (int i = hash; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return false;
+ if (element.compare(toRemove)) {
+ rehashTo(i);
+ elementCount--;
+ return true;
+ }
+ }
+
+ for (int i = 0; i < hash - 1; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return false;
+ if (element.compare(toRemove)) {
+ rehashTo(i);
+ elementCount--;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean shouldGrow() {
+ return elementCount > elements.length * 0.75;
+ }
+
+ public int size() {
+ return elementCount;
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(100);
+ result.append('{');
+ boolean first = true;
+ for (int i = 0; i < elements.length; i++) {
+ if (elements[i] != null) {
+ if (first)
+ first = false;
+ else
+ result.append(", "); //$NON-NLS-1$
+ result.append(elements[i]);
+ }
+ }
+ result.append('}');
+ return result.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Messages.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.core.internal.utils.messages"; //$NON-NLS-1$
+
+ // dtree
+ public static String dtree_immutable;
+ public static String dtree_malformedTree;
+ public static String dtree_missingChild;
+ public static String dtree_notFound;
+ public static String dtree_notImmutable;
+ public static String dtree_reverse;
+ public static String dtree_subclassImplement;
+ public static String dtree_switchError;
+
+ // events
+ public static String events_builderError;
+ public static String events_building_0;
+ public static String events_building_1;
+ public static String events_errors;
+ public static String events_instantiate_1;
+ public static String events_invoking_1;
+ public static String events_invoking_2;
+ public static String events_skippingBuilder;
+ public static String events_unknown;
+
+ public static String history_copyToNull;
+ public static String history_copyToSelf;
+ public static String history_errorContentDescription;
+ public static String history_notValid;
+ public static String history_problemsCleaning;
+
+ public static String links_creating;
+ public static String links_errorLinkReconcile;
+ public static String links_invalidLocation;
+ public static String links_localDoesNotExist;
+ public static String links_locationOverlapsLink;
+ public static String links_locationOverlapsProject;
+ public static String links_natureVeto;
+ public static String links_noPath;
+ public static String links_overlappingResource;
+ public static String links_parentNotAccessible;
+ public static String links_notFileFolder;
+ public static String links_updatingDuplicate;
+ public static String links_vetoNature;
+ public static String links_workspaceVeto;
+ public static String links_wrongLocalType;
+
+ // local store
+ public static String localstore_copying;
+ public static String localstore_copyProblem;
+ public static String localstore_couldnotDelete;
+ public static String localstore_couldNotMove;
+ public static String localstore_couldNotRead;
+ public static String localstore_couldNotWrite;
+ public static String localstore_couldNotWriteReadOnly;
+ public static String localstore_deleteProblem;
+ public static String localstore_deleting;
+ public static String localstore_failedReadDuringWrite;
+ public static String localstore_fileExists;
+ public static String localstore_fileNotFound;
+ public static String localstore_locationUndefined;
+ public static String localstore_refreshing;
+ public static String localstore_refreshingRoot;
+ public static String localstore_resourceExists;
+ public static String localstore_resourceIsOutOfSync;
+
+ // resource mappings and models
+ public static String mapping_invalidDef;
+ public static String mapping_wrongType;
+ public static String mapping_noIdentifier;
+ public static String mapping_validate;
+ public static String mapping_multiProblems;
+
+ // internal.resources
+ public static String natures_duplicateNature;
+ public static String natures_hasCycle;
+ public static String natures_invalidDefinition;
+ public static String natures_invalidRemoval;
+ public static String natures_invalidSet;
+ public static String natures_missingIdentifier;
+ public static String natures_missingNature;
+ public static String natures_missingPrerequisite;
+ public static String natures_multipleSetMembers;
+
+ public static String pathvar_beginLetter;
+ public static String pathvar_invalidChar;
+ public static String pathvar_invalidValue;
+ public static String pathvar_length;
+ public static String pathvar_undefined;
+ public static String pathvar_whitespace;
+
+ public static String preferences_deleteException;
+ public static String preferences_loadException;
+ public static String preferences_operationCanceled;
+ public static String preferences_removeNodeException;
+ public static String preferences_clearNodeException;
+ public static String preferences_saveProblems;
+ public static String preferences_syncException;
+
+ public static String projRead_badLinkLocation;
+ public static String projRead_badLinkName;
+ public static String projRead_badLinkType;
+ public static String projRead_badLinkType2;
+ public static String projRead_badLocation;
+ public static String projRead_emptyLinkName;
+ public static String projRead_failureReadingProjectDesc;
+ public static String projRead_notProjectDescription;
+ public static String projRead_whichKey;
+ public static String projRead_whichValue;
+
+ public static String properties_couldNotClose;
+ public static String properties_qualifierIsNull;
+ public static String properties_readProperties;
+ public static String properties_valueTooLong;
+
+ // auto-refresh
+ public static String refresh_installError;
+ public static String refresh_jobName;
+ public static String refresh_pollJob;
+ public static String refresh_refreshErr;
+ public static String refresh_task;
+
+ public static String resources_cannotModify;
+ public static String resources_changeInAdd;
+ public static String resources_charsetBroadcasting;
+ public static String resources_charsetUpdating;
+ public static String resources_closing_0;
+ public static String resources_closing_1;
+ public static String resources_copyDestNotSub;
+ public static String resources_copying;
+ public static String resources_copying_0;
+ public static String resources_copyNotMet;
+ public static String resources_copyProblem;
+ public static String resources_couldnotDelete;
+ public static String resources_create;
+ public static String resources_creating;
+ public static String resources_deleteMeta;
+ public static String resources_deleteProblem;
+ public static String resources_deleting;
+ public static String resources_deleting_0;
+ public static String resources_destNotNull;
+ public static String resources_errorContentDescription;
+ public static String resources_errorDeleting;
+ public static String resources_errorMarkersDelete;
+ public static String resources_errorMarkersMove;
+ public static String resources_wrongMarkerAttributeValueType;
+ public static String resources_errorMembers;
+ public static String resources_errorMoving;
+ public static String resources_errorMultiRefresh;
+ public static String resources_errorNature;
+ public static String resources_errorPropertiesMove;
+ public static String resources_errorReadProject;
+ public static String resources_errorRefresh;
+ public static String resources_errorValidator;
+ public static String resources_errorVisiting;
+ public static String resources_existsDifferentCase;
+ public static String resources_existsLocalDifferentCase;
+ public static String resources_exMasterTable;
+ public static String resources_exReadProjectLocation;
+ public static String resources_exSafeRead;
+ public static String resources_exSafeSave;
+ public static String resources_exSaveMaster;
+ public static String resources_exSaveProjectLocation;
+ public static String resources_fileExists;
+ public static String resources_fileToProj;
+ public static String resources_flushingContentDescriptionCache;
+ public static String resources_folderOverFile;
+ public static String resources_format;
+ public static String resources_initHook;
+ public static String resources_initTeamHook;
+ public static String resources_initValidator;
+ public static String resources_invalidCharInName;
+ public static String resources_invalidCharInPath;
+ public static String resources_invalidName;
+ public static String resources_invalidPath;
+ public static String resources_invalidProjDesc;
+ public static String resources_invalidResourceName;
+ public static String resources_invalidRoot;
+ public static String resources_markerNotFound;
+ public static String resources_missingProjectMeta;
+ public static String resources_missingProjectMetaRepaired;
+ public static String resources_moveDestNotSub;
+ public static String resources_moveMeta;
+ public static String resources_moveNotMet;
+ public static String resources_moveNotProject;
+ public static String resources_moveProblem;
+ public static String resources_moveRoot;
+ public static String resources_moving;
+ public static String resources_moving_0;
+ public static String resources_mustBeAbsolute;
+ public static String resources_mustBeLocal;
+ public static String resources_mustBeOpen;
+ public static String resources_mustExist;
+ public static String resources_mustNotExist;
+ public static String resources_nameEmpty;
+ public static String resources_nameNull;
+ public static String resources_natureClass;
+ public static String resources_natureDeconfig;
+ public static String resources_natureExtension;
+ public static String resources_natureFormat;
+ public static String resources_natureImplement;
+ public static String resources_notChild;
+ public static String resources_oneHook;
+ public static String resources_oneTeamHook;
+ public static String resources_oneValidator;
+ public static String resources_opening_1;
+ public static String resources_overlapWorkspace;
+ public static String resources_overlapProject;
+ public static String resources_pathNull;
+ public static String resources_projectDesc;
+ public static String resources_projectDescSync;
+ public static String resources_projectPath;
+ public static String resources_pruningHistory;
+ public static String resources_reading;
+ public static String resources_readingEncoding;
+ public static String resources_readingSnap;
+ public static String resources_readMarkers;
+ public static String resources_readMeta;
+ public static String resources_readMetaWrongVersion;
+ public static String resources_readOnly;
+ public static String resources_readOnly2;
+ public static String resources_readProjectMeta;
+ public static String resources_readProjectTree;
+ public static String resources_readSync;
+ public static String resources_readWorkspaceMeta;
+ public static String resources_readWorkspaceMetaValue;
+ public static String resources_readWorkspaceSnap;
+ public static String resources_readWorkspaceTree;
+ public static String resources_refreshing;
+ public static String resources_refreshingRoot;
+ public static String resources_resetMarkers;
+ public static String resources_resetSync;
+ public static String resources_resourcePath;
+ public static String resources_saveOp;
+ public static String resources_saveProblem;
+ public static String resources_saveWarnings;
+ public static String resources_saving_0;
+ public static String resources_savingEncoding;
+ public static String resources_setDesc;
+ public static String resources_setLocal;
+ public static String resources_settingCharset;
+ public static String resources_settingContents;
+ public static String resources_settingDefaultCharsetContainer;
+ public static String resources_shutdown;
+ public static String resources_shutdownProblems;
+ public static String resources_snapInit;
+ public static String resources_snapRead;
+ public static String resources_snapRequest;
+ public static String resources_snapshot;
+ public static String resources_startupProblems;
+ public static String resources_touch;
+ public static String resources_updating;
+ public static String resources_updatingEncoding;
+ public static String resources_workspaceClosed;
+ public static String resources_workspaceOpen;
+ public static String resources_writeMeta;
+ public static String resources_writeWorkspaceMeta;
+
+ public static String synchronizer_partnerNotRegistered;
+
+ // URL
+ public static String url_badVariant;
+ public static String url_couldNotResolve_projectDoesNotExist;
+ public static String url_couldNotResolve_URLProtocolHandlerCanNotResolveURL;
+ public static String url_couldNotResolve_resourceLocationCanNotBeDetermined;
+
+ // utils
+ public static String utils_clone;
+ public static String utils_stringJobName;
+ // watson
+ public static String watson_elementNotFound;
+ public static String watson_illegalSubtree;
+ public static String watson_immutable;
+ public static String watson_noModify;
+ public static String watson_nullArg;
+ public static String watson_unknown;
+
+ // auto-refresh win32 native
+ public static String WM_beginTask;
+ public static String WM_errCloseHandle;
+ public static String WM_errCreateHandle;
+ public static String WM_errFindChange;
+ public static String WM_errors;
+ public static String WM_jobName;
+ public static String WM_nativeErr;
+
+ static {
+ // initialize resource bundles
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/ObjectMap.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,314 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.util.*;
+
+/**
+ * A specialized map implementation that is optimized for a
+ * small set of object keys.
+ *
+ * Implemented as a single array that alternates keys and values.
+ */
+public class ObjectMap implements Map, IStringPoolParticipant {
+
+ // 8 attribute keys, 8 attribute values
+ protected static final int DEFAULT_SIZE = 16;
+ protected static final int GROW_SIZE = 10;
+ protected int count = 0;
+ protected Object[] elements = null;
+
+ /**
+ * Creates a new object map of default size
+ */
+ public ObjectMap() {
+ this(DEFAULT_SIZE);
+ }
+
+ /**
+ * Creates a new object map.
+ * @param initialCapacity The initial number of elements that will fit in the map.
+ */
+ public ObjectMap(int initialCapacity) {
+ if (initialCapacity > 0)
+ elements = new Object[Math.max(initialCapacity * 2, 0)];
+ }
+
+ /**
+ * Creates a new object map of the same size as the given map and
+ * populate it with the key/attribute pairs found in the map.
+ * @param map The entries in the given map will be added to the new map.
+ */
+ public ObjectMap(Map map) {
+ this(map.size());
+ putAll(map);
+ }
+
+ /**
+ * @see Map#clear()
+ */
+ public void clear() {
+ elements = null;
+ count = 0;
+ }
+
+ /**
+ * @see java.lang.Object#clone()
+ */
+ public Object clone() {
+ return new ObjectMap(this);
+ }
+
+ /**
+ * @see Map#containsKey(java.lang.Object)
+ */
+ public boolean containsKey(Object key) {
+ if (elements == null || count == 0)
+ return false;
+ for (int i = 0; i < elements.length; i = i + 2)
+ if (elements[i] != null && elements[i].equals(key))
+ return true;
+ return false;
+ }
+
+ /**
+ * @see Map#containsValue(java.lang.Object)
+ */
+ public boolean containsValue(Object value) {
+ if (elements == null || count == 0)
+ return false;
+ for (int i = 1; i < elements.length; i = i + 2)
+ if (elements[i] != null && elements[i].equals(value))
+ return true;
+ return false;
+ }
+
+ /**
+ * @see Map#entrySet()
+ * This implementation does not conform properly to the specification
+ * in the Map interface. The returned collection will not be bound to
+ * this map and will not remain in sync with this map.
+ */
+ public Set entrySet() {
+ return count == 0 ? Collections.EMPTY_SET : toHashMap().entrySet();
+ }
+
+ /**
+ * See Object#equals
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof Map))
+ return false;
+ Map other = (Map) o;
+ //must be same size
+ if (count != other.size())
+ return false;
+
+ //keysets must be equal
+ if (!keySet().equals(other.keySet()))
+ return false;
+
+ //values for each key must be equal
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null && (!elements[i + 1].equals(other.get(elements[i]))))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @see Map#get(java.lang.Object)
+ */
+ public Object get(Object key) {
+ if (elements == null || count == 0)
+ return null;
+ for (int i = 0; i < elements.length; i = i + 2)
+ if (elements[i] != null && elements[i].equals(key))
+ return elements[i + 1];
+ return null;
+ }
+
+ /**
+ * The capacity of the map has been exceeded, grow the array by
+ * GROW_SIZE to accommodate more entries.
+ */
+ protected void grow() {
+ Object[] expanded = new Object[elements.length + GROW_SIZE];
+ System.arraycopy(elements, 0, expanded, 0, elements.length);
+ elements = expanded;
+ }
+
+ /**
+ * See Object#hashCode
+ */
+ public int hashCode() {
+ int hash = 0;
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ hash += elements[i].hashCode();
+ }
+ }
+ return hash;
+ }
+
+ /**
+ * @see Map#isEmpty()
+ */
+ public boolean isEmpty() {
+ return count == 0;
+ }
+
+ /**
+ * @see Map#keySet()
+ * This implementation does not conform properly to the specification
+ * in the Map interface. The returned collection will not be bound to
+ * this map and will not remain in sync with this map.
+ */
+ public Set keySet() {
+ Set result = new HashSet(size());
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ result.add(elements[i]);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @see Map#put(java.lang.Object, java.lang.Object)
+ */
+ public Object put(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ if (value == null)
+ return remove(key);
+
+ // handle the case where we don't have any attributes yet
+ if (elements == null)
+ elements = new Object[DEFAULT_SIZE];
+ if (count == 0) {
+ elements[0] = key;
+ elements[1] = value;
+ count++;
+ return null;
+ }
+
+ int emptyIndex = -1;
+ // replace existing value if it exists
+ for (int i = 0; i < elements.length; i += 2) {
+ if (elements[i] != null) {
+ if (elements[i].equals(key)) {
+ Object oldValue = elements[i + 1];
+ elements[i + 1] = value;
+ return oldValue;
+ }
+ } else if (emptyIndex == -1) {
+ // keep track of the first empty index
+ emptyIndex = i;
+ }
+ }
+ // this will put the emptyIndex greater than the size but
+ // that's ok because we will grow first.
+ if (emptyIndex == -1)
+ emptyIndex = count * 2;
+
+ // otherwise add it to the list of elements.
+ // grow if necessary
+ if (elements.length <= (count * 2))
+ grow();
+ elements[emptyIndex] = key;
+ elements[emptyIndex + 1] = value;
+ count++;
+ return null;
+ }
+
+ /**
+ * @see Map#putAll(java.util.Map)
+ */
+ public void putAll(Map map) {
+ for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ Object value = map.get(key);
+ put(key, value);
+ }
+ }
+
+ /**
+ * @see Map#remove(java.lang.Object)
+ */
+ public Object remove(Object key) {
+ if (elements == null || count == 0)
+ return null;
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null && elements[i].equals(key)) {
+ elements[i] = null;
+ Object result = elements[i + 1];
+ elements[i + 1] = null;
+ count--;
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see Map#size()
+ */
+ public int size() {
+ return count;
+ }
+
+ /* (non-Javadoc
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool set) {
+ //copy elements for thread safety
+ Object[] array = elements;
+ if (array == null)
+ return;
+ for (int i = 0; i < array.length; i++) {
+ Object o = array[i];
+ if (o instanceof String)
+ array[i] = set.add((String) o);
+ if (o instanceof IStringPoolParticipant)
+ ((IStringPoolParticipant) o).shareStrings(set);
+ }
+ }
+
+ /**
+ * Creates a new hash map with the same contents as this map.
+ */
+ private HashMap toHashMap() {
+ HashMap result = new HashMap(size());
+ for (int i = 0; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ result.put(elements[i], elements[i + 1]);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @see Map#values()
+ * This implementation does not conform properly to the specification
+ * in the Map interface. The returned collection will not be bound to
+ * this map and will not remain in sync with this map.
+ */
+ public Collection values() {
+ Set result = new HashSet(size());
+ for (int i = 1; i < elements.length; i = i + 2) {
+ if (elements[i] != null) {
+ result.add(elements[i]);
+ }
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Policy.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.util.Date;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.*;
+import org.osgi.framework.Bundle;
+
+public class Policy {
+ public static final boolean buildOnCancel = false;
+ //general debug flag for the plugin
+ public static boolean DEBUG = false;
+
+ public static boolean DEBUG_AUTO_REFRESH = false;
+
+ //debug constants
+ public static boolean DEBUG_BUILD_DELTA = false;
+ public static boolean DEBUG_BUILD_FAILURE = false;
+ public static boolean DEBUG_BUILD_INTERRUPT = false;
+ public static boolean DEBUG_BUILD_INVOKING = false;
+ public static boolean DEBUG_BUILD_NEEDED = false;
+ public static boolean DEBUG_BUILD_NEEDED_STACK = false;
+ public static boolean DEBUG_BUILD_STACK = false;
+
+ public static boolean DEBUG_CONTENT_TYPE = false;
+ public static boolean DEBUG_CONTENT_TYPE_CACHE = false;
+ public static boolean DEBUG_HISTORY = false;
+ public static boolean DEBUG_NATURES = false;
+ public static boolean DEBUG_PREFERENCES = false;
+ // Get timing information for restoring data
+ public static boolean DEBUG_RESTORE = false;
+ public static boolean DEBUG_RESTORE_MARKERS = false;
+ public static boolean DEBUG_RESTORE_MASTERTABLE = false;
+
+ public static boolean DEBUG_RESTORE_METAINFO = false;
+ public static boolean DEBUG_RESTORE_SNAPSHOTS = false;
+ public static boolean DEBUG_RESTORE_SYNCINFO = false;
+ public static boolean DEBUG_RESTORE_TREE = false;
+ // Get timing information for save and snapshot data
+ public static boolean DEBUG_SAVE = false;
+ public static boolean DEBUG_SAVE_MARKERS = false;
+ public static boolean DEBUG_SAVE_MASTERTABLE = false;
+
+ public static boolean DEBUG_SAVE_METAINFO = false;
+ public static boolean DEBUG_SAVE_SYNCINFO = false;
+ public static boolean DEBUG_SAVE_TREE = false;
+ public static boolean DEBUG_STRINGS = false;
+ public static int endOpWork = 1;
+ public static final long MAX_BUILD_DELAY = 1000;
+
+ public static final long MIN_BUILD_DELAY = 100;
+ public static int opWork = 99;
+ public static final int totalWork = 100;
+
+ static {
+ //init debug options
+ if (ResourcesPlugin.getPlugin().isDebugging()) {
+ DEBUG = true;
+ String sTrue = Boolean.TRUE.toString();
+ DEBUG_AUTO_REFRESH = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/refresh")); //$NON-NLS-1$
+
+ DEBUG_BUILD_DELTA = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/delta")); //$NON-NLS-1$
+ DEBUG_BUILD_FAILURE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/failure")); //$NON-NLS-1$
+ DEBUG_BUILD_INVOKING = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/invoking")); //$NON-NLS-1$
+ DEBUG_BUILD_INTERRUPT = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/interrupt")); //$NON-NLS-1$
+ DEBUG_BUILD_NEEDED = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/needbuild")); //$NON-NLS-1$
+ DEBUG_BUILD_NEEDED_STACK = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/needbuildstack")); //$NON-NLS-1$
+ DEBUG_BUILD_STACK = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/build/stacktrace")); //$NON-NLS-1$
+
+ DEBUG_CONTENT_TYPE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/contenttype")); //$NON-NLS-1$
+ DEBUG_CONTENT_TYPE_CACHE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/contenttype/cache")); //$NON-NLS-1$
+ DEBUG_HISTORY = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/history")); //$NON-NLS-1$
+ DEBUG_NATURES = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/natures")); //$NON-NLS-1$
+ DEBUG_PREFERENCES = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/preferences")); //$NON-NLS-1$
+
+ DEBUG_RESTORE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore")); //$NON-NLS-1$
+ DEBUG_RESTORE_MARKERS = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore/markers")); //$NON-NLS-1$
+ DEBUG_RESTORE_MASTERTABLE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore/mastertable")); //$NON-NLS-1$
+ DEBUG_RESTORE_METAINFO = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore/metainfo")); //$NON-NLS-1$
+ DEBUG_RESTORE_SNAPSHOTS = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore/snapshots")); //$NON-NLS-1$
+ DEBUG_RESTORE_SYNCINFO = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore/syncinfo")); //$NON-NLS-1$
+ DEBUG_RESTORE_TREE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/restore/tree")); //$NON-NLS-1$
+
+ DEBUG_SAVE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/save")); //$NON-NLS-1$
+ DEBUG_SAVE_MARKERS = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/save/markers")); //$NON-NLS-1$
+ DEBUG_SAVE_MASTERTABLE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/save/mastertable")); //$NON-NLS-1$
+ DEBUG_SAVE_METAINFO = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/save/metainfo")); //$NON-NLS-1$
+ DEBUG_SAVE_SYNCINFO = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/save/syncinfo")); //$NON-NLS-1$
+ DEBUG_SAVE_TREE = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/save/tree")); //$NON-NLS-1$
+
+ DEBUG_STRINGS = sTrue.equalsIgnoreCase(Platform.getDebugOption(ResourcesPlugin.PI_RESOURCES + "/strings")); //$NON-NLS-1$
+ }
+ }
+
+ public static void checkCanceled(IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ }
+
+ /**
+ * Print a debug message to the console.
+ * Pre-pend the message with the current date and the name of the current thread.
+ */
+ public static void debug(String message) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(new Date(System.currentTimeMillis()));
+ buffer.append(" - ["); //$NON-NLS-1$
+ buffer.append(Thread.currentThread().getName());
+ buffer.append("] "); //$NON-NLS-1$
+ buffer.append(message);
+ System.out.println(buffer.toString());
+ }
+
+ public static void log(int severity, String message, Throwable t) {
+ if (message == null)
+ message = ""; //$NON-NLS-1$
+ log(new Status(severity, ResourcesPlugin.PI_RESOURCES, 1, message, t));
+ }
+
+ public static void log(IStatus status) {
+ final Bundle bundle = Platform.getBundle(ResourcesPlugin.PI_RESOURCES);
+ if (bundle == null)
+ return;
+ Platform.getLog(bundle).log(status);
+ }
+
+ /**
+ * Logs a throwable, assuming severity of error
+ * @param t
+ */
+ public static void log(Throwable t) {
+ log(IStatus.ERROR, "Internal Error", t); //$NON-NLS-1$
+ }
+
+ public static IProgressMonitor monitorFor(IProgressMonitor monitor) {
+ return monitor == null ? new NullProgressMonitor() : monitor;
+ }
+
+ public static IProgressMonitor subMonitorFor(IProgressMonitor monitor, int ticks) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ if (monitor instanceof NullProgressMonitor)
+ return monitor;
+ return new SubProgressMonitor(monitor, ticks);
+ }
+
+ public static IProgressMonitor subMonitorFor(IProgressMonitor monitor, int ticks, int style) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ if (monitor instanceof NullProgressMonitor)
+ return monitor;
+ return new SubProgressMonitor(monitor, ticks, style);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Queue.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * A Queue of objects.
+ */
+public class Queue {
+ protected Object[] elements;
+ protected int head;
+ protected int tail;
+ protected boolean reuse;
+
+ public Queue() {
+ this(20, false);
+ }
+
+ /**
+ * The parameter reuse indicates what do you want to happen with
+ * the object reference when you remove it from the queue. If
+ * reuse is false the queue no longer holds a reference to the
+ * object when it is removed. If reuse is true you can use the
+ * method getNextAvailableObject to get an used object, set its
+ * new values and add it again to the queue.
+ */
+ public Queue(int size, boolean reuse) {
+ elements = new Object[size];
+ head = tail = 0;
+ this.reuse = reuse;
+ }
+
+ public void add(Object element) {
+ int newTail = increment(tail);
+ if (newTail == head) {
+ grow();
+ newTail = tail + 1;
+ }
+ elements[tail] = element;
+ tail = newTail;
+ }
+
+ /**
+ * This method does not affect the queue itself. It is only a
+ * helper to decrement an index in the queue.
+ */
+ public int decrement(int index) {
+ return (index == 0) ? (elements.length - 1) : index - 1;
+ }
+
+ public Object elementAt(int index) {
+ return elements[index];
+ }
+
+ public Iterator iterator() {
+ /**/
+ if (isEmpty())
+ return Collections.EMPTY_LIST.iterator();
+
+ /* if head < tail we can use the same array */
+ if (head <= tail)
+ return new ArrayIterator(elements, head, tail - 1);
+
+ /* otherwise we need to create a new array */
+ Object[] newElements = new Object[size()];
+ int end = (elements.length - head);
+ System.arraycopy(elements, head, newElements, 0, end);
+ System.arraycopy(elements, 0, newElements, end, tail);
+ return new ArrayIterator(newElements);
+ }
+
+ /**
+ * Returns an object that has been removed from the queue, if any.
+ * The intention is to support reuse of objects that have already
+ * been processed and removed from the queue. Returns null if there
+ * are no available objects.
+ */
+ public Object getNextAvailableObject() {
+ int index = tail;
+ while (index != head) {
+ if (elements[index] != null) {
+ Object result = elements[index];
+ elements[index] = null;
+ return result;
+ }
+ index = increment(index);
+ }
+ return null;
+ }
+
+ protected void grow() {
+ int newSize = (int) (elements.length * 1.5);
+ Object[] newElements = new Object[newSize];
+ if (tail >= head)
+ System.arraycopy(elements, head, newElements, head, size());
+ else {
+ int newHead = newSize - (elements.length - head);
+ System.arraycopy(elements, 0, newElements, 0, tail + 1);
+ System.arraycopy(elements, head, newElements, newHead, (newSize - newHead));
+ head = newHead;
+ }
+ elements = newElements;
+ }
+
+ /**
+ * This method does not affect the queue itself. It is only a
+ * helper to increment an index in the queue.
+ */
+ public int increment(int index) {
+ return (index == (elements.length - 1)) ? 0 : index + 1;
+ }
+
+ public int indexOf(Object target) {
+ if (tail >= head) {
+ for (int i = head; i < tail; i++)
+ if (target.equals(elements[i]))
+ return i;
+ } else {
+ for (int i = head; i < elements.length; i++)
+ if (target.equals(elements[i]))
+ return i;
+ for (int i = 0; i < tail; i++)
+ if (target.equals(elements[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ public boolean isEmpty() {
+ return tail == head;
+ }
+
+ public Object peek() {
+ return elements[head];
+ }
+
+ public Object peekTail() {
+ return elements[decrement(tail)];
+ }
+
+ public Object remove() {
+ if (isEmpty())
+ return null;
+ Object result = peek();
+ if (!reuse)
+ elements[head] = null;
+ head = increment(head);
+ return result;
+ }
+
+ public Object removeTail() {
+ Object result = peekTail();
+ tail = decrement(tail);
+ if (!reuse)
+ elements[tail] = null;
+ return result;
+ }
+
+ public void reset() {
+ tail = head = 0;
+ }
+
+ public int size() {
+ return tail > head ? (tail - head) : ((elements.length - head) + tail);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ int count = 0;
+ if (!isEmpty()) {
+ Iterator it = iterator();
+ //only print a fixed number of elements to prevent debugger from choking
+ while (count < 100) {
+ sb.append(it.next());
+ if (it.hasNext())
+ sb.append(',').append(' ');
+ else
+ break;
+ }
+ }
+ if (count < size())
+ sb.append('.').append('.').append('.');
+ sb.append(']');
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/StringPool.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.util.HashMap;
+
+/**
+ * A string pool is used for sharing strings in a way that eliminates duplicate
+ * equal strings. A string pool instance can be maintained over a long period
+ * of time, or used as a temporary structure during a string sharing pass over
+ * a data structure.
+ * <p>
+ * This class is not intended to be subclassed by clients.
+ * </p>
+ *
+ * @see IStringPoolParticipant
+ * @since 3.1
+ */
+public final class StringPool {
+ private int savings;
+ private final HashMap map = new HashMap();
+
+ /**
+ * Creates a new string pool.
+ */
+ public StringPool() {
+ super();
+ }
+
+ /**
+ * Adds a <code>String</code> to the pool. Returns a <code>String</code>
+ * that is equal to the argument but that is unique within this pool.
+ * @param string The string to add to the pool
+ * @return A string that is equal to the argument.
+ */
+ public String add(String string) {
+ if (string == null)
+ return string;
+ Object result = map.get(string);
+ if (result != null) {
+ if (result != string)
+ savings += 44 + 2 * string.length();
+ return (String) result;
+ }
+ map.put(string, string);
+ return string;
+ }
+
+ /**
+ * Returns an estimate of the size in bytes that was saved by sharing strings in
+ * the pool. In particular, this returns the size of all strings that were added to the
+ * pool after an equal string had already been added. This value can be used
+ * to estimate the effectiveness of a string sharing operation, in order to
+ * determine if or when it should be performed again.
+ *
+ * In some cases this does not precisely represent the number of bytes that
+ * were saved. For example, say the pool already contains string S1. Now
+ * string S2, which is equal to S1 but not identical, is added to the pool five
+ * times. This method will return the size of string S2 multiplied by the
+ * number of times it was added, even though the actual savings in this case
+ * is only the size of a single copy of S2.
+ */
+ public int getSavedStringCount() {
+ return savings;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/StringPoolJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.*;
+import org.osgi.framework.Bundle;
+
+/**
+ * Performs string sharing passes on all string pool participants registered
+ * with the platform.
+ */
+public class StringPoolJob extends Job {
+ private static final long INITIAL_DELAY = 10000;//ten seconds
+ private static final long RESCHEDULE_DELAY = 300000;//five minutes
+ private long lastDuration;
+ /**
+ * Stores all registered string pool participants, along with the scheduling
+ * rule required when running it.
+ */
+ private Map participants = Collections.synchronizedMap(new HashMap(10));
+
+ private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
+
+ public StringPoolJob() {
+ super(Messages.utils_stringJobName);
+ setSystem(true);
+ setPriority(DECORATE);
+ }
+
+ /**
+ * Adds a string pool participant. The job periodically builds
+ * a string pool and asks all registered participants to share their strings in
+ * the pool. Once all participants have added their strings to the pool, the
+ * pool is discarded to avoid additional memory overhead.
+ *
+ * Adding a participant that is equal to a participant already registered will
+ * replace the scheduling rule associated with the participant, but will otherwise
+ * be ignored.
+ *
+ * @param participant The participant to add
+ * @param rule The scheduling rule that must be owned at the time the
+ * participant is called. This allows a participant to protect their data structures
+ * against access at unsafe times.
+ *
+ * @see #removeStringPoolParticipant(IStringPoolParticipant)
+ * @since 3.1
+ */
+ public void addStringPoolParticipant(IStringPoolParticipant participant, ISchedulingRule rule) {
+ participants.put(participant, rule);
+ if (getState() == Job.SLEEPING)
+ wakeUp(INITIAL_DELAY);
+ else
+ schedule(INITIAL_DELAY);
+ }
+
+ /**
+ * Removes the indicated log listener from the set of registered string
+ * pool participants. If no such participant is registered, no action is taken.
+ *
+ * @param participant the participant to deregister
+ * @see #addStringPoolParticipant(IStringPoolParticipant, ISchedulingRule)
+ * @since 3.1
+ */
+ public void removeStringPoolParticipant(IStringPoolParticipant participant) {
+ participants.remove(participant);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Job
+ */
+ protected IStatus run(IProgressMonitor monitor) {
+ //if the system is shutting down, don't build
+ if (systemBundle.getState() == Bundle.STOPPING)
+ return Status.OK_STATUS;
+
+ //copy current participants to handle concurrent additions and removals to map
+ Map.Entry[] entries = (Map.Entry[]) participants.entrySet().toArray(new Map.Entry[0]);
+ ISchedulingRule[] rules = new ISchedulingRule[entries.length];
+ IStringPoolParticipant[] toRun = new IStringPoolParticipant[entries.length];
+ for (int i = 0; i < toRun.length; i++) {
+ toRun[i] = (IStringPoolParticipant) entries[i].getKey();
+ rules[i] = (ISchedulingRule) entries[i].getValue();
+ }
+ final ISchedulingRule rule = MultiRule.combine(rules);
+ long start = -1;
+ int savings = 0;
+ final IJobManager jobManager = Job.getJobManager();
+ try {
+ jobManager.beginRule(rule, monitor);
+ start = System.currentTimeMillis();
+ savings = shareStrings(toRun, monitor);
+ } finally {
+ jobManager.endRule(rule);
+ }
+ if (start > 0) {
+ lastDuration = System.currentTimeMillis() - start;
+ if (Policy.DEBUG_STRINGS)
+ Policy.debug("String sharing saved " + savings + " bytes in: " + lastDuration); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ //throttle frequency if it takes too long
+ long scheduleDelay = Math.max(RESCHEDULE_DELAY, lastDuration*100);
+ if (Policy.DEBUG_STRINGS)
+ Policy.debug("Rescheduling string sharing job in: " + scheduleDelay); //$NON-NLS-1$
+ schedule(scheduleDelay);
+ return Status.OK_STATUS;
+ }
+
+ private int shareStrings(IStringPoolParticipant[] toRun, IProgressMonitor monitor) {
+ final StringPool pool = new StringPool();
+ for (int i = 0; i < toRun.length; i++) {
+ if (monitor.isCanceled())
+ break;
+ final IStringPoolParticipant current = toRun[i];
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ //exceptions are already logged, so nothing to do
+ }
+
+ public void run() {
+ current.shareStrings(pool);
+ }
+ });
+ }
+ return pool.getSavedStringCount();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/UniversalUniqueIdentifier.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.GregorianCalendar;
+import java.util.Random;
+import org.eclipse.core.runtime.Assert;
+
+public class UniversalUniqueIdentifier implements java.io.Serializable {
+
+ /**
+ * All serializable objects should have a stable serialVersionUID
+ */
+ private static final long serialVersionUID = 1L;
+
+ /* INSTANCE FIELDS =============================================== */
+
+ private byte[] fBits = new byte[BYTES_SIZE];
+
+ /* NON-FINAL PRIVATE STATIC FIELDS =============================== */
+
+ private static BigInteger fgPreviousClockValue;
+ private static int fgClockAdjustment = 0;
+ private static int fgClockSequence = -1;
+ private static byte[] nodeAddress;
+
+ static {
+ nodeAddress = computeNodeAddress();
+ }
+
+ /* PRIVATE STATIC FINAL FIELDS =================================== */
+
+ private static Random fgRandomNumberGenerator = new Random();
+
+ /* PUBLIC STATIC FINAL FIELDS ==================================== */
+
+ public static final int BYTES_SIZE = 16;
+ public static final byte[] UNDEFINED_UUID_BYTES = new byte[16];
+ public static final int MAX_CLOCK_SEQUENCE = 0x4000;
+ public static final int MAX_CLOCK_ADJUSTMENT = 0x7FFF;
+ public static final int TIME_FIELD_START = 0;
+ public static final int TIME_FIELD_STOP = 6;
+ public static final int TIME_HIGH_AND_VERSION = 7;
+ public static final int CLOCK_SEQUENCE_HIGH_AND_RESERVED = 8;
+ public static final int CLOCK_SEQUENCE_LOW = 9;
+ public static final int NODE_ADDRESS_START = 10;
+ public static final int NODE_ADDRESS_BYTE_SIZE = 6;
+
+ public static final int BYTE_MASK = 0xFF;
+
+ public static final int HIGH_NIBBLE_MASK = 0xF0;
+
+ public static final int LOW_NIBBLE_MASK = 0x0F;
+
+ public static final int SHIFT_NIBBLE = 4;
+
+ public static final int ShiftByte = 8;
+
+ /**
+ UniversalUniqueIdentifier default constructor returns a
+ new instance that has been initialized to a unique value.
+ */
+ public UniversalUniqueIdentifier() {
+ this.setVersion(1);
+ this.setVariant(1);
+ this.setTimeValues();
+ this.setNode(getNodeAddress());
+ }
+
+ /**
+ Constructor that accepts the bytes to use for the instance. The format
+ of the byte array is compatible with the <code>toBytes()</code> method.
+
+ <p>The constructor returns the undefined uuid if the byte array is invalid.
+
+ @see #toBytes()
+ @see #BYTES_SIZE
+ */
+ public UniversalUniqueIdentifier(byte[] byteValue) {
+ fBits = new byte[BYTES_SIZE];
+ if (byteValue.length >= BYTES_SIZE)
+ System.arraycopy(byteValue, 0, fBits, 0, BYTES_SIZE);
+ }
+
+ private void appendByteString(StringBuffer buffer, byte value) {
+ String hexString;
+
+ if (value < 0)
+ hexString = Integer.toHexString(256 + value);
+ else
+ hexString = Integer.toHexString(value);
+ if (hexString.length() == 1)
+ buffer.append("0"); //$NON-NLS-1$
+ buffer.append(hexString);
+ }
+
+ private static BigInteger clockValueNow() {
+ GregorianCalendar now = new GregorianCalendar();
+ BigInteger nowMillis = BigInteger.valueOf(now.getTime().getTime());
+ BigInteger baseMillis = BigInteger.valueOf(now.getGregorianChange().getTime());
+
+ return (nowMillis.subtract(baseMillis).multiply(BigInteger.valueOf(10000L)));
+ }
+
+ /**
+ Simply increases the visibility of <code>Object</code>'s clone.
+ Otherwise, no new behaviour.
+ */
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ Assert.isTrue(false, Messages.utils_clone);
+ return null;
+ }
+ }
+
+ public static int compareTime(byte[] fBits1, byte[] fBits2) {
+ for (int i = TIME_FIELD_STOP; i >= 0; i--)
+ if (fBits1[i] != fBits2[i])
+ return (0xFF & fBits1[i]) - (0xFF & fBits2[i]);
+ return 0;
+ }
+
+ /**
+ * Answers the node address attempting to mask the IP
+ * address of this machine.
+ *
+ * @return byte[] the node address
+ */
+ private static byte[] computeNodeAddress() {
+
+ byte[] address = new byte[NODE_ADDRESS_BYTE_SIZE];
+
+ // Seed the secure randomizer with some oft-varying inputs
+ int thread = Thread.currentThread().hashCode();
+ long time = System.currentTimeMillis();
+ int objectId = System.identityHashCode(new String());
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(byteOut);
+ byte[] ipAddress = getIPAddress();
+
+ try {
+ if (ipAddress != null)
+ out.write(ipAddress);
+ out.write(thread);
+ out.writeLong(time);
+ out.write(objectId);
+ out.close();
+ } catch (IOException exc) {
+ //ignore the failure, we're just trying to come up with a random seed
+ }
+ byte[] rand = byteOut.toByteArray();
+
+ SecureRandom randomizer = new SecureRandom(rand);
+ randomizer.nextBytes(address);
+
+ // set the MSB of the first octet to 1 to distinguish from IEEE node addresses
+ address[0] = (byte) (address[0] | (byte) 0x80);
+
+ return address;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof UniversalUniqueIdentifier))
+ return false;
+
+ byte[] other = ((UniversalUniqueIdentifier) obj).fBits;
+ if (fBits == other)
+ return true;
+ if (fBits.length != other.length)
+ return false;
+ for (int i = 0; i < fBits.length; i++) {
+ if (fBits[i] != other[i])
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ Answers the IP address of the local machine using the
+ Java API class <code>InetAddress</code>.
+
+ @return byte[] the network address in network order
+ @see java.net.InetAddress#getLocalHost()
+ @see java.net.InetAddress#getAddress()
+ */
+ protected static byte[] getIPAddress() {
+ try {
+ return InetAddress.getLocalHost().getAddress();
+ } catch (UnknownHostException e) {
+ //valid for this to be thrown be a machine with no IP connection
+ //It is VERY important NOT to throw this exception
+ return null;
+ }
+ }
+
+ private static byte[] getNodeAddress() {
+ return nodeAddress;
+ }
+
+ public int hashCode() {
+ return fBits[0] + fBits[3] + fBits[7] + fBits[11] + fBits[15];
+ }
+
+ private static int nextClockSequence() {
+
+ if (fgClockSequence == -1)
+ fgClockSequence = (int) (fgRandomNumberGenerator.nextDouble() * MAX_CLOCK_SEQUENCE);
+
+ fgClockSequence = (fgClockSequence + 1) % MAX_CLOCK_SEQUENCE;
+
+ return fgClockSequence;
+ }
+
+ private static BigInteger nextTimestamp() {
+
+ BigInteger timestamp = clockValueNow();
+ int timestampComparison;
+
+ timestampComparison = timestamp.compareTo(fgPreviousClockValue);
+
+ if (timestampComparison == 0) {
+ if (fgClockAdjustment == MAX_CLOCK_ADJUSTMENT) {
+ while (timestamp.compareTo(fgPreviousClockValue) == 0)
+ timestamp = clockValueNow();
+ timestamp = nextTimestamp();
+ } else
+ fgClockAdjustment++;
+ } else {
+ fgClockAdjustment = 0;
+
+ if (timestampComparison < 0)
+ nextClockSequence();
+ }
+
+ return timestamp;
+ }
+
+ private void setClockSequence(int clockSeq) {
+ int clockSeqHigh = (clockSeq >>> ShiftByte) & LOW_NIBBLE_MASK;
+ int reserved = fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & HIGH_NIBBLE_MASK;
+
+ fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] = (byte) (reserved | clockSeqHigh);
+ fBits[CLOCK_SEQUENCE_LOW] = (byte) (clockSeq & BYTE_MASK);
+ }
+
+ protected void setNode(byte[] bytes) {
+
+ for (int index = 0; index < NODE_ADDRESS_BYTE_SIZE; index++)
+ fBits[index + NODE_ADDRESS_START] = bytes[index];
+ }
+
+ private void setTimestamp(BigInteger timestamp) {
+ BigInteger value = timestamp;
+ BigInteger bigByte = BigInteger.valueOf(256L);
+ BigInteger[] results;
+ int version;
+ int timeHigh;
+
+ for (int index = TIME_FIELD_START; index < TIME_FIELD_STOP; index++) {
+ results = value.divideAndRemainder(bigByte);
+ value = results[0];
+ fBits[index] = (byte) results[1].intValue();
+ }
+ version = fBits[TIME_HIGH_AND_VERSION] & HIGH_NIBBLE_MASK;
+ timeHigh = value.intValue() & LOW_NIBBLE_MASK;
+ fBits[TIME_HIGH_AND_VERSION] = (byte) (timeHigh | version);
+ }
+
+ protected synchronized void setTimeValues() {
+ this.setTimestamp(timestamp());
+ this.setClockSequence(fgClockSequence);
+ }
+
+ protected int setVariant(int variantIdentifier) {
+ int clockSeqHigh = fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & LOW_NIBBLE_MASK;
+ int variant = variantIdentifier & LOW_NIBBLE_MASK;
+
+ fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] = (byte) ((variant << SHIFT_NIBBLE) | clockSeqHigh);
+ return (variant);
+ }
+
+ protected void setVersion(int versionIdentifier) {
+ int timeHigh = fBits[TIME_HIGH_AND_VERSION] & LOW_NIBBLE_MASK;
+ int version = versionIdentifier & LOW_NIBBLE_MASK;
+
+ fBits[TIME_HIGH_AND_VERSION] = (byte) (timeHigh | (version << SHIFT_NIBBLE));
+ }
+
+ private static BigInteger timestamp() {
+ BigInteger timestamp;
+
+ if (fgPreviousClockValue == null) {
+ fgClockAdjustment = 0;
+ nextClockSequence();
+ timestamp = clockValueNow();
+ } else
+ timestamp = nextTimestamp();
+
+ fgPreviousClockValue = timestamp;
+ return fgClockAdjustment == 0 ? timestamp : timestamp.add(BigInteger.valueOf(fgClockAdjustment));
+ }
+
+ /**
+ This representation is compatible with the (byte[]) constructor.
+
+ @see #UniversalUniqueIdentifier(byte[])
+ */
+ public byte[] toBytes() {
+ byte[] result = new byte[fBits.length];
+
+ System.arraycopy(fBits, 0, result, 0, fBits.length);
+ return result;
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < fBits.length; i++)
+ appendByteString(buffer, fBits[i]);
+ return buffer.toString();
+ }
+
+ public String toStringAsBytes() {
+ String result = "{"; //$NON-NLS-1$
+
+ for (int i = 0; i < fBits.length; i++) {
+ result += fBits[i];
+ if (i < fBits.length + 1)
+ result += ","; //$NON-NLS-1$
+ }
+ return result + "}"; //$NON-NLS-1$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/WrappedRuntimeException.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.utils;
+
+public class WrappedRuntimeException extends RuntimeException {
+
+ /**
+ * All serializable objects should have a stable serialVersionUID
+ */
+ private static final long serialVersionUID = 1L;
+
+ private Throwable target;
+
+ public WrappedRuntimeException(Throwable target) {
+ super();
+ this.target = target;
+ }
+
+ public Throwable getTargetException() {
+ return this.target;
+ }
+
+ public String getMessage() {
+ return target.getMessage();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/DefaultElementComparator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+/**
+ * This is what you would expect for an element tree comparator.
+ * Clients of the element tree that want specific comparison behaviour
+ * must define their own element comparator (without subclassing or
+ * otherwise extending this comparator). Internal element tree operations
+ * rely on the behaviour of this type, and the ElementTree maintainer
+ * reserves the right to change its behaviour as necessary.
+ */
+public final class DefaultElementComparator implements IElementComparator {
+ private static DefaultElementComparator singleton;
+
+ /**
+ * Force clients to use the singleton
+ */
+ protected DefaultElementComparator() {
+ super();
+ }
+
+ /**
+ * Returns the type of change.
+ */
+ public int compare(Object oldInfo, Object newInfo) {
+ if (oldInfo == null && newInfo == null)
+ return 0;
+ if (oldInfo == null || newInfo == null)
+ return 1;
+ return testEquality(oldInfo, newInfo) ? 0 : 1;
+ }
+
+ /**
+ * Returns the singleton instance
+ */
+ public static IElementComparator getComparator() {
+ if (singleton == null) {
+ singleton = new DefaultElementComparator();
+ }
+ return singleton;
+ }
+
+ /**
+ * Makes a comparison based on equality
+ */
+ protected boolean testEquality(Object oldInfo, Object newInfo) {
+ if (oldInfo == null && newInfo == null)
+ return true;
+ if (oldInfo == null || newInfo == null)
+ return false;
+
+ return oldInfo.equals(newInfo);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,728 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import java.util.HashMap;
+import org.eclipse.core.internal.dtree.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.StringPool;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * An ElementTree can be viewed as a generic rooted tree that stores
+ * a hierarchy of elements. An element in the tree consists of a
+ * (name, data, children) 3-tuple. The name can be any String, and
+ * the data can be any Object. The children are a collection of zero
+ * or more elements that logically fall below their parent in the tree.
+ * The implementation makes no guarantees about the ordering of children.
+ *
+ * Elements in the tree are referenced by a key that consists of the names
+ * of all elements on the path from the root to that element in the tree.
+ * For example, if root node "a" has child "b", which has child "c", element
+ * "c" can be referenced in the tree using the key (/a/b/c). Keys are represented
+ * using IPath objects, where the Paths are relative to the root element of the
+ * tree.
+ * @see IPath
+ *
+ * Each ElementTree has a single root element that is created implicitly and
+ * is always present in any tree. This root corresponds to the key (/),
+ * or the singleton <code>Path.ROOT</code>. The root element cannot be created
+ * or deleted, and its data and name cannot be set. The root element's children
+ * however can be modified (added, deleted, etc). The root path can be obtained
+ * using the <code>getRoot()</code> method.
+ *
+ * ElementTrees are modified in generations. The method <code>newEmptyDelta()</code>
+ * returns a new tree generation that can be modified arbitrarily by the user.
+ * For the purpose of explanation, we call such a tree "active".
+ * When the method <code>immutable()</code> is called, that tree generation is
+ * frozen, and can never again be modified. A tree must be immutable before
+ * a new tree generation can start. Since all ancestor trees are immutable,
+ * different active trees can have ancestors in common without fear of
+ * thread corruption problems.
+ *
+ * Internally, any single tree generation is simply stored as the
+ * set of changes between itself and its most recent ancestor (its parent).
+ * This compact delta representation allows chains of element trees to
+ * be created at relatively low cost. Clients of the ElementTree can
+ * instantaneously "undo" sets of changes by navigating up to the parent
+ * tree using the <code>getParent()</code> method.
+ *
+ * Although the delta representation is compact, extremely long delta
+ * chains make for a large structure that is potentially slow to query.
+ * For this reason, the client is encouraged to minimize delta chain
+ * lengths using the <code>collapsing(int)</code> and <code>makeComplete()</code>
+ * methods. The <code>getDeltaDepth()</code> method can be used to
+ * discover the length of the delta chain. The entire delta chain can
+ * also be re-oriented in terms of the current element tree using the
+ * <code>reroot()</code> operation.
+ *
+ * Classes are also available for tree serialization and navigation.
+ * @see ElementTreeReader
+ * @see ElementTreeWriter
+ * @see ElementTreeIterator
+ *
+ * Finally, why are ElementTrees in a package called "watson"?
+ * - "It's ElementTree my dear Watson, ElementTree."
+ */
+public class ElementTree {
+ protected DeltaDataTree tree;
+ protected IElementTreeData userData;
+
+ private class ChildIDsCache {
+ ChildIDsCache(IPath path, IPath[] childPaths) {
+ this.path = path;
+ this.childPaths = childPaths;
+ }
+
+ IPath path;
+ IPath[] childPaths;
+ }
+
+ private volatile ChildIDsCache childIDsCache = null;
+
+ private volatile DataTreeLookup lookupCache = null;
+
+ private volatile DataTreeLookup lookupCacheIgnoreCase = null;
+
+ private static int treeCounter = 0;
+ private int treeStamp;
+
+ /**
+ * Creates a new empty element tree.
+ */
+ public ElementTree() {
+ initialize(new DeltaDataTree());
+ }
+
+ /**
+ * Creates an element tree given its internal node representation.
+ */
+ protected ElementTree(DataTreeNode rootNode) {
+ initialize(rootNode);
+ }
+
+ /**
+ * Creates a new element tree with the given data tree as its representation.
+ */
+ protected ElementTree(DeltaDataTree tree) {
+ initialize(tree);
+ }
+
+ /**
+ * Creates a new empty delta element tree having the
+ * given tree as its parent.
+ */
+ protected ElementTree(ElementTree parent) {
+ if (!parent.isImmutable()) {
+ parent.immutable();
+ }
+
+ /* copy the user data forward */
+ IElementTreeData data = parent.getTreeData();
+ if (data != null) {
+ userData = (IElementTreeData) data.clone();
+ }
+
+ initialize(parent.tree.newEmptyDeltaTree());
+ }
+
+ /**
+ * Collapses this tree so that the given ancestor becomes its
+ * immediate parent. Afterwards, this tree will still have exactly the
+ * same contents, but its internal structure will be compressed.
+ *
+ * <p> This operation should be used to collapse chains of
+ * element trees created by newEmptyDelta()/immutable().
+ *
+ * <p>This element tree must be immutable at the start of this operation,
+ * and will be immutable afterwards.
+ * @return this tree.
+ */
+ public synchronized ElementTree collapseTo(ElementTree parent) {
+ Assert.isTrue(tree.isImmutable());
+ if (this == parent) {
+ //already collapsed
+ return this;
+ }
+ //collapse my tree to be a forward delta of the parent's tree.
+ tree.collapseTo(parent.tree, DefaultElementComparator.getComparator());
+ return this;
+ }
+
+ /**
+ * Creates the indicated element and sets its element info.
+ * The parent element must be present, otherwise an IllegalArgumentException
+ * is thrown. If the indicated element is already present in the tree,
+ * its element info is replaced and any existing children are
+ * deleted.
+ *
+ * @param key element key
+ * @param data element data, or <code>null</code>
+ */
+ public synchronized void createElement(IPath key, Object data) {
+ /* don't allow modification of the implicit root */
+ if (key.isRoot())
+ return;
+
+ // Clear the child IDs cache in case it's referring to this parent. This is conservative.
+ childIDsCache = null;
+
+ IPath parent = key.removeLastSegments(1);
+ try {
+ tree.createChild(parent, key.lastSegment(), data);
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(parent);
+ }
+ // Set the lookup to be this newly created object.
+ lookupCache = DataTreeLookup.newLookup(key, true, data, true);
+ lookupCacheIgnoreCase = null;
+ }
+
+ /**
+ * Creates or replaces the subtree below the given path with
+ * the given tree. The subtree can only have one child below
+ * the root, which will become the node specified by the given
+ * key in this tree.
+ *
+ * @param key The path of the new subtree in this tree.
+ * @see #getSubtree(IPath)
+ */
+ public synchronized void createSubtree(IPath key, ElementTree subtree) {
+ /* don't allow creating subtrees at the root */
+ if (key.isRoot()) {
+ throw new IllegalArgumentException(Messages.watson_noModify);
+ }
+
+ // Clear the child IDs cache in case it's referring to this parent.
+ // This is conservative.
+ childIDsCache = null;
+ // Clear the lookup cache, in case the element being created is the same
+ // as for the last lookup.
+ lookupCache = lookupCacheIgnoreCase = null;
+ try {
+ /* don't copy the implicit root node of the subtree */
+ IPath[] children = subtree.getChildren(subtree.getRoot());
+ if (children.length != 1) {
+ throw new IllegalArgumentException(Messages.watson_illegalSubtree);
+ }
+
+ /* get the subtree for the specified key */
+ DataTreeNode node = (DataTreeNode) subtree.tree.copyCompleteSubtree(children[0]);
+
+ /* insert the subtree in this tree */
+ tree.createSubtree(key, node);
+
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ }
+ }
+
+ /**
+ * Deletes the indicated element and its descendents.
+ * The element must be present.
+ */
+ public synchronized void deleteElement(IPath key) {
+ /* don't allow modification of the implicit root */
+ if (key.isRoot())
+ return;
+
+ // Clear the child IDs cache in case it's referring to this parent.
+ // This is conservative.
+ childIDsCache = null;
+ // Clear the lookup cache, in case the element being deleted is the same
+ // as for the last lookup.
+ lookupCache = lookupCacheIgnoreCase = null;
+ try {
+ tree.deleteChild(key.removeLastSegments(1), key.lastSegment());
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ }
+ }
+
+ /**
+ * Complains that an element was not found
+ */
+ protected void elementNotFound(IPath key) {
+ throw new IllegalArgumentException(NLS.bind(Messages.watson_elementNotFound, key));
+ }
+
+ /**
+ * Given an array of element trees, returns the index of the
+ * oldest tree. The oldest tree is the tree such that no
+ * other tree in the array is a descendent of that tree.
+ * Note that this counter-intuitive concept of oldest is based on the
+ * ElementTree orientation such that the complete tree is always the
+ * newest tree.
+ */
+ public static int findOldest(ElementTree[] trees) {
+
+ /* first put all the trees in a hashtable */
+ HashMap candidates = new HashMap((int) (trees.length * 1.5 + 1));
+ for (int i = 0; i < trees.length; i++) {
+ candidates.put(trees[i], trees[i]);
+ }
+
+ /* keep removing parents until only one tree remains */
+ ElementTree oldestSoFar = null;
+ while (candidates.size() > 0) {
+ /* get a new candidate */
+ ElementTree current = (ElementTree) candidates.values().iterator().next();
+
+ /* remove this candidate from the table */
+ candidates.remove(current);
+
+ /* remove all of this element's parents from the list of candidates*/
+ ElementTree parent = current.getParent();
+
+ /* walk up chain until we hit the root or a tree we have already tested */
+ while (parent != null && parent != oldestSoFar) {
+ candidates.remove(parent);
+ parent = parent.getParent();
+ }
+
+ /* the current candidate is the oldest tree seen so far */
+ oldestSoFar = current;
+
+ /* if the table is now empty, we have a winner */
+ }
+ Assert.isNotNull(oldestSoFar);
+
+ /* return the appropriate index */
+ for (int i = 0; i < trees.length; i++) {
+ if (trees[i] == oldestSoFar) {
+ return i;
+ }
+ }
+ Assert.isTrue(false, "Should not get here"); //$NON-NLS-1$
+ return -1;
+ }
+
+ /**
+ * Returns the number of children of the element
+ * specified by the given path.
+ * The given element must be present in this tree.
+ */
+ public synchronized int getChildCount(IPath key) {
+ Assert.isNotNull(key);
+ return getChildIDs(key).length;
+ }
+
+ /**
+ * Returns the IDs of the children of the specified element.
+ * If the specified element is null, returns the root element path.
+ */
+ protected IPath[] getChildIDs(IPath key) {
+ ChildIDsCache cache = childIDsCache; // Grab it in case it's replaced concurrently.
+ if (cache != null && cache.path == key) {
+ return cache.childPaths;
+ }
+ try {
+ if (key == null)
+ return new IPath[] {tree.rootKey()};
+ IPath[] children = tree.getChildren(key);
+ childIDsCache = new ChildIDsCache(key, children); // Cache the result
+ return children;
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ return null; // can't get here
+ }
+ }
+
+ /**
+ * Returns the paths of the children of the element
+ * specified by the given path.
+ * The given element must be present in this tree.
+ */
+ public synchronized IPath[] getChildren(IPath key) {
+ Assert.isNotNull(key);
+ return getChildIDs(key);
+ }
+
+ /**
+ * Returns the internal data tree.
+ */
+ public DeltaDataTree getDataTree() {
+ return tree;
+ }
+
+ /**
+ * Returns the element data for the given element identifier.
+ * The given element must be present in this tree.
+ */
+ public synchronized Object getElementData(IPath key) {
+ /* don't allow modification of the implicit root */
+ if (key.isRoot())
+ return null;
+ DataTreeLookup lookup = lookupCache; // Grab it in case it's replaced concurrently.
+ if (lookup == null || lookup.key != key)
+ lookupCache = lookup = tree.lookup(key);
+ if (lookup.isPresent)
+ return lookup.data;
+ elementNotFound(key);
+ return null; // can't get here
+ }
+
+ /**
+ * Returns the element data for the given element identifier.
+ * The given element must be present in this tree.
+ */
+ public synchronized Object getElementDataIgnoreCase(IPath key) {
+ /* don't allow modification of the implicit root */
+ if (key.isRoot())
+ return null;
+ DataTreeLookup lookup = lookupCacheIgnoreCase; // Grab it in case it's replaced concurrently.
+ if (lookup == null || lookup.key != key)
+ lookupCacheIgnoreCase = lookup = tree.lookupIgnoreCase(key);
+ if (lookup.isPresent)
+ return lookup.data;
+ elementNotFound(key);
+ return null; // can't get here
+ }
+
+ /**
+ * Returns the names of the children of the specified element.
+ * The specified element must exist in the tree.
+ * If the specified element is null, returns the root element path.
+ */
+ public synchronized String[] getNamesOfChildren(IPath key) {
+ try {
+ if (key == null)
+ return new String[] {""}; //$NON-NLS-1$
+ return tree.getNamesOfChildren(key);
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ return null; // can't get here
+ }
+ }
+
+ /**
+ * Returns the parent tree, or <code>null</code> if there is no parent.
+ */
+ public ElementTree getParent() {
+ DeltaDataTree parentTree = tree.getParent();
+ if (parentTree == null) {
+ return null;
+ }
+ // The parent ElementTree is stored as the node data of the parent DeltaDataTree,
+ // to simplify canonicalization in the presence of rerooting.
+ return (ElementTree) parentTree.getData(tree.rootKey());
+ }
+
+ /**
+ * Returns the root node of this tree.
+ */
+ public IPath getRoot() {
+ return getChildIDs(null)[0];
+ }
+
+ /**
+ * Returns the subtree rooted at the given key. In the resulting tree,
+ * the implicit root node (designated by Path.ROOT), has a single child,
+ * which is the node specified by the given key in this tree.
+ *
+ * The subtree must be present in this tree.
+ *
+ * @see #createSubtree(IPath, ElementTree)
+ */
+ public ElementTree getSubtree(IPath key) {
+ /* the subtree of the root of this tree is just this tree */
+ if (key.isRoot()) {
+ return this;
+ }
+ try {
+ DataTreeNode elementNode = (DataTreeNode) tree.copyCompleteSubtree(key);
+ return new ElementTree(elementNode);
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the user data associated with this tree.
+ */
+ public IElementTreeData getTreeData() {
+ return userData;
+ }
+
+ /**
+ * Returns true if there have been changes in the tree between the two
+ * given layers. The two must be related and new must be newer than old.
+ * That is, new must be an ancestor of old.
+ */
+ public static boolean hasChanges(ElementTree newLayer, ElementTree oldLayer, IElementComparator comparator, boolean inclusive) {
+ // if any of the layers are null, assume that things have changed
+ if (newLayer == null || oldLayer == null)
+ return true;
+ if (newLayer == oldLayer)
+ return false;
+ //if the tree data has changed, then the tree has changed
+ if (comparator.compare(newLayer.getTreeData(), oldLayer.getTreeData()) != IElementComparator.K_NO_CHANGE)
+ return true;
+
+ // The tree structure has the top layer(s) (i.e., tree) parentage pointing down to a complete
+ // layer whose parent is null. The bottom layers (i.e., operationTree) point up to the
+ // common complete layer whose parent is null. The complete layer moves up as
+ // changes happen. To see if any changes have happened, we should consider only
+ // layers whose parent is not null. That is, skip the complete layer as it will clearly not be
+ // empty.
+
+ // look down from the current layer (always inclusive) if the top layer is mutable
+ ElementTree stopLayer = null;
+ if (newLayer.isImmutable())
+ // if the newLayer is immutable, the tree structure all points up so ensure that
+ // when searching up, we stop at newLayer (inclusive)
+ stopLayer = newLayer.getParent();
+ else {
+ ElementTree layer = newLayer;
+ while (layer != null && layer.getParent() != null) {
+ if (!layer.getDataTree().isEmptyDelta())
+ return true;
+ layer = layer.getParent();
+ }
+ }
+
+ // look up from the layer at which we started to null or newLayer's parent (variably inclusive)
+ // depending on whether newLayer is mutable.
+ ElementTree layer = inclusive ? oldLayer : oldLayer.getParent();
+ while (layer != null && layer.getParent() != stopLayer) {
+ if (!layer.getDataTree().isEmptyDelta())
+ return true;
+ layer = layer.getParent();
+ }
+ // didn't find anything that changed
+ return false;
+ }
+
+ /**
+ * Makes this tree immutable (read-only); ignored if it is already
+ * immutable.
+ */
+ public synchronized void immutable() {
+ if (!tree.isImmutable()) {
+ tree.immutable();
+ /* need to clear the lookup cache since it reports whether results were found
+ in the topmost delta, and the order of deltas is changing */
+ lookupCache = lookupCacheIgnoreCase = null;
+ /* reroot the delta chain at this tree */
+ tree.reroot();
+ }
+ }
+
+ /**
+ * Returns true if this element tree includes an element with the given
+ * key, false otherwise.
+ */
+ public synchronized boolean includes(IPath key) {
+ DataTreeLookup lookup = lookupCache; // Grab it in case it's replaced concurrently.
+ if (lookup == null || lookup.key != key) {
+ lookupCache = lookup = tree.lookup(key);
+ }
+ return lookup.isPresent;
+ }
+
+ /**
+ * Returns true if this element tree includes an element with the given
+ * key, ignoring the case of the key, and false otherwise.
+ */
+ public boolean includesIgnoreCase(IPath key) {
+ DataTreeLookup lookup = lookupCacheIgnoreCase; // Grab it in case it's replaced concurrently.
+ if (lookup == null || lookup.key != key) {
+ lookupCacheIgnoreCase = lookup = tree.lookupIgnoreCase(key);
+ }
+ return lookup.isPresent;
+ }
+
+ protected void initialize(DataTreeNode rootNode) {
+ /* create the implicit root node */
+ initialize(new DeltaDataTree(new DataTreeNode(null, null, new AbstractDataTreeNode[] {rootNode})));
+ }
+
+ protected void initialize(DeltaDataTree newTree) {
+ // Keep this element tree as the data of the root node.
+ // Useful for canonical results for ElementTree.getParent().
+ // see getParent().
+ treeStamp = treeCounter++;
+ newTree.setData(newTree.rootKey(), this);
+ this.tree = newTree;
+ }
+
+ /**
+ * Returns whether this tree is immutable.
+ */
+ public boolean isImmutable() {
+ return tree.isImmutable();
+ }
+
+ /**
+ * Merges a chain of deltas for a certain subtree to this tree.
+ * If this tree has any data in the specified subtree, it will
+ * be overwritten. The receiver tree must be open, and it will
+ * be made immutable during the merge operation. The trees in the
+ * provided array will be replaced by new trees that have been
+ * merged into the receiver's delta chain.
+ *
+ * @param path The path of the subtree chain to merge
+ * @param trees The chain of trees to merge. The trees can be
+ * in any order, but they must all form a simple ancestral chain.
+ * @return A new open tree with the delta chain merged in.
+ */
+ public ElementTree mergeDeltaChain(IPath path, ElementTree[] trees) {
+ if (path == null || trees == null) {
+ throw new IllegalArgumentException(NLS.bind(Messages.watson_nullArg, "ElementTree.mergeDeltaChain")); //$NON-NLS-1$
+ }
+
+ /* The tree has to be open */
+ if (isImmutable()) {
+ throw new IllegalArgumentException(Messages.watson_immutable);
+ }
+ ElementTree current = this;
+ if (trees.length > 0) {
+ /* find the oldest tree to be merged */
+ ElementTree toMerge = trees[findOldest(trees)];
+
+ /* merge the trees from oldest to newest */
+ while (toMerge != null) {
+ if (path.isRoot()) {
+ //copy all the children
+ IPath[] children = toMerge.getChildren(Path.ROOT);
+ for (int i = 0; i < children.length; i++) {
+ current.createSubtree(children[i], toMerge.getSubtree(children[i]));
+ }
+ } else {
+ //just copy the specified node
+ current.createSubtree(path, toMerge.getSubtree(path));
+ }
+ current.immutable();
+
+ /* replace the tree in the array */
+ /* we have to go through all trees because there may be duplicates */
+ for (int i = 0; i < trees.length; i++) {
+ if (trees[i] == toMerge) {
+ trees[i] = current;
+ }
+ }
+ current = current.newEmptyDelta();
+ toMerge = toMerge.getParent();
+ }
+ }
+ return current;
+ }
+
+ /**
+ * Creates a new element tree which is represented as a delta on this one.
+ * Initially they have the same content. Subsequent changes to the new
+ * tree will not affect this one.
+ */
+ public synchronized ElementTree newEmptyDelta() {
+ // Don't want old trees hanging onto cached infos.
+ lookupCache = lookupCacheIgnoreCase = null;
+ return new ElementTree(this);
+ }
+
+ /**
+ * Returns a mutable copy of the element data for the given path.
+ * This copy will be held onto in the most recent delta.
+ * ElementTree data MUST implement the IElementTreeData interface
+ * for this method to work. If the data does not define that interface
+ * this method will fail.
+ */
+ public synchronized Object openElementData(IPath key) {
+ Assert.isTrue(!isImmutable());
+
+ /* don't allow modification of the implicit root */
+ if (key.isRoot())
+ return null;
+ DataTreeLookup lookup = lookupCache; // Grab it in case it's replaced concurrently.
+ if (lookup == null || lookup.key != key) {
+ lookupCache = lookup = tree.lookup(key);
+ }
+ if (lookup.isPresent) {
+ if (lookup.foundInFirstDelta)
+ return lookup.data;
+ /**
+ * The node has no data in the most recent delta.
+ * Pull it up to the present delta by setting its data with a clone.
+ */
+ IElementTreeData oldData = (IElementTreeData) lookup.data;
+ if (oldData != null) {
+ try {
+ Object newData = oldData.clone();
+ tree.setData(key, newData);
+ lookupCache = lookupCacheIgnoreCase = null;
+ return newData;
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ }
+ }
+ } else {
+ elementNotFound(key);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the element for the given element identifier.
+ * The given element must be present in this tree.
+ * @param key element identifier
+ * @param data element info, or <code>null</code>
+ */
+ public synchronized void setElementData(IPath key, Object data) {
+ /* don't allow modification of the implicit root */
+ if (key.isRoot())
+ return;
+
+ Assert.isNotNull(key);
+ // Clear the lookup cache, in case the element being modified is the same
+ // as for the last lookup.
+ lookupCache = lookupCacheIgnoreCase = null;
+ try {
+ tree.setData(key, data);
+ } catch (ObjectNotFoundException e) {
+ elementNotFound(key);
+ }
+ }
+
+ /**
+ * Sets the user data associated with this tree.
+ */
+ public void setTreeData(IElementTreeData data) {
+ userData = data;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IStringPoolParticipant
+ */
+ public void shareStrings(StringPool set) {
+ tree.storeStrings(set);
+ }
+
+ /**
+ * Returns a string representation of this element tree's
+ * structure suitable for debug purposes.
+ */
+ public String toDebugString() {
+ final StringBuffer buffer = new StringBuffer("\n"); //$NON-NLS-1$
+ IElementContentVisitor visitor = new IElementContentVisitor() {
+ public boolean visitElement(ElementTree aTree, IPathRequestor elementID, Object elementContents) {
+ buffer.append(elementID.requestPath() + " " + elementContents + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ return true;
+ }
+ };
+ new ElementTreeIterator(this, Path.ROOT).iterate(visitor);
+ return buffer.toString();
+ }
+
+ public String toString() {
+ return "ElementTree(" + treeStamp + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTreeIterator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import org.eclipse.core.internal.dtree.AbstractDataTreeNode;
+import org.eclipse.core.internal.dtree.DataTreeNode;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+/**
+ * A class for performing operations on each element in an element tree.
+ * For example, this can be used to print the contents of a tree.
+ * <p>
+ * When creating an ElementTree iterator, an element tree and root path must be
+ * supplied. When the <code>iterate()</code> method is called, a visitor object
+ * must be provided. The visitor is called once for each node of the tree. For
+ * each node, the visitor is passed the entire tree, the object in the tree at
+ * that node, and a callback for requesting the full path of that node.
+ * <p>
+ * <b>Example:</b>
+ <code><pre>
+ // printing a crude representation of the poster child
+ IElementContentVisitor visitor=
+ new IElementContentVisitor() {
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+ System.out.println(requestor.requestPath() + " -> " + elementContents);
+ return true;
+ }
+ });
+ ElementTreeIterator iterator = new ElementTreeIterator(tree, Path.ROOT);
+ iterator.iterate(visitor);
+ </pre></code>
+ */
+public class ElementTreeIterator implements IPathRequestor {
+ //for path requestor
+ private String[] segments = new String[10];
+ private int nextFreeSegment;
+
+ /* the tree being visited */
+ private ElementTree tree;
+
+ /* the root of the subtree to visit */
+ private IPath path;
+
+ /* the immutable data tree being visited */
+ private DataTreeNode treeRoot;
+
+ /**
+ * Creates a new element tree iterator for visiting the given tree starting
+ * at the given path.
+ */
+ public ElementTreeIterator(ElementTree tree, IPath path) {
+ this.tree = tree;
+ this.path = path;
+ //treeRoot can be null if deleted concurrently
+ //must copy the tree while owning the tree's monitor to prevent concurrent deletion while creating visitor's copy
+ synchronized (tree) {
+ treeRoot = (DataTreeNode) tree.getDataTree().safeCopyCompleteSubtree(path);
+ }
+ }
+
+ /**
+ * Iterates through the given element tree and visit each element (node)
+ * passing in the element's ID and element object.
+ */
+ private void doIteration(DataTreeNode node, IElementContentVisitor visitor) {
+ //push the name of this node to the requestor stack
+ if (nextFreeSegment >= segments.length) {
+ grow();
+ }
+ segments[nextFreeSegment++] = node.getName();
+
+ //do the visit
+ if (visitor.visitElement(tree, this, node.getData())) {
+ //recurse
+ AbstractDataTreeNode[] children = node.getChildren();
+ for (int i = children.length; --i >= 0;) {
+ doIteration((DataTreeNode) children[i], visitor);
+ }
+ }
+
+ //pop the segment from the requestor stack
+ nextFreeSegment--;
+ if (nextFreeSegment < 0)
+ nextFreeSegment = 0;
+ }
+
+ /**
+ * Method grow.
+ */
+ private void grow() {
+ //grow the segments array
+ int oldLen = segments.length;
+ String[] newPaths = new String[oldLen * 2];
+ System.arraycopy(segments, 0, newPaths, 0, oldLen);
+ segments = newPaths;
+ }
+
+ /**
+ * Iterates through this iterator's tree and visits each element in the
+ * subtree rooted at the given path. The visitor is passed each element's
+ * data and a request callback for obtaining the path.
+ */
+ public void iterate(IElementContentVisitor visitor) {
+ if (path.isRoot()) {
+ //special visit for root element to use special treeData
+ if (visitor.visitElement(tree, this, tree.getTreeData())) {
+ if (treeRoot == null)
+ return;
+ AbstractDataTreeNode[] children = treeRoot.getChildren();
+ for (int i = children.length; --i >= 0;) {
+ doIteration((DataTreeNode) children[i], visitor);
+ }
+ }
+ } else {
+ if (treeRoot == null)
+ return;
+ push(path, path.segmentCount() - 1);
+ doIteration(treeRoot, visitor);
+ }
+ }
+
+ /**
+ * Push the first "toPush" segments of this path.
+ */
+ private void push(IPath pathToPush, int toPush) {
+ if (toPush <= 0)
+ return;
+ for (int i = 0; i < toPush; i++) {
+ if (nextFreeSegment >= segments.length) {
+ grow();
+ }
+ segments[nextFreeSegment++] = pathToPush.segment(i);
+ }
+ }
+
+ public String requestName() {
+ if (nextFreeSegment == 0)
+ return ""; //$NON-NLS-1$
+ return segments[nextFreeSegment - 1];
+ }
+
+ public IPath requestPath() {
+ if (nextFreeSegment == 0)
+ return Path.ROOT;
+ int length = nextFreeSegment;
+ for (int i = 0; i < nextFreeSegment; i++) {
+ length += segments[i].length();
+ }
+ StringBuffer pathBuf = new StringBuffer(length);
+ for (int i = 0; i < nextFreeSegment; i++) {
+ pathBuf.append('/');
+ pathBuf.append(segments[i]);
+ }
+ return new Path(null, pathBuf.toString());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTreeReader.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import java.io.*;
+import org.eclipse.core.internal.dtree.DataTreeReader;
+import org.eclipse.core.internal.dtree.IDataFlattener;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.*;
+
+/** <code>ElementTreeReader</code> is the standard implementation
+ * of an element tree serialization reader.
+ *
+ * <p>Subclasses of this reader read can handle current and various
+ * known old formats of a saved element tree, and dispatch internally
+ * to an appropriate reader.
+ *
+ * <p>The reader has an <code>IElementInfoFactory</code>,
+ * which it consults for the schema and for creating
+ * and reading element infos.
+ *
+ * <p>Element tree readers are thread-safe; several
+ * threads may share a single reader provided, of course,
+ * that the <code>IElementInfoFactory</code> is thread-safe.
+ */
+public class ElementTreeReader {
+ /** The element info factory.
+ */
+ protected IElementInfoFlattener elementInfoFlattener;
+
+ /**
+ * For reading and writing delta trees
+ */
+ protected DataTreeReader dataTreeReader;
+
+ /**
+ * Constructs a new element tree reader that works for
+ * the given element info flattener.
+ */
+ public ElementTreeReader(final IElementInfoFlattener factory) {
+ Assert.isNotNull(factory);
+ elementInfoFlattener = factory;
+
+ /* wrap the IElementInfoFlattener in an IDataFlattener */
+ IDataFlattener f = new IDataFlattener() {
+ public void writeData(IPath path, Object data, DataOutput output) {
+ //not needed
+ }
+
+ public Object readData(IPath path, DataInput input) throws IOException {
+ //never read the root node of an ElementTree
+ //this node is reserved for the parent backpointer
+ if (!Path.ROOT.equals(path))
+ return factory.readElement(path, input);
+ return null;
+ }
+ };
+ dataTreeReader = new DataTreeReader(f);
+ }
+
+ /**
+ * Returns the appropriate reader for the given version.
+ */
+ public ElementTreeReader getReader(int formatVersion) throws IOException {
+ if (formatVersion == 1)
+ return new ElementTreeReaderImpl_1(elementInfoFlattener);
+ throw new IOException(Messages.watson_unknown);
+ }
+
+ /**
+ * Reads an element tree delta from the input stream, and
+ * reconstructs it as a delta on the given tree.
+ */
+ public ElementTree readDelta(ElementTree completeTree, DataInput input) throws IOException {
+ /* Dispatch to the appropriate reader. */
+ ElementTreeReader realReader = getReader(readNumber(input));
+ return realReader.readDelta(completeTree, input);
+ }
+
+ /**
+ * Reads a chain of ElementTrees from the given input stream.
+ * @return A chain of ElementTrees, where the first tree in the list is
+ * complete, and all other trees are deltas on the previous tree in the list.
+ */
+ public ElementTree[] readDeltaChain(DataInput input) throws IOException {
+ /* Dispatch to the appropriate reader. */
+ ElementTreeReader realReader = getReader(readNumber(input));
+ return realReader.readDeltaChain(input);
+ }
+
+ /**
+ * Reads an integer stored in compact format. Numbers between
+ * 0 and 254 inclusive occupy 1 byte; other numbers occupy 5 bytes,
+ * the first byte being 0xff and the next 4 bytes being the standard
+ * representation of an int.
+ */
+ protected static int readNumber(DataInput input) throws IOException {
+ byte b = input.readByte();
+ int number = (b & 0xff); // not a no-op! converts unsigned byte to int
+
+ if (number == 0xff) { // magic escape value
+ number = input.readInt();
+ }
+ return number;
+ }
+
+ /**
+ * Reads an element tree from the input stream and returns it.
+ * This method actually just dispatches to the appropriate reader
+ * depending on the stream version id.
+ */
+ public ElementTree readTree(DataInput input) throws IOException {
+ /* Dispatch to the appropriate reader. */
+ ElementTreeReader realReader = getReader(readNumber(input));
+ return realReader.readTree(input);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTreeReaderImpl_1.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import java.io.DataInput;
+import java.io.IOException;
+import org.eclipse.core.internal.dtree.DeltaDataTree;
+
+/** <code>ElementTreeReader_1</code> is an implementation
+ * of the <code>ElementTreeReader</code> for format version 1.
+ *
+ * <p>Instances of this reader read only format 1
+ * of a saved element tree (they do not deal with
+ * compatibility issues).
+ *
+ * @see ElementTreeReader
+ */
+/* package */class ElementTreeReaderImpl_1 extends ElementTreeReader {
+
+ /**
+ * Constructs a new element tree reader that works for
+ * the given element info factory.
+ */
+ ElementTreeReaderImpl_1(IElementInfoFlattener factory) {
+ super(factory);
+ }
+
+ /**
+ * Reads an element tree delta from the input stream, and
+ * reconstructs it as a delta on the given tree.
+ */
+ public ElementTree readDelta(ElementTree parentTree, DataInput input) throws IOException {
+ DeltaDataTree complete = parentTree.getDataTree();
+ DeltaDataTree delta = dataTreeReader.readTree(complete, input);
+
+ //if the delta is empty, just return the parent
+ if (delta.isEmptyDelta())
+ return parentTree;
+
+ ElementTree tree = new ElementTree(delta);
+
+ //copy the user data forward
+ IElementTreeData data = parentTree.getTreeData();
+ if (data != null) {
+ tree.setTreeData((IElementTreeData) data.clone());
+ }
+
+ //make the underlying data tree immutable
+ //can't call immutable() on the ElementTree because
+ //this would attempt to reroot.
+ delta.immutable();
+ return tree;
+ }
+
+ /**
+ * Reads a chain of ElementTrees from the given input stream.
+ * @return A chain of ElementTrees, where the first tree in the list is
+ * complete, and all other trees are deltas on the previous tree in the list.
+ */
+ public ElementTree[] readDeltaChain(DataInput input) throws IOException {
+ /* read the number of trees */
+ int treeCount = readNumber(input);
+ ElementTree[] results = new ElementTree[treeCount];
+
+ if (treeCount <= 0) {
+ return results;
+ }
+
+ /* read the sort order */
+ int[] order = new int[treeCount];
+ for (int i = 0; i < treeCount; i++) {
+ order[i] = readNumber(input);
+ }
+
+ /* read the complete tree */
+ results[order[0]] = super.readTree(input);
+
+ /* reconstitute each of the remaining trees from their written deltas */
+ for (int i = 1; i < treeCount; i++) {
+ results[order[i]] = super.readDelta(results[order[i - 1]], input);
+ }
+
+ return results;
+ }
+
+ /** Part of <code>ElementTreeReader</code> interface.
+ * @see ElementTreeReader
+ */
+ public ElementTree readTree(DataInput input) throws IOException {
+
+ /* The format version number has already been consumed
+ * by ElementTreeReader#readFrom.
+ */
+ ElementTree result = new ElementTree(dataTreeReader.readTree(null, input));
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTreeWriter.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.dtree.*;
+import org.eclipse.core.runtime.*;
+
+/** <code>ElementTreeWriter</code> flattens an ElementTree
+ * onto a data output stream.
+ *
+ * <p>This writer generates the most up-to-date format
+ * of a saved element tree (cf. readers, which must usually also
+ * deal with backward compatibility issues). The flattened
+ * representation always includes a format version number.
+ *
+ * <p>The writer has an <code>IElementInfoFactory</code>,
+ * which it consults for writing element infos.
+ *
+ * <p>Element tree writers are thread-safe; several
+ * threads may share a single writer.
+ *
+ */
+public class ElementTreeWriter {
+ /**
+ * The current format version number.
+ */
+ public static final int CURRENT_FORMAT = 1;
+
+ /**
+ * Constant representing infinite depth
+ */
+ public static final int D_INFINITE = DataTreeWriter.D_INFINITE;
+
+ /**
+ * For writing DeltaDataTrees
+ */
+ protected DataTreeWriter dataTreeWriter;
+
+ /**
+ * Constructs a new element tree writer that works for
+ * the given element info flattener.
+ */
+ public ElementTreeWriter(final IElementInfoFlattener flattener) {
+
+ /* wrap the IElementInfoFlattener in an IDataFlattener */
+ IDataFlattener f = new IDataFlattener() {
+ public void writeData(IPath path, Object data, DataOutput output) throws IOException {
+ // never write the root node of an ElementTree
+ //because it contains the parent backpointer.
+ if (!Path.ROOT.equals(path)) {
+ flattener.writeElement(path, data, output);
+ }
+ }
+
+ public Object readData(IPath path, DataInput input) {
+ return null;
+ }
+ };
+ dataTreeWriter = new DataTreeWriter(f);
+ }
+
+ /**
+ * Sorts the given array of trees so that the following rules are true:
+ * - The first tree has no parent
+ * - No tree has an ancestor with a greater index in the array.
+ * If there are no missing parents in the given trees array, this means
+ * that in the resulting array, the i'th tree's parent will be tree i-1.
+ * The input tree array may contain duplicate trees.
+ * The sort order is written to the given output stream.
+ */
+ protected ElementTree[] sortTrees(ElementTree[] trees, DataOutput output) throws IOException {
+
+ /* the sorted list */
+ int numTrees = trees.length;
+ ElementTree[] sorted = new ElementTree[numTrees];
+ int[] order = new int[numTrees];
+
+ /* first build a table of ElementTree -> Vector of Integers(indices in trees array) */
+ HashMap table = new HashMap(numTrees * 2 + 1);
+ for (int i = 0; i < trees.length; i++) {
+ List indices = (List) table.get(trees[i]);
+ if (indices == null) {
+ indices = new ArrayList();
+ table.put(trees[i], indices);
+ }
+ indices.add(new Integer(i));
+ }
+
+ /* find the oldest tree (a descendent of all other trees) */
+ ElementTree oldest = trees[ElementTree.findOldest(trees)];
+
+ /**
+ * Walk through the chain of trees from oldest to newest,
+ * adding them to the sorted list as we go.
+ */
+ int i = numTrees - 1;
+ while (i >= 0) {
+ /* add all instances of the current oldest tree to the sorted list */
+ List indices = (List) table.remove(oldest);
+ for (Enumeration e = Collections.enumeration(indices); e.hasMoreElements();) {
+ Integer next = (Integer) e.nextElement();
+ sorted[i] = oldest;
+ order[i] = next.intValue();
+ i--;
+ }
+ if (i >= 0) {
+ /* find the next tree in the list */
+ ElementTree parent = oldest.getParent();
+ while (table.get(parent) == null) {
+ parent = parent.getParent();
+ }
+ oldest = parent;
+ }
+ }
+
+ /* write the order array */
+ for (i = 0; i < numTrees; i++) {
+ writeNumber(order[i], output);
+ }
+ return sorted;
+ }
+
+ /**
+ * Writes the delta describing the changes that have to be made
+ * to newerTree to obtain olderTree.
+ *
+ * @param path The path of the subtree to write. All nodes on the path above
+ * the subtree are represented as empty nodes.
+ * @param depth The depth of the subtree to write. A depth of zero writes a
+ * single node, and a depth of D_INFINITE writes the whole subtree.
+ * @param output The stream to write the subtree to.
+ */
+ public void writeDelta(ElementTree olderTree, ElementTree newerTree, IPath path, int depth, final DataOutput output, IElementComparator comparator) throws IOException {
+
+ /* write the version number */
+ writeNumber(CURRENT_FORMAT, output);
+
+ /**
+ * Note that in current ElementTree usage, the newest
+ * tree is the complete tree, and older trees are just
+ * deltas on the new tree.
+ */
+ DeltaDataTree completeTree = newerTree.getDataTree();
+ DeltaDataTree derivedTree = olderTree.getDataTree();
+ DeltaDataTree deltaToWrite = null;
+
+ deltaToWrite = completeTree.forwardDeltaWith(derivedTree, comparator);
+
+ Assert.isTrue(deltaToWrite.isImmutable());
+ dataTreeWriter.writeTree(deltaToWrite, path, depth, output);
+ }
+
+ /**
+ * Writes an array of ElementTrees to the given output stream.
+ * @param trees A chain of ElementTrees, where on tree in the list is
+ * complete, and all other trees are deltas on the previous tree in the list.
+ * @param path The path of the subtree to write. All nodes on the path above
+ * the subtree are represented as empty nodes.
+ * @param depth The depth of the subtree to write. A depth of zero writes a
+ * single node, and a depth of D_INFINITE writes the whole subtree.
+ * @param output The stream to write the subtree to.
+
+ */
+ public void writeDeltaChain(ElementTree[] trees, IPath path, int depth, DataOutput output, IElementComparator comparator) throws IOException {
+ /* Write the format version number */
+ writeNumber(CURRENT_FORMAT, output);
+
+ /* Write the number of trees */
+ int treeCount = trees.length;
+ writeNumber(treeCount, output);
+
+ if (treeCount <= 0) {
+ return;
+ }
+
+ /**
+ * Sort the trees in ancestral order,
+ * which writes the tree order to the output
+ */
+ ElementTree[] sortedTrees = sortTrees(trees, output);
+
+ /* Write the complete tree */
+ writeTree(sortedTrees[0], path, depth, output);
+
+ /* Write the deltas for each of the remaining trees */
+ for (int i = 1; i < treeCount; i++) {
+ writeDelta(sortedTrees[i], sortedTrees[i - 1], path, depth, output, comparator);
+ }
+ }
+
+ /**
+ * Writes an integer in a compact format biased towards
+ * small non-negative numbers. Numbers between
+ * 0 and 254 inclusive occupy 1 byte; other numbers occupy 5 bytes.
+ */
+ protected void writeNumber(int number, DataOutput output) throws IOException {
+ if (number >= 0 && number < 0xff) {
+ output.writeByte(number);
+ } else {
+ output.writeByte(0xff);
+ output.writeInt(number);
+ }
+ }
+
+ /**
+ * Writes all or some of an element tree to an output stream.
+ * This always writes the most current version of the element tree
+ * file format, whereas the reader supports multiple versions.
+ *
+ * @param tree The tree to write
+ * @param path The path of the subtree to write. All nodes on the path above
+ * the subtree are represented as empty nodes.
+ * @param depth The depth of the subtree to write. A depth of zero writes a
+ * single node, and a depth of D_INFINITE writes the whole subtree.
+ * @param output The stream to write the subtree to.
+ */
+ public void writeTree(ElementTree tree, IPath path, int depth, final DataOutput output) throws IOException {
+
+ /* Write the format version number. */
+ writeNumber(CURRENT_FORMAT, output);
+
+ /* This actually just copies the root node, which is what we want */
+ DeltaDataTree subtree = new DeltaDataTree(tree.getDataTree().copyCompleteSubtree(Path.ROOT));
+
+ dataTreeWriter.writeTree(subtree, path, depth, output);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/IElementComparator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import org.eclipse.core.internal.dtree.IComparator;
+
+/**
+ * This interface allows clients of the element tree to specify
+ * how element infos are compared, and thus how element tree deltas
+ * are created.
+ */
+public interface IElementComparator extends IComparator {
+ /**
+ * The kinds of changes
+ */
+ public int K_NO_CHANGE = 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/IElementContentVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+/**
+ * An interface for objects which can visit an element of
+ * an element tree and access that element's node info.
+ * @see ElementTreeIterator
+ */
+public interface IElementContentVisitor {
+ /** Visits a node (element).
+ * <p> Note that <code>elementContents</code> is equal to<code>tree.
+ * getElement(elementPath)</code> but takes no time.
+ * @param tree the element tree being visited
+ * @param elementContents the object at the node being visited on this call
+ * @param requestor callback object for requesting the path of the object being
+ * visited.
+ * @return true if this element's children should be visited, and false
+ * otherwise.
+ */
+ public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/IElementInfoFlattener.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import java.io.*;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * The <code>IElementInfoFlattener</code> interface supports
+ * reading and writing element info objects.
+ */
+public interface IElementInfoFlattener {
+ /**
+ * Reads an element info from the given input stream.
+ * @param elementPath the path of the element to be read
+ * @param input the stream from which the element info should be read.
+ * @return the object associated with the given elementPath,
+ * which may be <code>null</code>.
+ */
+ public Object readElement(IPath elementPath, DataInput input) throws IOException;
+
+ /**
+ * Writes the given element to the output stream.
+ * <p> N.B. The bytes written must be sufficient for the
+ * purposes of reading the object back in.
+ * @param elementPath the element's path in the tree
+ * @param element the object associated with the given path,
+ * which may be <code>null</code>.
+ */
+ public void writeElement(IPath elementPath, Object element, DataOutput output) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/IElementTreeData.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+/**
+ * User data that can be attached to the element tree itself.
+ */
+public interface IElementTreeData extends Cloneable {
+ /**
+ * ElementTreeData must define a publicly accessible clone method.
+ * This method can simply invoke Object's clone method.
+ */
+ public Object clone();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/IPathRequestor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.watson;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Callback interface so visitors can request the path of the object they
+ * are visiting. This avoids creating paths when they are not needed.
+ */
+public interface IPathRequestor {
+ public IPath requestPath();
+
+ public String requestName();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ICommand.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.Map;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A builder command names a builder and supplies a table of
+ * name-value argument pairs.
+ * <p>
+ * Changes to a command will only take effect if the modified command is installed
+ * into a project description via {@link IProjectDescription#setBuildSpec(ICommand[])}.
+ * </p>
+ *
+ * @see IProjectDescription
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ICommand {
+
+ /**
+ * Returns a table of the arguments for this command, or <code>null</code>
+ * if there are no arguments. The argument names and values are both strings.
+ *
+ * @return a table of command arguments (key type : <code>String</code>
+ * value type : <code>String</code>), or <code>null</code>
+ * @see #setArguments(Map)
+ */
+ public Map getArguments();
+
+ /**
+ * Returns the name of the builder to run for this command, or
+ * <code>null</code> if the name has not been set.
+ *
+ * @return the name of the builder, or <code>null</code> if not set
+ * @see #setBuilderName(String)
+ */
+ public String getBuilderName();
+
+ /**
+ * Returns whether this build command responds to the given kind of build.
+ * <p>
+ * By default, build commands respond to all kinds of builds.
+ * </p>
+ *
+ * @param kind One of the <tt>*_BUILD</code> constants defined
+ * on <code>IncrementalProjectBuilder</code>
+ * @return <code>true</code> if this build command responds to the specified
+ * kind of build, and <code>false</code> otherwise.
+ * @see #setBuilding(int, boolean)
+ * @since 3.1
+ */
+ public boolean isBuilding(int kind);
+
+ /**
+ * Returns whether this command allows configuring of what kinds of builds
+ * it responds to. By default, commands are only configurable
+ * if the corresponding builder defines the {@link #isConfigurable}
+ * attribute in its builder extension declaration. A command that is not
+ * configurable will always respond to all kinds of builds.
+ *
+ * @return <code>true</code> If this command allows configuration of
+ * what kinds of builds it responds to, and <code>false</code> otherwise.
+ * @see #setBuilding(int, boolean)
+ * @since 3.1
+ */
+ public boolean isConfigurable();
+
+ /**
+ * Sets this command's arguments to be the given table of name-values
+ * pairs, or to <code>null</code> if there are no arguments. The argument
+ * names and values are both strings.
+ * <p>
+ * Individual builders specify their argument expectations.
+ * </p>
+ * <p>
+ * Note that modifications to the arguments of a command
+ * being used in a running builder may affect the run of that builder
+ * but will not affect any subsequent runs. To change a command
+ * permanently you must install the command into the relevant project
+ * build spec using {@link IProjectDescription#setBuildSpec(ICommand[])}.
+ * </p>
+ *
+ * @param args a table of command arguments (keys and values must
+ * both be of type <code>String</code>), or <code>null</code>
+ * @see #getArguments()
+ */
+ public void setArguments(Map args);
+
+ /**
+ * Sets the name of the builder to run for this command.
+ * <p>
+ * The builder name comes from the extension that plugs in
+ * to the standard <code>org.eclipse.core.resources.builders</code>
+ * extension point.
+ * </p>
+ *
+ * @param builderName the name of the builder
+ * @see #getBuilderName()
+ */
+ public void setBuilderName(String builderName);
+
+ /**
+ * Specifies whether this build command responds to the provided kind of build.
+ * <p>
+ * When a command is configured to not respond to a given kind of build, the
+ * builder instance will not be called when a build of that kind is initiated.
+ * </p><p>
+ * This method has no effect if this build command does not allow its
+ * build kinds to be configured.
+ * </p>
+ *
+ * @param kind One of the <tt>*_BUILD</code> constants defined
+ * on <code>IncrementalProjectBuilder</code>
+ * @param value <code>true</code> if this build command responds to the
+ * specified kind of build, and <code>false</code> otherwise.
+ * @see #isBuilding(int)
+ * @see #isConfigurable()
+ * @see IWorkspace#build(int, IProgressMonitor)
+ * @see IProject#build(int, IProgressMonitor)
+ * @since 3.1
+ */
+ public void setBuilding(int kind, boolean value);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IContainer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,468 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Interface for resources which may contain
+ * other resources (termed its <em>members</em>). While the
+ * workspace itself is not considered a container in this sense, the
+ * workspace root resource is a container.
+ * <p>
+ * Containers implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see Platform#getAdapterManager()
+ * @see IProject
+ * @see IFolder
+ * @see IWorkspaceRoot
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IContainer extends IResource, IAdaptable {
+
+ /*====================================================================
+ * Constants defining which members are wanted:
+ *====================================================================*/
+
+ /**
+ * Member constant (bit mask value 1) indicating that phantom member resources are
+ * to be included.
+ *
+ * @see IResource#isPhantom()
+ * @since 2.0
+ */
+ public static final int INCLUDE_PHANTOMS = 1;
+
+ /**
+ * Member constant (bit mask value 2) indicating that team private members are
+ * to be included.
+ *
+ * @see IResource#isTeamPrivateMember()
+ * @since 2.0
+ */
+ public static final int INCLUDE_TEAM_PRIVATE_MEMBERS = 2;
+
+ /**
+ * Member constant (bit mask value 4) indicating that derived resources
+ * are to be excluded.
+ *
+ * @see IResource#isDerived()
+ * @since 3.1
+ */
+ public static final int EXCLUDE_DERIVED = 4;
+
+ /**
+ * Member constant (bit mask value 8) indicating that hidden resources
+ * are to be included.
+ *
+ * @see IResource#isHidden()
+ * @since 3.4
+ */
+ public static final int INCLUDE_HIDDEN = 8;
+
+ /**
+ * Returns whether a resource of some type with the given path
+ * exists relative to this resource.
+ * The supplied path may be absolute or relative; in either case, it is
+ * interpreted as relative to this resource. Trailing separators are ignored.
+ * If the path is empty this container is checked for existence.
+ *
+ * @param path the path of the resource
+ * @return <code>true</code> if a resource of some type with the given path
+ * exists relative to this resource, and <code>false</code> otherwise
+ * @see IResource#exists()
+ */
+ public boolean exists(IPath path);
+
+ /**
+ * Finds and returns the member resource (project, folder, or file)
+ * with the given name in this container, or <code>null</code> if no such
+ * resource exists.
+ *
+ * <p> N.B. Unlike the methods which traffic strictly in resource
+ * handles, this method infers the resulting resource's type from the
+ * resource existing at the calculated path in the workspace.
+ * </p>
+ *
+ * @param name the string name of the member resource
+ * @return the member resource, or <code>null</code> if no such
+ * resource exists
+ */
+ public IResource findMember(String name);
+
+ /**
+ * Finds and returns the member resource (project, folder, or file)
+ * with the given name in this container, or <code>null</code> if
+ * there is no such resource.
+ * <p>
+ * If the <code>includePhantoms</code> argument is <code>false</code>,
+ * only a member resource with the given name that exists will be returned.
+ * If the <code>includePhantoms</code> argument is <code>true</code>,
+ * the method also returns a resource if the workspace is keeping track of a
+ * phantom with that name.
+ * </p><p>
+ * Note that no attempt is made to exclude team-private member resources
+ * as with <code>members</code>.
+ * </p><p>
+ * N.B. Unlike the methods which traffic strictly in resource
+ * handles, this method infers the resulting resource's type from the
+ * existing resource (or phantom) in the workspace.
+ * </p>
+ *
+ * @param name the string name of the member resource
+ * @param includePhantoms <code>true</code> if phantom resources are
+ * of interest; <code>false</code> if phantom resources are not of
+ * interest
+ * @return the member resource, or <code>null</code> if no such
+ * resource exists
+ * @see #members()
+ * @see IResource#isPhantom()
+ */
+ public IResource findMember(String name, boolean includePhantoms);
+
+ /**
+ * Finds and returns the member resource identified by the given path in
+ * this container, or <code>null</code> if no such resource exists.
+ * The supplied path may be absolute or relative; in either case, it is
+ * interpreted as relative to this resource. Trailing separators and the path's
+ * device are ignored. If the path is empty this container is returned. Parent
+ * references in the supplied path are discarded if they go above the workspace
+ * root.
+ * <p>
+ * Note that no attempt is made to exclude team-private member resources
+ * as with <code>members</code>.
+ * </p><p>
+ * N.B. Unlike the methods which traffic strictly in resource
+ * handles, this method infers the resulting resource's type from the
+ * resource existing at the calculated path in the workspace.
+ * </p>
+ *
+ * @param path the path of the desired resource
+ * @return the member resource, or <code>null</code> if no such
+ * resource exists
+ */
+ public IResource findMember(IPath path);
+
+ /**
+ * Finds and returns the member resource identified by the given path in
+ * this container, or <code>null</code> if there is no such resource.
+ * The supplied path may be absolute or relative; in either case, it is
+ * interpreted as relative to this resource. Trailing separators and the path's
+ * device are ignored. If the path is empty this container is returned.
+ * Parent references in the supplied path are discarded if they go above the
+ * workspace root.
+ * <p>
+ * If the <code>includePhantoms</code> argument is <code>false</code>,
+ * only a resource that exists at the given path will be returned.
+ * If the <code>includePhantoms</code> argument is <code>true</code>,
+ * the method also returns a resource if the workspace is keeping track of
+ * a phantom member resource at the given path.
+ * </p><p>
+ * Note that no attempt is made to exclude team-private member resources
+ * as with <code>members</code>.
+ * </p><p>
+ * N.B. Unlike the methods which traffic strictly in resource
+ * handles, this method infers the resulting resource's type from the
+ * existing resource (or phantom) at the calculated path in the workspace.
+ * </p>
+ *
+ * @param path the path of the desired resource
+ * @param includePhantoms <code>true</code> if phantom resources are
+ * of interest; <code>false</code> if phantom resources are not of
+ * interest
+ * @return the member resource, or <code>null</code> if no such
+ * resource exists
+ * @see #members(boolean)
+ * @see IResource#isPhantom()
+ */
+ public IResource findMember(IPath path, boolean includePhantoms);
+
+ /**
+ * Returns the default charset for resources in this container.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * getDefaultCharset(true);
+ * </pre>
+ * </p><p>
+ * Note that this method does not check whether the result is a supported
+ * charset name. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this charset is used.
+ * </p>
+ *
+ * @return the name of the default charset encoding for this container
+ * @exception CoreException if this method fails
+ * @see IContainer#getDefaultCharset(boolean)
+ * @see IFile#getCharset()
+ * @since 3.0
+ */
+ public String getDefaultCharset() throws CoreException;
+
+ /**
+ * Returns the default charset for resources in this container.
+ * <p>
+ * If checkImplicit is <code>false</code>, this method
+ * will return the charset defined by calling #setDefaultCharset, provided this
+ * container exists, or <code>null</code> otherwise.
+ * </p><p>
+ * If checkImplicit is <code>true</code>, this method uses the following
+ * algorithm to determine the charset to be returned:
+ * <ol>
+ * <li>the one explicitly set by calling #setDefaultCharset
+ * (with a non-null argument) on this container, if any, and this container
+ * exists, or</li>
+ * <li>the parent's default charset, if this container has a parent (is not the
+ * workspace root), or</li>
+ * <li>the charset returned by ResourcesPlugin#getEncoding.</li>
+ * </ol>
+ * </p><p>
+ * Note that this method does not check whether the result is a supported
+ * charset name. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this charset is used.
+ * </p>
+ * @return the name of the default charset encoding for this container,
+ * or <code>null</code>
+ * @exception CoreException if this method fails
+ * @see IFile#getCharset()
+ * @since 3.0
+ */
+ public String getDefaultCharset(boolean checkImplicit) throws CoreException;
+
+ /**
+ * Returns a handle to the file identified by the given path in this
+ * container.
+ * <p>
+ * This is a resource handle operation; neither the resource nor
+ * the result need exist in the workspace.
+ * The validation check on the resource name/path is not done
+ * when the resource handle is constructed; rather, it is done
+ * automatically as the resource is created.
+ * <p>
+ * The supplied path may be absolute or relative; in either case, it is
+ * interpreted as relative to this resource and is appended
+ * to this container's full path to form the full path of the resultant resource.
+ * A trailing separator is ignored. The path of the resulting resource must
+ * have at least two segments.
+ * </p>
+ *
+ * @param path the path of the member file
+ * @return the (handle of the) member file
+ * @see #getFolder(IPath)
+ */
+ public IFile getFile(IPath path);
+
+ /**
+ * Returns a handle to the folder identified by the given path in this
+ * container.
+ * <p>
+ * This is a resource handle operation; neither the resource nor
+ * the result need exist in the workspace.
+ * The validation check on the resource name/path is not done
+ * when the resource handle is constructed; rather, it is done
+ * automatically as the resource is created.
+ * <p>
+ * The supplied path may be absolute or relative; in either case, it is
+ * interpreted as relative to this resource and is appended
+ * to this container's full path to form the full path of the resultant resource.
+ * A trailing separator is ignored. The path of the resulting resource must
+ * have at least two segments.
+ * </p>
+ *
+ * @param path the path of the member folder
+ * @return the (handle of the) member folder
+ * @see #getFile(IPath)
+ */
+ public IFolder getFolder(IPath path);
+
+ /**
+ * Returns a list of existing member resources (projects, folders and files)
+ * in this resource, in no particular order.
+ * <p>
+ * This is a convenience method, fully equivalent to <code>members(IResource.NONE)</code>.
+ * Team-private member resources are <b>not</b> included in the result.
+ * </p><p>
+ * Note that the members of a project or folder are the files and folders
+ * immediately contained within it. The members of the workspace root
+ * are the projects in the workspace.
+ * </p>
+ *
+ * @return an array of members of this resource
+ * @exception CoreException if this request fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see #findMember(IPath)
+ * @see IResource#isAccessible()
+ */
+ public IResource[] members() throws CoreException;
+
+ /**
+ * Returns a list of all member resources (projects, folders and files)
+ * in this resource, in no particular order.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * members(includePhantoms ? INCLUDE_PHANTOMS : IResource.NONE);
+ * </pre>
+ * Team-private member resources are <b>not</b> included in the result.
+ * </p>
+ *
+ * @param includePhantoms <code>true</code> if phantom resources are
+ * of interest; <code>false</code> if phantom resources are not of
+ * interest
+ * @return an array of members of this resource
+ * @exception CoreException if this request fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> <code>includePhantoms</code> is <code>false</code> and
+ * this resource does not exist.</li>
+ * <li> <code>includePhantoms</code> is <code>false</code> and
+ * this resource is a project that is not open.</li>
+ * </ul>
+ * @see #members(int)
+ * @see IResource#exists()
+ * @see IResource#isPhantom()
+ */
+ public IResource[] members(boolean includePhantoms) throws CoreException;
+
+ /**
+ * Returns a list of all member resources (projects, folders and files)
+ * in this resource, in no particular order.
+ * <p>
+ * If the <code>INCLUDE_PHANTOMS</code> flag is not specified in the member
+ * flags (recommended), only member resources that exist will be returned.
+ * If the <code>INCLUDE_PHANTOMS</code> flag is specified,
+ * the result will also include any phantom member resources the
+ * workspace is keeping track of.
+ * </p><p>
+ * If the <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code> flag is specified
+ * in the member flags, team private members will be included along with
+ * the others. If the <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code> flag
+ * is not specified (recommended), the result will omit any team private
+ * member resources.
+ * </p><p>
+ * If the {@link #INCLUDE_HIDDEN} flag is specified in the member flags, hidden
+ * members will be included along with the others. If the {@link #INCLUDE_HIDDEN} flag
+ * is not specified (recommended), the result will omit any hidden
+ * member resources.
+ * </p>
+ * <p>
+ * If the <code>EXCLUDE_DERIVED</code> flag is not specified, derived
+ * resources are included. If the <code>EXCLUDE_DERIVED</code> flag is
+ * specified in the member flags, derived resources are not included.
+ * </p>
+ *
+ * @param memberFlags bit-wise or of member flag constants
+ * ({@link #INCLUDE_PHANTOMS}, {@link #INCLUDE_TEAM_PRIVATE_MEMBERS},
+ * {@link #INCLUDE_HIDDEN} and {@link #EXCLUDE_DERIVED}) indicating which members are of interest
+ * @return an array of members of this resource
+ * @exception CoreException if this request fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> the <code>INCLUDE_PHANTOMS</code> flag is not specified and
+ * this resource does not exist.</li>
+ * <li> the <code>INCLUDE_PHANTOMS</code> flag is not specified and
+ * this resource is a project that is not open.</li>
+ * </ul>
+ * @see IResource#exists()
+ * @since 2.0
+ */
+ public IResource[] members(int memberFlags) throws CoreException;
+
+ /**
+ * Returns a list of recently deleted files inside this container that
+ * have one or more saved states in the local history. The depth parameter
+ * determines how deep inside the container to look. This resource may or
+ * may not exist in the workspace.
+ * <p>
+ * When applied to an existing project resource, this method returns recently
+ * deleted files with saved states in that project. Note that local history is
+ * maintained with each individual project, and gets discarded when a project
+ * is deleted from the workspace. If applied to a deleted project, this method
+ * returns the empty list.
+ * </p><p>
+ * When applied to the workspace root resource (depth infinity), this method
+ * returns all recently deleted files with saved states in all existing projects.
+ * </p><p>
+ * When applied to a folder (or project) resource (depth one),
+ * this method returns all recently deleted member files with saved states.
+ * </p><p>
+ * When applied to a folder resource (depth zero),
+ * this method returns an empty list unless there was a recently deleted file
+ * with saved states at the same path as the folder.
+ * </p><p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param depth depth limit: one of <code>DEPTH_ZERO</code>, <code>DEPTH_ONE</code>
+ * or <code>DEPTH_INFINITE</code>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ * @return an array of recently deleted files
+ * @exception CoreException if this method fails
+ * @see IFile#getHistory(IProgressMonitor)
+ * @since 2.0
+ */
+ public IFile[] findDeletedMembersWithHistory(int depth, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the default charset for this container. Passing a value of <code>null</code>
+ * will remove the default charset setting for this resource.
+ *
+ * @param charset a charset string, or <code>null</code>
+ * @exception CoreException if this method fails Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> An error happened while persisting this setting.</li>
+ * </ul>
+ * @see IContainer#getDefaultCharset()
+ * @since 3.0
+ * @deprecated Replaced by {@link #setDefaultCharset(String, IProgressMonitor)} which
+ * is a workspace operation and reports changes in resource deltas.
+ */
+ public void setDefaultCharset(String charset) throws CoreException;
+
+ /**
+ * Sets the default charset for this container. Passing a value of <code>null</code>
+ * will remove the default charset setting for this resource.
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the encoding of affected resources has been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param charset a charset string, or <code>null</code>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @exception CoreException if this method fails Reasons include:
+ * <ul>
+ * <li> This resource is not accessible.</li>
+ * <li> An error happened while persisting this setting.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See {@link IResourceChangeEvent} for more details.</li>
+ * </ul>
+ * @see IContainer#getDefaultCharset()
+ * @see IResourceRuleFactory#charsetRule(IResource)
+ * @since 3.0
+ */
+ public void setDefaultCharset(String charset, IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IEncodedStorage.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A storage that knows how its contents are encoded.
+ *
+ * <p>The <code>IEncodedStorage</code> interface extends <code>IStorage</code>
+ * in order to provide access to the charset to be used when decoding its
+ * contents.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface IEncodedStorage extends IStorage {
+ /**
+ * Returns the name of a charset encoding to be used when decoding this
+ * storage's contents into characters. Returns <code>null</code> if a proper
+ * encoding cannot be determined.
+ * <p>
+ * Note that this method does not check whether the result is a supported
+ * charset name. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this charset is used.
+ * </p>
+ *
+ * @return the name of a charset, or <code>null</code>
+ * @exception CoreException if an error happens while determining
+ * the charset. See any refinements for more information.
+ * @see IStorage#getContents()
+ */
+ public String getCharset() throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IFile.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1112 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URI;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+
+/**
+ * Files are leaf resources which contain data.
+ * The contents of a file resource is stored as a file in the local
+ * file system.
+ * <p>
+ * Files, like folders, may exist in the workspace but
+ * not be local; non-local file resources serve as place-holders for
+ * files whose content and properties have not yet been fetched from
+ * a repository.
+ * </p>
+ * <p>
+ * Files implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IFile extends IResource, IEncodedStorage, IAdaptable {
+ /**
+ * Character encoding constant (value 0) which identifies
+ * files that have an unknown character encoding scheme.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_UNKNOWN = 0;
+ /**
+ * Character encoding constant (value 1) which identifies
+ * files that are encoded with the US-ASCII character encoding scheme.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_US_ASCII = 1;
+ /**
+ * Character encoding constant (value 2) which identifies
+ * files that are encoded with the ISO-8859-1 character encoding scheme,
+ * also known as ISO-LATIN-1.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_ISO_8859_1 = 2;
+ /**
+ * Character encoding constant (value 3) which identifies
+ * files that are encoded with the UTF-8 character encoding scheme.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_UTF_8 = 3;
+ /**
+ * Character encoding constant (value 4) which identifies
+ * files that are encoded with the UTF-16BE character encoding scheme.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_UTF_16BE = 4;
+ /**
+ * Character encoding constant (value 5) which identifies
+ * files that are encoded with the UTF-16LE character encoding scheme.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_UTF_16LE = 5;
+ /**
+ * Character encoding constant (value 6) which identifies
+ * files that are encoded with the UTF-16 character encoding scheme.
+ *
+ * @see IFile#getEncoding()
+ * @deprecated see getEncoding for details
+ */
+ public int ENCODING_UTF_16 = 6;
+
+ /**
+ * Appends the entire contents of the given stream to this file.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * appendContents(source, (keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's content have been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancelation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source an input stream containing the new contents of the file
+ * @param force a flag controlling how to deal with resources that
+ * are not in sync with the local file system
+ * @param keepHistory a flag indicating whether or not to store
+ * the current contents in the local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and <code>force </code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #appendContents(java.io.InputStream,int,IProgressMonitor)
+ */
+ public void appendContents(InputStream source, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Appends the entire contents of the given stream to this file.
+ * The stream, which must not be <code>null</code>, will get closed
+ * whether this method succeeds or fails.
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If <code>FORCE</code> is not specified, the method will only attempt
+ * to overwrite a corresponding file in the local file system provided
+ * it is in sync with the workspace. This option ensures there is no
+ * unintended data loss; it is the recommended setting.
+ * However, if <code>FORCE</code> is specified, an attempt will be made
+ * to write a corresponding file in the local file system, overwriting any
+ * existing one if need be. In either case, if this method succeeds, the
+ * resource will be marked as being local (even if it wasn't before).
+ * </p>
+ * <p>
+ * If this file is non-local then this method will always fail. The only exception
+ * is when <code>FORCE</code> is specified and the file exists in the local
+ * file system. In this case the file is made local and the given contents are appended.
+ * </p>
+ * <p>
+ * The <code>KEEP_HISTORY</code> update flag controls whether or not a copy of
+ * current contents of this file should be captured in the workspace's local
+ * history (properties are not recorded in the local history). The local history
+ * mechanism serves as a safety net to help the user recover from mistakes that
+ * might otherwise result in data loss. Specifying <code>KEEP_HISTORY</code>
+ * is recommended except in circumstances where past states of the files are of
+ * no conceivable interest to the user. Note that local history is maintained
+ * with each individual project, and gets discarded when a project is deleted
+ * from the workspace. This flag is ignored if the file was not previously local.
+ * </p>
+ * <p>
+ * Update flags other than <code>FORCE</code> and <code>KEEP_HISTORY</code>
+ * are ignored.
+ * </p>
+ * <p>
+ * Prior to modifying the contents of this file, the file modification validator (if provided
+ * by the VCM plug-in), will be given a chance to perform any last minute preparations. Validation
+ * is performed by calling <code>IFileModificationValidator.validateSave</code> on this file.
+ * If the validation fails, then this operation will fail.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's content have been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancelation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source an input stream containing the new contents of the file
+ * @param updateFlags bit-wise or of update flag constants
+ * (<code>FORCE</code> and <code>KEEP_HISTORY</code>)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and <code>FORCE</code> is not specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ * @since 2.0
+ */
+ public void appendContents(InputStream source, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new file resource as a member of this handle's parent resource.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * create(source, (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the file has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source an input stream containing the initial contents of the file,
+ * or <code>null</code> if the file should be marked as not local
+ * @param force a flag controlling how to deal with resources that
+ * are not in sync with the local file system
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The project of this resource is not accessible.</li>
+ * <li> The parent contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a directory.</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a file and <code>force </code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ */
+ public void create(InputStream source, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new file resource as a member of this handle's parent resource.
+ * The resource's contents are supplied by the data in the given stream.
+ * This method closes the stream whether it succeeds or fails.
+ * If the stream is <code>null</code> then a file is not created in the local
+ * file system and the created file resource is marked as being non-local.
+ * <p>
+ * The {@link IResource#FORCE} update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If {@link IResource#FORCE} is not specified, the method will only attempt
+ * to write a file in the local file system if it does not already exist.
+ * This option ensures there is no unintended data loss; it is the recommended
+ * setting. However, if {@link IResource#FORCE} is specified, this method will
+ * attempt to write a corresponding file in the local file system,
+ * overwriting any existing one if need be.
+ * </p>
+ * <p>
+ * The {@link IResource#DERIVED} update flag indicates that this resource
+ * should immediately be set as a derived resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setDerived(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * The {@link IResource#TEAM_PRIVATE} update flag indicates that this resource
+ * should immediately be set as a team private resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setTeamPrivateMember(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the file has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source an input stream containing the initial contents of the file,
+ * or <code>null</code> if the file should be marked as not local
+ * @param updateFlags bit-wise or of update flag constants
+ * ({@link IResource#FORCE}, {@link IResource#DERIVED}, and {@link IResource#TEAM_PRIVATE})
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The project of this resource is not accessible.</li>
+ * <li> The parent contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a directory.</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a file and <code>FORCE</code> is not specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceRuleFactory#createRule(IResource)
+ * @since 2.0
+ */
+ public void create(InputStream source, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new file resource as a member of this handle's parent resource.
+ * The file's contents will be located in the file specified by the given
+ * file system path. The given path must be either an absolute file system
+ * path, or a relative path whose first segment is the name of a workspace path
+ * variable.
+ * <p>
+ * The {@link IResource#ALLOW_MISSING_LOCAL} update flag controls how this
+ * method deals with cases where the local file system file to be linked does
+ * not exist, or is relative to a workspace path variable that is not defined.
+ * If {@link IResource#ALLOW_MISSING_LOCAL} is specified, the operation will succeed
+ * even if the local file is missing, or the path is relative to an undefined
+ * variable. If {@link IResource#ALLOW_MISSING_LOCAL} is not specified, the operation
+ * will fail in the case where the local file system file does not exist or the
+ * path is relative to an undefined variable.
+ * </p>
+ * <p>
+ * The {@link IResource#REPLACE} update flag controls how this
+ * method deals with cases where a resource of the same name as the
+ * prospective link already exists. If {@link IResource#REPLACE}
+ * is specified, then any existing resource with the same name is removed
+ * from the workspace to make way for creation of the link. This does <b>not</b>
+ * cause the underlying file system contents of that resource to be deleted.
+ * If {@link IResource#REPLACE} is not specified, this method will
+ * fail if an existing resource exists of the same name.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method synchronizes this resource with the local file system at the given
+ * location.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the file has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param localLocation a file system path where the file should be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * ({@link IResource#ALLOW_MISSING_LOCAL} and {@link IResource#REPLACE})
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The workspace contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The parent of this resource is not an open project</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system does not exist, or
+ * is relative to an undefined variable, and <code>ALLOW_MISSING_LOCAL</code> is
+ * not specified.</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a directory (as opposed to a file).</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li>The team provider for the project which contains this folder does not permit
+ * linked resources.</li>
+ * <li>This folder's project contains a nature which does not permit linked resources.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#isLinked()
+ * @see IResource#ALLOW_MISSING_LOCAL
+ * @since 2.1
+ */
+ public void createLink(IPath localLocation, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new file resource as a member of this handle's parent resource.
+ * The file's contents will be located in the file specified by the given
+ * URI. The given URI must be either absolute, or a relative URI whose first path
+ * segment is the name of a workspace path variable.
+ * <p>
+ * The <code>ALLOW_MISSING_LOCAL</code> update flag controls how this
+ * method deals with cases where the file system file to be linked does
+ * not exist, or is relative to a workspace path variable that is not defined.
+ * If <code>ALLOW_MISSING_LOCAL</code> is specified, the operation will succeed
+ * even if the local file is missing, or the path is relative to an undefined
+ * variable. If <code>ALLOW_MISSING_LOCAL</code> is not specified, the operation
+ * will fail in the case where the file system file does not exist or the
+ * path is relative to an undefined variable.
+ * </p>
+ * <p>
+ * The {@link IResource#REPLACE} update flag controls how this
+ * method deals with cases where a resource of the same name as the
+ * prospective link already exists. If {@link IResource#REPLACE}
+ * is specified, then any existing resource with the same name is removed
+ * from the workspace to make way for creation of the link. This does <b>not</b>
+ * cause the underlying file system contents of that resource to be deleted.
+ * If {@link IResource#REPLACE} is not specified, this method will
+ * fail if an existing resource exists of the same name.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method synchronizes this resource with the file system at the given
+ * location.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the file has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param location a file system URI where the file should be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * ({@link IResource#ALLOW_MISSING_LOCAL} and {@link IResource#REPLACE})
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The workspace contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The parent of this resource is not an open project</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the file system does not exist, or
+ * is relative to an undefined variable, and <code>ALLOW_MISSING_LOCAL</code> is
+ * not specified.</li>
+ * <li> The corresponding location in the file system is occupied
+ * by a directory (as opposed to a file).</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li>The team provider for the project which contains this folder does not permit
+ * linked resources.</li>
+ * <li>This folder's project contains a nature which does not permit linked resources.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#isLinked()
+ * @see IResource#ALLOW_MISSING_LOCAL
+ * @since 3.2
+ */
+ public void createLink(URI location, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes this file from the workspace.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * delete((keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this folder has been removed from its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param keepHistory a flag controlling whether files under this folder
+ * should be stored in the workspace's local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource could not be deleted for some reason.</li>
+ * <li> This resource is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int,IProgressMonitor)
+ * @see IResourceRuleFactory#deleteRule(IResource)
+ */
+ public void delete(boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns the name of a charset to be used when decoding the contents of this
+ * file into characters.
+ * <p>
+ * This refinement of the corresponding {@link IEncodedStorage} method
+ * is a convenience method, fully equivalent to:
+ * <pre>
+ * getCharset(true);
+ * </pre>
+ * </p><p>
+ * <b>Note 1</b>: this method does not check whether the result is a supported
+ * charset name. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this charset is used.
+ * </p>
+ * <p>
+ * <b>Note 2</b>: this method returns a cached value for the encoding
+ * that may be out of date if the file is not synchronized with the local file system
+ * and the encoding has since changed in the file system.
+ * </p>
+ *
+ * @return the name of a charset
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource could not be read.</li>
+ * <li> This resource is not local.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * </ul>
+ * @see IFile#getCharset(boolean)
+ * @see IEncodedStorage#getCharset()
+ * @see IContainer#getDefaultCharset()
+ * @since 3.0
+ */
+ public String getCharset() throws CoreException;
+
+ /**
+ * Returns the name of a charset to be used when decoding the contents of this
+ * file into characters.
+ * <p>
+ * If checkImplicit is <code>false</code>, this method will return the
+ * charset defined by calling <code>setCharset</code>, provided this file
+ * exists, or <code>null</code> otherwise.
+ * </p><p>
+ * If checkImplicit is <code>true</code>, this method uses the following
+ * algorithm to determine the charset to be returned:
+ * <ol>
+ * <li>the charset defined by calling #setCharset, if any, and this file
+ * exists, or</li>
+ * <li>the charset automatically discovered based on this file's contents,
+ * if one can be determined, or</li>
+ * <li>the default encoding for this file's parent (as defined by
+ * <code>IContainer#getDefaultCharset</code>).</li>
+ * </ol>
+ * </p><p>
+ * <b>Note 1</b>: this method does not check whether the result is a supported
+ * charset name. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this charset is used.
+ * </p>
+ * <p>
+ * <b>Note 2</b>: this method returns a cached value for the encoding
+ * that may be out of date if the file is not synchronized with the local file system
+ * and the encoding has since changed in the file system.
+ * </p>
+ *
+ * @return the name of a charset, or <code>null</code>
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource could not be read.</li>
+ * <li> This resource is not local.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * </ul>
+ * @see IEncodedStorage#getCharset()
+ * @see IContainer#getDefaultCharset()
+ * @since 3.0
+ */
+ public String getCharset(boolean checkImplicit) throws CoreException;
+
+ /**
+ * Returns the name of a charset to be used to encode the given contents
+ * when saving to this file. This file does not have to exist. The character stream is <em>not</em> automatically closed.
+ * <p>
+ * This method uses the following algorithm to determine the charset to be returned:
+ * <ol>
+ * <li>if this file exists, the charset returned by IFile#getCharset(false), if one is defined, or</li>
+ * <li>the charset automatically discovered based on the file name and the given contents,
+ * if one can be determined, or</li>
+ * <li>the default encoding for the parent resource (as defined by
+ * <code>IContainer#getDefaultCharset</code>).</li>
+ * </ol>
+ * </p><p>
+ * <b>Note</b>: this method does not check whether the result is a supported
+ * charset name. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this charset is used.
+ * </p>
+ *
+ * @param reader a character stream containing the contents to be saved into this file
+ * @return the name of a charset
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li>The given character stream could not be read.</li>
+ * </ul>
+ * @see #getCharset(boolean)
+ * @see IContainer#getDefaultCharset()
+ * @since 3.1
+ */
+ public String getCharsetFor(Reader reader) throws CoreException;
+
+ /**
+ * Returns a description for this file's current contents. Returns
+ * <code>null</code> if a description cannot be obtained.
+ * <p>
+ * Calling this method produces a similar effect as calling
+ * <code>getDescriptionFor(getContents(), getName(), IContentDescription.ALL)</code>
+ * on <code>IContentTypeManager</code>, but provides better
+ * opportunities for improved performance. Therefore, when manipulating
+ * <code>IFile</code>s, clients should call this method instead of
+ * <code>IContentTypeManager.getDescriptionFor</code>.
+ * </p>
+ *
+ * @return a description for this file's current contents, or
+ * <code>null</code>
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource could not be read.</li>
+ * <li> This resource is not local.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * </ul>
+ * @see IContentDescription
+ * @see IContentTypeManager#getDescriptionFor(InputStream, String, QualifiedName[])
+ * @since 3.0
+ */
+ public IContentDescription getContentDescription() throws CoreException;
+
+ /**
+ * Returns an open input stream on the contents of this file.
+ * This refinement of the corresponding <code>IStorage</code> method
+ * returns an open input stream on the contents of this file.
+ * The client is responsible for closing the stream when finished.
+ *
+ * @return an input stream containing the contents of the file
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system.</li>
+ * </ul>
+ */
+ public InputStream getContents() throws CoreException;
+
+ /**
+ * This refinement of the corresponding <code>IStorage</code> method
+ * returns an open input stream on the contents of this file.
+ * The client is responsible for closing the stream when finished.
+ * If force is <code>true</code> the file is opened and an input
+ * stream returned regardless of the sync state of the file. The file
+ * is not synchronized with the workspace.
+ * If force is <code>false</code> the method fails if not in sync.
+ *
+ * @param force a flag controlling how to deal with resources that
+ * are not in sync with the local file system
+ * @return an input stream containing the contents of the file
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and force is <code>false</code>.</li>
+ * </ul>
+ */
+ public InputStream getContents(boolean force) throws CoreException;
+
+ /**
+ * Returns a constant identifying the character encoding of this file, or
+ * ENCODING_UNKNOWN if it could not be determined. The returned constant
+ * will be one of the ENCODING_* constants defined on IFile.
+ *
+ * This method attempts to guess the file's character encoding by analyzing
+ * the first few bytes of the file. If no identifying pattern is found at the
+ * beginning of the file, ENC_UNKNOWN will be returned. This method will
+ * not attempt any complex analysis of the file to make a guess at the
+ * encoding that is used.
+ *
+ * @return The character encoding of this file
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource could not be read.</li>
+ * <li> This resource is not local.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * </ul>
+ * @deprecated use IFile#getCharset instead
+ */
+ public int getEncoding() throws CoreException;
+
+ /**
+ * Returns the full path of this file.
+ * This refinement of the corresponding <code>IStorage</code> and <code>IResource</code>
+ * methods links the semantics of resource and storage object paths such that
+ * <code>IFile</code>s always have a path and that path is relative to the
+ * containing workspace.
+ *
+ * @see IResource#getFullPath()
+ * @see IStorage#getFullPath()
+ */
+ public IPath getFullPath();
+
+ /**
+ * Returns a list of past states of this file known to this workspace.
+ * Recently added states first.
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return an array of states of this file
+ * @exception CoreException if this method fails.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ */
+ public IFileState[] getHistory(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns the name of this file.
+ * This refinement of the corresponding <code>IStorage</code> and <code>IResource</code>
+ * methods links the semantics of resource and storage object names such that
+ * <code>IFile</code>s always have a name and that name equivalent to the
+ * last segment of its full path.
+ *
+ * @see IResource#getName()
+ * @see IStorage#getName()
+ */
+ public String getName();
+
+ /**
+ * Returns whether this file is read-only.
+ * This refinement of the corresponding <code>IStorage</code> and <code>IResource</code>
+ * methods links the semantics of read-only resources and read-only storage objects.
+ *
+ * @see IResource#isReadOnly()
+ * @see IStorage#isReadOnly()
+ */
+ public boolean isReadOnly();
+
+ /**
+ * Moves this resource to be at the given location.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * move(destination, (keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file has been removed from its parent and a new file
+ * has been added to the parent of the destination.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param destination the destination path
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param keepHistory a flag controlling whether files under this folder
+ * should be stored in the workspace's local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> The resource corresponding to the parent destination path does not exist.</li>
+ * <li> The resource corresponding to the parent destination path is a closed
+ * project.</li>
+ * <li> A resource at destination path does exist.</li>
+ * <li> A resource of a different type exists at the destination path.</li>
+ * <li> This resource is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IResource#move(IPath,int,IProgressMonitor)
+ * @see IResourceRuleFactory#moveRule(IResource, IResource)
+ */
+ public void move(IPath destination, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the charset for this file. Passing a value of <code>null</code>
+ * will remove the charset setting for this resource.
+ *
+ * @param newCharset a charset name, or <code>null</code>
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> An error happened while persisting this setting.</li>
+ * </ul>
+ * @see #getCharset()
+ * @since 3.0
+ * @deprecated Replaced by {@link #setCharset(String, IProgressMonitor)} which
+ * is a workspace operation and reports changes in resource deltas.
+ */
+ public void setCharset(String newCharset) throws CoreException;
+
+ /**
+ * Sets the charset for this file. Passing a value of <code>null</code>
+ * will remove the charset setting for this resource.
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's encoding has changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param newCharset a charset name, or <code>null</code>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> An error happened while persisting this setting.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See {@link IResourceChangeEvent} for more details.</li>
+ * </ul>
+ * @see #getCharset()
+ * @see IResourceRuleFactory#charsetRule(IResource)
+ * @since 3.0
+ */
+ public void setCharset(String newCharset, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the contents of this file to the bytes in the given input stream.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * setContents(source, (keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's contents have been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source an input stream containing the new contents of the file
+ * @param force a flag controlling how to deal with resources that
+ * are not in sync with the local file system
+ * @param keepHistory a flag indicating whether or not store
+ * the current contents in the local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and <code>force </code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #setContents(java.io.InputStream,int,IProgressMonitor)
+ */
+ public void setContents(InputStream source, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the contents of this file to the bytes in the given file state.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * setContents(source, (keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's content have been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source a previous state of this resource
+ * @param force a flag controlling how to deal with resources that
+ * are not in sync with the local file system
+ * @param keepHistory a flag indicating whether or not store
+ * the current contents in the local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The state does not exist.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and <code>force </code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #setContents(IFileState,int,IProgressMonitor)
+ */
+ public void setContents(IFileState source, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the contents of this file to the bytes in the given input stream.
+ * The stream will get closed whether this method succeeds or fails.
+ * If the stream is <code>null</code> then the content is set to be the
+ * empty sequence of bytes.
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If <code>FORCE</code> is not specified, the method will only attempt
+ * to overwrite a corresponding file in the local file system provided
+ * it is in sync with the workspace. This option ensures there is no
+ * unintended data loss; it is the recommended setting.
+ * However, if <code>FORCE</code> is specified, an attempt will be made
+ * to write a corresponding file in the local file system, overwriting any
+ * existing one if need be. In either case, if this method succeeds, the
+ * resource will be marked as being local (even if it wasn't before).
+ * </p>
+ * <p>
+ * The <code>KEEP_HISTORY</code> update flag controls whether or not a copy of
+ * current contents of this file should be captured in the workspace's local
+ * history (properties are not recorded in the local history). The local history
+ * mechanism serves as a safety net to help the user recover from mistakes that
+ * might otherwise result in data loss. Specifying <code>KEEP_HISTORY</code>
+ * is recommended except in circumstances where past states of the files are of
+ * no conceivable interest to the user. Note that local history is maintained
+ * with each individual project, and gets discarded when a project is deleted
+ * from the workspace. This flag is ignored if the file was not previously local.
+ * </p>
+ * <p>
+ * Update flags other than <code>FORCE</code> and <code>KEEP_HISTORY</code>
+ * are ignored.
+ * </p>
+ * <p>
+ * Prior to modifying the contents of this file, the file modification validator (if provided
+ * by the VCM plug-in), will be given a chance to perform any last minute preparations. Validation
+ * is performed by calling <code>IFileModificationValidator.validateSave</code> on this file.
+ * If the validation fails, then this operation will fail.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's content have been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source an input stream containing the new contents of the file
+ * @param updateFlags bit-wise or of update flag constants
+ * (<code>FORCE</code> and <code>KEEP_HISTORY</code>)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and <code>FORCE</code> is not specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ * @since 2.0
+ */
+ public void setContents(InputStream source, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the contents of this file to the bytes in the given file state.
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If <code>FORCE</code> is not specified, the method will only attempt
+ * to overwrite a corresponding file in the local file system provided
+ * it is in sync with the workspace. This option ensures there is no
+ * unintended data loss; it is the recommended setting.
+ * However, if <code>FORCE</code> is specified, an attempt will be made
+ * to write a corresponding file in the local file system, overwriting any
+ * existing one if need be. In either case, if this method succeeds, the
+ * resource will be marked as being local (even if it wasn't before).
+ * </p>
+ * <p>
+ * The <code>KEEP_HISTORY</code> update flag controls whether or not a copy of
+ * current contents of this file should be captured in the workspace's local
+ * history (properties are not recorded in the local history). The local history
+ * mechanism serves as a safety net to help the user recover from mistakes that
+ * might otherwise result in data loss. Specifying <code>KEEP_HISTORY</code>
+ * is recommended except in circumstances where past states of the files are of
+ * no conceivable interest to the user. Note that local history is maintained
+ * with each individual project, and gets discarded when a project is deleted
+ * from the workspace. This flag is ignored if the file was not previously local.
+ * </p>
+ * <p>
+ * Update flags other than <code>FORCE</code> and <code>KEEP_HISTORY</code>
+ * are ignored.
+ * </p>
+ * <p>
+ * Prior to modifying the contents of this file, the file modification validator (if provided
+ * by the VCM plug-in), will be given a chance to perform any last minute preparations. Validation
+ * is performed by calling <code>IFileModificationValidator.validateSave</code> on this file.
+ * If the validation fails, then this operation will fail.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this file's content have been changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param source a previous state of this resource
+ * @param updateFlags bit-wise or of update flag constants
+ * (<code>FORCE</code> and <code>KEEP_HISTORY</code>)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The state does not exist.</li>
+ * <li> The corresponding location in the local file system
+ * is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the corresponding location
+ * in the local file system and <code>FORCE</code> is not specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ * @since 2.0
+ */
+ public void setContents(IFileState source, int updateFlags, IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IFileModificationValidator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.resources.team.FileModificationValidator;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * The file modification validator is a Team-related hook for pre-checking operations
+ * that modify the contents of files.
+ * <p>
+ * This interface is used only in conjunction with the
+ * "org.eclipse.core.resources.fileModificationValidator"
+ * extension point. It is intended to be implemented only
+ * by the Eclipse Platform Team plug-in.
+ * </p>
+ *
+ * @since 2.0
+ * @deprecated clients should subclass {@link FileModificationValidator} instead
+ * of implementing this interface
+ */
+public interface IFileModificationValidator {
+ /**
+ * Validates that the given files can be modified. The files must all exist
+ * in the workspace. The optional context object may be supplied if
+ * UI-based validation is required. If the context is <code>null</code>, the
+ * validator must attempt to perform the validation in a headless manner.
+ * The returned status is <code>IStatus.OK</code> if this validator
+ * believes the given file can be modified. Other return statuses indicate
+ * the reason why the individual files cannot be modified.
+ *
+ * @param files the files that are to be modified; these files must all exist in the workspace
+ * @param context the <code>org.eclipse.swt.widgets.Shell</code> that is to be used to
+ * parent any dialogs with the user, or <code>null</code> if there is no UI context (declared
+ * as an <code>Object</code> to avoid any direct references on the SWT component)
+ * @return a status object that is OK if things are fine, otherwise a status describing
+ * reasons why modifying the given files is not reasonable
+ * @see IWorkspace#validateEdit(IFile[], Object)
+ */
+ public IStatus validateEdit(IFile[] files, Object context);
+
+ /**
+ * Validates that the given file can be saved. This method is called from
+ * <code>IFile#setContents</code> and <code>IFile#appendContents</code>
+ * before any attempt to write data to disk. The returned status is
+ * <code>IStatus.OK</code> if this validator believes the given file can be
+ * successfully saved. In all other cases the return value is a non-OK status.
+ * Note that a return value of <code>IStatus.OK</code> does not guarantee
+ * that the save will succeed.
+ *
+ * @param file the file that is to be modified; this file must exist in the workspace
+ * @return a status indicating whether or not it is reasonable to try writing to the given file;
+ * <code>IStatus.OK</code> indicates a save should be attempted.
+ *
+ * @see IFile#setContents(java.io.InputStream, int, org.eclipse.core.runtime.IProgressMonitor)
+ * @see IFile#appendContents(java.io.InputStream, int, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus validateSave(IFile file);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IFileState.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.io.InputStream;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A previous state of a file stored in the workspace's local history.
+ * <p>
+ * Certain methods for updating, deleting, or moving a file cause the
+ * "before" contents of the file to be copied to an internal area of the
+ * workspace called the <b>local history area</b> thus providing
+ * a limited history of earlier states of a file.
+ * </p>
+ * <p>
+ * Moving or copying a file will cause a copy of its local history to appear
+ * at the new location as well as at the original location. Subsequent
+ * changes to either file will only affect the local history of the file
+ * changed. Deleting a file and creating another one at the
+ * same path does not affect the history. If the original file had
+ * history, that same history will be available for the new one.
+ * </p>
+ * <p>
+ * The local history does not track resource properties.
+ * File states are volatile; the platform does not guarantee that a
+ * certain state will always be in the local history.
+ * </p>
+ * <p>
+ * File state objects implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see IFile
+ * @see IStorage
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IFileState extends IEncodedStorage, IAdaptable {
+ /**
+ * Returns whether this file state still exists in the local history.
+ *
+ * @return <code>true</code> if this state exists, and <code>false</code>
+ * if it does not
+ */
+ public boolean exists();
+
+ /**
+ * Returns an open input stream on the contents of this file state.
+ * This refinement of the corresponding
+ * <code>IStorage</code> method returns an open input stream
+ * on the contents this file state represents.
+ * The client is responsible for closing the stream when finished.
+ *
+ * @return an input stream containing the contents of the file
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This state does not exist.</li>
+ * </ul>
+ */
+ public InputStream getContents() throws CoreException;
+
+ /**
+ * Returns the full path of this file state.
+ * This refinement of the corresponding <code>IStorage</code>
+ * method specifies that <code>IFileState</code>s always have a
+ * path and that path is the full workspace path of the file represented by this state.
+ *
+ * @see IResource#getFullPath()
+ * @see IStorage#getFullPath()
+ */
+ public IPath getFullPath();
+
+ /**
+ * Returns the modification time of the file. If you create a file at
+ * 9:00 and modify it at 11:00, the file state added to the history
+ * at 11:00 will have 9:00 as its modification time.
+ * <p>
+ * Note that is used only to give the user a general idea of how
+ * old this file state is.
+ *
+ * @return the time of last modification, in milliseconds since
+ * January 1, 1970, 00:00:00 GMT.
+ */
+ public long getModificationTime();
+
+ /**
+ * Returns the name of this file state.
+ * This refinement of the corresponding <code>IStorage</code>
+ * method specifies that <code>IFileState</code>s always have a
+ * name and that name is equivalent to the last segment of the full path
+ * of the resource represented by this state.
+ *
+ * @see IResource#getName()
+ * @see IStorage#getName()
+ */
+ public String getName();
+
+ /**
+ * Returns whether this file state is read-only.
+ * This refinement of the corresponding
+ * <code>IStorage</code> method restricts <code>IFileState</code>s to
+ * always be read-only.
+ *
+ * @see IStorage
+ */
+ public boolean isReadOnly();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IFolder.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,451 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.net.URI;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Folders may be leaf or non-leaf resources and may contain files and/or other folders.
+ * A folder resource is stored as a directory in the local file system.
+ * <p>
+ * Folders, like other resource types, may exist in the workspace but
+ * not be local; non-local folder resources serve as place-holders for
+ * folders whose properties have not yet been fetched from a repository.
+ * </p>
+ * <p>
+ * Folders implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IFolder extends IContainer, IAdaptable {
+
+ /**
+ * Creates a new folder resource as a member of this handle's parent resource.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * create((force ? FORCE : IResource.NONE), local, monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the folder has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param force a flag controlling how to deal with resources that
+ * are not in sync with the local file system
+ * @param local a flag controlling whether or not the folder will be local
+ * after the creation
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The workspace contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The parent of this resource is a project that is not open.</li>
+ * <li> The parent contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a file (as opposed to a directory).</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a folder and <code>force </code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IFolder#create(int,boolean,IProgressMonitor)
+ */
+ public void create(boolean force, boolean local, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new folder resource as a member of this handle's parent resource.
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If <code>FORCE</code> is not specified, the method will only attempt
+ * to create a directory in the local file system if there isn't one already.
+ * This option ensures there is no unintended data loss; it is the recommended
+ * setting. However, if <code>FORCE</code> is specified, this method will
+ * be deemed a success even if there already is a corresponding directory.
+ * </p>
+ * <p>
+ * The {@link IResource#DERIVED} update flag indicates that this resource
+ * should immediately be set as a derived resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setDerived(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * The {@link IResource#TEAM_PRIVATE} update flag indicates that this resource
+ * should immediately be set as a team private resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setTeamPrivateMember(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method synchronizes this resource with the local file system.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the folder has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param updateFlags bit-wise or of update flag constants
+ * ({@link IResource#FORCE}, {@link IResource#DERIVED}, and {@link IResource#TEAM_PRIVATE})
+ * @param local a flag controlling whether or not the folder will be local
+ * after the creation
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The workspace contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The parent of this resource is a project that is not open.</li>
+ * <li> The parent contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a file (as opposed to a directory).</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a folder and <code>FORCE</code> is not specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceRuleFactory#createRule(IResource)
+ * @since 2.0
+ */
+ public void create(int updateFlags, boolean local, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new folder resource as a member of this handle's parent resource.
+ * The folder's contents will be located in the directory specified by the given
+ * file system path. The given path must be either an absolute file system
+ * path, or a relative path whose first segment is the name of a workspace path
+ * variable.
+ * <p>
+ * The <code>ALLOW_MISSING_LOCAL</code> update flag controls how this
+ * method deals with cases where the local file system directory to be linked does
+ * not exist, or is relative to a workspace path variable that is not defined.
+ * If <code>ALLOW_MISSING_LOCAL</code> is specified, the operation will succeed
+ * even if the local directory is missing, or the path is relative to an
+ * undefined variable. If <code>ALLOW_MISSING_LOCAL</code> is not specified, the
+ * operation will fail in the case where the local file system directory does
+ * not exist or the path is relative to an undefined variable.
+ * </p>
+ * <p>
+ * The {@link IResource#REPLACE} update flag controls how this
+ * method deals with cases where a resource of the same name as the
+ * prospective link already exists. If {@link IResource#REPLACE}
+ * is specified, then any existing resource with the same name is removed
+ * from the workspace to make way for creation of the link. This does <b>not</b>
+ * cause the underlying file system contents of that resource to be deleted.
+ * If {@link IResource#REPLACE} is not specified, this method will
+ * fail if an existing resource exists of the same name.
+ * </p>
+ * <p>
+ * The {@link IResource#BACKGROUND_REFRESH} update flag controls how
+ * this method synchronizes the new resource with the filesystem. If this flag is
+ * specified, resources on disk will be synchronized in the background after the
+ * method returns. Child resources of the link may not be available until
+ * this background refresh completes. If this flag is not specified, resources are
+ * synchronized in the foreground before this method returns.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the folder has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param localLocation a file system path where the folder should be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * (only ALLOW_MISSING_LOCAL is relevant here)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The workspace contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The parent of this resource is not an open project</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system does not exist, or
+ * is relative to an undefined variable, and <code>ALLOW_MISSING_LOCAL</code> is
+ * not specified.</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a file (as opposed to a directory).</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li>The team provider for the project which contains this folder does not permit
+ * linked resources.</li>
+ * <li>This folder's project contains a nature which does not permit linked resources.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#isLinked()
+ * @see IResource#ALLOW_MISSING_LOCAL
+ * @since 2.1
+ */
+ public void createLink(IPath localLocation, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new folder resource as a member of this handle's parent resource.
+ * The folder's contents will be located in the directory specified by the given
+ * file system URI. The given URI must be either absolute, or a relative URI
+ * whose first path segment is the name of a workspace path variable.
+ * <p>
+ * The <code>ALLOW_MISSING_LOCAL</code> update flag controls how this
+ * method deals with cases where the local file system directory to be linked does
+ * not exist, or is relative to a workspace path variable that is not defined.
+ * If <code>ALLOW_MISSING_LOCAL</code> is specified, the operation will succeed
+ * even if the local directory is missing, or the path is relative to an
+ * undefined variable. If <code>ALLOW_MISSING_LOCAL</code> is not specified, the
+ * operation will fail in the case where the local file system directory does
+ * not exist or the path is relative to an undefined variable.
+ * </p>
+ * <p>
+ * The {@link IResource#REPLACE} update flag controls how this
+ * method deals with cases where a resource of the same name as the
+ * prospective link already exists. If {@link IResource#REPLACE}
+ * is specified, then any existing resource with the same name is removed
+ * from the workspace to make way for creation of the link. This does <b>not</b>
+ * cause the underlying file system contents of that resource to be deleted.
+ * If {@link IResource#REPLACE} is not specified, this method will
+ * fail if an existing resource exists of the same name.
+ * </p>
+ * <p>
+ * The {@link IResource#BACKGROUND_REFRESH} update flag controls how
+ * this method synchronizes the new resource with the filesystem. If this flag is
+ * specified, resources on disk will be synchronized in the background after the
+ * method returns. Child resources of the link may not be available until
+ * this background refresh completes. If this flag is not specified, resources are
+ * synchronized in the foreground before this method returns.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the folder has been added to its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param location a file system path where the folder should be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * (only ALLOW_MISSING_LOCAL is relevant here)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource already exists in the workspace.</li>
+ * <li> The workspace contains a resource of a different type
+ * at the same path as this resource.</li>
+ * <li> The parent of this resource does not exist.</li>
+ * <li> The parent of this resource is not an open project</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The corresponding location in the local file system does not exist, or
+ * is relative to an undefined variable, and <code>ALLOW_MISSING_LOCAL</code> is
+ * not specified.</li>
+ * <li> The corresponding location in the local file system is occupied
+ * by a file (as opposed to a directory).</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li>The team provider for the project which contains this folder does not permit
+ * linked resources.</li>
+ * <li>This folder's project contains a nature which does not permit linked resources.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#isLinked()
+ * @see IResource#ALLOW_MISSING_LOCAL
+ * @since 3.2
+ */
+ public void createLink(URI location, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes this resource from the workspace.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * delete((keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this folder has been removed from its parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param keepHistory a flag controlling whether files under this folder
+ * should be stored in the workspace's local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource could not be deleted for some reason.</li>
+ * <li> This resource is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IResourceRuleFactory#deleteRule(IResource)
+ * @see IResource#delete(int,IProgressMonitor)
+ */
+ public void delete(boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns a handle to the file with the given name in this folder.
+ * <p>
+ * This is a resource handle operation; neither the resource nor
+ * the result need exist in the workspace.
+ * The validation check on the resource name/path is not done
+ * when the resource handle is constructed; rather, it is done
+ * automatically as the resource is created.
+ * </p>
+ *
+ * @param name the string name of the member file
+ * @return the (handle of the) member file
+ * @see #getFolder(String)
+ */
+ public IFile getFile(String name);
+
+ /**
+ * Returns a handle to the folder with the given name in this folder.
+ * <p>
+ * This is a resource handle operation; neither the container
+ * nor the result need exist in the workspace.
+ * The validation check on the resource name/path is not done
+ * when the resource handle is constructed; rather, it is done
+ * automatically as the resource is created.
+ * </p>
+ *
+ * @param name the string name of the member folder
+ * @return the (handle of the) member folder
+ * @see #getFile(String)
+ */
+ public IFolder getFolder(String name);
+
+ /**
+ * Moves this resource so that it is located at the given path.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * move(destination, (keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this folder has been removed from its parent and a new folder
+ * has been added to the parent of the destination.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param destination the destination path
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param keepHistory a flag controlling whether files under this folder
+ * should be stored in the workspace's local history
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> The resource corresponding to the parent destination path does not exist.</li>
+ * <li> The resource corresponding to the parent destination path is a closed
+ * project.</li>
+ * <li> A resource at destination path does exist.</li>
+ * <li> A resource of a different type exists at the destination path.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IResourceRuleFactory#moveRule(IResource, IResource)
+ * @see IResource#move(IPath,int,IProgressMonitor)
+ */
+ public void move(IPath destination, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IMarker.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,574 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.Map;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Markers are a general mechanism for associating notes and meta-data with
+ * resources.
+ * <p>
+ * Markers themselves are handles in the same way as <code>IResources</code>
+ * are handles. Instances of <code>IMarker</code> do not hold the attributes
+ * themselves but rather uniquely refer to the attribute container. As such,
+ * their state may change underneath the handle with no warning to the holder
+ * of the handle.
+ * </p>
+ * The Resources plug-in provides a general framework for
+ * defining and manipulating markers and provides several standard marker types.
+ * </p>
+ * <p>
+ * Each marker has:<ul>
+ * <li> a type string, specifying its type (e.g.
+ * <code>"org.eclipse.core.resources.taskmarker"</code>), </li>
+ * <li> an identifier which is unique (relative to a particular resource)</li>
+ * </ul>
+ * Specific types of markers may carry additional information.
+ * </p>
+ * <p>
+ * The resources plug-in defines five standard types:
+ * <ul>
+ * <li><code>org.eclipse.core.resources.marker</code></li>
+ * <li><code>org.eclipse.core.resources.taskmarker</code></li>
+ * <li><code>org.eclipse.core.resources.problemmarker</code></li>
+ * <li><code>org.eclipse.core.resources.bookmark</code></li>
+ * <li><code>org.eclipse.core.resources.textmarker</code></li>
+ * </ul>
+ * The plug-in also provides an extension point (
+ * <code>org.eclipse.core.resources.markers</code>) into which other
+ * plug-ins can install marker type declaration extensions.
+ * </p>
+ * <p>
+ * Marker types are declared within a multiple inheritance type system.
+ * New markers are defined in the <code>plugin.xml</code> file of the
+ * declaring plug-in. A valid declaration contains elements as defined by
+ * the extension point DTD:
+ * <ul>
+ * <li><i>type</i> - the unique name of the marker type</li>
+ * <li><i>super</i> - the list of marker types of which this marker is to be considered a sub-type</li>
+ * <li><i>attributes</i> - the list of standard attributes which may be present on this type of marker</li>
+ * <li><i>persistent</i> - whether markers of this type should be persisted by the platform</li>
+ * </li>
+ * </p>
+ * <p>All markers declared as <code>persistent</code> are saved when the
+ * workspace is saved, except those explicitly set as transient (the
+ * <code>TRANSIENT</code> attribute is set as <code>true</code>). A plug-in
+ * which defines a persistent marker is not directly involved in saving and
+ * restoring the marker. Markers are not under version and configuration
+ * management, and cannot be shared via VCM repositories.
+ * </p>
+ * <p>
+ * Markers implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IMarker extends IAdaptable {
+
+ /*====================================================================
+ * Marker types:
+ *====================================================================*/
+
+ /**
+ * Base marker type.
+ *
+ * @see #getType()
+ */
+ public static final String MARKER = ResourcesPlugin.PI_RESOURCES + ".marker"; //$NON-NLS-1$
+
+ /**
+ * Task marker type.
+ *
+ * @see #getType()
+ */
+ public static final String TASK = ResourcesPlugin.PI_RESOURCES + ".taskmarker"; //$NON-NLS-1$
+
+ /**
+ * Problem marker type.
+ *
+ * @see #getType()
+ */
+ public static final String PROBLEM = ResourcesPlugin.PI_RESOURCES + ".problemmarker"; //$NON-NLS-1$
+
+ /**
+ * Text marker type.
+ *
+ * @see #getType()
+ */
+ public static final String TEXT = ResourcesPlugin.PI_RESOURCES + ".textmarker"; //$NON-NLS-1$
+
+ /**
+ * Bookmark marker type.
+ *
+ * @see #getType()
+ */
+ public static final String BOOKMARK = ResourcesPlugin.PI_RESOURCES + ".bookmark"; //$NON-NLS-1$
+
+ /*====================================================================
+ * Marker attributes:
+ *====================================================================*/
+
+ /**
+ * Severity marker attribute. A number from the set of error, warning and info
+ * severities defined by the platform.
+ *
+ * @see #SEVERITY_ERROR
+ * @see #SEVERITY_WARNING
+ * @see #SEVERITY_INFO
+ * @see #getAttribute(String, int)
+ */
+ public static final String SEVERITY = "severity"; //$NON-NLS-1$
+
+ /**
+ * Message marker attribute. A localized string describing the nature
+ * of the marker (e.g., a name for a bookmark or task). The content
+ * and form of this attribute is not specified or interpreted by the platform.
+ *
+ * @see #getAttribute(String, String)
+ */
+ public static final String MESSAGE = "message"; //$NON-NLS-1$
+
+ /**
+ * Location marker attribute. The location is a human-readable (localized) string which
+ * can be used to distinguish between markers on a resource. As such it
+ * should be concise and aimed at users. The content and
+ * form of this attribute is not specified or interpreted by the platform.
+ *
+ * @see #getAttribute(String, String)
+ */
+ public static final String LOCATION = "location"; //$NON-NLS-1$
+
+ /**
+ * Priority marker attribute. A number from the set of high, normal and low
+ * priorities defined by the platform.
+ *
+ * @see #PRIORITY_HIGH
+ * @see #PRIORITY_NORMAL
+ * @see #PRIORITY_LOW
+ * @see #getAttribute(String, int)
+ */
+ public static final String PRIORITY = "priority"; //$NON-NLS-1$
+
+ /**
+ * Done marker attribute. A boolean value indicating whether
+ * the marker (e.g., a task) is considered done.
+ *
+ * @see #getAttribute(String, String)
+ */
+ public static final String DONE = "done"; //$NON-NLS-1$
+
+ /**
+ * Character start marker attribute. An integer value indicating where a text
+ * marker starts. This attribute is zero-relative and inclusive.
+ *
+ * @see #getAttribute(String, String)
+ */
+ public static final String CHAR_START = "charStart"; //$NON-NLS-1$
+
+ /**
+ * Character end marker attribute. An integer value indicating where a text
+ * marker ends. This attribute is zero-relative and exclusive.
+ *
+ * @see #getAttribute(String, String)
+ */
+ public static final String CHAR_END = "charEnd"; //$NON-NLS-1$
+
+ /**
+ * Line number marker attribute. An integer value indicating the line number
+ * for a text marker. This attribute is 1-relative.
+ *
+ * @see #getAttribute(String, String)
+ */
+ public static final String LINE_NUMBER = "lineNumber"; //$NON-NLS-1$
+
+ /**
+ * Transient marker attribute. A boolean value indicating whether the
+ * marker (e. g., a task) is considered transient even if its type is
+ * declared as persistent.
+ *
+ * @see #getAttribute(String, String)
+ * @since 2.1
+ */
+ public static final String TRANSIENT = "transient"; //$NON-NLS-1$
+
+ /**
+ * User editable marker attribute. A boolean value indicating whether a
+ * user should be able to manually change the marker (e.g. a task). The
+ * default is <code>true</code>. Note that the value of this attribute
+ * is to be used by the UI as a suggestion and its value will NOT be
+ * interpreted by Core in any manner and will not be enforced by Core
+ * when performing any operations on markers.
+ *
+ * @see #getAttribute(String, String)
+ * @since 2.1
+ */
+ public static final String USER_EDITABLE = "userEditable"; //$NON-NLS-1$
+
+ /**
+ * Source id attribute. A string attribute that can be used by tools that
+ * generate markers to indicate the source of the marker. Use of this attribute is
+ * optional and its format or existence is not enforced. This attribute is
+ * intended to improve serviceability by providing a value that product support
+ * personnel or automated tools can use to determine appropriate help and
+ * resolutions for markers.
+ *
+ * @see #getAttribute(String, String)
+ * @since 3.3
+ */
+ public static final String SOURCE_ID = "sourceId"; //$NON-NLS-1$
+
+ /*====================================================================
+ * Marker attributes values:
+ *====================================================================*/
+
+ /**
+ * High priority constant (value 2).
+ *
+ * @see #getAttribute(String, int)
+ */
+ public static final int PRIORITY_HIGH = 2;
+
+ /**
+ * Normal priority constant (value 1).
+ *
+ * @see #getAttribute(String, int)
+ */
+ public static final int PRIORITY_NORMAL = 1;
+
+ /**
+ * Low priority constant (value 0).
+ *
+ * @see #getAttribute(String, int)
+ */
+ public static final int PRIORITY_LOW = 0;
+
+ /**
+ * Error severity constant (value 2) indicating an error state.
+ *
+ * @see #getAttribute(String, int)
+ */
+ public static final int SEVERITY_ERROR = 2;
+
+ /**
+ * Warning severity constant (value 1) indicating a warning.
+ *
+ * @see #getAttribute(String, int)
+ */
+ public static final int SEVERITY_WARNING = 1;
+
+ /**
+ * Info severity constant (value 0) indicating information only.
+ *
+ * @see #getAttribute(String, int)
+ */
+ public static final int SEVERITY_INFO = 0;
+
+ /**
+ * Deletes this marker from its associated resource. This method has no
+ * effect if this marker does not exist.
+ *
+ * @exception CoreException if this marker could not be deleted. Reasons include:
+ * <ul>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void delete() throws CoreException;
+
+ /**
+ * Tests this marker for equality with the given object.
+ * Two markers are equal if their id and resource are both equal.
+ *
+ * @param object the other object
+ * @return an indication of whether the objects are equal
+ */
+ public boolean equals(Object object);
+
+ /**
+ * Returns whether this marker exists in the workspace. A marker
+ * exists if its resource exists and has a marker with the marker's id.
+ *
+ * @return <code>true</code> if this marker exists, otherwise
+ * <code>false</code>
+ */
+ public boolean exists();
+
+ /**
+ * Returns the attribute with the given name. The result is an instance of one
+ * of the following classes: <code>String</code>, <code>Integer</code>,
+ * or <code>Boolean</code>.
+ * Returns <code>null</code> if the attribute is undefined.
+ *
+ * @param attributeName the name of the attribute
+ * @return the value, or <code>null</code> if the attribute is undefined.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * </ul>
+ */
+ public Object getAttribute(String attributeName) throws CoreException;
+
+ /**
+ * Returns the integer-valued attribute with the given name.
+ * Returns the given default value if the attribute is undefined.
+ * or the marker does not exist or is not an integer value.
+ *
+ * @param attributeName the name of the attribute
+ * @param defaultValue the value to use if no value is found
+ * @return the value or the default value if no value was found.
+ */
+ public int getAttribute(String attributeName, int defaultValue);
+
+ /**
+ * Returns the string-valued attribute with the given name.
+ * Returns the given default value if the attribute is undefined
+ * or the marker does not exist or is not a string value.
+ *
+ * @param attributeName the name of the attribute
+ * @param defaultValue the value to use if no value is found
+ * @return the value or the default value if no value was found.
+ */
+ public String getAttribute(String attributeName, String defaultValue);
+
+ /**
+ * Returns the boolean-valued attribute with the given name.
+ * Returns the given default value if the attribute is undefined
+ * or the marker does not exist or is not a boolean value.
+ *
+ * @param attributeName the name of the attribute
+ * @param defaultValue the value to use if no value is found
+ * @return the value or the default value if no value was found.
+ */
+ public boolean getAttribute(String attributeName, boolean defaultValue);
+
+ /**
+ * Returns a map with all the attributes for the marker.
+ * If the marker has no attributes then <code>null</code> is returned.
+ *
+ * @return a map of attribute keys and values (key type : <code>String</code>
+ * value type : <code>String</code>, <code>Integer</code>, or
+ * <code>Boolean</code>) or <code>null</code>.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * </ul>
+ */
+ public Map getAttributes() throws CoreException;
+
+ /**
+ * Returns the attributes with the given names. The result is an an array
+ * whose elements correspond to the elements of the given attribute name
+ * array. Each element is <code>null</code> or an instance of one
+ * of the following classes: <code>String</code>, <code>Integer</code>,
+ * or <code>Boolean</code>.
+ *
+ * @param attributeNames the names of the attributes
+ * @return the values of the given attributes.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * </ul>
+ */
+ public Object[] getAttributes(String[] attributeNames) throws CoreException;
+
+ /**
+ * Returns the time at which this marker was created.
+ *
+ * @return the difference, measured in milliseconds, between the time at which
+ * this marker was created and midnight, January 1, 1970 UTC, or <code>0L</code>
+ * if the creation time is not known (this can occur in workspaces created using v2.0 or earlier).
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * </ul>
+ * @since 2.1
+ */
+ public long getCreationTime() throws CoreException;
+
+ /**
+ * Returns the id of the marker. The id of a marker is unique
+ * relative to the resource with which the marker is associated.
+ * Marker ids are not globally unique.
+ *
+ * @return the id of the marker
+ * @see IResource#findMarker(long)
+ */
+ public long getId();
+
+ /**
+ * Returns the resource with which this marker is associated.
+ *
+ * @return the resource with which this marker is associated
+ */
+ public IResource getResource();
+
+ /**
+ * Returns the type of this marker. The returned marker type will not be
+ * <code>null</code>.
+ *
+ * @return the type of this marker
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * </ul>
+ */
+ public String getType() throws CoreException;
+
+ /**
+ * Returns whether the type of this marker is considered to be a sub-type of
+ * the given marker type.
+ *
+ * @return boolean <code>true</code>if the marker's type
+ * is the same as (or a sub-type of) the given type.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * </ul>
+ */
+ public boolean isSubtypeOf(String superType) throws CoreException;
+
+ /**
+ * Sets the integer-valued attribute with the given name.
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this marker has been modified.
+ * </p>
+ *
+ * @param attributeName the name of the attribute
+ * @param value the value
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void setAttribute(String attributeName, int value) throws CoreException;
+
+ /**
+ * Sets the attribute with the given name. The value must be <code>null</code> or
+ * an instance of one of the following classes:
+ * <code>String</code>, <code>Integer</code>, or <code>Boolean</code>.
+ * If the value is <code>null</code>, the attribute is considered to be undefined.
+ *
+ * <p>
+ * The attribute value cannot be <code>String</code>
+ * whose UTF encoding exceeds 65535 bytes.
+ * </p>
+ *
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this marker has been modified.
+ * </p>
+ *
+ * @param attributeName the name of the attribute
+ * @param value the value, or <code>null</code> if the attribute is to be undefined
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void setAttribute(String attributeName, Object value) throws CoreException;
+
+ /**
+ * Sets the boolean-valued attribute with the given name.
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this marker has been modified.
+ * </p>
+ *
+ * @param attributeName the name of the attribute
+ * @param value the value
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void setAttribute(String attributeName, boolean value) throws CoreException;
+
+ /**
+ * Sets the given attribute key-value pairs on this marker.
+ * The values must be <code>null</code> or an instance of
+ * one of the following classes: <code>String</code>,
+ * <code>Integer</code>, or <code>Boolean</code>.
+ * If a value is <code>null</code>, the new value of the
+ * attribute is considered to be undefined.
+ *
+ * <p>
+ * The values of the attributes cannot be <code>String</code>
+ * whose UTF encoding exceeds 65535 bytes.
+ * </p>
+ *
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this marker has been modified.
+ * </p>
+ *
+ * @param attributeNames an array of attribute names
+ * @param values an array of attribute values
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void setAttributes(String[] attributeNames, Object[] values) throws CoreException;
+
+ /**
+ * Sets the attributes for this marker to be the ones contained in the
+ * given table. The values must be an instance of one of the following classes:
+ * <code>String</code>, <code>Integer</code>, or <code>Boolean</code>.
+ * Attributes previously set on the marker but not included in the given map
+ * are considered to be removals. Setting the given map to be <code>null</code>
+ * is equivalent to removing all marker attributes.
+ *
+ * <p>
+ * The values of the attributes cannot be <code>String</code>
+ * whose UTF encoding exceeds 65535 bytes.
+ * </p>
+ *
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this marker has been modified.
+ * </p>
+ *
+ * @param attributes a map of attribute names to attribute values
+ * (key type : <code>String</code> value type : <code>String</code>,
+ * <code>Integer</code>, or <code>Boolean</code>) or <code>null</code>
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This marker does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void setAttributes(Map attributes) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IMarkerDelta.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.Map;
+
+/**
+ * A marker delta describes the change to a single marker.
+ * A marker can either be added, removed or changed.
+ * Marker deltas give access to the state of the marker as it
+ * was (in the case of deletions and changes) before the modifying
+ * operation occurred.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IMarkerDelta {
+ /**
+ * Returns the object attribute with the given name. The result is an instance of one
+ * of the following classes: <code>String</code>, <code>Integer</code>,
+ * or <code>Boolean</code>.
+ * Returns <code>null</code> if the attribute is undefined.
+ * The set of valid attribute names is defined elsewhere.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ * @param attributeName the name of the attribute
+ * @return the value, or <code>null</code> if the attribute is undefined.
+ */
+ public Object getAttribute(String attributeName);
+
+ /**
+ * Returns the integer-valued attribute with the given name.
+ * Returns the given default value if the attribute is undefined
+ * or is not an integer value.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @param attributeName the name of the attribute
+ * @param defaultValue the value to use if the attribute does not exist
+ * @return the value or the default value if the attribute is undefined.
+ */
+ public int getAttribute(String attributeName, int defaultValue);
+
+ /**
+ * Returns the string-valued attribute with the given name.
+ * Returns the given default value if the attribute is undefined or
+ * is not a string value.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @param attributeName the name of the attribute
+ * @param defaultValue the value to use if the attribute does not exist
+ * @return the value or the default value if the attribute is undefined.
+ */
+ public String getAttribute(String attributeName, String defaultValue);
+
+ /**
+ * Returns the boolean-valued attribute with the given name.
+ * Returns the given default value if the attribute is undefined
+ * or is not a boolean value.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @param attributeName the name of the attribute
+ * @param defaultValue the value to use if the attribute does not exist
+ * @return the value or the default value if the attribute is undefined.
+ */
+ public boolean getAttribute(String attributeName, boolean defaultValue);
+
+ /**
+ * Returns a Map with all the attributes for the marker. The result is a Map
+ * whose keys are attributes names and whose values are attribute values.
+ * Each value an instance of one of the following classes: <code>String</code>,
+ * <code>Integer</code>, or <code>Boolean</code>. If the marker has no
+ * attributes then <code>null</code> is returned.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @return a map of attribute keys and values (key type : <code>String</code>
+ * value type : <code>String</code>, <code>Integer</code>, or
+ * <code>Boolean</code>) or <code>null</code>.
+ */
+ public Map getAttributes();
+
+ /**
+ * Returns the attributes with the given names. The result is an array
+ * whose elements correspond to the elements of the given attribute name
+ * array. Each element is <code>null</code> or an instance of one
+ * of the following classes: <code>String</code>, <code>Integer</code>,
+ * or <code>Boolean</code>.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @param attributeNames the names of the attributes
+ * @return the values of the given attributes.
+ */
+ public Object[] getAttributes(String[] attributeNames);
+
+ /**
+ * Returns the id of the marker. The id of a marker is unique
+ * relative to the resource with which the marker is associated.
+ * Marker ids are not globally unique.
+ *
+ * @return the id of the marker
+ */
+ public long getId();
+
+ /**
+ * Returns the kind of this marker delta:
+ * one of <code>IResourceDelta.ADDED</code>,
+ * <code>IResourceDelta.REMOVED</code>, or <code>IResourceDelta.CHANGED</code>.
+ *
+ * @return the kind of marker delta
+ * @see IResourceDelta#ADDED
+ * @see IResourceDelta#REMOVED
+ * @see IResourceDelta#CHANGED
+ */
+ public int getKind();
+
+ /**
+ * Returns the marker described by this change.
+ * If kind is <code>IResourceDelta.REMOVED</code>, then this is the old marker,
+ * otherwise this is the new marker. Note that if the marker was deleted,
+ * the value returned cannot be used to access attributes.
+ *
+ * @return the marker
+ */
+ public IMarker getMarker();
+
+ /**
+ * Returns the resource with which this marker is associated.
+ *
+ * @return the resource
+ */
+ public IResource getResource();
+
+ /**
+ * Returns the type of this marker.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @return the type of this marker
+ */
+ public String getType();
+
+ /**
+ * Returns whether the type of this marker is considered to be a sub-type of
+ * the given marker type.
+ * <p>
+ * If kind is <code>IResourceDelta.ADDED</code>, then the information is
+ * from the new marker, otherwise it is from the old marker.
+ * </p>
+ *
+ * @return boolean <code>true</code>if the marker's type
+ * is the same as (or a sub-type of) the given type.
+ */
+ public boolean isSubtypeOf(String superType);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IPathVariableChangeEvent.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Describes a change in a path variable. The change may denote that a
+ * variable has been created, deleted or had its value changed.
+ *
+ * @since 2.1
+ * @see IPathVariableChangeListener
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IPathVariableChangeEvent {
+
+ /** Event type constant (value = 1) that denotes a value change . */
+ public final static int VARIABLE_CHANGED = 1;
+
+ /** Event type constant (value = 2) that denotes a variable creation. */
+ public final static int VARIABLE_CREATED = 2;
+
+ /** Event type constant (value = 3) that denotes a variable deletion. */
+ public final static int VARIABLE_DELETED = 3;
+
+ /**
+ * Returns the variable's current value. If the event type is
+ * <code>VARIABLE_CHANGED</code> then it is the new value, if the event
+ * type is <code>VARIABLE_CREATED</code> then it is the new value, or
+ * if the event type is <code>VARIABLE_DELETED</code> then it will
+ * be <code>null</code>.
+ *
+ * @return the variable's current value, or <code>null</code>
+ */
+ public IPath getValue();
+
+ /**
+ * Returns the affected variable's name.
+ *
+ * @return the affected variable's name
+ */
+ public String getVariableName();
+
+ /**
+ * Returns an object identifying the source of this event.
+ *
+ * @return an object identifying the source of this event
+ * @see java.util.EventObject
+ */
+ public Object getSource();
+
+ /**
+ * Returns the type of event being reported.
+ *
+ * @return one of the event type constants
+ * @see #VARIABLE_CHANGED
+ * @see #VARIABLE_CREATED
+ * @see #VARIABLE_DELETED
+ */
+ public int getType();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IPathVariableChangeListener.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.resources;
+
+import java.util.EventListener;
+
+/**
+ * An interface to be implemented by objects interested in path variable
+ * creation, removal and value change events.
+ *
+ * <p>Clients may implement this interface.</p>
+ *
+ * @since 2.1
+ */
+public interface IPathVariableChangeListener extends EventListener {
+ /**
+ * Notification that a path variable has changed.
+ * <p>
+ * This method is called when a path variable is added, removed or has its value
+ * changed in the observed <code>IPathVariableManager</code> object.
+ * </p>
+ *
+ * @param event the path variable change event object describing which variable
+ * changed and how
+ * @see IPathVariableManager#addChangeListener(IPathVariableChangeListener)
+ * @see IPathVariableManager#removeChangeListener(IPathVariableChangeListener)
+ * @see IPathVariableChangeEvent
+ */
+ public void pathVariableChanged(IPathVariableChangeEvent event);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IPathVariableManager.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.resources;
+
+import java.net.URI;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Manages a collection of path variables and resolves paths containing a
+ * variable reference.
+ * <p>
+ * A path variable is a pair of non-null elements (name,value) where name is
+ * a case-sensitive string (containing only letters, digits and the underscore
+ * character, and not starting with a digit), and value is an absolute
+ * <code>IPath</code> object.
+ * </p>
+ * <p>
+ * Path variables allow for the creation of relative paths whose exact
+ * location in the file system depends on the value of a variable. A variable
+ * reference may only appear as the first segment of a relative path.
+ * </p>
+ *
+ * @see org.eclipse.core.runtime.IPath
+ * @since 2.1
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IPathVariableManager {
+
+ /**
+ * Sets the path variable with the given name to be the specified value.
+ * Depending on the value given and if the variable is currently defined
+ * or not, there are several possible outcomes for this operation:
+ * <p>
+ * <ul>
+ * <li>A new variable will be created, if there is no variable defined with
+ * the given name, and the given value is not <code>null</code>.
+ * </li>
+ *
+ * <li>The referred variable's value will be changed, if it already exists
+ * and the given value is not <code>null</code>.</li>
+ *
+ * <li>The referred variable will be removed, if a variable with the given
+ * name is currently defined and the given value is <code>null</code>.
+ * </li>
+ *
+ * <li>The call will be ignored, if a variable with the given name is not
+ * currently defined and the given value is <code>null</code>, or if it is
+ * defined but the given value is equal to its current value.
+ * </li>
+ * </ul>
+ * <p>If a variable is effectively changed, created or removed by a call to
+ * this method, notification will be sent to all registered listeners.</p>
+ *
+ * @param name the name of the variable
+ * @param value the value for the variable (may be <code>null</code>)
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li>The variable name is not valid</li>
+ * <li>The variable value is relative</li>
+ * </ul>
+ */
+ public void setValue(String name, IPath value) throws CoreException;
+
+ /**
+ * Returns the value of the path variable with the given name. If there is
+ * no variable defined with the given name, returns <code>null</code>.
+ *
+ * @param name the name of the variable to return the value for
+ * @return the value for the variable, or <code>null</code> if there is no
+ * variable defined with the given name
+ */
+ public IPath getValue(String name);
+
+ /**
+ * Returns an array containing all defined path variable names.
+ *
+ * @return an array containing all defined path variable names
+ */
+ public String[] getPathVariableNames();
+
+ /**
+ * Registers the given listener to receive notification of changes to path
+ * variables. The listener will be notified whenever a variable has been
+ * added, removed or had its value changed. Has no effect if an identical
+ * path variable change listener is already registered.
+ *
+ * @param listener the listener
+ * @see IPathVariableChangeListener
+ */
+ public void addChangeListener(IPathVariableChangeListener listener);
+
+ /**
+ * Removes the given path variable change listener from the listeners list.
+ * Has no effect if an identical listener is not registered.
+ *
+ * @param listener the listener
+ * @see IPathVariableChangeListener
+ */
+ public void removeChangeListener(IPathVariableChangeListener listener);
+
+ /**
+ * Resolves a relative <code>URI</code> object potentially containing a
+ * variable reference as its first segment, replacing the variable reference
+ * (if any) with the variable's value (which is a concrete absolute URI).
+ * If the given URI is absolute or has a non- <code>null</code> device then
+ * no variable substitution is done and that URI is returned as is. If the
+ * given URI is relative and has a <code>null</code> device, but the first
+ * segment does not correspond to a defined variable, then the URI is
+ * returned as is.
+ * <p>
+ * If the given URI is <code>null</code> then <code>null</code> will be
+ * returned. In all other cases the result will be non-<code>null</code>.
+ * </p>
+ *
+ * @param uri the URI to be resolved
+ * @return the resolved URI or <code>null</code>
+ * @since 3.2
+ */
+ public URI resolveURI(URI uri);
+
+ /**
+ * Resolves a relative <code>IPath</code> object potentially containing a
+ * variable reference as its first segment, replacing the variable reference
+ * (if any) with the variable's value (which is a concrete absolute path).
+ * If the given path is absolute or has a non- <code>null</code> device then
+ * no variable substitution is done and that path is returned as is. If the
+ * given path is relative and has a <code>null</code> device, but the first
+ * segment does not correspond to a defined variable, then the path is
+ * returned as is.
+ * <p>
+ * If the given path is <code>null</code> then <code>null</code> will be
+ * returned. In all other cases the result will be non-<code>null</code>.
+ * </p>
+ *
+ * <p>
+ * For example, consider the following collection of path variables:
+ * </p>
+ * <ul>
+ * <li>TEMP = c:/temp</li>
+ * <li>BACKUP = /tmp/backup</li>
+ * </ul>
+ * <p>The following paths would be resolved as:
+ * <p>c:/bin => c:/bin</p>
+ * <p>c:TEMP => c:TEMP</p>
+ * <p>/TEMP => /TEMP</p>
+ * <p>TEMP => c:/temp</p>
+ * <p>TEMP/foo => c:/temp/foo</p>
+ * <p>BACKUP => /tmp/backup</p>
+ * <p>BACKUP/bar.txt => /tmp/backup/bar.txt</p>
+ * <p>SOMEPATH/foo => SOMEPATH/foo</p></p>
+ *
+ * @param path the path to be resolved
+ * @return the resolved path or <code>null</code>
+ */
+ public IPath resolvePath(IPath path);
+
+ /**
+ * Returns <code>true</code> if the given variable is defined and
+ * <code>false</code> otherwise. Returns <code>false</code> if the given
+ * name is not a valid path variable name.
+ *
+ * @param name the variable's name
+ * @return <code>true</code> if the variable exists, <code>false</code>
+ * otherwise
+ */
+ public boolean isDefined(String name);
+
+ /**
+ * Validates the given name as the name for a path variable. A valid path
+ * variable name is made exclusively of letters, digits and the underscore
+ * character, and does not start with a digit.
+ *
+ * @param name a possibly valid path variable name
+ * @return a status object with code <code>IStatus.OK</code> if
+ * the given name is a valid path variable name, otherwise a status
+ * object indicating what is wrong with the string
+ * @see IStatus#OK
+ */
+ public IStatus validateName(String name);
+
+ /**
+ * Validates the given path as the value for a path variable. A path
+ * variable value must be a valid path that is absolute.
+ *
+ * @param path a possibly valid path variable value
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * path is a valid path variable value, otherwise a status object indicating
+ * what is wrong with the value
+ * @see IPath#isValidPath(String)
+ * @see IStatus#OK
+ */
+ public IStatus validateValue(IPath path);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,846 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.Map;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentTypeMatcher;
+
+/**
+ * A project is a type of resource which groups resources
+ * into buildable, reusable units.
+ * <p>
+ * Features of projects include:
+ * <ul>
+ * <li>A project collects together a set of files and folders.</li>
+ * <li>A project's location controls where the project's resources are
+ * stored in the local file system.</li>
+ * <li>A project's build spec controls how building is done on the project.</li>
+ * <li>A project can carry session and persistent properties.</li>
+ * <li>A project can be open or closed; a closed project is
+ * passive and has a minimal in-memory footprint.</li>
+ * <li>A project can carry references to other projects.</li>
+ * <li>A project can have one or more project natures.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Projects implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IProject extends IContainer, IAdaptable {
+ /**
+ * Invokes the <code>build</code> method of the specified builder
+ * for this project. Does nothing if this project is closed. If this project
+ * has multiple builders on its build spec matching the given name, only
+ * the first matching builder will be run.
+ * <p>
+ * The builder name is declared in the extension that plugs in
+ * to the standard <code>org.eclipse.core.resources.builders</code>
+ * extension point. The arguments are builder specific.
+ * </p>
+ * <p>
+ * This method may change resources; these changes will be reported
+ * in a subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param kind the kind of build being requested. Valid values are:
+ * <ul>
+ * <li>{@link IncrementalProjectBuilder#FULL_BUILD}- indicates a full build.</li>
+ * <li>{@link IncrementalProjectBuilder#INCREMENTAL_BUILD}- indicates a incremental build.</li>
+ * <li>{@link IncrementalProjectBuilder#CLEAN_BUILD}- indicates a clean request. Clean does
+ * not actually build anything, but rather discards all problems and build states.</li>
+ * </ul>
+ * @param builderName the name of the builder
+ * @param args a table of builder-specific arguments keyed by argument name
+ * (key type: <code>String</code>, value type: <code>String</code>);
+ * <code>null</code> is equivalent to an empty map
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if the build fails.
+ * The status contained in the exception may be a generic {@link IResourceStatus#BUILD_FAILED}
+ * code, but it could also be any other status code; it might
+ * also be a {@link MultiStatus}.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IProjectDescription
+ * @see IncrementalProjectBuilder#build(int, Map, IProgressMonitor)
+ * @see IncrementalProjectBuilder#FULL_BUILD
+ * @see IncrementalProjectBuilder#INCREMENTAL_BUILD
+ * @see IncrementalProjectBuilder#CLEAN_BUILD
+ * @see IResourceRuleFactory#buildRule()
+ */
+ public void build(int kind, String builderName, Map args, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Builds this project. Does nothing if the project is closed.
+ * <p>
+ * Building a project involves executing the commands found
+ * in this project's build spec.
+ * </p>
+ * <p>
+ * This method may change resources; these changes will be reported
+ * in a subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param kind the kind of build being requested. Valid values are:
+ * <ul>
+ * <li> <code>IncrementalProjectBuilder.FULL_BUILD</code> - indicates a full build.</li>
+ * <li> <code>IncrementalProjectBuilder.INCREMENTAL_BUILD</code> - indicates an incremental build.</li>
+ * <li><code>CLEAN_BUILD</code>- indicates a clean request. Clean does
+ * not actually build anything, but rather discards all problems and build states.
+ * </ul>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if the build fails.
+ * The status contained in the exception may be a generic <code>BUILD_FAILED</code>
+ * code, but it could also be any other status code; it might
+ * also be a multi-status.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IProjectDescription
+ * @see IncrementalProjectBuilder#FULL_BUILD
+ * @see IncrementalProjectBuilder#INCREMENTAL_BUILD
+ * @see IResourceRuleFactory#buildRule()
+ */
+ public void build(int kind, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Closes this project. The project need not be open. Closing
+ * a closed project does nothing.
+ * <p>
+ * Closing a project involves ensuring that all important project-related
+ * state is safely stored on disk, and then discarding the in-memory
+ * representation of its resources and other volatile state,
+ * including session properties.
+ * After this method, the project continues to exist in the workspace
+ * but its member resources (and their members, etc.) do not.
+ * A closed project can later be re-opened.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event that includes
+ * an indication that this project has been closed and its members
+ * have been removed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #open(IProgressMonitor)
+ * @see #isOpen()
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ */
+ public void close(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new project resource in the workspace using the given project
+ * description. Upon successful completion, the project will exist but be closed.
+ * <p>
+ * Newly created projects have no session or persistent properties.
+ * </p>
+ * <p>
+ * If the project content area given in the project description does not
+ * contain a project description file, a project description file is written
+ * in the project content area with the natures, build spec, comment, and
+ * referenced projects as specified in the given project description.
+ * If there is an existing project description file, it is not overwritten. In either
+ * case, this method does not cause natures to be configured.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the project has been added to the workspace.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the project description
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project already exists in the workspace.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The project location is not valid (according to
+ * <code>IWorkspace.validateProjectLocation</code>).</li>
+ * <li> The project description file could not be created in the project
+ * content area.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IWorkspace#validateProjectLocation(IProject, IPath)
+ * @see IResourceRuleFactory#createRule(IResource)
+ */
+ public void create(IProjectDescription description, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new project resource in the workspace with files in the default
+ * location in the local file system. Upon successful completion, the project
+ * will exist but be closed.
+ * <p>
+ * Newly created projects have no session or persistent properties.
+ * </p>
+ * <p>
+ * If the project content area does not contain a project description file,
+ * an initial project description file is written in the project content area
+ * with the following information:
+ * <ul>
+ * <li>no references to other projects</li>
+ * <li>no natures</li>
+ * <li>an empty build spec</li>
+ * <li>an empty comment</li>
+ * </ul>
+ * If there is an existing project description file, it is not overwritten.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that this project has been added to the workspace.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project already exists in the workspace.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The project location is not valid (according to
+ * <code>IWorkspace.validateProjectLocation</code>).</li>
+ * <li> The project description file could not be created in the project
+ * content area.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IWorkspace#validateProjectLocation(IProject, IPath)
+ * @see IResourceRuleFactory#createRule(IResource)
+ */
+ public void create(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates a new project resource in the workspace using the given project
+ * description. Upon successful completion, the project will exist but be closed.
+ * <p>
+ * Newly created projects have no session or persistent properties.
+ * </p>
+ * <p>
+ * If the project content area given in the project description does not
+ * contain a project description file, a project description file is written
+ * in the project content area with the natures, build spec, comment, and
+ * referenced projects as specified in the given project description.
+ * If there is an existing project description file, it is not overwritten. In either
+ * case, this method does not cause natures to be configured.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the project has been added to the workspace.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * <p>
+ * The {@link IResource#HIDDEN} update flag indicates that this resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link IResource#setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ *
+ * @param description the project description
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project already exists in the workspace.</li>
+ * <li> The name of this resource is not valid (according to
+ * <code>IWorkspace.validateName</code>).</li>
+ * <li> The project location is not valid (according to
+ * <code>IWorkspace.validateProjectLocation</code>).</li>
+ * <li> The project description file could not be created in the project
+ * content area.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IWorkspace#validateProjectLocation(IProject, IPath)
+ * @see IResourceRuleFactory#createRule(IResource)
+ *
+ * @since 3.4
+ */
+ public void create(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes this project from the workspace.
+ * No action is taken if this project does not exist.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * delete(
+ * (deleteContent ? IResource.ALWAYS_DELETE_PROJECT_CONTENT : IResource.NEVER_DELETE_PROJECT_CONTENT )
+ * | (force ? FORCE : IResource.NONE),
+ * monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param deleteContent a flag controlling how whether content is
+ * aggressively deleted
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project could not be deleted.</li>
+ * <li> This project's contents could not be deleted.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int, IProgressMonitor)
+ * @see #open(IProgressMonitor)
+ * @see #close(IProgressMonitor)
+ * @see IResource#delete(int,IProgressMonitor)
+ * @see IResourceRuleFactory#deleteRule(IResource)
+ */
+ public void delete(boolean deleteContent, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns this project's content type matcher. This content type matcher takes
+ * project specific preferences and nature-content type associations into
+ * account.
+ *
+ * @return the content type matcher for this project
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist.</li>
+ * <li> This project is not open.</li>
+ * </ul>
+ * @see IContentTypeMatcher
+ * @since 3.1
+ */
+ public IContentTypeMatcher getContentTypeMatcher() throws CoreException;
+
+ /**
+ * Returns the description for this project.
+ * The returned value is a copy and cannot be used to modify
+ * this project. The returned value is suitable for use in creating,
+ * copying and moving other projects.
+ *
+ * @return the description for this project
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist.</li>
+ * <li> This project is not open.</li>
+ * </ul>
+ * @see #create(IProgressMonitor)
+ * @see #create(IProjectDescription, IProgressMonitor)
+ * @see IResource#copy(IProjectDescription, int, IProgressMonitor)
+ * @see #move(IProjectDescription, boolean, IProgressMonitor)
+ */
+ public IProjectDescription getDescription() throws CoreException;
+
+ /**
+ * Returns a handle to the file with the given name in this project.
+ * <p>
+ * This is a resource handle operation; neither the resource nor
+ * the result need exist in the workspace.
+ * The validation check on the resource name/path is not done
+ * when the resource handle is constructed; rather, it is done
+ * automatically as the resource is created.
+ * </p>
+ *
+ * @param name the string name of the member file
+ * @return the (handle of the) member file
+ * @see #getFolder(String)
+ */
+ public IFile getFile(String name);
+
+ /**
+ * Returns a handle to the folder with the given name in this project.
+ * <p>
+ * This is a resource handle operation; neither the container
+ * nor the result need exist in the workspace.
+ * The validation check on the resource name/path is not done
+ * when the resource handle is constructed; rather, it is done
+ * automatically as the resource is created.
+ * </p>
+ *
+ * @param name the string name of the member folder
+ * @return the (handle of the) member folder
+ * @see #getFile(String)
+ */
+ public IFolder getFolder(String name);
+
+ /**
+ * Returns the specified project nature for this project or <code>null</code> if
+ * the project nature has not been added to this project.
+ * Clients may downcast to a more concrete type for more nature-specific methods.
+ * The documentation for a project nature specifies any such additional protocol.
+ * <p>
+ * This may cause the plug-in that provides the given nature to be activated.
+ * </p>
+ *
+ * @param natureId the fully qualified nature extension identifier, formed
+ * by combining the nature extension id with the id of the declaring plug-in.
+ * (e.g. <code>"com.example.acmeplugin.coolnature"</code>)
+ * @return the project nature object
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist.</li>
+ * <li> This project is not open.</li>
+ * <li> The project nature extension could not be found.</li>
+ * </ul>
+ */
+ public IProjectNature getNature(String natureId) throws CoreException;
+
+ /**
+ * Returns the location in the local file system of the project-specific
+ * working data area for use by the given plug-in or <code>null</code>
+ * if the project does not exist.
+ * <p>
+ * The content, structure, and management of this area is
+ * the responsibility of the plug-in. This area is deleted when the
+ * project is deleted.
+ * </p><p>
+ * This project needs to exist but does not need to be open.
+ * </p>
+ * @param plugin the plug-in
+ * @return a local file system path
+ * @deprecated Use <code>IProject.getWorkingLocation(plugin.getUniqueIdentifier())</code>.
+ */
+ public IPath getPluginWorkingLocation(IPluginDescriptor plugin);
+
+ /**
+ * Returns the location in the local file system of the project-specific
+ * working data area for use by the bundle/plug-in with the given identifier,
+ * or <code>null</code> if the project does not exist.
+ * <p>
+ * The content, structure, and management of this area is
+ * the responsibility of the bundle/plug-in. This area is deleted when the
+ * project is deleted.
+ * </p><p>
+ * This project needs to exist but does not need to be open.
+ * </p>
+ * @param id the bundle or plug-in's identifier
+ * @return a local file system path
+ * @since 3.0
+ */
+ public IPath getWorkingLocation(String id);
+
+ /**
+ * Returns the projects referenced by this project. This includes
+ * both the static and dynamic references of this project.
+ * The returned projects need not exist in the workspace.
+ * The result will not contain duplicates. Returns an empty
+ * array if there are no referenced projects.
+ *
+ * @return a list of projects
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist.</li>
+ * <li> This project is not open.</li>
+ * </ul>
+ * @see IProjectDescription#getReferencedProjects()
+ * @see IProjectDescription#getDynamicReferences()
+ */
+ public IProject[] getReferencedProjects() throws CoreException;
+
+ /**
+ * Returns the list of all open projects which reference
+ * this project. This project may or may not exist. Returns
+ * an empty array if there are no referencing projects.
+ *
+ * @return a list of open projects referencing this project
+ */
+ public IProject[] getReferencingProjects();
+
+ /**
+ * Returns whether the project nature specified by the given
+ * nature extension id has been added to this project.
+ *
+ * @param natureId the nature extension identifier
+ * @return <code>true</code> if the project has the given nature
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist.</li>
+ * <li> This project is not open.</li>
+ * </ul>
+ */
+ public boolean hasNature(String natureId) throws CoreException;
+
+ /**
+ * Returns true if the project nature specified by the given
+ * nature extension id is enabled for this project, and false otherwise.
+ * <p>
+ * <ul>Reasons for a nature not to be enabled include:
+ * <li> The nature is not available in the install.</li>
+ * <li> The nature has not been added to this project.</li>
+ * <li> The nature has a prerequisite that is not enabled
+ * for this project.</li>
+ * <li> The nature specifies "one-of-nature" membership in
+ * a set, and there is another nature on this project belonging
+ * to that set.</li>
+ * <li> The prerequisites for the nature form a cycle.</li>
+ * </ul>
+ * </p>
+ * @param natureId the nature extension identifier
+ * @return <code>true</code> if the given nature is enabled for this project
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist.</li>
+ * <li> This project is not open.</li>
+ * </ul>
+ * @since 2.0
+ * @see IWorkspace#validateNatureSet(String[])
+ */
+ public boolean isNatureEnabled(String natureId) throws CoreException;
+
+ /**
+ * Returns whether this project is open.
+ * <p>
+ * A project must be opened before it can be manipulated.
+ * A closed project is passive and has a minimal memory
+ * footprint; a closed project has no members.
+ * </p>
+ *
+ * @return <code>true</code> if this project is open, <code>false</code> if
+ * this project is closed or does not exist
+ * @see #open(IProgressMonitor)
+ * @see #close(IProgressMonitor)
+ */
+ public boolean isOpen();
+
+ /**
+ * Renames this project so that it is located at the name in
+ * the given description.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * move(description, (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event that will include
+ * an indication that the resource has been removed from its parent
+ * and that a corresponding resource has been added to its new parent.
+ * Additional information provided with resource delta shows that these
+ * additions and removals are related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the description for the destination project
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource is not accessible.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceDelta#getFlags()
+ * @see IResource#move(IProjectDescription,int,IProgressMonitor)
+ * @see IResourceRuleFactory#moveRule(IResource, IResource)
+ */
+ public void move(IProjectDescription description, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Opens this project. No action is taken if the project is already open.
+ * <p>
+ * Opening a project constructs an in-memory representation
+ * of its resources from information stored on disk.
+ * </p>
+ * <p>
+ * The <code>BACKGROUND_REFRESH</code> update flag controls how
+ * this method behaves when a project is opened for the first time on a location
+ * that has existing resources on disk. If this flag is specified, resources on disk
+ * will be added to the project in the background after this method returns.
+ * Child resources of the project may not be available until this background
+ * refresh completes. If this flag is not specified, resources on disk are added
+ * to the project in the foreground before this method returns.
+ * </p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event that includes
+ * an indication that the project has been opened and its resources
+ * have been added to the tree. If the <code>BACKGROUND_REFRESH</code>
+ * update flag is specified, multiple resource change events may occur as
+ * resources on disk are discovered and added to the tree.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #close(IProgressMonitor)
+ * @see IResource#BACKGROUND_REFRESH
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ * @since 3.1
+ */
+ public void open(int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Opens this project. No action is taken if the project is already open.
+ * <p>
+ * This is a convenience method, fully equivalent to
+ * <code>open(IResource.NONE, monitor)</code>.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event that includes
+ * an indication that the project has been opened and its resources
+ * have been added to the tree.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #close(IProgressMonitor)
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ */
+ public void open(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Changes this project resource to match the given project
+ * description. This project should exist and be open.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * setDescription(description, KEEP_HISTORY, monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method requires the {@link IWorkspaceRoot} scheduling rule.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the project's content has changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the project description
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist in the workspace.</li>
+ * <li> This project is not open.</li>
+ * <li> The location in the local file system corresponding to the project
+ * description file is occupied by a directory.</li>
+ * <li> The workspace is out of sync with the project description file
+ * in the local file system .</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see #getDescription()
+ * @see IProjectNature#configure()
+ * @see IProjectNature#deconfigure()
+ * @see #setDescription(IProjectDescription,int,IProgressMonitor)
+ */
+ public void setDescription(IProjectDescription description, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Changes this project resource to match the given project
+ * description. This project should exist and be open.
+ * <p>
+ * The given project description is used to change the project's
+ * natures, build spec, comment, and referenced projects.
+ * The name and location of a project cannot be changed using this method;
+ * these settings in the project description are ignored. To change a project's
+ * name or location, use {@link IResource#move(IProjectDescription, int, IProgressMonitor)}.
+ * The project's session and persistent properties are not affected.
+ * </p>
+ * <p>
+ * If the new description includes nature ids of natures that the project
+ * did not have before, these natures will be configured in automatically,
+ * which involves instantiating the project nature and calling
+ * {@link IProjectNature#configure()} on it. An internal reference to the
+ * nature object is retained, and will be returned on subsequent calls to
+ * <code>getNature</code> for the specified nature id. Similarly, any natures
+ * the project had which are no longer required will be automatically
+ * de-configured by calling {@link IProjectNature#deconfigure}
+ * on the nature object and letting go of the internal reference to it.
+ * </p>
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If <code>FORCE</code> is not specified, the method will only attempt
+ * to overwrite the project's description file in the local file system
+ * provided it is in sync with the workspace. This option ensures there is no
+ * unintended data loss; it is the recommended setting.
+ * However, if <code>FORCE</code> is specified, an attempt will be made
+ * to write the project description file in the local file system, overwriting
+ * any existing one if need be.
+ * </p>
+ * <p>
+ * The <code>KEEP_HISTORY</code> update flag controls whether or not a copy of
+ * current contents of the project description file should be captured in the
+ * workspace's local history. The local history mechanism serves as a safety net
+ * to help the user recover from mistakes that might otherwise result in data
+ * loss. Specifying <code>KEEP_HISTORY</code> is recommended. Note that local
+ * history is maintained with each individual project, and gets discarded when
+ * a project is deleted from the workspace.
+ * </p>
+ * <p>
+ * The <code>AVOID_NATURE_CONFIG</code> update flag controls whether or
+ * not added and removed natures should be configured or de-configured. If this
+ * flag is not specified, then added natures will be configured and removed natures
+ * will be de-configured. If this flag is specified, natures can still be added or
+ * removed, but they will not be configured or de-configured.
+ * </p>
+ * <p>
+ * The scheduling rule required for this operation depends on the
+ * <code>AVOID_NATURE_CONFIG</code> flag. If the flag is specified the
+ * {@link IResourceRuleFactory#modifyRule} is required; If the flag is not specified,
+ * the {@link IWorkspaceRoot} scheduling rule is required.
+ * </p>
+ * <p>
+ * Update flags other than <code>FORCE</code>, <code>KEEP_HISTORY</code>,
+ * and <code>AVOID_NATURE_CONFIG</code> are ignored.
+ * </p>
+ * <p>
+ * Prior to modifying the project description file, the file modification
+ * validator (if provided by the Team plug-in), will be given a chance to
+ * perform any last minute preparations. Validation is performed by calling
+ * <code>IFileModificationValidator.validateSave</code> on the project
+ * description file. If the validation fails, then this operation will fail.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event, including an indication
+ * that the project's content has changed.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the project description
+ * @param updateFlags bit-wise or of update flag constants
+ * (<code>FORCE</code>, <code>KEEP_HISTORY</code> and
+ * <code>AVOID_NATURE_CONFIG</code>)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This project does not exist in the workspace.</li>
+ * <li> This project is not open.</li>
+ * <li> The location in the local file system corresponding to the project
+ * description file is occupied by a directory.</li>
+ * <li> The workspace is not in sync with the project
+ * description file in the local file system and <code>FORCE</code> is not
+ * specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The file modification validator disallowed the change.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see #getDescription()
+ * @see IProjectNature#configure()
+ * @see IProjectNature#deconfigure()
+ * @see IResource#FORCE
+ * @see IResource#KEEP_HISTORY
+ * @see IResource#AVOID_NATURE_CONFIG
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ * @since 2.0
+ */
+ public void setDescription(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectDescription.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.net.URI;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A project description contains the meta-data required to define
+ * a project. In effect, a project description is a project's "content".
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IProjectDescription {
+ /**
+ * Constant that denotes the name of the project description file (value
+ * <code>".project"</code>).
+ * The handle of a project's description file is
+ * <code>project.getFile(DESCRIPTION_FILE_NAME)</code>.
+ * The project description file is located in the root of the project's content area.
+ *
+ * @since 2.0
+ */
+ public static final String DESCRIPTION_FILE_NAME = ".project"; //$NON-NLS-1$
+
+ /**
+ * Returns the list of build commands to run when building the described project.
+ * The commands are listed in the order in which they are to be run.
+ *
+ * @return the list of build commands for the described project
+ */
+ public ICommand[] getBuildSpec();
+
+ /**
+ * Returns the descriptive comment for the described project.
+ *
+ * @return the comment for the described project
+ */
+ public String getComment();
+
+ /**
+ * Returns the dynamic project references for the described project. Dynamic
+ * project references can be used instead of simple project references in cases
+ * where the reference information is computed dynamically be a third party.
+ * These references are persisted by the workspace in a private location outside
+ * the project description file, and as such will not be shared when a project is
+ * exported or persisted in a repository. A client using project references
+ * is always responsible for setting these references when a project is created
+ * or recreated.
+ * <p>
+ * The returned projects need not exist in the workspace. The result will not
+ * contain duplicates. Returns an empty array if there are no dynamic project
+ * references on this description.
+ *
+ * @see #getReferencedProjects()
+ * @see #setDynamicReferences(IProject[])
+ * @return a list of projects
+ * @since 3.0
+ */
+ public IProject[] getDynamicReferences();
+
+ /**
+ * Returns the local file system location for the described project. The path
+ * will be either an absolute file system path, or a relative path whose first
+ * segment is the name of a workspace path variable. <code>null</code> is
+ * returned if the default location should be used. This method will return
+ * <code>null</code> if this project is not located in the local file system.
+ *
+ * @return the location for the described project or <code>null</code>
+ * @deprecated Since 3.2, project locations are not necessarily in the local file
+ * system. The more general {@link #getLocationURI()} method should be used instead.
+ */
+ public IPath getLocation();
+
+ /**
+ * Returns the location URI for the described project. <code>null</code> is
+ * returned if the default location should be used.
+ *
+ * @return the location for the described project or <code>null</code>
+ * @since 3.2
+ * @see #setLocationURI(URI)
+ */
+ public URI getLocationURI();
+
+ /**
+ * Returns the name of the described project.
+ *
+ * @return the name of the described project
+ */
+ public String getName();
+
+ /**
+ * Returns the list of natures associated with the described project.
+ * Returns an empty array if there are no natures on this description.
+ *
+ * @return the list of natures for the described project
+ * @see #setNatureIds(String[])
+ */
+ public String[] getNatureIds();
+
+ /**
+ * Returns the projects referenced by the described project. These references
+ * are persisted in the project description file (".project") and as such
+ * will be shared whenever the project is exported to another workspace. For
+ * references that are likely to change from one workspace to another, dynamic
+ * references should be used instead.
+ * <p>
+ * The projects need not exist in the workspace.
+ * The result will not contain duplicates. Returns an empty
+ * array if there are no referenced projects on this description.
+ *
+ *@see #getDynamicReferences()
+ * @return a list of projects
+ */
+ public IProject[] getReferencedProjects();
+
+ /**
+ * Returns whether the project nature specified by the given
+ * nature extension id has been added to the described project.
+ *
+ * @param natureId the nature extension identifier
+ * @return <code>true</code> if the described project has the given nature
+ */
+ public boolean hasNature(String natureId);
+
+ /**
+ * Returns a new build command.
+ * <p>
+ * Note that the new command does not become part of this project
+ * description's build spec until it is installed via the <code>setBuildSpec</code>
+ * method.
+ * </p>
+ *
+ * @return a new command
+ * @see #setBuildSpec(ICommand[])
+ */
+ public ICommand newCommand();
+
+ /**
+ * Sets the list of build command to run when building the described project.
+ * <p>
+ * Users must call {@link IProject#setDescription(IProjectDescription, int, IProgressMonitor)}
+ * before changes made to this description take effect.
+ * </p>
+ *
+ * @param buildSpec the array of build commands to run
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @see #getBuildSpec()
+ * @see #newCommand()
+ */
+ public void setBuildSpec(ICommand[] buildSpec);
+
+ /**
+ * Sets the comment for the described project.
+ * <p>
+ * Users must call {@link IProject#setDescription(IProjectDescription, int, IProgressMonitor)}
+ * before changes made to this description take effect.
+ * </p>
+ *
+ * @param comment the comment for the described project
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @see #getComment()
+ */
+ public void setComment(String comment);
+
+ /**
+ * Sets the dynamic project references for the described project.
+ * The projects need not exist in the workspace. Duplicates will be
+ * removed.
+ * <p>
+ * Users must call {@link IProject#setDescription(IProjectDescription, int, IProgressMonitor)}
+ * before changes made to this description take effect.
+ * </p>
+ * @see #getDynamicReferences()
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @param projects list of projects
+ * @since 3.0
+ */
+ public void setDynamicReferences(IProject[] projects);
+
+ /**
+ * Sets the local file system location for the described project. The path must
+ * be either an absolute file system path, or a relative path whose first
+ * segment is the name of a defined workspace path variable. If
+ * <code>null</code> is specified, the default location is used.
+ * <p>
+ * Setting the location on a description for a project which already
+ * exists has no effect; the new project location is ignored when the
+ * description is set on the already existing project. This method is
+ * intended for use on descriptions for new projects or for destination
+ * projects for <code>copy</code> and <code>move</code>.
+ * </p>
+ * <p>
+ * This operation maps the root folder of the project to the exact location
+ * provided. For example, if the location for project named "P" is set
+ * to the path c:\my_plugins\Project1, the file resource at workspace path
+ * /P/index.html would be stored in the local file system at
+ * c:\my_plugins\Project1\index.html.
+ * </p>
+ *
+ * @param location the location for the described project or <code>null</code>
+ * @see #getLocation()
+ */
+ public void setLocation(IPath location);
+
+ /**
+ * Sets the location for the described project.
+ * If <code>null</code> is specified, the default location is used.
+ * <p>
+ * Setting the location on a description for a project which already
+ * exists has no effect; the new project location is ignored when the
+ * description is set on the already existing project. This method is
+ * intended for use on descriptions for new projects or for destination
+ * projects for <code>copy</code> and <code>move</code>.
+ * </p>
+ * <p>
+ * This operation maps the root folder of the project to the exact location
+ * provided. For example, if the location for project named "P" is set
+ * to the URI file://c:/my_plugins/Project1, the file resource at workspace path
+ * /P/index.html would be stored in the local file system at
+ * file://c:/my_plugins/Project1/index.html.
+ * </p>
+ *
+ * @param location the location for the described project or <code>null</code>
+ * @see #getLocationURI()
+ * @see IWorkspace#validateProjectLocationURI(IProject, URI)
+ * @since 3.2
+ */
+ public void setLocationURI(URI location);
+
+ /**
+ * Sets the name of the described project.
+ * <p>
+ * Setting the name on a description and then setting the
+ * description on the project has no effect; the new name is ignored.
+ * </p>
+ * <p>
+ * Creating a new project with a description name which doesn't
+ * match the project handle name results in the description name
+ * being ignored; the project will be created using the name
+ * in the handle.
+ * </p>
+ *
+ * @param projectName the name of the described project
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @see #getName()
+ */
+ public void setName(String projectName);
+
+ /**
+ * Sets the list of natures associated with the described project.
+ * A project created with this description will have these natures
+ * added to it in the given order.
+ * <p>
+ * Users must call {@link IProject#setDescription(IProjectDescription, int, IProgressMonitor)}
+ * before changes made to this description take effect.
+ * </p>
+ *
+ * @param natures the list of natures
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @see #getNatureIds()
+ */
+ public void setNatureIds(String[] natures);
+
+ /**
+ * Sets the referenced projects, ignoring any duplicates.
+ * The order of projects is preserved.
+ * The projects need not exist in the workspace.
+ * <p>
+ * Users must call {@link IProject#setDescription(IProjectDescription, int, IProgressMonitor)}
+ * before changes made to this description take effect.
+ * </p>
+ *
+ * @param projects a list of projects
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @see #getReferencedProjects()
+ */
+ public void setReferencedProjects(IProject[] projects);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectNature.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Interface for project nature runtime classes.
+ * It can configure a project with the project nature, or de-configure it.
+ * When a project is configured with a project nature, this is
+ * recorded in the list of project natures on the project.
+ * Individual project natures may expose a more specific runtime type,
+ * with additional API for manipulating the project in a
+ * nature-specific way.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IProject#getNature(String)
+ * @see IProject#hasNature(String)
+ * @see IProjectDescription#getNatureIds()
+ * @see IProjectDescription#hasNature(String)
+ * @see IProjectDescription#setNatureIds(String[])
+ */
+public interface IProjectNature {
+ /**
+ * Configures this nature for its project. This is called by the workspace
+ * when natures are added to the project using <code>IProject.setDescription</code>
+ * and should not be called directly by clients. The nature extension
+ * id is added to the list of natures before this method is called,
+ * and need not be added here.
+ *
+ * Exceptions thrown by this method will be propagated back to the caller
+ * of <code>IProject.setDescription</code>, but the nature will remain in
+ * the project description.
+ *
+ * @exception CoreException if this method fails.
+ */
+ public void configure() throws CoreException;
+
+ /**
+ * De-configures this nature for its project. This is called by the workspace
+ * when natures are removed from the project using
+ * <code>IProject.setDescription</code> and should not be called directly by
+ * clients. The nature extension id is removed from the list of natures before
+ * this method is called, and need not be removed here.
+ *
+ * Exceptions thrown by this method will be propagated back to the caller
+ * of <code>IProject.setDescription</code>, but the nature will still be
+ * removed from the project description.
+ * *
+ * @exception CoreException if this method fails.
+ */
+ public void deconfigure() throws CoreException;
+
+ /**
+ * Returns the project to which this project nature applies.
+ *
+ * @return the project handle
+ */
+ public IProject getProject();
+
+ /**
+ * Sets the project to which this nature applies.
+ * Used when instantiating this project nature runtime.
+ * This is called by <code>IProject.create()</code> or
+ * <code>IProject.setDescription()</code>
+ * and should not be called directly by clients.
+ *
+ * @param project the project to which this nature applies
+ */
+ public void setProject(IProject project);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectNatureDescriptor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+/**
+ * A project nature descriptor contains information about a project nature
+ * obtained from the plug-in manifest (<code>plugin.xml</code>) file.
+ * <p>
+ * Nature descriptors are platform-defined objects that exist
+ * independent of whether that nature's plug-in has been started.
+ * In contrast, a project nature's runtime object (<code>IProjectNature</code>)
+ * generally runs plug-in-defined code.
+ * </p>
+ *
+ * @see IProjectNature
+ * @see IWorkspace#getNatureDescriptor(String)
+ * @since 2.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IProjectNatureDescriptor {
+ /**
+ * Returns the unique identifier of this nature.
+ * <p>
+ * The nature identifier is composed of the nature's plug-in id and the simple
+ * id of the nature extension. For example, if plug-in <code>"com.xyz"</code>
+ * defines a nature extension with id <code>"myNature"</code>, the unique
+ * nature identifier will be <code>"com.xyz.myNature"</code>.
+ * </p>
+ * @return the unique nature identifier
+ */
+ public String getNatureId();
+
+ /**
+ * Returns a displayable label for this nature.
+ * Returns the empty string if no label for this nature
+ * is specified in the plug-in manifest file.
+ * <p> Note that any translation specified in the plug-in manifest
+ * file is automatically applied.
+ * </p>
+ *
+ * @return a displayable string label for this nature,
+ * possibly the empty string
+ */
+ public String getLabel();
+
+ /**
+ * Returns the unique identifiers of the natures required by this nature.
+ * Nature requirements are specified by the <code>"requires-nature"</code>
+ * element on a nature extension.
+ * Returns an empty array if no natures are required by this nature.
+ *
+ * @return an array of nature ids that this nature requires,
+ * possibly an empty array.
+ */
+ public String[] getRequiredNatureIds();
+
+ /**
+ * Returns the identifiers of the nature sets that this nature belongs to.
+ * Nature set inclusion is specified by the <code>"one-of-nature"</code>
+ * element on a nature extension.
+ * Returns an empty array if no nature sets are specified for this nature.
+ *
+ * @return an array of nature set ids that this nature belongs to,
+ * possibly an empty array.
+ */
+ public String[] getNatureSetIds();
+
+ /**
+ * Returns whether this project nature allows linked resources to be created
+ * in projects where this nature is installed.
+ *
+ * @return boolean <code>true</code> if creating links is allowed,
+ * and <code>false</code> otherwise.
+ * @see IFolder#createLink(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor)
+ * @see IFile#createLink(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor)
+ * @since 2.1
+ */
+ public boolean isLinkingAllowed();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResource.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,2595 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Incorporated - get/setResourceAttribute code
+ * Oakland Software Incorporated - added getSessionProperties and getPersistentProperties
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.net.URI;
+import java.util.Map;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * The workspace analog of file system files
+ * and directories. There are exactly four <i>types</i> of resource:
+ * files, folders, projects and the workspace root.
+ * <p>
+ * File resources are similar to files in that they
+ * hold data directly. Folder resources are analogous to directories in that they
+ * hold other resources but cannot directly hold data. Project resources
+ * group files and folders into reusable clusters. The workspace root is the
+ * top level resource under which all others reside.
+ * </p>
+ * <p>
+ * Features of resources:
+ * <ul>
+ * <li><code>IResource</code> objects are <i>handles</i> to state maintained
+ * by a workspace. That is, resource objects do not actually contain data
+ * themselves but rather represent resource state and give it behavior. Programmers
+ * are free to manipulate handles for resources that do not exist in a workspace
+ * but must keep in mind that some methods and operations require that an actual
+ * resource be available.</li>
+ * <li>Resources have two different kinds of properties as detailed below. All
+ * properties are keyed by qualified names.</li>
+ * <ul>
+ * <li>Session properties: Session properties live for the lifetime of one execution of
+ * the workspace. They are not stored on disk. They can carry arbitrary
+ * object values. Clients should be aware that these values are kept in memory
+ * at all times and, as such, the values should not be large.</li>
+ * <li>Persistent properties: Persistent properties have string values which are stored
+ * on disk across platform sessions. The value of a persistent property is a
+ * string which should be short (i.e., under 2KB). </li>
+ * </ul>
+ * </li>
+ * <li>Resources are identified by type and by their <i>path</i>, which is similar to a file system
+ * path. The name of a resource is the last segment of its path. A resource's parent
+ * is located by removing the last segment (the resource's name) from the resource's full path.</li>
+ * <li>Resources can be local or non-local. A non-local resource is one whose
+ * contents and properties have not been fetched from a repository.</li>
+ * <li><i>Phantom</i> resources represent incoming additions or outgoing deletions
+ * which have yet to be reconciled with a synchronization partner. </li>
+ * </ul>
+ * </p>
+ * <p>
+ * Resources implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see IWorkspace
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResource extends IAdaptable, ISchedulingRule {
+
+ /*====================================================================
+ * Constants defining resource types: There are four possible resource types
+ * and their type constants are in the integer range 1 to 8 as defined below.
+ *====================================================================*/
+
+ /**
+ * Type constant (bit mask value 1) which identifies file resources.
+ *
+ * @see IResource#getType()
+ * @see IFile
+ */
+ public static final int FILE = 0x1;
+
+ /**
+ * Type constant (bit mask value 2) which identifies folder resources.
+ *
+ * @see IResource#getType()
+ * @see IFolder
+ */
+ public static final int FOLDER = 0x2;
+
+ /**
+ * Type constant (bit mask value 4) which identifies project resources.
+ *
+ * @see IResource#getType()
+ * @see IProject
+ */
+ public static final int PROJECT = 0x4;
+
+ /**
+ * Type constant (bit mask value 8) which identifies the root resource.
+ *
+ * @see IResource#getType()
+ * @see IWorkspaceRoot
+ */
+ public static final int ROOT = 0x8;
+
+ /*====================================================================
+ * Constants defining the depth of resource tree traversal:
+ *====================================================================*/
+
+ /**
+ * Depth constant (value 0) indicating this resource, but not any of its members.
+ */
+ public static final int DEPTH_ZERO = 0;
+
+ /**
+ * Depth constant (value 1) indicating this resource and its direct members.
+ */
+ public static final int DEPTH_ONE = 1;
+
+ /**
+ * Depth constant (value 2) indicating this resource and its direct and
+ * indirect members at any depth.
+ */
+ public static final int DEPTH_INFINITE = 2;
+
+ /*====================================================================
+ * Constants for update flags for delete, move, copy, open, etc.:
+ *====================================================================*/
+
+ /**
+ * Update flag constant (bit mask value 1) indicating that the operation
+ * should proceed even if the resource is out of sync with the local file
+ * system.
+ *
+ * @since 2.0
+ */
+ public static final int FORCE = 0x1;
+
+ /**
+ * Update flag constant (bit mask value 2) indicating that the operation
+ * should maintain local history by taking snapshots of the contents of
+ * files just before being overwritten or deleted.
+ *
+ * @see IFile#getHistory(IProgressMonitor)
+ * @since 2.0
+ */
+ public static final int KEEP_HISTORY = 0x2;
+
+ /**
+ * Update flag constant (bit mask value 4) indicating that the operation
+ * should delete the files and folders of a project.
+ * <p>
+ * Deleting a project that is open ordinarily deletes all its files and folders,
+ * whereas deleting a project that is closed retains its files and folders.
+ * Specifying <code>ALWAYS_DELETE_PROJECT_CONTENT</code> indicates that the contents
+ * of a project are to be deleted regardless of whether the project is open or closed
+ * at the time; specifying <code>NEVER_DELETE_PROJECT_CONTENT</code> indicates that
+ * the contents of a project are to be retained regardless of whether the project
+ * is open or closed at the time.
+ * </p>
+ *
+ * @see #NEVER_DELETE_PROJECT_CONTENT
+ * @since 2.0
+ */
+ public static final int ALWAYS_DELETE_PROJECT_CONTENT = 0x4;
+
+ /**
+ * Update flag constant (bit mask value 8) indicating that the operation
+ * should preserve the files and folders of a project.
+ * <p>
+ * Deleting a project that is open ordinarily deletes all its files and folders,
+ * whereas deleting a project that is closed retains its files and folders.
+ * Specifying <code>ALWAYS_DELETE_PROJECT_CONTENT</code> indicates that the contents
+ * of a project are to be deleted regardless of whether the project is open or closed
+ * at the time; specifying <code>NEVER_DELETE_PROJECT_CONTENT</code> indicates that
+ * the contents of a project are to be retained regardless of whether the project
+ * is open or closed at the time.
+ * </p>
+ *
+ * @see #ALWAYS_DELETE_PROJECT_CONTENT
+ * @since 2.0
+ */
+ public static final int NEVER_DELETE_PROJECT_CONTENT = 0x8;
+
+ /**
+ * Update flag constant (bit mask value 16) indicating that the link creation
+ * should proceed even if the local file system file or directory is missing.
+ *
+ * @see IFolder#createLink(IPath, int, IProgressMonitor)
+ * @see IFile#createLink(IPath, int, IProgressMonitor)
+ * @since 2.1
+ */
+ public static final int ALLOW_MISSING_LOCAL = 0x10;
+
+ /**
+ * Update flag constant (bit mask value 32) indicating that a copy or move
+ * operation should only copy the link, rather than copy the underlying
+ * contents of the linked resource.
+ *
+ * @see #copy(IPath, int, IProgressMonitor)
+ * @see #move(IPath, int, IProgressMonitor)
+ * @since 2.1
+ */
+ public static final int SHALLOW = 0x20;
+
+ /**
+ * Update flag constant (bit mask value 64) indicating that setting the
+ * project description should not attempt to configure and de-configure
+ * natures.
+ *
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @since 3.0
+ */
+ public static final int AVOID_NATURE_CONFIG = 0x40;
+
+ /**
+ * Update flag constant (bit mask value 128) indicating that opening a
+ * project for the first time or creating a linked folder should refresh in the
+ * background.
+ *
+ * @see IProject#open(int, IProgressMonitor)
+ * @see IFolder#createLink(URI, int, IProgressMonitor)
+ * @since 3.1
+ */
+ public static final int BACKGROUND_REFRESH = 0x80;
+
+ /**
+ * Update flag constant (bit mask value 256) indicating that a
+ * resource should be replaced with a resource of the same name
+ * at a different file system location.
+ *
+ * @see IFile#createLink(URI, int, IProgressMonitor)
+ * @see IFolder#createLink(URI, int, IProgressMonitor)
+ * @see IResource#move(IProjectDescription, int, IProgressMonitor)
+ * @since 3.2
+ */
+ public static final int REPLACE = 0x100;
+
+ /**
+ * Update flag constant (bit mask value 512) indicating that ancestor
+ * resources of the target resource should be checked.
+ *
+ * @see IResource#isLinked(int)
+ * @since 3.2
+ */
+ public static final int CHECK_ANCESTORS = 0x200;
+
+ /**
+ * Update flag constant (bit mask value 0x400) indicating that a
+ * resource should be marked as derived.
+ *
+ * @see IFile#create(java.io.InputStream, int, IProgressMonitor)
+ * @see IFolder#create(int, boolean, IProgressMonitor)
+ * @see IResource#setDerived(boolean)
+ * @since 3.2
+ */
+ public static final int DERIVED = 0x400;
+
+ /**
+ * Update flag constant (bit mask value 0x800) indicating that a
+ * resource should be marked as team private.
+ *
+ * @see IFile#create(java.io.InputStream, int, IProgressMonitor)
+ * @see IFolder#create(int, boolean, IProgressMonitor)
+ * @see IResource#copy(IPath, int, IProgressMonitor)
+ * @see IResource#setTeamPrivateMember(boolean)
+ * @since 3.2
+ */
+ public static final int TEAM_PRIVATE = 0x800;
+
+ /**
+ * Update flag constant (bit mask value 0x1000) indicating that a
+ * resource should be marked as a hidden resource.
+ *
+ * @since 3.4
+ */
+ public static final int HIDDEN = 0x1000;
+
+ /*====================================================================
+ * Other constants:
+ *====================================================================*/
+
+ /**
+ * Modification stamp constant (value -1) indicating no modification stamp is
+ * available.
+ *
+ * @see #getModificationStamp()
+ */
+ public static final int NULL_STAMP = -1;
+
+ /**
+ * General purpose zero-valued bit mask constant. Useful whenever you need to
+ * supply a bit mask with no bits set.
+ * <p>
+ * Example usage:
+ * <code>
+ * <pre>
+ * delete(IResource.NONE, null)
+ * </pre>
+ * </code>
+ * </p>
+ *
+ * @since 2.0
+ */
+ public static final int NONE = 0;
+
+ /**
+ * Accepts the given visitor for an optimized traversal.
+ * The visitor's <code>visit</code> method is called, and is provided with a
+ * proxy to this resource. The proxy is a transient object that can be queried
+ * very quickly for information about the resource. If the actual resource
+ * handle is needed, it can be obtained from the proxy. Requesting the resource
+ * handle, or the full path of the resource, will degrade performance of the
+ * visit.
+ * <p>
+ * The entire subtree under the given resource is traversed to infinite depth,
+ * unless the visitor ignores a subtree by returning <code>false</code> from its
+ * <code>visit</code> method.
+ * </p>
+ * <p>No guarantees are made about the behavior of this method if resources
+ * are deleted or added during the traversal of this resource hierarchy. If
+ * resources are deleted during the traversal, they may still be passed to the
+ * visitor; if resources are created, they may not be passed to the visitor. If
+ * resources other than the one being visited are modified during the traversal,
+ * the resource proxy may contain stale information when that resource is
+ * visited.
+ * </p>
+ <p>
+ * If the {@link IContainer#INCLUDE_PHANTOMS} flag is not specified in the member
+ * flags (recommended), only member resources that exist will be visited.
+ * If the {@link IContainer#INCLUDE_PHANTOMS} flag is specified, the visit will
+ * also include any phantom member resource that the workspace is keeping track of.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} flag is not specified
+ * (recommended), team private members will not be visited. If the
+ * {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} flag is specified in the member
+ * flags, team private member resources are visited as well.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_HIDDEN} flag is not specified (recommended),
+ * hidden resources will not be visited. If the {@link IContainer#INCLUDE_HIDDEN} flag is specified
+ * in the member flags, hidden resources are visited as well.
+ * </p>
+ *
+ * @param visitor the visitor
+ * @param memberFlags bit-wise or of member flag constants
+ * ({@link IContainer#INCLUDE_PHANTOMS}, {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS}
+ * and {@link IContainer#INCLUDE_HIDDEN}) indicating which members are of interest
+ * @exception CoreException if this request fails. Reasons include:
+ * <ul>
+ * <li> the {@link IContainer#INCLUDE_PHANTOMS} flag is not specified and
+ * this resource does not exist.</li>
+ * <li> the {@link IContainer#INCLUDE_PHANTOMS} flag is not specified and
+ * this resource is a project that is not open.</li>
+ * <li> The visitor failed with this exception.</li>
+ * </ul>
+ * @see IContainer#INCLUDE_PHANTOMS
+ * @see IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS
+ * @see IContainer#INCLUDE_HIDDEN
+ * @see IResource#isPhantom()
+ * @see IResource#isTeamPrivateMember()
+ * @see IResourceProxyVisitor#visit(IResourceProxy)
+ * @since 2.1
+ */
+ public void accept(final IResourceProxyVisitor visitor, int memberFlags) throws CoreException;
+
+ /**
+ * Accepts the given visitor.
+ * The visitor's <code>visit</code> method is called with this
+ * resource. If the visitor returns <code>true</code>, this method
+ * visits this resource's members.
+ * <p>
+ * This is a convenience method, fully equivalent to
+ * <code>accept(visitor, IResource.DEPTH_INFINITE, IResource.NONE)</code>.
+ * </p>
+ *
+ * @param visitor the visitor
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> The visitor failed with this exception.</li>
+ * </ul>
+ * @see IResourceVisitor#visit(IResource)
+ * @see #accept(IResourceVisitor,int,int)
+ */
+ public void accept(IResourceVisitor visitor) throws CoreException;
+
+ /**
+ * Accepts the given visitor.
+ * The visitor's <code>visit</code> method is called with this
+ * resource. If the visitor returns <code>false</code>,
+ * this resource's members are not visited.
+ * <p>
+ * The subtree under the given resource is traversed to the supplied depth.
+ * </p>
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * accept(visitor, depth, includePhantoms ? IContainer.INCLUDE_PHANTOMS : IResource.NONE);
+ * </pre>
+ * </p>
+ *
+ * @param visitor the visitor
+ * @param depth the depth to which members of this resource should be
+ * visited. One of {@link IResource#DEPTH_ZERO}, {@link IResource#DEPTH_ONE},
+ * or {@link IResource#DEPTH_INFINITE}.
+ * @param includePhantoms <code>true</code> if phantom resources are
+ * of interest; <code>false</code> if phantom resources are not of
+ * interest.
+ * @exception CoreException if this request fails. Reasons include:
+ * <ul>
+ * <li> <code>includePhantoms</code> is <code>false</code> and
+ * this resource does not exist.</li>
+ * <li> <code>includePhantoms</code> is <code>true</code> and
+ * this resource does not exist and is not a phantom.</li>
+ * <li> The visitor failed with this exception.</li>
+ * </ul>
+ * @see IResource#isPhantom()
+ * @see IResourceVisitor#visit(IResource)
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ * @see IResource#accept(IResourceVisitor,int,int)
+ */
+ public void accept(IResourceVisitor visitor, int depth, boolean includePhantoms) throws CoreException;
+
+ /**
+ * Accepts the given visitor.
+ * The visitor's <code>visit</code> method is called with this
+ * resource. If the visitor returns <code>false</code>,
+ * this resource's members are not visited.
+ * <p>
+ * The subtree under the given resource is traversed to the supplied depth.
+ * </p>
+ * <p>
+ * No guarantees are made about the behavior of this method if resources are
+ * deleted or added during the traversal of this resource hierarchy. If
+ * resources are deleted during the traversal, they may still be passed to the
+ * visitor; if resources are created, they may not be passed to the visitor.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_PHANTOMS} flag is not specified in the member
+ * flags (recommended), only member resources that exists are visited.
+ * If the {@link IContainer#INCLUDE_PHANTOMS} flag is specified, the visit also
+ * includes any phantom member resource that the workspace is keeping track of.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} flag is not specified
+ * (recommended), team private members are not visited. If the
+ * {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS} flag is specified in the member
+ * flags, team private member resources are visited as well.
+ * </p>
+ * <p>
+ * If the {@link IContainer#EXCLUDE_DERIVED} flag is not specified
+ * (recommended), derived resources are visited. If the
+ * {@link IContainer#EXCLUDE_DERIVED} flag is specified in the member
+ * flags, derived resources are not visited.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_HIDDEN} flag is not specified (recommended),
+ * hidden resources will not be visited. If the {@link IContainer#INCLUDE_HIDDEN} flag is specified
+ * in the member flags, hidden resources are visited as well.
+ * </p>
+ *
+ * @param visitor the visitor
+ * @param depth the depth to which members of this resource should be
+ * visited. One of {@link IResource#DEPTH_ZERO}, {@link IResource#DEPTH_ONE},
+ * or {@link IResource#DEPTH_INFINITE}.
+ * @param memberFlags bit-wise or of member flag constants
+ * ({@link IContainer#INCLUDE_PHANTOMS}, {@link IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS},
+ * {@link IContainer#INCLUDE_HIDDEN} and {@link IContainer#EXCLUDE_DERIVED}) indicating which members are of interest
+ * @exception CoreException if this request fails. Reasons include:
+ * <ul>
+ * <li> the {@link IContainer#INCLUDE_PHANTOMS} flag is not specified and
+ * this resource does not exist.</li>
+ * <li> the {@link IContainer#INCLUDE_PHANTOMS} flag is not specified and
+ * this resource is a project that is not open.</li>
+ * <li> The visitor failed with this exception.</li>
+ * </ul>
+ * @see IContainer#INCLUDE_PHANTOMS
+ * @see IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS
+ * @see IContainer#INCLUDE_HIDDEN
+ * @see IContainer#EXCLUDE_DERIVED
+ * @see IResource#isDerived()
+ * @see IResource#isPhantom()
+ * @see IResource#isTeamPrivateMember()
+ * @see IResource#isHidden()
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ * @see IResourceVisitor#visit(IResource)
+ * @since 2.0
+ */
+ public void accept(IResourceVisitor visitor, int depth, int memberFlags) throws CoreException;
+
+ /**
+ * Removes the local history of this resource and its descendents.
+ * <p>
+ * This operation is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ */
+ public void clearHistory(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Makes a copy of this resource at the given path.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * copy(destination, (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This operation changes resources; these changes will be reported
+ * in a subsequent resource change event that will include
+ * an indication that the resource copy has been added to its new parent.
+ * </p>
+ * <p>
+ * This operation is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param destination the destination path
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be copied. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> The source or destination is the workspace root.</li>
+ * <li> The source is a project but the destination is not.</li>
+ * <li> The destination is a project but the source is not.</li>
+ * <li> The resource corresponding to the parent destination path does not exist.</li>
+ * <li> The resource corresponding to the parent destination path is a closed project.</li>
+ * <li> A resource at destination path does exist.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file
+ * system and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> The source resource is a file and the destination path specifies a project.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ */
+ public void copy(IPath destination, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Makes a copy of this resource at the given path. The resource's
+ * descendents are copied as well. The path of this resource must not be a
+ * prefix of the destination path. The workspace root may not be the source or
+ * destination location of a copy operation, and a project can only be copied to
+ * another project. After successful completion, corresponding new resources
+ * will exist at the given path; their contents and properties will be copies of
+ * the originals. The original resources are not affected.
+ * <p>
+ * The supplied path may be absolute or relative. Absolute paths fully specify
+ * the new location for the resource, including its project. Relative paths are
+ * considered to be relative to the container of the resource being copied. A
+ * trailing separator is ignored.
+ * </p>
+ * <p>
+ * Calling this method with a one segment absolute destination path is
+ * equivalent to calling:
+ * <pre>
+ * copy(workspace.newProjectDescription(folder.getName()),updateFlags,monitor);
+ * </pre>
+ * </p>
+ * <p> When a resource is copied, its persistent properties are copied with it.
+ * Session properties and markers are not copied.
+ * </p>
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with cases
+ * where the workspace is not completely in sync with the local file system. If
+ * <code>FORCE</code> is not specified, the method will only attempt to copy
+ * resources that are in sync with the corresponding files and directories in
+ * the local file system; it will fail if it encounters a resource that is out
+ * of sync with the file system. However, if <code>FORCE</code> is specified,
+ * the method copies all corresponding files and directories from the local file
+ * system, including ones that have been recently updated or created. Note that
+ * in both settings of the <code>FORCE</code> flag, the operation fails if the
+ * newly created resources in the workspace would be out of sync with the local
+ * file system; this ensures files in the file system cannot be accidentally
+ * overwritten.
+ * </p>
+ * <p>
+ * The <code>SHALLOW</code> update flag controls how this method deals with linked
+ * resources. If <code>SHALLOW</code> is not specified, then the underlying
+ * contents of the linked resource will always be copied in the file system. In
+ * this case, the destination of the copy will never be a linked resource or
+ * contain any linked resources. If <code>SHALLOW</code> is specified when a
+ * linked resource is copied into another project, a new linked resource is
+ * created in the destination project that points to the same file system
+ * location. When a project containing linked resources is copied, the new
+ * project will contain the same linked resources pointing to the same file
+ * system locations. For both of these shallow cases, no files on disk under
+ * the linked resource are actually copied. With the <code>SHALLOW</code> flag,
+ * copying of linked resources into anything other than a project is not
+ * permitted. The <code>SHALLOW</code> update flag is ignored when copying non-
+ * linked resources.
+ * </p>
+ * <p>
+ * The {@link #DERIVED} update flag indicates that the new resource
+ * should immediately be set as a derived resource. Specifying this flag
+ * is equivalent to atomically calling {@link #setDerived(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * The {@link #TEAM_PRIVATE} update flag indicates that the new resource
+ * should immediately be set as a team private resource. Specifying this flag
+ * is equivalent to atomically calling {@link #setTeamPrivateMember(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * The {@link #HIDDEN} update flag indicates that the new resource
+ * should immediately be set as a hidden resource. Specifying this flag
+ * is equivalent to atomically calling {@link #setHidden(boolean)}
+ * with a value of <code>true</code> immediately after creating the resource.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This operation changes resources; these changes will be reported in a
+ * subsequent resource change event that will include an indication that the
+ * resource copy has been added to its new parent.
+ * </p>
+ * <p>
+ * An attempt will be made to copy the local history for this resource and its children,
+ * to the destination. Since local history existence is a safety-net mechanism, failure
+ * of this action will not result in automatic failure of the copy operation.
+ * </p>
+ * <p>
+ * This operation is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param destination the destination path
+ * @param updateFlags bit-wise or of update flag constants
+ * ({@link #FORCE}, {@link #SHALLOW}, {@link #DERIVED}, {@link #TEAM_PRIVATE}, {@link #HIDDEN})
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be copied. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> The source or destination is the workspace root.</li>
+ * <li> The source is a project but the destination is not.</li>
+ * <li> The destination is a project but the source is not.</li>
+ * <li> The resource corresponding to the parent destination path does not exist.</li>
+ * <li> The resource corresponding to the parent destination path is a closed project.</li>
+ * <li> The source is a linked resource, but the destination is not a project,
+ * and <code>SHALLOW</code> is specified.</li>
+ * <li> A resource at destination path does exist.</li>
+ * <li> This resource or one of its descendants is out of sync with the local file
+ * system and <code>FORCE</code> is not specified.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendants.</li>
+ * <li> The source resource is a file and the destination path specifies a project.</li>
+ * <li> The source is a linked resource, and the destination path does not
+ * specify a project.</li>
+ * <li> The location of the source resource on disk is the same or a prefix of
+ * the location of the destination resource on disk.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancellation can occur even if no progress monitor is provided.
+ * @see #FORCE
+ * @see #SHALLOW
+ * @see #DERIVED
+ * @see #TEAM_PRIVATE
+ * @see IResourceRuleFactory#copyRule(IResource, IResource)
+ * @since 2.0
+ */
+ public void copy(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Makes a copy of this project using the given project description.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * copy(description, (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This operation changes resources; these changes will be reported
+ * in a subsequent resource change event that will include
+ * an indication that the resource copy has been added to its new parent.
+ * </p>
+ * <p>
+ * This operation is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the destination project description
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be copied. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> This resource is not a project.</li>
+ * <li> The project described by the given description already exists.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file
+ * system and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ */
+ public void copy(IProjectDescription description, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Makes a copy of this project using the given project description.
+ * The project's descendents are copied as well. The description specifies the
+ * name, location and attributes of the new project. After successful
+ * completion, corresponding new resources will exist at the given path; their
+ * contents and properties will be copies of the originals. The original
+ * resources are not affected.
+ * <p>
+ * When a resource is copied, its persistent properties are copied with it.
+ * Session properties and markers are not copied.
+ * </p>
+ * <p> The <code>FORCE</code> update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local file
+ * system. If <code>FORCE</code> is not specified, the method will only attempt
+ * to copy resources that are in sync with the corresponding files and
+ * directories in the local file system; it will fail if it encounters a
+ * resource that is out of sync with the file system. However, if
+ * <code>FORCE</code> is specified, the method copies all corresponding files
+ * and directories from the local file system, including ones that have been
+ * recently updated or created. Note that in both settings of the
+ * <code>FORCE</code> flag, the operation fails if the newly created resources
+ * in the workspace would be out of sync with the local file system; this
+ * ensures files in the file system cannot be accidentally overwritten.
+ * </p>
+ * <p>
+ * The <code>SHALLOW</code> update flag controls how this method deals with
+ * linked resources. If <code>SHALLOW</code> is not specified, then the
+ * underlying contents of any linked resources in the project will always be
+ * copied in the file system. In this case, the destination of the copy will
+ * never contain any linked resources. If <code>SHALLOW</code> is specified
+ * when a project containing linked resources is copied, new linked resources
+ * are created in the destination project that point to the same file system
+ * locations. In this case, no files on disk under linked resources are
+ * actually copied. The <code>SHALLOW</code> update flag is ignored when copying
+ * non- linked resources.
+ * </p>
+ * <p>
+ * Update flags other than <code>FORCE</code> or <code>SHALLOW</code> are ignored.
+ * </p>
+ * <p>
+ * An attempt will be made to copy the local history for this resource and its children,
+ * to the destination. Since local history existence is a safety-net mechanism, failure
+ * of this action will not result in automatic failure of the copy operation.
+ * </p>
+ * <p> This operation changes resources; these changes will be reported in a
+ * subsequent resource change event that will include an indication that the
+ * resource copy has been added to its new parent.
+ * </p>
+ * <p>
+ * This operation is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the destination project description
+ * @param updateFlags bit-wise or of update flag constants
+ * (<code>FORCE</code> and <code>SHALLOW</code>)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be copied. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> This resource is not a project.</li>
+ * <li> The project described by the given description already exists.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file
+ * system and <code>FORCE</code> is not specified.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #FORCE
+ * @see #SHALLOW
+ * @see IResourceRuleFactory#copyRule(IResource, IResource)
+ * @since 2.0
+ */
+ public void copy(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates and returns the marker with the specified type on this resource.
+ * Marker type ids are the id of an extension installed in the
+ * <code>org.eclipse.core.resources.markers</code> extension
+ * point. The specified type string must not be <code>null</code>.
+ *
+ * @param type the type of the marker to create
+ * @return the handle of the new marker
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public IMarker createMarker(String type) throws CoreException;
+
+ /**
+ * Creates a resource proxy representing the current state of this resource.
+ * <p>
+ * Note that once a proxy has been created, it does not stay in sync
+ * with the corresponding resource. Changes to the resource after
+ * the proxy is created will not be reflected in the state of the proxy.
+ * </p>
+ *
+ * @return A proxy representing this resource
+ * @since 3.2
+ */
+ public IResourceProxy createProxy();
+
+ /**
+ * Deletes this resource from the workspace.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * delete(force ? FORCE : IResource.NONE, monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource could not be deleted for some reason.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IResource#delete(int,IProgressMonitor)
+ */
+ public void delete(boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes this resource from the workspace.
+ * Deletion applies recursively to all members of this resource in a "best-
+ * effort" fashion. That is, all resources which can be deleted are deleted.
+ * Resources which could not be deleted are noted in a thrown exception. The
+ * method does not fail if resources do not exist; it fails only if resources
+ * could not be deleted.
+ * <p>
+ * Deleting a non-linked resource also deletes its contents from the local file
+ * system. In the case of a file or folder resource, the corresponding file or
+ * directory in the local file system is deleted. Deleting an open project
+ * recursively deletes its members; deleting a closed project just gets rid of
+ * the project itself (closed projects have no members); files in the project's
+ * local content area are retained; referenced projects are unaffected.
+ * </p>
+ * <p>
+ * Deleting a linked resource does not delete its contents from the file system,
+ * it just removes that resource and its children from the workspace. Deleting
+ * children of linked resources does remove the contents from the file system.
+ * </p>
+ * <p>
+ * Deleting a resource also deletes its session and persistent properties and
+ * markers.
+ * </p>
+ * <p>
+ * Deleting a non-project resource which has sync information converts the
+ * resource to a phantom and retains the sync information for future use.
+ * </p>
+ * <p>
+ * Deleting the workspace root resource recursively deletes all projects,
+ * and removes all markers, properties, sync info and other data related to the
+ * workspace root; the root resource itself is not deleted, however.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ * <p>
+ * The {@link #FORCE} update flag controls how this method deals with
+ * cases where the workspace is not completely in sync with the local
+ * file system. If {@link #FORCE} is not specified, the method will only
+ * attempt to delete files and directories in the local file system that
+ * correspond to, and are in sync with, resources in the workspace; it will fail
+ * if it encounters a file or directory in the file system that is out of sync
+ * with the workspace. This option ensures there is no unintended data loss;
+ * it is the recommended setting. However, if {@link #FORCE} is specified,
+ * the method will ruthlessly attempt to delete corresponding files and
+ * directories in the local file system, including ones that have been recently
+ * updated or created.
+ * </p>
+ * <p>
+ * The {@link #KEEP_HISTORY} update flag controls whether or not files that
+ * are about to be deleted from the local file system have their current
+ * contents saved in the workspace's local history. The local history mechanism
+ * serves as a safety net to help the user recover from mistakes that might
+ * otherwise result in data loss. Specifying {@link #KEEP_HISTORY} is
+ * recommended except in circumstances where past states of the files are of no
+ * conceivable interest to the user. Note that local history is maintained
+ * with each individual project, and gets discarded when a project is deleted
+ * from the workspace. Hence {@link #KEEP_HISTORY} is only really applicable
+ * when deleting files and folders, but not projects.
+ * </p>
+ * <p>
+ * The {@link #ALWAYS_DELETE_PROJECT_CONTENT} update flag controls how
+ * project deletions are handled. If {@link #ALWAYS_DELETE_PROJECT_CONTENT}
+ * is specified, then the files and folders in a project's local content area
+ * are deleted, regardless of whether the project is open or closed;
+ * {@link #FORCE} is assumed regardless of whether it is specified. If
+ * {@link #NEVER_DELETE_PROJECT_CONTENT} is specified, then the files and
+ * folders in a project's local content area are retained, regardless of whether
+ * the project is open or closed; the {@link #FORCE} flag is ignored. If
+ * neither of these flags is specified, files and folders in a project's local
+ * content area from open projects (subject to the {@link #FORCE} flag), but
+ * never from closed projects.
+ * </p>
+ *
+ * @param updateFlags bit-wise or of update flag constants (
+ * {@link #FORCE}, {@link #KEEP_HISTORY},
+ * {@link #ALWAYS_DELETE_PROJECT_CONTENT},
+ * and {@link #NEVER_DELETE_PROJECT_CONTENT})
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource could not be deleted for some reason.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file system
+ * and {@link #FORCE} is not specified.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IFile#delete(boolean, boolean, IProgressMonitor)
+ * @see IFolder#delete(boolean, boolean, IProgressMonitor)
+ * @see #FORCE
+ * @see #KEEP_HISTORY
+ * @see #ALWAYS_DELETE_PROJECT_CONTENT
+ * @see #NEVER_DELETE_PROJECT_CONTENT
+ * @see IResourceRuleFactory#deleteRule(IResource)
+ * @since 2.0
+ */
+ public void delete(int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes all markers on this resource of the given type, and,
+ * optionally, deletes such markers from its children. If <code>includeSubtypes</code>
+ * is <code>false</code>, only markers whose type exactly matches
+ * the given type are deleted.
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event.
+ * </p>
+ *
+ * @param type the type of marker to consider, or <code>null</code> to indicate all types
+ * @param includeSubtypes whether or not to consider sub-types of the given type
+ * @param depth how far to recurse (see <code>IResource.DEPTH_* </code>)
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is a project that is not open.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void deleteMarkers(String type, boolean includeSubtypes, int depth) throws CoreException;
+
+ /**
+ * Compares two objects for equality;
+ * for resources, equality is defined in terms of their handles:
+ * same resource type, equal full paths, and identical workspaces.
+ * Resources are not equal to objects other than resources.
+ *
+ * @param other the other object
+ * @return an indication of whether the objects are equals
+ * @see #getType()
+ * @see #getFullPath()
+ * @see #getWorkspace()
+ */
+ public boolean equals(Object other);
+
+ /**
+ * Returns whether this resource exists in the workspace.
+ * <p>
+ * <code>IResource</code> objects are lightweight handle objects
+ * used to access resources in the workspace. However, having a
+ * handle object does not necessarily mean the workspace really
+ * has such a resource. When the workspace does have a genuine
+ * resource of a matching type, the resource is said to
+ * <em>exist</em>, and this method returns <code>true</code>;
+ * in all other cases, this method returns <code>false</code>.
+ * In particular, it returns <code>false</code> if the workspace
+ * has no resource at that path, or if it has a resource at that
+ * path with a type different from the type of this resource handle.
+ * </p>
+ * <p>
+ * Note that no resources ever exist under a project
+ * that is closed; opening a project may bring some
+ * resources into existence.
+ * </p>
+ * <p>
+ * The name and path of a resource handle may be invalid.
+ * However, validation checks are done automatically as a
+ * resource is created; this means that any resource that exists
+ * can be safely assumed to have a valid name and path.
+ * </p>
+ *
+ * @return <code>true</code> if the resource exists, otherwise
+ * <code>false</code>
+ */
+ public boolean exists();
+
+ /**
+ * Returns the marker with the specified id on this resource,
+ * Returns <code>null</code> if there is no matching marker.
+ *
+ * @param id the id of the marker to find
+ * @return a marker or <code>null</code>
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ */
+ public IMarker findMarker(long id) throws CoreException;
+
+ /**
+ * Returns all markers of the specified type on this resource,
+ * and, optionally, on its children. If <code>includeSubtypes</code>
+ * is <code>false</code>, only markers whose type exactly matches
+ * the given type are returned. Returns an empty array if there
+ * are no matching markers.
+ *
+ * @param type the type of marker to consider, or <code>null</code> to indicate all types
+ * @param includeSubtypes whether or not to consider sub-types of the given type
+ * @param depth how far to recurse (see <code>IResource.DEPTH_* </code>)
+ * @return an array of markers
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ */
+ public IMarker[] findMarkers(String type, boolean includeSubtypes, int depth) throws CoreException;
+
+ /**
+ * Returns the maximum value of the {@link IMarker#SEVERITY} attribute across markers
+ * of the specified type on this resource, and, optionally, on its children.
+ * If <code>includeSubtypes</code>is <code>false</code>, only markers whose type
+ * exactly matches the given type are considered.
+ * Returns <code>-1</code> if there are no matching markers.
+ * Returns {@link IMarker#SEVERITY_ERROR} if any of the markers has a severity
+ * greater than or equal to {@link IMarker#SEVERITY_ERROR}.
+ *
+ * @param type the type of marker to consider (normally {@link IMarker#PROBLEM}
+ * or one of its subtypes), or <code>null</code> to indicate all types
+ *
+ * @param includeSubtypes whether or not to consider sub-types of the given type
+ * @param depth how far to recurse (see <code>IResource.DEPTH_* </code>)
+ * @return {@link IMarker#SEVERITY_INFO}, {@link IMarker#SEVERITY_WARNING}, {@link IMarker#SEVERITY_ERROR}, or -1
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ * @since 3.3
+ */
+ public int findMaxProblemSeverity(String type, boolean includeSubtypes, int depth) throws CoreException;
+
+ /**
+ * Returns the file extension portion of this resource's name,
+ * or <code>null</code> if it does not have one.
+ * <p>
+ * The file extension portion is defined as the string
+ * following the last period (".") character in the name.
+ * If there is no period in the name, the path has no
+ * file extension portion. If the name ends in a period,
+ * the file extension portion is the empty string.
+ * </p>
+ * <p>
+ * This is a resource handle operation; the resource need
+ * not exist.
+ * </p>
+ *
+ * @return a string file extension
+ * @see #getName()
+ */
+ public String getFileExtension();
+
+ /**
+ * Returns the full, absolute path of this resource relative to the
+ * workspace.
+ * <p>
+ * This is a resource handle operation; the resource need
+ * not exist.
+ * If this resource does exist, its path can be safely assumed to be valid.
+ * </p>
+ * <p>
+ * A resource's full path indicates the route from the root of the workspace
+ * to the resource. Within a workspace, there is exactly one such path
+ * for any given resource. The first segment of these paths name a project;
+ * remaining segments, folders and/or files within that project.
+ * The returned path never has a trailing separator. The path of the
+ * workspace root is <code>Path.ROOT</code>.
+ * </p>
+ * <p>
+ * Since absolute paths contain the name of the project, they are
+ * vulnerable when the project is renamed. For most situations,
+ * project-relative paths are recommended over absolute paths.
+ * </p>
+ *
+ * @return the absolute path of this resource
+ * @see #getProjectRelativePath()
+ * @see Path#ROOT
+ */
+ public IPath getFullPath();
+
+ /**
+ * Returns a cached value of the local time stamp on disk for this resource, or
+ * <code>NULL_STAMP</code> if the resource does not exist or is not local or is
+ * not accessible. The return value is represented as the number of milliseconds
+ * since the epoch (00:00:00 GMT, January 1, 1970).
+ * The returned value may not be the same as the actual time stamp
+ * on disk if the file has been modified externally since the last local refresh.
+ * <p>
+ * Note that due to varying file system timing granularities, this value is not guaranteed
+ * to change every time the file is modified. For a more reliable indication of whether
+ * the file has changed, use <code>getModificationStamp</code>.
+ *
+ * @return a local file system time stamp, or <code>NULL_STAMP</code>.
+ * @since 3.0
+ */
+ public long getLocalTimeStamp();
+
+ /**
+ * Returns the absolute path in the local file system to this resource,
+ * or <code>null</code> if no path can be determined.
+ * <p>
+ * If this resource is the workspace root, this method returns
+ * the absolute local file system path of the platform working area.
+ * </p><p>
+ * If this resource is a project that exists in the workspace, this method
+ * returns the path to the project's local content area. This is true regardless
+ * of whether the project is open or closed. This value will be null in the case
+ * where the location is relative to an undefined workspace path variable.
+ * </p><p>
+ * If this resource is a linked resource under a project that is open, this
+ * method returns the resolved path to the linked resource's local contents.
+ * This value will be null in the case where the location is relative to an
+ * undefined workspace path variable.
+ * </p><p>
+ * If this resource is a file or folder under a project that exists, or a
+ * linked resource under a closed project, this method returns a (non-
+ * <code>null</code>) path computed from the location of the project's local
+ * content area and the project- relative path of the file or folder. This is
+ * true regardless of whether the file or folders exists, or whether the project
+ * is open or closed. In the case of linked resources, the location of a linked resource
+ * within a closed project is too computed from the location of the
+ * project's local content area and the project-relative path of the resource. If the
+ * linked resource resides in an open project then its location is computed
+ * according to the link.
+ * </p><p>
+ * If this resource is a project that does not exist in the workspace,
+ * or a file or folder below such a project, this method returns
+ * <code>null</code>. This method also returns <code>null</code> if called
+ * on a resource that is not stored in the local file system. For such resources
+ * {@link #getLocationURI()} should be used instead.
+ * </p>
+ *
+ * @return the absolute path of this resource in the local file system,
+ * or <code>null</code> if no path can be determined
+ * @see #getRawLocation()
+ * @see #getLocationURI()
+ * @see IProjectDescription#setLocation(IPath)
+ * @see Platform#getLocation()
+ */
+ public IPath getLocation();
+
+ /**
+ * Returns the absolute URI of this resource,
+ * or <code>null</code> if no URI can be determined.
+ * <p>
+ * If this resource is the workspace root, this method returns
+ * the absolute location of the platform working area.
+ * </p><p>
+ * If this resource is a project that exists in the workspace, this method
+ * returns the URI to the project's local content area. This is true regardless
+ * of whether the project is open or closed. This value will be null in the case
+ * where the location is relative to an undefined workspace path variable.
+ * </p><p>
+ * If this resource is a linked resource under a project that is open, this
+ * method returns the resolved URI to the linked resource's local contents.
+ * This value will be null in the case where the location is relative to an
+ * undefined workspace path variable.
+ * </p><p>
+ * If this resource is a file or folder under a project that exists, or a
+ * linked resource under a closed project, this method returns a (non-
+ * <code>null</code>) URI computed from the location of the project's local
+ * content area and the project- relative path of the file or folder. This is
+ * true regardless of whether the file or folders exists, or whether the project
+ * is open or closed. In the case of linked resources, the location of a linked resource
+ * within a closed project is computed from the location of the
+ * project's local content area and the project-relative path of the resource. If the
+ * linked resource resides in an open project then its location is computed
+ * according to the link.
+ * </p><p>
+ * If this resource is a project that does not exist in the workspace,
+ * or a file or folder below such a project, this method returns
+ * <code>null</code>.
+ * </p>
+ *
+ * @return the absolute URI of this resource,
+ * or <code>null</code> if no URI can be determined
+ * @see #getRawLocation()
+ * @see IProjectDescription#setLocation(IPath)
+ * @see Platform#getLocation()
+ * @see java.net.URI
+ * @since 3.2
+ */
+ public URI getLocationURI();
+
+ /**
+ * Returns a marker handle with the given id on this resource.
+ * This resource is not checked to see if it has such a marker.
+ * The returned marker need not exist.
+ * This resource need not exist.
+ *
+ * @param id the id of the marker
+ * @return the specified marker handle
+ * @see IMarker#getId()
+ */
+ public IMarker getMarker(long id);
+
+ /**
+ * Returns a non-negative modification stamp, or <code>NULL_STAMP</code> if
+ * the resource does not exist or is not local or is not accessible.
+ * <p>
+ * A resource's modification stamp gets updated each time a resource is modified.
+ * If a resource's modification stamp is the same, the resource has not changed.
+ * Conversely, if a resource's modification stamp is different, some aspect of it
+ * (other than properties) has been modified at least once (possibly several times).
+ * Resource modification stamps are preserved across project close/re-open,
+ * and across workspace shutdown/restart.
+ * The magnitude or sign of the numerical difference between two modification stamps
+ * is not significant.
+ * </p>
+ * <p>
+ * The following things affect a resource's modification stamp:
+ * <ul>
+ * <li>creating a non-project resource (changes from <code>NULL_STAMP</code>)</li>
+ * <li>changing the contents of a file</li>
+ * <li><code>touch</code>ing a resource</li>
+ * <li>setting the attributes of a project presented in a project description</li>
+ * <li>deleting a resource (changes to <code>NULL_STAMP</code>)</li>
+ * <li>moving a resource (source changes to <code>NULL_STAMP</code>,
+ destination changes from <code>NULL_STAMP</code>)</li>
+ * <li>copying a resource (destination changes from <code>NULL_STAMP</code>)</li>
+ * <li>making a resource local</li>
+ * <li>closing a project (changes to <code>NULL_STAMP</code>)</li>
+ * <li>opening a project (changes from <code>NULL_STAMP</code>)</li>
+ * <li>adding or removing a project nature (changes from <code>NULL_STAMP</code>)</li>
+ * </ul>
+ * The following things do not affect a resource's modification stamp:
+ * <ul>
+ * <li>"reading" a resource</li>
+ * <li>adding or removing a member of a project or folder</li>
+ * <li>setting a session property</li>
+ * <li>setting a persistent property</li>
+ * <li>saving the workspace</li>
+ * <li>shutting down and re-opening a workspace</li>
+ * </ul>
+ * </p>
+ *
+ * @return the modification stamp, or <code>NULL_STAMP</code> if this resource either does
+ * not exist or exists as a closed project
+ * @see IResource#NULL_STAMP
+ * @see #revertModificationStamp(long)
+ */
+ public long getModificationStamp();
+
+ /**
+ * Returns the name of this resource.
+ * The name of a resource is synonymous with the last segment
+ * of its full (or project-relative) path for all resources other than the
+ * workspace root. The workspace root's name is the empty string.
+ * <p>
+ * This is a resource handle operation; the resource need
+ * not exist.
+ * </p>
+ * <p>
+ * If this resource exists, its name can be safely assumed to be valid.
+ * </p>
+ *
+ * @return the name of the resource
+ * @see #getFullPath()
+ * @see #getProjectRelativePath()
+ */
+ public String getName();
+
+ /**
+ * Returns the resource which is the parent of this resource,
+ * or <code>null</code> if it has no parent (that is, this
+ * resource is the workspace root).
+ * <p>
+ * The full path of the parent resource is the same as this
+ * resource's full path with the last segment removed.
+ * </p>
+ * <p>
+ * This is a resource handle operation; neither the resource
+ * nor the resulting resource need exist.
+ * </p>
+ *
+ * @return the parent resource of this resource,
+ * or <code>null</code> if it has no parent
+ */
+ public IContainer getParent();
+
+ /**
+ * Returns a copy of the map of this resource's persistent properties.
+ * Returns an empty map if this resource has no persistent properties.
+ *
+ * @return the map containing the persistent properties where the key is
+ * the {@link QualifiedName} of the property and the value is the {@link String}
+ * value of the property.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see #setPersistentProperty(QualifiedName, String)
+ * @since 3.4
+ */
+ public Map getPersistentProperties() throws CoreException;
+
+ /**
+ * Returns the value of the persistent property of this resource identified
+ * by the given key, or <code>null</code> if this resource has no such property.
+ *
+ * @param key the qualified name of the property
+ * @return the string value of the property,
+ * or <code>null</code> if this resource has no such property
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see #setPersistentProperty(QualifiedName, String)
+ */
+ public String getPersistentProperty(QualifiedName key) throws CoreException;
+
+ /**
+ * Returns the project which contains this resource.
+ * Returns itself for projects and <code>null</code>
+ * for the workspace root.
+ * <p>
+ * A resource's project is the one named by the first segment
+ * of its full path.
+ * </p>
+ * <p>
+ * This is a resource handle operation; neither the resource
+ * nor the resulting project need exist.
+ * </p>
+ *
+ * @return the project handle
+ */
+ public IProject getProject();
+
+ /**
+ * Returns a relative path of this resource with respect to its project.
+ * Returns the empty path for projects and the workspace root.
+ * <p>
+ * This is a resource handle operation; the resource need not exist.
+ * If this resource does exist, its path can be safely assumed to be valid.
+ * </p>
+ * <p>
+ * A resource's project-relative path indicates the route from the project
+ * to the resource. Within a project, there is exactly one such path
+ * for any given resource. The returned path never has a trailing slash.
+ * </p>
+ * <p>
+ * Project-relative paths are recommended over absolute paths, since
+ * the former are not affected if the project is renamed.
+ * </p>
+ *
+ * @return the relative path of this resource with respect to its project
+ * @see #getFullPath()
+ * @see #getProject()
+ * @see Path#EMPTY
+ */
+ public IPath getProjectRelativePath();
+
+ /**
+ * Returns the file system location of this resource, or <code>null</code> if no
+ * path can be determined. The returned path will either be an absolute file
+ * system path, or a relative path whose first segment is the name of a
+ * workspace path variable.
+ * <p>
+ * If this resource is an existing project, the returned path will be equal to
+ * the location path in the project description. If this resource is a linked
+ * resource in an open project, the returned path will be equal to the location
+ * path supplied when the linked resource was created. In all other cases, this
+ * method returns the same value as {@link #getLocation()}.
+ * </p>
+ *
+ * @return the raw path of this resource in the local file system, or
+ * <code>null</code> if no path can be determined
+ * @see #getLocation()
+ * @see IFile#createLink(IPath, int, IProgressMonitor)
+ * @see IFolder#createLink(IPath, int, IProgressMonitor)
+ * @see IPathVariableManager
+ * @see IProjectDescription#getLocation()
+ * @since 2.1
+ */
+ public IPath getRawLocation();
+
+ /**
+ * Returns the file system location of this resource, or <code>null</code> if no
+ * path can be determined. The returned path will either be an absolute URI,
+ * or a relative URI whose first path segment is the name of a workspace path variable.
+ * <p>
+ * If this resource is an existing project, the returned path will be equal to
+ * the location path in the project description. If this resource is a linked
+ * resource in an open project, the returned path will be equal to the location
+ * path supplied when the linked resource was created. In all other cases, this
+ * method returns the same value as {@link #getLocationURI()}.
+ * </p>
+ *
+ * @return the raw path of this resource in the file system, or
+ * <code>null</code> if no path can be determined
+ * @see #getLocationURI()
+ * @see IFile#createLink(URI, int, IProgressMonitor)
+ * @see IFolder#createLink(URI, int, IProgressMonitor)
+ * @see IPathVariableManager
+ * @see IProjectDescription#getLocationURI()
+ * @since 3.2
+ */
+ public URI getRawLocationURI();
+
+ /**
+ * Gets this resource's extended attributes from the file system,
+ * or <code>null</code> if the attributes could not be obtained.
+ * <p>
+ * Reasons for a <code>null</code> return value include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * </p><p>
+ * Attributes that are not supported by the underlying file system
+ * will have a value of <code>false</code>.
+ * </p><p>
+ * Sample usage: <br>
+ * <br>
+ * <code>
+ * IResource resource; <br>
+ * ... <br>
+ * ResourceAttributes attributes = resource.getResourceAttributes(); <br>
+ * if (attributes != null) {
+ * attributes.setExecutable(true); <br>
+ * resource.setResourceAttributes(attributes); <br>
+ * }
+ * </code>
+ * </p>
+ *
+ * @return the extended attributes from the file system, or
+ * <code>null</code> if they could not be obtained
+ * @see #setResourceAttributes(ResourceAttributes)
+ * @see ResourceAttributes
+ * @since 3.1
+ */
+ public ResourceAttributes getResourceAttributes();
+
+ /**
+ * Returns a copy of the map of this resource's session properties.
+ * Returns an empty map if this resource has no session properties.
+ *
+ * @return the map containing the session properties where the key is
+ * the {@link QualifiedName} of the property and the value is the property
+ * value (an {@link Object}.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see #setSessionProperty(QualifiedName, Object)
+ * @since 3.4
+ */
+ public Map getSessionProperties() throws CoreException;
+
+ /**
+ * Returns the value of the session property of this resource identified
+ * by the given key, or <code>null</code> if this resource has no such property.
+ *
+ * @param key the qualified name of the property
+ * @return the value of the session property,
+ * or <code>null</code> if this resource has no such property
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see #setSessionProperty(QualifiedName, Object)
+ */
+ public Object getSessionProperty(QualifiedName key) throws CoreException;
+
+ /**
+ * Returns the type of this resource.
+ * The returned value will be one of <code>FILE</code>,
+ * <code>FOLDER</code>, <code>PROJECT</code>, <code>ROOT</code>.
+ * <p>
+ * <ul>
+ * <li> All resources of type <code>FILE</code> implement <code>IFile</code>.</li>
+ * <li> All resources of type <code>FOLDER</code> implement <code>IFolder</code>.</li>
+ * <li> All resources of type <code>PROJECT</code> implement <code>IProject</code>.</li>
+ * <li> All resources of type <code>ROOT</code> implement <code>IWorkspaceRoot</code>.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This is a resource handle operation; the resource need
+ * not exist in the workspace.
+ * </p>
+ *
+ * @return the type of this resource
+ * @see #FILE
+ * @see #FOLDER
+ * @see #PROJECT
+ * @see #ROOT
+ */
+ public int getType();
+
+ /**
+ * Returns the workspace which manages this resource.
+ * <p>
+ * This is a resource handle operation; the resource need
+ * not exist in the workspace.
+ * </p>
+ *
+ * @return the workspace
+ */
+ public IWorkspace getWorkspace();
+
+ /**
+ * Returns whether this resource is accessible. For files and folders,
+ * this is equivalent to existing; for projects,
+ * this is equivalent to existing and being open. The workspace root
+ * is always accessible.
+ *
+ * @return <code>true</code> if this resource is accessible, and
+ * <code>false</code> otherwise
+ * @see #exists()
+ * @see IProject#isOpen()
+ */
+ public boolean isAccessible();
+
+ /**
+ * Returns whether this resource subtree is marked as derived. Returns
+ * <code>false</code> if this resource does not exist.
+ *
+ * <p>
+ * This is a convenience method,
+ * fully equivalent to <code>isDerived(IResource.NONE)</code>.
+ * </p>
+ *
+ * @return <code>true</code> if this resource is marked as derived, and
+ * <code>false</code> otherwise
+ * @see #setDerived(boolean)
+ * @since 2.0
+ */
+ public boolean isDerived();
+
+ /**
+ * Returns whether this resource subtree is marked as derived. Returns
+ * <code>false</code> if this resource does not exist.
+ *
+ * <p>
+ * The {@link #CHECK_ANCESTORS} option flag indicates whether this method
+ * should consider ancestor resources in its calculation. If the
+ * {@link #CHECK_ANCESTORS} flag is present, this method will return
+ * <code>true</code>, if this resource, or any parent resource, is marked
+ * as derived. If the {@link #CHECK_ANCESTORS} option flag is not specified,
+ * this method returns false for children of derived resources.
+ * </p>
+ *
+ * @param options bit-wise or of option flag constants
+ * (only {@link #CHECK_ANCESTORS} is applicable)
+ * @return <code>true</code> if this resource subtree is derived, and
+ * <code>false</code> otherwise
+ * @see IResource#setDerived(boolean)
+ * @since 3.4
+ */
+ public boolean isDerived(int options);
+
+ /**
+ * Returns whether this resource is hidden in the resource tree. Returns
+ * <code>false</code> if this resource does not exist.
+ * <p>
+ * This operation is not related to the file system hidden attribute accessible using
+ * {@link ResourceAttributes#isHidden()}.
+ * </p>
+ *
+ * @return <code>true</code> if this resource is hidden , and
+ * <code>false</code> otherwise
+ * @see #setHidden(boolean)
+ * @since 3.4
+ */
+ public boolean isHidden();
+
+ /**
+ * Returns whether this resource is hidden in the resource tree. Returns
+ * <code>false</code> if this resource does not exist.
+ * <p>
+ * This operation is not related to the file system hidden attribute
+ * accessible using {@link ResourceAttributes#isHidden()}.
+ * </p>
+ * <p>
+ * The {@link #CHECK_ANCESTORS} option flag indicates whether this method
+ * should consider ancestor resources in its calculation. If the
+ * {@link #CHECK_ANCESTORS} flag is present, this method will return
+ * <code>true</code> if this resource, or any parent resource, is a hidden
+ * resource. If the {@link #CHECK_ANCESTORS} option flag is not specified,
+ * this method returns false for children of hidden resources.
+ * </p>
+ *
+ * @param options
+ * bit-wise or of option flag constants (only
+ * {@link #CHECK_ANCESTORS} is applicable)
+ * @return <code>true</code> if this resource is hidden , and
+ * <code>false</code> otherwise
+ * @see #setHidden(boolean)
+ * @since 3.5
+ */
+ public boolean isHidden(int options);
+
+ /**
+ * Returns whether this resource has been linked to
+ * a location other than the default location calculated by the platform.
+ * <p>
+ * This is a convenience method, fully equivalent to
+ * <code>isLinked(IResource.NONE)</code>.
+ * </p>
+ *
+ * @return <code>true</code> if this resource is linked, and
+ * <code>false</code> otherwise
+ * @see IFile#createLink(IPath, int, IProgressMonitor)
+ * @see IFolder#createLink(IPath, int, IProgressMonitor)
+ * @since 2.1
+ */
+ public boolean isLinked();
+
+ /**
+ * Returns <code>true</code> if this resource has been linked to
+ * a location other than the default location calculated by the platform. This
+ * location can be outside the project's content area or another location
+ * within the project. Returns <code>false</code> in all other cases, including
+ * the case where this resource does not exist. The workspace root and
+ * projects are never linked.
+ * <p>
+ * This method returns true only for a resource that has been linked using
+ * the <code>createLink</code> method.
+ * </p>
+ * <p>
+ * The {@link #CHECK_ANCESTORS} option flag indicates whether this method
+ * should consider ancestor resources in its calculation. If the
+ * {@link #CHECK_ANCESTORS} flag is present, this method will return
+ * <code>true</code> if this resource, or any parent resource, is a linked
+ * resource. If the {@link #CHECK_ANCESTORS} option flag is not specified,
+ * this method returns false for children of linked resources.
+ * </p>
+ *
+ * @param options bit-wise or of option flag constants
+ * (only {@link #CHECK_ANCESTORS} is applicable)
+ * @return <code>true</code> if this resource is linked, and
+ * <code>false</code> otherwise
+ * @see IFile#createLink(IPath, int, IProgressMonitor)
+ * @see IFolder#createLink(IPath, int, IProgressMonitor)
+ * @since 3.2
+ */
+ public boolean isLinked(int options);
+
+ /**
+ * Returns whether this resource and its members (to the
+ * specified depth) are expected to have their contents (and properties)
+ * available locally. Returns <code>false</code> in all other cases,
+ * including the case where this resource does not exist. The workspace
+ * root and projects are always local.
+ * <p>
+ * When a resource is not local, its content and properties are
+ * unavailable for both reading and writing.
+ * </p>
+ *
+ * @param depth valid values are <code>DEPTH_ZERO</code>,
+ * <code>DEPTH_ONE</code>, or <code>DEPTH_INFINITE</code>
+ * @return <code>true</code> if this resource is local, and
+ * <code>false</code> otherwise
+ *
+ * @see #setLocal(boolean, int, IProgressMonitor)
+ * @deprecated This API is no longer in use. Note that this API is unrelated
+ * to whether the resource is in the local file system versus some other file system.
+ */
+ public boolean isLocal(int depth);
+
+ /**
+ * Returns whether this resource is a phantom resource.
+ * <p>
+ * The workspace uses phantom resources to remember outgoing deletions and
+ * incoming additions relative to an external synchronization partner. Phantoms
+ * appear and disappear automatically as a byproduct of synchronization.
+ * Since the workspace root cannot be synchronized in this way, it is never a phantom.
+ * Projects are also never phantoms.
+ * </p>
+ * <p>
+ * The key point is that phantom resources do not exist (in the technical
+ * sense of <code>exists</code>, which returns <code>false</code>
+ * for phantoms) are therefore invisible except through a handful of
+ * phantom-enabled API methods (notably <code>IContainer.members(boolean)</code>).
+ * </p>
+ *
+ * @return <code>true</code> if this resource is a phantom resource, and
+ * <code>false</code> otherwise
+ * @see #exists()
+ * @see IContainer#members(boolean)
+ * @see IContainer#findMember(String, boolean)
+ * @see IContainer#findMember(IPath, boolean)
+ * @see ISynchronizer
+ */
+ public boolean isPhantom();
+
+ /**
+ * Returns whether this resource is marked as read-only in the file system.
+ *
+ * @return <code>true</code> if this resource is read-only,
+ * <code>false</code> otherwise
+ * @deprecated use <tt>IResource#getResourceAttributes()</tt>
+ */
+ public boolean isReadOnly();
+
+ /**
+ * Returns whether this resource and its descendents to the given depth
+ * are considered to be in sync with the local file system.
+ * <p>
+ * A resource is considered to be in sync if all of the following
+ * conditions are true:
+ * <ul>
+ * <li>The resource exists in both the workspace and the file system.</li>
+ * <li>The timestamp in the file system has not changed since the
+ * last synchronization.</li>
+ * <li>The resource in the workspace is of the same type as the corresponding
+ * file in the file system (they are either both files or both folders).</li>
+ * </ul>
+ * A resource is also considered to be in sync if it is missing from both
+ * the workspace and the file system. In all other cases the resource is
+ * considered to be out of sync.
+ * </p>
+ * <p>
+ * This operation interrogates files and folders in the local file system;
+ * depending on the speed of the local file system and the requested depth,
+ * this operation may be time-consuming.
+ * </p>
+ *
+ * @param depth the depth (one of <code>IResource.DEPTH_ZERO</code>,
+ * <code>DEPTH_ONE</code>, or <code>DEPTH_INFINITE</code>)
+ * @return <code>true</code> if this resource and its descendents to the
+ * specified depth are synchronized, and <code>false</code> in all other
+ * cases
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ * @see #refreshLocal(int, IProgressMonitor)
+ * @since 2.0
+ */
+ public boolean isSynchronized(int depth);
+
+ /**
+ * Returns whether this resource is a team private member of its parent container.
+ * Returns <code>false</code> if this resource does not exist.
+ *
+ * @return <code>true</code> if this resource is a team private member, and
+ * <code>false</code> otherwise
+ * @see #setTeamPrivateMember(boolean)
+ * @since 2.0
+ */
+ public boolean isTeamPrivateMember();
+
+ /**
+ * Returns whether this resource is a team private member of its parent
+ * container. Returns <code>false</code> if this resource does not exist.
+ * <p>
+ * The {@link #CHECK_ANCESTORS} option flag indicates whether this method
+ * should consider ancestor resources in its calculation. If the
+ * {@link #CHECK_ANCESTORS} flag is present, this method will return
+ * <code>true</code> if this resource, or any parent resource, is a team
+ * private member. If the {@link #CHECK_ANCESTORS} option flag is not
+ * specified, this method returns false for children of team private
+ * members.
+ * </p>
+ *
+ * @param options
+ * bit-wise or of option flag constants (only
+ * {@link #CHECK_ANCESTORS} is applicable)
+ * @return <code>true</code> if this resource is a team private member, and
+ * <code>false</code> otherwise
+ * @see #setTeamPrivateMember(boolean)
+ * @since 3.5
+ */
+ public boolean isTeamPrivateMember(int options);
+
+ /**
+ * Moves this resource so that it is located at the given path.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * move(destination, force ? FORCE : IResource.NONE, monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event that will include
+ * an indication that the resource has been removed from its parent
+ * and that a corresponding resource has been added to its new parent.
+ * Additional information provided with resource delta shows that these
+ * additions and removals are related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param destination the destination path
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> The source or destination is the workspace root.</li>
+ * <li> The source is a project but the destination is not.</li>
+ * <li> The destination is a project but the source is not.</li>
+ * <li> The resource corresponding to the parent destination path does not exist.</li>
+ * <li> The resource corresponding to the parent destination path is a closed
+ * project.</li>
+ * <li> A resource at destination path does exist.</li>
+ * <li> A resource of a different type exists at the destination path.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file
+ * system and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The source resource is a file and the destination path specifies a project.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceDelta#getFlags()
+ */
+ public void move(IPath destination, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Moves this resource so that it is located at the given path.
+ * The path of the resource must not be a prefix of the destination path. The
+ * workspace root may not be the source or destination location of a move
+ * operation, and a project can only be moved to another project. After
+ * successful completion, the resource and any direct or indirect members will
+ * no longer exist; but corresponding new resources will now exist at the given
+ * path.
+ * <p>
+ * The supplied path may be absolute or relative. Absolute paths fully specify
+ * the new location for the resource, including its project. Relative paths are
+ * considered to be relative to the container of the resource being moved. A
+ * trailing slash is ignored.
+ * </p>
+ * <p>
+ * Calling this method with a one segment absolute destination path is
+ * equivalent to calling:
+ * <pre>
+ IProjectDescription description = getDescription();
+ description.setName(path.lastSegment());
+ move(description, updateFlags, monitor);
+ * </pre>
+ * </p>
+ * <p> When a resource moves, its session and persistent properties move with
+ * it. Likewise for all other attributes of the resource including markers.
+ * </p>
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with cases
+ * where the workspace is not completely in sync with the local file system. If
+ * <code>FORCE</code> is not specified, the method will only attempt to move
+ * resources that are in sync with the corresponding files and directories in
+ * the local file system; it will fail if it encounters a resource that is out
+ * of sync with the file system. However, if <code>FORCE</code> is specified,
+ * the method moves all corresponding files and directories from the local file
+ * system, including ones that have been recently updated or created. Note that
+ * in both settings of the <code>FORCE</code> flag, the operation fails if the
+ * newly created resources in the workspace would be out of sync with the local
+ * file system; this ensures files in the file system cannot be accidentally
+ * overwritten.
+ * </p>
+ * <p>
+ * The <code>KEEP_HISTORY</code> update flag controls whether or not
+ * file that are about to be deleted from the local file system have their
+ * current contents saved in the workspace's local history. The local history
+ * mechanism serves as a safety net to help the user recover from mistakes that
+ * might otherwise result in data loss. Specifying <code>KEEP_HISTORY</code>
+ * is recommended except in circumstances where past states of the files are of
+ * no conceivable interest to the user. Note that local history is maintained
+ * with each individual project, and gets discarded when a project is deleted
+ * from the workspace. Hence <code>KEEP_HISTORY</code> is only really applicable
+ * when moving files and folders, but not whole projects.
+ * </p>
+ * <p>
+ * If this resource is not a project, an attempt will be made to copy the local history
+ * for this resource and its children, to the destination. Since local history existence
+ * is a safety-net mechanism, failure of this action will not result in automatic failure
+ * of the move operation.
+ * </p>
+ * <p>
+ * The <code>SHALLOW</code> update flag controls how this method deals with linked
+ * resources. If <code>SHALLOW</code> is not specified, then the underlying
+ * contents of the linked resource will always be moved in the file system. In
+ * this case, the destination of the move will never be a linked resource or
+ * contain any linked resources. If <code>SHALLOW</code> is specified when a
+ * linked resource is moved into another project, a new linked resource is
+ * created in the destination project that points to the same file system
+ * location. When a project containing linked resources is moved, the new
+ * project will contain the same linked resources pointing to the same file
+ * system locations. For either of these cases, no files on disk under the
+ * linked resource are actually moved. With the <code>SHALLOW</code> flag,
+ * moving of linked resources into anything other than a project is not
+ * permitted. The <code>SHALLOW</code> update flag is ignored when moving non-
+ * linked resources.
+ * </p>
+ * <p>
+ * Update flags other than <code>FORCE</code>, <code>KEEP_HISTORY</code>and
+ * <code>SHALLOW</code> are ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a subsequent
+ * resource change event that will include an indication that the resource has
+ * been removed from its parent and that a corresponding resource has been added
+ * to its new parent. Additional information provided with resource delta shows
+ * that these additions and removals are related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param destination the destination path
+ * @param updateFlags bit-wise or of update flag constants
+ * (<code>FORCE</code>, <code>KEEP_HISTORY</code> and <code>SHALLOW</code>)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> The source or destination is the workspace root.</li>
+ * <li> The source is a project but the destination is not.</li>
+ * <li> The destination is a project but the source is not.</li>
+ * <li> The resource corresponding to the parent destination path does not exist.</li>
+ * <li> The resource corresponding to the parent destination path is a closed
+ * project.</li>
+ * <li> The source is a linked resource, but the destination is not a project
+ * and <code>SHALLOW</code> is specified.</li>
+ * <li> A resource at destination path does exist.</li>
+ * <li> A resource of a different type exists at the destination path.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file system
+ * and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> The source resource is a file and the destination path specifies a project.</li>
+ * <li> The location of the source resource on disk is the same or a prefix of
+ * the location of the destination resource on disk.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceDelta#getFlags()
+ * @see #FORCE
+ * @see #KEEP_HISTORY
+ * @see #SHALLOW
+ * @see IResourceRuleFactory#moveRule(IResource, IResource)
+ * @since 2.0
+ */
+ public void move(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Renames or relocates this project so that it is the project specified by the given project
+ * description.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * move(description, (keepHistory ? KEEP_HISTORY : IResource.NONE) | (force ? FORCE : IResource.NONE), monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This operation changes resources; these changes will be reported
+ * in a subsequent resource change event that will include
+ * an indication that the resource has been removed from its parent
+ * and that a corresponding resource has been added to its new parent.
+ * Additional information provided with resource delta shows that these
+ * additions and removals are related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the destination project description
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param keepHistory a flag indicating whether or not to keep
+ * local history for files
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> This resource is not a project.</li>
+ * <li> The project at the destination already exists.</li>
+ * <li> This resource or one of its descendents is out of sync with the local file
+ * system and <code>force</code> is <code>false</code>.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceDelta#getFlags()
+ */
+ public void move(IProjectDescription description, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Renames or relocates this project so that it is the project specified by the
+ * given project description. The description specifies the name and location
+ * of the new project. After successful completion, the old project
+ * and any direct or indirect members will no longer exist; but corresponding
+ * new resources will now exist in the new project.
+ * <p>
+ * When a resource moves, its session and persistent properties move with it.
+ * Likewise for all the other attributes of the resource including markers.
+ * </p>
+ * <p>
+ * When this project's location is the default location, then the directories
+ * and files on disk are moved to be in the location specified by the given
+ * description. If the given description specifies the default location for the
+ * project, the directories and files are moved to the default location. If the name
+ * in the given description is the same as this project's name and the location
+ * is different, then the project contents will be moved to the new location.
+ * In all other cases the directories and files on disk are left untouched.
+ * Parts of the supplied description other than the name and location are ignored.
+ * </p>
+ * <p>
+ * The <code>FORCE</code> update flag controls how this method deals with cases
+ * where the workspace is not completely in sync with the local file system. If
+ * <code>FORCE</code> is not specified, the method will only attempt to move
+ * resources that are in sync with the corresponding files and directories in
+ * the local file system; it will fail if it encounters a resource that is out
+ * of sync with the file system. However, if <code>FORCE</code> is specified,
+ * the method moves all corresponding files and directories from the local file
+ * system, including ones that have been recently updated or created. Note that
+ * in both settings of the <code>FORCE</code> flag, the operation fails if the
+ * newly created resources in the workspace would be out of sync with the local
+ * file system; this ensures files in the file system cannot be accidentally
+ * overwritten.
+ * </p>
+ * <p>
+ * The <code>KEEP_HISTORY</code> update flag controls whether or not file that
+ * are about to be deleted from the local file system have their current
+ * contents saved in the workspace's local history. The local history mechanism
+ * serves as a safety net to help the user recover from mistakes that might
+ * otherwise result in data loss. Specifying <code>KEEP_HISTORY</code> is
+ * recommended except in circumstances where past states of the files are of no
+ * conceivable interest to the user. Note that local history is maintained
+ * with each individual project, and gets discarded when a project is deleted
+ * from the workspace. Hence <code>KEEP_HISTORY</code> is only really applicable
+ * when moving files and folders, but not whole projects.
+ * </p>
+ * <p>
+ * Local history information for this project and its children will not be moved to the
+ * destination.
+ * </p>
+ * <p>
+ * The <code>SHALLOW</code> update flag controls how this method deals with linked
+ * resources. If <code>SHALLOW</code> is not specified, then the underlying
+ * contents of any linked resource will always be moved in the file system. In
+ * this case, the destination of the move will not contain any linked resources.
+ * If <code>SHALLOW</code> is specified when a project containing linked
+ * resources is moved, new linked resources are created in the destination
+ * project pointing to the same file system locations. In this case, no files
+ * on disk under any linked resource are actually moved. The
+ * <code>SHALLOW</code> update flag is ignored when moving non- linked
+ * resources.
+ * </p>
+ * <p>
+ * The {@link #REPLACE} update flag controls how this method deals
+ * with a change of location. If the location changes and the {@link #REPLACE}
+ * flag is not specified, then the projects contents on disk are moved to the new
+ * location. If the location changes and the {@link #REPLACE}
+ * flag is specified, then the project is reoriented to correspond to the new
+ * location, but no contents are moved on disk. The contents already on
+ * disk at the new location become the project contents. If the new project
+ * location does not exist, it will be created.
+ * </p>
+ * <p>
+ * Update flags other than those listed above are ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a subsequent
+ * resource change event that will include an indication that the resource has
+ * been removed from its parent and that a corresponding resource has been added
+ * to its new parent. Additional information provided with resource delta shows
+ * that these additions and removals are related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param description the destination project description
+ * @param updateFlags bit-wise or of update flag constants
+ * ({@link #FORCE}, {@link #KEEP_HISTORY}, {@link #SHALLOW}
+ * and {@link #REPLACE}).
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this resource could not be moved. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource or one of its descendents is not local.</li>
+ * <li> This resource is not a project.</li>
+ * <li> The project at the destination already exists.</li>
+ * <li> This resource or one of its descendents is out of sync with the
+ * local file system and <code>FORCE</code> is not specified.</li>
+ * <li> The workspace and the local file system are out of sync
+ * at the destination resource or one of its descendents.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * <li> The destination file system location is occupied. When moving a project
+ * in the file system, the destination directory must either not exist or be empty.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceDelta#getFlags()
+ * @see #FORCE
+ * @see #KEEP_HISTORY
+ * @see #SHALLOW
+ * @see #REPLACE
+ * @see IResourceRuleFactory#moveRule(IResource, IResource)
+ * @since 2.0
+ */
+ public void move(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Refreshes the resource hierarchy from this resource and its
+ * children (to the specified depth) relative to the local file system.
+ * Creations, deletions, and changes detected in the local file system
+ * will be reflected in the workspace's resource tree.
+ * This resource need not exist or be local.
+ * <p>
+ * This method may discover changes to resources; any such
+ * changes will be reported in a subsequent resource change event.
+ * </p>
+ * <p>
+ * If a new file or directory is discovered in the local file
+ * system at or below the location of this resource,
+ * any parent folders required to contain the new
+ * resource in the workspace will also be created automatically as required.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param depth valid values are <code>DEPTH_ZERO</code>,
+ * <code>DEPTH_ONE</code>, or <code>DEPTH_INFINITE</code>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#DEPTH_ZERO
+ * @see IResource#DEPTH_ONE
+ * @see IResource#DEPTH_INFINITE
+ * @see IResourceRuleFactory#refreshRule(IResource)
+ */
+ public void refreshLocal(int depth, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Reverts this resource's modification stamp. This is intended to be used by
+ * a client that is rolling back or undoing a previous change to this resource.
+ * <p>
+ * It is the caller's responsibility to ensure that the value of the reverted
+ * modification stamp matches this resource's modification stamp prior to the
+ * change that has been rolled back. More generally, the caller must ensure
+ * that the specification of modification stamps outlined in
+ * <code>getModificationStamp</code> is honored; the modification stamp
+ * of two distinct resource states should be different if and only if one or more
+ * of the attributes listed in the specification as affecting the modification
+ * stamp have changed.
+ * <p>
+ * Reverting the modification stamp will <b>not</b> be reported in a
+ * subsequent resource change event.
+ * <p>
+ * Note that a resource's modification stamp is unrelated to the local
+ * time stamp for this resource on disk, if any. A resource's local time
+ * stamp is modified using the <code>setLocalTimeStamp</code> method.
+ *
+ * @param value A non-negative modification stamp value
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is not accessible.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see #getModificationStamp()
+ * @since 3.1
+ */
+ public void revertModificationStamp(long value) throws CoreException;
+
+ /**
+ * Sets whether this resource subtree is marked as derived.
+ * <p>
+ * A <b>derived</b> resource is a regular file or folder that is
+ * created in the course of translating, compiling, copying, or otherwise
+ * processing other files. Derived resources are not original data, and can be
+ * recreated from other resources. It is commonplace to exclude derived
+ * resources from version and configuration management because they would
+ * otherwise clutter the team repository with version of these ever-changing
+ * files as each user regenerates them.
+ * </p>
+ * <p>
+ * If a resource or any of its ancestors is marked as derived, a team
+ * provider should assume that the resource is not under version and
+ * configuration management <i>by default</i>. That is, the resource
+ * should only be stored in a team repository if the user explicitly indicates
+ * that this resource is worth saving.
+ * </p>
+ * <p>
+ * Newly-created resources are not marked as derived; rather, the mark must be
+ * set explicitly using <code>setDerived(true)</code>. Derived marks are maintained
+ * in the in-memory resource tree, and are discarded when the resources are deleted.
+ * Derived marks are saved to disk when a project is closed, or when the workspace
+ * is saved.
+ * </p>
+ * <p>
+ * Projects and the workspace root are never considered derived; attempts to
+ * mark them as derived are ignored.
+ * </p>
+ * <p>
+ * This operation does <b>not</b> result in a resource change event, and does not
+ * trigger autobuilds.
+ * </p>
+ *
+ * @param isDerived <code>true</code> if this resource is to be marked
+ * as derived, and <code>false</code> otherwise
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see #isDerived()
+ * @since 2.0
+ */
+ public void setDerived(boolean isDerived) throws CoreException;
+
+ /**
+ * Sets whether this resource and its members are hidden in the resource tree.
+ * <p>
+ * Hidden resources are invisible to most clients. Newly-created resources
+ * are not hidden resources by default.
+ * </p>
+ * <p>
+ * The workspace root is never considered hidden resource;
+ * attempts to mark it as hidden are ignored.
+ * </p>
+ * <p>
+ * This operation does <b>not</b> result in a resource change event, and does not
+ * trigger autobuilds.
+ * </p>
+ * <p>
+ * This operation is not related to {@link ResourceAttributes#setHidden(boolean)}.
+ * Whether a resource is hidden in the resource tree is unrelated to whether the
+ * underlying file is hidden in the file system.
+ * </p>
+ *
+ * @param isHidden <code>true</code> if this resource is to be marked
+ * as hidden, and <code>false</code> otherwise
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see #isHidden()
+ * @since 3.4
+ */
+ public void setHidden(boolean isHidden) throws CoreException;
+
+ /**
+ * Set whether or not this resource and its members (to the
+ * specified depth) are expected to have their contents (and properties)
+ * available locally. The workspace root and projects are always local and
+ * attempting to set either to non-local (i.e., passing <code>false</code>)
+ * has no affect on the resource.
+ * <p>
+ * When a resource is not local, its content and properties are
+ * unavailable for both reading and writing.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param flag whether this resource should be considered local
+ * @param depth valid values are <code>DEPTH_ZERO</code>,
+ * <code>DEPTH_ONE</code>, or <code>DEPTH_INFINITE</code>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #isLocal(int)
+ * @deprecated This API is no longer in use. Note that this API is unrelated
+ * to whether the resource is in the local file system versus some other file system.
+ */
+ public void setLocal(boolean flag, int depth, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the local time stamp on disk for this resource. The time must be represented
+ * as the number of milliseconds since the epoch (00:00:00 GMT, January 1, 1970).
+ * Returns the actual time stamp that was recorded.
+ * Due to varying file system timing granularities, the provided value may be rounded
+ * or otherwise truncated, so the actual recorded time stamp that is returned may
+ * not be the same as the supplied value.
+ *
+ * @param value a time stamp in milliseconds.
+ * @return a local file system time stamp.
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is not accessible.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @since 3.0
+ */
+ public long setLocalTimeStamp(long value) throws CoreException;
+
+ /**
+ * Sets the value of the persistent property of this resource identified
+ * by the given key. If the supplied value is <code>null</code>,
+ * the persistent property is removed from this resource. The change
+ * is made immediately on disk.
+ * <p>
+ * Persistent properties are intended to be used by plug-ins to store
+ * resource-specific information that should be persisted across platform sessions.
+ * The value of a persistent property is a string that must be short -
+ * 2KB or less in length. Unlike session properties, persistent properties are
+ * stored on disk and maintained across workspace shutdown and restart.
+ * </p>
+ * <p>
+ * The qualifier part of the property name must be the unique identifier
+ * of the declaring plug-in (e.g. <code>"com.example.plugin"</code>).
+ * </p>
+ *
+ * @param key the qualified name of the property
+ * @param value the string value of the property,
+ * or <code>null</code> if the property is to be removed
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see #getPersistentProperty(QualifiedName)
+ * @see #isLocal(int)
+ */
+ public void setPersistentProperty(QualifiedName key, String value) throws CoreException;
+
+ /**
+ * Sets or unsets this resource as read-only in the file system.
+ *
+ * @param readOnly <code>true</code> to set it to read-only,
+ * <code>false</code> to unset
+ * @deprecated use <tt>IResource#setResourceAttributes(ResourceAttributes)</tt>
+ */
+ public void setReadOnly(boolean readOnly);
+
+ /**
+ * Sets this resource with the given extended attributes. This sets the
+ * attributes in the file system. Only attributes that are supported by
+ * the underlying file system will be set.
+ * <p>
+ * Sample usage: <br>
+ * <br>
+ * <code>
+ * IResource resource; <br>
+ * ... <br>
+ * if (attributes != null) {
+ * attributes.setExecutable(true); <br>
+ * resource.setResourceAttributes(attributes); <br>
+ * }
+ * </code>
+ * </p>
+ * <p>
+ * Note that a resource cannot be converted into a symbolic link by
+ * setting resource attributes with {@link ResourceAttributes#isSymbolicLink()}
+ * set to true.
+ * </p>
+ *
+ * @param attributes the attributes to set
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * </ul>
+ * @see #getResourceAttributes()
+ * @since 3.1
+ */
+ void setResourceAttributes(ResourceAttributes attributes) throws CoreException;
+
+ /**
+ * Sets the value of the session property of this resource identified
+ * by the given key. If the supplied value is <code>null</code>,
+ * the session property is removed from this resource.
+ * <p>
+ * Sessions properties are intended to be used as a caching mechanism
+ * by ISV plug-ins. They allow key-object associations to be stored with
+ * existing resources in the workspace. These key-value associations are
+ * maintained in memory (at all times), and the information is lost when a
+ * resource is deleted from the workspace, when the parent project
+ * is closed, or when the workspace is closed.
+ * </p>
+ * <p>
+ * The qualifier part of the property name must be the unique identifier
+ * of the declaring plug-in (e.g. <code>"com.example.plugin"</code>).
+ * </p>
+ *
+ * @param key the qualified name of the property
+ * @param value the value of the session property,
+ * or <code>null</code> if the property is to be removed
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> This resource is a project that is not open.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see #getSessionProperty(QualifiedName)
+ */
+ public void setSessionProperty(QualifiedName key, Object value) throws CoreException;
+
+ /**
+ * Sets whether this resource subtree is a team private member of its parent container.
+ * <p>
+ * A <b>team private member</b> resource is a special file or folder created by a team
+ * provider to hold team-provider-specific information. Resources marked as team private
+ * members are invisible to most clients.
+ * </p>
+ * <p>
+ * Newly-created resources are not team private members by default; rather, the
+ * team provider must mark a resource explicitly using
+ * <code>setTeamPrivateMember(true)</code>. Team private member marks are
+ * maintained in the in-memory resource tree, and are discarded when the
+ * resources are deleted. Team private member marks are saved to disk when a
+ * project is closed, or when the workspace is saved.
+ * </p>
+ * <p>
+ * Projects and the workspace root are never considered team private members;
+ * attempts to mark them as team private are ignored.
+ * </p>
+ * <p>
+ * This operation does <b>not</b> result in a resource change event, and does not
+ * trigger autobuilds.
+ * </p>
+ *
+ * @param isTeamPrivate <code>true</code> if this resource is to be marked
+ * as team private, and <code>false</code> otherwise
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @see #isTeamPrivateMember()
+ * @since 2.0
+ */
+ public void setTeamPrivateMember(boolean isTeamPrivate) throws CoreException;
+
+ /**
+ * Marks this resource as having changed even though its content
+ * may not have changed. This method can be used to trigger
+ * the rebuilding of resources/structures derived from this resource.
+ * Touching the workspace root has no effect.
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event. If the resource is a project,
+ * the change event will indicate a description change.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> This resource does not exist.</li>
+ * <li> This resource is not local.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResourceRuleFactory#modifyRule(IResource)
+ * @see IResourceDelta#CONTENT
+ * @see IResourceDelta#DESCRIPTION
+ */
+ public void touch(IProgressMonitor monitor) throws CoreException;
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceChangeEvent.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Resource change events describe changes to resources.
+ * <p>
+ * There are currently five different types of resource change events:
+ * <ul>
+ * <li>
+ * Before-the-fact batch reports of arbitrary creations,
+ * deletions and modifications to one or more resources expressed
+ * as a hierarchical resource delta. Event type is
+ * <code>PRE_BUILD</code>, and <code>getDelta</code> returns
+ * the hierarchical delta. The resource delta is rooted at the
+ * workspace root. The <code>getBuildKind</code> method returns
+ * the kind of build that is about to occur, and the <code>getSource</code>
+ * method returns the scope of the build (either the workspace or a single project).
+ * These events are broadcast to interested parties immediately
+ * before each build operation. If autobuilding is not enabled, these events still
+ * occur at times when autobuild would have occurred. The workspace is open
+ * for change during notification of these events. The delta reported in this event
+ * cycle is identical across all listeners registered for this type of event.
+ * Resource changes attempted during a <code>PRE_BUILD</code> callback
+ * <b>must</b> be done in the thread doing the notification.
+ * </li>
+ * <li>
+ * After-the-fact batch reports of arbitrary creations,
+ * deletions and modifications to one or more resources expressed
+ * as a hierarchical resource delta. Event type is
+ * <code>POST_BUILD</code>, and <code>getDelta</code> returns
+ * the hierarchical delta. The resource delta is rooted at the
+ * workspace root. The <code>getBuildKind</code> method returns
+ * the kind of build that occurred, and the <code>getSource</code>
+ * method returns the scope of the build (either the workspace or a single project).
+ * These events are broadcast to interested parties at the end of every build operation.
+ * If autobuilding is not enabled, these events still occur at times when autobuild
+ * would have occurred. The workspace is open for change during notification of
+ * these events. The delta reported in this event cycle is identical across
+ * all listeners registered for this type of event.
+ * Resource changes attempted during a <code>POST_BUILD</code> callback
+ * <b>must</b> be done in the thread doing the notification.
+ * </li>
+ * <li>
+ * After-the-fact batch reports of arbitrary creations,
+ * deletions and modifications to one or more resources expressed
+ * as a hierarchical resource delta. Event type is
+ * <code>POST_CHANGE</code>, and <code>getDelta</code> returns
+ * the hierarchical delta. The resource delta is rooted at the
+ * workspace root. These events are broadcast to interested parties after
+ * a set of resource changes and happen whether or not autobuilding is enabled.
+ * The workspace is closed for change during notification of these events.
+ * The delta reported in this event cycle is identical across all listeners registered for
+ * this type of event.
+ * </li>
+ * <li>
+ * Before-the-fact reports of the impending closure of a single
+ * project. Event type is <code>PRE_CLOSE</code>,
+ * and <code>getResource</code> returns the project being closed.
+ * The workspace is closed for change during notification of these events.
+ * </li>
+ * <li>
+ * Before-the-fact reports of the impending deletion of a single
+ * project. Event type is <code>PRE_DELETE</code>,
+ * and <code>getResource</code> returns the project being deleted.
+ * The workspace is closed for change during notification of these events.
+ * </li>
+ * <li>
+ * Before-the-fact reports of the impending refresh of a single project or the workspace.
+ * Event type is <code>PRE_REFRESH</code> and the <code>getSource</code>
+ * method returns the scope of the refresh (either the workspace or a single project).
+ * If the event is fired by a project refresh the <code>getResource</code>
+ * method returns the project being refreshed.
+ * The workspace is closed for changes during notification of these events.
+ * </li>
+ * </ul>
+ * <p>
+ * In order to handle additional event types that may be introduced
+ * in future releases of the platform, clients should do not write code
+ * that presumes the set of event types is closed.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResourceChangeEvent {
+ /**
+ * Event type constant (bit mask) indicating an after-the-fact
+ * report of creations, deletions, and modifications
+ * to one or more resources expressed as a hierarchical
+ * resource delta as returned by <code>getDelta</code>.
+ * See class comments for further details.
+ *
+ * @see #getType()
+ * @see #getDelta()
+ */
+ public static final int POST_CHANGE = 1;
+
+ /**
+ * Event type constant (bit mask) indicating a before-the-fact
+ * report of the impending closure of a single
+ * project as returned by <code>getResource</code>.
+ * See class comments for further details.
+ *
+ * @see #getType()
+ * @see #getResource()
+ */
+ public static final int PRE_CLOSE = 2;
+
+ /**
+ * Event type constant (bit mask) indicating a before-the-fact
+ * report of the impending deletion of a single
+ * project as returned by <code>getResource</code>.
+ * See class comments for further details.
+ *
+ * @see #getType()
+ * @see #getResource()
+ */
+ public static final int PRE_DELETE = 4;
+
+ /**
+ * @deprecated This event type has been renamed to
+ * <code>PRE_BUILD</code>
+ */
+ public static final int PRE_AUTO_BUILD = 8;
+
+ /**
+ * Event type constant (bit mask) indicating an before-the-fact
+ * report of creations, deletions, and modifications
+ * to one or more resources expressed as a hierarchical
+ * resource delta as returned by <code>getDelta</code>.
+ * See class comments for further details.
+ *
+ * @see #getType()
+ * @see #getResource()
+ * @since 3.0
+ */
+ public static final int PRE_BUILD = 8;
+
+ /**
+ * @deprecated This event type has been renamed to
+ * <code>POST_BUILD</code>
+ */
+ public static final int POST_AUTO_BUILD = 16;
+
+ /**
+ * Event type constant (bit mask) indicating an after-the-fact
+ * report of creations, deletions, and modifications
+ * to one or more resources expressed as a hierarchical
+ * resource delta as returned by <code>getDelta</code>.
+ * See class comments for further details.
+ *
+ * @see #getType()
+ * @see #getResource()
+ * @since 3.0
+ */
+ public static final int POST_BUILD = 16;
+
+ /**
+ * Event type constant (bit mask) indicating a before-the-fact
+ * report of refreshing the workspace or a project.
+ * See class comments for further details.
+ *
+ * @see #getType()
+ * @see #getSource()
+ * @see #getResource()
+ * @since 3.4
+ */
+ public static final int PRE_REFRESH = 32;
+
+ /**
+ * Returns all marker deltas of the specified type that are associated
+ * with resource deltas for this event. If <code>includeSubtypes</code>
+ * is <code>false</code>, only marker deltas whose type exactly matches
+ * the given type are returned. Returns an empty array if there
+ * are no matching marker deltas.
+ * <p>
+ * Calling this method is equivalent to walking the entire resource
+ * delta for this event, and collecting all marker deltas of a given type.
+ * The speed of this method will be proportional to the number of changed
+ * markers, regardless of the size of the resource delta tree.
+ * </p>
+ * @param type the type of marker to consider, or <code>null</code> to indicate all types
+ * @param includeSubtypes whether or not to consider sub-types of the given type
+ * @return an array of marker deltas
+ * @since 2.0
+ */
+ public IMarkerDelta[] findMarkerDeltas(String type, boolean includeSubtypes);
+
+ /**
+ * Returns the kind of build that caused this event,
+ * or <code>0</code> if not applicable to this type of event.
+ * <p>
+ * If the event is a <code>PRE_BUILD</code> or <code>POST_BUILD</code>
+ * then this will be the kind of build that occurred to cause the event.
+ * </p>
+ *
+ * @see IProject#build(int, IProgressMonitor)
+ * @see IWorkspace#build(int, IProgressMonitor)
+ * @see IncrementalProjectBuilder#AUTO_BUILD
+ * @see IncrementalProjectBuilder#FULL_BUILD
+ * @see IncrementalProjectBuilder#INCREMENTAL_BUILD
+ * @see IncrementalProjectBuilder#CLEAN_BUILD
+ * @return the kind of build, or <code>0</code> if not applicable
+ * @since 3.1
+ */
+ public int getBuildKind();
+
+ /**
+ * Returns a resource delta, rooted at the workspace, describing the set
+ * of changes that happened to resources in the workspace.
+ * Returns <code>null</code> if not applicable to this type of event.
+ *
+ * @return the resource delta, or <code>null</code> if not
+ * applicable
+ */
+ public IResourceDelta getDelta();
+
+ /**
+ * Returns the resource in question or <code>null</code>
+ * if not applicable to this type of event.
+ * <p>
+ * If the event is of type <code>PRE_CLOSE</code>,
+ * <code>PRE_DELETE</code>, or <code>PRE_REFRESH</code>, then the resource
+ * will be the affected project. Otherwise the resource will be <code>null</code>.
+ * </p>
+ * @return the resource, or <code>null</code> if not applicable
+ */
+ public IResource getResource();
+
+ /**
+ * Returns an object identifying the source of this event.
+ * <p>
+ * If the event is a <code>PRE_BUILD</code>, <code>POST_BUILD</code>,
+ * or <code>PRE_REFRESH</code> then this will be the scope of the build
+ * (either the {@link IWorkspace} or a single {@link IProject}).
+ * </p>
+ *
+ * @return an object identifying the source of this event
+ * @see java.util.EventObject
+ */
+ public Object getSource();
+
+ /**
+ * Returns the type of event being reported.
+ *
+ * @return one of the event type constants
+ * @see #POST_CHANGE
+ * @see #POST_BUILD
+ * @see #PRE_BUILD
+ * @see #PRE_CLOSE
+ * @see #PRE_DELETE
+ * @see #PRE_REFRESH
+ */
+ public int getType();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceChangeListener.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.EventListener;
+
+/**
+ * A resource change listener is notified of changes to resources
+ * in the workspace.
+ * These changes arise from direct manipulation of resources, or
+ * indirectly through re-synchronization with the local file system.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IResourceDelta
+ * @see IWorkspace#addResourceChangeListener(IResourceChangeListener, int)
+ */
+public interface IResourceChangeListener extends EventListener {
+ /**
+ * Notifies this listener that some resource changes
+ * are happening, or have already happened.
+ * <p>
+ * The supplied event gives details. This event object (and the
+ * resource delta within it) is valid only for the duration of
+ * the invocation of this method.
+ * </p>
+ * <p>
+ * Note: This method is called by the platform; it is not intended
+ * to be called directly by clients.
+ * <p>
+ * Note that during resource change event notification, further changes
+ * to resources may be disallowed.
+ * </p>
+ *
+ * @param event the resource change event
+ * @see IResourceDelta
+ */
+ public void resourceChanged(IResourceChangeEvent event);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceDelta.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,572 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.internal.watson.IElementComparator;
+import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A resource delta represents changes in the state of a resource tree
+ * between two discrete points in time.
+ * <p>
+ * Resource deltas implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see IResource
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResourceDelta extends IAdaptable {
+
+ /*====================================================================
+ * Constants defining resource delta kinds:
+ *====================================================================*/
+
+ /**
+ * Delta kind constant indicating that the resource has not been changed in any way.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int NO_CHANGE = IElementComparator.K_NO_CHANGE;
+
+ /**
+ * Delta kind constant (bit mask) indicating that the resource has been added
+ * to its parent. That is, one that appears in the "after" state,
+ * not in the "before" one.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int ADDED = 0x1;
+
+ /**
+ * Delta kind constant (bit mask) indicating that the resource has been removed
+ * from its parent. That is, one that appears in the "before" state,
+ * not in the "after" one.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int REMOVED = 0x2;
+
+ /**
+ * Delta kind constant (bit mask) indicating that the resource has been changed.
+ * That is, one that appears in both the "before" and "after" states.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int CHANGED = 0x4;
+
+ /**
+ * Delta kind constant (bit mask) indicating that a phantom resource has been added at
+ * the location of the delta node.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int ADDED_PHANTOM = 0x8;
+
+ /**
+ * Delta kind constant (bit mask) indicating that a phantom resource has been removed from
+ * the location of the delta node.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int REMOVED_PHANTOM = 0x10;
+
+ /**
+ * The bit mask which describes all possible delta kinds,
+ * including ones involving phantoms.
+ *
+ * @see IResourceDelta#getKind()
+ */
+ public static final int ALL_WITH_PHANTOMS = CHANGED | ADDED | REMOVED | ADDED_PHANTOM | REMOVED_PHANTOM;
+
+ /*====================================================================
+ * Constants which describe resource changes:
+ *====================================================================*/
+
+ /**
+ * Change constant (bit mask) indicating that the content of the resource has changed.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int CONTENT = 0x100;
+
+ /**
+ * Change constant (bit mask) indicating that the resource was moved from another location.
+ * The location in the "before" state can be retrieved using <code>getMovedFromPath()</code>.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int MOVED_FROM = 0x1000;
+
+ /**
+ * Change constant (bit mask) indicating that the resource was moved to another location.
+ * The location in the new state can be retrieved using <code>getMovedToPath()</code>.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int MOVED_TO = 0x2000;
+
+ /**
+ * Change constant (bit mask) indicating that the resource was copied from another location.
+ * The location in the "before" state can be retrieved using <code>getMovedFromPath()</code>.
+ * This flag is only used when describing potential changes using an {@link IResourceChangeDescriptionFactory}.
+ *
+ * @see IResourceDelta#getFlags()
+ * @since 3.2
+ */
+ public static final int COPIED_FROM = 0x800;
+ /**
+ * Change constant (bit mask) indicating that the resource was opened or closed.
+ * This flag is also set when the project did not exist in the "before" state.
+ * For example, if the current state of the resource is open then it was previously closed
+ * or did not exist.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int OPEN = 0x4000;
+
+ /**
+ * Change constant (bit mask) indicating that the type of the resource has changed.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int TYPE = 0x8000;
+
+ /**
+ * Change constant (bit mask) indicating that the resource's sync status has changed.
+ * This type of change is not included in build deltas, only in those for resource notification.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int SYNC = 0x10000;
+
+ /**
+ * Change constant (bit mask) indicating that the resource's markers have changed.
+ * This type of change is not included in build deltas, only in those for resource notification.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int MARKERS = 0x20000;
+
+ /**
+ * Change constant (bit mask) indicating that the resource has been
+ * replaced by another at the same location (i.e., the resource has
+ * been deleted and then added).
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int REPLACED = 0x40000;
+
+ /**
+ * Change constant (bit mask) indicating that a project's description has changed.
+ *
+ * @see IResourceDelta#getFlags()
+ */
+ public static final int DESCRIPTION = 0x80000;
+
+ /**
+ * Change constant (bit mask) indicating that the encoding of the resource has changed.
+ *
+ * @see IResourceDelta#getFlags()
+ * @since 3.0
+ */
+ public static final int ENCODING = 0x100000;
+
+ /**
+ * Change constant (bit mask) indicating that the underlying file or folder of the linked resource has been added or removed.
+ *
+ * @see IResourceDelta#getFlags()
+ * @since 3.4
+ */
+ public static final int LOCAL_CHANGED = 0x200000;
+
+ /**
+ * Accepts the given visitor.
+ * The only kinds of resource deltas visited
+ * are <code>ADDED</code>, <code>REMOVED</code>,
+ * and <code>CHANGED</code>.
+ * The visitor's <code>visit</code> method is called with this
+ * resource delta if applicable. If the visitor returns <code>true</code>,
+ * the resource delta's children are also visited.
+ * <p>
+ * This is a convenience method, fully equivalent to
+ * <code>accept(visitor, IResource.NONE)</code>.
+ * Although the visitor will be invoked for this resource delta, it will not be
+ * invoked for any team-private member resources.
+ * </p>
+ *
+ * @param visitor the visitor
+ * @exception CoreException if the visitor failed with this exception.
+ * @see IResourceDeltaVisitor#visit(IResourceDelta)
+ */
+ public void accept(IResourceDeltaVisitor visitor) throws CoreException;
+
+ /**
+ * Accepts the given visitor.
+ * The visitor's <code>visit</code> method is called with this
+ * resource delta. If the visitor returns <code>true</code>,
+ * the resource delta's children are also visited.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * accept(visitor, includePhantoms ? INCLUDE_PHANTOMS : IResource.NONE);
+ * </pre>
+ * Although the visitor will be invoked for this resource delta, it will not be
+ * invoked for any team-private member resources.
+ * </p>
+ *
+ * @param visitor the visitor
+ * @param includePhantoms <code>true</code> if phantom resources are
+ * of interest; <code>false</code> if phantom resources are not of
+ * interest
+ * @exception CoreException if the visitor failed with this exception.
+ * @see #accept(IResourceDeltaVisitor)
+ * @see IResource#isPhantom()
+ * @see IResourceDeltaVisitor#visit(IResourceDelta)
+ */
+ public void accept(IResourceDeltaVisitor visitor, boolean includePhantoms) throws CoreException;
+
+ /**
+ * Accepts the given visitor.
+ * The visitor's <code>visit</code> method is called with this
+ * resource delta. If the visitor returns <code>true</code>,
+ * the resource delta's children are also visited.
+ * <p>
+ * The member flags determine which child deltas of this resource delta will be visited.
+ * The visitor will always be invoked for this resource delta.
+ * <p>
+ * If the <code>INCLUDE_PHANTOMS</code> member flag is not specified
+ * (recommended), only child resource deltas involving existing resources will be visited
+ * (kinds <code>ADDED</code>, <code>REMOVED</code>, and <code>CHANGED</code>).
+ * If the <code>INCLUDE_PHANTOMS</code> member flag is specified,
+ * the result will also include additions and removes of phantom resources
+ * (kinds <code>ADDED_PHANTOM</code> and <code>REMOVED_PHANTOM</code>).
+ * </p>
+ * <p>
+ * If the <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code> member flag is not specified
+ * (recommended), resource deltas involving team private member resources will be
+ * excluded from the visit. If the <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code> member
+ * flag is specified, the visit will also include additions and removes of
+ * team private member resources.
+ * </p>
+ *
+ * @param visitor the visitor
+ * @param memberFlags bit-wise or of member flag constants
+ * (<code>IContainer.INCLUDE_PHANTOMS</code>, <code>INCLUDE_HIDDEN</code>
+ * and <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code>) indicating which members are of interest
+ * @exception CoreException if the visitor failed with this exception.
+ * @see IResource#isPhantom()
+ * @see IResource#isTeamPrivateMember()
+ * @see IResource#isHidden()
+ * @see IContainer#INCLUDE_PHANTOMS
+ * @see IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS
+ * @see IContainer#INCLUDE_HIDDEN
+ * @see IResourceDeltaVisitor#visit(IResourceDelta)
+ * @since 2.0
+ */
+ public void accept(IResourceDeltaVisitor visitor, int memberFlags) throws CoreException;
+
+ /**
+ * Finds and returns the descendent delta identified by the given path in
+ * this delta, or <code>null</code> if no such descendent exists.
+ * The supplied path may be absolute or relative; in either case, it is
+ * interpreted as relative to this delta. Trailing separators are ignored.
+ * If the path is empty this delta is returned.
+ * <p>
+ * This is a convenience method to avoid manual traversal of the delta
+ * tree in cases where the listener is only interested in changes to
+ * particular resources. Calling this method will generally be
+ * faster than manually traversing the delta to a particular descendent.
+ * </p>
+ * @param path the path of the desired descendent delta
+ * @return the descendent delta, or <code>null</code> if no such
+ * descendent exists in the delta
+ * @since 2.0
+ */
+ public IResourceDelta findMember(IPath path);
+
+ /**
+ * Returns resource deltas for all children of this resource
+ * which were added, removed, or changed. Returns an empty
+ * array if there are no affected children.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * getAffectedChildren(ADDED | REMOVED | CHANGED, IResource.NONE);
+ * </pre>
+ * Team-private member resources are <b>not</b> included in the result; neither are
+ * phantom resources.
+ * </p>
+ *
+ * @return the resource deltas for all affected children
+ * @see IResourceDelta#ADDED
+ * @see IResourceDelta#REMOVED
+ * @see IResourceDelta#CHANGED
+ * @see #getAffectedChildren(int,int)
+ */
+ public IResourceDelta[] getAffectedChildren();
+
+ /**
+ * Returns resource deltas for all children of this resource
+ * whose kind is included in the given mask. Kind masks are formed
+ * by the bitwise or of <code>IResourceDelta</code> kind constants.
+ * Returns an empty array if there are no affected children.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * getAffectedChildren(kindMask, IResource.NONE);
+ * </pre>
+ * Team-private member resources are <b>not</b> included in the result.
+ * </p>
+ *
+ * @param kindMask a mask formed by the bitwise or of <code>IResourceDelta </code>
+ * delta kind constants
+ * @return the resource deltas for all affected children
+ * @see IResourceDelta#ADDED
+ * @see IResourceDelta#REMOVED
+ * @see IResourceDelta#CHANGED
+ * @see IResourceDelta#ADDED_PHANTOM
+ * @see IResourceDelta#REMOVED_PHANTOM
+ * @see IResourceDelta#ALL_WITH_PHANTOMS
+ * @see #getAffectedChildren(int,int)
+ */
+ public IResourceDelta[] getAffectedChildren(int kindMask);
+
+ /**
+ * Returns resource deltas for all children of this resource
+ * whose kind is included in the given mask. Masks are formed
+ * by the bitwise or of <code>IResourceDelta</code> kind constants.
+ * Returns an empty array if there are no affected children.
+ * <p>
+ * If the <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code> member flag is not specified,
+ * (recommended), resource deltas involving team private member resources will be
+ * excluded. If the <code>INCLUDE_TEAM_PRIVATE_MEMBERS</code> member
+ * flag is specified, the result will also include resource deltas of the
+ * specified kinds to team private member resources.
+ * </p>
+ * <p>
+ * If the {@link IContainer#INCLUDE_HIDDEN} member flag is not specified,
+ * (recommended), resource deltas involving hidden resources will be
+ * excluded. If the {@link IContainer#INCLUDE_HIDDEN} member
+ * flag is specified, the result will also include resource deltas of the
+ * specified kinds to hidden resources.
+ * </p>
+ * <p>
+ * Specifying the <code>IContainer.INCLUDE_PHANTOMS</code> member flag is equivalent
+ * to including <code>IContainer.ADDED_PHANTOM</code> and <code>IContainer.REMOVED_PHANTOM</code>
+ * in the kind mask.
+ * </p>
+ *
+ * @param kindMask a mask formed by the bitwise or of <code>IResourceDelta</code>
+ * delta kind constants
+ * @param memberFlags bit-wise or of member flag constants
+ * (<code>IContainer.INCLUDE_PHANTOMS</code>, <code>IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS</code>
+ * and <code>IContainer.INCLUDE_HIDDEN</code>)
+ * indicating which members are of interest
+ * @return the resource deltas for all affected children
+ * @see IResourceDelta#ADDED
+ * @see IResourceDelta#REMOVED
+ * @see IResourceDelta#CHANGED
+ * @see IResourceDelta#ADDED_PHANTOM
+ * @see IResourceDelta#REMOVED_PHANTOM
+ * @see IResourceDelta#ALL_WITH_PHANTOMS
+ * @see IContainer#INCLUDE_PHANTOMS
+ * @see IContainer#INCLUDE_TEAM_PRIVATE_MEMBERS
+ * @see IContainer#INCLUDE_HIDDEN
+ * @since 2.0
+ */
+ public IResourceDelta[] getAffectedChildren(int kindMask, int memberFlags);
+
+ /**
+ * Returns flags which describe in more detail how a resource has been affected.
+ * <p>
+ * The following codes (bit masks) are used when kind is <code>CHANGED</code>, and
+ * also when the resource is involved in a move:
+ * <ul>
+ * <li><code>CONTENT</code> - The bytes contained by the resource have
+ * been altered, or <code>IResource.touch</code> has been called on
+ * the resource.</li>
+ * <li><code>ENCODING</code> - The encoding of the resource may have been altered.
+ * This flag is not set when the encoding changes due to the file being modified,
+ * or being moved.</li>
+ * <li><code>DESCRIPTION</code> - The description of the project has been altered,
+ * or <code>IResource.touch</code> has been called on the project.
+ * This flag is only valid for project resources.</li>
+ * <li><code>OPEN</code> - The project's open/closed state has changed.
+ * If it is not open, it was closed, and vice versa. This flag is only valid for project resources.</li>
+ * <li><code>TYPE</code> - The resource (a folder or file) has changed its type.</li>
+ * <li><code>SYNC</code> - The resource's sync status has changed.</li>
+ * <li><code>MARKERS</code> - The resource's markers have changed.</li>
+ * <li><code>REPLACED</code> - The resource (and all its properties)
+ * was deleted (either by a delete or move), and was subsequently re-created
+ * (either by a create, move, or copy).</li>
+ * <li><code>LOCAL_CHANGED</code> - The resource is a linked resource,
+ * and the underlying file system object has been added or removed.</li>
+ * </ul>
+ * The following code is only used if kind is <code>REMOVED</code>
+ * (or <code>CHANGED</code> in conjunction with <code>REPLACED</code>):
+ * <ul>
+ * <li><code>MOVED_TO</code> - The resource has moved.
+ * <code>getMovedToPath</code> will return the path of where it was moved to.</li>
+ * </ul>
+ * The following code is only used if kind is <code>ADDED</code>
+ * (or <code>CHANGED</code> in conjunction with <code>REPLACED</code>):
+ * <ul>
+ * <li><code>MOVED_FROM</code> - The resource has moved.
+ * <code>getMovedFromPath</code> will return the path of where it was moved from.</li>
+ * </ul>
+ * A simple move operation would result in the following delta information.
+ * If a resource is moved from A to B (with no other changes to A or B),
+ * then A will have kind <code>REMOVED</code>, with flag <code>MOVED_TO</code>,
+ * and <code>getMovedToPath</code> on A will return the path for B.
+ * B will have kind <code>ADDED</code>, with flag <code>MOVED_FROM</code>,
+ * and <code>getMovedFromPath</code> on B will return the path for A.
+ * B's other flags will describe any other changes to the resource, as compared
+ * to its previous location at A.
+ * </p>
+ * <p>
+ * Note that the move flags only describe the changes to a single resource; they
+ * don't necessarily imply anything about the parent or children of the resource.
+ * If the children were moved as a consequence of a subtree move operation,
+ * they will have corresponding move flags as well.
+ * </p>
+ * <p>
+ * Note that it is possible for a file resource to be replaced in the workspace
+ * by a folder resource (or the other way around).
+ * The resource delta, which is actually expressed in terms of
+ * paths instead or resources, shows this as a change to either the
+ * content or children.
+ * </p>
+ *
+ * @return the flags
+ * @see IResourceDelta#CONTENT
+ * @see IResourceDelta#DESCRIPTION
+ * @see IResourceDelta#ENCODING
+ * @see IResourceDelta#OPEN
+ * @see IResourceDelta#MOVED_TO
+ * @see IResourceDelta#MOVED_FROM
+ * @see IResourceDelta#TYPE
+ * @see IResourceDelta#SYNC
+ * @see IResourceDelta#MARKERS
+ * @see IResourceDelta#REPLACED
+ * @see #getKind()
+ * @see #getMovedFromPath()
+ * @see #getMovedToPath()
+ * @see IResource#move(IPath, int, IProgressMonitor)
+ */
+ public int getFlags();
+
+ /**
+ * Returns the full, absolute path of this resource delta.
+ * <p>
+ * Note: the returned path never has a trailing separator.
+ * </p>
+ * @return the full, absolute path of this resource delta
+ * @see IResource#getFullPath()
+ * @see #getProjectRelativePath()
+ */
+ public IPath getFullPath();
+
+ /**
+ * Returns the kind of this resource delta.
+ * Normally, one of <code>ADDED</code>,
+ * <code>REMOVED</code>, <code>CHANGED</code>.
+ * When phantom resources have been explicitly requested,
+ * there are two additional kinds: <code>ADDED_PHANTOM</code>
+ * and <code>REMOVED_PHANTOM</code>.
+ *
+ * @return the kind of this resource delta
+ * @see IResourceDelta#ADDED
+ * @see IResourceDelta#REMOVED
+ * @see IResourceDelta#CHANGED
+ * @see IResourceDelta#ADDED_PHANTOM
+ * @see IResourceDelta#REMOVED_PHANTOM
+ */
+ public int getKind();
+
+ /**
+ * Returns the changes to markers on the corresponding resource.
+ * Returns an empty array if no markers changed.
+ *
+ * @return the marker deltas
+ */
+ public IMarkerDelta[] getMarkerDeltas();
+
+ /**
+ * Returns the full path (in the "before" state) from which this resource
+ * (in the "after" state) was moved. This value is only valid
+ * if the <code>MOVED_FROM</code> change flag is set; otherwise,
+ * <code>null</code> is returned.
+ * <p>
+ * Note: the returned path never has a trailing separator.
+ *
+ * @return a path, or <code>null</code>
+ * @see #getMovedToPath()
+ * @see #getFullPath()
+ * @see #getFlags()
+ */
+ public IPath getMovedFromPath();
+
+ /**
+ * Returns the full path (in the "after" state) to which this resource
+ * (in the "before" state) was moved. This value is only valid if the
+ * <code>MOVED_TO</code> change flag is set; otherwise,
+ * <code>null</code> is returned.
+ * <p>
+ * Note: the returned path never has a trailing separator.
+ *
+ * @return a path, or <code>null</code>
+ * @see #getMovedFromPath()
+ * @see #getFullPath()
+ * @see #getFlags()
+ */
+ public IPath getMovedToPath();
+
+ /**
+ * Returns the project-relative path of this resource delta.
+ * Returns the empty path for projects and the workspace root.
+ * <p>
+ * A resource's project-relative path indicates the route from the project
+ * to the resource. Within a workspace, there is exactly one such path
+ * for any given resource. The returned path never has a trailing separator.
+ * </p>
+ * @return the project-relative path of this resource delta
+ * @see IResource#getProjectRelativePath()
+ * @see #getFullPath()
+ * @see Path#EMPTY
+ */
+ public IPath getProjectRelativePath();
+
+ /**
+ * Returns a handle for the affected resource.
+ * <p>
+ * For additions (<code>ADDED</code>), this handle describes the newly-added resource; i.e.,
+ * the one in the "after" state.
+ * <p>
+ * For changes (<code>CHANGED</code>), this handle also describes the resource in the "after"
+ * state. When a file or folder resource has changed type, the
+ * former type of the handle can be inferred.
+ * <p>
+ * For removals (<code>REMOVED</code>), this handle describes the resource in the "before"
+ * state. Even though this resource would not normally exist in the
+ * current workspace, the type of resource that was removed can be
+ * determined from the handle.
+ * <p>
+ * For phantom additions and removals (<code>ADDED_PHANTOM</code>
+ * and <code>REMOVED_PHANTOM</code>), this is the handle of the phantom resource.
+ *
+ * @return the affected resource (handle)
+ */
+ public IResource getResource();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceDeltaVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * An objects that visits resource deltas.
+ * <p>
+ * Usage:
+ * <pre>
+ * class Visitor implements IResourceDeltaVisitor {
+ * public boolean visit(IResourceDelta delta) {
+ * switch (delta.getKind()) {
+ * case IResourceDelta.ADDED :
+ * // handle added resource
+ * break;
+ * case IResourceDelta.REMOVED :
+ * // handle removed resource
+ * break;
+ * case IResourceDelta.CHANGED :
+ * // handle changed resource
+ * break;
+ * }
+ * return true;
+ * }
+ * }
+ * IResourceDelta rootDelta = ...;
+ * rootDelta.accept(new Visitor());
+ * </pre>
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IResource#accept(IResourceVisitor)
+ */
+public interface IResourceDeltaVisitor {
+ /**
+ * Visits the given resource delta.
+ *
+ * @return <code>true</code> if the resource delta's children should
+ * be visited; <code>false</code> if they should be skipped.
+ * @exception CoreException if the visit fails for some reason.
+ */
+ public boolean visit(IResourceDelta delta) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceProxy.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * A lightweight interface for requesting information about a resource.
+ * All of the "get" methods on a resource proxy have trivial performance cost.
+ * Requesting the full path or the actual resource handle will cause extra objects
+ * to be created and will thus have greater cost.
+ * <p>
+ * When a resource proxy is used within an {@link IResourceProxyVisitor},
+ * it is a transient object that is only valid for the duration of a single visit method.
+ * A proxy should not be referenced once the single resource visit is complete.
+ * The equals and hashCode methods should not be relied on.
+ * </p>
+ * <p>
+ * A proxy can also be created using {@link IResource#createProxy()}. In
+ * this case the proxy is valid indefinitely, but will not remain in sync with
+ * the state of the corresponding resource.
+ * </p>
+ *
+ * @see IResourceProxyVisitor
+ * @since 2.1
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResourceProxy {
+ /**
+ * Returns the modification stamp of the resource being visited.
+ *
+ * @return the modification stamp, or <code>NULL_STAMP</code> if the
+ * resource either does not exist or exists as a closed project
+ * @see IResource#getModificationStamp()
+ */
+ public long getModificationStamp();
+
+ /**
+ * Returns whether the resource being visited is accessible.
+ *
+ * @return <code>true</code> if the resource is accessible, and
+ * <code>false</code> otherwise
+ * @see IResource#isAccessible()
+ */
+ public boolean isAccessible();
+
+ /**
+ * Returns whether the resource being visited is derived.
+ *
+ * @return <code>true</code> if the resource is marked as derived, and
+ * <code>false</code> otherwise
+ * @see IResource#isDerived()
+ */
+ public boolean isDerived();
+
+ /**
+ * Returns whether the resource being visited is a linked resource.
+ *
+ * @return <code>true</code> if the resource is linked, and
+ * <code>false</code> otherwise
+ * @see IResource#isLinked()
+ */
+ public boolean isLinked();
+
+ /**
+ * Returns whether the resource being visited is a phantom resource.
+ *
+ * @return <code>true</code> if the resource is a phantom resource, and
+ * <code>false</code> otherwise
+ * @see IResource#isPhantom()
+ */
+ public boolean isPhantom();
+
+ /**
+ * Returns whether the resource being visited is a hidden resource.
+ *
+ * @return <code>true</code> if the resource is a hidden resource, and
+ * <code>false</code> otherwise
+ * @see IResource#isHidden()
+ *
+ * @since 3.4
+ */
+ public boolean isHidden();
+
+ /**
+ * Returns whether the resource being visited is a team private member.
+ *
+ * @return <code>true</code> if the resource is a team private member, and
+ * <code>false</code> otherwise
+ * @see IResource#isTeamPrivateMember()
+ */
+ public boolean isTeamPrivateMember();
+
+ /**
+ * Returns the simple name of the resource being visited.
+ *
+ * @return the name of the resource
+ * @see IResource#getName()
+ */
+ public String getName();
+
+ /**
+ * Returns the value of the session property of the resource being
+ * visited, identified by the given key. Returns <code>null</code> if this
+ * resource has no such property.
+ * <p>
+ * Note that this method can return an out of date property value, or a
+ * value that no longer exists, if session properties are being modified
+ * concurrently with the resource visit.
+ * </p>
+ *
+ * @param key the qualified name of the property
+ * @return the string value of the session property,
+ * or <code>null</code> if the resource has no such property
+ * @see IResource#getSessionProperty(QualifiedName)
+ */
+ public Object getSessionProperty(QualifiedName key);
+
+ /**
+ * Returns the type of the resource being visited.
+ *
+ * @return the resource type
+ * @see IResource#getType()
+ */
+ public int getType();
+
+ /**
+ * Returns the full workspace path of the resource being visited.
+ * <p>
+ * Note that this is not a "free" proxy operation. This method
+ * will generally cause a path object to be created. For an optimal
+ * visitor, only call this method when absolutely necessary. Note that the
+ * simple resource name can be obtained from the proxy with no cost.
+ * </p>
+ * @return the full path of the resource
+ * @see IResource#getFullPath()
+ */
+ public IPath requestFullPath();
+
+ /**
+ * Returns the handle of the resource being visited.
+ * <p>
+ * Note that this is not a "free" proxy operation. This method will
+ * generally cause both a path object and a resource object to be created.
+ * For an optimal visitor, only call this method when absolutely necessary.
+ * Note that the simple resource name can be obtained from the proxy with no
+ * cost, and the full path of the resource can be obtained through the proxy
+ * with smaller cost.
+ * </p>
+ * @return the resource handle
+ */
+ public IResource requestResource();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceProxyVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This interface is implemented by objects that visit resource trees. The fast
+ * visitor is an optimized mechanism for tree traversal that creates a minimal
+ * number of objects. The visitor is provided with a callback interface,
+ * instead of a resource. Through the callback, the visitor can request
+ * information about the resource being visited.
+ * <p>
+ * Usage:
+ * <pre>
+ * class Visitor implements IResourceProxyVisitor {
+ * public boolean visit (IResourceProxy proxy) {
+ * // your code here
+ * return true;
+ * }
+ * }
+ * ResourcesPlugin.getWorkspace().getRoot().accept(new Visitor(), IResource.NONE);
+ * </pre>
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IResource#accept(IResourceVisitor)
+ * @since 2.1
+ */
+public interface IResourceProxyVisitor {
+ /**
+ * Visits the given resource.
+ *
+ * @param proxy for requesting information about the resource being visited;
+ * this object is only valid for the duration of the invocation of this
+ * method, and must not be used after this method has completed
+ * @return <code>true</code> if the resource's members should
+ * be visited; <code>false</code> if they should be skipped
+ * @exception CoreException if the visit fails for some reason.
+ */
+ public boolean visit(IResourceProxy proxy) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceRuleFactory.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * A resource rule factory returns scheduling rules for API methods
+ * that modify the workspace. These rules can be used when creating jobs
+ * or other operations that perform a series of modifications on the workspace.
+ * This allows clients to implement two phase commit semantics, where all
+ * necessary rules are obtained prior to executing a long running operation.
+ * <p>
+ * Note that simple use of the workspace APIs does not require use of scheduling
+ * rules. All workspace API methods that modify the workspace will automatically
+ * obtain any scheduling rules needed to perform the modification. However, if you
+ * are aggregating a set of changes to the workspace using <code>WorkspaceJob</code>
+ * or <code>IWorkspaceRunnable</code> you can use scheduling rules to lock a
+ * portion of the workspace for the duration of the job or runnable. If you
+ * provide a non-null scheduling rule, a runtime exception will occur if you try to
+ * modify a portion of the workspace that is not covered by the rule for the runnable or job.
+ * <p>
+ * If more than one rule is needed, they can be aggregated using the
+ * <code>MultiRule.combine</code> method. Simplifying a group of rules does not change
+ * the set of resources that are covered, but can improve job scheduling performance.
+ * <p>
+ * Note that <code>null</code> is a valid scheduling rule (indicating that no
+ * resources need to be locked), and thus all methods in this class may
+ * return <code>null</code>.
+ *
+ * @see WorkspaceJob
+ * @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, org.eclipse.core.runtime.IProgressMonitor)
+ * @see org.eclipse.core.runtime.jobs.MultiRule#combine(ISchedulingRule, ISchedulingRule)
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResourceRuleFactory {
+ /**
+ * Returns the scheduling rule that is required for creating a project, folder,
+ * or file.
+ *
+ * @param resource the resource being created
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule createRule(IResource resource);
+
+ /**
+ * Returns the scheduling rule that is required for building a project or the
+ * entire workspace.
+ *
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule buildRule();
+
+ /**
+ * Returns the scheduling rule that is required for changing the charset
+ * setting for a file or the default charset setting for a container.
+ *
+ * @param resource the resource the charset will be changed
+ * @return a scheduling rule, or <code>null</code>
+ * @since 3.1
+ */
+ public ISchedulingRule charsetRule(IResource resource);
+
+ /**
+ * Returns the scheduling rule that is required for copying a resource.
+ *
+ * @param source the source of the copy
+ * @param destination the destination of the copy
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule copyRule(IResource source, IResource destination);
+
+ /**
+ * Returns the scheduling rule that is required for deleting a resource.
+ *
+ * @param resource the resource to be deleted
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule deleteRule(IResource resource);
+
+ /**
+ * Returns the scheduling rule that is required for creating, modifying, or
+ * deleting markers on a resource.
+ *
+ * @param resource the resource owning the marker to be modified
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule markerRule(IResource resource);
+
+ /**
+ * Returns the scheduling rule that is required for modifying a resource.
+ * For files, modification includes setting and appending contents. For
+ * projects, modification includes opening or closing the project, or
+ * setting the project description using the
+ * {@link IResource#AVOID_NATURE_CONFIG} flag. For all resources
+ * <code>touch</code> is considered to be a modification.
+ *
+ * @param resource the resource being modified
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule modifyRule(IResource resource);
+
+ /**
+ * Returns the scheduling rule that is required for moving a resource.
+ *
+ * @param source the source of the move
+ * @param destination the destination of the move
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule moveRule(IResource source, IResource destination);
+
+ /**
+ * Returns the scheduling rule that is required for performing
+ * <code>refreshLocal</code> on a resource.
+ *
+ * @param resource the resource to refresh
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule refreshRule(IResource resource);
+
+ /**
+ * Returns the scheduling rule that is required for a <code>validateEdit</code>
+ *
+ * @param resources the resources to be validated
+ * @return a scheduling rule, or <code>null</code>
+ */
+ public ISchedulingRule validateEditRule(IResource[] resources);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceStatus.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Represents status related to resources in the Resources plug-in and
+ * defines the relevant status code constants.
+ * Status objects created by the Resources plug-in bear its unique id
+ * (<code>ResourcesPlugin.PI_RESOURCES</code>) and one of
+ * these status codes.
+ *
+ * @see org.eclipse.core.runtime.IStatus
+ * @see ResourcesPlugin#PI_RESOURCES
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResourceStatus extends IStatus {
+
+ /*
+ * Status code definitions
+ */
+
+ // General constants [0-98]
+ // Information Only [0-32]
+ // Warnings [33-65]
+ /** Status code constant (value 35) indicating that a given
+ * nature set does not satisfy its constraints.
+ * Severity: warning. Category: general.
+ */
+ public static final int INVALID_NATURE_SET = 35;
+
+ // Errors [66-98]
+
+ /** Status code constant (value 75) indicating that a builder failed.
+ * Severity: error. Category: general.
+ */
+ public static final int BUILD_FAILED = 75;
+
+ /** Status code constant (value 76) indicating that an operation failed.
+ * Severity: error. Category: general.
+ */
+ public static final int OPERATION_FAILED = 76;
+
+ /** Status code constant (value 77) indicating an invalid value.
+ * Severity: error. Category: general.
+ */
+ public static final int INVALID_VALUE = 77;
+
+ // Local file system constants [200-298]
+ // Information Only [200-232]
+
+ // Warnings [233-265]
+
+ /** Status code constant (value 234) indicating that a project
+ * description file (.project), was missing but it has been repaired.
+ * Severity: warning. Category: local file system.
+ */
+ public static final int MISSING_DESCRIPTION_REPAIRED = 234;
+
+ /** Status code constant (value 235) indicating the local file system location
+ * for a resource overlaps the location of another resource.
+ * Severity: warning. Category: local file system.
+ */
+ public static final int OVERLAPPING_LOCATION = 235;
+
+ // Errors [266-298]
+
+ /** Status code constant (value 268) indicating a resource unexpectedly
+ * exists on the local file system.
+ * Severity: error. Category: local file system.
+ */
+ public static final int EXISTS_LOCAL = 268;
+
+ /** Status code constant (value 269) indicating a resource unexpectedly
+ * does not exist on the local file system.
+ * Severity: error. Category: local file system.
+ */
+ public static final int NOT_FOUND_LOCAL = 269;
+
+ /** Status code constant (value 270) indicating the local file system location for
+ * a resource could not be computed.
+ * Severity: error. Category: local file system.
+ */
+ public static final int NO_LOCATION_LOCAL = 270;
+
+ /** Status code constant (value 271) indicating an error occurred while
+ * reading part of a resource from the local file system.
+ * Severity: error. Category: local file system.
+ */
+ public static final int FAILED_READ_LOCAL = 271;
+
+ /** Status code constant (value 272) indicating an error occurred while
+ * writing part of a resource to the local file system.
+ * Severity: error. Category: local file system.
+ */
+ public static final int FAILED_WRITE_LOCAL = 272;
+
+ /** Status code constant (value 273) indicating an error occurred while
+ * deleting a resource from the local file system.
+ * Severity: error. Category: local file system.
+ */
+ public static final int FAILED_DELETE_LOCAL = 273;
+
+ /** Status code constant (value 274) indicating the workspace view of
+ * the resource differs from that of the local file system. The requested
+ * operation has been aborted to prevent the possible loss of data.
+ * Severity: error. Category: local file system.
+ */
+ public static final int OUT_OF_SYNC_LOCAL = 274;
+
+ /** Status code constant (value 275) indicating this file system is not case
+ * sensitive and a resource that differs only in case unexpectedly exists on
+ * the local file system.
+ * Severity: error. Category: local file system.
+ */
+ public static final int CASE_VARIANT_EXISTS = 275;
+
+ /** Status code constant (value 276) indicating a file exists in the
+ * file system but is not of the expected type (file instead of directory,
+ * or vice-versa).
+ * Severity: error. Category: local file system.
+ */
+ public static final int WRONG_TYPE_LOCAL = 276;
+
+ /** Status code constant (value 277) indicating that the parent
+ * file in the file system is marked as read-only.
+ * Severity: error. Category: local file system.
+ * @since 2.1
+ */
+ public static final int PARENT_READ_ONLY = 277;
+
+ /** Status code constant (value 278) indicating a file exists in the
+ * file system but its name is not a valid resource name.
+ * Severity: error. Category: local file system.
+ */
+ public static final int INVALID_RESOURCE_NAME = 278;
+
+ /** Status code constant (value 279) indicating that the
+ * file in the file system is marked as read-only.
+ * Severity: error. Category: local file system.
+ * @since 3.0
+ */
+ public static final int READ_ONLY_LOCAL = 279;
+
+ // Workspace constants [300-398]
+ // Information Only [300-332]
+
+ // Warnings [333-365]
+
+ /** Status code constant (value 333) indicating that a workspace path
+ * variable unexpectedly does not exist.
+ * Severity: warning. Category: workspace.
+ * @since 2.1
+ */
+ public static final int VARIABLE_NOT_DEFINED_WARNING = 333;
+
+ // Errors [366-398]
+
+ /** Status code constant (value 366) indicating a resource exists in the
+ * workspace but is not of the expected type.
+ * Severity: error. Category: workspace.
+ */
+ public static final int RESOURCE_WRONG_TYPE = 366;
+
+ /** Status code constant (value 367) indicating a resource unexpectedly
+ * exists in the workspace.
+ * Severity: error. Category: workspace.
+ */
+ public static final int RESOURCE_EXISTS = 367;
+
+ /** Status code constant (value 368) indicating a resource unexpectedly
+ * does not exist in the workspace.
+ * Severity: error. Category: workspace.
+ */
+ public static final int RESOURCE_NOT_FOUND = 368;
+
+ /** Status code constant (value 369) indicating a resource unexpectedly
+ * does not have content local to the workspace.
+ * Severity: error. Category: workspace.
+ */
+ public static final int RESOURCE_NOT_LOCAL = 369;
+
+ /** Status code constant (value 370) indicating a workspace
+ * is unexpectedly closed.
+ * Severity: error. Category: workspace.
+ */
+ public static final int WORKSPACE_NOT_OPEN = 370;
+
+ /** Status code constant (value 372) indicating a project is
+ * unexpectedly closed.
+ * Severity: error. Category: workspace.
+ */
+ public static final int PROJECT_NOT_OPEN = 372;
+
+ /** Status code constant (value 374) indicating that the path
+ * of a resource being created is occupied by an existing resource
+ * of a different type.
+ * Severity: error. Category: workspace.
+ */
+ public static final int PATH_OCCUPIED = 374;
+
+ /** Status code constant (value 375) indicating that the sync partner
+ * is not registered with the workspace synchronizer.
+ * Severity: error. Category: workspace.
+ */
+ public static final int PARTNER_NOT_REGISTERED = 375;
+
+ /** Status code constant (value 376) indicating a marker unexpectedly
+ * does not exist in the workspace tree.
+ * Severity: error. Category: workspace.
+ */
+ public static final int MARKER_NOT_FOUND = 376;
+
+ /** Status code constant (value 377) indicating a resource is
+ * unexpectedly not a linked resource.
+ * Severity: error. Category: workspace.
+ * @since 2.1
+ */
+ public static final int RESOURCE_NOT_LINKED = 377;
+
+ /** Status code constant (value 378) indicating that linking is
+ * not permitted on a certain project.
+ * Severity: error. Category: workspace.
+ * @since 2.1
+ */
+ public static final int LINKING_NOT_ALLOWED = 378;
+
+ /** Status code constant (value 379) indicating that a workspace path
+ * variable unexpectedly does not exist.
+ * Severity: error. Category: workspace.
+ * @since 2.1
+ */
+ public static final int VARIABLE_NOT_DEFINED = 379;
+
+ /** Status code constant (value 380) indicating that an attempt was made to modify
+ * the workspace while it was locked. Resource changes are disallowed
+ * during certain types of resource change event notification.
+ * Severity: error. Category: workspace.
+ * @see IResourceChangeEvent
+ * @since 2.1
+ */
+ public static final int WORKSPACE_LOCKED = 380;
+
+ /** Status code constant (value 381) indicating that a problem occurred while
+ * retrieving the content description for a resource.
+ * Severity: error. Category: workspace.
+ * @see IFile#getContentDescription
+ * @since 3.0
+ */
+ public static final int FAILED_DESCRIBING_CONTENTS = 381;
+
+ /** Status code constant (value 382) indicating that a problem occurred while
+ * setting the charset for a resource.
+ * Severity: error. Category: workspace.
+ * @see IContainer#setDefaultCharset(String, IProgressMonitor)
+ * @see IFile#setCharset(String, IProgressMonitor)
+ * @since 3.0
+ */
+ public static final int FAILED_SETTING_CHARSET = 382;
+
+ /** Status code constant (value 383) indicating that a problem occurred while
+ * getting the charset for a resource.
+ * Severity: error. Category: workspace.
+ * @since 3.0
+ */
+ public static final int FAILED_GETTING_CHARSET = 383;
+
+ // Internal constants [500-598]
+ // Information Only [500-532]
+
+ // Warnings [533-565]
+
+ // Errors [566-598]
+
+ /** Status code constant (value 566) indicating an error internal to the
+ * platform has occurred.
+ * Severity: error. Category: internal.
+ */
+ public static final int INTERNAL_ERROR = 566;
+
+ /** Status code constant (value 567) indicating the platform could not read
+ * some of its metadata.
+ * Severity: error. Category: internal.
+ */
+ public static final int FAILED_READ_METADATA = 567;
+
+ /** Status code constant (value 568) indicating the platform could not write
+ * some of its metadata.
+ * Severity: error. Category: internal.
+ */
+ public static final int FAILED_WRITE_METADATA = 568;
+
+ /** Status code constant (value 569) indicating the platform could not delete
+ * some of its metadata.
+ * Severity: error. Category: internal.
+ */
+ public static final int FAILED_DELETE_METADATA = 569;
+
+ /**
+ * Returns the path of the resource associated with this status.
+ *
+ * @return the path of the resource related to this status
+ */
+ public IPath getPath();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IResourceVisitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This interface is implemented by objects that visit resource trees.
+ * <p>
+ * Usage:
+ * <pre>
+ * class Visitor implements IResourceVisitor {
+ * public boolean visit(IResource res) {
+ * // your code here
+ * return true;
+ * }
+ * }
+ * IResource root = ...;
+ * root.accept(new Visitor());
+ * </pre>
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IResource#accept(IResourceVisitor)
+ */
+public interface IResourceVisitor {
+ /**
+ * Visits the given resource.
+ *
+ * @param resource the resource to visit
+ * @return <code>true</code> if the resource's members should
+ * be visited; <code>false</code> if they should be skipped
+ * @exception CoreException if the visit fails for some reason.
+ */
+ public boolean visit(IResource resource) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ISaveContext.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A context for workspace <code>save</code> operations.
+ * <p>
+ * Note that <code>IWorkspace.save</code> uses a
+ * different save context for each registered participant,
+ * allowing each to declare whether they have actively
+ * participated and decide whether to receive a resource
+ * delta on reactivation.
+ * </p>
+ *
+ * @see IWorkspace#save(boolean, org.eclipse.core.runtime.IProgressMonitor)
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ISaveContext {
+
+ /*====================================================================
+ * Constants related to save kind
+ *====================================================================*/
+
+ /**
+ * Type constant which identifies a full save.
+ *
+ * @see ISaveContext#getKind()
+ */
+ public static final int FULL_SAVE = 1;
+
+ /**
+ * Type constant which identifies a snapshot.
+ *
+ * @see ISaveContext#getKind()
+ */
+ public static final int SNAPSHOT = 2;
+
+ /**
+ * Type constant which identifies a project save.
+ *
+ * @see ISaveContext#getKind()
+ */
+ public static final int PROJECT_SAVE = 3;
+
+ /**
+ * Returns current files mapped with the <code>ISaveContext.map</code>
+ * facility or an empty array if there are no mapped files.
+ *
+ * @return the files currently mapped by the participant
+ *
+ * @see #map(IPath, IPath)
+ */
+ public IPath[] getFiles();
+
+ /**
+ * Returns the type of this save. The types can be:
+ * <ul>
+ * <li> <code>ISaveContext.FULL_SAVE</code></li>
+ * <li> <code>ISaveContext.SNAPSHOT</code></li>
+ * <li> <code>ISaveContext.PROJECT_SAVE</code></li>
+ * </ul>
+ *
+ * @return the type of the current save
+ */
+ public int getKind();
+
+ /**
+ * Returns the number for the previous save in
+ * which the plug-in actively participated, or <code>0</code>
+ * if the plug-in has never actively participated in a save before.
+ * <p>
+ * In the event of an unsuccessful save, this is the value to
+ * <code>rollback</code> to.
+ * </p>
+ *
+ * @return the previous save number if positive, or <code>0</code>
+ * if never saved before
+ * @see ISaveParticipant#rollback(ISaveContext)
+ */
+ public int getPreviousSaveNumber();
+
+ /**
+ * If the current save is a project save, this method returns the project
+ * being saved.
+ *
+ * @return the project being saved or <code>null</code> if this is not
+ * project save
+ *
+ * @see #getKind()
+ */
+ public IProject getProject();
+
+ /**
+ * Returns the number for this save. This number is
+ * guaranteed to be <code>1</code> more than the
+ * previous save number.
+ * <p>
+ * This is the value to use when, for example, creating files
+ * in which a participant will save its data.
+ * </p>
+ *
+ * @return the save number
+ * @see ISaveParticipant#saving(ISaveContext)
+ */
+ public int getSaveNumber();
+
+ /**
+ * Returns the current location for the given file or
+ * <code>null</code> if none.
+ *
+ * @return the location of a given file or <code>null</code>
+ * @see #map(IPath, IPath)
+ * @see ISavedState#lookup(IPath)
+ */
+ public IPath lookup(IPath file);
+
+ /**
+ * Maps the given plug-in file to its real location. This method is intended to be used
+ * with <code>ISaveContext.getSaveNumber()</code> to map plug-in configuration
+ * file names to real locations.
+ * <p>
+ * For example, assume a plug-in has a configuration file named "config.properties".
+ * The map facility can be used to map that logical name onto a real
+ * name which is specific to a particular save (e.g., 10.config.properties,
+ * where 10 is the current save number). The paths specified here should
+ * always be relative to the plug-in state location for the plug-in saving the state.
+ * </p>
+ * <p>
+ * Each save participant must manage the deletion of its old state files. Old state files
+ * can be discovered using <code>getPreviousSaveNumber</code> or by using
+ * <code>getFiles</code> to discover the current files and comparing that to the
+ * list of files on disk.
+ * </p>
+ * @param file the logical name of the participant's data file
+ * @param location the real (i.e., filesystem) name by which the file should be known
+ * for this save, or <code>null</code> to remove the entry
+ * @see #lookup(IPath)
+ * @see #getSaveNumber()
+ * @see #needSaveNumber()
+ * @see ISavedState#lookup(IPath)
+ */
+ public void map(IPath file, IPath location);
+
+ /**
+ * Indicates that the saved workspace tree should be remembered so that a delta
+ * will be available in a subsequent session when the plug-in re-registers
+ * to participate in saves. If this method is not called, no resource delta will
+ * be made available. This facility is not available for marker deltas.
+ * Plug-ins must assume that all markers may have changed when they are activated.
+ * <p>
+ * Note that this is orthogonal to <code>needSaveNumber</code>. That is,
+ * one can ask for a delta regardless of whether or not one is an active participant.
+ * </p>
+ * <p>
+ * Note that deltas are not guaranteed to be saved even if saving is requested.
+ * Deltas cannot be supplied where the previous state is too old or has become invalid.
+ * </p>
+ * <p>
+ * This method is only valid for full saves. It is ignored during snapshots
+ * or project saves.
+ * </p>
+ *
+ * @see IWorkspace#addSaveParticipant(org.eclipse.core.runtime.Plugin, ISaveParticipant)
+ * @see ISavedState#processResourceChangeEvents(IResourceChangeListener)
+ */
+ public void needDelta();
+
+ /**
+ * Indicates that this participant has actively participated in this save.
+ * If the save is successful, the current save number will be remembered;
+ * this save number will be the previous save number for subsequent saves
+ * until the participant again actively participates.
+ * <p>
+ * If this method is not called, the plug-in is not deemed to be an active
+ * participant in this save.
+ * </p>
+ * <p>
+ * Note that this is orthogonal to <code>needDelta</code>. That is,
+ * one can be an active participant whether or not one asks for a delta.
+ * </p>
+ *
+ * @see IWorkspace#addSaveParticipant(org.eclipse.core.runtime.Plugin, ISaveParticipant)
+ * @see ISavedState#getSaveNumber()
+ */
+ public void needSaveNumber();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ISaveParticipant.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.EventListener;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A participant in the saving of the workspace.
+ * <p>
+ * Plug-ins implement this interface and register to participate
+ * in workspace save operations.
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IWorkspace#save(boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+public interface ISaveParticipant extends EventListener {
+ /**
+ * Tells this participant that the workspace save operation is now
+ * complete and it is free to go about its normal business.
+ * Exceptions are not expected to be thrown at this point, so they
+ * should be handled internally.
+ * <p>
+ * Note: This method is called by the platform; it is not intended
+ * to be called directly by clients.
+ * </p>
+ *
+ * @param context the save context object
+ */
+ public void doneSaving(ISaveContext context);
+
+ /**
+ * Tells this participant that the workspace is about to be
+ * saved. In preparation, the participant is expected to suspend
+ * its normal operation until further notice. <code>saving</code>
+ * will be next, followed by either <code>doneSaving</code>
+ * or <code>rollback</code> depending on whether the workspace
+ * save was successful.
+ * <p>
+ * Note: This method is called by the platform; it is not intended
+ * to be called directly by clients.
+ * </p>
+ *
+ * @param context the save context object
+ * @exception CoreException if this method fails to snapshot
+ * the state of this workspace
+ */
+ public void prepareToSave(ISaveContext context) throws CoreException;
+
+ /**
+ * Tells this participant to rollback its important state.
+ * The context's previous state number indicates what it was prior
+ * to the failed save.
+ * Exceptions are not expected to be thrown at this point, so they
+ * should be handled internally.
+ * <p>
+ * Note: This method is called by the platform; it is not intended
+ * to be called directly by clients.
+ * </p>
+ *
+ * @param context the save context object
+ * @see ISaveContext#getPreviousSaveNumber()
+ */
+ public void rollback(ISaveContext context);
+
+ /**
+ * Tells this participant to save its important state because
+ * the workspace is being saved, as described in the supplied
+ * save context.
+ * <p>
+ * Note: This method is called by the platform; it is not intended
+ * to be called directly by clients.
+ * </p>
+ * <p>
+ * The basic contract for this method is the same for full saves,
+ * snapshots and project saves: the participant must absolutely guarantee that any
+ * important user data it has gathered will not be irrecoverably lost
+ * in the event of a crash. The only difference is in the space-time
+ * tradeoffs that the participant should make.
+ * <ul>
+ * <li>Full saves: the participant is
+ * encouraged to save additional non-essential information that will aid
+ * it in retaining user state and configuration information and quickly getting
+ * back in sync with the state of the platform at a later point.
+ * </li>
+ * <li>Snapshots: the participant is discouraged from saving non-essential
+ * information that could be recomputed in the unlikely event of a crash.
+ * This lifecycle event will happen often and participant actions should take
+ * an absolute minimum of time.
+ * </li>
+ * <li>Project saves: the participant should only save project related data.
+ * It is discouraged from saving non-essential information that could be recomputed
+ * in the unlikely event of a crash.
+ * </li>
+ * </ul>
+ * For instance, the Java IDE gathers various user preferences and would want to
+ * make sure that the current settings are safe and sound after a
+ * <code>save</code> (if not saved immediately).
+ * The Java IDE would likely save computed image builder state on full saves,
+ * because this would allow the Java IDE to be restarted later and not
+ * have to recompile the world at that time. On the other hand, the Java
+ * IDE would not save the image builder state on a snapshot because
+ * that information is non-essential; in the unlikely event of a crash,
+ * the image should be rebuilt either from scratch or from the last saved
+ * state.
+ * </p>
+ * <p>
+ * The following snippet shows how a plug-in participant would write
+ * its important state to a file whose name is based on the save
+ * number for this save operation.
+ * <pre>
+ * Plugin plugin = ...; // known
+ * int saveNumber = context.getSaveNumber();
+ * String saveFileName = "save-" + Integer.toString(saveNumber);
+ * File f = plugin.getStateLocation().append(saveFileName).toFile();
+ * plugin.writeImportantState(f);
+ * context.map(new Path("save"), new Path(saveFileName));
+ * context.needSaveNumber();
+ * context.needDelta(); // optional
+ * </pre>
+ * When the plug-in is reactivated in a subsequent workspace session,
+ * it needs to re-register to participate in workspace saves. When it
+ * does so, it is handed back key information about what state it had last
+ * saved. If it's interested, it can also ask for a resource delta
+ * describing all resource changes that have happened since then, if this
+ * information is still available.
+ * The following snippet shows what a participant plug-in would
+ * need to do if and when it is reactivated:
+ * <pre>
+ * IWorkspace ws = ...; // known
+ * Plugin plugin = ...; // known
+ * ISaveParticipant saver = ...; // known
+ * ISavedState ss = ws.addSaveParticipant(plugin, saver);
+ * if (ss == null) {
+ * // activate for very first time
+ * plugin.buildState();
+ * } else {
+ * String saveFileName = ss.lookup(new Path("save"));
+ * File f = plugin.getStateLocation().append(saveFileName).toFile();
+ * plugin.readImportantState(f);
+ * IResourceChangeListener listener = new IResourceChangeListener() {
+ * public void resourceChanged(IResourceChangeEvent event) {
+ * IResourceDelta delta = event.getDelta();
+ * if (delta != null) {
+ * // fast reactivation using delta
+ * plugin.updateState(delta);
+ * } else {
+ * // slower reactivation without benefit of delta
+ * plugin.rebuildState();
+ * }
+ * };
+ * ss.processResourceChangeEvents(listener);
+ * }
+ * </pre>
+ * </p>
+ *
+ * @param context the save context object
+ * @exception CoreException if this method fails
+ * @see ISaveContext#getSaveNumber()
+ */
+ public void saving(ISaveContext context) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ISavedState.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A data structure returned by {@link IWorkspace#addSaveParticipant(org.eclipse.core.runtime.Plugin, ISaveParticipant)}
+ * containing a save number and an optional resource delta.
+ *
+ * @see IWorkspace#addSaveParticipant(org.eclipse.core.runtime.Plugin, ISaveParticipant)
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ISavedState {
+ /**
+ * Returns the files mapped with the {@link ISaveContext#map(IPath, IPath)}
+ * facility. Returns an empty array if there are no mapped files.
+ *
+ * @return the files currently mapped by the participant
+ *
+ * @see #lookup(IPath)
+ * @see ISaveContext#map(IPath, IPath)
+ */
+ public IPath[] getFiles();
+
+ /**
+ * Returns the save number for the save participant.
+ * This is the save number of the last successful save in which the plug-in
+ * <b>actively</b> participated, or <code>0</code> if the plug-in has
+ * never actively participated in a successful save.
+ *
+ * @return the save number
+ */
+ public int getSaveNumber();
+
+ /**
+ * Returns the mapped location associated with the given path
+ * or <code>null</code> if none.
+ *
+ * @return the mapped location of a given path
+ * @see #getFiles()
+ * @see ISaveContext#map(IPath, IPath)
+ */
+ public IPath lookup(IPath file);
+
+ /**
+ * Used to receive notification of changes that might have happened
+ * while this plug-in was not active. The listener receives notifications of changes to
+ * the workspace resource tree since the time this state was saved. After this
+ * method is run, the delta is forgotten. Subsequent calls to this method
+ * will have no effect.
+ * <p>
+ * No notification is received in the following cases:
+ * <ul>
+ * <li>if a saved state was never recorded ({@link ISaveContext#needDelta()}</code>
+ * was not called) </li>
+ * <li>a saved state has since been forgotten (using {@link IWorkspace#forgetSavedTree(String)}) </li>
+ * <li>a saved state has been deemed too old or has become invalid</li>
+ * </ul>
+ * <p>
+ * All clients should have a contingency plan in place in case
+ * a changes are not available (the case should be very similar
+ * to the first time a plug-in is activated, and only has the
+ * current state of the workspace to work from).
+ * </p>
+ * <p>
+ * The supplied event is of type {@link IResourceChangeEvent#POST_BUILD}
+ * and contains the delta detailing changes since this plug-in last participated
+ * in a save. This event object (and the resource delta within it) is valid only
+ * for the duration of the invocation of this method.
+ * </p>
+ *
+ * @param listener the listener
+ * @see ISaveContext#needDelta()
+ * @see IResourceChangeListener
+ */
+ public void processResourceChangeEvents(IResourceChangeListener listener);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IStorage.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.io.InputStream;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A storage object represents a set of bytes which can be accessed.
+ * These may be in the form of an <code>IFile</code> or <code>IFileState</code>
+ * or any other object supplied by user code. The main role of an <code>IStorage</code>
+ * is to provide a uniform API for access to, and presentation of, its content.
+ * <p>
+ * Storage objects implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * </p>
+ */
+public interface IStorage extends IAdaptable {
+ /**
+ * Returns an open input stream on the contents of this storage.
+ * The caller is responsible for closing the stream when finished.
+ *
+ * @return an input stream containing the contents of this storage
+ * @exception CoreException if the contents of this storage could
+ * not be accessed. See any refinements for more information.
+ */
+ public InputStream getContents() throws CoreException;
+
+ /**
+ * Returns the full path of this storage. The returned value
+ * depends on the implementor/extender. A storage need not
+ * have a path.
+ *
+ * @return the path related to the data represented by this storage or
+ * <code>null</code> if none.
+ */
+ public IPath getFullPath();
+
+ /**
+ * Returns the name of this storage.
+ * The name of a storage is synonymous with the last segment
+ * of its full path though if the storage does not have a path,
+ * it may still have a name.
+ *
+ * @return the name of the data represented by this storage,
+ * or <code>null</code> if this storage has no name
+ * @see #getFullPath()
+ */
+ public String getName();
+
+ /**
+ * Returns whether this storage is read-only.
+ *
+ * @return <code>true</code> if this storage is read-only
+ */
+ public boolean isReadOnly();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ISynchronizer.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * A synchronizer which maintains a list of registered partners and, on behalf of
+ * each partner, it keeps resource level synchronization information
+ * (a byte array). Sync info is saved only when the workspace is saved.
+ *
+ * @see IWorkspace#getSynchronizer()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ISynchronizer {
+ /**
+ * Visits the given resource and its descendents with the specified visitor
+ * if sync information for the given sync partner is found on the resource. If
+ * sync information for the given sync partner is not found on the resource,
+ * still visit the children of the resource if the depth specifies to do so.
+ *
+ * @param partner the sync partner name
+ * @param start the parent resource to start the visitation
+ * @param visitor the visitor to use when visiting the resources
+ * @param depth the depth to which members of this resource should be
+ * visited. One of <code>IResource.DEPTH_ZERO</code>, <code>IResource.DEPTH_ONE</code>,
+ * or <code>IResource.DEPTH_INFINITE</code>.
+ * @exception CoreException if this operation fails. Reasons include:
+ * <ul>
+ * <li>The resource does not exist.</li>
+ * <li><code>IResourceStatus.PARTNER_NOT_REGISTERED</code>
+ The sync partner is not registered.</li>
+ * </ul>
+ */
+ public void accept(QualifiedName partner, IResource start, IResourceVisitor visitor, int depth) throws CoreException;
+
+ /**
+ * Adds the named synchronization partner to this synchronizer's
+ * registry of partners. Once a partner's name has been registered, sync
+ * information can be set and retrieve on resources relative to the name.
+ * Adding a sync partner multiple times has no effect.
+ *
+ * @param partner the partner name to register
+ * @see #remove(QualifiedName)
+ */
+ public void add(QualifiedName partner);
+
+ /**
+ * Discards the named partner's synchronization information
+ * associated with the specified resource and its descendents to the
+ * specified depth.
+ *
+ * @param partner the sync partner name
+ * @param resource the resource
+ * @param depth the depth to which members of this resource should be
+ * visited. One of <code>IResource.DEPTH_ZERO</code>, <code>IResource.DEPTH_ONE</code>,
+ * or <code>IResource.DEPTH_INFINITE</code>.
+ * @exception CoreException if this operation fails. Reasons include:
+ * <ul>
+ * <li>The resource does not exist.</li>
+ * <li><code>IResourceStatus.PARTNER_NOT_REGISTERED</code>
+ The sync partner is not registered.</li>
+ * </ul>
+ */
+ public void flushSyncInfo(QualifiedName partner, IResource resource, int depth) throws CoreException;
+
+ /**
+ * Returns a list of synchronization partner names currently registered
+ * with this synchronizer. Returns an empty array if there are no
+ * registered sync partners.
+ *
+ * @return a list of sync partner names
+ */
+ public QualifiedName[] getPartners();
+
+ /**
+ * Returns the named sync partner's synchronization information for the given resource.
+ * Returns <code>null</code> if no information is found.
+ *
+ * @param partner the sync partner name
+ * @param resource the resource
+ * @return the synchronization information, or <code>null</code> if none
+ * @exception CoreException if this operation fails. Reasons include:
+ * <ul>
+ * <li><code>IResourceStatus.PARTNER_NOT_REGISTERED</code>
+ The sync partner is not registered.</li>
+ * </ul>
+ */
+ public byte[] getSyncInfo(QualifiedName partner, IResource resource) throws CoreException;
+
+ /**
+ * Removes the named synchronization partner from this synchronizer's
+ * registry. Does nothing if the partner is not registered.
+ * This discards all sync information for the defunct partner.
+ * After a partner has been unregistered, sync information for it can no
+ * longer be stored on resources.
+ *
+ * @param partner the partner name to remove from the registry
+ * @see #add(QualifiedName)
+ */
+ public void remove(QualifiedName partner);
+
+ /**
+ * Sets the named sync partner's synchronization information for the given resource.
+ * If the given info is non-<code>null</code> and the resource neither exists
+ * nor is a phantom, this method creates a phantom resource to hang on to the info.
+ * If the given info is <code>null</code>, any sync info for the resource stored by the
+ * given sync partner is discarded; in some cases, this may result in the deletion
+ * of a phantom resource if there is no more sync info to maintain for that resource.
+ * <p>
+ * Sync information is not stored on the workspace root. Attempts to set information
+ * on the root will be ignored.
+ * </p>
+ *
+ * @param partner the sync partner name
+ * @param resource the resource
+ * @param info the synchronization information, or <code>null</code>
+ * @exception CoreException if this operation fails. Reasons include:
+ * <ul>
+ * <li><code>IResourceStatus.PARTNER_NOT_REGISTERED</code>
+ The sync partner is not registered.</li>
+ * </ul>
+ */
+ public void setSyncInfo(QualifiedName partner, IResource resource, byte[] info) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspace.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,1595 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Incorporated - loadProjectDescription(InputStream)
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Map;
+import org.eclipse.core.resources.team.FileModificationValidationContext;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.LockListener;
+
+/**
+ * Workspaces are the basis for Eclipse Platform resource management. There is
+ * only one workspace per running platform. All resources exist in the context
+ * of this workspace.
+ * <p>
+ * A workspace corresponds closely to discreet areas in the local file system.
+ * Each project in a workspace maps onto a specific area of the file system. The
+ * folders and files within a project map directly onto the corresponding
+ * directories and files in the file system. One sub-directory, the workspace
+ * metadata area, contains internal information about the workspace and its
+ * resources. This metadata area should be accessed only by the Platform or via
+ * Platform API calls.
+ * </p>
+ * <p>
+ * Workspaces add value over using the file system directly in that they allow
+ * for comprehensive change tracking (through <code>IResourceDelta</code> s),
+ * various forms of resource metadata (e.g., markers and properties) as well as
+ * support for managing application/tool state (e.g., saving and restoring).
+ * </p>
+ * <p>
+ * The workspace as a whole is thread safe and allows one writer concurrent with
+ * multiple readers. It also supports mechanisms for saving and snapshooting the
+ * current resource state.
+ * </p>
+ * <p>
+ * The workspace is provided by the Resources plug-in and is automatically
+ * created when that plug-in is activated. The default workspace data area
+ * (i.e., where its resources are stored) overlap exactly with the platform's
+ * data area. That is, by default, the workspace's projects are found directly
+ * in the platform's data area. Individual project locations can be specified
+ * explicitly.
+ * </p>
+ * <p>
+ * The workspace resource namespace is always case-sensitive and
+ * case-preserving. Thus the workspace allows multiple sibling resources to exist
+ * with names that differ only in case. The workspace also imposes no
+ * restrictions on valid characters in resource names, the length of resource names,
+ * or the size of resources on disk. In situations where one or more resources
+ * are stored in a file system that is not case-sensitive, or that imposes restrictions
+ * on resource names, any failure to store or retrieve those resources will
+ * be propagated back to the caller of workspace API.
+ * </p>
+ * <p>
+ * Workspaces implement the <code>IAdaptable</code> interface; extensions are
+ * managed by the platform's adapter manager.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IWorkspace extends IAdaptable {
+ /**
+ * flag constant (bit mask value 1) indicating that resource change
+ * notifications should be avoided during the invocation of a compound
+ * resource changing operation.
+ *
+ * @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)
+ * @since 3.0
+ */
+ public static final int AVOID_UPDATE = 1;
+
+ /**
+ * Constant that can be passed to {@link #validateEdit(org.eclipse.core.resources.IFile[], Object)}
+ * to indicate that the caller does not have access to a UI context but would still
+ * like to have UI-based validation if possible.
+ * @since 3.3
+ * @see #validateEdit(IFile[], Object)
+ */
+ public static final Object VALIDATE_PROMPT = FileModificationValidationContext.VALIDATE_PROMPT;
+
+ /**
+ * The name of the IWorkspace OSGi service (value "org.eclipse.core.resources.IWorkspace").
+ * @since 3.5
+ */
+ public static final String SERVICE_NAME = IWorkspace.class.getName();
+
+ /**
+ * Adds the given listener for resource change events to this workspace. Has
+ * no effect if an identical listener is already registered.
+ * <p>
+ * This method is equivalent to:
+ *
+ * <pre>
+ * addResourceChangeListener(listener, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE);
+ * </pre>
+ *
+ * </p>
+ *
+ * @param listener the listener
+ * @see IResourceChangeListener
+ * @see IResourceChangeEvent
+ * @see #addResourceChangeListener(IResourceChangeListener, int)
+ * @see #removeResourceChangeListener(IResourceChangeListener)
+ */
+ public void addResourceChangeListener(IResourceChangeListener listener);
+
+ /**
+ * Adds the given listener for the specified resource change events to this
+ * workspace. Has no effect if an identical listener is already registered
+ * for these events. After completion of this method, the given listener
+ * will be registered for exactly the specified events. If they were
+ * previously registered for other events, they will be de-registered.
+ * <p>
+ * Once registered, a listener starts receiving notification of changes to
+ * resources in the workspace. The resource deltas in the resource change
+ * event are rooted at the workspace root. Most resource change
+ * notifications occur well after the fact; the exception is
+ * pre-notification of impending project closures and deletions. The
+ * listener continues to receive notifications until it is replaced or
+ * removed.
+ * </p>
+ * <p>
+ * Listeners can listen for several types of event as defined in
+ * <code>IResourceChangeEvent</code>. Clients are free to register for
+ * any number of event types however if they register for more than one, it
+ * is their responsibility to ensure they correctly handle the case where
+ * the same resource change shows up in multiple notifications. Clients are
+ * guaranteed to receive only the events for which they are registered.
+ * </p>
+ *
+ * @param listener the listener
+ * @param eventMask the bit-wise OR of all event types of interest to the
+ * listener
+ * @see IResourceChangeListener
+ * @see IResourceChangeEvent
+ * @see #removeResourceChangeListener(IResourceChangeListener)
+ */
+ public void addResourceChangeListener(IResourceChangeListener listener, int eventMask);
+
+ /**
+ * Registers the given plug-in's workspace save participant, and returns an
+ * object describing the workspace state at the time of the last save in
+ * which the plug-in participated.
+ * <p>
+ * Once registered, the workspace save participant will actively participate
+ * in the saving of this workspace.
+ * </p>
+ *
+ * @param plugin the plug-in
+ * @param participant the participant
+ * @return the last saved state in which the plug-in participated, or
+ * <code>null</code> if the plug-in has not participated before
+ * @exception CoreException if the method fails to add the participant.
+ * Reasons include:
+ * <ul>
+ * <li>The previous state could not be recovered.</li>
+ * </ul>
+ * @see ISaveParticipant
+ * @see #removeSaveParticipant(Plugin)
+ */
+ public ISavedState addSaveParticipant(Plugin plugin, ISaveParticipant participant) throws CoreException;
+
+ /**
+ * Builds all projects in this workspace. Projects are built in the order
+ * specified in this workspace's description. Projects not mentioned in the
+ * order or for which the order cannot be determined are built in an
+ * undefined order after all other projects have been built. If no order is
+ * specified, the workspace computes an order determined by project
+ * references.
+ * <p>
+ * This method may change resources; these changes will be reported in a
+ * subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param kind the kind of build being requested. Valid values are
+ * <ul>
+ * <li>{@link IncrementalProjectBuilder#FULL_BUILD}- indicates a full build.</li>
+ * <li>{@link IncrementalProjectBuilder#INCREMENTAL_BUILD}- indicates a incremental build.</li>
+ * <li>{@link IncrementalProjectBuilder#CLEAN_BUILD}- indicates a clean request. Clean does
+ * not actually build anything, but rather discards all problems and build states.</li>
+ * </ul>
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if the build fails.
+ * The status contained in the exception may be a generic {@link IResourceStatus#BUILD_FAILED}
+ * code, but it could also be any other status code; it might
+ * also be a {@link MultiStatus}.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ *
+ * @see IProject#build(int, IProgressMonitor)
+ * @see #computeProjectOrder(IProject[])
+ * @see IncrementalProjectBuilder#FULL_BUILD
+ * @see IncrementalProjectBuilder#INCREMENTAL_BUILD
+ * @see IncrementalProjectBuilder#CLEAN_BUILD
+ * @see IResourceRuleFactory#buildRule()
+ */
+ public void build(int kind, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Checkpoints the operation currently in progress. This method is used in
+ * the middle of a group of operations to force a background autobuild (if
+ * the build argument is true) and send an interim notification of resource
+ * change events.
+ * <p>
+ * When invoked in the dynamic scope of a call to the
+ * <code>IWorkspace.run</code> method, this method reports a single
+ * resource change event describing the net effect of all changes done to
+ * resources since the last round of notifications. When the outermost
+ * <code>run</code> method eventually completes, it will do another
+ * autobuild (if enabled) and report the resource changes made after this
+ * call.
+ * </p>
+ * <p>
+ * This method has no effect if invoked outside the dynamic scope of a call
+ * to the <code>IWorkspace.run</code> method.
+ * </p>
+ * <p>
+ * This method should be used under controlled circumstance (e.g., to break
+ * up extremely long-running operations).
+ * </p>
+ *
+ * @param build whether or not to run a build
+ * @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)
+ */
+ public void checkpoint(boolean build);
+
+ /**
+ * Returns the prerequisite ordering of the given projects. The computation
+ * is done by interpreting project references as dependency relationships.
+ * For example if A references B and C, and C references B, this method,
+ * given the list A, B, C will return the order B, C, A. That is, projects
+ * with no dependencies are listed first.
+ * <p>
+ * The return value is a two element array of project arrays. The first
+ * project array is the list of projects which could be sorted (as outlined
+ * above). The second element of the return value is an array of the
+ * projects which are ambiguously ordered (e.g., they are part of a cycle).
+ * </p>
+ * <p>
+ * Cycles and ambiguities are handled by elimination. Projects involved in
+ * cycles are simply cut out of the ordered list and returned in an
+ * undefined order. Closed and non-existent projects are ignored and do not
+ * appear in the returned value at all.
+ * </p>
+ *
+ * @param projects the projects to order
+ * @return the projects in sorted order and a list of projects which could
+ * not be ordered
+ * @deprecated Replaced by <code>IWorkspace.computeProjectOrder</code>,
+ * which produces a more usable result when there are cycles in project
+ * reference graph.
+ */
+ public IProject[][] computePrerequisiteOrder(IProject[] projects);
+
+ /**
+ * Data structure for holding the multi-part outcome of
+ * <code>IWorkspace.computeProjectOrder</code>.
+ * <p>
+ * This class is not intended to be instantiated by clients.
+ * </p>
+ *
+ * @see IWorkspace#computeProjectOrder(IProject[])
+ * @since 2.1
+ */
+ public final class ProjectOrder {
+ /**
+ * Creates an instance with the given values.
+ * <p>
+ * This class is not intended to be instantiated by clients.
+ * </p>
+ *
+ * @param projects initial value of <code>projects</code> field
+ * @param hasCycles initial value of <code>hasCycles</code> field
+ * @param knots initial value of <code>knots</code> field
+ */
+ public ProjectOrder(IProject[] projects, boolean hasCycles, IProject[][] knots) {
+ this.projects = projects;
+ this.hasCycles = hasCycles;
+ this.knots = knots;
+ }
+
+ /**
+ * A list of projects ordered so as to honor the project reference
+ * relationships between these projects wherever possible. The elements
+ * are a subset of the ones passed as the <code>projects</code>
+ * parameter to <code>IWorkspace.computeProjectOrder</code>, where
+ * inaccessible (closed or non-existent) projects have been omitted.
+ */
+ public IProject[] projects;
+ /**
+ * Indicates whether any of the accessible projects in
+ * <code>projects</code> are involved in non-trivial cycles.
+ * <code>true</code> if the project reference graph contains at least
+ * one cycle involving two or more of the projects in
+ * <code>projects</code>, and <code>false</code> if none of the
+ * projects in <code>projects</code> are involved in cycles.
+ */
+ public boolean hasCycles;
+ /**
+ * A list of knots in the project reference graph. This list is empty if
+ * the project reference graph does not contain cycles. If the project
+ * reference graph contains cycles, each element is a knot of two or
+ * more accessible projects from <code>projects</code> that are
+ * involved in a cycle of mutually dependent references.
+ */
+ public IProject[][] knots;
+ }
+
+ /**
+ * Computes a total ordering of the given projects based on both static and
+ * dynamic project references. If an existing and open project P references
+ * another existing and open project Q also included in the list, then Q
+ * should come before P in the resulting ordering. Closed and non-existent
+ * projects are ignored, and will not appear in the result. References to
+ * non-existent or closed projects are also ignored, as are any
+ * self-references. The total ordering is always consistent with the global
+ * total ordering of all open projects in the workspace.
+ * <p>
+ * When there are choices, the choice is made in a reasonably stable way.
+ * For example, given an arbitrary choice between two projects, the one with
+ * the lower collating project name is usually selected.
+ * </p>
+ * <p>
+ * When the project reference graph contains cyclic references, it is
+ * impossible to honor all of the relationships. In this case, the result
+ * ignores as few relationships as possible. For example, if P2 references
+ * P1, P4 references P3, and P2 and P3 reference each other, then exactly
+ * one of the relationships between P2 and P3 will have to be ignored. The
+ * outcome will be either [P1, P2, P3, P4] or [P1, P3, P2, P4]. The result
+ * also contains complete details of any cycles present.
+ * </p>
+ * <p>
+ * This method is time-consuming and should not be called unnecessarily.
+ * There are a very limited set of changes to a workspace that could affect
+ * the outcome: creating, renaming, or deleting a project; opening or
+ * closing a project; adding or removing a project reference.
+ * </p>
+ *
+ * @param projects the projects to order
+ * @return result describing the project order
+ * @since 2.1
+ */
+ public ProjectOrder computeProjectOrder(IProject[] projects);
+
+ /**
+ * Copies the given sibling resources so that they are located as members of
+ * the resource at the given path; the names of the copies are the same as
+ * the corresponding originals.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ *
+ * <pre>
+ * copy(resources, destination, (force ? IResource.FORCE : IResource.NONE), monitor);
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event that will include an indication that the
+ * resources have been added to the new parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param resources the resources to copy
+ * @param destination the destination container path
+ * @param force a flag controlling whether resources that are not in sync
+ * with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a status object with code <code>OK</code> if there were no
+ * problems; otherwise a description (possibly a multi-status) consisting of
+ * low-severity warnings or informational messages
+ * @exception CoreException if the method fails to copy some resources. The
+ * status contained in the exception may be a multi-status indicating where
+ * the individual failures occurred.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #copy(IResource[],IPath,int,IProgressMonitor)
+ */
+ public IStatus copy(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Copies the given sibling resources so that they are located as members of
+ * the resource at the given path; the names of the copies are the same as
+ * the corresponding originals.
+ * <p>
+ * This method can be expressed as a series of calls to
+ * <code>IResource.copy(IPath,int,IProgressMonitor)</code>, with "best
+ * effort" semantics:
+ * <ul>
+ * <li>Resources are copied in the order specified, using the given update
+ * flags.</li>
+ * <li>Duplicate resources are only copied once.</li>
+ * <li>The method fails if the resources are not all siblings.</li>
+ * <li>The failure of an individual copy does not necessarily prevent the
+ * method from attempting to copy other resources.</li>
+ * <li>The method fails if there are projects among the resources.</li>
+ * <li>The method fails if the path of the resources is a prefix of the
+ * destination path.</li>
+ * <li>This method also fails if one or more of the individual resource
+ * copy steps fails.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * After successful completion, corresponding new resources will now exist
+ * as members of the resource at the given path.
+ * </p>
+ * <p>
+ * The supplied path may be absolute or relative. Absolute paths fully
+ * specify the new location for the resource, including its project.
+ * Relative paths are considered to be relative to the container of the
+ * resources being copied. A trailing separator is ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event that will include an indication that the
+ * resources have been added to the new parent.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param resources the resources to copy
+ * @param destination the destination container path
+ * @param updateFlags bit-wise or of update flag constants
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a status object with code <code>OK</code> if there were no
+ * problems; otherwise a description (possibly a multi-status) consisting of
+ * low-severity warnings or informational messages
+ * @exception CoreException if the method fails to copy some resources. The
+ * status contained in the exception may be a multi-status indicating where
+ * the individual failures occurred. Reasons include:
+ * <ul>
+ * <li>One of the resources does not exist.</li>
+ * <li>The resources are not siblings.</li>
+ * <li>One of the resources, or one of its descendents, is not local.</li>
+ * <li>The resource corresponding to the destination path does not exist.
+ * </li>
+ * <li>The resource corresponding to the parent destination path is a
+ * closed project.</li>
+ * <li>A corresponding target resource does exist.</li>
+ * <li>A resource of a different type exists at the target path.</li>
+ * <li>One of the resources is a project.</li>
+ * <li>The path of one of the resources is a prefix of the destination
+ * path.</li>
+ * <li>One of the resources, or one of its descendents, is out of sync with
+ * the local file system and <code>FORCE</code> is not specified.</li>
+ * <li>Resource changes are disallowed during certain types of resource
+ * change event notification. See <code>IResourceChangeEvent</code> for
+ * more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#copy(IPath,int,IProgressMonitor)
+ * @see IResourceRuleFactory#copyRule(IResource, IResource)
+ * @since 2.0
+ */
+ public IStatus copy(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes the given resources.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ *
+ * <pre>
+ * delete(resources, IResource.KEEP_HISTORY | (force ? IResource.FORCE : IResource.NONE), monitor);
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param resources the resources to delete
+ * @param force a flag controlling whether resources that are not in sync
+ * with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return status with code <code>OK</code> if there were no problems;
+ * otherwise a description (possibly a multi-status) consisting of
+ * low-severity warnings or informational messages
+ * @exception CoreException if the method fails to delete some resource. The
+ * status contained in the exception is a multi-status indicating where the
+ * individual failures occurred.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #delete(IResource[],int,IProgressMonitor)
+ */
+ public IStatus delete(IResource[] resources, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Deletes the given resources.
+ * <p>
+ * This method can be expressed as a series of calls to
+ * <code>IResource.delete(int,IProgressMonitor)</code>.
+ * </p>
+ * <p>
+ * The semantics of multiple deletion are:
+ * <ul>
+ * <li>Resources are deleted in the order presented, using the given update
+ * flags.</li>
+ * <li>Resources that do not exist are ignored.</li>
+ * <li>An individual deletion fails if the resource still exists
+ * afterwards.</li>
+ * <li>The failure of an individual deletion does not prevent the method
+ * from attempting to delete other resources.</li>
+ * <li>This method fails if one or more of the individual resource
+ * deletions fails; that is, if at least one of the resources in the list
+ * still exists at the end of this method.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param resources the resources to delete
+ * @param updateFlags bit-wise or of update flag constants
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return status with code <code>OK</code> if there were no problems;
+ * otherwise a description (possibly a multi-status) consisting of
+ * low-severity warnings or informational messages
+ * @exception CoreException if the method fails to delete some resource. The
+ * status contained in the exception is a multi-status indicating where the
+ * individual failures occurred.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int,IProgressMonitor)
+ * @see IResourceRuleFactory#deleteRule(IResource)
+ * @since 2.0
+ */
+ public IStatus delete(IResource[] resources, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Removes the given markers from the resources with which they are
+ * associated. Markers that do not exist are ignored.
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event.
+ * </p>
+ *
+ * @param markers the markers to remove
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li>Resource changes are disallowed during certain types of resource
+ * change event notification. See <code>IResourceChangeEvent</code> for
+ * more details.</li>
+ * </ul>
+ * @see IResourceRuleFactory#markerRule(IResource)
+ */
+ public void deleteMarkers(IMarker[] markers) throws CoreException;
+
+ /**
+ * Forgets any resource tree being saved for the plug-in with the given
+ * name. If the plug-in id is <code>null</code>, all trees are forgotten.
+ * <p>
+ * Clients should not call this method unless they have a reason to do so. A
+ * plug-in which uses <code>ISaveContext.needDelta</code> in the process
+ * of a save indicates that it would like to be fed the resource delta the
+ * next time it is reactivated. If a plug-in never gets reactivated (or if
+ * it fails to successfully register to participate in workspace saves), the
+ * workspace nevertheless retains the necessary information to generate the
+ * resource delta if asked. This method allows such a long term leak to be
+ * plugged.
+ * </p>
+ *
+ * @param pluginId the unique identifier of the plug-in, or <code>null</code>
+ * @see ISaveContext#needDelta()
+ */
+ public void forgetSavedTree(String pluginId);
+
+ /**
+ * Returns all nature descriptors known to this workspace. Returns an empty
+ * array if there are no installed natures.
+ *
+ * @return the nature descriptors known to this workspace
+ * @since 2.0
+ */
+ public IProjectNatureDescriptor[] getNatureDescriptors();
+
+ /**
+ * Returns the nature descriptor with the given unique identifier, or
+ * <code>null</code> if there is no such nature.
+ *
+ * @param natureId the nature extension identifier (e.g.
+ * <code>"com.example.coolNature"</code>).
+ * @return the nature descriptor, or <code>null</code>
+ * @since 2.0
+ */
+ public IProjectNatureDescriptor getNatureDescriptor(String natureId);
+
+ /**
+ * Finds all dangling project references in this workspace. Projects which
+ * are not open are ignored. Returns a map with one entry for each open
+ * project in the workspace that has at least one dangling project
+ * reference; the value of the entry is an array of projects which are
+ * referenced by that project but do not exist in the workspace. Returns an
+ * empty Map if there are no projects in the workspace.
+ *
+ * @return a map (key type: <code>IProject</code>, value type:
+ * <code>IProject[]</code>) from project to dangling project references
+ */
+ public Map getDanglingReferences();
+
+ /**
+ * Returns the workspace description. This object is responsible for
+ * defining workspace preferences. The returned value is a modifiable copy
+ * but changes are not automatically applied to the workspace. In order to
+ * changes take effect, <code>IWorkspace.setDescription</code> needs to be
+ * called. The workspace description values are store in the preference
+ * store.
+ *
+ * @return the workspace description
+ * @see #setDescription(IWorkspaceDescription)
+ */
+ public IWorkspaceDescription getDescription();
+
+ /**
+ * Returns the root resource of this workspace.
+ *
+ * @return the workspace root
+ */
+ public IWorkspaceRoot getRoot();
+
+ /**
+ * Returns a factory for obtaining scheduling rules prior to modifying
+ * resources in the workspace.
+ *
+ * @see IResourceRuleFactory
+ * @return a resource rule factory
+ * @since 3.0
+ */
+ public IResourceRuleFactory getRuleFactory();
+
+ /**
+ * Returns the synchronizer for this workspace.
+ *
+ * @return the synchronizer
+ * @see ISynchronizer
+ */
+ public ISynchronizer getSynchronizer();
+
+ /**
+ * Returns whether this workspace performs autobuilds.
+ *
+ * @return <code>true</code> if autobuilding is on, <code>false</code>
+ * otherwise
+ */
+ public boolean isAutoBuilding();
+
+ /**
+ * Returns whether the workspace tree is currently locked. Resource changes
+ * are disallowed during certain types of resource change event
+ * notification. See <code>IResourceChangeEvent</code> for more details.
+ *
+ * @return boolean <code>true</code> if the workspace tree is locked,
+ * <code>false</code> otherwise
+ * @see IResourceChangeEvent
+ * @since 2.1
+ */
+ public boolean isTreeLocked();
+
+ /**
+ * Reads the project description file (".project") from the given location
+ * in the local file system. This object is useful for discovering the
+ * correct name for a project before importing it into the workspace.
+ * <p>
+ * The returned value is writeable.
+ * </p>
+ *
+ * @param projectDescriptionFile the path in the local file system of an
+ * existing project description file
+ * @return a new project description
+ * @exception CoreException if the operation failed. Reasons include:
+ * <ul>
+ * <li>The project description file does not exist.</li>
+ * <li>The file cannot be opened or read.</li>
+ * <li>The file cannot be parsed as a legal project description.</li>
+ * </li>
+ * @see #newProjectDescription(String)
+ * @see IProject#getDescription()
+ * @since 2.0
+ */
+ public IProjectDescription loadProjectDescription(IPath projectDescriptionFile) throws CoreException;
+
+ /**
+ * Reads the project description file (".project") from the given InputStream.
+ * This object will not attempt to set the location since the project may not
+ * have a valid location on the local file system.
+ * This object is useful for discovering the correct name for a project before
+ * importing it into the workspace.
+ * <p>
+ * The returned value is writeable.
+ * </p>
+ *
+ * @param projectDescriptionFile an InputStream pointing to an existing project
+ * description file
+ * @return a new project description
+ * @exception CoreException if the operation failed. Reasons include:
+ * <ul>
+ * <li>The stream could not be read.</li>
+ * <li>The stream does not contain a legal project description.</li>
+ * </li>
+ * @see #newProjectDescription(String)
+ * @see IProject#getDescription()
+ * @see IWorkspace#loadProjectDescription(IPath)
+ * @since 3.1
+ */
+ public IProjectDescription loadProjectDescription(InputStream projectDescriptionFile) throws CoreException;
+
+ /**
+ * Moves the given sibling resources so that they are located as members of
+ * the resource at the given path; the names of the new members are the
+ * same.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ *
+ * <pre>
+ * move(resources, destination, IResource.KEEP_HISTORY | (force ? IResource.FORCE : IResource.NONE), monitor);
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event that will include an indication that the
+ * resources have been removed from their parent and that corresponding
+ * resources have been added to the new parent. Additional information
+ * provided with resource delta shows that these additions and removals are
+ * pairwise related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param resources the resources to move
+ * @param destination the destination container path
+ * @param force a flag controlling whether resources that are not in sync
+ * with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return status with code <code>OK</code> if there were no problems;
+ * otherwise a description (possibly a multi-status) consisting of
+ * low-severity warnings or informational messages.
+ * @exception CoreException if the method fails to move some resources. The
+ * status contained in the exception may be a multi-status indicating where
+ * the individual failures occurred.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #move(IResource[],IPath,int,IProgressMonitor)
+ */
+ public IStatus move(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Moves the given sibling resources so that they are located as members of
+ * the resource at the given path; the names of the new members are the
+ * same.
+ * <p>
+ * This method can be expressed as a series of calls to
+ * <code>IResource.move</code>, with "best effort" semantics:
+ * <ul>
+ * <li>Resources are moved in the order specified.</li>
+ * <li>Duplicate resources are only moved once.</li>
+ * <li>The <code>force</code> flag has the same meaning as it does on the
+ * corresponding single-resource method.</li>
+ * <li>The method fails if the resources are not all siblings.</li>
+ * <li>The method fails the path of any of the resources is a prefix of the
+ * destination path.</li>
+ * <li>The failure of an individual move does not necessarily prevent the
+ * method from attempting to move other resources.</li>
+ * <li>This method also fails if one or more of the individual resource
+ * moves fails; that is, if at least one of the resources in the list still
+ * exists at the end of this method.</li>
+ * <li>History is kept for moved files. When projects are moved, no history
+ * is kept</li>
+ * </ul>
+ * </p>
+ * <p>
+ * After successful completion, the resources and descendents will no longer
+ * exist; but corresponding new resources will now exist as members of the
+ * resource at the given path.
+ * </p>
+ * <p>
+ * The supplied path may be absolute or relative. Absolute paths fully
+ * specify the new location for the resource, including its project.
+ * Relative paths are considered to be relative to the container of the
+ * resources being moved. A trailing separator is ignored.
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported in a
+ * subsequent resource change event that will include an indication that the
+ * resources have been removed from their parent and that corresponding
+ * resources have been added to the new parent. Additional information
+ * provided with resource delta shows that these additions and removals are
+ * pairwise related.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor.
+ * </p>
+ *
+ * @param resources the resources to move
+ * @param destination the destination container path
+ * @param updateFlags bit-wise or of update flag constants
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return status with code <code>OK</code> if there were no problems;
+ * otherwise a description (possibly a multi-status) consisting of
+ * low-severity warnings or informational messages.
+ * @exception CoreException if the method fails to move some resources. The
+ * status contained in the exception may be a multi-status indicating where
+ * the individual failures occurred. Reasons include:
+ * <ul>
+ * <li>One of the resources does not exist.</li>
+ * <li>The resources are not siblings.</li>
+ * <li>One of the resources, or one of its descendents, is not local.</li>
+ * <li>The resource corresponding to the destination path does not exist.
+ * </li>
+ * <li>The resource corresponding to the parent destination path is a
+ * closed project.</li>
+ * <li>A corresponding target resource does exist.</li>
+ * <li>A resource of a different type exists at the target path.</li>
+ * <li>The path of one of the resources is a prefix of the destination
+ * path.</li>
+ * <li>One of the resources, or one of its descendents, is out of sync with
+ * the local file system and <code>FORCE</code> is <code>false</code>.
+ * </li>
+ * <li>Resource changes are disallowed during certain types of resource
+ * change event notification. See <code>IResourceChangeEvent</code> for
+ * more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#move(IPath,int,IProgressMonitor)
+ * @see IResourceRuleFactory#moveRule(IResource, IResource)
+ * @since 2.0
+ */
+ public IStatus move(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Creates and returns a new project description for a project with the
+ * given name. This object is useful when creating, moving or copying
+ * projects.
+ * <p>
+ * The project description is initialized to:
+ * <ul>
+ * <li>the given project name</li>
+ * <li>no references to other projects</li>
+ * <li>an empty build spec</li>
+ * <li>an empty comment</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The returned value is writeable.
+ * </p>
+ *
+ * @param projectName the name of the project
+ * @return a new project description
+ * @see IProject#getDescription()
+ * @see IProject#create(IProjectDescription, IProgressMonitor)
+ * @see IResource#copy(IProjectDescription, int, IProgressMonitor)
+ * @see IProject#move(IProjectDescription, boolean, IProgressMonitor)
+ */
+ public IProjectDescription newProjectDescription(String projectName);
+
+ /**
+ * Removes the given resource change listener from this workspace. Has no
+ * effect if an identical listener is not registered.
+ *
+ * @param listener the listener
+ * @see IResourceChangeListener
+ * @see #addResourceChangeListener(IResourceChangeListener)
+ */
+ public void removeResourceChangeListener(IResourceChangeListener listener);
+
+ /**
+ * Removes the workspace save participant for the given plug-in from this
+ * workspace. If no such participant is registered, no action is taken.
+ * <p>
+ * Once removed, the workspace save participant no longer actively
+ * participates in any future saves of this workspace.
+ * </p>
+ *
+ * @param plugin the plug-in
+ * @see ISaveParticipant
+ * @see #addSaveParticipant(Plugin, ISaveParticipant)
+ */
+ public void removeSaveParticipant(Plugin plugin);
+
+ /**
+ * Runs the given action as an atomic workspace operation.
+ * <p>
+ * After running a method that modifies resources in the workspace,
+ * registered listeners receive after-the-fact notification of what just
+ * transpired, in the form of a resource change event. This method allows
+ * clients to call a number of methods that modify resources and only have
+ * resource change event notifications reported at the end of the entire
+ * batch.
+ * </p>
+ * <p>
+ * If this method is called outside the dynamic scope of another such call,
+ * this method runs the action and then reports a single resource change
+ * event describing the net effect of all changes done to resources by the
+ * action.
+ * </p>
+ * <p>
+ * If this method is called in the dynamic scope of another such call, this
+ * method simply runs the action.
+ * </p>
+ * <p>
+ * The supplied scheduling rule is used to determine whether this operation
+ * can be run simultaneously with workspace changes in other threads. If the
+ * scheduling rule conflicts with another workspace change that is currently
+ * running, the calling thread will be blocked until that change completes.
+ * If the action attempts to make changes to the workspace that were not
+ * specified in the scheduling rule, it will fail. If no scheduling rule is
+ * supplied, then any attempt to change resources will fail. If a non-<code>null</code>
+ * scheduling rule is supplied, this operation must always support cancelation
+ * in the case where this operation becomes blocked by a long running background
+ * operation.
+ * </p>
+ * <p>
+ * The AVOID_UPDATE flag controls whether periodic resource change
+ * notifications should occur during the scope of this call. If this flag is
+ * specified, and no other threads modify the workspace concurrently, then
+ * all resource change notifications will be deferred until the end of this
+ * call. If this flag is not specified, the platform may decide to broadcast
+ * periodic resource change notifications during the scope of this call.
+ * </p>
+ * <p>
+ * Flags other than <code>AVOID_UPDATE</code> are ignored.
+ * </p>
+ *
+ * @param action the action to perform
+ * @param rule the scheduling rule to use when running this operation, or
+ * <code>null</code> if there are no scheduling restrictions for this
+ * operation.
+ * @param flags bit-wise or of flag constants (only AVOID_UPDATE is relevant
+ * here)
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired.
+ * @exception CoreException if the operation failed.
+ * @exception OperationCanceledException if the operation is canceled. If a
+ * non-<code>null</code> scheduling rule is supplied, cancelation can occur
+ * even if no progress monitor is provided.
+ *
+ * @see #AVOID_UPDATE
+ * @see IResourceRuleFactory
+ * @since 3.0
+ */
+ public void run(IWorkspaceRunnable action, ISchedulingRule rule, int flags, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Runs the given action as an atomic workspace operation.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ *
+ * <pre>
+ * workspace.run(action, workspace.getRoot(), IWorkspace.AVOID_UPDATE, monitor);
+ * </pre>
+ *
+ * </p>
+ *
+ * @param action the action to perform
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if the operation failed.
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ */
+ public void run(IWorkspaceRunnable action, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Saves this workspace's valuable state on disk. Consults with all
+ * registered plug-ins so that they can coordinate the saving of their
+ * persistent state as well.
+ * <p>
+ * The <code>full</code> parameter indicates whether a full save or a
+ * snapshot is being requested. Snapshots save the workspace information
+ * that is considered hard to be recomputed in the unlikely event of a
+ * crash. It includes parts of the workspace tree, workspace and projects
+ * descriptions, markers and sync information. Full saves are heavy weight
+ * operations which save the complete workspace state.
+ * </p>
+ * <p>
+ * To ensure that all outstanding changes to the workspace have been
+ * reported to interested parties prior to saving, a full save cannot be
+ * used within the dynamic scope of an <code>IWorkspace.run</code>
+ * invocation. Snapshots can be called any time and are interpreted by the
+ * workspace as a hint that a snapshot is required. The workspace will
+ * perform the snapshot when possible. Even as a hint, snapshots should only
+ * be called when necessary as they impact system performance. Although
+ * saving does not change the workspace per se, its execution is serialized
+ * like methods that write the workspace.
+ * </p>
+ * <p>
+ * The workspace is comprised of several different kinds of data with
+ * varying degrees of importance. The most important data, the resources
+ * themselves and their persistent properties, are written to disk
+ * immediately; other data are kept in volatile memory and only written to
+ * disk periodically; and other data are maintained in memory and never
+ * written out. The following table summarizes what gets saved when:
+ * <ul>
+ * <li>creating or deleting resource - immediately</li>
+ * <li>setting contents of file - immediately</li>
+ * <li>changes to project description - immediately</li>
+ * <li>session properties - never</li>
+ * <li>changes to persistent properties - immediately</li>
+ * <li>markers -<code>save</code></li>
+ * <li>synchronizer info -<code>save</code></li>
+ * <li>shape of the workspace resource tree -<code>save</code></li>
+ * <li>list of active plug-ins - never</li>
+ * </ul>
+ * Resource-based plug-in also have data with varying degrees of importance.
+ * Each plug-in gets to decide the policy for protecting its data, either
+ * immediately, never, or at <code>save</code> time. For the latter, the
+ * plug-in coordinates its actions with the workspace (see
+ * <code>ISaveParticipant</code> for details).
+ * </p>
+ * <p>
+ * If the platform is shutdown (or crashes) after saving the workspace, any
+ * information written to disk by the last successful workspace
+ * <code>save</code> will be restored the next time the workspace is
+ * reopened for the next session. Naturally, information that is written to
+ * disk immediately will be as of the last time it was changed.
+ * </p>
+ * <p>
+ * The workspace provides a general mechanism for keeping concerned parties
+ * apprised of any and all changes to resources in the workspace (
+ * <code>IResourceChangeListener</code>). It is even possible for a
+ * plug-in to find out about changes to resources that happen between
+ * workspace sessions (see <code>IWorkspace.addSaveParticipant</code>).
+ * </p>
+ * <p>
+ * At certain points during this method, the entire workspace resource tree
+ * must be locked to prevent resources from being changed (read access to
+ * resources is permitted).
+ * </p>
+ * <p>
+ * Implementation note: The execution sequence is as follows.
+ * <ul>
+ * <li>A long-term lock on the workspace is taken out to prevent further
+ * changes to workspace until the save is done.</li>
+ * <li>The list of saveable resource tree snapshots is initially empty.
+ * </li>
+ * <li>A different <code>ISaveContext</code> object is created for each
+ * registered workspace save participant plug-in, reflecting the kind of
+ * save (<code>ISaveContext.getKind</code>), the previous save number in
+ * which this plug-in actively participated, and the new save number (=
+ * previous save number plus 1).</li>
+ * <li>Each registered workspace save participant is sent
+ * <code>prepareToSave(context)</code>, passing in its own context
+ * object.
+ * <ul>
+ * <li>Plug-in suspends all activities until further notice.</li>
+ * </ul>
+ * If <code>prepareToSave</code> fails (throws an exception), the problem
+ * is logged and the participant is marked as unstable.</li>
+ * <li>In dependent-before-prerequisite order, each registered workspace
+ * save participant is sent <code>saving(context)</code>, passing in its
+ * own context object.
+ * <ul>
+ * <li>Plug-in decides whether it wants to actively participate in this
+ * save. The plug-in only needs to actively participate if some of its
+ * important state has changed since the last time it actively participated.
+ * If it does decide to actively participate, it writes its important state
+ * to a brand new file in its plug-in state area under a generated file name
+ * based on <code>context.getStateNumber()</code> and calls
+ * <code>context.needStateNumber()</code> to indicate that it has actively
+ * participated. If upon reactivation the plug-in will want a resource delta
+ * covering all changes between now and then, the plug-in should invoke
+ * <code>context.needDelta()</code> to request this now; otherwise, a
+ * resource delta for the intervening period will not be available on
+ * reactivation.</li>
+ * </ul>
+ * If <code>saving</code> fails (throws an exception), the problem is
+ * logged and the participant is marked as unstable.</li>
+ * <li>The plug-in save table contains an entry for each plug-in that has
+ * registered to participate in workspace saves at some time in the past
+ * (the list of plug-ins increases monotonically). Each entry records the
+ * save number of the last successful save in which that plug-in actively
+ * participated, and, optionally, a saved resource tree (conceptually, this
+ * is a complete tree; in practice, it is compressed into a special delta
+ * tree representation). A copy of the plug-in save table is made. Entries
+ * are created or modified for each registered plug-in to record the
+ * appropriate save number (either the previous save number, or the previous
+ * save number plus 1, depending on whether the participant was active and
+ * asked for a new number).</li>
+ * <li>The workspace tree, the modified copy of the plug-in save table, all
+ * markers, etc. and all saveable resource tree snapshots are written to
+ * disk as <b>one atomic operation </b>.</li>
+ * <li>The long-term lock on the workspace is released.</li>
+ * <li>If the atomic save succeeded:
+ * <ul>
+ * <li>The modified copy of the plug-in save table becomes the new plug-in
+ * save table.</li>
+ * <li>In prerequisite-before-dependent order, each registered workspace
+ * save participant is sent <code>doneSaving(context)</code>, passing in
+ * its own context object.
+ * <ul>
+ * <li>Plug-in may perform clean up by deleting obsolete state files in its
+ * plug-in state area.</li>
+ * <li>Plug-in resumes its normal activities.</li>
+ * </ul>
+ * If <code>doneSaving</code> fails (throws an exception), the problem is
+ * logged and the participant is marked as unstable. (The state number in
+ * the save table is not rolled back just because of this instability.)
+ * </li>
+ * <li>The workspace save operation returns.</li>
+ * </ul>
+ * <li>If it failed:
+ * <ul>
+ * <li>The workspace previous state is restored.</li>
+ * <li>In prerequisite-before-dependent order, each registered workspace
+ * save participant is sent <code>rollback(context)</code>, passing in
+ * its own context object.
+ * <ul>
+ * <li>Plug-in may perform clean up by deleting newly-created but obsolete
+ * state file in its plug-in state area.</li>
+ * <li>Plug-in resumes its normal activities.</li>
+ * </ul>
+ * If <code>rollback</code> fails (throws an exception), the problem is
+ * logged and the participant is marked as unstable. (The state number in
+ * the save table is rolled back anyway.)</li>
+ * <li>The workspace save operation fails.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * After a full save, the platform can be shutdown. This will cause the
+ * Resources plug-in and all the other plug-ins to shutdown, without
+ * disturbing the saved workspace on disk.
+ * </p>
+ * <p>
+ * When the platform is later restarted, activating the Resources plug-in
+ * opens the saved workspace. This reads into memory the workspace's
+ * resource tree, plug-in save table, and saved resource tree snapshots
+ * (everything that was written to disk in the atomic operation above).
+ * Later, when a plug-in gets reactivated and registers to participate in
+ * workspace saves, it is handed back the info from its entry in the plug-in
+ * save table, if it has one. It gets back the number of the last save in
+ * which it actively participated and, possibly, a resource delta.
+ * </p>
+ * <p>
+ * The only source of long term garbage would come from a plug-in that never
+ * gets reactivated, or one that gets reactivated but fails to register for
+ * workspace saves. (There is no such problem with a plug-in that gets
+ * uninstalled; its easy enough to scrub its state areas and delete its
+ * entry in the plug-in save table.)
+ * </p>
+ *
+ * @param full <code>true</code> if this is a full save, and
+ * <code>false</code> if this is only a snapshot for protecting against
+ * crashes
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a status that may contain warnings, such as the failure of an
+ * individual participant
+ * @exception CoreException if this method fails to save the state of this
+ * workspace. Reasons include:
+ * <ul>
+ * <li>The operation cannot be batched with others.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see #addSaveParticipant(Plugin, ISaveParticipant)
+ */
+ public IStatus save(boolean full, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Sets the workspace description. Its values are stored in the preference
+ * store.
+ *
+ * @param description the new workspace description.
+ * @see #getDescription()
+ * @exception CoreException if the method fails. Reasons include:
+ * <ul>
+ * <li>There was a problem setting the workspace description.</li>
+ * </ul>
+ */
+ public void setDescription(IWorkspaceDescription description) throws CoreException;
+
+ /**
+ * Sets the lock to use for controlling write access to this workspace. The
+ * lock must only be set once.
+ * <p>
+ * This method is for internal use by the platform-related plug-ins. Clients
+ * should not call this method.
+ * </p>
+ *
+ * @param lock the lock to install on this workspace.
+ *
+ * @deprecated it is no longer possible to override the workspace lock
+ * behavior. This functionality is now provided in the platform API by
+ * subclassing the {@link LockListener} class.
+ */
+ public void setWorkspaceLock(WorkspaceLock lock);
+
+ /**
+ * Returns a copy of the given set of natures sorted in prerequisite order.
+ * For each nature, it is guaranteed that all of its prerequisites will
+ * precede it in the resulting array.
+ *
+ * <p>
+ * Natures that are missing from the install or are involved in a
+ * prerequisite cycle are sorted arbitrarily. Duplicate nature IDs are
+ * removed, so the returned array may be smaller than the original.
+ * </p>
+ *
+ * @param natureIds a valid set of nature extension identifiers
+ * @return the set of nature Ids sorted in prerequisite order
+ * @see #validateNatureSet(String[])
+ * @since 2.0
+ */
+ public String[] sortNatureSet(String[] natureIds);
+
+ /**
+ * Advises that the caller intends to modify the contents of the given files
+ * in the near future and asks whether modifying all these files would be
+ * reasonable. The files must all exist. This method is used to give the VCM
+ * component an opportunity to check out (or otherwise prepare) the files if
+ * required. (It is provided in this component rather than in the UI so that
+ * "core" (i.e., head-less) clients can use it. Similarly, it is located
+ * outside the VCM component for the convenience of clients that must also
+ * operate in configurations without VCM.)
+ * </p>
+ * <p>
+ * A client (such as an editor) should perform a <code>validateEdit</code>
+ * on a file whenever it finds itself in the following position: (a) the
+ * file is marked read-only, and (b) the client believes it likely (not
+ * necessarily certain) that it will modify the file's contents at some
+ * point. A case in point is an editor that has a buffer opened on a file.
+ * When the user starts to dirty the buffer, the editor should check to see
+ * whether the file is read-only. If it is, it should call
+ * <code>validateEdit</code>, and can reasonably expect this call, when
+ * successful, to cause the file to become read-write. An editor should also
+ * be sensitive to a file becoming read-only again even after a successful
+ * <code>validateEdit</code> (e.g., due to the user checking in the file
+ * in a different view); the editor should again call
+ * <code>validateEdit</code> if the file is read-only before attempting to
+ * save the contents of the file.
+ * </p>
+ * <p>
+ * By passing a UI context, the caller indicates that the VCM component may
+ * contact the user to help decide how best to proceed. If no UI context is
+ * provided, the VCM component will make its decision without additional
+ * interaction with the user. If OK is returned, the caller can safely
+ * assume that all of the given files haven been prepared for modification
+ * and that there is good reason to believe that
+ * <code>IFile.setContents</code> (or <code>appendContents</code>)
+ * would be successful on any of them. If the result is not OK, modifying
+ * the given files might not succeed for the reason(s) indicated.
+ * </p>
+ * <p>
+ * If a shell is passed in as the context, the VCM component may bring up a
+ * dialogs to query the user or report difficulties; the shell should be
+ * used to parent any such dialogs; the caller may safely assume that the
+ * reasons for failure will have been made clear to the user. If
+ * {@link IWorkspace#VALIDATE_PROMPT} is passed
+ * as the context, this indicates that the caller does not have access to
+ * a UI context but would still like the user to be prompted if required.
+ * If <code>null</code> is passed, the user should not be contacted; any
+ * failures should be reported via the result; the caller may chose to
+ * present these to the user however they see fit. The ideal implementation
+ * of this method is transactional; no files would be affected unless the
+ * go-ahead could be given. (In practice, there may be no feasible way to
+ * ensure such changes get done atomically.)
+ * </p>
+ * <p>
+ * The method calls <code>FileModificationValidator.validateEdit</code>
+ * for the file modification validator (if provided by the VCM plug-in).
+ * When there is no file modification validator, this method returns a
+ * status with an <code>IResourceStatus.READ_ONLY_LOCAL</code> code if one
+ * of the files is read-only, and a status with an <code>IStatus.OK</code>
+ * code otherwise.
+ * </p>
+ * <p>
+ * This method may be called from any thread. If the UI context is used, it
+ * is the responsibility of the implementor of
+ * <code>FileModificationValidator.validateEdit</code> to interact with
+ * the UI context in an appropriate thread.
+ * </p>
+ *
+ * @param files the files that are to be modified; these files must all
+ * exist in the workspace
+ * @param context either {@link IWorkspace#VALIDATE_PROMPT},
+ * or the <code>org.eclipse.swt.widgets.Shell</code> that is
+ * to be used to parent any dialogs with the user, or <code>null</code> if
+ * there is no UI context (declared as an <code>Object</code> to avoid any
+ * direct references on the SWT component)
+ * @return a status object that is <code>OK</code> if things are fine,
+ * otherwise a status describing reasons why modifying the given files is not
+ * reasonable. A status with a severity of <code>CANCEL</code> is returned
+ * if the validation was canceled, indicating the edit should not proceed.
+ * @see IResourceRuleFactory#validateEditRule(IResource[])
+ * @since 2.0
+ */
+ public IStatus validateEdit(IFile[] files, Object context);
+
+ /**
+ * Validates the given path as the location of the given resource on disk.
+ * The path must be either an absolute file system path, or a relative path
+ * whose first segment is the name of a defined workspace path variable. In
+ * addition to the restrictions for paths in general (see <code>IPath.
+ * isValidPath</code>),
+ * a link location must also obey the following rules:
+ * <ul>
+ * <li>must not overlap with the platform's metadata directory</li>
+ * <li>must not be the same as or a parent of the root directory of the
+ * project the linked resource is contained in</li>
+ * </ul>
+ * <p>
+ * This method also checks that the given resource can legally become a
+ * linked resource. This includes the following restrictions:
+ * <ul>
+ * <li>must have a project as its immediate parent</li>
+ * <li>project natures and the team hook may disallow linked resources on
+ * projects they are associated with</li>
+ * <li>the global workspace preference to disable linking,
+ * <code>ResourcesPlugin.PREF_DISABLE_LINKING</code> must not be set to
+ * "true"</li>.
+ * </ul>
+ * </p>
+ * <p>
+ * This method will return a status with severity <code>IStatus.ERROR</code>
+ * if the location does not obey the above rules. Also, this method will
+ * return a status with severity <code>IStatus.WARNING</code> if the
+ * location overlaps the location of any existing resource in the workspace.
+ * </p>
+ * <p>
+ * Note: this method does not consider whether files or directories exist in
+ * the file system at the specified path.
+ *
+ * @param resource the resource to validate the location for
+ * @param location the location of the linked resource contents on disk
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * location is valid as the linked resource location, otherwise a status
+ * object with severity <code>IStatus.WARNING</code> or
+ * <code>IStatus.ERROR</code> indicating what is wrong with the location
+ * @see IStatus#OK
+ * @see ResourcesPlugin#PREF_DISABLE_LINKING
+ * @since 2.1
+ */
+ public IStatus validateLinkLocation(IResource resource, IPath location);
+
+ /**
+ * Validates the given {@link URI} as the location of the given resource on disk.
+ * The location must be either an absolute URI, or a relative URI
+ * whose first segment is the name of a defined workspace path variable.
+ * A link location must obey the following rules:
+ * <ul>
+ * <li>must not overlap with the platform's metadata directory</li>
+ * <li>must not be the same as or a parent of the root directory of the
+ * project the linked resource is contained in</li>
+ * </ul>
+ * <p>
+ * This method also checks that the given resource can legally become a
+ * linked resource. This includes the following restrictions:
+ * <ul>
+ * <li>must have a project as its immediate parent</li>
+ * <li>project natures and the team hook may disallow linked resources on
+ * projects they are associated with</li>
+ * <li>the global workspace preference to disable linking,
+ * <code>ResourcesPlugin.PREF_DISABLE_LINKING</code> must not be set to
+ * "true"</li>.
+ * </ul>
+ * </p>
+ * <p>
+ * This method will return a status with severity <code>IStatus.ERROR</code>
+ * if the location does not obey the above rules. Also, this method will
+ * return a status with severity <code>IStatus.WARNING</code> if the
+ * location overlaps the location of any existing resource in the workspace.
+ * </p>
+ * <p>
+ * Note: this method does not consider whether files or directories exist in
+ * the file system at the specified location.
+ *
+ * @param resource the resource to validate the location for
+ * @param location the location of the linked resource contents in some file system
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * location is valid as the linked resource location, otherwise a status
+ * object with severity <code>IStatus.WARNING</code> or
+ * <code>IStatus.ERROR</code> indicating what is wrong with the location
+ * @see IStatus#OK
+ * @see ResourcesPlugin#PREF_DISABLE_LINKING
+ * @since 3.2
+ */
+ public IStatus validateLinkLocationURI(IResource resource, URI location);
+
+ /**
+ * Validates the given string as the name of a resource valid for one of the
+ * given types.
+ * <p>
+ * In addition to the basic restrictions on paths in general (see
+ * {@link IPath#isValidSegment(String)}), a resource name must also not
+ * contain any characters or substrings that are not valid on the file system
+ * on which workspace root is located. In addition, the names "." and ".."
+ * are reserved due to their special meaning in file system paths.
+ * </p>
+ * <p>
+ * This validation check is done automatically as a resource is created (but
+ * not when the resource handle is constructed); this means that any
+ * resource that exists can be safely assumed to have a valid name and path.
+ * Note that the name of the workspace root resource is inherently invalid.
+ * </p>
+ *
+ * @param segment the name segment to be checked
+ * @param typeMask bitwise-or of the resource type constants (
+ * <code>FILE</code>,<code>FOLDER</code>,<code>PROJECT</code> or
+ * <code>ROOT</code>) indicating expected resource type(s)
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * string is valid as a resource name, otherwise a status object indicating
+ * what is wrong with the string
+ * @see IResource#PROJECT
+ * @see IResource#FOLDER
+ * @see IResource#FILE
+ * @see IStatus#OK
+ */
+ public IStatus validateName(String segment, int typeMask);
+
+ /**
+ * Validates that each of the given natures exists, and that all nature
+ * constraints are satisfied within the given set.
+ * <p>
+ * The following conditions apply to validation of a set of natures:
+ * <ul>
+ * <li>all natures in the set exist in the plug-in registry
+ * <li>all prerequisites of each nature are present in the set
+ * <li>there are no cycles in the prerequisite graph of the set
+ * <li>there are no two natures in the set that specify one-of-nature
+ * inclusion in the same group.
+ * <li>there are no two natures in the set with the same id
+ * </ul>
+ * </p>
+ * <p>
+ * An empty nature set is always valid.
+ * </p>
+ *
+ * @param natureIds an array of nature extension identifiers
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * set of natures is valid, otherwise a status object indicating what is
+ * wrong with the set
+ * @since 2.0
+ */
+ public IStatus validateNatureSet(String[] natureIds);
+
+ /**
+ * Validates the given string as a path for a resource of the given type(s).
+ * <p>
+ * In addition to the restrictions for paths in general (see
+ * <code>IPath.isValidPath</code>), a resource path should also obey the
+ * following rules:
+ * <ul>
+ * <li>a resource path should be an absolute path with no device id
+ * <li>its segments should be valid names according to
+ * <code>validateName</code>
+ * <li>a path for the workspace root must be the canonical root path
+ * <li>a path for a project should have exactly 1 segment
+ * <li>a path for a file or folder should have more than 1 segment
+ * <li>the first segment should be a valid project name
+ * <li>the second through penultimate segments should be valid folder names
+ * <li>the last segment should be a valid name of the given type
+ * </ul>
+ * </p>
+ * <p>
+ * Note: this method does not consider whether a resource at the specified
+ * path exists.
+ * </p>
+ * <p>
+ * This validation check is done automatically as a resource is created (but
+ * not when the resource handle is constructed); this means that any
+ * resource that exists can be safely assumed to have a valid name and path.
+ * </p>
+ *
+ * @param path the path string to be checked
+ * @param typeMask bitwise-or of the resource type constants (
+ * <code>FILE</code>,<code>FOLDER</code>,<code>PROJECT</code>, or
+ * <code>ROOT</code>) indicating expected resource type(s)
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * path is valid as a resource path, otherwise a status object indicating
+ * what is wrong with the string
+ * @see IResource#PROJECT
+ * @see IResource#FOLDER
+ * @see IResource#FILE
+ * @see IStatus#OK
+ * @see IResourceStatus#getPath()
+ */
+ public IStatus validatePath(String path, int typeMask);
+
+ /**
+ * Validates the given path as the location of the given project on disk.
+ * The path must be either an absolute file system path, or a relative path
+ * whose first segment is the name of a defined workspace path variable. In
+ * addition to the restrictions for paths in general (see <code>IPath.
+ * isValidPath</code>),
+ * a location path should also obey the following rules:
+ * <ul>
+ * <li>must not be the same as another open or closed project</li>
+ * <li>must not occupy the default location for any project, whether existing or not</li>
+ * <li>must not be the same as or a parent of the platform's working directory</li>
+ * <li>must not be the same as or a child of the location of any existing
+ * linked resource in the given project</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Note: this method does not consider whether files or directories exist in
+ * the file system at the specified path.
+ * </p>
+ *
+ * @param project the project to validate the location for, can be <code>null</code>
+ * if non default project location is validated
+ * @param location the location of the project contents on disk, or <code>null</code>
+ * if the default project location is used
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * location is valid as the project content location, otherwise a status
+ * object indicating what is wrong with the location
+ * @see IProjectDescription#getLocationURI()
+ * @see IProjectDescription#setLocation(IPath)
+ * @see IStatus#OK
+ */
+ public IStatus validateProjectLocation(IProject project, IPath location);
+
+ /**
+ * Validates the given URI as the location of the given project.
+ * The location must be either an absolute URI, or a relative URI
+ * whose first segment is the name of a defined workspace path variable.
+ * A project location must obey the following rules:
+ * <ul>
+ * <li>must not be the same as another open or closed project</li>
+ * <li>must not occupy the default location for any project, whether existing or not</li>
+ * <li>must not be the same as or a parent of the platform's working directory</li>
+ * <li>must not be the same as or a child of the location of any existing
+ * linked resource in the given project</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Note: this method does not consider whether files or directories exist in
+ * the file system at the specified path.
+ * </p>
+ *
+ * @param project the project to validate the location for, can be <code>null</code>
+ * if non default project location is validated
+ * @param location the location of the project contents on disk, or <code>null</code>
+ * if the default project location is used
+ * @return a status object with code <code>IStatus.OK</code> if the given
+ * location is valid as the project content location, otherwise a status
+ * object indicating what is wrong with the location
+ * @see IProjectDescription#getLocationURI()
+ * @see IProjectDescription#setLocationURI(URI)
+ * @see IStatus#OK
+ * @since 3.2
+ */
+ public IStatus validateProjectLocationURI(IProject project, URI location);
+
+ /**
+ * Returns the path variable manager for this workspace.
+ *
+ * @return the path variable manager
+ * @see IPathVariableManager
+ * @since 2.1
+ */
+ public IPathVariableManager getPathVariableManager();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspaceDescription.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+/**
+ * A workspace description represents the workspace preferences. It can be
+ * used to query the current preferences and set new ones. The workspace
+ * preference values are stored in the preference store and are also accessible
+ * via the preference mechanism. Constants for the preference keys are found
+ * on the <code>ResourcesPlugin</code> class.
+ *
+ * @see IWorkspace#getDescription()
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see IWorkspace#newProjectDescription(String)
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IWorkspaceDescription {
+ /**
+ * Returns the order in which projects in the workspace should be built.
+ * The returned value is <code>null</code> if the workspace's default build
+ * order is being used.
+ *
+ * @return the names of projects in the order they will be built,
+ * or <code>null</code> if the default build order should be used
+ * @see #setBuildOrder(String[])
+ * @see ResourcesPlugin#PREF_BUILD_ORDER
+ */
+ public String[] getBuildOrder();
+
+ /**
+ * Returns the maximum length of time, in milliseconds, a file state should be
+ * kept in the local history.
+ *
+ * @return the maximum time a file state should be kept in the local history
+ * represented in milliseconds
+ * @see #setFileStateLongevity(long)
+ * @see ResourcesPlugin#PREF_FILE_STATE_LONGEVITY
+ */
+ public long getFileStateLongevity();
+
+ /**
+ * Returns the maximum number of times that the workspace should rebuild when
+ * builders affect projects that have already been built.
+ *
+ * @return the maximum number of times that the workspace should rebuild when
+ * builders affect projects that have already been built.
+ * @see #setMaxBuildIterations(int)
+ * @see ResourcesPlugin#PREF_MAX_BUILD_ITERATIONS
+ * @since 2.1
+ */
+ public int getMaxBuildIterations();
+
+ /**
+ * Returns the maximum number of states per file that can be stored in the local history.
+ *
+ * @return the maximum number of states per file that can be stored in the local history
+ * @see #setMaxFileStates(int)
+ * @see ResourcesPlugin#PREF_MAX_FILE_STATES
+ */
+ public int getMaxFileStates();
+
+ /**
+ * Returns the maximum permitted size of a file, in bytes, to be stored in the
+ * local history.
+ *
+ * @return the maximum permitted size of a file to be stored in the local history
+ * @see #setMaxFileStateSize(long)
+ * @see ResourcesPlugin#PREF_MAX_FILE_STATE_SIZE
+ */
+ public long getMaxFileStateSize();
+
+ /**
+ * Returns the interval between automatic workspace snapshots.
+ *
+ * @return the amount of time in milliseconds between automatic workspace snapshots
+ * @see #setSnapshotInterval(long)
+ * @see ResourcesPlugin#PREF_SNAPSHOT_INTERVAL
+ * @since 2.0
+ */
+ public long getSnapshotInterval();
+
+ /**
+ * Returns whether this workspace performs autobuilds.
+ *
+ * @return <code>true</code> if autobuilding is on, otherwise
+ * <code>false</code>
+ * @see #setAutoBuilding(boolean)
+ * @see ResourcesPlugin#PREF_AUTO_BUILDING
+ */
+ public boolean isAutoBuilding();
+
+ /**
+ * Records whether this workspace performs autobuilds.
+ * <p>
+ * When autobuild is on, any changes made to a project and its
+ * resources automatically triggers an incremental build of the workspace.
+ * </p>
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param value <code>true</code> to turn on autobuilding,
+ * and <code>false</code> to turn it off
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #isAutoBuilding()
+ * @see ResourcesPlugin#PREF_AUTO_BUILDING
+ */
+ public void setAutoBuilding(boolean value);
+
+ /**
+ * Sets the order in which projects in the workspace should be built.
+ * Projects not named in this list are built in a default order defined
+ * by the workspace. Set this value to <code>null</code> to use the
+ * default ordering for all projects. Projects not named in the list are
+ * built in unspecified order after all ordered projects.
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param value the names of projects in the order in which they are built,
+ * or <code>null</code> to use the workspace's default order for all projects
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #getBuildOrder()
+ * @see ResourcesPlugin#PREF_BUILD_ORDER
+ */
+ public void setBuildOrder(String[] value);
+
+ /**
+ * Sets the maximum time, in milliseconds, a file state should be kept in the
+ * local history.
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param time the maximum number of milliseconds a file state should be
+ * kept in the local history
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #getFileStateLongevity()
+ * @see ResourcesPlugin#PREF_FILE_STATE_LONGEVITY
+ */
+ public void setFileStateLongevity(long time);
+
+ /**
+ * Sets the maximum number of times that the workspace should rebuild when
+ * builders affect projects that have already been built.
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param number the maximum number of times that the workspace should rebuild
+ * when builders affect projects that have already been built.
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #getMaxBuildIterations()
+ * @see ResourcesPlugin#PREF_MAX_BUILD_ITERATIONS
+ * @since 2.1
+ */
+ public void setMaxBuildIterations(int number);
+
+ /**
+ * Sets the maximum number of states per file that can be stored in the local history.
+ * If the maximum number is reached, older states are removed in favor of
+ * new ones.
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param number the maximum number of states per file that can be stored in the local history
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #getMaxFileStates()
+ * @see ResourcesPlugin#PREF_MAX_FILE_STATES
+ */
+ public void setMaxFileStates(int number);
+
+ /**
+ * Sets the maximum permitted size of a file, in bytes, to be stored in the
+ * local history.
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param size the maximum permitted size of a file to be stored in the local history
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #getMaxFileStateSize()
+ * @see ResourcesPlugin#PREF_MAX_FILE_STATE_SIZE
+ */
+ public void setMaxFileStateSize(long size);
+
+ /**
+ * Sets the interval between automatic workspace snapshots. The new interval
+ * will only take effect after the next snapshot.
+ * <p>
+ * Users must call <code>IWorkspace.setDescription</code> before changes
+ * made to this description take effect.
+ * </p>
+ *
+ * @param delay the amount of time in milliseconds between automatic workspace snapshots
+ * @see IWorkspace#setDescription(IWorkspaceDescription)
+ * @see #getSnapshotInterval()
+ * @see ResourcesPlugin#PREF_SNAPSHOT_INTERVAL
+ * @since 2.0
+ */
+ public void setSnapshotInterval(long delay);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspaceRoot.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.net.URI;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A root resource represents the top of the resource hierarchy in a workspace.
+ * There is exactly one root in a workspace. The root resource has the following
+ * behavior:
+ * <ul>
+ * <li>It cannot be moved or copied </li>
+ * <li>It always exists.</li>
+ * <li>Deleting the root deletes all of the children under the root but leaves the root itself</li>
+ * <li>It is always local.</li>
+ * <li>It is never a phantom.</li>
+ * </ul>
+ * <p>
+ * Workspace roots implement the <code>IAdaptable</code> interface;
+ * extensions are managed by the platform's adapter manager.
+ * </p>
+ *
+ * @see Platform#getAdapterManager()
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IWorkspaceRoot extends IContainer, IAdaptable {
+
+ /**
+ * Deletes everything in the workspace except the workspace root resource
+ * itself.
+ * <p>
+ * This is a convenience method, fully equivalent to:
+ * <pre>
+ * delete(
+ * (deleteContent ? IResource.ALWAYS_DELETE_PROJECT_CONTENT : IResource.NEVER_DELETE_PROJECT_CONTENT )
+ * | (force ? FORCE : IResource.NONE),
+ * monitor);
+ * </pre>
+ * </p>
+ * <p>
+ * This method changes resources; these changes will be reported
+ * in a subsequent resource change event.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided
+ * by the given progress monitor.
+ * </p>
+ *
+ * @param deleteContent a flag controlling how whether content is
+ * aggressively deleted
+ * @param force a flag controlling whether resources that are not
+ * in sync with the local file system will be tolerated
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> A project could not be deleted.</li>
+ * <li> A project's contents could not be deleted.</li>
+ * <li> Resource changes are disallowed during certain types of resource change
+ * event notification. See <code>IResourceChangeEvent</code> for more details.</li>
+ * </ul>
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int,IProgressMonitor)
+ */
+ public void delete(boolean deleteContent, boolean force, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns the handles to all the resources (workspace root, project,
+ * folder) in the workspace which are mapped to the given path in the local
+ * file system. Returns an empty array if there are none.
+ * <p>
+ * If the path maps to the platform working location, the returned object
+ * will be a single element array consisting of an object of type
+ * <code>ROOT</code>.
+ * </p>
+ * <p>
+ * If the path maps to a project, the resulting array will contain a
+ * resource of type <code>PROJECT</code>, along with any linked folders that
+ * share the same location. Otherwise the resulting array will contain
+ * folders (type <code>FOLDER</code>).
+ * </p>
+ * <p>
+ * The path should be absolute; a relative path will be treated as absolute.
+ * The path segments need not be valid names; a trailing separator is
+ * ignored. The resulting resources may not currently exist.
+ * </p>
+ * <p>
+ * The result will omit team private members and hidden resources. The
+ * result will omit resources within team private members or hidden
+ * containers.
+ * </p>
+ *
+ * @param location
+ * a path in the local file system
+ * @return the corresponding containers in the workspace, or an empty array
+ * if none
+ * @since 2.1
+ * @deprecated use {@link #findContainersForLocationURI(URI)} instead
+ */
+ public IContainer[] findContainersForLocation(IPath location);
+
+ /**
+ * Returns the handles to all the resources (workspace root, project,
+ * folder) in the workspace which are mapped to the given URI. Returns an
+ * empty array if there are none.
+ * <p>
+ * If the path maps to the platform working location, the returned object
+ * will be a single element array consisting of an object of type
+ * <code>ROOT</code>.
+ * </p>
+ * <p>
+ * If the path maps to a project, the resulting array will contain a
+ * resource of type <code>PROJECT</code>, along with any linked folders that
+ * share the same location. Otherwise the resulting array will contain
+ * folders (type <code>FOLDER</code>).
+ * </p>
+ * <p>
+ * The URI must be absolute; its segments need not be valid names; a
+ * trailing separator is ignored. The resulting resources may not currently
+ * exist.
+ * </p>
+ * <p>
+ * The result will omit team private members and hidden resources. The
+ * result will omit resources within team private member sor hidden
+ * containers.
+ * </p>
+ * <p>
+ * This is a convenience method, fully equivalent to
+ * <code>findContainersForLocationURI(location, IResource.NONE)</code>.
+ * </p>
+ *
+ * @param location
+ * a URI path into some file system
+ * @return the corresponding containers in the workspace, or an empty array
+ * if none
+ * @since 3.2
+ */
+ public IContainer[] findContainersForLocationURI(URI location);
+
+ /**
+ * Returns the handles to all the resources (workspace root, project,
+ * folder) in the workspace which are mapped to the given URI. Returns an
+ * empty array if there are none.
+ * <p>
+ * If the {@link #INCLUDE_TEAM_PRIVATE_MEMBERS} flag is specified in the
+ * member flags, team private members will be included along with the
+ * others. If the {@link #INCLUDE_TEAM_PRIVATE_MEMBERS} flag is not
+ * specified (recommended), the result will omit any team private member
+ * resources.
+ * </p>
+ * <p>
+ * If the {@link #INCLUDE_HIDDEN} flag is specified in the member flags,
+ * hidden members will be included along with the others. If the
+ * {@link #INCLUDE_HIDDEN} flag is not specified (recommended), the result
+ * will omit any hidden member resources.
+ * </p>
+ *
+ * @param location
+ * a URI path into some file system
+ * @param memberFlags
+ * bit-wise or of member flag constants (
+ * {@link #INCLUDE_TEAM_PRIVATE_MEMBERS} and {@link #INCLUDE_HIDDEN})
+ * indicating which members are of interest
+ * @return the corresponding files in the workspace, or an empty array if
+ * none
+ * @since 3.5
+ */
+ public IContainer[] findContainersForLocationURI(URI location, int memberFlags);
+
+ /**
+ * Returns the handles of all files that are mapped to the given path in the
+ * local file system. Returns an empty array if there are none. The path
+ * should be absolute; a relative path will be treated as absolute. The path
+ * segments need not be valid names. The resulting files may not currently
+ * exist.
+ * <p>
+ * The result will omit any team private member and hidden resources. The
+ * result will omit resources within team private member or hidden
+ * containers.
+ * </p>
+ *
+ * @param location
+ * a path in the local file system
+ * @return the corresponding files in the workspace, or an empty array if
+ * none
+ * @since 2.1
+ * @deprecated use {@link #findFilesForLocationURI(URI)} instead
+ */
+ public IFile[] findFilesForLocation(IPath location);
+
+ /**
+ * Returns the handles of all files that are mapped to the given URI.
+ * Returns an empty array if there are none. The URI must be absolute; its
+ * path segments need not be valid names. The resulting files may not
+ * currently exist.
+ * <p>
+ * The result will omit any team private member and hidden resources. The
+ * result will omit resources within team private member or hidden
+ * containers.
+ * </p>
+ * <p>
+ * This is a convenience method, fully equivalent to
+ * <code>findFilesForLocationURI(location, IResource.NONE)</code>.
+ * </p>
+ *
+ * @param location
+ * a URI path into some file system
+ * @return the corresponding files in the workspace, or an empty array if
+ * none
+ * @since 3.2
+ */
+ public IFile[] findFilesForLocationURI(URI location);
+
+ /**
+ * Returns the handles of all files that are mapped to the given URI.
+ * Returns an empty array if there are none. The URI must be absolute; its
+ * path segments need not be valid names. The resulting files may not
+ * currently exist.
+ * <p>
+ * If the {@link #INCLUDE_TEAM_PRIVATE_MEMBERS} flag is specified in the
+ * member flags, team private members will be included along with the
+ * others. If the {@link #INCLUDE_TEAM_PRIVATE_MEMBERS} flag is not
+ * specified (recommended), the result will omit any team private member
+ * resources.
+ * </p>
+ * <p>
+ * If the {@link #INCLUDE_HIDDEN} flag is specified in the member flags,
+ * hidden members will be included along with the others. If the
+ * {@link #INCLUDE_HIDDEN} flag is not specified (recommended), the result
+ * will omit any hidden member resources.
+ * </p>
+ *
+ * @param location
+ * a URI path into some file system
+ * @param memberFlags
+ * bit-wise or of member flag constants (
+ * {@link #INCLUDE_TEAM_PRIVATE_MEMBERS} and {@link #INCLUDE_HIDDEN})
+ * indicating which members are of interest
+ * @return the corresponding files in the workspace, or an empty array if
+ * none
+ * @since 3.5
+ */
+ public IFile[] findFilesForLocationURI(URI location, int memberFlags);
+
+ /**
+ * Returns a handle to the workspace root, project or folder
+ * which is mapped to the given path
+ * in the local file system, or <code>null</code> if none.
+ * If the path maps to the platform working location, the returned object
+ * will be of type <code>ROOT</code>. If the path maps to a
+ * project, the resulting object will be
+ * of type <code>PROJECT</code>; otherwise the resulting object
+ * will be a folder (type <code>FOLDER</code>).
+ * The path should be absolute; a relative path will be treated as
+ * absolute. The path segments need not be valid names; a trailing separator is ignored.
+ * The resulting resource may not currently exist.
+ * <p>
+ * This method returns null when the given file system location is not equal to
+ * or under the location of any existing project in the workspace, or equal to the
+ * location of the platform working location.
+ * </p>
+ * <p>
+ * Warning: This method ignores linked resources and their children. Since
+ * linked resources may overlap other resources, a unique mapping from a
+ * file system location to a single resource is not guaranteed. To find all
+ * resources for a given location, including linked resources, use the method
+ * <code>findContainersForLocation</code>.
+ * </p>
+ *
+ * @param location a path in the local file system
+ * @return the corresponding project or folder in the workspace,
+ * or <code>null</code> if none
+ */
+ public IContainer getContainerForLocation(IPath location);
+
+ /**
+ * Returns a handle to the file which is mapped to the given path
+ * in the local file system, or <code>null</code> if none.
+ * The path should be absolute; a relative path will be treated as
+ * absolute. The path segments need not be valid names.
+ * The resulting file may not currently exist.
+ * <p>
+ * This method returns null when the given file system location is not under
+ * the location of any existing project in the workspace.
+ * </p>
+ * <p>
+ * Warning: This method ignores linked resources and their children. Since
+ * linked resources may overlap other resources, a unique mapping from a
+ * file system location to a single resource is not guaranteed. To find all
+ * resources for a given location, including linked resources, use the method
+ * <code>findFilesForLocation</code>.
+ * </p>
+ *
+ * @param location a path in the local file system
+ * @return the corresponding file in the workspace,
+ * or <code>null</code> if none
+ */
+ public IFile getFileForLocation(IPath location);
+
+ /**
+ * Returns a handle to the project resource with the given name
+ * which is a child of this root. The given name must be a valid
+ * path segment as defined by {@link IPath#isValidSegment(String)}.
+ * <p>
+ * Note: This method deals exclusively with resource handles,
+ * independent of whether the resources exist in the workspace.
+ * With the exception of validating that the name is a valid path segment,
+ * validation checking of the project name is not done
+ * when the project handle is constructed; rather, it is done
+ * automatically as the project is created.
+ * </p>
+ *
+ * @param name the name of the project
+ * @return a project resource handle
+ * @see #getProjects()
+ */
+ public IProject getProject(String name);
+
+ /**
+ * Returns the collection of projects which exist under this root.
+ * The projects can be open or closed.
+ * <p>
+ * This is a convenience method, fully equivalent to <code>getProjects(IResource.NONE)</code>.
+ * Hidden projects are <b>not</b> included.
+ * </p>
+ * @return an array of projects
+ * @see #getProject(String)
+ * @see IResource#isHidden()
+ */
+ public IProject[] getProjects();
+
+ /**
+ * Returns the collection of projects which exist under this root.
+ * The projects can be open or closed.
+ * </p><p>
+ * If the {@link #INCLUDE_HIDDEN} flag is specified in the member flags, hidden
+ * projects will be included along with the others. If the {@link #INCLUDE_HIDDEN} flag
+ * is not specified (recommended), the result will omit any hidden projects.
+ * </p>
+ *
+ * @param memberFlags bit-wise or of member flag constants indicating which
+ * projects are of interest (only {@link #INCLUDE_HIDDEN} is currently applicable)
+ * @return an array of projects
+ * @see #getProject(String)
+ * @see IResource#isHidden()
+ * @since 3.4
+ */
+ public IProject[] getProjects(int memberFlags);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspaceRunnable.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A runnable which executes as a batch operation within the workspace.
+ * The <code>IWorkspaceRunnable</code> interface should be implemented
+ * by any class whose instances are intended to be run by
+ * <code>IWorkspace.run</code>.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IWorkspace#run(IWorkspaceRunnable, IProgressMonitor)
+ */
+public interface IWorkspaceRunnable {
+ /**
+ * Runs the operation reporting progress to and accepting
+ * cancellation requests from the given progress monitor.
+ * <p>
+ * Implementors of this method should check the progress monitor
+ * for cancellation when it is safe and appropriate to do so. The cancellation
+ * request should be propagated to the caller by throwing
+ * <code>OperationCanceledException</code>.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ * @exception CoreException if this operation fails.
+ */
+ public void run(IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/IncrementalProjectBuilder.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,357 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.Map;
+import org.eclipse.core.internal.events.InternalBuilder;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * The abstract base class for all incremental project builders. This class
+ * provides the infrastructure for defining a builder and fulfills the contract
+ * specified by the <code>org.eclipse.core.resources.builders</code> standard
+ * extension point.
+ * <p>
+ * All builders must subclass this class according to the following guidelines:
+ * <ul>
+ * <li>must re-implement at least <code>build</code></li>
+ * <li>may implement other methods</li>
+ * <li>must supply a public, no-argument constructor</li>
+ * </ul>
+ * On creation, the <code>setInitializationData</code> method is called with
+ * any parameter data specified in the declaring plug-in's manifest.
+ */
+public abstract class IncrementalProjectBuilder extends InternalBuilder implements IExecutableExtension {
+ /**
+ * Build kind constant (value 6) indicating a full build request. A full
+ * build discards all previously built state and builds all resources again.
+ * Resource deltas are not applicable for this kind of build.
+ *
+ * @see IProject#build(int, IProgressMonitor)
+ * @see IProject#build(int, String, Map, IProgressMonitor)
+ * @see IWorkspace#build(int, IProgressMonitor)
+ */
+ public static final int FULL_BUILD = 6;
+ /**
+ * Build kind constant (value 9) indicating an automatic build request. When
+ * autobuild is turned on, these builds are triggered automatically whenever
+ * resources change. Apart from the method by which autobuilds are triggered,
+ * they otherwise operate like an incremental build.
+ *
+ * @see IWorkspaceDescription#setAutoBuilding(boolean)
+ * @see IWorkspace#isAutoBuilding()
+ */
+ public static final int AUTO_BUILD = 9;
+ /**
+ * Build kind constant (value 10) indicating an incremental build request.
+ * Incremental builds use an {@link IResourceDelta} that describes what
+ * resources have changed since the last build. The builder calculates
+ * what resources are affected by the delta, and rebuilds the affected resources.
+ *
+ * @see IProject#build(int, IProgressMonitor)
+ * @see IProject#build(int, String, Map, IProgressMonitor)
+ * @see IWorkspace#build(int, IProgressMonitor)
+ */
+ public static final int INCREMENTAL_BUILD = 10;
+ /**
+ * Build kind constant (value 15) indicating a clean build request. A clean
+ * build discards any additional state that has been computed as a result of
+ * previous builds, and returns the project to a clean slate. Resource
+ * deltas are not applicable for this kind of build.
+ *
+ * @see IProject#build(int, IProgressMonitor)
+ * @see IProject#build(int, String, Map, IProgressMonitor)
+ * @see IWorkspace#build(int, IProgressMonitor)
+ * @see #clean(IProgressMonitor)
+ * @since 3.0
+ */
+ public static final int CLEAN_BUILD = 15;
+
+ /**
+ * Runs this builder in the specified manner. Subclasses should implement
+ * this method to do the processing they require.
+ * <p>
+ * If the build kind is {@link #INCREMENTAL_BUILD} or
+ * {@link #AUTO_BUILD}, the <code>getDelta</code> method can be
+ * used during the invocation of this method to obtain information about
+ * what changes have occurred since the last invocation of this method. Any
+ * resource delta acquired is valid only for the duration of the invocation
+ * of this method.
+ * </p>
+ * <p>
+ * After completing a build, this builder may return a list of projects for
+ * which it requires a resource delta the next time it is run. This
+ * builder's project is implicitly included and need not be specified. The
+ * build mechanism will attempt to maintain and compute deltas relative to
+ * the identified projects when asked the next time this builder is run.
+ * Builders must re-specify the list of interesting projects every time they
+ * are run as this is not carried forward beyond the next build. Projects
+ * mentioned in return value but which do not exist will be ignored and no
+ * delta will be made available for them.
+ * </p>
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor. All builders should report their progress and
+ * honor cancel requests in a timely manner. Cancelation requests should be
+ * propagated to the caller by throwing
+ * <code>OperationCanceledException</code>.
+ * </p>
+ * <p>
+ * All builders should try to be robust in the face of trouble. In
+ * situations where failing the build by throwing <code>CoreException</code>
+ * is the only option, a builder has a choice of how best to communicate the
+ * problem back to the caller. One option is to use the
+ * {@link IResourceStatus#BUILD_FAILED} status code along with a suitable message;
+ * another is to use a {@link MultiStatus} containing finer-grained problem
+ * diagnoses.
+ * </p>
+ *
+ * @param kind the kind of build being requested. Valid values are
+ * <ul>
+ * <li>{@link #FULL_BUILD} - indicates a full build.</li>
+ * <li>{@link #INCREMENTAL_BUILD}- indicates an incremental build.</li>
+ * <li>{@link #AUTO_BUILD} - indicates an automatically triggered
+ * incremental build (autobuilding on).</li>
+ * </ul>
+ * @param args a table of builder-specific arguments keyed by argument name
+ * (key type: <code>String</code>, value type: <code>String</code>);
+ * <code>null</code> is equivalent to an empty map
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ * @return the list of projects for which this builder would like deltas the
+ * next time it is run or <code>null</code> if none
+ * @exception CoreException if this build fails.
+ * @see IProject#build(int, String, Map, IProgressMonitor)
+ */
+ protected abstract IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Clean is an opportunity for a builder to discard any additional state that has
+ * been computed as a result of previous builds. It is recommended that builders
+ * override this method to delete all derived resources created by previous builds,
+ * and to remove all markers of type {@link IMarker#PROBLEM} that
+ * were created by previous invocations of the builder. The platform will
+ * take care of discarding the builder's last built state (there is no need
+ * to call <code>forgetLastBuiltState</code>).
+ * </p>
+ * <p>
+ * This method is called as a result of invocations of
+ * <code>IWorkspace.build</code> or <code>IProject.build</code> where
+ * the build kind is {@link #CLEAN_BUILD}.
+ * <p>
+ * This default implementation does nothing. Subclasses may override.
+ * <p>
+ * This method is long-running; progress and cancellation are provided by
+ * the given progress monitor. All builders should report their progress and
+ * honor cancel requests in a timely manner. Cancelation requests should be
+ * propagated to the caller by throwing
+ * <code>OperationCanceledException</code>.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancellation are not desired
+ * @exception CoreException if this build fails.
+ * @see IWorkspace#build(int, IProgressMonitor)
+ * @see #CLEAN_BUILD
+ * @since 3.0
+ */
+ protected void clean(IProgressMonitor monitor) throws CoreException {
+ //default implementation does nothing
+ //thwart compiler warning
+ }
+
+ /**
+ * Requests that this builder forget any state it may be retaining regarding
+ * previously built states. Typically this means that the next time the
+ * builder runs, it will have to do a full build since it does not have any
+ * state upon which to base an incremental build.
+ */
+ public final void forgetLastBuiltState() {
+ super.forgetLastBuiltState();
+ }
+
+ /**
+ * Returns the build command associated with this builder. The returned
+ * command may or may not be in the build specification for the project
+ * on which this builder operates.
+ * <p>
+ * Any changes made to the returned command will only take effect if
+ * the modified command is installed on a project build spec.
+ * </p>
+ *
+ * @see IProjectDescription#setBuildSpec(ICommand [])
+ * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
+ * @since 3.1
+ */
+ public final ICommand getCommand() {
+ return super.getCommand();
+ }
+
+ /**
+ * Returns the resource delta recording the changes in the given project
+ * since the last time this builder was run. <code>null</code> is returned
+ * if no such delta is available. An empty delta is returned if no changes
+ * have occurred, or if deltas are not applicable for the current build kind.
+ * If <code>null</code> is returned, clients should assume
+ * that unspecified changes have occurred and take the appropriate action.
+ * <p>
+ * The system reserves the right to trim old state in an effort to conserve
+ * space. As such, callers should be prepared to receive <code>null</code>
+ * even if they previously requested a delta for a particular project by
+ * returning that project from a <code>build</code> call.
+ * </p>
+ * <p>
+ * A non- <code>null</code> delta will only be supplied for the given
+ * project if either the result returned from the previous
+ * <code>build</code> included the project or the project is the one
+ * associated with this builder.
+ * </p>
+ * <p>
+ * If the given project was mentioned in the previous <code>build</code>
+ * and subsequently deleted, a non- <code>null</code> delta containing the
+ * deletion will be returned. If the given project was mentioned in the
+ * previous <code>build</code> and was subsequently created, the returned
+ * value will be <code>null</code>.
+ * </p>
+ * <p>
+ * A valid delta will be returned only when this method is called during a
+ * build. The delta returned will be valid only for the duration of the
+ * enclosing build execution.
+ * </p>
+ *
+ * @return the resource delta for the project or <code>null</code>
+ */
+ public final IResourceDelta getDelta(IProject project) {
+ return super.getDelta(project);
+ }
+
+ /**
+ * Returns the project for which this builder is defined.
+ *
+ * @return the project
+ */
+ public final IProject getProject() {
+ return super.getProject();
+ }
+
+ /**
+ * Returns whether the given project has already been built during this
+ * build iteration.
+ * <p>
+ * When the entire workspace is being built, the projects are built in
+ * linear sequence. This method can be used to determine if another project
+ * precedes this builder's project in that build sequence. If only a single
+ * project is being built, then there is no build order and this method will
+ * always return <code>false</code>.
+ * </p>
+ *
+ * @param project the project to check against in the current build order
+ * @return <code>true</code> if the given project has been built in this
+ * iteration, and <code>false</code> otherwise.
+ * @see #needRebuild()
+ * @since 2.1
+ */
+ public final boolean hasBeenBuilt(IProject project) {
+ return super.hasBeenBuilt(project);
+ }
+
+ /**
+ * Returns whether an interrupt request has been made for this build.
+ * Background autobuild is interrupted when another thread tries to modify
+ * the workspace concurrently with the build thread. When this occurs, the
+ * build cycle is flagged as interrupted and the build will be terminated at
+ * the earliest opportunity. This method allows long running builders to
+ * respond to this interruption in a timely manner. Builders are not
+ * required to respond to interruption requests.
+ * <p>
+ *
+ * @return <code>true</code> if the build cycle has been interrupted, and
+ * <code>false</code> otherwise.
+ * @since 3.0
+ */
+ public final boolean isInterrupted() {
+ return super.isInterrupted();
+ }
+
+ /**
+ * Indicates that this builder made changes that affect a project that
+ * precedes this project in the currently executing build order, and thus a
+ * rebuild will be necessary.
+ * <p>
+ * This is an advanced feature that builders should use with caution. This
+ * can cause workspace builds to iterate until no more builders require
+ * rebuilds.
+ * </p>
+ *
+ * @see #hasBeenBuilt(IProject)
+ * @since 2.1
+ */
+ public final void needRebuild() {
+ super.needRebuild();
+ }
+
+ /**
+ * Sets initialization data for this builder.
+ * <p>
+ * This method is part of the {@link IExecutableExtension} interface.
+ * </p>
+ * <p>
+ * Subclasses are free to extend this method to pick up initialization
+ * parameters from the plug-in plug-in manifest (<code>plugin.xml</code>)
+ * file, but should be sure to invoke this method on their superclass.
+ * <p>
+ * For example, the following method looks for a boolean-valued parameter
+ * named "trace":
+ *
+ * <pre>
+ * public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) throws CoreException {
+ * super.setInitializationData(cfig, propertyName, data);
+ * if (data instanceof Hashtable) {
+ * Hashtable args = (Hashtable) data;
+ * String traceValue = (String) args.get("trace");
+ * TRACING = (traceValue != null && traceValue.equals("true"));
+ * }
+ * }
+ * </pre>
+ * </p>
+ * @throws CoreException if fails.
+ */
+ public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
+ //default implementation does nothing
+ //thwart compiler warning
+ }
+
+ /**
+ * Informs this builder that it is being started by the build management
+ * infrastructure. By the time this method is run, the builder's project is
+ * available and <code>setInitializationData</code> has been called. The
+ * default implementation should be called by all overriding methods.
+ *
+ * @see #setInitializationData(IConfigurationElement, String, Object)
+ */
+ protected void startupOnInitialize() {
+ // reserved for future use
+ }
+
+ /**
+ * Returns the scheduling rule that is required for building
+ * the project for which this builder is defined. The default
+ * is the workspace root rule.
+ *
+ * @return scheduling rule
+ *
+ * @since 3.5
+ */
+ public ISchedulingRule getRule() {
+ return ResourcesPlugin.getWorkspace().getRoot();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ProjectScope.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.internal.preferences.EclipsePreferences;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+
+/**
+ * Object representing the project scope in the Eclipse preferences
+ * hierarchy. Can be used as a context for searching for preference
+ * values (in the <code>org.eclipse.core.runtime.IPreferencesService</code>
+ * APIs) or for determining the correct preference node to set values in the store.
+ * <p>
+ * Project preferences are stored on a per project basis in the
+ * project's content area as specified by <code>IProject#getLocation</code>.
+ * </p><p>
+ * The path for preferences defined in the project scope hierarchy
+ * is as follows: <code>/project/<projectName>/<qualifier></code>
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed. This class may be instantiated.
+ * </p>
+ * @see IProject#getLocation()
+ * @since 3.0
+ */
+public final class ProjectScope implements IScopeContext {
+
+ /**
+ * String constant (value of <code>"project"</code>) used for the
+ * scope name for this preference scope.
+ */
+ public static final String SCOPE = "project"; //$NON-NLS-1$
+
+ private IProject context;
+
+ /**
+ * Create and return a new project scope for the given project. The given
+ * project must not be <code>null</code>.
+ *
+ * @param context the project
+ * @exception IllegalArgumentException if the project is <code>null</code>
+ */
+ public ProjectScope(IProject context) {
+ super();
+ if (context == null)
+ throw new IllegalArgumentException();
+ this.context = context;
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.IScopeContext#getNode(java.lang.String)
+ */
+ public IEclipsePreferences getNode(String qualifier) {
+ if (qualifier == null)
+ throw new IllegalArgumentException();
+ return (IEclipsePreferences) Platform.getPreferencesService().getRootNode().node(SCOPE).node(context.getName()).node(qualifier);
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.preferences.IScopeContext#getLocation()
+ */
+ public IPath getLocation() {
+ IProject project = ((IResource) context).getProject();
+ IPath location = project.getLocation();
+ return location == null ? null : location.append(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME);
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.preferences.IScopeContext#getName()
+ */
+ public String getName() {
+ return SCOPE;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (!(obj instanceof ProjectScope))
+ return false;
+ ProjectScope other = (ProjectScope) obj;
+ return context.equals(other.context);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return super.hashCode() + context.getFullPath().hashCode();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourceAttributes.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 Red Hat Incorporated and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/org/documents/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API
+ * Red Hat Incorporated - initial implementation
+ * Martin Oberhuber (Wind River) - [44107] Add symbolic links to ResourceAttributes API
+ *******************************************************************************/
+
+package org.eclipse.core.resources;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.internal.utils.FileUtil;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This class represents platform specific attributes of files.
+ * Any attributes can be added, but only the attributes that are
+ * supported by the platform will be used. These methods do not set the
+ * attributes in the file system.
+ *
+ * @author Red Hat Incorporated
+ * @see IResource#getResourceAttributes()
+ * @see IResource#setResourceAttributes(ResourceAttributes)
+ * @since 3.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ResourceAttributes {
+ private int attributes;
+
+ /**
+ * Creates a new resource attributes instance with attributes
+ * taken from the specified file in the file system. If the specified
+ * file does not exist or is not accessible, this method has the
+ * same effect as calling the default constructor.
+ *
+ * @param file The file to get attributes from
+ * @return A resource attributes object
+ */
+ public static ResourceAttributes fromFile(java.io.File file) {
+ try {
+ return FileUtil.fileInfoToAttributes(EFS.getStore(file.toURI()).fetchInfo());
+ } catch (CoreException e) {
+ //file could not be accessed
+ return new ResourceAttributes();
+ }
+ }
+
+ /**
+ * Creates a new instance of <code>ResourceAttributes</code>.
+ */
+ public ResourceAttributes() {
+ super();
+ }
+
+ /**
+ * Returns whether this ResourceAttributes object is marked archive.
+ *
+ * @return <code>true</code> if this resource is marked archive,
+ * <code>false</code> otherwise
+ * @see #setArchive(boolean)
+ */
+ public boolean isArchive() {
+ return (attributes & EFS.ATTRIBUTE_ARCHIVE) != 0;
+ }
+
+ /**
+ * Returns whether this ResourceAttributes object is marked executable.
+ *
+ * @return <code>true</code> if this resource is marked executable,
+ * <code>false</code> otherwise
+ * @see #setExecutable(boolean)
+ */
+ public boolean isExecutable() {
+ return (attributes & EFS.ATTRIBUTE_EXECUTABLE) != 0;
+ }
+
+ /**
+ * Returns whether this ResourceAttributes object is marked hidden.
+ *
+ * @return <code>true</code> if this resource is marked hidden,
+ * <code>false</code> otherwise
+ * @see #setHidden(boolean)
+ * @since 3.2
+ */
+ public boolean isHidden() {
+ return (attributes & EFS.ATTRIBUTE_HIDDEN) != 0;
+ }
+
+ /**
+ * Returns whether this ResourceAttributes object is marked read only.
+ *
+ * @return <code>true</code> if this resource is marked as read only,
+ * <code>false</code> otherwise
+ * @see #setReadOnly(boolean)
+ */
+ public boolean isReadOnly() {
+ return (attributes & EFS.ATTRIBUTE_READ_ONLY) != 0;
+ }
+
+ /**
+ * Returns whether this ResourceAttributes object is marked read only.
+ *
+ * @return <code>true</code> if this resource is marked as symbolic link,
+ * <code>false</code> otherwise
+ * @see #setSymbolicLink(boolean)
+ * @since 3.4
+ */
+ public boolean isSymbolicLink() {
+ return (attributes & EFS.ATTRIBUTE_SYMLINK) != 0;
+ }
+
+ /**
+ * Sets or unsets whether this ResourceAttributes object is marked archive.
+ *
+ * @param archive <code>true</code> to set it to be archive,
+ * <code>false</code> to unset
+ * @see #isArchive()
+ */
+ public void setArchive(boolean archive) {
+ set(EFS.ATTRIBUTE_ARCHIVE, archive);
+ }
+
+ /**
+ * Clears all of the bits indicated by the mask.
+ */
+ private void set(int mask, boolean value) {
+ if (value)
+ attributes |= mask;
+ else
+ attributes &= ~mask;
+ }
+
+ /**
+ * Sets or unsets whether this ResourceAttributes object is marked executable.
+ *
+ * @param executable <code>true</code> to set it to be executable,
+ * <code>false</code> to unset
+ * @see #isExecutable()
+ */
+ public void setExecutable(boolean executable) {
+ set(EFS.ATTRIBUTE_EXECUTABLE, executable);
+ }
+
+ /**
+ * Sets or unsets whether this ResourceAttributes object is marked hidden
+ *
+ * @param hidden <code>true</code> to set it to be marked hidden,
+ * <code>false</code> to unset
+ * @see #isHidden()
+ * @since 3.2
+ */
+ public void setHidden(boolean hidden) {
+ set(EFS.ATTRIBUTE_HIDDEN, hidden);
+ }
+
+ /**
+ * Sets or unsets whether this ResourceAttributes object is marked read only.
+ *
+ * @param readOnly <code>true</code> to set it to be marked read only,
+ * <code>false</code> to unset
+ * @see #isReadOnly()
+ */
+ public void setReadOnly(boolean readOnly) {
+ set(EFS.ATTRIBUTE_READ_ONLY, readOnly);
+ }
+
+ /**
+ * Sets or unsets whether this ResourceAttributes object is marked as symbolic link.
+ *
+ * @param symLink <code>true</code> to set it to be marked as symbolic link,
+ * <code>false</code> to unset
+ * @see #isSymbolicLink()
+ * @since 3.4
+ */
+ public void setSymbolicLink(boolean symLink) {
+ set(EFS.ATTRIBUTE_SYMLINK, symLink);
+ }
+
+ /**
+ * Returns a string representation of the attributes, suitable
+ * for debugging purposes only.
+ */
+ public String toString() {
+ return "ResourceAttributes(" + attributes + ')'; //$NON-NLS-1$
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,384 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.internal.resources.*;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.framework.*;
+
+/**
+ * The plug-in runtime class for the Resources plug-in. This is
+ * the starting point for all workspace and resource manipulation.
+ * A typical sequence of events would be for a dependent plug-in
+ * to call <code>ResourcesPlugin.getWorkspace()</code>.
+ * Doing so would cause this plug-in to be activated and the workspace
+ * (if any) to be loaded from disk and initialized.
+ *
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class ResourcesPlugin extends Plugin {
+ /**
+ * Unique identifier constant (value <code>"org.eclipse.core.resources"</code>)
+ * for the standard Resources plug-in.
+ */
+ public static final String PI_RESOURCES = "org.eclipse.core.resources"; //$NON-NLS-1$
+
+ /*====================================================================
+ * Constants defining the ids of the standard workspace extension points:
+ *====================================================================*/
+
+ /**
+ * Simple identifier constant (value <code>"builders"</code>)
+ * for the builders extension point.
+ */
+ public static final String PT_BUILDERS = "builders"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"natures"</code>)
+ * for the natures extension point.
+ */
+ public static final String PT_NATURES = "natures"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"markers"</code>)
+ * for the markers extension point.
+ */
+ public static final String PT_MARKERS = "markers"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"fileModificationValidator"</code>)
+ * for the file modification validator extension point.
+ */
+ public static final String PT_FILE_MODIFICATION_VALIDATOR = "fileModificationValidator"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"moveDeleteHook"</code>)
+ * for the move/delete hook extension point.
+ *
+ * @since 2.0
+ */
+ public static final String PT_MOVE_DELETE_HOOK = "moveDeleteHook"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"teamHook"</code>)
+ * for the team hook extension point.
+ *
+ * @since 2.1
+ */
+ public static final String PT_TEAM_HOOK = "teamHook"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"refreshProviders"</code>)
+ * for the auto-refresh refresh providers extension point.
+ *
+ * @since 3.0
+ */
+ public static final String PT_REFRESH_PROVIDERS = "refreshProviders"; //$NON-NLS-1$
+
+ /**
+ * Simple identifier constant (value <code>"modelProviders"</code>)
+ * for the model providers extension point.
+ *
+ * @since 3.2
+ */
+ public static final String PT_MODEL_PROVIDERS = "modelProviders"; //$NON-NLS-1$
+
+ /**
+ * Constant identifying the job family identifier for the background autobuild job.
+ *
+ * @see IJobManager#join(Object, IProgressMonitor)
+ * @since 3.0
+ */
+ public static final Object FAMILY_AUTO_BUILD = new Object();
+
+ /**
+ * Constant identifying the job family identifier for the background auto-refresh job.
+ *
+ * @see IJobManager#join(Object, IProgressMonitor)
+ * @since 3.1
+ */
+ public static final Object FAMILY_AUTO_REFRESH = new Object();
+
+ /**
+ * Constant identifying the job family identifier for a background build job. All clients
+ * that schedule background jobs for performing builds should include this job
+ * family in their implementation of <code>belongsTo</code>.
+ *
+ * @see IJobManager#join(Object, IProgressMonitor)
+ * @see Job#belongsTo(Object)
+ * @since 3.0
+ */
+ public static final Object FAMILY_MANUAL_BUILD = new Object();
+
+ /**
+ * Constant identifying the job family identifier for a background refresh job. All clients
+ * that schedule background jobs for performing refreshing should include this job
+ * family in their implementation of <code>belongsTo</code>.
+ *
+ * @see IJobManager#join(Object, IProgressMonitor)
+ * @see Job#belongsTo(Object)
+ * @since 3.4
+ */
+ public static final Object FAMILY_MANUAL_REFRESH = new Object();
+
+ /**
+ * Name of a preference indicating the encoding to use when reading text
+ * files in the workspace. The value is a string, and may
+ * be the default empty string, indicating that the file system encoding should
+ * be used instead. The file system encoding can be retrieved using
+ * <code>System.getProperty("file.encoding")</code>.
+ * There is also a convenience method <code>getEncoding</code> which returns
+ * the value of this preference, or the file system encoding if this
+ * preference is not set.
+ * <p>
+ * Note that there is no guarantee that the value is a supported encoding.
+ * Callers should be prepared to handle <code>UnsupportedEncodingException</code>
+ * where this encoding is used.
+ * </p>
+ *
+ * @see #getEncoding()
+ * @see java.io.UnsupportedEncodingException
+ */
+ public static final String PREF_ENCODING = "encoding"; //$NON-NLS-1$
+
+ /**
+ * Common prefix for workspace preference names.
+ * @since 2.1
+ */
+ private static final String PREF_DESCRIPTION_PREFIX = "description."; //$NON-NLS-1$
+
+ /**
+ * @deprecated Do not use.
+ * @since 3.0
+ */
+ public static final String PREF_MAX_NOTIFICATION_DELAY = "maxnotifydelay"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring whether the workspace performs auto-
+ * builds.
+ *
+ * @see IWorkspaceDescription#isAutoBuilding()
+ * @see IWorkspaceDescription#setAutoBuilding(boolean)
+ * @since 2.1
+ */
+ public static final String PREF_AUTO_BUILDING = PREF_DESCRIPTION_PREFIX + "autobuilding"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring the order projects in the workspace
+ * are built.
+ *
+ * @see IWorkspaceDescription#getBuildOrder()
+ * @see IWorkspaceDescription#setBuildOrder(String[])
+ * @since 2.1
+ */
+ public static final String PREF_BUILD_ORDER = PREF_DESCRIPTION_PREFIX + "buildorder"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring whether to use the workspace's
+ * default order for building projects.
+ * @since 2.1
+ */
+ public static final String PREF_DEFAULT_BUILD_ORDER = PREF_DESCRIPTION_PREFIX + "defaultbuildorder"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring the maximum number of times that the
+ * workspace should rebuild when builders affect projects that have already
+ * been built.
+ *
+ * @see IWorkspaceDescription#getMaxBuildIterations()
+ * @see IWorkspaceDescription#setMaxBuildIterations(int)
+ * @since 2.1
+ */
+ public static final String PREF_MAX_BUILD_ITERATIONS = PREF_DESCRIPTION_PREFIX + "maxbuilditerations"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring the maximum number of milliseconds a
+ * file state should be kept in the local history
+ *
+ * @see IWorkspaceDescription#getFileStateLongevity()
+ * @see IWorkspaceDescription#setFileStateLongevity(long)
+ * @since 2.1
+ */
+ public static final String PREF_FILE_STATE_LONGEVITY = PREF_DESCRIPTION_PREFIX + "filestatelongevity"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring the maximum permitted size of a file
+ * to be stored in the local history
+ *
+ * @see IWorkspaceDescription#getMaxFileStateSize()
+ * @see IWorkspaceDescription#setMaxFileStateSize(long)
+ * @since 2.1
+ */
+ public static final String PREF_MAX_FILE_STATE_SIZE = PREF_DESCRIPTION_PREFIX + "maxfilestatesize"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring the maximum number of states per
+ * file that can be stored in the local history.
+ *
+ * @see IWorkspaceDescription#getMaxFileStates()
+ * @see IWorkspaceDescription#setMaxFileStates(int)
+ * @since 2.1
+ */
+ public static final String PREF_MAX_FILE_STATES = PREF_DESCRIPTION_PREFIX + "maxfilestates"; //$NON-NLS-1$
+ /**
+ * Name of a preference for configuring the amount of time in milliseconds
+ * between automatic workspace snapshots
+ *
+ * @see IWorkspaceDescription#getSnapshotInterval()
+ * @see IWorkspaceDescription#setSnapshotInterval(long)
+ * @since 2.1
+ */
+ public static final String PREF_SNAPSHOT_INTERVAL = PREF_DESCRIPTION_PREFIX + "snapshotinterval"; //$NON-NLS-1$
+
+ /**
+ * Name of a preference for turning off support for linked resources. When
+ * this preference is set to "true", attempting to create linked resources will fail.
+ * @since 2.1
+ */
+ public static final String PREF_DISABLE_LINKING = PREF_DESCRIPTION_PREFIX + "disableLinking";//$NON-NLS-1$
+
+ /**
+ * Name of a preference for configuring whether the workspace performs auto-
+ * refresh.
+ * @since 3.0
+ */
+ public static final String PREF_AUTO_REFRESH = "refresh.enabled"; //$NON-NLS-1$
+
+ /**
+ * The single instance of this plug-in runtime class.
+ */
+ private static ResourcesPlugin plugin;
+
+ /**
+ * The workspace managed by the single instance of this
+ * plug-in runtime class, or <code>null</code> is there is none.
+ */
+ private static Workspace workspace = null;
+
+ private ServiceRegistration workspaceRegistration;
+
+ /**
+ * Constructs an instance of this plug-in runtime class.
+ * <p>
+ * An instance of this plug-in runtime class is automatically created
+ * when the facilities provided by the Resources plug-in are required.
+ * <b>Clients must never explicitly instantiate a plug-in runtime class.</b>
+ * </p>
+ */
+ public ResourcesPlugin() {
+ plugin = this;
+ }
+
+ /**
+ * Constructs a brand new workspace structure at the location in the local file system
+ * identified by the given path and returns a new workspace object.
+ *
+ * @exception CoreException if the workspace structure could not be constructed.
+ * Reasons include:
+ * <ul>
+ * <li> There is an existing workspace structure on at the given location
+ * in the local file system.
+ * <li> A file exists at the given location in the local file system.
+ * <li> A directory could not be created at the given location in the
+ * local file system.
+ * </ul>
+ */
+ private static void constructWorkspace() throws CoreException {
+ new LocalMetaArea().createMetaArea();
+ }
+
+ /**
+ * Returns the encoding to use when reading text files in the workspace.
+ * This is the value of the <code>PREF_ENCODING</code> preference, or the
+ * file system encoding (<code>System.getProperty("file.encoding")</code>)
+ * if the preference is not set.
+ * <p>
+ * Note that this method does not check whether the result is a supported
+ * encoding. Callers should be prepared to handle
+ * <code>UnsupportedEncodingException</code> where this encoding is used.
+ *
+ * @return the encoding to use when reading text files in the workspace
+ * @see java.io.UnsupportedEncodingException
+ */
+ public static String getEncoding() {
+ String enc = getPlugin().getPluginPreferences().getString(PREF_ENCODING);
+ if (enc == null || enc.length() == 0) {
+ enc = System.getProperty("file.encoding"); //$NON-NLS-1$
+ }
+ return enc;
+ }
+
+ /**
+ * Returns the Resources plug-in.
+ *
+ * @return the single instance of this plug-in runtime class
+ */
+ public static ResourcesPlugin getPlugin() {
+ return plugin;
+ }
+
+ /**
+ * Returns the workspace. The workspace is not accessible after the resources
+ * plug-in has shutdown.
+ *
+ * @return the workspace that was created by the single instance of this
+ * plug-in class.
+ */
+ public static IWorkspace getWorkspace() {
+ if (workspace == null)
+ throw new IllegalStateException(Messages.resources_workspaceClosed);
+ return workspace;
+ }
+
+ /**
+ * This implementation of the corresponding {@link BundleActivator} method
+ * closes the workspace without saving.
+ * @see BundleActivator#stop(BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ super.stop(context);
+ if (workspace == null)
+ return;
+ workspaceRegistration.unregister();
+ // save the preferences for this plug-in
+ getPlugin().savePluginPreferences();
+ workspace.close(null);
+
+ // Forget workspace only if successfully closed, to
+ // make it easier to debug cases where close() is failing.
+ workspace = null;
+ workspaceRegistration = null;
+ }
+
+ /**
+ * This implementation of the corresponding {@link BundleActivator} method
+ * opens the workspace.
+ * @see BundleActivator#start(BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ if (!new LocalMetaArea().hasSavedWorkspace()) {
+ constructWorkspace();
+ }
+ Workspace.DEBUG = ResourcesPlugin.getPlugin().isDebugging();
+ // Remember workspace before opening, to
+ // make it easier to debug cases where open() is failing.
+ workspace = new Workspace();
+ PlatformURLResourceConnection.startup(workspace.getRoot().getLocation());
+ IStatus result = workspace.open(null);
+ if (!result.isOK())
+ getLog().log(result);
+ workspaceRegistration = context.registerService(IWorkspace.SERVICE_NAME, workspace, null);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/WorkspaceJob.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.internal.resources.InternalWorkspaceJob;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * A job that makes an atomic modification to the workspace. Clients must
+ * implement the abstract method <code>runInWorkspace</code> instead
+ * of the usual <code>Job.run</code> method.
+ * <p>
+ * After running a method that modifies resources in the workspace,
+ * registered listeners receive after-the-fact notification of
+ * what just transpired, in the form of a resource change event.
+ * This method allows clients to call a number of
+ * methods that modify resources and only have resource
+ * change event notifications reported at the end of the entire
+ * batch. This mechanism is used to avoid unnecessary builds
+ * and notifications.
+ * </p>
+ * <p>
+ * Platform may decide to perform notifications during the operation.
+ * The reason for this is that it is possible for multiple threads
+ * to be modifying the workspace concurrently. When one thread finishes modifying
+ * the workspace, a notification is required to prevent responsiveness problems,
+ * even if the other operation has not yet completed.
+ * </p>
+ * <p>
+ * A WorkspaceJob is the asynchronous equivalent of IWorkspaceRunnable
+ * </p>
+ * <p>
+ * Note that the workspace is not locked against other threads during the execution
+ * of a workspace job. Other threads can be modifying the workspace concurrently
+ * with a workspace job. To obtain exclusive access to a portion of the workspace,
+ * set the scheduling rule on the job to be a resource scheduling rule. The
+ * interface <tt>IResourceRuleFactory</tt> is used to create a scheduling rule
+ * for a particular workspace modification operation.
+ * </p>
+ * @see IWorkspaceRunnable
+ * @see org.eclipse.core.resources.IResourceRuleFactory
+ * @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)
+ * @since 3.0
+ */
+public abstract class WorkspaceJob extends InternalWorkspaceJob {
+ /**
+ * Creates a new workspace job.
+ * @param name the name of the job
+ */
+ public WorkspaceJob(String name) {
+ super(name);
+ }
+
+ /**
+ * Runs the operation, reporting progress to and accepting
+ * cancelation requests from the given progress monitor.
+ * <p>
+ * Implementors of this method should check the progress monitor
+ * for cancelation when it is safe and appropriate to do so. The cancelation
+ * request should be propagated to the caller by throwing
+ * <code>OperationCanceledException</code>.
+ * </p>
+ *
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting and cancelation are not desired
+ * @return the result of running the operation
+ * @exception CoreException if this operation fails.
+ */
+ public abstract IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/WorkspaceLock.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A lock used to control write access to the resources in a workspace.
+ * Clients may subclass.
+ *
+ * @see IWorkspace#setWorkspaceLock(WorkspaceLock)
+ * @deprecated it is no longer possible to override the workspace lock behavior.
+ * This functionality is now provided in the platform API by implementing the
+ * org.eclipse.core.runtime.jobs.ILockListener interface.
+ */
+public class WorkspaceLock {
+
+ /**
+ * Returns a new workspace lock.
+ * @throws CoreException if fails.
+ */
+ public WorkspaceLock(IWorkspace workspace) throws CoreException {
+ //default implementation does nothing
+ //thwart compiler warning
+ }
+
+ /**
+ * Attempts to acquire this lock. Callers will block indefinitely until this lock comes
+ * available to them.
+ * <p>
+ * Clients may extend this method but should not otherwise call it.
+ * </p>
+ * @throws InterruptedException if fails while acquiring the lock.
+ * @see #release()
+ */
+ public boolean acquire() throws InterruptedException {
+ //default implementation does nothing
+ //thwart compiler warning
+ return false;
+ }
+
+ /**
+ * Returns the thread that currently owns the workspace lock.
+ */
+ protected Thread getCurrentOperationThread() {
+ //deprecated API
+ return null;
+ }
+
+ /**
+ * Releases this lock allowing others to acquire it.
+ * <p>
+ * Clients may extend this method but should not otherwise call it.
+ * </p>
+ * @see #acquire()
+ */
+ public void release() {
+ //deprecated API
+ }
+
+ /**
+ * Returns whether the workspace tree is locked
+ * for resource changes.
+ *
+ * @return <code>true</code> if the tree is locked, otherwise
+ * <code>false</code>
+ */
+ protected boolean isTreeLocked() {
+ //deprecated API
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/CompositeResourceMapping.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import java.util.*;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A resource mapping that obtains the traversals for its model object
+ * from a set of child mappings.
+ * <p>
+ * This class is not intended to be subclasses by clients.
+ *
+ * @since 3.2
+ */
+public final class CompositeResourceMapping extends ResourceMapping {
+
+ private final ResourceMapping[] mappings;
+ private final Object modelObject;
+ private IProject[] projects;
+ private String providerId;
+
+ /**
+ * Create a composite mapping that obtains its traversals from a set of sub-mappings.
+ * @param modelObject the model object for this mapping
+ * @param mappings the sub-mappings from which the traversals are obtained
+ */
+ public CompositeResourceMapping(String providerId, Object modelObject, ResourceMapping[] mappings) {
+ this.modelObject = modelObject;
+ this.mappings = mappings;
+ this.providerId = providerId;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ResourceMapping#contains(org.eclipse.core.resources.mapping.ResourceMapping)
+ */
+ public boolean contains(ResourceMapping mapping) {
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping childMapping = mappings[i];
+ if (childMapping.contains(mapping)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the resource mappings contained in this composite.
+ * @return Return the resource mappings contained in this composite.
+ */
+ public ResourceMapping[] getMappings() {
+ return mappings;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ResourceMapping#getModelObject()
+ */
+ public Object getModelObject() {
+ return modelObject;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ResourceMapping#getModelProviderId()
+ */
+ public String getModelProviderId() {
+ return providerId;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ResourceMapping#getProjects()
+ */
+ public IProject[] getProjects() {
+ if (projects == null) {
+ Set result = new HashSet();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ result.addAll(Arrays.asList(mapping.getProjects()));
+ }
+ projects = (IProject[]) result.toArray(new IProject[result.size()]);
+ }
+ return projects;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.mapping.ResourceMapping#getTraversals(org.eclipse.core.internal.resources.mapping.ResourceMappingContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public ResourceTraversal[] getTraversals(ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ if (monitor == null)
+ monitor = new NullProgressMonitor();
+ try {
+ monitor.beginTask("", 100 * mappings.length); //$NON-NLS-1$
+ List result = new ArrayList();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ result.addAll(Arrays.asList(mapping.getTraversals(context, new SubProgressMonitor(monitor, 100))));
+ }
+ return (ResourceTraversal[]) result.toArray(new ResourceTraversal[result.size()]);
+ } finally {
+ monitor.done();
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/IModelProviderDescriptor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A model provider descriptor contains information about a model provider
+ * obtained from the plug-in manifest (<code>plugin.xml</code>) file.
+ * <p>
+ * Model provider descriptors are platform-defined objects that exist
+ * independent of whether that model provider's plug-in has been started.
+ * In contrast, a model provider's runtime object (<code>ModelProvider</code>)
+ * generally runs plug-in-defined code.
+ * </p>
+ *
+ * @see org.eclipse.core.resources.mapping.ModelProvider
+ * @since 3.2
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IModelProviderDescriptor {
+
+ /**
+ * Return the ids of model providers that this model provider extends.
+ * @return the ids of model providers that this model provider extends
+ */
+ public String[] getExtendedModels();
+
+ /**
+ * Returns the unique identifier of this model provider.
+ * <p>
+ * The model provider identifier is composed of the model provider's
+ * plug-in id and the simple id of the provider extension. For example, if
+ * plug-in <code>"com.xyz"</code> defines a provider extension with id
+ * <code>"myModelProvider"</code>, the unique model provider identifier will be
+ * <code>"com.xyz.myModelProvider"</code>.
+ * </p>
+ *
+ * @return the unique model provider identifier
+ */
+ public String getId();
+
+ /**
+ * Returns a displayable label for this model provider.
+ * Returns the empty string if no label for this provider
+ * is specified in the plug-in manifest file.
+ * <p> Note that any translation specified in the plug-in manifest
+ * file is automatically applied.
+ * </p>
+ *
+ * @return a displayable string label for this model provider,
+ * possibly the empty string
+ */
+ public String getLabel();
+
+ /**
+ * From the provides set of resources, return those that match the enablement
+ * rule specified for the model provider descriptor. The resource mappings
+ * for the returned resources can then be obtained by invoking
+ * {@link ModelProvider#getMappings(IResource[], ResourceMappingContext, IProgressMonitor)}
+ *
+ * @param resources the resources
+ * @return the resources that match the descriptor's enablement rule
+ */
+ public IResource[] getMatchingResources(IResource[] resources) throws CoreException;
+
+ /**
+ * Return the set of traversals that overlap with the resources that
+ * this descriptor matches.
+ *
+ * @param traversals the traversals being tested
+ * @return the subset of these traversals that overlap with the resources
+ * that match this descriptor
+ * @throws CoreException
+ */
+ public ResourceTraversal[] getMatchingTraversals(ResourceTraversal[] traversals) throws CoreException;
+
+ /**
+ * Return the model provider for this descriptor, instantiating it if it is
+ * the first time the method is called.
+ *
+ * @return the model provider for this descriptor
+ * @exception CoreException if the model provider could not be instantiated for
+ * some reason
+ */
+ public ModelProvider getModelProvider() throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/IResourceChangeDescriptionFactory.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * This factory is used to build a resource delta that represents a proposed change
+ * that can then be passed to the {@link ResourceChangeValidator#validateChange(IResourceDelta, IProgressMonitor)}
+ * method in order to validate the change with any model providers stored in those resources.
+ * The deltas created by calls to the methods of this interface will be the same as
+ * those generated by the workspace if the proposed operations were performed.
+ * <p>
+ * This factory does not validate that the proposed operation is valid given the current
+ * state of the resources and any other proposed changes. It only records the
+ * delta that would result.
+ *
+ * @see ResourceChangeValidator
+ * @see ModelProvider
+ * @since 3.2
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IResourceChangeDescriptionFactory {
+
+ /**
+ * Record a delta that represents a content change for the given file.
+ * @param file the file whose contents will be changed
+ */
+ public void change(IFile file);
+
+ /**
+ * Record the set of deltas representing the closed of a project.
+ * @param project the project that will be closed
+ */
+ public void close(IProject project);
+
+ /**
+ * Record the set of deltas representing a copy of the given resource to the
+ * given workspace path.
+ * @param resource the resource that will be copied
+ * @param destination the full workspace path of the destination the resource is being copied to
+ */
+ public void copy(IResource resource, IPath destination);
+
+ /**
+ * Record a delta that represents a resource being created.
+ * @param resource the resource that is created
+ */
+ public void create(IResource resource);
+
+ /**
+ * Record the set of deltas representing a deletion of the given resource.
+ * @param resource the resource that will be deleted
+ */
+ public void delete(IResource resource);
+
+ /**
+ * Return the proposed delta that has been accumulated by this factory.
+ * @return the proposed delta that has been accumulated by this factory
+ */
+ public IResourceDelta getDelta();
+
+ /**
+ * Record the set of deltas representing a move of the given resource to the
+ * given workspace path. Note that this API is used to describe a resource
+ * being moved to another path in the workspace, rather than a move in the
+ * file system.
+ * @param resource the resource that will be moved
+ * @param destination the full workspace path of the destination the resource is being moved to
+ */
+ public void move(IResource resource, IPath destination);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ModelProvider.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import java.util.*;
+import org.eclipse.core.internal.resources.mapping.ModelProviderManager;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * Represents the provider of a logical model. The main purpose of this
+ * API is to support batch operations on sets of <code>ResourceMapping</code>
+ * objects that are part of the same model.
+ *
+ * <p>
+ * This class may be subclassed by clients.
+ * </p>
+ * @see org.eclipse.core.resources.mapping.ResourceMapping
+ * @since 3.2
+ */
+public abstract class ModelProvider extends PlatformObject {
+
+ /**
+ * The model provider id of the Resources model.
+ */
+ public static final String RESOURCE_MODEL_PROVIDER_ID = "org.eclipse.core.resources.modelProvider"; //$NON-NLS-1$
+
+ private IModelProviderDescriptor descriptor;
+
+ /**
+ * Return the descriptor for the model provider of the given id
+ * or <code>null</code> if the provider has not been registered.
+ * @param id a model provider id.
+ * @return the descriptor for the model provider of the given id
+ * or <code>null</code> if the provider has not been registered
+ */
+ public static IModelProviderDescriptor getModelProviderDescriptor(String id) {
+ IModelProviderDescriptor[] descs = ModelProviderManager.getDefault().getDescriptors();
+ for (int i = 0; i < descs.length; i++) {
+ IModelProviderDescriptor descriptor = descs[i];
+ if (descriptor.getId().equals(id)) {
+ return descriptor;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the descriptors for all model providers that are registered.
+ *
+ * @return the descriptors for all model providers that are registered.
+ */
+ public static IModelProviderDescriptor[] getModelProviderDescriptors() {
+ return ModelProviderManager.getDefault().getDescriptors();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof ModelProvider) {
+ ModelProvider other = (ModelProvider) obj;
+ return other.getDescriptor().getId().equals(getDescriptor().getId());
+ }
+ return super.equals(obj);
+ }
+
+ /**
+ * Return the descriptor of this model provider. The descriptor
+ * is set during initialization so implements cannot call this method
+ * until after the <code>initialize</code> method is invoked.
+ * @return the descriptor of this model provider
+ */
+ public final IModelProviderDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Returns the unique identifier of this model provider.
+ * <p>
+ * The model provider identifier is composed of the model provider's
+ * plug-in id and the simple id of the provider extension. For example, if
+ * plug-in <code>"com.xyz"</code> defines a provider extension with id
+ * <code>"myModelProvider"</code>, the unique model provider identifier will be
+ * <code>"com.xyz.myModelProvider"</code>.
+ * </p>
+ *
+ * @return the unique model provider identifier
+ */
+ public final String getId() {
+ return descriptor.getId();
+ }
+
+ /**
+ * Return the resource mappings that cover the given resource.
+ * By default, an empty array is returned. Subclass may override
+ * this method but should consider overriding either
+ * {@link #getMappings(IResource[], ResourceMappingContext, IProgressMonitor)}
+ * or ({@link #getMappings(ResourceTraversal[], ResourceMappingContext, IProgressMonitor)}
+ * if more context is needed to determine the proper mappings.
+ *
+ * @param resource the resource
+ * @param context a resource mapping context
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the resource mappings that cover the given resource.
+ * @exception CoreException
+ */
+ public ResourceMapping[] getMappings(IResource resource, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ return new ResourceMapping[0];
+ }
+
+ /**
+ * Return the set of mappings that cover the given resources.
+ * This method is used to map operations on resources to
+ * operations on resource mappings. By default, this method
+ * calls <code>getMapping(IResource)</code> for each resource.
+ * <p>
+ * Subclasses may override this method.
+ * </p>
+ *
+ * @param resources the resources
+ * @param context a resource mapping context
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the set of mappings that cover the given resources
+ * @exception CoreException
+ */
+ public ResourceMapping[] getMappings(IResource[] resources, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ Set mappings = new HashSet();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ ResourceMapping[] resourceMappings = getMappings(resource, context, monitor);
+ if (resourceMappings.length > 0)
+ mappings.addAll(Arrays.asList(resourceMappings));
+ }
+ return (ResourceMapping[]) mappings.toArray(new ResourceMapping[mappings.size()]);
+ }
+
+ /**
+ * Return the set of mappings that overlap with the given resource traversals.
+ * This method is used to map operations on resources to
+ * operations on resource mappings. By default, this method
+ * calls {@link #getMappings(IResource[], ResourceMappingContext, IProgressMonitor)}
+ * with the resources extract from each traversal.
+ * <p>
+ * Subclasses may override this method.
+ * </p>
+ *
+ * @param traversals the traversals
+ * @param context a resource mapping context
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the set of mappings that overlap with the given resource traversals
+ */
+ public ResourceMapping[] getMappings(ResourceTraversal[] traversals, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ Set result = new HashSet();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ ResourceMapping[] mappings = getMappings(traversal.getResources(), context, monitor);
+ for (int j = 0; j < mappings.length; j++)
+ result.add(mappings[j]);
+ }
+ return (ResourceMapping[]) result.toArray(new ResourceMapping[result.size()]);
+ }
+
+ /**
+ * Return a set of traversals that cover the given resource mappings. The
+ * provided mappings must be from this provider or one of the providers this
+ * provider extends.
+ * <p>
+ * The default implementation accumulates the traversals from the given
+ * mappings. Subclasses can override to provide a more optimal
+ * transformation.
+ * </p>
+ *
+ * @param mappings the mappings being mapped to resources
+ * @param context the context used to determine the set of traversals that
+ * cover the mappings
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a set of traversals that cover the given mappings
+ * @exception CoreException
+ */
+ public ResourceTraversal[] getTraversals(ResourceMapping[] mappings, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ try {
+ monitor.beginTask("", 100 * mappings.length); //$NON-NLS-1$
+ List traversals = new ArrayList();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ traversals.addAll(Arrays.asList(mapping.getTraversals(context, new SubProgressMonitor(monitor, 100))));
+ }
+ return (ResourceTraversal[]) traversals.toArray(new ResourceTraversal[traversals.size()]);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return getDescriptor().getId().hashCode();
+ }
+
+ /**
+ * This method is called by the model provider framework when the model
+ * provider is instantiated. This method should not be called by clients and
+ * cannot be overridden by subclasses. However, it invokes the
+ * <code>initialize</code> method once the descriptor is set so subclasses
+ * can override that method if they need to do additional initialization.
+ *
+ * @param desc the description of the provider as it appears in the plugin manifest
+ */
+ public final void init(IModelProviderDescriptor desc) {
+ if (descriptor != null)
+ // prevent subsequent calls from damaging this instance
+ return;
+ descriptor = desc;
+ initialize();
+ }
+
+ /**
+ * Initialization method that is called after the descriptor
+ * of this provider is set. Subclasses may override.
+ */
+ protected void initialize() {
+ // Do nothing
+ }
+
+ /**
+ * Validate the proposed changes contained in the given delta.
+ * <p>
+ * This method must return either a {@link ModelStatus}, or a {@link MultiStatus}
+ * whose children are {@link ModelStatus}. The severity of the returned status
+ * indicates the severity of the possible side-effects of the operation. Any
+ * severity other than <code>OK</code> will be shown to the user. The
+ * message should be a human readable message that will allow the user to
+ * make a decision on whether to continue with the operation. The model
+ * provider id should indicate which model is flagging the possible side effects.
+ * <p>
+ * This default implementation accepts all changes and returns a status with
+ * severity <code>OK</code>. Subclasses should override to perform
+ * validation specific to their model.
+ * </p>
+ *
+ * @param delta a delta tree containing the proposed changes
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a status indicating any potential side effects
+ * on the model that provided this validator.
+ */
+ public IStatus validateChange(IResourceDelta delta, IProgressMonitor monitor) {
+ return new ModelStatus(IStatus.OK, ResourcesPlugin.PI_RESOURCES, descriptor.getId(), Status.OK_STATUS.getMessage());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ModelStatus.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A status returned by a model from the resource operation validator.
+ * The severity indicates the severity of the possible side effects
+ * of the operation. Any severity other than <code>OK</code> should be
+ * shown to the user. The message should be a human readable message that
+ * will allow the user to make a decision as to whether to continue with the
+ * operation. The model provider id should indicate which model is flagging the
+ * the possible side effects.
+ * <p>
+ * Clients may instantiate or subclass this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class ModelStatus extends Status {
+
+ private final String modelProviderId;
+
+ /**
+ * Create a model status.
+ *
+ * @param severity the severity
+ * @param pluginId the plugin id
+ * @param modelProviderId the model provider id
+ * @param message the message
+ */
+ public ModelStatus(int severity, String pluginId, String modelProviderId, String message) {
+ super(severity, pluginId, 0, message, null);
+ Assert.isNotNull(modelProviderId);
+ this.modelProviderId = modelProviderId;
+ }
+
+ /**
+ * Return the id of the model provider from which this status originated.
+ *
+ * @return the id of the model provider from which this status originated
+ */
+ public String getModelProviderId() {
+ return modelProviderId;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/RemoteResourceMappingContext.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A remote mapping context provides a model element with a view of the remote
+ * state of local resources as they relate to a repository operation that is in
+ * progress. A repository provider can pass an instance of this interface to a
+ * model element when obtaining a set of traversals for a model element. This
+ * allows the model element to query the remote state of a resource in order to
+ * determine if there are resources that exist remotely but do not exist locally
+ * that should be included in the traversal.
+ * <p>
+ * This class may be subclassed by clients.
+ * </p>
+ *
+ * @see ResourceMapping
+ * @see ResourceMappingContext
+ * @since 3.2
+ */
+public abstract class RemoteResourceMappingContext extends ResourceMappingContext {
+
+ /**
+ * Refresh flag constant (bit mask value 1) indicating that the mapping will
+ * be making use of the contents of the files covered by the traversals
+ * being refreshed.
+ */
+ public static final int FILE_CONTENTS_REQUIRED = 1;
+
+ /**
+ * Refresh flag constant (bit mask value 0) indicating that no additional
+ * refresh behavior is required.
+ */
+ public static final int NONE = 0;
+
+ /**
+ * For three-way comparisons, returns an instance of IStorage in order to
+ * allow the caller to access the contents of the base resource that
+ * corresponds to the given local resource. The base of a resource is the
+ * contents of the resource before any local modifications were made. If the
+ * base file does not exist, or if this is a two-way comparison, <code>null</code>
+ * is returned. The provided local file handle need not exist locally. A exception
+ * is thrown if the corresponding base resource is not a file.
+ * <p>
+ * This method may be long running as a server may need to be contacted to
+ * obtain the contents of the file.
+ * </p>
+ *
+ * @param file the local file
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a storage that provides access to the contents of the local
+ * resource's corresponding remote resource. If the remote file does not
+ * exist, <code>null</code> is returned
+ * @exception CoreException if the contents could not be fetched. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.
+ * <li>The corresponding remote resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ */
+ public abstract IStorage fetchBaseContents(IFile file, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns the members of the base resource corresponding to the given container.
+ * The container and the returned members need not exist locally and may not
+ * include all children that exist locally. An empty list is returned if the base resource
+ * is empty or does not exist. An exception is thrown if the base resource is not
+ * capable of having members. This method returns <code>null</code> if
+ * the base members cannot be computed, in which case clients should call
+ * {@link #fetchMembers(IContainer, IProgressMonitor)} which returns the
+ * combined members for the base and remote.
+ * </p>
+ * <p>
+ * This method may be long running as a server may need to be contacted to
+ * obtain the members of the base resource.
+ * </p>
+ * <p>
+ * This default implementation always returns <code>null</code>, but subclasses
+ * may override.
+ * </p>
+ * @param container the local container
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the members of the base resource corresponding to the given container
+ * @exception CoreException if the members could not be fetched. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.</li>
+ * <li>The base resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ * @since 3.3
+ */
+ public IResource[] fetchBaseMembers(IContainer container, IProgressMonitor monitor) throws CoreException {
+ //default implementation does nothing
+ //thwart compiler warning
+ return null;
+ }
+
+ /**
+ * Returns the combined members of the base and remote resources corresponding
+ * to the given container. The container need not exist locally and the result may
+ * include entries that do not exist locally and may not include all local
+ * children. An empty list is returned if the remote resource which
+ * corresponds to the container is empty or if the remote does not exist. An
+ * exception is thrown if the corresponding remote is not capable of having
+ * members.
+ * <p>
+ * This method may be long running as a server may need to be contacted to
+ * obtain the members of the container's corresponding remote resource.
+ * </p>
+ *
+ * @param container the local container
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return returns the combined members of the base and remote resources
+ * corresponding to the given container.
+ * @exception CoreException if the members could not be fetched. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.</li>
+ * <li>The corresponding remote resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ */
+ public abstract IResource[] fetchMembers(IContainer container, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns an instance of IStorage in order to allow the caller to access
+ * the contents of the remote that corresponds to the given local resource.
+ * If the remote file does not exist, <code>null</code> is returned. The
+ * provided local file handle need not exist locally. A exception is thrown
+ * if the corresponding remote resource is not a file.
+ * <p>
+ * This method may be long running as a server may need to be contacted to
+ * obtain the contents of the file.
+ * </p>
+ *
+ * @param file the local file
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a storage that provides access to the contents of the local
+ * resource's corresponding remote resource. If the remote file does not
+ * exist, <code>null</code> is returned
+ * @exception CoreException if the contents could not be fetched. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.</li>
+ * <li>The corresponding remote resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ */
+ public abstract IStorage fetchRemoteContents(IFile file, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Returns the members of the remote resource corresponding to the given container.
+ * The container and the returned members need not exist locally and may not
+ * include all children that exist locally. An empty list is returned if the remote resource
+ * is empty or does not exist. An exception is thrown if the remote resource is not
+ * capable of having members. This method returns <code>null</code> if
+ * the remote members cannot be computed, in which case clients should call
+ * {@link #fetchMembers(IContainer, IProgressMonitor)} which returns the
+ * combined members for the base and remote.
+ * </p>
+ * <p>
+ * This method may be long running as a server may need to be contacted to
+ * obtain the members of the remote resource.
+ * </p>
+ * <p>
+ * This default implementation always returns <code>null</code>, but subclasses
+ * may override.
+ * </p>
+ * @param container the local container
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return the members of the remote resource corresponding to the given container
+ * @exception CoreException if the members could not be fetched. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.</li>
+ * <li>The remote resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ * @since 3.3
+ */
+ public IResource[] fetchRemoteMembers(IContainer container, IProgressMonitor monitor) throws CoreException {
+ //default implementation does nothing
+ //thwart compiler warning
+ return null;
+ }
+
+ /**
+ * Return the list of projects that apply to this context.
+ * In other words, the context is only capable of querying the
+ * remote state for projects that are contained in the
+ * returned list.
+ * @return the list of projects that apply to this context
+ */
+ public abstract IProject[] getProjects();
+
+ /**
+ * For three-way comparisons, this method indicates whether local
+ * modifications have been made to the given resource. For two-way
+ * comparisons, calling this method has the same effect as calling
+ * {@link #hasRemoteChange(IResource, IProgressMonitor)}.
+ *
+ * @param resource the resource being tested
+ * @param monitor a progress monitor
+ * @return whether the resource contains local modifications
+ * @exception CoreException if the contents could not be compared. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.</li>
+ * <li>The corresponding remote resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ */
+ public abstract boolean hasLocalChange(IResource resource, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * For two-way comparisons, return whether the contents of the corresponding
+ * remote differs from the content of the local file in the context of the
+ * current operation. By this we mean that this method will return
+ * <code>true</code> if the remote contents differ from the local
+ * contents.
+ * <p>
+ * For three-way comparisons, return whether the contents of the
+ * corresponding remote differ from the contents of the base. In other
+ * words, this method returns <code>true</code> if the corresponding
+ * remote has changed since the last time the local resource was updated
+ * with the remote contents.
+ * <p>
+ * For two-way comparisons, return <code>true</code> if the remote
+ * contents differ from the local contents. In this case, this method is
+ * equivalent to {@link #hasLocalChange(IResource, IProgressMonitor)}
+ * <p>
+ * This can be used by clients to determine if they need to fetch the remote
+ * contents in order to determine if the resources that constitute the model
+ * element are different in the remote location. If the local file exists
+ * and the remote file does not, or the remote file exists and the local
+ * does not then the contents will be said to differ (i.e. <code>true</code>
+ * is returned). Also, implementors will most likely use a timestamp based
+ * comparison to determine if the contents differ. This may lead to a
+ * situation where <code>true</code> is returned but the actual contents
+ * do not differ. Clients must be prepared handle this situation.
+ * </p>
+ *
+ * @param resource the local resource
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return whether the contents of the corresponding remote differ from the
+ * base.
+ * @exception CoreException if the contents could not be compared. Reasons
+ * include:
+ * <ul>
+ * <li>The server could not be contacted for some reason.</li>
+ * <li>The corresponding remote resource is not a container (status code
+ * will be {@link IResourceStatus#RESOURCE_WRONG_TYPE}).</li>
+ * </ul>
+ */
+ public abstract boolean hasRemoteChange(IResource resource, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Return <code>true</code> if the context is associated with an operation
+ * that is using a three-way comparison and <code>false</code> if it is
+ * using a two-way comparison.
+ *
+ * @return whether the context is a three-way or two-way
+ */
+ public abstract boolean isThreeWay();
+
+ /**
+ * Refresh the known remote state for any resources covered by the given
+ * traversals. Clients who require the latest remote state should invoke
+ * this method before invoking any others of the class. Mappings can use
+ * this method as a hint to the context provider of which resources will be
+ * required for the mapping to generate the proper set of traversals.
+ * <p>
+ * Note that this is really only a hint to the context provider. It is up to
+ * implementors to decide, based on the provided traversals, how to
+ * efficiently perform the refresh. In the ideal case, calls to
+ * {@link #hasRemoteChange(IResource, IProgressMonitor)} and
+ * {@link #fetchMembers} would not need to contact the server after a call to a
+ * refresh with appropriate traversals. Also, ideally, if
+ * {@link #FILE_CONTENTS_REQUIRED} is on of the flags, then the contents
+ * for these files will be cached as efficiently as possible so that calls to
+ * {@link #fetchRemoteContents} will also not need to contact the server. This
+ * may not be possible for all context providers, so clients cannot assume that
+ * the above mentioned methods will not be long running. It is still advisable
+ * for clients to call {@link #refresh} with as much details as possible since, in
+ * the case where a provider is optimized, performance will be much better.
+ * </p>
+ *
+ * @param traversals the resource traversals that indicate which resources
+ * are to be refreshed
+ * @param flags additional refresh behavior. For instance, if
+ * {@link #FILE_CONTENTS_REQUIRED} is one of the flags, this indicates
+ * that the client will be accessing the contents of the files covered by
+ * the traversals. {@link #NONE} should be used when no additional
+ * behavior is required
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if the refresh fails. Reasons include:
+ * <ul>
+ * <li>The server could not be contacted for some reason. </li>
+ * </ul>
+ */
+ public abstract void refresh(ResourceTraversal[] traversals, int flags, IProgressMonitor monitor) throws CoreException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ResourceChangeValidator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.internal.resources.mapping.ChangeDescription;
+import org.eclipse.core.internal.resources.mapping.ResourceChangeDescriptionFactory;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The resource change validator is used to validate that changes made to
+ * resources will not adversely affect the models stored in those resources.
+ * <p>
+ * The validator is used by first creating a resource delta describing the
+ * proposed changes. A delta can be generated using a {@link IResourceChangeDescriptionFactory}.
+ * The change is then validated by calling the {@link #validateChange(IResourceDelta, IProgressMonitor)}
+ * method. This example validates a change to a single file:
+ * <code>
+ * IFile file = ..;//some file that is going to be changed
+ * ResourceChangeValidator validator = ResourceChangeValidator.getValidator();
+ * IResourceChangeDescriptionFactory factory = validator.createDeltaFactory();
+ * factory.change(file);
+ * IResourceDelta delta = factory.getDelta();
+ * IStatus result = validator.validateChange(delta, null);
+ * </code>
+ * If the result status does not have severity {@link IStatus#OK}, then
+ * the changes may cause problems for models that are built on those
+ * resources. In this case the user should be presented with the status message
+ * to determine if they want to proceed with the modification.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class ResourceChangeValidator {
+
+ private static ResourceChangeValidator instance;
+
+ /**
+ * Return the singleton change validator.
+ * @return the singleton change validator
+ */
+ public static ResourceChangeValidator getValidator() {
+ if (instance == null)
+ instance = new ResourceChangeValidator();
+ return instance;
+ }
+
+ /**
+ * Singleton accessor method should be used instead.
+ * @see #getValidator()
+ */
+ private ResourceChangeValidator() {
+ super();
+ }
+
+ private IStatus combineResults(IStatus[] result) {
+ List notOK = new ArrayList();
+ for (int i = 0; i < result.length; i++) {
+ IStatus status = result[i];
+ if (!status.isOK()) {
+ notOK.add(status);
+ }
+ }
+ if (notOK.isEmpty()) {
+ return Status.OK_STATUS;
+ }
+ if (notOK.size() == 1) {
+ return (IStatus) notOK.get(0);
+ }
+ return new MultiStatus(ResourcesPlugin.PI_RESOURCES, 0, (IStatus[]) notOK.toArray(new IStatus[notOK.size()]), Messages.mapping_multiProblems, null);
+ }
+
+ /**
+ * Return an empty change description factory that can be used to build a
+ * proposed resource delta.
+ * @return an empty change description factory that can be used to build a
+ * proposed resource delta
+ */
+ public IResourceChangeDescriptionFactory createDeltaFactory() {
+ return new ResourceChangeDescriptionFactory();
+ }
+
+ private ModelProvider[] getProviders(IResource[] resources) {
+ IModelProviderDescriptor[] descriptors = ModelProvider.getModelProviderDescriptors();
+ List result = new ArrayList();
+ for (int i = 0; i < descriptors.length; i++) {
+ IModelProviderDescriptor descriptor = descriptors[i];
+ try {
+ IResource[] matchingResources = descriptor.getMatchingResources(resources);
+ if (matchingResources.length > 0) {
+ result.add(descriptor.getModelProvider());
+ }
+ } catch (CoreException e) {
+ Policy.log(e.getStatus().getSeverity(), NLS.bind("Could not instantiate provider {0}", descriptor.getId()), e); //$NON-NLS-1$
+ }
+ }
+ return (ModelProvider[]) result.toArray(new ModelProvider[result.size()]);
+ }
+
+ /*
+ * Get the roots of any changes.
+ */
+ private IResource[] getRootResources(IResourceDelta root) {
+ final ChangeDescription changeDescription = new ChangeDescription();
+ try {
+ root.accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) {
+ return changeDescription.recordChange(delta);
+ }
+ });
+ } catch (CoreException e) {
+ // Shouldn't happen since the ProposedResourceDelta accept doesn't throw an
+ // exception and our visitor doesn't either
+ Policy.log(IStatus.ERROR, "Internal error", e); //$NON-NLS-1$
+ }
+ return changeDescription.getRootResources();
+ }
+
+ /**
+ * Validate the proposed changes contained in the given delta
+ * by consulting all model providers to determine if the changes
+ * have any adverse side effects.
+ * <p>
+ * This method returns either a {@link ModelStatus}, or a {@link MultiStatus}
+ * whose children are {@link ModelStatus}. In either case, the severity
+ * of the status indicates the severity of the possible side-effects of
+ * the operation. Any severity other than <code>OK</code> should be
+ * shown to the user. The message should be a human readable message that
+ * will allow the user to make a decision on whether to continue with the
+ * operation. The model provider id should indicate which model is flagging the
+ * the possible side effects.
+ * </p>
+ *
+ * @param delta a delta tree containing the proposed changes
+ * @return a status indicating any potential side effects
+ * on models stored in the affected resources.
+ */
+ public IStatus validateChange(IResourceDelta delta, IProgressMonitor monitor) {
+ monitor = Policy.monitorFor(monitor);
+ try {
+ IResource[] resources = getRootResources(delta);
+ ModelProvider[] providers = getProviders(resources);
+ if (providers.length == 0)
+ return Status.OK_STATUS;
+ monitor.beginTask(Messages.mapping_validate, providers.length);
+ IStatus[] result = new IStatus[providers.length];
+ for (int i = 0; i < providers.length; i++)
+ result[i] = providers[i].validateChange(delta, Policy.subMonitorFor(monitor, 1));
+ return combineResults(result);
+ } finally {
+ monitor.done();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ResourceMapping.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import java.util.ArrayList;
+import org.eclipse.core.internal.resources.mapping.ModelProviderManager;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A resource mapping supports the transformation of an application model
+ * object into its underlying file system resources. It provides the
+ * bridge between a logical element and the physical resource(s) into which it
+ * is stored but does not provide more comprehensive model access or
+ * manipulations.
+ * <p>
+ * Mappings provide two means of model traversal. The {@link #accept} method
+ * can be used to visit the resources that constitute the model object. Alternatively,
+ * a set or traversals can be obtained by calling {@link #getTraversals}. A traversal
+ * contains a set of resources and a depth. This allows clients (such a repository providers)
+ * to do optimal traversals of the resources w.r.t. the operation that is being performed
+ * on the model object.
+ * </p>
+ * <p>
+ * This class may be subclassed by clients.
+ * </p>
+
+ * @see IResource
+ * @see ResourceTraversal
+ * @since 3.2
+ */
+public abstract class ResourceMapping extends PlatformObject {
+
+ /**
+ * Accepts the given visitor for all existing resources in this mapping.
+ * The visitor's {@link IResourceVisitor#visit} method is called for each
+ * accessible resource in this mapping.
+ *
+ * @param context the traversal context
+ * @param visitor the visitor
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> The visitor failed with this exception.</li>
+ * <li> The traversals for this mapping could not be obtained.</li>
+ * </ul>
+ */
+ public void accept(ResourceMappingContext context, IResourceVisitor visitor, IProgressMonitor monitor) throws CoreException {
+ ResourceTraversal[] traversals = getTraversals(context, monitor);
+ for (int i = 0; i < traversals.length; i++)
+ traversals[i].accept(visitor);
+ }
+
+ /**
+ * Return whether this resource mapping contains all the resources
+ * of the given mapping.
+ * <p>
+ * This method always returns <code>false</code> when the given resource
+ * mapping's model provider id does not match that the of the receiver.
+ * </p>
+ *
+ * @param mapping the given resource mapping
+ * @return <code>true</code> if this mapping contains all the resources
+ * of the given mapping, and <code>false</code> otherwise.
+ */
+ public boolean contains(ResourceMapping mapping) {
+ return false;
+ }
+
+ /**
+ * Override equals to compare the model objects of the
+ * mapping in order to determine equality.
+ * @param obj the object to compare
+ * @return <code>true</code> if the receiver is equal to the
+ * given object, and <code>false</code> otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof ResourceMapping) {
+ ResourceMapping other = (ResourceMapping) obj;
+ return other.getModelObject().equals(getModelObject());
+ }
+ return false;
+ }
+
+ /**
+ * Returns all markers of the specified type on the resources in this mapping.
+ * If <code>includeSubtypes</code> is <code>false</code>, only markers
+ * whose type exactly matches the given type are returned. Returns an empty
+ * array if there are no matching markers.
+ *
+ * @param type the type of marker to consider, or <code>null</code> to indicate all types
+ * @param includeSubtypes whether or not to consider sub-types of the given type
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return an array of markers
+ * @exception CoreException if this method fails.
+ */
+ public IMarker[] findMarkers(String type, boolean includeSubtypes, IProgressMonitor monitor) throws CoreException {
+ final ResourceTraversal[] traversals = getTraversals(ResourceMappingContext.LOCAL_CONTEXT, monitor);
+ ArrayList result = new ArrayList();
+ for (int i = 0; i < traversals.length; i++)
+ traversals[i].doFindMarkers(result, type, includeSubtypes);
+ return (IMarker[]) result.toArray(new IMarker[result.size()]);
+ }
+
+ /**
+ * Returns the application model element associated with this
+ * resource mapping.
+ *
+ * @return the application model element associated with this
+ * resource mapping.
+ */
+ public abstract Object getModelObject();
+
+ /**
+ * Return the model provider for the model object
+ * of this resource mapping. The model provider is obtained
+ * using the id returned from <code>getModelProviderId()</code>.
+ * @return the model provider
+ */
+ public final ModelProvider getModelProvider() {
+ try {
+ return ModelProviderManager.getDefault().getModelProvider(getModelProviderId());
+ } catch (CoreException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns the id of the model provider that generated this resource
+ * mapping.
+ *
+ * @return the model provider id
+ */
+ public abstract String getModelProviderId();
+
+ /**
+ * Returns the projects that contain the resources that constitute this
+ * application model.
+ *
+ * @return the projects
+ */
+ public abstract IProject[] getProjects();
+
+ /**
+ * Returns one or more traversals that can be used to access all the
+ * physical resources that constitute the logical resource. A traversal is
+ * simply a set of resources and the depth to which they are to be
+ * traversed. This method returns an array of traversals in order to provide
+ * flexibility in describing the traversals that constitute a model element.
+ * <p>
+ * Subclasses should, when possible, include
+ * all resources that are or may be members of the model element.
+ * For instance, a model element should return the same list of
+ * resources regardless of the existence of the files on the file system.
+ * For example, if a logical resource called "form" maps to "/p1/form.xml"
+ * and "/p1/form.java" then whether form.xml or form.java existed, they
+ * should be returned by this method.
+ *</p><p>
+ * In some cases, it may not be possible for a model element to know all the
+ * resources that may constitute the element without accessing the state of
+ * the model element in another location (e.g. a repository). This method is
+ * provided with a context which, when provided, gives access to
+ * the members of corresponding remote containers and the contents of
+ * corresponding remote files. This gives the model element the opportunity
+ * to deduce what additional resources should be included in the traversal.
+ * </p>
+ *
+ * @param context gives access to the state of
+ * remote resources that correspond to local resources for the
+ * purpose of determining traversals that adequately cover the
+ * model element resources given the state of the model element
+ * in another location. This parameter may be <code>null</code>, in
+ * which case the implementor can assume that only the local
+ * resources are of interest to the client.
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @return a set of traversals that cover the resources that constitute the
+ * model element
+ * @exception CoreException if the traversals could not be obtained.
+ */
+ public abstract ResourceTraversal[] getTraversals(ResourceMappingContext context, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Override hashCode to use the model object.
+ */
+ public int hashCode() {
+ return getModelObject().hashCode();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ResourceMappingContext.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+/**
+ * A resource mapping context is provided to a resource mapping when traversing
+ * the resources of the mapping. The type of context may determine what resources
+ * are included in the traversals of a mapping.
+ * <p>
+ * There are currently two resource mapping contexts: the local mapping context
+ * (represented by the singleton {@link #LOCAL_CONTEXT}),
+ * and {@link RemoteResourceMappingContext}. Implementors of {@link ResourceMapping}
+ * should not assume that these are the only valid contexts (in order to allow future
+ * extensibility). Therefore, if the provided context is not of one of the above mentioned types,
+ * the implementor can assume that the context is a local context.
+ * </p>
+ * <p>
+ * This class may be subclassed by clients; this class is not intended to be
+ * instantiated directly.
+ * </p>
+ *
+ * @see ResourceMapping
+ * @see RemoteResourceMappingContext
+ * @since 3.2
+ */
+public class ResourceMappingContext {
+
+ /**
+ * This resource mapping context is used to indicate that the operation
+ * that is requesting the traversals is performing a local operation.
+ * Because the operation is local, the resource mapping is free to be
+ * as precise as desired about what resources make up the mapping without
+ * concern for performing optimized remote operations.
+ */
+ public static final ResourceMappingContext LOCAL_CONTEXT = new ResourceMappingContext();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ResourceTraversal.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.mapping;
+
+import java.util.ArrayList;
+import org.eclipse.core.internal.resources.MarkerManager;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A resource traversal is simply a set of resources and the depth to which
+ * each is to be traversed. A set of traversals is used to describe the
+ * resources that constitute a model element.
+ * <p>
+ * The flags of the traversal indicate which special resources should be
+ * included or excluded from the traversal. The flags used are the same as
+ * those passed to the {@link IResource#accept(IResourceVisitor, int, int)} method.
+ *
+ * <p>
+ * This class may be instantiated or subclassed by clients.
+ * </p>
+
+ * @see org.eclipse.core.resources.IResource
+ * @since 3.2
+ */
+public class ResourceTraversal {
+
+ private final int depth;
+ private final int flags;
+ private final IResource[] resources;
+
+ /**
+ * Creates a new resource traversal.
+ * @param resources The resources in the traversal
+ * @param depth The traversal depth
+ * @param flags the flags for this traversal. The traversal flags match those
+ * that are passed to the <code>IResource#accept</code> method.
+ */
+ public ResourceTraversal(IResource[] resources, int depth, int flags) {
+ if (resources == null)
+ throw new NullPointerException();
+ this.resources = resources;
+ this.depth = depth;
+ this.flags = flags;
+ }
+
+ /**
+ * Visits all existing resources defined by this traversal.
+ *
+ * @param visitor a resource visitor
+ * @exception CoreException if this method fails. Reasons include:
+ * <ul>
+ * <li> The visitor failed with this exception.</li>
+ * </ul>
+ */
+ public void accept(IResourceVisitor visitor) throws CoreException {
+ for (int i = 0, imax = resources.length; i < imax; i++)
+ try {
+ if (resources[i].exists())
+ resources[i].accept(visitor, depth, flags);
+ } catch (CoreException e) {
+ //ignore failure in the case of concurrent deletion
+ if (e.getStatus().getCode() != IResourceStatus.RESOURCE_NOT_FOUND)
+ throw e;
+ }
+ }
+
+ /**
+ * Return whether the given resource is contained in or
+ * covered by this traversal, regardless of whether the resource
+ * currently exists.
+ *
+ * @param resource the resource to be tested
+ * @return <code>true</code> if the resource is in this traversal, and
+ * <code>false</code> otherwise.
+ */
+ public boolean contains(IResource resource) {
+ for (int i = 0; i < resources.length; i++) {
+ IResource member = resources[i];
+ if (contains(member, resource)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean contains(IResource resource, IResource child) {
+ if (resource.equals(child))
+ return true;
+ if (depth == IResource.DEPTH_ZERO)
+ return false;
+ if (child.getParent().equals(resource))
+ return true;
+ if (depth == IResource.DEPTH_INFINITE)
+ return resource.getFullPath().isPrefixOf(child.getFullPath());
+ return false;
+ }
+
+ /**
+ * Efficient implementation of {@link #findMarkers(String, boolean)}, not
+ * available to clients because underlying non-API methods are used that
+ * may change.
+ */
+ void doFindMarkers(ArrayList result, String type, boolean includeSubtypes) {
+ MarkerManager markerMan = ((Workspace) ResourcesPlugin.getWorkspace()).getMarkerManager();
+ for (int i = 0; i < resources.length; i++)
+ markerMan.doFindMarkers(resources[i], result, type, includeSubtypes, depth);
+ }
+
+ /**
+ * Returns all markers of the specified type on existing resources in this traversal.
+ * If <code>includeSubtypes</code> is <code>false</code>, only markers
+ * whose type exactly matches the given type are returned. Returns an empty
+ * array if there are no matching markers.
+ *
+ * @param type the type of marker to consider, or <code>null</code> to indicate all types
+ * @param includeSubtypes whether or not to consider sub-types of the given type
+ * @return an array of markers
+ * @exception CoreException if this method fails.
+ * @see IResource#findMarkers(String, boolean, int)
+ */
+ public IMarker[] findMarkers(String type, boolean includeSubtypes) throws CoreException {
+ if (resources.length == 0)
+ return new IMarker[0];
+ ArrayList result = new ArrayList();
+ doFindMarkers(result, type, includeSubtypes);
+ return (IMarker[]) result.toArray(new IMarker[result.size()]);
+ }
+
+ /**
+ * Returns the depth to which the resources should be traversed.
+ *
+ * @return the depth to which the physical resources are to be traversed
+ * (one of IResource.DEPTH_ZERO, IResource.DEPTH_ONE or
+ * IResource.DEPTH_INFINITE)
+ */
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Return the flags for this traversal.
+ * The flags of the traversal indicate which special resources should be
+ * included or excluded from the traversal. The flags used are the same as
+ * those passed to the <code>IResource#accept(IResourceVisitor, int, int)</code> method.
+ * Clients who traverse the resources manually (i.e. without calling <code>accept</code>)
+ * should respect the flags when determining which resources are included
+ * in the traversal.
+ *
+ * @return the flags for this traversal
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Returns the file system resource(s) for this traversal. The returned
+ * resources must be contained within the same project and need not exist in
+ * the local file system. The traversal of the returned resources should be
+ * done considering the flag returned by getDepth. If a resource returned by
+ * a traversal is a file, it should always be visited. If a resource of a
+ * traversal is a folder then files contained in the folder can only be
+ * visited if the folder is IResource.DEPTH_ONE or IResource.DEPTH_INFINITE.
+ * Child folders should only be visited if the depth is
+ * IResource.DEPTH_INFINITE.
+ *
+ * @return The resources in this traversal
+ */
+ public IResource[] getResources() {
+ return resources;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/refresh/IRefreshMonitor.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.refresh;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * An <code>IRefreshMonitor</code> monitors trees of <code>IResources</code>
+ * for changes in the local file system.
+ * <p>
+ * When an <code>IRefreshMonitor</code> notices changes, it should report them
+ * to the <code>IRefreshResult</code> provided at the time of the monitor's
+ * creation.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface IRefreshMonitor {
+ /**
+ * Informs the monitor that it should stop monitoring the given resource.
+ *
+ * @param resource the resource that should no longer be monitored, or
+ * <code>null</code> if this monitor should stop monitoring all resources
+ * it is currently monitoring
+ */
+ public void unmonitor(IResource resource);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/refresh/IRefreshResult.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.refresh;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * An <code>IRefreshResult</code> is provided to an auto-refresh
+ * monitor. The result is used to submit resources to be refreshed, and
+ * for reporting failure of the monitor.
+ *
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IRefreshResult {
+ /**
+ * Notifies that the given monitor has encountered a failure from which it
+ * cannot recover while monitoring the given resource.
+ * <p>
+ * If the given resource is <code>null</code> it indicates that the
+ * monitor has failed completely, and the refresh manager will have to
+ * take over the monitoring responsibilities for all resources that the
+ * monitor was monitoring.
+ *
+ * @param monitor a monitor which has encountered a failure that it
+ * cannot recover from
+ * @param resource the resource that the monitor can no longer
+ * monitor, or <code>null</code> to indicate that the monitor can no
+ * longer monitor any of the resources it was monitoring
+ */
+ public void monitorFailed(IRefreshMonitor monitor, IResource resource);
+
+ /**
+ * Requests that the provided resource be refreshed. The refresh will
+ * occur in the background during the next scheduled refresh.
+ *
+ * @param resource the resource to refresh
+ */
+ public void refresh(IResource resource);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/refresh/RefreshProvider.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.refresh;
+
+import org.eclipse.core.internal.refresh.InternalRefreshProvider;
+import org.eclipse.core.resources.IResource;
+
+/**
+ * The abstract base class for all auto-refresh providers. This class provides
+ * the infrastructure for defining an auto-refresh provider and fulfills the
+ * contract specified by the <code>org.eclipse.core.resources.refreshProviders</code>
+ * standard extension point.
+ * <p>
+ * All auto-refresh providers must subclass this class. A
+ * <code>RefreshProvider</code> is responsible for creating
+ * <code>IRefreshMonitor</code> objects. The provider must decide if
+ * it is capable of monitoring the file, or folder and subtree under the path that is provided.
+ *
+ * @since 3.0
+ */
+public abstract class RefreshProvider extends InternalRefreshProvider {
+ /**
+ * Creates a new refresh monitor that performs naive polling of the resource
+ * in the file system to detect changes. The returned monitor will immediately begin
+ * monitoring the specified resource root and report changes back to the workspace.
+ * <p>
+ * This default monitor can be returned by subclasses when
+ * <code>installMonitor</code> is called.
+ * <p>
+ * If the returned monitor is not immediately returned from the <code>installMonitor</code>
+ * method, then clients are responsible for telling the returned monitor to
+ * stop polling when it is no longer needed. The returned monitor can be told to
+ * stop working by invoking <code>IRefreshMonitor.unmonitor(IResource)</code>.
+ *
+ * @param resource The resource to begin monitoring
+ * @return A refresh monitor instance
+ * @see #installMonitor(IResource, IRefreshResult)
+ */
+ protected IRefreshMonitor createPollingMonitor(IResource resource) {
+ return super.createPollingMonitor(resource);
+ }
+
+ /**
+ * Returns an <code>IRefreshMonitor</code> that will monitor a resource. If
+ * the resource is an <code>IContainer</code> the monitor will also
+ * monitor the subtree under the container. Returns <code>null</code> if
+ * this provider cannot create a monitor for the given resource. The
+ * provider may return the same monitor instance that has been provided for
+ * other resources.
+ * <p>
+ * The monitor should send results and failures to the provided refresh
+ * result.
+ *
+ * @param resource the resource to monitor
+ * @param result the result callback for notifying of failure or of resources that need
+ * refreshing
+ * @return a monitor on the resource, or <code>null</code>
+ * if the resource cannot be monitored
+ * @see #createPollingMonitor(IResource)
+ */
+ public abstract IRefreshMonitor installMonitor(IResource resource, IRefreshResult result);
+
+ /**
+ * Resets the installed monitors for the given resource. This will remove all
+ * existing monitors that are installed on the resource, and then ask all
+ * refresh providers to begin monitoring the resource again.
+ * <p>
+ * This method is intended to be used by refresh providers that need to change
+ * the refresh monitor that they previously used to monitor a resource.
+ *
+ * @param resource The resource to reset the monitors for
+ */
+ public void resetMonitors(IResource resource) {
+ super.resetMonitors(resource);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/team/FileModificationValidationContext.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.team;
+
+import org.eclipse.core.resources.IWorkspace;
+
+/**
+ * A context that is used in conjunction with the {@link FileModificationValidator}
+ * to indicate that UI-based validation is desired.
+ * <p>
+ * This class is not intended to be instantiated or subclassed by clients.
+ *
+ * @see FileModificationValidator
+ * @since 3.3
+ */
+public class FileModificationValidationContext {
+
+ /**
+ * Constant that can be passed to {@link IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], Object)}
+ * to indicate that the caller does not have access to a UI context but would still
+ * like to have UI-based validation if possible.
+ */
+ public static final FileModificationValidationContext VALIDATE_PROMPT = new FileModificationValidationContext(null);
+
+ private final Object shell;
+
+ /**
+ * Create a context with the given shell.
+ *
+ * @param shell the shell
+ */
+ FileModificationValidationContext(Object shell) {
+ this.shell = shell;
+ }
+
+ /**
+ * Return the <code>org.eclipse.swt.widgets.Shell</code> that is to be used to
+ * parent any dialogs with the user, or <code>null</code> if there is no UI context
+ * available (declared as an <code>Object</code> to avoid any direct references on the SWT component).
+ * If there is no shell, the {@link FileModificationValidator} may still perform
+ * UI-based validation if they can obtain a Shell from another source.
+ * @return the <code>org.eclipse.swt.widgets.Shell</code> that is to be used to
+ * parent any dialogs with the user, or <code>null</code>
+ */
+ public Object getShell() {
+ return shell;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/team/FileModificationValidator.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.team;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * The file modification validator is a Team-related hook for pre-checking operations
+ * that modify the contents of files.
+ * <p>
+ * This class is used only in conjunction with the
+ * "org.eclipse.core.resources.fileModificationValidator"
+ * extension point. It is intended to be implemented only
+ * by the Eclipse Platform Team plug-in or by repository providers
+ * whose validator get invoked by Team.
+ * </p>
+ * @since 3.3
+ */
+public abstract class FileModificationValidator implements IFileModificationValidator {
+
+ /**
+ * Validates that the given files can be modified. The files must all exist
+ * in the workspace. The optional context object may be supplied if
+ * UI-based validation is required. If the context is <code>null</code>, the
+ * validator must attempt to perform the validation in a headless manner.
+ * The returned status is <code>IStatus.OK</code> if this validator
+ * believes the given file can be modified. Other return statuses indicate
+ * the reason why the individual files cannot be modified.
+ *
+ * @param files the files that are to be modified; these files must all exist in the workspace
+ * @param context the <code>org.eclipse.swt.widgets.Shell</code> that is to be used to
+ * parent any dialogs with the user, or <code>null</code> if there is no UI context (declared
+ * as an <code>Object</code> to avoid any direct references on the SWT component)
+ * @return a status object that is OK if things are fine, otherwise a status describing
+ * reasons why modifying the given files is not reasonable
+ * @see IWorkspace#validateEdit(IFile[], Object)
+ * @deprecated this method is part of the deprecated {@link IFileModificationValidator}
+ * interface. Clients should call {@link #validateEdit(IFile[], FileModificationValidationContext)}
+ * instead.
+ */
+ public final IStatus validateEdit(IFile[] files, Object context) {
+ FileModificationValidationContext validationContext;
+ if (context == null)
+ validationContext = null;
+ else if (context instanceof FileModificationValidationContext)
+ validationContext = (FileModificationValidationContext) context;
+ else
+ validationContext = new FileModificationValidationContext(context);
+ return validateEdit(files, validationContext);
+ }
+
+ /**
+ * Validates that the given file can be saved. This method is called from
+ * <code>IFile#setContents</code> and <code>IFile#appendContents</code>
+ * before any attempt to write data to disk. The returned status is
+ * <code>IStatus.OK</code> if this validator believes the given file can be
+ * successfully saved. In all other cases the return value is a non-OK status.
+ * Note that a return value of <code>IStatus.OK</code> does not guarantee
+ * that the save will succeed.
+ *
+ * @param file the file that is to be modified; this file must exist in the workspace
+ * @return a status indicating whether or not it is reasonable to try writing to the given file;
+ * <code>IStatus.OK</code> indicates a save should be attempted.
+ *
+ * @see IFile#setContents(java.io.InputStream, int, org.eclipse.core.runtime.IProgressMonitor)
+ * @see IFile#appendContents(java.io.InputStream, int, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus validateSave(IFile file) {
+ return validateEdit(new IFile[] {file}, (FileModificationValidationContext) null);
+ }
+
+ /**
+ * Validates that the given files can be modified. The files must all exist
+ * in the workspace. The optional context may be supplied if
+ * UI-based validation is required. If the context is <code>null</code>, the
+ * validator must attempt to perform the validation in a headless manner.
+ * The returned status is <code>IStatus.OK</code> if this validator
+ * believes the given file can be modified. Other return statuses indicate
+ * the reason why the individual files cannot be modified.
+ *
+ * @param files the files that are to be modified; these files must all exist in the workspace
+ * @param context the context to aid in UI-based validation or <code>null</code> if the validation
+ * must be headless
+ * @return a status object that is OK if things are fine, otherwise a status describing
+ * reasons why modifying the given files is not reasonable
+ * @see IWorkspace#validateEdit(IFile[], Object)
+ */
+ public abstract IStatus validateEdit(IFile[] files, FileModificationValidationContext context);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/team/IMoveDeleteHook.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,383 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.team;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+/**
+ * Primary interface for hooking the implementation of
+ * <code>IResource.move</code> and <code>IResource.delete</code>.
+ * <p>
+ * This interface is intended to be implemented by the team component in
+ * conjunction with the <code>org.eclipse.core.resources.moveDeleteHook</code>
+ * standard extension point. Individual team providers may also implement this
+ * interface. It is not intended to be implemented by other clients. The methods
+ * defined on this interface are called from within the implementations of
+ * <code>IResource.move</code> and <code>IResource.delete</code>. They are not
+ * intended to be called from anywhere else.
+ * </p>
+ *
+ * @since 2.0
+ */
+public interface IMoveDeleteHook {
+
+ /**
+ * Implements <code>IResource.delete(int,IProgressMonitor)</code> where the
+ * receiver is a file. Returns <code>true</code> to accept responsibility
+ * for implementing this operation as per the API contract.
+ * <p>
+ * In broad terms, a full re-implementation should delete the file in the
+ * local file system and then call <code>tree.deletedFile</code> to complete
+ * the updating of the workspace resource tree to reflect this fact. If
+ * unsuccessful in deleting the file from the local file system, it
+ * should instead call <code>tree.failed</code> to report the reason for
+ * the failure. In either case, it should return <code>true</code> to
+ * indicate that the operation was attempted. The <code>FORCE</code> update
+ * flag needs to be honored: unless <code>FORCE</code> is specified, the
+ * implementation must use <code>tree.isSynchronized</code> to determine
+ * whether the file is in sync before attempting to delete it.
+ * The <code>KEEP_HISTORY</code> update flag needs to be honored as well;
+ * use <code>tree.addToLocalHistory</code> to capture the contents of the
+ * file before deleting it from the local file system.
+ * </p><p>
+ * An extending implementation should perform whatever pre-processing it
+ * needs to do and then call <code>tree.standardDeleteFile</code> to
+ * explicitly invoke the standard file deletion behavior, which deletes
+ * both the file from the local file system and updates the workspace
+ * resource tree. It should return <code>true</code> to indicate that the
+ * operation was attempted.
+ * </p><p>
+ * Returning <code>false</code> is the easy way for the implementation to
+ * say "pass". It is equivalent to calling
+ * <code>tree.standardDeleteFile</code> and returning <code>true</code>.
+ * </p><p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p>
+ *
+ * @param tree the workspace resource tree; this object is only valid
+ * for the duration of the invocation of this method, and must not be
+ * used after this method has completed
+ * @param file the handle of the file to delete; the receiver of
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @return <code>false</code> if this method declined to assume
+ * responsibility for this operation, and <code>true</code> if this method
+ * attempted to carry out the operation
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int,IProgressMonitor)
+ */
+ public boolean deleteFile(IResourceTree tree, IFile file, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Implements <code>IResource.delete(int,IProgressMonitor)</code> where the
+ * receiver is a folder. Returns <code>true</code> to accept responsibility
+ * for implementing this operation as per the API contract.
+ * <p>
+ * In broad terms, a full re-implementation should delete the directory tree
+ * in the local file system and then call <code>tree.deletedFolder</code> to
+ * complete the updating of the workspace resource tree to reflect this fact.
+ * If unsuccessful in deleting the directory or any of its descendents from
+ * the local file system, it should instead call <code>tree.failed</code> to
+ * report each reason for failure. In either case it should return
+ * <code>true</code> to indicate that the operation was attempted.
+ * The <code>FORCE</code> update flag needs to be honored: unless
+ * <code>FORCE</code> is specified, the implementation must use
+ * <code>tree.isSynchronized</code> to determine whether the folder
+ * subtree is in sync before attempting to delete it.
+ * The <code>KEEP_HISTORY</code> update flag needs to be honored as well;
+ * use <code>tree.addToLocalHistory</code> to capture the contents of any
+ * files being deleted.
+ * </p><p>
+ * A partial re-implementation should perform whatever pre-processing it
+ * needs to do and then call <code>tree.standardDeleteFolder</code> to
+ * explicitly invoke the standard folder deletion behavior, which deletes
+ * both the folder and its descendents from the local file system and
+ * updates the workspace resource tree. It should return <code>true</code>
+ * to indicate that the operation was attempted.
+ * </p><p>
+ * Returning <code>false</code> is the easy way for the implementation to
+ * say "pass". It is equivalent to calling
+ * <code>tree.standardDeleteFolder</code> and returning <code>true</code>.
+ * </p><p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p>
+ *
+ * @param tree the workspace resource tree; this object is only valid
+ * for the duration of the invocation of this method, and must not be
+ * used after this method has completed
+ * @param folder the handle of the folder to delete; the receiver of
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @return <code>false</code> if this method declined to assume
+ * responsibility for this operation, and <code>true</code> if this
+ * method attempted to carry out the operation
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int,IProgressMonitor)
+ */
+ public boolean deleteFolder(IResourceTree tree, IFolder folder, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Implements <code>IResource.delete(int,IProgressMonitor)</code> where the
+ * receiver is a project. Returns <code>true</code> to accept responsibility
+ * for implementing this operation as per the API contract.
+ * <p>
+ * In broad terms, a full re-implementation should delete the project content area in
+ * the local file system if required (the files of a closed project should be deleted
+ * only if the <code>IResource.ALWAYS_DELETE_PROJECT_CONTENTS</code> update
+ * flag is specified; the files of an open project should be deleted unless the
+ * the <code>IResource.NEVER_DELETE_PROJECT_CONTENTS</code> update flag is
+ * specified). It should then call <code>tree.deletedProject</code> to complete
+ * the updating of the workspace resource tree to reflect this fact. If unsuccessful
+ * in deleting the project's files from the local file system, it should instead call
+ * <code>tree.failed</code> to report the reason for the failure. In either case, it
+ * should return <code>true</code> to indicate that the operation was attempted.
+ * The <code>FORCE</code> update flag may need to be honored if the project is open:
+ * unless <code>FORCE</code> is specified, the implementation must use
+ * <code>tree.isSynchronized</code> to determine whether the project subtree is in
+ * sync before attempting to delete it.
+ * Note that local history is not maintained when a project is deleted,
+ * regardless of the setting of the <code>KEEP_HISTORY</code> update flag.
+ * </p><p>
+ * A partial re-implementation should perform whatever pre-processing it needs
+ * to do and then call <code>tree.standardDeleteProject</code> to explicitly
+ * invoke the standard project deletion behavior. It should return <code>true</code>
+ * to indicate that the operation was attempted.
+ * </p><p>
+ * Returning <code>false</code> is the easy way for the implementation to
+ * say "pass". It is equivalent to calling
+ * <code>tree.standardDeleteProject</code> and returning <code>true</code>.
+ * </p><p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p>
+ *
+ * @param tree the workspace resource tree; this object is only valid
+ * for the duration of the invocation of this method, and must not be
+ * used after this method has completed
+ * @param project the handle of the project to delete; the receiver of
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @return <code>false</code> if this method declined to assume
+ * responsibility for this operation, and <code>true</code> if this
+ * method attempted to carry out the operation
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#delete(int,IProgressMonitor)
+ */
+ public boolean deleteProject(IResourceTree tree, IProject project, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Implements <code>IResource.move(IPath,int,IProgressMonitor)</code> where
+ * the receiver is a file. Returns <code>true</code> to accept
+ * responsibility for implementing this operation as per the API contract.
+ * <p>
+ * On entry to this hook method, the following is guaranteed about the
+ * workspace resource tree: the source file exists; the destination file
+ * does not exist; the container of the destination file exists and is
+ * accessible. In broad terms, a full re-implementation should move the file
+ * in the local file system and then call <code>tree.moveFile</code> to
+ * complete the updating of the workspace resource tree to reflect this
+ * fact. If unsuccessful in moving the file in the local file system,
+ * it should instead call <code>tree.failed</code> to report the reason for
+ * the failure. In either case, it should return <code>true</code> to
+ * indicate that the operation was attempted.
+ * The <code>FORCE</code> update flag needs to be honored: unless
+ * <code>FORCE</code> is specified, the implementation must use
+ * <code>tree.isSynchronized</code> to determine whether the file is in sync before
+ * attempting to move it.
+ * The <code>KEEP_HISTORY</code> update flag needs to be honored as well; use
+ * <code>tree.addToLocalHistory</code> to capture the contents of the file
+ * (naturally, this must be before moving the file from the local file system).
+ * </p><p>
+ * An extending implementation should perform whatever pre-processing it needs
+ * to do and then call <code>tree.standardMoveFile</code> to explicitly
+ * invoke the standard file moving behavior, which moves both the file in the
+ * local file system and updates the workspace resource tree. It should return
+ * <code>true</code> to indicate that the operation was attempted.
+ * </p><p>
+ * Returning <code>false</code> is the easy way for the implementation to
+ * say "pass". It is equivalent to calling
+ * <code>tree.standardMoveFile</code> and returning <code>true</code>.
+ * </p><p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p>
+ *
+ * @param tree the workspace resource tree; this object is only valid
+ * for the duration of the invocation of this method, and must not be
+ * used after this method has completed
+ * @param source the handle of the file to move; the receiver of
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param destination the handle of where the file will move to; the handle
+ * equivalent of the first parameter to
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @return <code>false</code> if this method declined to assume
+ * responsibility for this operation, and <code>true</code> if this
+ * method attempted to carry out the operation
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#move(org.eclipse.core.runtime.IPath,int,IProgressMonitor)
+ */
+ public boolean moveFile(IResourceTree tree, IFile source, IFile destination, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Implements <code>IResource.move(IPath,int,IProgressMonitor)</code> where
+ * the receiver is a project. Returns <code>true</code> to accept
+ * responsibility for implementing this operation as per the API contract.
+ * <p>
+ * On entry to this hook method, the following is guaranteed about the
+ * workspace resource tree: the source folder exists; the destination folder
+ * does not exist; the container of the destination folder exists and is
+ * accessible. In broad terms, a full re-implementation should move the
+ * directory tree in the local file system and then call
+ * <code>tree.movedFolder</code> to complete the updating of the workspace
+ * resource tree to reflect this fact. If unsuccessful in moving the
+ * directory or any of its descendents in the local file system,
+ * call <code>tree.failed</code> to report each reason for failure.
+ * In either case, return <code>true</code> to indicate that the operation
+ * was attempted.
+ * The <code>FORCE</code> update flag needs to be honored: unless
+ * <code>FORCE</code> is specified, the implementation must use
+ * <code>tree.isSynchronized</code> to determine whether the folder subtree is in sync
+ * before attempting to move it.
+ * The <code>KEEP_HISTORY</code> update flag needs to be honored as well; use
+ * <code>tree.addToLocalHistory</code> to capture the contents of any files being
+ * moved.
+ * </p><p>
+ * A partial re-implementation should perform whatever pre-processing it needs
+ * to do and then call <code>tree.standardMoveFolder</code> to explicitly
+ * invoke the standard folder move behavior, which move both the folder
+ * and its descendents in the local file system and updates the workspace resource
+ * tree. Return <code>true</code> to indicate that the operation was attempted.
+ * </p><p>
+ * Returning <code>false</code> is the easy way for the implementation to
+ * say "pass". It is equivalent to calling
+ * <code>tree.standardDeleteFolder</code> and returning <code>true</code>.
+ * </p><p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p>
+ *
+ * @param tree the workspace resource tree; this object is only valid
+ * for the duration of the invocation of this method, and must not be
+ * used after this method has completed
+ * @param source the handle of the folder to move; the receiver of
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param destination the handle of where the folder will move to; the
+ * handle equivalent of the first parameter to
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @return <code>false</code> if this method declined to assume
+ * responsibility for this operation, and <code>true</code> if this
+ * method attempted to carry out the operation
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#move(org.eclipse.core.runtime.IPath,int,IProgressMonitor)
+ */
+ public boolean moveFolder(IResourceTree tree, IFolder source, IFolder destination, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Implements <code>IResource.move(IPath,int,IProgressMonitor)</code> and
+ * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
+ * where the receiver is a project. Returns <code>true</code> to accept
+ * responsibility for implementing this operation as per the API contracts.
+ * <p>
+ * On entry to this hook method, the source project is guaranteed to exist
+ * and be open in the workspace resource tree. If the given description
+ * contains a different name from that of the given project, then the
+ * project is being renamed (and its content possibly relocated). If the
+ * given description contains the same name as the given project, then the
+ * project is being relocated but not renamed. When the project is being
+ * renamed, the destination project is guaranteed not to exist in the
+ * workspace resource tree.
+ * </p><p>
+ * Returning <code>false</code> is the easy way for the implementation to
+ * say "pass". It is equivalent to calling
+ * <code>tree.standardMoveProject</code> and returning <code>true</code>.
+ * </p><p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p>
+ *
+ * @param tree the workspace resource tree; this object is only valid
+ * for the duration of the invocation of this method, and must not be
+ * used after this method has completed
+ * @param source the handle of the open project to move; the receiver of
+ * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
+ * or <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param description the new description of the project; the first
+ * parameter to
+ * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>, or
+ * a copy of the project's description with the location changed to the
+ * path given in the first parameter to
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
+ * or <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
+ * or <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @return <code>false</code> if this method declined to assume
+ * responsibility for this operation, and <code>true</code> if this method
+ * attempted to carry out the operation
+ * @exception OperationCanceledException if the operation is canceled.
+ * Cancelation can occur even if no progress monitor is provided.
+ * @see IResource#move(org.eclipse.core.runtime.IPath,int,IProgressMonitor)
+ * @see IResource#move(IProjectDescription,int,IProgressMonitor)
+ */
+ public boolean moveProject(IResourceTree tree, IProject source, IProjectDescription description, int updateFlags, IProgressMonitor monitor);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/team/IResourceTree.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,383 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.team;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Provides internal access to the workspace resource tree for the purposes of
+ * implementing the move and delete operations. Implementations of
+ * <code>IMoveDeleteHook</code> call these methods.
+ *
+ * @since 2.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IResourceTree {
+
+ /**
+ * Constant indicating that no file timestamp was supplied.
+ *
+ * @see #movedFile(IFile, IFile)
+ */
+ public static final long NULL_TIMESTAMP = 0L;
+
+ /**
+ * Adds the current state of the given file to the local history.
+ * Does nothing if the file does not exist in the workspace resource tree,
+ * or if it exists in the workspace resource tree but not in the local file
+ * system.
+ * <p>
+ * This method is used to capture the state of a file in the workspace
+ * local history before it is overwritten or deleted.
+ * </p>
+ *
+ * @param file the file to be captured
+ */
+ public void addToLocalHistory(IFile file);
+
+ /**
+ * Returns whether the given resource and its descendents to the given depth
+ * are considered to be in sync with the local file system. Returns
+ * <code>false</code> if the given resource does not exist in the workspace
+ * resource tree, but exists in the local file system; and conversely.
+ *
+ * @param resource the resource of interest
+ * @param depth the depth (one of <code>IResource.DEPTH_ZERO</code>,
+ * <code>DEPTH_ONE</code>, or <code>DEPTH_INFINITE</code>)
+ * @return <code>true</code> if the resource is synchronized, and
+ * <code>false</code> in all other cases
+ */
+ public boolean isSynchronized(IResource resource, int depth);
+
+ /**
+ * Computes the timestamp for the given file in the local file system.
+ * Returns <code>NULL_TIMESTAMP</code> if the timestamp of the file in
+ * the local file system cannot be determined. The file need not exist in
+ * the workspace resource tree; however, if the file's project does not
+ * exist in the workspace resource tree, this method returns
+ * <code>NULL_TIMESTAMP</code> because the project's local content area
+ * is indeterminate.
+ * <p>
+ * Note that the timestamps used for workspace resource tree file
+ * synchronization are not necessarily interchangeable with
+ * <code>java.io.File</code> last modification time.The ones computed by
+ * <code>computeTimestamp</code> may have a higher resolution in some
+ * operating environments.
+ * </p>
+ *
+ * @param file the file of interest
+ * @return the local file system timestamp for the file, or
+ * <code>NULL_TIMESTAMP</code> if it could not be computed
+ */
+ public long computeTimestamp(IFile file);
+
+ /**
+ * Returns the timestamp for the given file as recorded in the workspace
+ * resource tree. Returns <code>NULL_TIMESTAMP</code> if the given file
+ * does not exist in the workspace resource tree, or if the timestamp is
+ * not known.
+ * <p>
+ * Note that the timestamps used for workspace resource tree file
+ * synchronization are not necessarily interchangeable with
+ * <code>java.io.File</code> last modification time.The ones computed by
+ * <code>computeTimestamp</code> may have a higher resolution in some
+ * operating environments.
+ * </p>
+ *
+ * @param file the file of interest
+ * @return the workspace resource tree timestamp for the file, or
+ * <code>NULL_TIMESTAMP</code> if the file does not exist in the
+ * workspace resource tree, or if the timestamp is not known
+ */
+ public long getTimestamp(IFile file);
+
+ /**
+ * Updates the timestamp for the given file in the workspace resource tree.
+ * The file is the local file system is not affected. Does nothing if the
+ * given file does not exist in the workspace resource tree.
+ * <p>
+ * The given timestamp should be that of the corresponding file in the local
+ * file system (as computed by <code>computeTimestamp</code>). A discrepancy
+ * between the timestamp of the file in the local file system and the
+ * timestamp recorded in the workspace resource tree means that the file is
+ * out of sync (<code>isSynchronized</code> returns <code>false</code>).
+ * </p>
+ * <p>
+ * This operation should be used after <code>movedFile/Folder/Project</code>
+ * to correct the workspace resource tree record when file timestamps change
+ * in the course of a move operation.
+ * </p>
+ * <p>
+ * Note that the timestamps used for workspace resource tree file
+ * synchronization are not necessarily interchangeable with
+ * <code>java.io.File</code> last modification time.The ones computed by
+ * <code>computeTimestamp</code> may have a higher resolution in some
+ * operating environments.
+ * </p>
+ *
+ * @param file the file of interest
+ * @param timestamp the local file system timestamp for the file, or
+ * <code>NULL_TIMESTAMP</code> if unknown
+ * @see #computeTimestamp(IFile)
+ */
+ public void updateMovedFileTimestamp(IFile file, long timestamp);
+
+ /**
+ * Declares that the operation has failed for the specified reason.
+ * This method may be called multiple times to report multiple
+ * failures. All reasons will be accumulated and taken into consideration
+ * when deciding the outcome of the hooked operation as a whole.
+ *
+ * @param reason the reason the operation (or sub-operation) failed
+ */
+ public void failed(IStatus reason);
+
+ /**
+ * Declares that the given file has been successfully deleted from the
+ * local file system, and requests that the corresponding deletion should
+ * now be made to the workspace resource tree. No action is taken if the
+ * given file does not exist in the workspace resource tree.
+ * <p>
+ * This method clears out any markers, session properties, and persistent
+ * properties associated with the given file.
+ * </p>
+ *
+ * @param file the file that was just deleted from the local file system
+ */
+ public void deletedFile(IFile file);
+
+ /**
+ * Declares that the given folder and all its descendents have been
+ * successfully deleted from the local file system, and requests that the
+ * corresponding deletion should now be made to the workspace resource tree.
+ * No action is taken if the given folder does not exist in the workspace
+ * resource tree.
+ * <p>
+ * This method clears out any markers, session properties, and persistent
+ * properties associated with the given folder or its descendents.
+ * </p>
+ *
+ * @param folder the folder that was just deleted from the local file system
+ */
+ public void deletedFolder(IFolder folder);
+
+ /**
+ * Declares that the given project's content area in the local file system
+ * has been successfully dealt with in an appropriate manner, and requests
+ * that the corresponding deletion should now be made to the workspace
+ * resource tree. No action is taken if the given project does not exist in
+ * the workspace resource tree.
+ * <p>
+ * This method clears out everything associated with this project and any of
+ * its descendent resources, including: markers; session properties;
+ * persistent properties; local history; and project-specific plug-ins
+ * working data areas. The project's content area is not affected.
+ * </p>
+ *
+ * @param project the project being deleted
+ */
+ public void deletedProject(IProject project);
+
+ /**
+ * Declares that the given source file has been successfully moved to the
+ * given destination in the local file system, and requests that the
+ * corresponding changes should now be made to the workspace resource tree.
+ * No action is taken if the given source file does not exist in the
+ * workspace resource tree.
+ * <p>
+ * The destination file must not already exist in the workspace resource
+ * tree.
+ * </p><p>
+ * This operation carries over the file timestamp unchanged. Use
+ * <code>updateMovedFileTimestamp</code> to update the timestamp
+ * of the file if its timestamp changed as a direct consequence of the move.
+ * </p>
+ *
+ * @param source the handle of the source file that was moved
+ * @param destination the handle of where the file moved to
+ * @see #computeTimestamp(IFile)
+ */
+ public void movedFile(IFile source, IFile destination);
+
+ /**
+ * Declares that the given source folder and its descendents have been
+ * successfully moved to the given destination in the local file system,
+ * and requests that the corresponding changes should now be made to the
+ * workspace resource tree for the folder and all its descendents. No action
+ * is taken if the given source folder does not exist in the workspace
+ * resource tree.
+ * <p>
+ * This operation carries over file timestamps unchanged. Use
+ * <code>updateMovedFileTimestamp</code> to update the timestamp of files
+ * whose timestamps changed as a direct consequence of the move.
+ * </p><p>
+ * The destination folder must not already exist in the workspace resource
+ * tree.
+ * </p>
+ *
+ * @param source the handle of the source folder that was moved
+ * @param destination the handle of where the folder moved to
+ */
+ public void movedFolderSubtree(IFolder source, IFolder destination);
+
+ /**
+ * Declares that the given source project and its files and folders have
+ * been successfully relocated in the local file system if required, and
+ * requests that the rename and/or relocation should now be made to the
+ * workspace resource tree for the project and all its descendents. No
+ * action is taken if the given project does not exist in the workspace
+ * resource tree.
+ * <p>
+ * This operation carries over file timestamps unchanged. Use
+ * <code>updateMovedFileTimestamp</code> to update the timestamp of files whose
+ * timestamps changed as a direct consequence of the move.
+ * </p><p>
+ * If the project is being renamed, the destination project must not
+ * already exist in the workspace resource tree.
+ * </p><p>
+ * Local history is not preserved if the project is renamed. It is preserved
+ * when the project's content area is relocated without renaming the
+ * project.
+ * </p>
+ *
+ * @param source the handle of the source project that was moved
+ * @param description the new project description
+ * @return <code>true</code> if the move succeeded, and <code>false</code>
+ * otherwise
+ */
+ public boolean movedProjectSubtree(IProject source, IProjectDescription description);
+
+ /**
+ * Deletes the given file in the standard manner from both the local file
+ * system and from the workspace resource tree.
+ * <p>
+ * Implementations of <code>IMoveDeleteHook</code> must invoke this method
+ * in lieu of <code>file.delete(updateFlags, monitor)</code> because all
+ * regular API operations that modify resources are off limits.
+ * </p><p>
+ * If the operation fails, the reason for the failure is automatically
+ * collected by an internal call to <code>failed</code>.
+ * </p>
+ *
+ * @param file the file to delete
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ */
+ public void standardDeleteFile(IFile file, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Deletes the given folder and its descendents in the standard manner from
+ * both the local file system and from the workspace resource tree.
+ * <p>
+ * Implementations of <code>IMoveDeleteHook</code> must invoke this method
+ * in lieu of <code>folder.delete(updateFlags, monitor)</code> because all
+ * regular API operations that modify resources are off limits.
+ * </p><p>
+ * If the operation fails, the reason for the failure is automatically
+ * collected by an internal call to <code>failed</code>.
+ * </p>
+ *
+ * @param folder the folder to delete
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ */
+ public void standardDeleteFolder(IFolder folder, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Deletes the given project and its descendents in the standard manner from
+ * both the local file system and from the workspace resource tree.
+ * <p>
+ * Implementations of <code>IMoveDeleteHook</code> must invoke this method
+ * in lieu of <code>project.delete(updateFlags, monitor)</code> because all
+ * regular API operations that modify resources are off limits.
+ * </p><p>
+ * If the operation fails, the reason for the failure is automatically
+ * collected by an internal call to <code>failed</code>.
+ * </p>
+ *
+ * @param project the project to delete
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.delete(int,IProgressMonitor)</code>
+ */
+ public void standardDeleteProject(IProject project, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Moves the given file in the standard manner from both the local file
+ * system and from the workspace resource tree.
+ * <p>
+ * Implementations of <code>IMoveDeleteHook</code> must invoke this method
+ * in lieu of <code>source.move(destination.getProjectRelativePath(),
+ * updateFlags, monitor)</code> because all regular API operations that
+ * modify resources are off limits.
+ * </p><p>
+ * If the operation fails, the reason for the failure is automatically
+ * collected by an internal call to <code>failed</code>.
+ * </p>
+ *
+ * @param source the handle of the source file to move
+ * @param destination the handle of where the file will move to
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ */
+ public void standardMoveFile(IFile source, IFile destination, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Moves the given folder and its descendents in the standard manner from
+ * both the local file system and from the workspace resource tree.
+ * <p>
+ * Implementations of <code>IMoveDeleteHook</code> must invoke this method
+ * in lieu of <code>source.move(destination.getProjectRelativePath(),
+ * updateFlags, monitor)</code> because all regular API operations that
+ * modify resources are off limits.
+ * </p><p>
+ * If the operation fails, the reason for the failure is automatically
+ * collected by an internal call to <code>failed</code>.
+ * </p>
+ *
+ * @param source the handle of the source folder to move
+ * @param destination the handle of where the folder will move to
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ */
+ public void standardMoveFolder(IFolder source, IFolder destination, int updateFlags, IProgressMonitor monitor);
+
+ /**
+ * Renames and/or relocates the given project in the standard manner.
+ * <p>
+ * Implementations of <code>IMoveDeleteHook</code> must invoke this method
+ * in lieu of <code>source.move(description, updateFlags, monitor)</code>
+ * because all regular API operations that modify resources are off limits.
+ * </p><p>
+ * If the operation fails, the reason for the failure is automatically
+ * collected by an internal call to <code>failed</code>.
+ * </p>
+ *
+ * @param source the handle of the source folder to move
+ * @param description the new project description
+ * @param updateFlags bit-wise or of update flag constants as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ * @param monitor the progress monitor, or <code>null</code> as per
+ * <code>IResource.move(IPath,int,IProgressMonitor)</code>
+ */
+ public void standardMoveProject(IProject source, IProjectDescription description, int updateFlags, IProgressMonitor monitor);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/team/ResourceRuleFactory.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.team;
+
+import java.util.HashSet;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+
+/**
+ * Default implementation of IResourceRuleFactory. The teamHook extension
+ * may subclass to provide more specialized scheduling rules for workspace operations that
+ * they participate in.
+ *
+ * @see IResourceRuleFactory
+ * @since 3.0
+ */
+public class ResourceRuleFactory implements IResourceRuleFactory {
+ private final IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ /**
+ * Creates a new default resource rule factory. This constructor must only
+ * be called by subclasses.
+ */
+ protected ResourceRuleFactory() {
+ super();
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#buildRule</code>.
+ * This default implementation always returns the workspace root.
+ * <p>
+ * Subclasses may not currently override this method.
+ *
+ * @see org.eclipse.core.resources.IResourceRuleFactory#buildRule()
+ */
+ public final ISchedulingRule buildRule() {
+ return workspace.getRoot();
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#charsetRule</code>.
+ * This default implementation always returns the project of the resource
+ * whose charset setting is being changed, or <code>null</code> if the
+ * resource is the workspace root.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ * </p>
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#charsetRule(IResource)
+ * @since 3.1
+ */
+ public ISchedulingRule charsetRule(IResource resource) {
+ if (resource.getType() == IResource.ROOT)
+ return null;
+ return resource.getProject();
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#copyRule</code>.
+ * This default implementation always returns the parent of the destination
+ * resource.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#copyRule(IResource, IResource)
+ */
+ public ISchedulingRule copyRule(IResource source, IResource destination) {
+ //source is not modified, destination is created
+ return parent(destination);
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#createRule</code>.
+ * This default implementation always returns the parent of the resource
+ * being created.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#createRule(IResource)
+ */
+ public ISchedulingRule createRule(IResource resource) {
+ return parent(resource);
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#deleteRule</code>.
+ * This default implementation always returns the parent of the resource
+ * being deleted.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#deleteRule(IResource)
+ */
+ public ISchedulingRule deleteRule(IResource resource) {
+ return parent(resource);
+ }
+
+ private boolean isReadOnly(IResource resource) {
+ ResourceAttributes attributes = resource.getResourceAttributes();
+ return attributes == null ? false : attributes.isReadOnly();
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#markerRule</code>.
+ * This default implementation always returns <code>null</code>.
+ * <p>
+ * Subclasses may not currently override this method.
+ *
+ * @see org.eclipse.core.resources.IResourceRuleFactory#markerRule(IResource)
+ */
+ public final ISchedulingRule markerRule(IResource resource) {
+ return null;
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#modifyRule</code>.
+ * This default implementation returns the resource being modified, or the
+ * parent resource if modifying a project description file.
+ * Note that this must encompass any rule required by the <code>validateSave</code> hook.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#modifyRule(IResource)
+ * @see FileModificationValidator#validateSave(IFile)
+ * @see IProjectDescription#DESCRIPTION_FILE_NAME
+ */
+ public ISchedulingRule modifyRule(IResource resource) {
+ IPath path = resource.getFullPath();
+ //modifying the project description may cause linked resources to be created or deleted
+ if (path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME))
+ return parent(resource);
+ return resource;
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#moveRule</code>.
+ * This default implementation returns a rule that combines the parent
+ * of the source resource and the parent of the destination resource.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#moveRule(IResource, IResource)
+ */
+ public ISchedulingRule moveRule(IResource source, IResource destination) {
+ //move needs the parent of both source and destination
+ return MultiRule.combine(parent(source), parent(destination));
+ }
+
+ /**
+ * Convenience method to return the parent of the given resource,
+ * or the resource itself for projects and the workspace root.
+ * @param resource the resource to compute the parent of
+ * @return the parent resource for folders and files, and the
+ * resource itself for projects and the workspace root.
+ */
+ protected final ISchedulingRule parent(IResource resource) {
+ switch (resource.getType()) {
+ case IResource.ROOT :
+ case IResource.PROJECT :
+ return resource;
+ default :
+ return resource.getParent();
+ }
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#refreshRule</code>.
+ * This default implementation always returns the parent of the resource
+ * being refreshed.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#refreshRule(IResource)
+ */
+ public ISchedulingRule refreshRule(IResource resource) {
+ return parent(resource);
+ }
+
+ /**
+ * Default implementation of <code>IResourceRuleFactory#validateEditRule</code>.
+ * This default implementation returns a rule that combines the parents of
+ * all read-only resources, or <code>null</code> if there are no read-only
+ * resources.
+ * <p>
+ * Subclasses may override this method. The rule provided by an overriding
+ * method must at least contain the rule from this default implementation.
+ *
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ * @see org.eclipse.core.resources.IResourceRuleFactory#validateEditRule(IResource[])
+ */
+ public ISchedulingRule validateEditRule(IResource[] resources) {
+ if (resources.length == 0)
+ return null;
+ //optimize rule for single file
+ if (resources.length == 1)
+ return isReadOnly(resources[0]) ? parent(resources[0]) : null;
+ //need a lock on the parents of all read-only files
+ HashSet rules = new HashSet();
+ for (int i = 0; i < resources.length; i++)
+ if (isReadOnly(resources[i]))
+ rules.add(parent(resources[i]));
+ if (rules.isEmpty())
+ return null;
+ if (rules.size() == 1)
+ return (ISchedulingRule) rules.iterator().next();
+ ISchedulingRule[] ruleArray = (ISchedulingRule[]) rules.toArray(new ISchedulingRule[rules.size()]);
+ return new MultiRule(ruleArray);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/resources/team/TeamHook.java Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.team;
+
+import java.net.URI;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.internal.resources.InternalTeamHook;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+/**
+ * A general hook class for operations that team providers may be
+ * interested in participating in. Implementors of the hook should provide
+ * a concrete subclass, and override any methods they are interested in.
+ * <p>
+ * This class is intended to be subclassed by the team component in
+ * conjunction with the <code>org.eclipse.core.resources.teamHook</code>
+ * standard extension point. Individual team providers may also subclass this
+ * class. It is not intended to be subclassed by other clients. The methods
+ * defined on this class are called from within the implementations of
+ * workspace API methods and must not be invoked directly by clients.
+ * </p>
+ *
+ * @since 2.1
+ */
+public abstract class TeamHook extends InternalTeamHook {
+ /**
+ * The default resource scheduling rule factory. This factory can be used for projects
+ * that the team hook methods do not participate in.
+ *
+ * @see #getRuleFactory(IProject)
+ * @see #setRuleFactory(IProject, IResourceRuleFactory)
+ * @since 3.0
+ */
+ protected final IResourceRuleFactory defaultFactory = new ResourceRuleFactory();
+
+ /**
+ * Creates a new team hook. Default constructor for use by subclasses and the
+ * resources plug-in only.
+ */
+ protected TeamHook() {
+ super();
+ }
+
+ /**
+ * Returns the resource scheduling rule factory that should be used when workspace
+ * operations are invoked on resources in that project. The workspace will ask the
+ * team hook this question only once per project, per session. The workspace will
+ * assume the returned result is valid for the rest of that session, unless the rule
+ * is changed by calling <code>setRuleFactory</code>.
+ * <p>
+ * This method must not return <code>null</code>. If no special rules are required
+ * by the team hook for the given project, the value of the <code>defaultFactory</code>
+ * field should be returned.
+ * <p>
+ * This default implementation always returns the value of the <code>defaultFactory</code>
+ * field. Subclasses may override and provide a subclass of <code>ResourceRuleFactory</code>.
+ *
+ * @param project the project to return scheduling rules for
+ * @return the resource scheduling rules for a project
+ * @see #setRuleFactory(IProject, IResourceRuleFactory)
+ * @see ResourceRuleFactory
+ * @since 3.0
+ */
+ public IResourceRuleFactory getRuleFactory(IProject project) {
+ return defaultFactory;
+ }
+
+ /**
+ * Sets the resource scheduling rule factory to use for resource modifications
+ * in the given project. This method only needs to be called if the factory has changed
+ * since the initial call to <code>getRuleFactory</code> for the given project
+ * <p>
+ * The supplied factory must not be <code>null</code>. If no special rules are required
+ * by the team hook for the given project, the value of the <code>defaultFactory</code>
+ * field should be used.
+ * <p>
+ * Note that the new rule factory will only take effect for resource changing
+ * operations that begin after this method completes. Care should be taken to
+ * avoid calling this method during the invocation of any resource changing
+ * operation (in any thread). The best time to change rule factories is during resource
+ * change notification when the workspace is locked for modification.
+ *
+ * @param project the project to change the resource rule factory for
+ * @param factory the new resource rule factory
+ * @see #getRuleFactory(IProject)
+ * @see IResourceRuleFactory
+ * @since 3.0
+ */
+ protected final void setRuleFactory(IProject project, IResourceRuleFactory factory) {
+ super.setRuleFactory(project, factory);
+ }
+
+ /**
+ * Validates whether a particular attempt at link creation is allowed. This gives
+ * team providers an opportunity to hook into the beginning of the implementation
+ * of <code>IFile.createLink</code>.
+ * <p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p><p>
+ * This method should be overridden by subclasses that want to control what
+ * links are created. The default implementation of this method allows all links
+ * to be created.
+ * </p>
+ *
+ * @param file the file to be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * (only ALLOW_MISSING_LOCAL is relevant here)
+ * @param location a file system path where the file should be linked
+ * @return a status object with code <code>IStatus.OK</code>
+ * if linking is allowed, otherwise a status object with severity
+ * <code>IStatus.ERROR</code> indicating why the creation is not allowed.
+ * @see org.eclipse.core.resources.IResource#ALLOW_MISSING_LOCAL
+ */
+ public IStatus validateCreateLink(IFile file, int updateFlags, IPath location) {
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates whether a particular attempt at link creation is allowed. This gives
+ * team providers an opportunity to hook into the beginning of the implementation
+ * of {@link IFile#createLink(URI, int, IProgressMonitor) }
+ * <p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p><p>
+ * This method should be overridden by subclasses that want to control what
+ * links are created. The default implementation of this method allows all links
+ * to be created.
+ * </p>
+ *
+ * @param file the file to be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * (only ALLOW_MISSING_LOCAL is relevant here)
+ * @param location a file system URI where the file should be linked
+ * @return a status object with code <code>IStatus.OK</code>
+ * if linking is allowed, otherwise a status object with severity
+ * <code>IStatus.ERROR</code> indicating why the creation is not allowed.
+ * @see org.eclipse.core.resources.IResource#ALLOW_MISSING_LOCAL
+ * @since 3.2
+ */
+ public IStatus validateCreateLink(IFile file, int updateFlags, URI location) {
+ //forward to old method to ensure old hooks get a chance to validate in the local case
+ if (EFS.SCHEME_FILE.equals(location.getScheme()))
+ return validateCreateLink(file, updateFlags, URIUtil.toPath(location));
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates whether a particular attempt at link creation is allowed. This gives
+ * team providers an opportunity to hook into the beginning of the implementation
+ * of <code>IFolder.createLink</code>.
+ * <p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p><p>
+ * This method should be overridden by subclasses that want to control what
+ * links are created. The default implementation of this method allows all links
+ * to be created.
+ * </p>
+ *
+ * @param folder the file to be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * (only ALLOW_MISSING_LOCAL is relevant here)
+ * @param location a file system path where the folder should be linked
+ * @return a status object with code <code>IStatus.OK</code>
+ * if linking is allowed, otherwise a status object with severity
+ * <code>IStatus.ERROR</code> indicating why the creation is not allowed.
+ * @see org.eclipse.core.resources.IResource#ALLOW_MISSING_LOCAL
+ */
+ public IStatus validateCreateLink(IFolder folder, int updateFlags, IPath location) {
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Validates whether a particular attempt at link creation is allowed. This gives
+ * team providers an opportunity to hook into the beginning of the implementation
+ * of {@link IFolder#createLink(URI, int, IProgressMonitor)}
+ * <p>
+ * The implementation of this method runs "below" the resources API and is
+ * therefore very restricted in what resource API method it can call. The
+ * list of useable methods includes most resource operations that read but
+ * do not update the resource tree; resource operations that modify
+ * resources and trigger deltas must not be called from within the dynamic
+ * scope of the invocation of this method.
+ * </p><p>
+ * This method should be overridden by subclasses that want to control what
+ * links are created. The default implementation of this method allows all links
+ * to be created.
+ * </p>
+ *
+ * @param folder the file to be linked
+ * @param updateFlags bit-wise or of update flag constants
+ * (only ALLOW_MISSING_LOCAL is relevant here)
+ * @param location a file system path where the folder should be linked
+ * @return a status object with code <code>IStatus.OK</code>
+ * if linking is allowed, otherwise a status object with severity
+ * <code>IStatus.ERROR</code> indicating why the creation is not allowed.
+ * @see org.eclipse.core.resources.IResource#ALLOW_MISSING_LOCAL
+ * @since 3.2
+ */
+ public IStatus validateCreateLink(IFolder folder, int updateFlags, URI location) {
+ //forward to old method to ensure old hooks get a chance to validate in the local case
+ if (EFS.SCHEME_FILE.equals(location.getScheme()))
+ return validateCreateLink(folder, updateFlags, URIUtil.toPath(location));
+ return Status.OK_STATUS;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.platform-feature/.project Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.platform-feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.platform-feature/build.properties Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,5 @@
+bin.includes = eclipse_update_120.jpg,\
+ epl-v10.html,\
+ feature.properties,\
+ feature.xml,\
+ license.html
Binary file platform35/org.eclipse.platform-feature/eclipse_update_120.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.platform-feature/epl-v10.html Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,328 @@
+<html xmlns:o="urn:schemas-microsoft-com:office:office"
+xmlns:w="urn:schemas-microsoft-com:office:word"
+xmlns="http://www.w3.org/TR/REC-html40">
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+<meta name=ProgId content=Word.Document>
+<meta name=Generator content="Microsoft Word 9">
+<meta name=Originator content="Microsoft Word 9">
+<link rel=File-List
+href="./Eclipse%20EPL%202003_11_10%20Final_files/filelist.xml">
+<title>Eclipse Public License - Version 1.0</title>
+<!--[if gte mso 9]><xml>
+ <o:DocumentProperties>
+ <o:Revision>2</o:Revision>
+ <o:TotalTime>3</o:TotalTime>
+ <o:Created>2004-03-05T23:03:00Z</o:Created>
+ <o:LastSaved>2004-03-05T23:03:00Z</o:LastSaved>
+ <o:Pages>4</o:Pages>
+ <o:Words>1626</o:Words>
+ <o:Characters>9270</o:Characters>
+ <o:Lines>77</o:Lines>
+ <o:Paragraphs>18</o:Paragraphs>
+ <o:CharactersWithSpaces>11384</o:CharactersWithSpaces>
+ <o:Version>9.4402</o:Version>
+ </o:DocumentProperties>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:WordDocument>
+ <w:TrackRevisions/>
+ </w:WordDocument>
+</xml><![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+@font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:swiss;
+ mso-font-pitch:variable;
+ mso-font-signature:553679495 -2147483648 8 0 66047 0;}
+ /* Style Definitions */
+p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {mso-style-parent:"";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";}
+p
+ {margin-right:0in;
+ mso-margin-top-alt:auto;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";}
+p.BalloonText, li.BalloonText, div.BalloonText
+ {mso-style-name:"Balloon Text";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:8.0pt;
+ font-family:Tahoma;
+ mso-fareast-font-family:"Times New Roman";}
+@page Section1
+ {size:8.5in 11.0in;
+ margin:1.0in 1.25in 1.0in 1.25in;
+ mso-header-margin:.5in;
+ mso-footer-margin:.5in;
+ mso-paper-source:0;}
+div.Section1
+ {page:Section1;}
+-->
+</style>
+</head>
+
+<body lang=EN-US style='tab-interval:.5in'>
+
+<div class=Section1>
+
+<p align=center style='text-align:center'><b>Eclipse Public License - v 1.0</b>
+</p>
+
+<p><span style='font-size:10.0pt'>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER
+THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE,
+REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE
+OF THIS AGREEMENT.</span> </p>
+
+<p><b><span style='font-size:10.0pt'>1. DEFINITIONS</span></b> </p>
+
+<p><span style='font-size:10.0pt'>"Contribution" means:</span> </p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a)
+in the case of the initial Contributor, the initial code and documentation
+distributed under this Agreement, and<br clear=left>
+b) in the case of each subsequent Contributor:</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>i)
+changes to the Program, and</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>ii)
+additions to the Program;</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>where
+such changes and/or additions to the Program originate from and are distributed
+by that particular Contributor. A Contribution 'originates' from a Contributor
+if it was added to the Program by such Contributor itself or anyone acting on
+such Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are not derivative
+works of the Program. </span></p>
+
+<p><span style='font-size:10.0pt'>"Contributor" means any person or
+entity that distributes the Program.</span> </p>
+
+<p><span style='font-size:10.0pt'>"Licensed Patents " mean patent
+claims licensable by a Contributor which are necessarily infringed by the use
+or sale of its Contribution alone or when combined with the Program. </span></p>
+
+<p><span style='font-size:10.0pt'>"Program" means the Contributions
+distributed in accordance with this Agreement.</span> </p>
+
+<p><span style='font-size:10.0pt'>"Recipient" means anyone who
+receives the Program under this Agreement, including all Contributors.</span> </p>
+
+<p><b><span style='font-size:10.0pt'>2. GRANT OF RIGHTS</span></b> </p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a)
+Subject to the terms of this Agreement, each Contributor hereby grants Recipient
+a non-exclusive, worldwide, royalty-free copyright license to<span
+style='color:red'> </span>reproduce, prepare derivative works of, publicly
+display, publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and object code
+form.</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>b)
+Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide,<span style='color:green'> </span>royalty-free
+patent license under Licensed Patents to make, use, sell, offer to sell, import
+and otherwise transfer the Contribution of such Contributor, if any, in source
+code and object code form. This patent license shall apply to the combination
+of the Contribution and the Program if, at the time the Contribution is added
+by the Contributor, such addition of the Contribution causes such combination
+to be covered by the Licensed Patents. The patent license shall not apply to
+any other combinations which include the Contribution. No hardware per se is
+licensed hereunder. </span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>c)
+Recipient understands that although each Contributor grants the licenses to its
+Contributions set forth herein, no assurances are provided by any Contributor
+that the Program does not infringe the patent or other intellectual property
+rights of any other entity. Each Contributor disclaims any liability to Recipient
+for claims brought by any other entity based on infringement of intellectual
+property rights or otherwise. As a condition to exercising the rights and
+licenses granted hereunder, each Recipient hereby assumes sole responsibility
+to secure any other intellectual property rights needed, if any. For example,
+if a third party patent license is required to allow Recipient to distribute
+the Program, it is Recipient's responsibility to acquire that license before
+distributing the Program.</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>d)
+Each Contributor represents that to its knowledge it has sufficient copyright
+rights in its Contribution, if any, to grant the copyright license set forth in
+this Agreement. </span></p>
+
+<p><b><span style='font-size:10.0pt'>3. REQUIREMENTS</span></b> </p>
+
+<p><span style='font-size:10.0pt'>A Contributor may choose to distribute the
+Program in object code form under its own license agreement, provided that:</span>
+</p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a)
+it complies with the terms and conditions of this Agreement; and</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>b)
+its license agreement:</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>i)
+effectively disclaims on behalf of all Contributors all warranties and
+conditions, express and implied, including warranties or conditions of title
+and non-infringement, and implied warranties or conditions of merchantability
+and fitness for a particular purpose; </span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>ii)
+effectively excludes on behalf of all Contributors all liability for damages,
+including direct, indirect, special, incidental and consequential damages, such
+as lost profits; </span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>iii)
+states that any provisions which differ from this Agreement are offered by that
+Contributor alone and not by any other party; and</span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>iv)
+states that source code for the Program is available from such Contributor, and
+informs licensees how to obtain it in a reasonable manner on or through a
+medium customarily used for software exchange.<span style='color:blue'> </span></span></p>
+
+<p><span style='font-size:10.0pt'>When the Program is made available in source
+code form:</span> </p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a)
+it must be made available under this Agreement; and </span></p>
+
+<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>b) a
+copy of this Agreement must be included with each copy of the Program. </span></p>
+
+<p><span style='font-size:10.0pt'>Contributors may not remove or alter any
+copyright notices contained within the Program. </span></p>
+
+<p><span style='font-size:10.0pt'>Each Contributor must identify itself as the
+originator of its Contribution, if any, in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution. </span></p>
+
+<p><b><span style='font-size:10.0pt'>4. COMMERCIAL DISTRIBUTION</span></b> </p>
+
+<p><span style='font-size:10.0pt'>Commercial distributors of software may
+accept certain responsibilities with respect to end users, business partners
+and the like. While this license is intended to facilitate the commercial use
+of the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes the
+Program in a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified Contributor to
+the extent caused by the acts or omissions of such Commercial Contributor in
+connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In order
+to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial Contributor
+to control, and cooperate with the Commercial Contributor in, the defense and
+any related settlement negotiations. The Indemnified Contributor may participate
+in any such claim at its own expense.</span> </p>
+
+<p><span style='font-size:10.0pt'>For example, a Contributor might include the
+Program in a commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance claims and
+warranties are such Commercial Contributor's responsibility alone. Under this
+section, the Commercial Contributor would have to defend claims against the
+other Contributors related to those performance claims and warranties, and if a
+court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.</span> </p>
+
+<p><b><span style='font-size:10.0pt'>5. NO WARRANTY</span></b> </p>
+
+<p><span style='font-size:10.0pt'>EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING,
+WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and distributing the
+Program and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs or
+equipment, and unavailability or interruption of operations. </span></p>
+
+<p><b><span style='font-size:10.0pt'>6. DISCLAIMER OF LIABILITY</span></b> </p>
+
+<p><span style='font-size:10.0pt'>EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF
+THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.</span> </p>
+
+<p><b><span style='font-size:10.0pt'>7. GENERAL</span></b> </p>
+
+<p><span style='font-size:10.0pt'>If any provision of this Agreement is invalid
+or unenforceable under applicable law, it shall not affect the validity or
+enforceability of the remainder of the terms of this Agreement, and without
+further action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.</span> </p>
+
+<p><span style='font-size:10.0pt'>If Recipient institutes patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the date
+such litigation is filed. </span></p>
+
+<p><span style='font-size:10.0pt'>All Recipient's rights under this Agreement
+shall terminate if it fails to comply with any of the material terms or
+conditions of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive. </span></p>
+
+<p><span style='font-size:10.0pt'>Everyone is permitted to copy and distribute
+copies of this Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The Agreement
+Steward reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the initial
+Agreement Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each new version
+of the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version of
+the Agreement under which it was received. In addition, after a new version of
+the Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
+the intellectual property of any Contributor under this Agreement, whether
+expressly, by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.</span> </p>
+
+<p><span style='font-size:10.0pt'>This Agreement is governed by the laws of the
+State of New York and the intellectual property laws of the United States of
+America. No party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.</span> </p>
+
+<p class=MsoNormal><![if !supportEmptyParas]> <![endif]><o:p></o:p></p>
+
+</div>
+
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.platform-feature/feature.properties Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,148 @@
+###############################################################################
+# Copyright (c) 2000, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+# feature.properties
+# contains externalized strings for feature.xml
+# "%foo" in feature.xml corresponds to the key "foo" in this file
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# This file should be translated.
+
+# "featureName" property - name of the feature
+featureName=Eclipse Platform
+
+# "providerName" property - name of the company that provides the feature
+providerName=Eclipse.org
+
+# "updateSiteName" property - label for the update site
+updateSiteName=The Eclipse Project Updates
+
+# "secondarySiteName" property - label for the update site
+secondaryUpdateSiteName=Galileo Discovery Site
+
+
+# "description" property - description of the feature
+description=Common OS-independent base of the Eclipse platform. (Binary runtime and user documentation.)
+
+# "copyright" property - text of the "Feature Update Copyright"
+copyright=\
+Copyright (c) 2000, 2009 IBM Corporation and others.\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Public License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/legal/epl-v10.html\n\
+\n\
+Contributors:\n\
+ IBM Corporation - initial API and implementation\n
+################ end of copyright property ####################################
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=license.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+ECLIPSE FOUNDATION SOFTWARE USER AGREEMENT\n\
+March 17, 2005\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\
+OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\
+USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\
+AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\
+NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\
+AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\
+AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\
+OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\
+OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\
+is provided to you under the terms and conditions of the Eclipse Public\n\
+License Version 1.0 ("EPL"). A copy of the EPL is provided with this\n\
+Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\
+For purposes of the EPL, "Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code,\n\
+documentation and other files maintained in the Eclipse.org CVS\n\
+repository ("Repository") in CVS modules ("Modules") and made available\n\
+as downloadable archives ("Downloads").\n\
+\n\
+ - Content may be structured and packaged into modules to facilitate delivering,\n\
+ extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\
+ plug-in fragments ("Fragments"), and features ("Features").\n\
+ - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java? ARchive)\n\
+ in a directory named "plugins".\n\
+ - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\
+ Each Feature may be packaged as a sub-directory in a directory named "features".\n\
+ Within a Feature, files named "feature.xml" may contain a list of the names and version\n\
+ numbers of the Plug-ins and/or Fragments associated with that Feature.\n\
+ - Features may also include other Features ("Included Features"). Within a Feature, files\n\
+ named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\
+\n\
+Features may also include other Features ("Included Features"). Files named\n\
+"feature.xml" may contain a list of the names and version numbers of\n\
+Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be\n\
+contained in files named "about.html" ("Abouts"). The terms and\n\
+conditions governing Features and Included Features should be contained\n\
+in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\
+Licenses may be located in any directory of a Download or Module\n\
+including, but not limited to the following locations:\n\
+\n\
+ - The top-level (root) directory\n\
+ - Plug-in and Fragment directories\n\
+ - Inside Plug-ins and Fragments packaged as JARs\n\
+ - Sub-directories of the directory named "src" of certain Plug-ins\n\
+ - Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using the\n\
+Eclipse Update Manager, you must agree to a license ("Feature Update\n\
+License") during the installation process. If the Feature contains\n\
+Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform\n\
+you where you can locate them. Feature Update Licenses may be found in\n\
+the "license" property of files named "feature.properties". Such Abouts,\n\
+Feature Licenses and Feature Update Licenses contain the terms and\n\
+conditions (or references to such terms and conditions) that govern your\n\
+use of the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES AND FEATURE UPDATE LICENSES MAY REFER\n\
+TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\
+SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+ - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\
+ - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\
+ - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\
+ - IBM Public License 1.0 (available at http://oss.software.ibm.com/developerworks/opensource/license10.html)\n\
+ - Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)\n\
+ - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\
+TO USE OF THE CONTENT. If no About, Feature License or Feature Update License\n\
+is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\
+govern that particular Content.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are\n\
+currently may have restrictions on the import, possession, and use,\n\
+and/or re-export to another country, of encryption software. BEFORE\n\
+using any encryption software, please check the country's laws,\n\
+regulations and policies concerning the import, possession, or use,\n\
+and re-export of encryption software, to see if this is permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.\n
+########### end of license property ##########################################
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.platform-feature/feature.xml Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,619 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="org.eclipse.platform"
+ label="%featureName"
+ version="3.5.0.v20090611a-9gEeG1HFtQcmRThO4O3aR_fqSMvJR2sJ"
+ provider-name="%providerName"
+ image="eclipse_update_120.jpg">
+
+ <description>
+ %description
+ </description>
+
+ <copyright>
+ %copyright
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <url>
+ <update label="%updateSiteName" url="http://download.eclipse.org/eclipse/updates/3.5"/>
+ <discovery label="%updateSiteName" url="http://download.eclipse.org/eclipse/updates/3.5"/>
+ <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/galileo"/>
+ </url>
+
+ <includes
+ id="org.eclipse.rcp"
+ version="3.5.0.v20090519-9SA0FwxFv6x089WEf-TWh11"/>
+
+ <includes
+ id="org.eclipse.equinox.p2.user.ui"
+ version="1.1.0.v20090605-1440-7u6Fb3FbPbJP5MjJjZgifk"
+ optional="true"/>
+
+ <includes
+ id="org.eclipse.help"
+ version="1.1.0.v20090602-7e7eFAlFEx2XZoYqwI68e2F"/>
+
+ <plugin
+ id="org.apache.ant"
+ download-size="0"
+ install-size="0"
+ version="1.7.1.v20090120-1145"/>
+
+ <plugin
+ id="org.eclipse.ant.core"
+ download-size="0"
+ install-size="0"
+ version="3.2.100.v20090520"
+ unpack="false"/>
+
+ <plugin
+ id="com.jcraft.jsch"
+ download-size="0"
+ install-size="0"
+ version="0.1.41.v200903070017"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.compare.core"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.I20090430-0408"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.compare"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.I20090514-0808"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.compare.win32"
+ os="win32"
+ download-size="0"
+ install-size="0"
+ version="1.0.0.I20090430-0408"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.boot"
+ download-size="0"
+ install-size="0"
+ version="3.1.100.v20080218"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filebuffers"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090526-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem"
+ download-size="0"
+ install-size="0"
+ version="1.2.0.v20090507"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.net"
+ download-size="0"
+ install-size="0"
+ version="1.2.0.I20090522-1010"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.net.win32.x86"
+ os="win32"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.0.0.I20080909"
+ fragment="true"
+ unpack="false"/>
+
+<plugin
+ id="org.eclipse.core.net.win32.x86_64"
+ os="win32"
+ arch="x86_64"
+ download-size="0"
+ install-size="0"
+ version="1.0.0.I20090306-1030"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.net.linux.x86"
+ os="linux"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.1.0.I20081021"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.resources"
+ download-size="0"
+ install-size="0"
+ version="3.5.10.qualifier"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.resources.compatibility"
+ download-size="0"
+ install-size="0"
+ version="3.4.0.v20090505"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.runtime.compatibility"
+ download-size="0"
+ install-size="0"
+ version="3.2.0.v20090413"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.runtime.compatibility.registry"
+ download-size="0"
+ install-size="0"
+ version="3.2.200.v20090429-1800"
+ fragment="true"/>
+
+ <plugin
+ id="org.eclipse.osgi.util"
+ download-size="0"
+ install-size="0"
+ version="3.2.0.v20090520-1800"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.debug.core"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090526-1600"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.debug.ui"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090603"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.help.appserver"
+ download-size="0"
+ install-size="0"
+ version="3.1.400.v20090429_1800"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ltk.core.refactoring"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090513-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ltk.ui.refactoring"
+ download-size="0"
+ install-size="0"
+ version="3.4.100.v20090604"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.platform"
+ download-size="0"
+ install-size="0"
+ version="3.3.200.v200906111540"/>
+
+ <plugin
+ id="org.eclipse.platform.doc.user"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090611"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.search"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090526-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.team.core"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.I20090527-0620"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.team.ui"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.I20090430-0408"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.text"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090513-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.jface.text"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090602"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.jsch.core"
+ download-size="0"
+ install-size="0"
+ version="1.1.100.I20090430-0408"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.jsch.ui"
+ download-size="0"
+ install-size="0"
+ version="1.1.200.I20090430-0408"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.console"
+ download-size="0"
+ install-size="0"
+ version="3.4.0.v20090513"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.presentations.r21"
+ download-size="0"
+ install-size="0"
+ version="3.2.100.I20081007-0800"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.intro"
+ download-size="0"
+ install-size="0"
+ version="3.3.0.v20090526"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.intro.universal"
+ download-size="0"
+ install-size="0"
+ version="3.2.300.v20090526"/>
+
+ <plugin
+ id="org.eclipse.ui.cheatsheets"
+ download-size="0"
+ install-size="0"
+ version="3.3.200.v20090526"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.browser"
+ download-size="0"
+ install-size="0"
+ version="3.2.300.v20090526"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.navigator"
+ download-size="0"
+ install-size="0"
+ version="3.4.0.I20090525-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.navigator.resources"
+ download-size="0"
+ install-size="0"
+ version="3.4.0.I20090525-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.net"
+ download-size="0"
+ install-size="0"
+ version="1.2.0.I20090430-0408"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.workbench.texteditor"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090603"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.views"
+ download-size="0"
+ install-size="0"
+ version="3.4.0.I20090525-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.editors"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20090527-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.externaltools"
+ download-size="0"
+ install-size="0"
+ version="3.2.0.v20090504"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.ide"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.I20090525-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.ide.application"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.I20090525-2000"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.win32"
+ ws="win32"
+ download-size="0"
+ install-size="0"
+ version="3.2.100.v20090429-1800"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.update.core"
+ download-size="0"
+ install-size="0"
+ version="3.2.300.v20090525"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.update.scheduler"
+ download-size="0"
+ install-size="0"
+ version="3.2.200.v20081127"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.update.ui"
+ download-size="0"
+ install-size="0"
+ version="3.2.200.v20090213"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.workbench.compatibility"
+ download-size="0"
+ install-size="0"
+ version="3.2.0.I20090429-1800"
+ fragment="true"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.hpux.ia64_32"
+ os="hpux"
+ arch="ia64_32"
+ download-size="0"
+ install-size="0"
+ version="1.0.0.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.hpux.PA_RISC"
+ os="hpux"
+ arch="PA_RISC"
+ download-size="0"
+ install-size="0"
+ version="1.0.0.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.linux.ppc"
+ os="linux"
+ arch="ppc"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.linux.x86"
+ os="linux"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.2.0.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.linux.x86_64"
+ os="linux"
+ arch="x86_64"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.solaris.sparc"
+ os="solaris"
+ arch="sparc"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.macosx"
+ os="macosx"
+ download-size="0"
+ install-size="0"
+ version="1.1.0.v20090112"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.qnx.x86"
+ os="qnx"
+ ws="photon"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.0.0.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.filesystem.win32.x86"
+ os="win32"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.1.0.v20080604-1400"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.resources.win32.x86"
+ os="win32"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.v20081020"
+ fragment="true"
+ unpack="false"/>
+
+
+ <plugin
+ id="org.eclipse.core.filesystem.win32.x86_64"
+ os="win32"
+ arch="x86_64"
+ download-size="0"
+ install-size="0"
+ version="1.1.0.v20090316-0910"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.core.variables"
+ download-size="0"
+ install-size="0"
+ version="3.2.200.v20090521"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.forms"
+ download-size="0"
+ install-size="0"
+ version="3.4.0.v20090526"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.update.core.linux"
+ os="linux"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="3.2.100.v20081008"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.update.core.win32"
+ os="win32"
+ download-size="0"
+ install-size="0"
+ version="3.2.100.v20080107"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.ui.views.properties.tabbed"
+ download-size="0"
+ install-size="0"
+ version="3.5.0.I20090429-1800"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.equinox.security"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.v20090520-1800"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.equinox.security.ui"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.v20090520-1800"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.equinox.security.win32.x86"
+ os="win32"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.0.100.v20090520-1800"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.equinox.security.macosx"
+ os="macosx"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="1.100.0.v20090520-1800"
+ fragment="true"
+ unpack="false"/>
+ <plugin
+ id="org.eclipse.equinox.security.macosx"
+ os="macosx"
+ arch="x86_64"
+ download-size="0"
+ install-size="0"
+ version="1.100.0.v20090520-1800"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.equinox.security.macosx"
+ os="macosx"
+ arch="ppc"
+ download-size="0"
+ install-size="0"
+ version="1.100.0.v20090520-1800"
+ fragment="true"
+ unpack="false"/>
+
+
+</feature>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.platform-feature/license.html Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=iso-8859-1">
+<title>Eclipse.org Software User Agreement</title>
+</head>
+
+<body lang="EN-US" link=blue vlink=purple>
+<h2>Eclipse Foundation Software User Agreement</h2>
+<p>March 17, 2005</p>
+
+<h3>Usage Of Content</h3>
+
+<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+ (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+ CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
+ OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
+ NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
+ CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
+
+<h3>Applicable Licenses</h3>
+
+<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
+ ("EPL"). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+ For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse.org CVS repository ("Repository") in CVS
+ modules ("Modules") and made available as downloadable archives ("Downloads").</p>
+
+<ul>
+ <li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
+ <li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".</li>
+ <li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
+ and/or Fragments associated with that Feature.</li>
+ <li>Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.</li>
+</ul>
+
+<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
+Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
+including, but not limited to the following locations:</p>
+
+<ul>
+ <li>The top-level (root) directory</li>
+ <li>Plug-in and Fragment directories</li>
+ <li>Inside Plug-ins and Fragments packaged as JARs</li>
+ <li>Sub-directories of the directory named "src" of certain Plug-ins</li>
+ <li>Feature directories</li>
+</ul>
+
+<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Eclipse Update Manager, you must agree to a license ("Feature Update License") during the
+installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
+inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
+that directory.</p>
+
+<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
+
+<ul>
+ <li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
+ <li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
+ <li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
+ <li>IBM Public License 1.0 (available at <a href="http://oss.software.ibm.com/developerworks/opensource/license10.html">http://oss.software.ibm.com/developerworks/opensource/license10.html</a>)</li>
+ <li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
+ <li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
+</ul>
+
+<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
+contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
+
+<h3>Cryptography</h3>
+
+<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
+ another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
+ possession, or use, and re-export of encryption software, to see if this is permitted.</p>
+
+<small>Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.</small>
+</body>
+</html>