Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[cdt-patch] WorkingCopy and LRU Caching in the Core Model

Index: ChangeLog
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/ChangeLog,v
retrieving revision 1.73
diff -u -r1.73 ChangeLog
--- ChangeLog	12 Mar 2003 15:09:33 -0000	1.73
+++ ChangeLog	19 Mar 2003 20:02:38 -0000
@@ -1,3 +1,55 @@
+2003-03-19 Alain Magloire
+
+	Patch from Amer Hoda.
+	Introducing the WorkingCopy in the Core Model.
+	Those changes introduce the same functionnality as JDT.
+
+	All CElements have a common way of opening/modifying the source. In other words and
+	opening of Translation Unit (ITranslationUnit) is done via the buffer mechanism
+	To commit changes isConsistent and makeConsistent must be called.
+
+	The Core Model maintains an LRU cache of open elements, and automatically closes elements
+	as they are swapped out of the cache to make room for other elements.
+	Elements with unsaved changes are never removed from the cache, and thus, if the client
+	maintains many open elements with unsaved changes, the LRU cache can grow in size
+	(in this case the cache is not bounded). However, as elements
+	are saved, the cache will shrink back to its original bounded size. 
+
+	* model/org/eclipse/cdt/core/model/ElementChangeEvent.java
+	* model/org/eclipse/cdt/core/model/ICOpenable.java
+	* model/org/eclipse/cdt/core/model/ICResource.java
+	* model/org/eclipse/cdt/core/model/ITranslationUnit.java
+
+	* model/org/eclipse/cdt/internal/core/model/Buffer.java
+	* model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java
+	* model/org/eclipse/cdt/internal/core/model/CElement.java
+	* model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java
+	* model/org/eclipse/cdt/internal/core/model/CElementInfo.java
+	* model/org/eclipse/cdt/internal/core/model/CFile.java
+	* model/org/eclipse/cdt/internal/core/model/CFolder.java
+	* model/org/eclipse/cdt/internal/core/model/CModelCache.java
+	* model/org/eclipse/cdt/internal/core/model/CModelManager.java
+	* model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java
+	* model/org/eclipse/cdt/internal/core/model/CProject.java
+	* model/org/eclipse/cdt/internal/core/model/CResource.java
+	* model/org/eclipse/cdt/internal/core/model/CRoot.java
+	* model/org/eclipse/cdt/internal/core/model/CElementCache.java
+	* model/org/eclipse/cdt/internal/core/model/IBuffer.java
+	* model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java
+	* model/org/eclipse/cdt/internal/core/model/IBufferFactory.java
+	* model/org/eclipse/cdt/internal/core/model/TranslationUnit.java
+	* model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java
+	* model/org/eclipse/cdt/internal/core/model/Util.java
+	* model/org/eclipse/cdt/internal/core/model/WorkingCopy.java
+	* model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java
+
+
+	* model/org/eclipse/cdt/internal/core/util/ICacheEnumeration.java
+	* model/org/eclipse/cdt/internal/core/util/ILRUCache.java
+	* model/org/eclipse/cdt/internal/core/util/LRUCacheEnumeration.java
+	* model/org/eclipse/cdt/internal/core/util/OverFlowingLRUCache.java
+	* model/org/eclipse/cdt/internal/core/util/ToStringSorter.java
+
 2003-03-12 Alain Magloire
 
 	* utils/org/eclipse/cdt/utils/elf/Elf.java:
Index: model/org/eclipse/cdt/core/model/ElementChangedEvent.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java,v
retrieving revision 1.1
diff -u -r1.1 ElementChangedEvent.java
--- model/org/eclipse/cdt/core/model/ElementChangedEvent.java	26 Jun 2002 20:37:15 -0000	1.1
+++ model/org/eclipse/cdt/core/model/ElementChangedEvent.java	19 Mar 2003 20:02:39 -0000
@@ -15,6 +15,9 @@
  * @see ICElementDelta
  */
 public class ElementChangedEvent extends EventObject {
+	public static final int POST_CHANGE = 1;
+	public static final int PRE_AUTO_BUILD = 2;
+	public static final int 	POST_RECONCILE = 4;	
 	/**
 	 * Creates an new element changed event (based on a <code>ICElementDelta</code>).
 	 *
Index: model/org/eclipse/cdt/core/model/ICOpenable.java
===================================================================
RCS file: model/org/eclipse/cdt/core/model/ICOpenable.java
diff -N model/org/eclipse/cdt/core/model/ICOpenable.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/core/model/ICOpenable.java	19 Mar 2003 20:02:39 -0000
@@ -0,0 +1,70 @@
+package org.eclipse.cdt.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import org.eclipse.cdt.internal.core.model.IBuffer;
+import org.eclipse.cdt.internal.core.model.IBufferChangedListener;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * An openable is an element that can be opened, saved, and closed.
+ * An openable might or might not have an associated buffer.
+ */
+public interface ICOpenable extends IBufferChangedListener{
+	/**
+	 * Closes this element and its buffer (if any).
+	 */
+	public void close() throws CModelException;
+	/**
+	 * Returns the buffer opened for this element, or <code>null</code>
+	 * if this element does not have a buffer.
+	 */
+	public IBuffer getBuffer() throws CModelException;
+	/**
+	 * returns true if the associated buffer has some unsaved changes 
+	 */
+	boolean hasUnsavedChanges() throws CModelException;
+	/**
+	 * Returns whether the element is consistent with its underlying resource or buffer.
+	 * The element is consistent when opened, and is consistent if the underlying resource
+	 * or buffer has not been modified since it was last consistent.
+	 */
+	boolean isConsistent() throws CModelException;
+
+	/**
+	 * Returns whether this CFile is open.
+	 */
+	boolean isOpen();
+	
+	/**
+	 * Makes this element consistent with its underlying resource or buffer 
+	 * by updating the element's structure and properties as necessary.
+	 */
+	void makeConsistent(IProgressMonitor progress) throws CModelException;
+
+	/**
+	 * Opens this element and all parent elements that are not already open.
+	 * For translation units, a buffer is opened on the contents of the
+	 * underlying resource.
+	 */
+	public void open(IProgressMonitor progress) throws CModelException;
+	
+	/**
+	 * Saves any changes in this element's buffer to its underlying resource
+	 * via a workspace resource operation. 
+	 * <p>
+	 * The <code>force</code> parameter controls how this method deals with
+	 * cases where the workbench is not completely in sync with the local file system.
+	 */
+	public void save(IProgressMonitor progress, boolean force) throws CModelException;
+
+}
Index: model/org/eclipse/cdt/core/model/ICResource.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICResource.java,v
retrieving revision 1.2
diff -u -r1.2 ICResource.java
--- model/org/eclipse/cdt/core/model/ICResource.java	24 Feb 2003 04:48:34 -0000	1.2
+++ model/org/eclipse/cdt/core/model/ICResource.java	19 Mar 2003 20:02:39 -0000
@@ -6,5 +6,5 @@
 package org.eclipse.cdt.core.model;
 
 
-public interface ICResource extends IParent, ICElement {
+public interface ICResource extends IParent, ICElement, ICOpenable {
 }
Index: model/org/eclipse/cdt/core/model/ITranslationUnit.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ITranslationUnit.java,v
retrieving revision 1.1
diff -u -r1.1 ITranslationUnit.java
--- model/org/eclipse/cdt/core/model/ITranslationUnit.java	26 Jun 2002 20:37:15 -0000	1.1
+++ model/org/eclipse/cdt/core/model/ITranslationUnit.java	19 Mar 2003 20:02:39 -0000
@@ -4,8 +4,9 @@
  * (c) Copyright IBM Corp. 2000, 2001.
  * All Rights Reserved.
  */
+import org.eclipse.cdt.internal.core.model.IBufferFactory;
+import org.eclipse.cdt.internal.core.model.IWorkingCopy;
 import org.eclipse.core.runtime.IProgressMonitor;
-
 /**
  * Represents an entire C translation unit (<code>.c</code> source file).
  * The children are of type <code>IStructureElement</code>,
@@ -65,6 +66,26 @@
 	IUsing createUsing (String name, IProgressMonitor monitor) throws CModelException;   
 
 	/**
+	 * Finds the shared working copy for this element, given a <code>IBuffer</code> factory. 
+	 * If no working copy has been created for this element associated with this
+	 * buffer factory, returns <code>null</code>.
+	 * <p>
+	 * Users of this method must not destroy the resulting working copy. 
+	 * 
+	 * @param bufferFactory the given <code>IBuffer</code> factory
+	 * @return the found shared working copy for this element, <code>null</code> if none
+	 * @see IBufferFactory
+	 * @since 2.0
+	 */
+	IWorkingCopy findSharedWorkingCopy(IBufferFactory bufferFactory);
+
+	/**
+	 * Returns the contents of a translation unit as a char[]
+	 * @return char[]
+	 */
+	char[] getContents();
+
+	/**
 	 * Returns the smallest element within this translation unit that 
 	 * includes the given source position (that is, a method, field, etc.), or
 	 * <code>null</code> if there is no element other than the translation
@@ -100,6 +121,55 @@
 	IInclude[] getIncludes() throws CModelException;
 
 	/**
+	 * Returns a shared working copy on this element using the given factory to create
+	 * the buffer, or this element if this element is already a working copy.
+	 * This API can only answer an already existing working copy if it is based on the same
+	 * original translation unit AND was using the same buffer factory (i.e. as
+	 * defined by <code>Object#equals</code>).
+	 * <p>
+	 * The life time of a shared working copy is as follows:
+	 * <ul>
+	 * <li>The first call to <code>getSharedWorkingCopy(...)</code> creates a new working copy for this
+	 *     element</li>
+	 * <li>Subsequent calls increment an internal counter.</li>
+	 * <li>A call to <code>destroy()</code> decrements the internal counter.</li>
+	 * <li>When this counter is 0, the working copy is destroyed.
+	 * </ul>
+	 * So users of this method must destroy exactly once the working copy.
+	 * <p>
+	 * Note that the buffer factory will be used for the life time of this working copy, i.e. if the 
+	 * working copy is closed then reopened, this factory will be used.
+	 * The buffer will be automatically initialized with the original's compilation unit content
+	 * upon creation.
+	 * <p>
+	 * When the shared working copy instance is created, an ADDED ICElementDelta is reported on this
+	 * working copy.
+	 *
+	 * @param monitor a progress monitor used to report progress while opening this compilation unit
+	 *                 or <code>null</code> if no progress should be reported 
+	 * @param factory the factory that creates a buffer that is used to get the content of the working copy
+	 *                 or <code>null</code> if the internal factory should be used
+	 * @param problemRequestor a requestor which will get notified of problems detected during
+	 * 	reconciling as they are discovered. The requestor can be set to <code>null</code> indicating
+	 * 	that the client is not interested in problems.
+	 * @exception CModelException if the contents of this element can   not be
+	 * determined. Reasons include:
+	 * <ul>
+	 * <li> This C element does not exist (ELEMENT_DOES_NOT_EXIST)</li>
+	 * </ul>
+	 * @return a shared working copy on this element using the given factory to create
+	 * the buffer, or this element if this element is already a working copy
+	 * @see IBufferFactory
+	 * @see IProblemRequestor
+	 * @since 2.0
+	 */
+	
+	IWorkingCopy getSharedWorkingCopy(
+		IProgressMonitor monitor,
+		IBufferFactory factory)
+		throws CModelException;
+
+	/**
 	 * Returns the first namespace declaration in this translation unit with the given package name
 	 * This is a handle-only method. The namespace declaration may or may not exist.
 	 *
@@ -117,4 +187,21 @@
 	 *		exception occurs while accessing its corresponding resource
 	 */
 	IUsing[] getUsings() throws CModelException;
+	/**
+	 * Returns a new working copy for the Translation Unit.
+	 * @return IWorkingCopy
+	 */
+	IWorkingCopy getWorkingCopy() throws CModelException;
+
+	/**
+	 * Returns a new working copy for the Translation Unit.
+	 * @return IWorkingCopy
+	 */
+	IWorkingCopy getWorkingCopy(IProgressMonitor monitor, IBufferFactory factory) throws CModelException;
+	
+	/**
+	 * Checks if this is a working copy.
+	 * @return boolean
+	 */
+	boolean isWorkingCopy();	
 }
Index: model/org/eclipse/cdt/internal/core/model/Buffer.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/Buffer.java
diff -N model/org/eclipse/cdt/internal/core/model/Buffer.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/Buffer.java	19 Mar 2003 20:02:39 -0000
@@ -0,0 +1,443 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICOpenable;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Platform;
+
+/**
+  * @see IBuffer
+  * This class is similar to the JDT Buffer class.
+  */
+public class Buffer implements IBuffer {
+	protected IFile file;
+	protected int flags;
+	protected char[] contents;
+	protected ArrayList changeListeners;
+	protected ICOpenable owner;
+	protected int gapStart= -1;
+	protected int gapEnd= -1;
+
+	protected Object lock= new Object();
+
+	protected static final int F_HAS_UNSAVED_CHANGES= 1;
+	protected static final int F_IS_READ_ONLY= 2;
+	protected static final int F_IS_CLOSED= 4;
+	/**
+	 * Creates a new buffer on an underlying resource.
+	 */
+	protected Buffer(IFile file, ICOpenable owner, boolean readOnly) {
+		this.file = file;
+		this.owner = owner;
+		if (file == null) {
+			setReadOnly(readOnly);
+		}
+	}
+
+	/**
+	 * @see IBuffer
+	 */
+	public void addBufferChangedListener(IBufferChangedListener listener) {
+		if (this.changeListeners == null) {
+			this.changeListeners = new ArrayList(5);
+		}
+		if (!this.changeListeners.contains(listener)) {
+			this.changeListeners.add(listener);
+		}
+	}
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#append(char)
+	 */
+	public void append(char[] text) {
+		if (!isReadOnly()) {
+			if (text == null || text.length == 0) {
+				return;
+			}
+			int length = getLength();
+			moveAndResizeGap(length, text.length);
+			System.arraycopy(text, 0, this.contents, length, text.length);
+			this.gapStart += text.length;
+			this.flags |= F_HAS_UNSAVED_CHANGES;
+			notifyChanged(new BufferChangedEvent(this, length, 0, new String(text)));
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#append(java.lang.String)
+	 */
+	public void append(String text) {
+		if (text == null) {
+			return;
+		}
+		this.append(text.toCharArray());
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#close()
+	 */
+	public void close() {
+		BufferChangedEvent event = null;
+		synchronized (this.lock) {
+			if (isClosed())
+				return;
+			event = new BufferChangedEvent(this, 0, 0, null);
+			this.contents = null;
+			this.flags |= F_IS_CLOSED;
+		}
+		notifyChanged(event); // notify outside of synchronized block
+		this.changeListeners = null;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getChar(int)
+	 */
+	public char getChar(int position) {
+		synchronized (this.lock) {
+			if (position < this.gapStart) {
+				return this.contents[position];
+			}
+			int gapLength = this.gapEnd - this.gapStart;
+			return this.contents[position + gapLength];
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getCharacters()
+	 */
+	public char[] getCharacters() {
+		if (this.contents == null) return null;
+		synchronized (this.lock) {
+			if (this.gapStart < 0) {
+				return this.contents;
+			}
+			int length = this.contents.length;
+			char[] newContents = new char[length - this.gapEnd + this.gapStart];
+			System.arraycopy(this.contents, 0, newContents, 0, this.gapStart);
+			System.arraycopy(this.contents, this.gapEnd, newContents, this.gapStart, length - this.gapEnd);
+			return newContents;
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getContents()
+	 */
+	public String getContents() {
+		if (this.contents == null) return null;
+		return new String(this.getCharacters());
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getLength()
+	 */
+	public int getLength() {
+		synchronized (this.lock) {
+			int length = this.gapEnd - this.gapStart;
+			return (this.contents.length - length);
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getOwner()
+	 */
+	public ICOpenable getOwner() {
+		return this.owner;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getText(int, int)
+	 */
+	public String getText(int offset, int length) {
+		if (this.contents == null)
+			return ""; //$NON-NLS-1$
+		synchronized (this.lock) {
+			if (offset + length < this.gapStart)
+				return new String(this.contents, offset, length);
+			if (this.gapStart < offset) {
+				int gapLength = this.gapEnd - this.gapStart;
+				return new String(this.contents, offset + gapLength, length);
+			}
+			StringBuffer buf = new StringBuffer();
+			buf.append(this.contents, offset, this.gapStart - offset);
+			buf.append(this.contents, this.gapEnd, offset + length - this.gapStart);
+			return buf.toString();
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#getUnderlyingResource()
+	 */
+	public IResource getUnderlyingResource() {
+		return this.file;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#hasUnsavedChanges()
+	 */
+	public boolean hasUnsavedChanges() {
+		return (this.flags & F_HAS_UNSAVED_CHANGES) != 0;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#isClosed()
+	 */
+	public boolean isClosed() {
+		return (this.flags & F_IS_CLOSED) != 0;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#isReadOnly()
+	 */
+	public boolean isReadOnly() {
+		if (this.file == null) {
+			return (this.flags & F_IS_READ_ONLY) != 0;
+		} else {
+			return this.file.isReadOnly();
+		}
+	}
+
+	/**
+	 * Notify the listeners that this buffer has changed.
+	 * To avoid deadlock, this should not be called in a synchronized block.
+	 */
+	protected void notifyChanged(final BufferChangedEvent event) {
+		if (this.changeListeners != null) {
+			for (int i = 0, size = this.changeListeners.size(); i < size; ++i) {
+				final IBufferChangedListener listener = (IBufferChangedListener) this.changeListeners.get(i);
+				Platform.run(new ISafeRunnable() {
+					public void handleException(Throwable exception) {
+						Util.log(exception, "Exception occurred in listener of buffer change notification"); //$NON-NLS-1$
+					}
+					public void run() throws Exception {
+						listener.bufferChanged(event);
+					}
+				});
+			}
+		}
+	}
+	/**
+	 * @see IBuffer
+	 */
+	public void removeBufferChangedListener(IBufferChangedListener listener) {
+		if (this.changeListeners != null) {
+			this.changeListeners.remove(listener);
+			if (this.changeListeners.size() == 0) {
+				this.changeListeners = null;
+			}
+		}
+	}
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#replace(int, int, char)
+	 */
+	public void replace(int position, int length, char[] text) {
+		if (!isReadOnly()) {
+			int textLength = text == null ? 0 : text.length;
+			synchronized (this.lock) {
+				// move gap
+				moveAndResizeGap(position + length, textLength - length);
+
+				// overwrite
+				int min = Math.min(textLength, length);
+				if (min > 0) {
+					System.arraycopy(text, 0, this.contents, position, min);
+				}
+				if (length > textLength) {
+					// enlarge the gap
+					this.gapStart -= length - textLength;
+				} else if (textLength > length) {
+					// shrink gap
+					this.gapStart += textLength - length;
+					System.arraycopy(text, 0, this.contents, position, textLength);
+				}
+			}
+			this.flags |= F_HAS_UNSAVED_CHANGES;
+			String string = null;
+			if (textLength > 0) {
+				string = new String(text);
+			}
+			notifyChanged(new BufferChangedEvent(this, position, length, string));			
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#replace(int, int, java.lang.String)
+	 */
+	public void replace(int position, int length, String text) {
+		this.replace(position, length, text == null ? null : text.toCharArray());
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#save(org.eclipse.core.runtime.IProgressMonitor, boolean)
+	 */
+	public void save(IProgressMonitor progress, boolean force)
+		throws CModelException {
+			// determine if saving is required 
+			if (isReadOnly() || this.file == null) {
+				return;
+			}
+			synchronized (this.lock) {
+				if (!hasUnsavedChanges())
+					return;
+			
+				// use a platform operation to update the resource contents
+				try {
+					String contents = this.getContents();
+					if (contents == null) return;
+					byte[] bytes = contents.getBytes(); 
+					ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+
+					this.file.setContents(
+						stream, 
+						force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, 
+						null);
+				} 
+				catch (CoreException e) {
+					throw new CModelException(e);
+				}
+
+				// the resource no longer has unsaved changes
+				this.flags &= ~ (F_HAS_UNSAVED_CHANGES);
+			}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#setContents(char)
+	 */
+	public void setContents(char[] newContents) {
+		// allow special case for first initialization 
+		// after creation by buffer factory
+		if (this.contents == null) {
+			this.contents = newContents;
+			this.flags &= ~ (F_HAS_UNSAVED_CHANGES);
+			return;
+		}
+	
+		if (!isReadOnly()) {
+			String string = null;
+			if (newContents != null) {
+				string = new String(newContents);
+			}
+			BufferChangedEvent event = new BufferChangedEvent(this, 0, this.getLength(), string);
+			synchronized (this.lock) {
+				this.contents = newContents;
+				this.flags |= F_HAS_UNSAVED_CHANGES;
+				this.gapStart = -1;
+				this.gapEnd = -1;
+			}
+			notifyChanged(event);
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBuffer#setContents(java.lang.String)
+	 */
+	public void setContents(String newContents) {
+		this.setContents(newContents.toCharArray());
+	}
+	
+	/**
+	 * Moves the gap to location and adjust its size to the
+	 * anticipated change size. The size represents the expected 
+	 * range of the gap that will be filled after the gap has been moved.
+	 * Thus the gap is resized to actual size + the specified size and
+	 * moved to the given position.
+	 */
+	protected void moveAndResizeGap(int position, int size) {
+		char[] content = null;
+		int oldSize = this.gapEnd - this.gapStart;
+		if (size < 0) {
+			if (oldSize > 0) {
+				content = new char[this.contents.length - oldSize];
+				System.arraycopy(this.contents, 0, content, 0, this.gapStart);
+				System.arraycopy(this.contents, this.gapEnd, content, this.gapStart, content.length - this.gapStart);
+				this.contents = content;
+			}
+			this.gapStart = this.gapEnd = position;
+			return;
+		}
+		content = new char[this.contents.length + (size - oldSize)];
+		int newGapStart = position;
+		int newGapEnd = newGapStart + size;
+		if (oldSize == 0) {
+			System.arraycopy(this.contents, 0, content, 0, newGapStart);
+			System.arraycopy(this.contents, newGapStart, content, newGapEnd, content.length - newGapEnd);
+		} else
+			if (newGapStart < this.gapStart) {
+				int delta = this.gapStart - newGapStart;
+				System.arraycopy(this.contents, 0, content, 0, newGapStart);
+				System.arraycopy(this.contents, newGapStart, content, newGapEnd, delta);
+				System.arraycopy(this.contents, this.gapEnd, content, newGapEnd + delta, this.contents.length - this.gapEnd);
+			} else {
+				int delta = newGapStart - this.gapStart;
+				System.arraycopy(this.contents, 0, content, 0, this.gapStart);
+				System.arraycopy(this.contents, this.gapEnd, content, this.gapStart, delta);
+				System.arraycopy(this.contents, this.gapEnd + delta, content, newGapEnd, content.length - newGapEnd);
+			}
+		this.contents = content;
+		this.gapStart = newGapStart;
+		this.gapEnd = newGapEnd;
+	}
+	
+	/**
+	 * Sets this <code>Buffer</code> to be read only.
+	 */
+	protected void setReadOnly(boolean readOnly) {
+		if (readOnly) {
+			this.flags |= F_IS_READ_ONLY;
+		} else {
+			this.flags &= ~(F_IS_READ_ONLY);
+		}
+	}
+	
+	public String toString() {
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("Owner: " + ((CElement)this.owner).toString()); //$NON-NLS-1$
+		buffer.append("\nHas unsaved changes: " + this.hasUnsavedChanges()); //$NON-NLS-1$
+		buffer.append("\nIs readonly: " + this.isReadOnly()); //$NON-NLS-1$
+		buffer.append("\nIs closed: " + this.isClosed()); //$NON-NLS-1$
+		buffer.append("\nContents:\n"); //$NON-NLS-1$
+		char[] contents = this.getCharacters();
+		if (contents == null) {
+			buffer.append("<null>"); //$NON-NLS-1$
+		} else {
+			int length = contents.length;
+			for (int i = 0; i < length; i++) {
+				char car = contents[i];
+				switch (car) {
+					case '\n': 
+						buffer.append("\\n\n"); //$NON-NLS-1$
+						break;
+					case '\r':
+						if (i < length-1 && this.contents[i+1] == '\n') {
+							buffer.append("\\r\\n\n"); //$NON-NLS-1$
+							i++;
+						} else {
+							buffer.append("\\r\n"); //$NON-NLS-1$
+						}
+						break;
+					default:
+						buffer.append(car);
+						break;
+				}
+			}
+		}
+		return buffer.toString();
+	}
+}
Index: model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java
diff -N model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java	19 Mar 2003 20:02:39 -0000
@@ -0,0 +1,111 @@
+package org.eclipse.cdt.internal.core.model;
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import java.util.EventObject;
+
+/**
+ * A   buffer changed event describes how a buffer has changed. These events are
+ * used in <code>IBufferChangedListener</code> notifications.
+ * <p>
+ * For text insertions, <code>getOffset</code> is the offset
+ * of the first inserted character, <code>getText</code> is the
+ * inserted text, and <code>getLength</code> is 0.
+ * </p>
+ * <p>
+ * For text removals, <code>getOffset</code> is the offset
+ * of the first removed character, <code>getText</code> is <code>null</code>,
+ * and <code>getLength</code> is the length of the text that was removed.
+ * </p>
+ * <p>
+ * For replacements (including <code>IBuffer.setContents</code>), 
+ * <code>getOffset</code> is the offset
+ * of the first replaced character, <code>getText</code> is the replacement
+ * text, and <code>getLength</code> is the length of the original text
+ * that was replaced.
+ * </p>
+ * <p>
+ * When a buffer is closed, <code>getOffset</code> is 0, <code>getLength</code>
+ * is 0, and <code>getText</code> is <code>null</code>.
+ * </p>
+ * <p>
+ * This class is not intended to be instantiated or subclassed by clients.
+ * Instances of this class are automatically created by the C model.
+ * </p>
+ *
+ * @see IBuffer
+ * This class is similar to the JDT BufferChangedEvent class. 
+ */
+
+public class BufferChangedEvent extends EventObject {
+	/**
+	 * The length of text that has been modified in the buffer.
+	 */
+	private int length;
+
+	/**
+	 * The offset into the buffer where the modification took place.
+	 */
+	private int offset;
+
+	/**
+	 * The text that was modified.
+	 */
+	private String text;
+
+	/**
+	 * Creates a new buffer changed event indicating that the given buffer has changed.
+	 */
+	public BufferChangedEvent(IBuffer buffer, int offset, int length, String text) {
+		super(buffer);
+		this.offset = offset;
+		this.length = length;
+		this.text = text;
+	}
+	/**
+	 * Returns the buffer which has changed.
+	 *
+	 * @return the buffer affected by the change
+	 */
+	public IBuffer getBuffer() {
+		return (IBuffer) source;
+	}
+	/**
+	 * Returns the length of text removed or replaced in the buffer, or
+	 * 0 if text has been inserted into the buffer.
+	 *
+	 * @return the length of the original text fragment modified by the 
+	 *   buffer change (<code> 0 </code> in case of insertion).
+	 */
+	public int getLength() {
+		return this.length;
+	}
+	/**
+	 * Returns the index of the first character inserted, removed, or replaced
+	 * in the buffer.
+	 *
+	 * @return the source offset of the textual manipulation in the buffer
+	 */
+	public int getOffset() {
+		return this.offset;
+	}
+	/**
+	 * Returns the text that was inserted, the replacement text,
+	 * or <code>null</code> if text has been removed.
+	 *
+	 * @return the text corresponding to the buffer change (<code> null </code>
+	 *   in case of deletion).
+	 */
+	public String getText() {
+		return this.text;
+	}
+
+}
Index: model/org/eclipse/cdt/internal/core/model/BufferManager.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/BufferManager.java
diff -N model/org/eclipse/cdt/internal/core/model/BufferManager.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/BufferManager.java	19 Mar 2003 20:02:39 -0000
@@ -0,0 +1,156 @@
+package org.eclipse.cdt.internal.core.model;
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+
+import java.util.Enumeration;
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ICOpenable;
+import org.eclipse.cdt.internal.core.util.LRUCache;
+import org.eclipse.cdt.internal.core.util.OverflowingLRUCache;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+/**
+ * The buffer manager manages the set of open buffers.
+ * It implements an LRU cache of buffers.
+ * 
+ * This class is similar to the JDT BufferManager class
+ */
+
+public class BufferManager implements IBufferFactory {
+
+	/**
+	 * An LRU cache of <code>IBuffers</code>.
+	 */
+	public class BufferCache extends OverflowingLRUCache {
+		/**
+		 * Constructs a new buffer cache of the given size.
+		 */
+		public BufferCache(int size) {
+			super(size);
+		}
+		/**
+		 * Constructs a new buffer cache of the given size.
+		 */
+		public BufferCache(int size, int overflow) {
+			super(size, overflow);
+		}
+		/**
+		 * Returns true if the buffer is successfully closed and
+		 * removed from the cache, otherwise false.
+		 *
+		 * <p>NOTE: this triggers an external removal of this buffer
+		 * by closing the buffer.
+		 */
+		protected boolean close(LRUCacheEntry entry) {
+			IBuffer buffer= (IBuffer) entry._fValue;
+			if (buffer.hasUnsavedChanges()) {
+				return false;
+			} else {
+				buffer.close();
+				return true;
+			}
+		}
+		/**
+		 * Returns a new instance of the reciever.
+		 */
+		protected LRUCache newInstance(int size, int overflow) {
+			return new BufferCache(size, overflow);
+		}
+	}
+
+	protected static BufferManager DEFAULT_BUFFER_MANAGER;
+
+	/**
+	 * LRU cache of buffers. The key and value for an entry
+	 * in the table is the identical buffer.
+	 */
+	protected OverflowingLRUCache openBuffers = new BufferCache(60);
+
+	/**
+	 * Creates a new buffer manager.
+	 */
+	public BufferManager() {
+	}
+	/**
+	 * Adds a buffer to the table of open buffers.
+	 */
+	protected void addBuffer(IBuffer buffer) {
+		openBuffers.put(buffer.getOwner(), buffer);
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IBufferFactory#createBuffer(org.eclipse.cdt.core.model.IOpenable)
+	 */
+	public IBuffer createBuffer(ICOpenable owner) {
+		ICElement element = (ICElement)owner;
+		try{
+			IResource resource = element.getResource();
+			return 
+				new Buffer(
+					resource instanceof IFile ? (IFile)resource : null, 
+					owner, 
+					element.isReadOnly());
+		}
+		catch (CModelException e) {
+			return null;
+		}
+	}
+	
+	/**
+	 * Returns the open buffer associated with the given owner,
+	 * or <code>null</code> if the owner does not have an open
+	 * buffer associated with it.
+	 */
+	public IBuffer getBuffer(ICOpenable owner) {
+		return (IBuffer)openBuffers.get(owner);
+	}
+	/**
+	 * Returns the default buffer factory.
+	 */
+	public IBufferFactory getDefaultBufferFactory() {
+		return this;
+	}
+	/**
+	 * Returns the default buffer manager.
+	 */
+	public synchronized static BufferManager getDefaultBufferManager() {
+		if (DEFAULT_BUFFER_MANAGER == null) {
+			DEFAULT_BUFFER_MANAGER = new BufferManager();
+		}
+		return DEFAULT_BUFFER_MANAGER;
+	}
+	/**
+	 * Returns an enumeration of all open buffers.
+	 * <p> 
+	 * The <code>Enumeration</code> answered is thread safe.
+	 *
+	 * @see OverflowingLRUCache
+	 * @return Enumeration of IBuffer
+	 */
+	public Enumeration getOpenBuffers() {
+		synchronized (openBuffers) {
+			openBuffers.shrink();
+			return openBuffers.elements();
+		}
+	}
+	
+	
+	/**
+	 * Removes a buffer from the table of open buffers.
+	 */
+	protected void removeBuffer(IBuffer buffer) {
+		openBuffers.remove(buffer.getOwner());
+	}
+
+}
Index: model/org/eclipse/cdt/internal/core/model/CElement.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElement.java,v
retrieving revision 1.3
diff -u -r1.3 CElement.java
--- model/org/eclipse/cdt/internal/core/model/CElement.java	4 Mar 2003 18:25:40 -0000	1.3
+++ model/org/eclipse/cdt/internal/core/model/CElement.java	19 Mar 2003 20:02:39 -0000
@@ -4,15 +4,17 @@
  * All Rights Reserved.
  */
 
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ICModelStatusConstants;
+import org.eclipse.cdt.core.model.ICOpenable;
+import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.core.model.ICRoot;
+import org.eclipse.cdt.core.model.IParent;
 import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.PlatformObject;
-import org.eclipse.core.runtime.IPath;
-
-import org.eclipse.cdt.core.model.ICElement;
-import org.eclipse.cdt.core.model.ICRoot;
-import org.eclipse.cdt.core.model.ICProject;
-import org.eclipse.cdt.core.model.CModelException;
 
 public abstract class CElement extends PlatformObject implements ICElement {
 	
@@ -20,8 +22,6 @@
 	
 	protected ICElement fParent;
 
-	protected CElementInfo fCElementInfo;
-	
 	protected String fName;
 
 	protected int fStartPos;
@@ -37,7 +37,6 @@
 		fParent= parent;
 		fName= name;
 		fType= type;
-		fCElementInfo = null;
 	}
 	
 	// setters
@@ -89,7 +88,14 @@
 	}
 	
 	public boolean isReadOnly () {
-		return getElementInfo().isReadOnly();
+		try {
+			IResource r = getUnderlyingResource();
+			if (r != null) {
+				return r.isReadOnly();
+			}			
+		} catch (CModelException e) {
+		}
+		return false;
 	}
 
 	public boolean isStructureKnown() throws CModelException {
@@ -206,10 +212,22 @@
 	}
 	
 	public CElementInfo getElementInfo () {
-		if (fCElementInfo == null) {
-			fCElementInfo = createElementInfo();
+		try {
+			CModelManager manager;
+			synchronized(manager = CModelManager.getDefault()){
+				Object info = manager.getInfo(this);
+				if (info == null) {
+					openHierarchy();
+					info= manager.getInfo(this);
+					if (info == null) {
+						throw newNotPresentException();
+					}
+				}
+				return (CElementInfo)info;
+			}
+		} catch(CModelException e) {
+			return null;
 		}
-		return fCElementInfo;
 	}
 
 	public String toString() {
@@ -264,4 +282,116 @@
 	protected void runOperation(CModelOperation operation, IProgressMonitor monitor) throws CModelException {
 		CModelManager.getDefault().runOperation(operation, monitor);
 	}
+	
+	/**
+	 * Close the C Element
+	 * @throws CModelException
+	 */
+	public void close() throws CModelException {
+		Object info = CModelManager.getDefault().peekAtInfo(this);
+		if (info != null) {
+			if (this instanceof IParent) {
+				ICElement[] children = ((CElementInfo) info).getChildren();
+				for (int i = 0, size = children.length; i < size; ++i) {
+					CElement child = (CElement) children[i];
+					child.close();
+				}
+			}
+			closing(info);
+			CModelManager.getDefault().removeInfo(this);
+		}
+	}
+	/**
+	 * This element is being closed.  Do any necessary cleanup.
+	 */
+	protected void closing(Object info) throws CModelException {
+	}
+
+	/**
+	 * This element has just been opened.  Do any necessary setup.
+	 */
+	protected void opening(Object info) {
+	}
+
+	/**
+	 * Return the first instance of IOpenable in the parent
+	 * hierarchy of this element.
+	 *
+	 * <p>Subclasses that are not IOpenable's must override this method.
+	 */
+	public ICOpenable getOpenableParent() {
+		
+		return (ICOpenable)fParent;
+	}
+
+
+	/**
+	 * Opens this element and all parents that are not already open.
+	 *
+	 * @exception CModelException this element is not present or accessible
+	 */
+	protected void openHierarchy() throws CModelException {
+		if (this instanceof ICOpenable) {
+			((CResource) this).openWhenClosed(null);
+		} else {
+			CResource openableParent = (CResource)getOpenableParent();
+			if (openableParent != null) {
+				CElementInfo openableParentInfo = (CElementInfo) CModelManager.getDefault().getInfo((ICElement) openableParent);
+				if (openableParentInfo == null) {
+					openableParent.openWhenClosed(null);
+				} else {
+					CModelManager.getDefault().putInfo( this, createElementInfo());
+				}
+			}
+		}
+	}	
+	/**
+	 * Returns true if this element is an ancestor of the given element,
+	 * otherwise false.
+	 */
+	protected boolean isAncestorOf(ICElement e) {
+		ICElement parent= e.getParent();
+		while (parent != null && !parent.equals(this)) {
+			parent= parent.getParent();
+		}
+		return parent != null;
+	}
+	
+	/**
+	 * Creates and returns and not present exception for this element.
+	 */
+	protected CModelException newNotPresentException() {
+		return new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
+	}
+	/**
+	 * Removes all cached info from the C Model, including all children,
+	 * but does not close this element.
+	 */
+	protected void removeInfo() {
+		Object info = CModelManager.getDefault().peekAtInfo(this);
+		if (info != null) {
+			if (this instanceof IParent) {
+				ICElement[] children = ((CElementInfo)info).getChildren();
+				for (int i = 0, size = children.length; i < size; ++i) {
+					CElement child = (CElement) children[i];
+					child.removeInfo();
+				}
+			}
+			CModelManager.getDefault().removeInfo(this);
+		}
+	}
+	
+	/**
+	 * Returns the hash code for this Java element. By default,
+	 * the hash code for an element is a combination of its name
+	 * and parent's hash code. Elements with other requirements must
+	 * override this method.
+	 */
+	// CHECKPOINT: making not equal objects seem equal
+	// What elements should override this?
+	public int hashCode() {
+		if (fParent == null) return super.hashCode();
+		return Util.combineHashCodes(fName.hashCode(), fParent.hashCode());
+	}
+	
 }
Index: model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java
diff -N model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java	19 Mar 2003 20:02:39 -0000
@@ -0,0 +1,37 @@
+package org.eclipse.cdt.internal.core.model;
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import org.eclipse.cdt.core.model.ICElement;
+
+/**
+ * A C element delta biulder creates a C element delta on a C element between
+ * the version of the C element at the time the comparator was created and the
+ * current version of the C element.
+ *
+ * It performs this operation by locally caching the contents of 
+ * the C element when it is created. When the method buildDeltas() is called, it
+ * creates a delta over the cached contents and the new contents.
+ * 
+ * This class is similar to the JDT CElementDeltaBuilder class.
+ */
+
+public class CElementDeltaBuilder {
+	
+	CElementDelta delta;
+	
+	public CElementDeltaBuilder(ICElement cElement) {
+
+	}
+
+	public void buildDeltas() {
+	}
+}
\ No newline at end of file
Index: model/org/eclipse/cdt/internal/core/model/CElementInfo.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementInfo.java,v
retrieving revision 1.2
diff -u -r1.2 CElementInfo.java
--- model/org/eclipse/cdt/internal/core/model/CElementInfo.java	18 Nov 2002 15:47:31 -0000	1.2
+++ model/org/eclipse/cdt/internal/core/model/CElementInfo.java	19 Mar 2003 20:02:39 -0000
@@ -95,20 +95,6 @@
 	}
 
 	/**
-	 * @see ICElement.isStructureKnown()
-	 */
-	public boolean isReadOnly () {
-		try {
-			IResource r = getElement().getUnderlyingResource();
-			if (r != null) {
-				return r.isReadOnly();
-			}
-		} catch (CModelException e) {
-		}
-		return true;
-	}
-
-	/**
 	 * Returns an array with all the same elements as the specified array except for
 	 * the element to remove. Assumes that the deletion is contained in the array.
 	 */
Index: model/org/eclipse/cdt/internal/core/model/CFile.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFile.java,v
retrieving revision 1.1
diff -u -r1.1 CFile.java
--- model/org/eclipse/cdt/internal/core/model/CFile.java	26 Jun 2002 20:37:14 -0000	1.1
+++ model/org/eclipse/cdt/internal/core/model/CFile.java	19 Mar 2003 20:02:40 -0000
@@ -4,15 +4,13 @@
  * All Rights Reserved.
  */
 
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ICFile;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.IPath;
-//import org.eclipse.core.runtime.CoreException;
-
-import org.eclipse.cdt.core.model.CModelException;
-import org.eclipse.cdt.core.model.ICElement;
-import org.eclipse.cdt.core.model.ICFile;
 
 public class CFile extends CResource implements ICFile {
 	
Index: model/org/eclipse/cdt/internal/core/model/CFolder.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFolder.java,v
retrieving revision 1.1
diff -u -r1.1 CFolder.java
--- model/org/eclipse/cdt/internal/core/model/CFolder.java	26 Jun 2002 20:37:14 -0000	1.1
+++ model/org/eclipse/cdt/internal/core/model/CFolder.java	19 Mar 2003 20:02:40 -0000
@@ -5,11 +5,11 @@
  * All Rights Reserved.
  */
  
-import org.eclipse.core.resources.IFolder;
-
+import org.eclipse.cdt.core.model.CModelException;
 import org.eclipse.cdt.core.model.ICElement;
 import org.eclipse.cdt.core.model.ICFolder;
-import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.runtime.IProgressMonitor;
 
 public class CFolder extends CResource implements ICFolder {
 
@@ -29,4 +29,10 @@
 	protected CElementInfo createElementInfo () {
 		return new CFolderInfo(this);
 	}
+	
+	// CHECKPOINT: folders will return the hash code of their path
+	public int hashCode() {
+		return getPath().hashCode();
+	}
+	
 }
Index: model/org/eclipse/cdt/internal/core/model/CModelCache.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/CModelCache.java
diff -N model/org/eclipse/cdt/internal/core/model/CModelCache.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/CModelCache.java	19 Mar 2003 20:02:40 -0000
@@ -0,0 +1,138 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.internal.core.util.OverflowingLRUCache;
+
+
+/**
+ * The cache of C elements to their respective info.
+ * 
+ * This class is similar to the JDT CModelCache class.
+ */
+public class CModelCache {
+	public static final int PROJ_CACHE_SIZE = 50;	
+	public static final int FOLDER_CACHE_SIZE = 500;	
+	public static final int FILE_CACHE_SIZE = 2000;
+	public static final int CHILDREN_CACHE_SIZE = FILE_CACHE_SIZE * 20;
+	
+	/**
+	 * Cache of open projects and roots.
+	 */
+	protected Map projectAndRootCache;
+	
+	/**
+	 * Cache of open containers
+	 */
+	protected Map folderCache;
+
+	/**
+	 * Cache of open translation unit files
+	 */
+	protected OverflowingLRUCache fileCache;
+
+	/**
+	 * Cache of children of C elements
+	 */
+	protected Map childrenCache;
+	
+public CModelCache() {
+	this.projectAndRootCache = new HashMap(PROJ_CACHE_SIZE);
+	this.folderCache = new HashMap(FOLDER_CACHE_SIZE);	
+	this.fileCache = new ElementCache(FILE_CACHE_SIZE);
+	this.childrenCache = new HashMap(CHILDREN_CACHE_SIZE); // average 20 children per openable
+}
+
+public double openableFillingRatio() {
+	return this.fileCache.fillingRatio();
+}
+	
+/**
+ *  Returns the info for the element.
+ */
+public Object getInfo(ICElement element) {
+	switch (element.getElementType()) {
+		case ICElement.C_PROJECT:
+		case ICElement.C_ROOT:
+			return this.projectAndRootCache.get(element);
+		case ICElement.C_FOLDER:
+			return this.folderCache.get(element);		
+		case ICElement.C_FILE:
+			return this.fileCache.get(element);
+		default:
+			return this.childrenCache.get(element);
+	}
+}
+
+/**
+ *  Returns the info for this element without
+ *  disturbing the cache ordering.
+ */
+protected Object peekAtInfo(ICElement element) {
+	switch (element.getElementType()) {
+		case ICElement.C_PROJECT:
+		case ICElement.C_ROOT:
+			return this.projectAndRootCache.get(element);
+		case ICElement.C_FOLDER:
+			return this.folderCache.get(element);
+		case ICElement.C_FILE:
+			return this.fileCache.peek(element);
+		default:
+			return this.childrenCache.get(element);
+	}
+}
+
+/**
+ * Remember the info for the element.
+ */
+protected void putInfo(ICElement element, Object info) {
+	switch (element.getElementType()) {
+		case ICElement.C_PROJECT:
+		case ICElement.C_ROOT:
+			this.projectAndRootCache.put(element, info);
+			break;
+		case ICElement.C_FOLDER:
+			this.folderCache.put(element, info);
+			break;
+		case ICElement.C_FILE:
+			this.fileCache.put(element, info);
+			break;
+		default:
+			this.childrenCache.put(element, info);
+	}
+}
+/**
+ * Removes the info of the element from the cache.
+ */
+protected void removeInfo(ICElement element) {
+	switch (element.getElementType()) {
+		case ICElement.C_PROJECT:
+		case ICElement.C_ROOT:
+			this.projectAndRootCache.remove(element);
+			break;
+		case ICElement.C_FOLDER:
+			this.folderCache.remove(element);
+			break;
+		case ICElement.C_FILE:
+			this.fileCache.remove(element);
+			break;
+		default:
+			this.childrenCache.remove(element);
+	}
+}
+
+}
Index: model/org/eclipse/cdt/internal/core/model/CModelManager.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java,v
retrieving revision 1.15
diff -u -r1.15 CModelManager.java
--- model/org/eclipse/cdt/internal/core/model/CModelManager.java	28 Feb 2003 21:29:38 -0000	1.15
+++ model/org/eclipse/cdt/internal/core/model/CModelManager.java	19 Mar 2003 20:02:41 -0000
@@ -53,7 +53,7 @@
 	//private static HashMap fParsers = new HashMap();
 
 	/**
-	 * Used to convert <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s.
+	 * Used to convert <code>IResourceDelta</code>s into <code>ICElementDelta</code>s.
 	 */
 	protected DeltaProcessor fDeltaProcessor= new DeltaProcessor();
 
@@ -73,6 +73,25 @@
 	 */
 	protected ArrayList fElementChangedListeners= new ArrayList();
 
+	/**
+	 * A map from ITranslationUnit to IWorkingCopy of the shared working copies.
+	 */
+	public Map sharedWorkingCopies = new HashMap();
+	/**
+	 * Set of elements which are out of sync with their buffers.
+	 */
+	protected Map elementsOutOfSynchWithBuffers = new HashMap(11);
+
+	/**
+	 * Infos cache.
+	 */
+	protected CModelCache cache = new CModelCache();
+
+	/**
+	 * This is a cache of the projects before any project addition/deletion has started.
+	 */
+	public ICProject[] cProjectsCache;
+
 	public static final String [] sourceExtensions = {"c", "cxx", "cc", "C", "cpp"};
 
 	public static final String [] headerExtensions = {"h", "hh", "hpp"};
@@ -658,11 +677,14 @@
 
 				case IResourceChangeEvent.PRE_AUTO_BUILD :
 					// No need now.
+					if(delta != null) {
+						this.checkProjectsBeingAddedOrRemoved(delta);
+					}										
 				break;
 
 				case IResourceChangeEvent.POST_CHANGE :
-					if (delta != null) {
-						try {
+					try {
+						if (delta != null) {
 							ICElementDelta[] translatedDeltas = fDeltaProcessor.processResourceDelta(delta);
 							if (translatedDeltas.length > 0) {
 								for (int i= 0; i < translatedDeltas.length; i++) {
@@ -670,10 +692,10 @@
 								}
 							}
 							fire();
-						} catch (Exception e) {
-							e.printStackTrace();
 						}
-					}
+					} catch (Exception e) {
+						e.printStackTrace();
+					}					
 				break;
 			}
 		}
@@ -795,4 +817,62 @@
 			} // else deltas are fired while processing the resource delta
 		}
 	}
+	
+	/**
+	 * Process the given delta and look for projects being added, opened,
+	 * or closed
+	 */
+	public void checkProjectsBeingAddedOrRemoved(IResourceDelta delta) {
+		IResource resource = delta.getResource();
+		switch (resource.getType()) {
+			case IResource.ROOT :
+			if (this.cProjectsCache == null) {
+				this.cProjectsCache = this.getCRoot().getCProjects();
+			}
+				
+			IResourceDelta[] children = delta.getAffectedChildren();
+			for (int i = 0, length = children.length; i < length; i++) {
+				this.checkProjectsBeingAddedOrRemoved(children[i]);
+			}			
+			break;
+		case IResource.PROJECT :
+			// TO BE COMPLETED ...
+			break;
+		}
+	}
+	/** 
+	 * Returns the set of elements which are out of synch with their buffers.
+	 */
+	protected Map getElementsOutOfSynchWithBuffers() {
+		return this.elementsOutOfSynchWithBuffers;
+	}
+	
+	/**
+	 *  Returns the info for the element.
+	 */
+	public Object getInfo(ICElement element) {
+		return this.cache.getInfo(element);
+	}
+	/**
+	 *  Returns the info for this element without
+	 *  disturbing the cache ordering.
+	 */
+	protected Object peekAtInfo(ICElement element) {
+		return this.cache.peekAtInfo(element);
+	}
+
+	/**
+	 * Puts the info for a C Model Element
+	 */
+	protected void putInfo(ICElement element, Object info) {
+		this.cache.putInfo(element, info);
+	}
+	
+	/** 
+	 * Removes the info of this model element.
+	 */
+	protected void removeInfo(ICElement element) {
+		this.cache.removeInfo(element);
+	}
+	
 }
Index: model/org/eclipse/cdt/internal/core/model/CProject.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CProject.java,v
retrieving revision 1.3
diff -u -r1.3 CProject.java
--- model/org/eclipse/cdt/internal/core/model/CProject.java	18 Nov 2002 15:48:23 -0000	1.3
+++ model/org/eclipse/cdt/internal/core/model/CProject.java	19 Mar 2003 20:02:41 -0000
@@ -77,4 +77,10 @@
 	protected CElementInfo createElementInfo() {
 		return new CProjectInfo(this);
 	}
+	
+	// CHECKPOINT: CProjects will return the hash code of their underlying IProject
+	public int hashCode() {
+		return getProject().hashCode();
+	}
+
 }
Index: model/org/eclipse/cdt/internal/core/model/CResource.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CResource.java,v
retrieving revision 1.3
diff -u -r1.3 CResource.java
--- model/org/eclipse/cdt/internal/core/model/CResource.java	18 Nov 2002 15:48:44 -0000	1.3
+++ model/org/eclipse/cdt/internal/core/model/CResource.java	19 Mar 2003 20:02:41 -0000
@@ -5,13 +5,19 @@
  * All Rights Reserved.
  */
  
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ICModelStatusConstants;
+import org.eclipse.cdt.core.model.ICResource;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.IPath;
-
-import org.eclipse.cdt.core.model.*;
-import org.eclipse.cdt.core.model.ICElement;
-import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.core.runtime.IProgressMonitor;
 
 public abstract class CResource extends Parent implements ICResource {
 	
@@ -38,4 +44,246 @@
 	}
 	
 	protected abstract CElementInfo createElementInfo ();
+	/**
+	 * The buffer associated with this element has changed. Registers
+	 * this element as being out of synch with its buffer's contents.
+	 * If the buffer has been closed, this element is set as NOT out of
+	 * synch with the contents.
+	 *
+	 * @see IBufferChangedListener
+	 */
+	public void bufferChanged(BufferChangedEvent event) {
+		if (event.getBuffer().isClosed()) {
+			CModelManager.getDefault().getElementsOutOfSynchWithBuffers().remove(this);
+			getBufferManager().removeBuffer(event.getBuffer());
+		} else {
+			CModelManager.getDefault().getElementsOutOfSynchWithBuffers().put(this, this);
+		}
+	}	
+	/**
+	 * Updates the info objects for this element and all of its children by
+	 * removing the current infos, generating new infos, and then placing
+	 * the new infos into the C Model cache tables.
+	 */
+	protected void buildStructure(CResourceInfo info, IProgressMonitor monitor) throws CModelException {
+
+		if (monitor != null && monitor.isCanceled()) return;
+	
+		// remove existing (old) infos
+		removeInfo();
+		HashMap newElements = new HashMap(11);
+		info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
+		CModelManager.getDefault().getElementsOutOfSynchWithBuffers().remove(this);
+		for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
+			ICElement key = (ICElement) iter.next();
+			Object value = newElements.get(key);
+			CModelManager.getDefault().putInfo(key, value);
+		}
+		
+		// add the info for this at the end, to ensure that a getInfo cannot reply null in case the LRU cache needs
+		// to be flushed. Might lead to performance issues.
+		CModelManager.getDefault().putInfo(this, info);	
+	}
+
+	/**
+	 * Close the buffer associated with this element, if any.
+	 */
+	protected void closeBuffer(CFileInfo info) {
+		if (!hasBuffer()) return; // nothing to do
+		IBuffer buffer = null;
+		buffer = getBufferManager().getBuffer(this);
+		if (buffer != null) {
+			buffer.close();
+			buffer.removeBufferChangedListener(this);
+		}
+	}
+	/**
+	 * Derived classes may override.
+	 */
+	protected boolean generateInfos(CResourceInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws CModelException{
+		return false;
+	}
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#getBuffer()
+	 */
+	public IBuffer getBuffer() throws CModelException {
+		if (hasBuffer()) {
+			// ensure element is open
+			if (!isOpen()) {
+				getElementInfo();
+			}
+			IBuffer buffer = getBufferManager().getBuffer(this);
+			if (buffer == null) {
+				// try to (re)open a buffer
+				buffer = openBuffer(null);
+			}
+			return buffer;
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Answers the buffer factory to use for creating new buffers
+	 */
+	public IBufferFactory getBufferFactory(){
+		return getBufferManager().getDefaultBufferFactory();
+	}
+
+	/**
+	 * Returns the buffer manager for this element.
+	 */
+	protected BufferManager getBufferManager() {
+		return BufferManager.getDefaultBufferManager();
+	}
+	
+	/**
+	 * Returns true if this element may have an associated source buffer,
+	 * otherwise false. Subclasses must override as required.
+	 */
+	protected boolean hasBuffer() {
+		return false;
+	}
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#hasUnsavedChanges()
+	 */
+	public boolean hasUnsavedChanges() throws CModelException{
+	
+		if (isReadOnly() || !isOpen()) {
+			return false;
+		}
+		IBuffer buf = this.getBuffer();
+		if (buf != null && buf.hasUnsavedChanges()) {
+			return true;
+		}
+		// for roots and projects must check open buffers
+		// to see if they have an child with unsaved changes
+		if (fType == C_ROOT ||
+			fType == C_PROJECT) {
+			Enumeration openBuffers= getBufferManager().getOpenBuffers();
+			while (openBuffers.hasMoreElements()) {
+				IBuffer buffer= (IBuffer)openBuffers.nextElement();
+				if (buffer.hasUnsavedChanges()) {
+					ICElement owner= (ICElement)buffer.getOwner();
+					if (isAncestorOf(owner)) {
+						return true;
+					}
+				}
+			}
+		}
+	
+		return false;
+	}
+	/**
+	 * Subclasses must override as required.
+	 * 
+	 * @see org.eclipse.cdt.core.model.ICOpenable#isConsistent()
+	 */
+	public boolean isConsistent() throws CModelException {
+		return true;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#isOpen()
+	 */	
+	public boolean isOpen() {
+		synchronized(CModelManager.getDefault()){
+			return CModelManager.getDefault().getInfo(this) != null;
+		}
+	}
+
+	/**
+	 * Returns true if this represents a source element.
+	 * Openable source elements have an associated buffer created
+	 * when they are opened.
+	 */
+	protected boolean isSourceElement() {
+		return false;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#makeConsistent(IProgressMonitor)
+	 */
+	public void makeConsistent(IProgressMonitor pm) throws CModelException {
+		if (!isConsistent()) {
+			buildStructure((CFileInfo)getElementInfo(), pm);
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#open(IProgressMonitor)
+	 */
+	public void open(IProgressMonitor pm) throws CModelException {
+		if (!isOpen()) {
+			this.openWhenClosed(pm);
+		}
+	}
+
+	/**
+	 * Opens a buffer on the contents of this element, and returns
+	 * the buffer, or returns <code>null</code> if opening fails.
+	 * By default, do nothing - subclasses that have buffers
+	 * must override as required.
+	 */
+	protected IBuffer openBuffer(IProgressMonitor pm) throws CModelException {
+		return null;
+	}
+
+	/**
+	 * 	Open the parent element if necessary
+	 * 
+	 */
+	protected void openParent(IProgressMonitor pm) throws CModelException {
+
+		CResource openableParent = (CResource)getOpenableParent();
+		if (openableParent != null) {
+			if (!openableParent.isOpen()){
+				openableParent.openWhenClosed(pm);
+			}
+		}
+	}
+
+	/**
+	 * Open a <code>IOpenable</code> that is known to be closed (no check for
+	 * <code>isOpen()</code>).
+	 */
+	protected void openWhenClosed(IProgressMonitor pm) throws CModelException {
+		try {
+			
+			// 1) Parent must be open - open the parent if necessary
+			openParent(pm);
+
+			// 2) create the new element info and open a buffer if needed
+			CResourceInfo info = (CResourceInfo) createElementInfo();
+			IResource resource = getResource();
+			if (resource != null && isSourceElement()) {
+				this.openBuffer(pm);
+			} 
+
+			// 3) build the structure of the openable
+			buildStructure(info, pm);
+		
+			// if any problems occuring openning the element, ensure that it's info
+			// does not remain in the cache	(some elements, pre-cache their info
+			// as they are being opened).
+		} catch (CModelException e) {
+			CModelManager.getDefault().removeInfo(this);
+			throw e;
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#save(IProgressMonitor, boolean)
+	 */
+	public void save(IProgressMonitor pm, boolean force) throws CModelException {
+		if (isReadOnly() || this.getResource().isReadOnly()) {
+			throw new CModelException(new CModelStatus(ICModelStatusConstants.READ_ONLY, this));
+		}
+		IBuffer buf = getBuffer();
+		if (buf != null) { 
+			buf.save(pm, force);
+			this.makeConsistent(pm); // update the element info of this element
+		}
+	}
+		
 }
Index: model/org/eclipse/cdt/internal/core/model/CRoot.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CRoot.java,v
retrieving revision 1.3
diff -u -r1.3 CRoot.java
--- model/org/eclipse/cdt/internal/core/model/CRoot.java	26 Jul 2002 19:15:12 -0000	1.3
+++ model/org/eclipse/cdt/internal/core/model/CRoot.java	19 Mar 2003 20:02:41 -0000
@@ -5,6 +5,8 @@
  * All Rights Reserved.
  */
  
+import java.util.ArrayList;
+
 import org.eclipse.cdt.core.model.CModelException;
 import org.eclipse.cdt.core.model.ICElement;
 import org.eclipse.cdt.core.model.ICProject;
@@ -29,10 +31,10 @@
 	}
 
 	public ICProject[] getCProjects() {
-		ICElement[] e = getChildren();
-		ICProject[] p = new ICProject[e.length];
-		System.arraycopy(e, 0, p, 0, e.length);
-		return p;
+		ArrayList list = getChildrenOfType(C_PROJECT);
+		ICProject[] array= new ICProject[list.size()];
+		list.toArray(array);
+		return array;
 	}
 
 	public IWorkspace getWorkspace() {
@@ -109,4 +111,10 @@
 	protected CElementInfo createElementInfo () {
 		return new CRootInfo(this);
 	}
+
+	// CHECKPOINT: Roots will return the hashcode of their resource
+	public int hashCode() {
+		return resource.hashCode();
+	}
+	
 }
Index: model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java
diff -N model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java	19 Mar 2003 20:02:41 -0000
@@ -0,0 +1,145 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ICModelStatus;
+import org.eclipse.cdt.core.model.ICModelStatusConstants;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.core.resources.IResource;
+
+/**
+ * Commits the contents of a working copy translation unit to its original
+ * element and resource, bringing the C Model up-to-date with the current
+ * contents of the working copy.
+ *
+ * <p>It is possible that the contents of the
+ * original resource have changed since the working copy was created,
+ * in which case there is an update conflict. This operation allows
+ * for two settings to resolve conflict set by the <code>fForce</code> flag:<ul>
+ * <li>force flag is <code>false</code> - in this case a <code>CModelException</code> 	
+ * is thrown</li>
+ * <li>force flag is <code>true</code> - in this case the contents of
+ * 	the working copy are applied to the underlying resource even though
+ * 	the working copy was created before a subsequent change in the
+ * 	resource</li>
+ * </ul>
+ *
+ * <p>The default conflict resolution setting is the force flag is <code>false</code>
+ *
+ * A CModelOperation exception is thrown either if the commit could not be
+ * performed.
+ * 
+ * This class is similar to the JDT CommitWorkingCopyOperation class.
+ */
+
+public class CommitWorkingCopyOperation extends CModelOperation {
+	/**
+	 * Constructs an operation to commit the contents of a working copy
+	 * to its original translation unit.
+	 */
+
+	public CommitWorkingCopyOperation(ITranslationUnit element, boolean force) {
+		super(new ICElement[] {element}, force);
+	}
+	
+	
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.CModelOperation#executeOperation()
+	 */
+	protected void executeOperation() throws CModelException {
+		try {
+			beginTask("workingCopy.commit", 2); //$NON-NLS-1$
+			WorkingCopy copy = (WorkingCopy)getElementToProcess();
+			ITranslationUnit original = (ITranslationUnit) copy.getOriginalElement();
+		
+			
+			// creates the delta builder (this remembers the content of the cu)	
+			if (!original.isOpen()) {
+				// force opening so that the delta builder can get the old info
+				original.open(null);
+			}
+			CElementDeltaBuilder deltaBuilder = new CElementDeltaBuilder(original);
+		
+			// save the cu
+			IBuffer originalBuffer = original.getBuffer();
+			if (originalBuffer == null) return;
+			char[] originalContents = originalBuffer.getCharacters();
+			boolean hasSaved = false;
+			try {
+				IBuffer copyBuffer = copy.getBuffer();
+				if (copyBuffer == null) return;
+				originalBuffer.setContents(copyBuffer.getCharacters());
+				original.save(fMonitor, fForce);
+				this.hasModifiedResource = true; 
+				hasSaved = true;
+			} finally {
+				if (!hasSaved){
+					// restore original buffer contents since something went wrong
+					originalBuffer.setContents(originalContents);
+				}
+			}
+			// make sure working copy is in sync
+			copy.updateTimeStamp((TranslationUnit)original);
+			copy.makeConsistent(this);
+			worked(1);
+		
+			if (deltaBuilder != null) {
+				// build the deltas
+				deltaBuilder.buildDeltas();
+			
+				// add the deltas to the list of deltas created during this operation
+				if (deltaBuilder.delta != null) {
+					addDelta(deltaBuilder.delta);
+				}
+			}
+			worked(1);
+		} finally {	
+			done();
+		}		
+	}
+	/**
+	 * Possible failures: <ul>
+	 *	<li>INVALID_ELEMENT_TYPES - the Translation unit supplied to this
+	 *		operation is not a working copy
+	 *  <li>ELEMENT_NOT_PRESENT - the Translation unit the working copy is
+	 *		based on no longer exists.
+	 *  <li>UPDATE_CONFLICT - the original Translation unit has changed since
+	 *		the working copy was created and the operation specifies no force
+	 *  </ul>
+	 */
+
+	public ICModelStatus verify() {
+		
+		IWorkingCopy wc = (IWorkingCopy) getElementToProcess();
+		if (!wc.isWorkingCopy()) {
+			return new CModelStatus(ICModelStatusConstants.INVALID_ELEMENT_TYPES, wc);
+		}
+	
+		try {
+			ITranslationUnit original= (ITranslationUnit)wc.getOriginalElement();
+			IResource resource = original.getResource();
+			if (!wc.isBasedOn(resource) && !fForce) {
+				return new CModelStatus(ICModelStatusConstants.UPDATE_CONFLICT);
+			}
+		} catch (CModelException e){
+			// unable to get the underlying resource
+			return new CModelStatus(ICModelStatusConstants.INVALID_RESOURCE);
+		}
+		// no read-only check, since some repository adapters can change the flag on save
+		// operation.	
+		return CModelStatus.VERIFIED_OK;
+		
+	}
+
+}
Index: model/org/eclipse/cdt/internal/core/model/ElementCache.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/ElementCache.java
diff -N model/org/eclipse/cdt/internal/core/model/ElementCache.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/ElementCache.java	19 Mar 2003 20:02:41 -0000
@@ -0,0 +1,71 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICOpenable;
+import org.eclipse.cdt.internal.core.util.LRUCache;
+import org.eclipse.cdt.internal.core.util.OverflowingLRUCache;
+
+/**
+ * An LRU cache of <code>CElements</code>.
+ * 
+ * This class is similar to the JDT ElementCache class.
+ */
+public class ElementCache extends OverflowingLRUCache {
+
+	/**
+	 * Constructs a new element cache of the given size.
+	 */
+	public ElementCache(int size) {
+		super(size);
+	}
+	/**
+	 * Constructs a new element cache of the given size.
+	 */
+	public ElementCache(int size, int overflow) {
+		super(size, overflow);
+	}
+	/**
+	 * Returns true if the element is successfully closed and
+	 * removed from the cache, otherwise false.
+	 *
+	 * <p>NOTE: this triggers an external removal of this element
+	 * by closing the element.
+	 */
+	protected boolean close(LRUCacheEntry entry) {
+		ICOpenable element = (ICOpenable) entry._fKey;
+		try {
+			if (element.hasUnsavedChanges()) {
+				return false;
+			} /*else {
+				// We must close an entire JarPackageFragmentRoot at once.
+				if (element instanceof JarPackageFragment) {
+					JarPackageFragment packageFragment= (JarPackageFragment) element;
+					JarPackageFragmentRoot root = (JarPackageFragmentRoot) packageFragment.getParent();
+					root.close();
+				}*/ else {
+					element.close();
+				}
+				return true;
+		//	}
+		} catch (CModelException npe) {
+			return false;
+		}
+	}
+		/**
+		 * Returns a new instance of the reciever.
+		 */
+		protected LRUCache newInstance(int size, int overflow) {
+			return new ElementCache(size, overflow);
+		}
+}
Index: model/org/eclipse/cdt/internal/core/model/IBuffer.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/IBuffer.java
diff -N model/org/eclipse/cdt/internal/core/model/IBuffer.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/IBuffer.java	19 Mar 2003 20:02:42 -0000
@@ -0,0 +1,259 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICOpenable;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A buffer contains the text contents of a resource. It is not language-specific.
+ * The contents may be in the process of being edited, differing from the actual contents of the 
+ * underlying resource. A buffer has an owner, which is an
+ * <code>IOpenable</code>. If a buffer does not have an underlying resource,
+ * saving the buffer has no effect. Buffers can be read-only.
+ * <p>
+ * This interface is similar to the JDT IBuffer interface.
+ */
+
+public interface IBuffer {
+
+	/**
+	 * Adds the given listener for changes to this buffer.
+	 * Has no effect if an identical listener is already registered or if the buffer
+	 * is closed.
+	 *
+	 * @param listener the listener of buffer changes
+	 */
+	public void addBufferChangedListener(IBufferChangedListener listener);
+	/**
+	 * Appends the given character array to the contents of the buffer.
+	 * This buffer will now have unsaved changes.
+	 * Any client can append to the contents of the buffer, not just the owner of the buffer.
+	 * Reports a buffer changed event.
+	 * <p>
+	 * Has no effect if this buffer is read-only.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param text the given character array to append to contents of the buffer
+	 */
+	public void append(char[] text);
+	/**
+	 * Appends the given string to the contents of the buffer.
+	 * This buffer will now have unsaved changes.
+	 * Any client can append to the contents of the buffer, not just the owner of the buffer.
+	 * Reports a buffer changed event.
+	 * <p>
+	 * Has no effect if this buffer is read-only.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param text the <code>String</code> to append to the contents of the buffer
+	 */
+	public void append(String text);
+	/**
+	 * Closes the buffer. Any unsaved changes are lost. Reports a buffer changed event
+	 * with a 0 offset and a 0 length. When this event is fired, the buffer should already
+	 * be closed.
+	 * <p>
+	 * Further operations on the buffer are not allowed, except for close.  If an
+	 * attempt is made to close an already closed buffer, the second attempt has no effect.
+	 */
+	public void close();
+	/**
+	 * Returns the character at the given position in this buffer.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param position a zero-based source offset in this buffer
+	 * @return the character at the given position in this buffer
+	 */
+	public char getChar(int position);
+	/**
+	 * Returns the contents of this buffer as a character array, or <code>null</code> if
+	 * the buffer has not been initialized.
+	 * <p>
+	 * Callers should make no assumption about whether the returned character array
+	 * is or is not the genuine article or a copy. In other words, if the client
+	 * wishes to change this array, they should make a copy. Likewise, if the
+	 * client wishes to hang on to the array in its current state, they should
+	 * make a copy.
+	 * </p>
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @return the characters contained in this buffer
+	 */
+	public char[] getCharacters();
+	/**
+	 * Returns the contents of this buffer as a <code>String</code>. Like all strings,
+	 * the result is an immutable value object., It can also answer <code>null</code> if
+	 * the buffer has not been initialized.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @return the contents of this buffer as a <code>String</code>
+	 */
+	public String getContents();
+	/**
+	 * Returns number of characters stored in this buffer.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @return the number of characters in this buffer
+	 */
+	public int getLength();
+	/**
+	 * Returns the resource element owning of this buffer.
+	 *
+	 * @return the resource element owning this buffer
+	 */
+	public ICOpenable getOwner();
+	/**
+	 * Returns the given range of text in this buffer.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param offset the  zero-based starting offset
+	 * @param length the number of characters to retrieve
+	 * @return the given range of text in this buffer
+	 */
+	public String getText(int offset, int length);
+	/**
+	 * Returns the underlying resource for which this buffer was opened,
+	 * or <code>null</code> if this buffer was not opened on a resource.
+	 *
+	 * @return the underlying resource for this buffer, or <code>null</code>
+	 *  if none.
+	 */
+	public IResource getUnderlyingResource();
+	/**
+	 * Returns whether this buffer has been modified since it
+	 * was opened or since it was last saved.
+	 * If a buffer does not have an underlying resource, this method always
+	 * returns <code>true</code>.
+	 *
+	 * @return a <code>boolean</code> indicating presence of unsaved changes (in
+	 *   the absence of any underlying resource, it will always return <code>true</code>).
+	 */
+	public boolean hasUnsavedChanges();
+	/**
+	 * Returns whether this buffer has been closed.
+	 *
+	 * @return a <code>boolean</code> indicating whether this buffer is closed.
+	 */
+	public boolean isClosed();
+	/**
+	 * Returns whether this buffer is read-only.
+	 *
+	 * @return a <code>boolean</code> indicating whether this buffer is read-only
+	 */
+	public boolean isReadOnly();
+	/**
+	 * Removes the given listener from this buffer.
+	 * Has no affect if an identical listener is not registered or if the buffer is closed.
+	 *
+	 * @param listener the listener
+	 */
+	public void removeBufferChangedListener(IBufferChangedListener listener);
+	/**
+	 * Replaces the given range of characters in this buffer with the given text.
+	 * <code>position</code> and <code>position + length</code> must be in the range [0, getLength()].
+	 * <code>length</code> must not be negative.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param position the zero-based starting position of the affected text range in this buffer
+	 * @param length the length of the affected text range in this buffer
+	 * @param text the replacing text as a character array
+	 */
+	public void replace(int position, int length, char[] text);
+	/**
+	 * Replaces the given range of characters in this buffer with the given text.
+	 * <code>position</code> and <code>position + length</code> must be in the range [0, getLength()].
+	 * <code>length</code> must not be negative.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param position the zero-based starting position of the affected text range in this buffer
+	 * @param length the length of the affected text range in this buffer
+	 * @param text the replacing text as a <code>String</code>
+	 */
+	public void replace(int position, int length, String text);
+	/**
+	 * Saves the contents of this buffer to its underlying resource. If
+	 * successful, this buffer will have no unsaved changes.
+	 * The buffer is left open. Saving a buffer with no unsaved
+	 * changes has no effect - the underlying resource is not changed.
+	 * If the buffer does not have an underlying resource or is read-only, this
+	 * has no effect.
+	 * <p>
+	 * The <code>force</code> parameter controls how this method deals with
+	 * cases where the workbench is not completely in sync with the local file system.
+	 * If <code>false</code> is specified, this method will only attempt
+	 * to overwrite a corresponding file in the local file system provided
+	 * it is in sync with the workbench. This option ensures there is no 
+	 * unintended data loss; it is the recommended setting.
+	 * However, if <code>true</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>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param progress the progress monitor to notify
+	 * @param force a <code> boolean </code> flag indicating how to deal with resource
+	 *   inconsistencies.
+	 *
+	 * @exception CModelException if an error occurs writing the buffer 	to
+	 * the underlying resource
+	 *
+	 * @see org.eclipse.core.resources.IFile#setContents(java.io.InputStream, boolean, boolean, org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void save(IProgressMonitor progress, boolean force) throws CModelException;
+	/**
+	 * Sets the contents of this buffer to the given character array.
+	 * This buffer will now have unsaved changes.
+	 * Any client can set the contents of the buffer, not just the owner of the buffer.
+	 * Reports a buffer changed event.
+	 * <p>
+	 * Equivalent to <code>replace(0,getLength(),contents)</code>.
+	 * </p>
+	 * <p>
+	 * Has no effect if this buffer is read-only.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param contents the new contents of this buffer as a character array
+	 */
+	public void setContents(char[] contents);
+	/**
+	 * Sets the contents of this buffer to the given <code>String</code>.
+	 * This buffer will now have unsaved changes.
+	 * Any client can set the contents of the buffer, not just the owner of the buffer.
+	 * Reports a buffer changed event.
+	 * <p>
+	 * Equivalent to <code>replace(0,getLength(),contents)</code>.
+	 * </p>
+	 * <p>
+	 * Has no effect if this buffer is read-only.
+	 * <p>
+	 * A <code>RuntimeException</code> might be thrown if the buffer is closed.
+	 *
+	 * @param contents the new contents of this buffer as a <code>String</code>
+	 */
+	public void setContents(String contents);
+
+}
Index: model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java
diff -N model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java	19 Mar 2003 20:02:42 -0000
@@ -0,0 +1,32 @@
+package org.eclipse.cdt.internal.core.model;
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+/**
+ * A listener, which gets notified when the contents of a specific buffer
+ * have changed, or when the buffer is closed.
+ * When a buffer is closed, the listener is notified <em>after</em> the buffer has been closed.
+ * A listener is not notified when a buffer is saved.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ * 
+ * This interface is similar to the JDT IBufferChangedListener interface
+ */
+public interface IBufferChangedListener {
+	/** 
+	 * Notifies that the given event has occurred.
+	 *
+	 * @param event the change event
+	 */
+	public void bufferChanged(BufferChangedEvent event);
+
+}
Index: model/org/eclipse/cdt/internal/core/model/IBufferFactory.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/IBufferFactory.java
diff -N model/org/eclipse/cdt/internal/core/model/IBufferFactory.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/IBufferFactory.java	19 Mar 2003 20:02:42 -0000
@@ -0,0 +1,35 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import org.eclipse.cdt.core.model.ICOpenable;
+/**
+ * A factory that creates <code>IBuffer</code>s for CFiles.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ * 
+ * This    interface is similar to the JDT IBufferFactory interface.
+ */
+public interface IBufferFactory {
+
+	/**
+	 * Creates a buffer for the given owner.
+	 * The new buffer will be initialized with the contents of the owner 
+	 * if and only if it was not already initialized by the factory (a buffer is uninitialized if 
+	 * its content is <code>null</code>).
+	 * 
+	 * @param owner the owner of the buffer
+	 * @see IBuffer
+	 */
+	IBuffer createBuffer(ICOpenable owner);
+}
Index: model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java
diff -N model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java	19 Mar 2003 20:02:42 -0000
@@ -0,0 +1,129 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * <p> A working copy of a C element acts just like a regular element (handle),
+ * except it is not attached to an underlying resource. A working copy is not
+ * visible to the rest of the C model. Changes in a working copy's buffer are
+ * not realized in a resource. To bring the C model up-to-date with a working
+ * copy's contents, an explicit commit must be performed on the working copy.
+ * Other operations performed on a working copy update the contents of the
+ * working copy's buffer but do not commit the contents of the working copy.
+ * </p>
+ * <p>
+ * Note: The contents of a working copy is determined when a working
+ * copy is created, based on the current content of the element the working
+ * copy is created from. If a working copy is an <code>ICFile</code> and is
+ * explicitly closed, the working copy's buffer will be thrown away. However,
+ * clients should not explicitly open and close working copies.
+ * </p>
+ * <p>
+ * The client that creates a working copy is responsible for
+ * destroying the working copy. The C model will never automatically destroy or
+ * close a working copy. (Note that destroying a working copy does not commit it
+ * to the model, it only frees up the memory occupied by the element). After a
+ * working copy is destroyed, the working copy cannot be accessed again. Non-
+ * handle methods will throw a <code>CModelException</code> indicating the
+ * C element does not exist.
+ * </p>
+ * <p>
+ * A working copy cannot be created from another working copy.
+ * Calling <code>getWorkingCopy</code> on a working copy returns the receiver.
+ * </p>
+ */
+public interface IWorkingCopy extends ITranslationUnit{
+	/**
+	 * Commits the contents of this working copy to its original element
+	 * and underlying resource, bringing the C model up-to-date with the current
+	 * contents of the working copy.
+	 *
+	 * <p>It is possible that the contents of the original resource have changed
+	 * since this working copy was created, in which case there is an update conflict.
+	 * The value of the <code>force</code> parameter effects the resolution of
+	 * such a conflict:<ul>
+	 * <li> <code>true</code> - in this case the contents of this working copy are applied to
+	 * 	the underlying resource even though this working copy was created before
+	 *	a subsequent change in the resource</li>
+	 * <li> <code>false</code> - in this case a <code>CModelException</code> is
+	 * thrown</li>
+	 * </ul>
+	 */
+	void commit(boolean force, IProgressMonitor monitor) throws CModelException;
+	/**
+	 * Destroys this working copy, closing its buffer and discarding
+	 * its structure. Subsequent attempts to access non-handle information
+	 * for this working copy will result in <code>CModelException</code>s. Has
+	 * no effect if this element is not a working copy.
+	 * <p>
+	 * If this working copy is shared, it is destroyed only when the number of calls to
+	 * <code>destroy()</code> is the same as the number of calls to <code>
+	 * getSharedWorkingCopy(IProgressMonitor, IBufferFactory)</code>. 
+	 * A REMOVED CElementDelta is then reported on this working copy.
+	 */
+	
+	void destroy();
+	
+	/**
+	 * Returns the original element this working copy was created from,
+	 * or <code>null</code> if this is not a working copy.
+	 */ 
+	ITranslationUnit getOriginalElement();
+		
+	/**
+	 * Returns whether this working copy's original element's content
+	 * has not changed since the inception of this working copy.
+	 * 
+	 * @return true if this working copy's original element's content
+	 * has not changed since the inception of this working copy, false otherwise
+	 */
+	boolean isBasedOn(IResource resource);
+	
+	/**
+	 * Reconciles the contents of this working copy.
+	 * It performs the reconciliation by locally caching the contents of 
+	 * the working copy, updating the contents, then creating a delta 
+	 * over the cached contents and the new contents, and finally firing
+	 * this delta.
+	 * <p>
+	 * If the working copy hasn't changed, then no problem will be detected,
+	 * this is equivalent to <code>IWorkingCopy#reconcile(false, null)</code>.
+	 * <p>
+	 */	
+	IMarker[] reconcile() throws CModelException;
+	
+	/**
+	 * Reconciles the contents of this working copy.
+	 * It performs the reconciliation by locally caching the contents of 
+	 * the working copy, updating the contents, then creating a delta 
+	 * over the cached contents and the new contents, and finally firing
+	 * this delta.
+	 * <p>
+	 * The boolean argument allows to force problem detection even if the
+	 * working copy is already consistent.
+	 */
+	void reconcile(boolean forceProblemDetection, IProgressMonitor monitor) throws CModelException;
+	/**
+	 * Restores the contents of this working copy to the current contents of
+	 * this working copy's original element. Has no effect if this element
+	 * is not a working copy.
+	 */	
+	void restore() throws CModelException;
+}
Index: model/org/eclipse/cdt/internal/core/model/Parent.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Parent.java,v
retrieving revision 1.2
diff -u -r1.2 Parent.java
--- model/org/eclipse/cdt/internal/core/model/Parent.java	18 Nov 2002 15:46:45 -0000	1.2
+++ model/org/eclipse/cdt/internal/core/model/Parent.java	19 Mar 2003 20:02:42 -0000
@@ -5,15 +5,15 @@
  * All Rights Reserved.
  */
 
+import java.util.ArrayList;
 
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.IParent;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.IPath;
 
-import org.eclipse.cdt.core.model.ICElement;
-import org.eclipse.cdt.core.model.IParent;
-import org.eclipse.cdt.core.model.CModelException;
-
 public abstract class Parent extends CElement implements IParent {
 	
 	protected IResource resource;
@@ -62,6 +62,24 @@
 	 */		
 	public ICElement[] getChildren() {
 		return getElementInfo().getChildren();
+	}
+
+	/**
+	 * Gets the children of a certain type
+	 * @param type
+	 * @return ArrayList
+	 */
+	public ArrayList getChildrenOfType(int type){
+		ICElement[] children = getChildren();
+		int size = children.length;
+		ArrayList list = new ArrayList(size);
+		for (int i = 0; i < size; ++i) {
+			CElement elt = (CElement)children[i];
+			if (elt.getElementType() == type) {
+				list.add(elt);
+			}
+		}
+		return list;
 	}
 
 	public boolean hasChildren () {
Index: model/org/eclipse/cdt/internal/core/model/SourceManipulation.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/SourceManipulation.java,v
retrieving revision 1.2
diff -u -r1.2 SourceManipulation.java
--- model/org/eclipse/cdt/internal/core/model/SourceManipulation.java	18 Nov 2002 15:46:38 -0000	1.2
+++ model/org/eclipse/cdt/internal/core/model/SourceManipulation.java	19 Mar 2003 20:02:42 -0000
@@ -5,15 +5,15 @@
  * All Rights Reserved.
  */
  
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.IProgressMonitor;
-
+import org.eclipse.cdt.core.model.CModelException;
 import org.eclipse.cdt.core.model.ICElement;
-import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.core.model.ICOpenable;
 import org.eclipse.cdt.core.model.ISourceManipulation;
-import org.eclipse.cdt.core.model.ISourceReference;
 import org.eclipse.cdt.core.model.ISourceRange;
-import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ISourceReference;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
 
 /**
  * Abstract class for C elements which implement ISourceReference.
@@ -102,6 +102,21 @@
 	 * @see ICElement
 	 */
 	public IResource getCorrespondingResource() throws CModelException {
+		return null;
+	}
+
+	/**
+	 * Returns the first parent of the element that is an instance of
+	 * ICOpenable.
+	 */
+	public ICOpenable getOpenableParent() {
+		ICElement current = getParent();
+		while (current != null){
+			if (current instanceof ICOpenable){
+				return (ICOpenable) current;
+			}
+			current = current.getParent();
+		}
 		return null;
 	}
 
Index: model/org/eclipse/cdt/internal/core/model/TranslationUnit.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java,v
retrieving revision 1.1
diff -u -r1.1 TranslationUnit.java
--- model/org/eclipse/cdt/internal/core/model/TranslationUnit.java	26 Jun 2002 20:37:14 -0000	1.1
+++ model/org/eclipse/cdt/internal/core/model/TranslationUnit.java	19 Mar 2003 20:02:42 -0000
@@ -7,21 +7,25 @@
 
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-
+import org.eclipse.cdt.core.model.CModelException;
 import org.eclipse.cdt.core.model.ICElement;
 import org.eclipse.cdt.core.model.IInclude;
-import org.eclipse.cdt.core.model.IUsing;
-import org.eclipse.cdt.core.model.ITranslationUnit;
-import org.eclipse.cdt.core.model.ISourceReference;
+import org.eclipse.cdt.core.model.IParent;
 import org.eclipse.cdt.core.model.ISourceRange;
-import org.eclipse.cdt.core.model.CModelException;
-
+import org.eclipse.cdt.core.model.ISourceReference;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.core.model.IUsing;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
 
 /**
+ * @see ITranslationUnit
  */
 public class TranslationUnit extends CFile implements ITranslationUnit {
 
@@ -180,4 +184,259 @@
 	protected CElementInfo createElementInfo () {
 		return new TranslationUnitInfo(this);
 	}
+	
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.CFile#buildStructure(CFileInfo, IProgressMonitor)
+	 */
+	protected void buildStructure(CFileInfo info, IProgressMonitor monitor) throws CModelException {
+		if (monitor != null && monitor.isCanceled()) return;
+
+		// remove existing (old) infos
+		removeInfo();
+
+		HashMap newElements = new HashMap(11);
+		info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
+		CModelManager.getDefault().getElementsOutOfSynchWithBuffers().remove(this);
+		for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
+			ICElement key = (ICElement) iter.next();
+			Object value = newElements.get(key);
+			CModelManager.getDefault().putInfo(key, value);
+		}
+		// problem detection 
+		if (monitor != null && monitor.isCanceled()) return;
+
+		//IProblemRequestor problemRequestor = this.getProblemRequestor();
+		//if (problemRequestor != null && problemRequestor.isActive()){
+		//	problemRequestor.beginReporting();
+		//	CompilationUnitProblemFinder.process(this, problemRequestor, monitor);
+		//	problemRequestor.endReporting();
+		//}
+	
+		// add the info for this at the end, to ensure that a getInfo cannot reply null in case the LRU cache needs
+		// to be flushed. Might lead to performance issues.
+		CModelManager.getDefault().putInfo(this, info);	
+		
+	}
+	/**
+	 * Returns true if this handle represents the same Java element
+	 * as the given handle.
+	 *
+	 * <p>Compilation units must also check working copy state;
+	 *
+	 * @see Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object o) {
+		return super.equals(o) && !((ITranslationUnit)o).isWorkingCopy();
+	}
+
+	/**
+	 * @see IWorkingCopy#findSharedWorkingCopy(IBufferFactory)
+	 */
+	public IWorkingCopy findSharedWorkingCopy(IBufferFactory factory) {
+
+		// if factory is null, default factory must be used
+		if (factory == null) factory = BufferManager.getDefaultBufferManager();
+
+		// In order to be shared, working copies have to denote the same translation unit 
+		// AND use the same buffer factory.
+		// Assuming there is a little set of buffer factories, then use a 2 level Map cache.
+		Map sharedWorkingCopies = CModelManager.getDefault().sharedWorkingCopies;
+	
+		Map perFactoryWorkingCopies = (Map) sharedWorkingCopies.get(factory);
+		if (perFactoryWorkingCopies == null) return null;
+		return (WorkingCopy)perFactoryWorkingCopies.get(this);
+	}
+
+	/**
+	 * To be removed with the new model builder in place
+	 * @param newElements
+	 * @param element
+	 */
+	private void getNewElements(Map newElements, CElement element){
+		Object info = element.getElementInfo();
+		if(info != null){
+			if(element instanceof IParent){
+				ICElement[] children = ((CElementInfo)info).getChildren();
+				int size = children.length;
+				for (int i = 0; i < size; ++i) {
+					CElement child = (CElement) children[i];
+					getNewElements(newElements, child);		
+				}		
+			}
+		}
+		newElements.put(element, info);		
+	}
+	
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.CResource#generateInfos(CResourceInfo, IProgressMonitor, Map, IResource)
+	 */
+	protected boolean generateInfos(CResourceInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws CModelException {
+		// put the info now, because getting the contents requires it
+		CModelManager.getDefault().putInfo(this, info);
+		TranslationUnitInfo unitInfo = (TranslationUnitInfo) info;
+
+		// generate structure
+		this.parse();
+		
+		// this is temporary until the New Model Builder is implemented
+		getNewElements(newElements, this);		
+		///////////////////////////////////////////////////////////////
+		
+		if (isWorkingCopy()) {
+			ITranslationUnit original = (ITranslationUnit) ((IWorkingCopy)this).getOriginalElement();
+			// might be IResource.NULL_STAMP if original does not exist
+			unitInfo.fTimestamp = ((IFile) original.getResource()).getModificationStamp();
+		}
+		
+		return unitInfo.isStructureKnown();
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#getContents()
+	 */
+	public char[] getContents() {
+		try {
+			IBuffer buffer = this.getBuffer();
+			return buffer == null ? null : buffer.getCharacters();
+		} catch (CModelException e) {
+			return new char[0];
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#getSharedWorkingCopy(IProgressMonitor, IBufferFactory)
+	 */
+	public IWorkingCopy getSharedWorkingCopy(IProgressMonitor monitor,IBufferFactory factory)
+		throws CModelException {
+	
+		// if factory is null, default factory must be used
+		if (factory == null) factory = BufferManager.getDefaultBufferManager();
+
+		CModelManager manager = CModelManager.getDefault();
+	
+		// In order to be shared, working copies have to denote the same translation unit 
+		// AND use the same buffer factory.
+		// Assuming there is a little set of buffer factories, then use a 2 level Map cache.
+		Map sharedWorkingCopies = manager.sharedWorkingCopies;
+	
+		Map perFactoryWorkingCopies = (Map) sharedWorkingCopies.get(factory);
+		if (perFactoryWorkingCopies == null){
+			perFactoryWorkingCopies = new HashMap();
+			sharedWorkingCopies.put(factory, perFactoryWorkingCopies);
+		}
+		WorkingCopy workingCopy = (WorkingCopy)perFactoryWorkingCopies.get(this);
+		if (workingCopy != null) {
+			workingCopy.useCount++;
+			return workingCopy;
+
+		} else {
+			workingCopy = (WorkingCopy)this.getWorkingCopy(monitor, factory);
+			perFactoryWorkingCopies.put(this, workingCopy);
+
+			// report added java delta
+//			CElementDelta delta = new CElementDelta(this.getCModel());
+//			delta.added(workingCopy);
+//			manager.fire(delta, CModelManager.DEFAULT_CHANGE_EVENT);
+
+			return workingCopy;
+		}
+	}
+	/**
+	 * 
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#getWorkingCopy()
+	 */
+	public IWorkingCopy getWorkingCopy()throws CModelException{
+		return this.getWorkingCopy(null, null);
+	}
+
+	/**
+	 * 
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#getWorkingCopy()
+	 */
+	public IWorkingCopy getWorkingCopy(IProgressMonitor monitor, IBufferFactory factory)throws CModelException{
+		WorkingCopy workingCopy = new WorkingCopy(getParent(), getFile(), factory);
+		// open the working copy now to ensure contents are that of the current state of this element
+		workingCopy.open(monitor);
+		return workingCopy;
+	}
+
+	/**
+	 * Returns true if this element may have an associated source buffer.
+	 */
+	protected boolean hasBuffer() {
+		return true;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#isConsistent()
+	 */
+	public boolean isConsistent() throws CModelException {
+		return CModelManager.getDefault().getElementsOutOfSynchWithBuffers().get(this) == null;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.CResource#isSourceElement()
+	 */
+	protected boolean isSourceElement() {
+		return true;
+	}
+	/**
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#isWorkingCopy()
+	 */
+	public boolean isWorkingCopy() {
+		return false;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICOpenable#makeConsistent(IProgressMonitor)
+	 */
+	public void makeConsistent(IProgressMonitor pm) throws CModelException {
+		if (!isConsistent()) {
+			// create a new info and make it the current info
+			CFileInfo info = (CFileInfo) createElementInfo();
+			buildStructure(info, pm);
+		}
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.CResource#openBuffer(IProgressMonitor)
+	 */
+	protected IBuffer openBuffer(IProgressMonitor pm) throws CModelException {
+
+		// create buffer -  translation units only use default buffer factory
+		BufferManager bufManager = getBufferManager();		
+		IBuffer buffer = getBufferFactory().createBuffer(this);
+		if (buffer == null) 
+			return null;
+	
+		// set the buffer source
+		if (buffer.getCharacters() == null){
+			IResource file = this.getResource();
+			if (file != null && file.getType() == IResource.FILE) {
+				buffer.setContents(Util.getResourceContentsAsCharArray((IFile)file));
+			}
+		}
+
+		// add buffer to buffer cache
+		bufManager.addBuffer(buffer);
+			
+		// listen to buffer changes
+		buffer.addBufferChangedListener(this);
+	
+		return buffer;
+	}
+
+	/**
+	 * Parse the buffer contents of this element.
+	 */
+	public void parse(){
+		try{
+			getTranslationUnitInfo().parse(this.getBuffer().getContents());
+		} catch (CModelException e){
+			// error getting the buffer
+		}
+	}
+	
+
+	
 }
Index: model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java,v
retrieving revision 1.3
diff -u -r1.3 TranslationUnitInfo.java
--- model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java	4 Mar 2003 18:25:40 -0000	1.3
+++ model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java	19 Mar 2003 20:02:42 -0000
@@ -5,21 +5,27 @@
  * All Rights Reserved.
  */
 
-import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringBufferInputStream;
 
 import org.eclipse.cdt.core.CCorePlugin;
 import org.eclipse.cdt.core.model.ICElement;
 import org.eclipse.cdt.core.model.ISourceRange;
 import org.eclipse.cdt.internal.core.parser.Parser;
 import org.eclipse.cdt.internal.parser.CStructurizer;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 
+/**
+ * The Element Info of a Translation Unit.
+ */
 class TranslationUnitInfo extends CFileInfo {
 
+	/** 
+	 * Timestamp of original resource at the time this element
+	 * was opened or last updated.
+	 */
+	protected long fTimestamp;
+
 	protected TranslationUnitInfo (CElement element) {
 		super(element);
 	}
@@ -29,27 +35,8 @@
 	}
 
 	protected ICElement [] getChildren() {
-		if (hasChanged()) {
-			InputStream in = null;
-			try {
-				IResource res = getElement().getUnderlyingResource();
-				if (res != null && res.getType() == IResource.FILE) {
-					in = ((IFile)res).getContents();
-					parse(in);
-				} 
-			} catch (CoreException e) {
-				//e.printStackTrace();
-			} finally {
-				if (in != null) {
-					try {
-						in.close();	
-					} catch (IOException e) {
-					}
-				}
-			}
-				
-		}
-		return super.getChildren();
+		// CHECKPOINT: replacing the parsing done here before
+		return fChildren;		
 	}
 
 	protected void parse(InputStream in) {
@@ -67,6 +54,15 @@
 			}
 		} catch (Exception e) {
 			System.out.println(e);
+		}
+	}
+
+	protected void parse(String buf) {
+		// CHECKPOINT: Parsing a string using the StringBufferInputStream
+		// FIXME: quick fix for the IBinary which uses fake translationUnit
+		if (buf != null) {
+			StringBufferInputStream in = new StringBufferInputStream (buf);
+			parse (in);
 		}
 	}
 
Index: model/org/eclipse/cdt/internal/core/model/Util.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java,v
retrieving revision 1.1
diff -u -r1.1 Util.java
--- model/org/eclipse/cdt/internal/core/model/Util.java	26 Jun 2002 20:37:14 -0000	1.1
+++ model/org/eclipse/cdt/internal/core/model/Util.java	19 Mar 2003 20:02:42 -0000
@@ -5,14 +5,19 @@
  * All Rights Reserved.
  */
 
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICModelStatusConstants;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
 
 public class Util {
 
@@ -113,5 +118,52 @@
 		// use a platform operation to update the resource contents
 		boolean force = true;
 		file.setContents(stream, force, true, null); // record history
+	}
+	
+	/**
+	 * Returns the given file's contents as a character array.
+	 */
+	public static char[] getResourceContentsAsCharArray(IFile file) throws CModelException {
+		return getResourceContentsAsCharArray(file, null);
+	}
+	
+	public static char[] getResourceContentsAsCharArray(IFile file, String encoding) throws CModelException {
+		InputStream stream= null;
+		try {
+			stream = new BufferedInputStream(file.getContents(true));
+		} catch (CoreException e) {
+			throw new CModelException(e, ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST);
+		}
+		try {
+			return Util.getInputStreamAsCharArray(stream, -1, encoding);
+		} catch (IOException e) {
+			throw new CModelException(e, ICModelStatusConstants.IO_EXCEPTION);
+		} finally {
+			try {
+				stream.close();
+			} catch (IOException e) {
+			}
+		}
+	}
+	
+	/*
+	 * Add a log entry
+	 */
+	public static void log(Throwable e, String message) {
+		IStatus status= new Status(
+			IStatus.ERROR, 
+			CCorePlugin.getDefault().getDescriptor().getUniqueIdentifier(), 
+			IStatus.ERROR, 
+			message, 
+			e); 
+			
+		CCorePlugin.getDefault().getLog().log(status);
+	}	
+	
+	/**
+	 * Combines two hash codes to make a new one.
+	 */
+	public static int combineHashCodes(int hashCode1, int hashCode2) {
+		return hashCode1 * 17 + hashCode2;
 	}
 }
Index: model/org/eclipse/cdt/internal/core/model/WorkingCopy.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/WorkingCopy.java
diff -N model/org/eclipse/cdt/internal/core/model/WorkingCopy.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/WorkingCopy.java	19 Mar 2003 20:02:43 -0000
@@ -0,0 +1,379 @@
+package org.eclipse.cdt.internal.core.model;
+
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+import java.io.ByteArrayInputStream;
+import java.util.Map;
+
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.ICModelStatusConstants;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Implementation of a working copy translation unit. A working copy maintains
+ * the timestamp of the resource it was created from.
+ */
+public class WorkingCopy extends TranslationUnit implements IWorkingCopy {
+	
+	/**
+	 * If set, this is the factory that will be used to create the buffer.
+	 */
+	protected IBufferFactory bufferFactory;
+	/**
+	 * A counter of the number of time clients have asked for this 
+	 * working copy. It is set to 1, if the working
+	 * copy is not managed. When destroyed, this counter is
+	 * set to 0. Once destroyed, this working copy cannot be opened
+	 * and non-handle info can not be accessed. This is
+	 * never true if this translation unit is not a working copy.
+	 */
+	protected int useCount = 1;
+	
+	/**
+	 * Creates a working copy of this element
+	 */
+	public WorkingCopy(ICElement parent, IFile file, IBufferFactory bufferFactory) {
+		super(parent, file);
+		this.bufferFactory = 
+			bufferFactory == null ? 
+				getBufferManager() :
+				bufferFactory;
+	}
+
+	public WorkingCopy(ICElement parent, IPath path, IBufferFactory bufferFactory) {
+		super(parent, path);
+		this.bufferFactory = 
+			bufferFactory == null ? 
+				getBufferManager() :
+				bufferFactory;
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IWorkingCopy#commit(boolean, org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void commit(boolean force, IProgressMonitor monitor)
+		throws CModelException {
+			ITranslationUnit original = (ITranslationUnit)this.getOriginalElement();
+			if (original.exists()) {
+				CommitWorkingCopyOperation op= new CommitWorkingCopyOperation(this, force);
+				runOperation(op, monitor);
+			} else {
+				String contents = this.getSource();
+				if (contents == null) return;
+				try {
+					byte[] bytes = contents.getBytes(); 
+					ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+					IFile originalRes = (IFile)original.getResource();
+					if (originalRes.exists()) {
+						originalRes.setContents(
+							stream, 
+							force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, 
+							null);
+					} else {
+						originalRes.create(
+							stream,
+							force,
+							monitor);
+					}
+				} catch (CoreException e) {
+					throw new CModelException(e);
+				}
+			}			
+	}
+	
+	/**
+	 * Returns a new element info for this element.
+	 */
+	protected CElementInfo createElementInfo() {
+		return new WorkingCopyInfo(this);
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IWorkingCopy#destroy()
+	 */
+	public void destroy() {
+		if (--this.useCount > 0) {
+			return;
+		}
+		try {
+			close();
+		
+			// if original element is not on classpath flush it from the cache 
+			ICElement originalElement = this.getOriginalElement();
+			if (!this.getParent().exists()) {
+				((TranslationUnit)originalElement).close();
+			}
+		
+			// remove working copy from the cache
+			CModelManager manager = CModelManager.getDefault();
+		
+			// In order to be shared, working copies have to denote the same compilation unit 
+			// AND use the same buffer factory.
+			// Assuming there is a little set of buffer factories, then use a 2 level Map cache.
+			Map sharedWorkingCopies = manager.sharedWorkingCopies;
+		
+			Map perFactoryWorkingCopies = (Map) sharedWorkingCopies.get(this.bufferFactory);
+			if (perFactoryWorkingCopies != null){
+				if (perFactoryWorkingCopies.remove(originalElement) != null) {
+	
+					// report removed java delta
+					//CElementDelta delta = new CElementDelta(this.getCoreModel());
+					//delta.removed(this);
+					//manager.fire(delta, CModelManager.DEFAULT_CHANGE_EVENT);
+				}
+			}		
+		} catch (CModelException e) {
+			// do nothing
+		}		
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ICElement#exists()
+	 */
+	public boolean exists() {
+		// working copy always exists in the model until it is detroyed
+		return this.useCount != 0;
+	}
+
+
+	/**
+	 * Answers custom buffer factory
+	 */
+	public IBufferFactory getBufferFactory(){
+
+		return this.bufferFactory;
+	}
+
+	/**
+	 * Working copies must be identical to be equal.
+	 *
+	 * @see Object#equals
+	 */
+	public boolean equals(Object o) {
+		return this == o; 
+	}
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IWorkingCopy#getOriginalElement()
+	 */
+	public ITranslationUnit getOriginalElement() {
+		return new TranslationUnit(getParent(), getFile());
+	}
+
+	/**
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#getSharedWorkingCopy(IProgressMonitor, IBufferFactory)
+	 */
+	public IWorkingCopy getSharedWorkingCopy(IProgressMonitor monitor,IBufferFactory factory)
+		throws CModelException{
+			return this;		
+	}
+	/**
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#getWorkingCopy()
+	 */	
+	public IWorkingCopy getWorkingCopy() {
+		return this;
+	}
+
+	/**
+	 * @see IWorkingCopy
+	 */
+	public IWorkingCopy getWorkingCopy(IProgressMonitor monitor, IBufferFactory factory){
+		return this;
+	}
+
+
+	/**
+	 * @see IWorkingCopy
+	 */
+	public boolean isBasedOn(IResource resource) {
+		if (resource.getType() != IResource.FILE) {
+			return false;
+		}
+		if (this.useCount == 0) {
+			return false;
+		}
+		// if resource got deleted, then #getModificationStamp() will answer IResource.NULL_STAMP, which is always different from the cached
+		// timestamp
+		return ((TranslationUnitInfo) getElementInfo()).fTimestamp == ((IFile) resource).getModificationStamp();
+	}
+	/**
+	 * @see org.eclipse.cdt.core.model.ITranslationUnit#isWorkingCopy()
+	 */
+	public boolean isWorkingCopy() {
+		return true;
+	}
+
+	/**
+	 * @see ICFile
+	 * @see IWorkingCopy
+	 *
+	 * @exception CModelException attempting to open a read only element for
+	 * something other than navigation 	or if this is a working copy being
+	 * opened after it has been destroyed.
+	 */
+	public void open(IProgressMonitor pm) throws CModelException {
+		if (this.useCount == 0) { // was destroyed
+			throw newNotPresentException();
+		} else {
+			super.open(pm);
+		}
+	}
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.CFile#openBuffer(IProgressMonitor)
+	 */
+	protected IBuffer openBuffer(IProgressMonitor pm) throws CModelException {
+
+		if (this.useCount == 0) throw newNotPresentException();
+	
+		// create buffer - working copies may use custom buffer factory
+		IBuffer buffer = getBufferFactory().createBuffer(this);
+		if (buffer == null) 
+			return null;
+
+		// set the buffer source if needed
+		if (buffer.getCharacters() == null){
+			ITranslationUnit original= (ITranslationUnit)this.getOriginalElement();
+			IBuffer originalBuffer = null;
+			try {
+				originalBuffer = original.getBuffer();
+			} catch (CModelException e) {
+				// original element does not exist: create an empty working copy
+				if (!e.getCModelStatus().doesNotExist()) {
+					throw e;
+				}
+			}
+			if (originalBuffer != null) {
+				char[] originalContents = originalBuffer.getCharacters();
+				if (originalContents != null) {
+					buffer.setContents((char[])originalContents.clone());
+				}
+			} else {
+				// initialize buffer
+				buffer.setContents(new char[0]);
+			}
+		}
+
+		// add buffer to buffer cache
+		this.getBufferManager().addBuffer(buffer);
+
+		// listen to buffer changes
+		buffer.addBufferChangedListener(this);
+
+		return buffer;	
+	}
+	
+
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IWorkingCopy#reconcile()
+	 */
+	public IMarker[] reconcile() throws CModelException {
+		reconcile(false, null);
+		return null;
+	}
+	
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IWorkingCopy#reconcile(boolean, org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void reconcile(boolean forceProblemDetection, IProgressMonitor monitor)
+		throws CModelException {
+			
+		if (this.useCount == 0) throw newNotPresentException(); //was destroyed
+
+		if (monitor != null){
+			if (monitor.isCanceled()) return;
+			monitor.beginTask("element.reconciling", 10); //$NON-NLS-1$
+		}
+
+		boolean wasConsistent = isConsistent();
+		CElementDeltaBuilder deltaBuilder = null;
+
+		try {
+			// create the delta builder (this remembers the current content of the cu)
+			if (!wasConsistent){
+				deltaBuilder = new CElementDeltaBuilder(this);
+		
+				// update the element infos with the content of the working copy
+				this.makeConsistent(monitor);
+				deltaBuilder.buildDeltas();
+
+			}
+
+			if (monitor != null) monitor.worked(2);
+	
+			// force problem detection? - if structure was consistent
+			if (forceProblemDetection && wasConsistent){
+				if (monitor != null && monitor.isCanceled()) return;
+
+				//IProblemRequestor problemRequestor = this.getProblemRequestor();
+				//if (problemRequestor != null && problemRequestor.isActive()){
+				//	problemRequestor.beginReporting();
+				//	CompilationUnitProblemFinder.process(this, problemRequestor, monitor);
+				//	problemRequestor.endReporting();
+				//}
+			}
+	
+			// fire the deltas
+			//if (deltaBuilder != null){
+			//	if ((deltaBuilder.delta != null) && (deltaBuilder.delta.getAffectedChildren().length > 0)) {
+			//		CModelManager.getDefault().fire(deltaBuilder.delta, ElementChangedEvent.POST_RECONCILE);
+			//	}
+			//}
+		} finally {
+			if (monitor != null) monitor.done();
+		}
+			
+	}
+	/**
+	 * @see org.eclipse.cdt.internal.core.model.IWorkingCopy#restore()
+	 */
+	public void restore() throws CModelException{
+		if (this.useCount == 0) throw newNotPresentException(); //was destroyed
+
+		TranslationUnit original = (TranslationUnit) getOriginalElement();
+		IBuffer buffer = this.getBuffer();
+		if (buffer == null) return;
+		buffer.setContents(original.getContents());
+		updateTimeStamp(original);
+		makeConsistent(null);		
+	}
+	/**
+	 * @see org.eclipse.cdt.core.model.ICFile#save(IProgressMonitor, boolean)
+	 */
+	public void save(IProgressMonitor pm, boolean force) throws CModelException {
+		if (isReadOnly()) {
+			throw new CModelException(new CModelStatus(ICModelStatusConstants.READ_ONLY, this));
+		}
+		// computes fine-grain deltas in case the working copy is being reconciled already (if not it would miss one iteration of deltas).
+		this.reconcile();   
+	}
+	/**
+	 * @param original
+	 * @throws CModelException
+	 */
+	protected void updateTimeStamp(TranslationUnit original) throws CModelException {
+		long timeStamp =
+			((IFile) original.getResource()).getModificationStamp();
+		if (timeStamp == IResource.NULL_STAMP) {
+			throw new CModelException(
+				new CModelStatus(ICModelStatusConstants.INVALID_RESOURCE));
+		}
+		((TranslationUnitInfo) getElementInfo()).fTimestamp = timeStamp;
+	}
+
+}
Index: model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java
===================================================================
RCS file: model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java
diff -N model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java	19 Mar 2003 20:02:43 -0000
@@ -0,0 +1,22 @@
+package org.eclipse.cdt.internal.core.model;
+/**********************************************************************
+ * Copyright (c) 2002,2003 Rational Software Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ * 
+ * Contributors: 
+ * Rational Software - Initial API and implementation
+***********************************************************************/
+
+/**
+  * The Element Info of a Working Copy.
+  */
+public class WorkingCopyInfo extends TranslationUnitInfo {
+
+	public WorkingCopyInfo (CElement element) {
+		super(element);
+	}
+
+}
\ No newline at end of file



Back to the top