From 31945e2ce6c2ed250b83c71797266f0091cba296 Mon Sep 17 00:00:00 2001 From: Alain Magloire Date: Wed, 19 Mar 2003 20:19:48 +0000 Subject: [PATCH] Changes from Oda introducing LRUCaching and the notion of WorkingCopy in the mode. --- .../cdt/core/model/ElementChangedEvent.java | 3 + .../eclipse/cdt/core/model/ICOpenable.java | 70 +++ .../eclipse/cdt/core/model/ICResource.java | 2 +- .../cdt/core/model/ITranslationUnit.java | 89 +++- .../cdt/internal/core/model/Buffer.java | 443 ++++++++++++++++++ .../core/model/BufferChangedEvent.java | 111 +++++ .../internal/core/model/BufferManager.java | 156 ++++++ .../cdt/internal/core/model/CElement.java | 156 +++++- .../core/model/CElementDeltaBuilder.java | 37 ++ .../cdt/internal/core/model/CElementInfo.java | 14 - .../cdt/internal/core/model/CFile.java | 8 +- .../cdt/internal/core/model/CFolder.java | 12 +- .../cdt/internal/core/model/CModelCache.java | 138 ++++++ .../internal/core/model/CModelManager.java | 92 +++- .../cdt/internal/core/model/CProject.java | 6 + .../cdt/internal/core/model/CResource.java | 256 +++++++++- .../cdt/internal/core/model/CRoot.java | 16 +- .../model/CommitWorkingCopyOperation.java | 145 ++++++ .../cdt/internal/core/model/ElementCache.java | 71 +++ .../cdt/internal/core/model/IBuffer.java | 259 ++++++++++ .../core/model/IBufferChangedListener.java | 32 ++ .../internal/core/model/IBufferFactory.java | 35 ++ .../cdt/internal/core/model/IWorkingCopy.java | 129 +++++ .../cdt/internal/core/model/Parent.java | 26 +- .../core/model/SourceManipulation.java | 29 +- .../internal/core/model/TranslationUnit.java | 277 ++++++++++- .../core/model/TranslationUnitInfo.java | 46 +- .../eclipse/cdt/internal/core/model/Util.java | 58 ++- .../cdt/internal/core/model/WorkingCopy.java | 379 +++++++++++++++ .../internal/core/model/WorkingCopyInfo.java | 22 + 30 files changed, 3018 insertions(+), 99 deletions(-) create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICOpenable.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Buffer.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferManager.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelCache.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ElementCache.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBuffer.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferFactory.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopy.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java index 92feb5953fa..767d909cdd8 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java @@ -15,6 +15,9 @@ import java.util.EventObject; * @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 ICElementDelta). * diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICOpenable.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICOpenable.java new file mode 100644 index 00000000000..02114977968 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICOpenable.java @@ -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 null + * 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. + *

+ * The force 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; + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICResource.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICResource.java index 746a795d335..bffc4730ed2 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICResource.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ICResource.java @@ -6,5 +6,5 @@ package org.eclipse.cdt.core.model; -public interface ICResource extends IParent, ICElement { +public interface ICResource extends IParent, ICElement, ICOpenable { } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ITranslationUnit.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ITranslationUnit.java index 888fcb3b55e..5ce58ffc2ec 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ITranslationUnit.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ITranslationUnit.java @@ -4,8 +4,9 @@ package org.eclipse.cdt.core.model; * (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 (.c source file). * The children are of type IStructureElement, @@ -64,6 +65,26 @@ public interface ITranslationUnit extends ICFile , ISourceReference, ISourceMani */ IUsing createUsing (String name, IProgressMonitor monitor) throws CModelException; + /** + * Finds the shared working copy for this element, given a IBuffer factory. + * If no working copy has been created for this element associated with this + * buffer factory, returns null. + *

+ * Users of this method must not destroy the resulting working copy. + * + * @param bufferFactory the given IBuffer factory + * @return the found shared working copy for this element, null 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 @@ -99,6 +120,55 @@ public interface ITranslationUnit extends ICFile , ISourceReference, ISourceMani */ 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 Object#equals). + *

+ * The life time of a shared working copy is as follows: + *

+ * So users of this method must destroy exactly once the working copy. + *

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

+ * 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 null 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 null 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 null indicating + * that the client is not interested in problems. + * @exception CModelException if the contents of this element can not be + * determined. Reasons include: + *

+ * @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 @@ public interface ITranslationUnit extends ICFile , ISourceReference, ISourceMani * 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(); } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Buffer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Buffer.java new file mode 100644 index 00000000000..bb86f998572 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Buffer.java @@ -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 Buffer 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(""); //$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(); + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java new file mode 100644 index 00000000000..9abfab0a1fa --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferChangedEvent.java @@ -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 IBufferChangedListener notifications. + *

+ * For text insertions, getOffset is the offset + * of the first inserted character, getText is the + * inserted text, and getLength is 0. + *

+ *

+ * For text removals, getOffset is the offset + * of the first removed character, getText is null, + * and getLength is the length of the text that was removed. + *

+ *

+ * For replacements (including IBuffer.setContents), + * getOffset is the offset + * of the first replaced character, getText is the replacement + * text, and getLength is the length of the original text + * that was replaced. + *

+ *

+ * When a buffer is closed, getOffset is 0, getLength + * is 0, and getText is null. + *

+ *

+ * This class is not intended to be instantiated or subclassed by clients. + * Instances of this class are automatically created by the C model. + *

+ * + * @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 ( 0 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 null if text has been removed. + * + * @return the text corresponding to the buffer change ( null + * in case of deletion). + */ + public String getText() { + return this.text; + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferManager.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferManager.java new file mode 100644 index 00000000000..889b1bcd0af --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/BufferManager.java @@ -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 IBuffers. + */ + 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. + * + *

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 null 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. + *

+ * The Enumeration 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()); + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElement.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElement.java index 5efd24e05c1..9ef9f1afadb 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElement.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElement.java @@ -4,15 +4,17 @@ package org.eclipse.cdt.internal.core.model; * 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 @@ public abstract class CElement extends PlatformObject implements ICElement { protected ICElement fParent; - protected CElementInfo fCElementInfo; - protected String fName; protected int fStartPos; @@ -37,7 +37,6 @@ public abstract class CElement extends PlatformObject implements ICElement { fParent= parent; fName= name; fType= type; - fCElementInfo = null; } // setters @@ -89,7 +88,14 @@ public abstract class CElement extends PlatformObject implements ICElement { } 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 abstract class CElement extends PlatformObject implements ICElement { } 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 @@ public abstract class CElement extends PlatformObject implements ICElement { 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. + * + *

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()); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java new file mode 100644 index 00000000000..ef3a4664341 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementDeltaBuilder.java @@ -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 diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementInfo.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementInfo.java index c37308c8ec1..333c15e2ac0 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementInfo.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CElementInfo.java @@ -94,20 +94,6 @@ class CElementInfo { return fIsStructureKnown; } - /** - * @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. diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFile.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFile.java index 5ac68f0a25d..61e80946793 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFile.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFile.java @@ -4,15 +4,13 @@ package org.eclipse.cdt.internal.core.model; * 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 { diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFolder.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFolder.java index a7547a12ef9..28c191cc76e 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFolder.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CFolder.java @@ -5,11 +5,11 @@ package org.eclipse.cdt.internal.core.model; * 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 @@ public class CFolder extends CResource implements ICFolder { protected CElementInfo createElementInfo () { return new CFolderInfo(this); } + + // CHECKPOINT: folders will return the hash code of their path + public int hashCode() { + return getPath().hashCode(); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelCache.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelCache.java new file mode 100644 index 00000000000..cf771685d5e --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelCache.java @@ -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); + } +} + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java index 00d035125f6..9099a9a4173 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java @@ -53,7 +53,7 @@ public class CModelManager implements IResourceChangeListener { //private static HashMap fParsers = new HashMap(); /** - * Used to convert IResourceDeltas into IJavaElementDeltas. + * Used to convert IResourceDeltas into ICElementDeltas. */ protected DeltaProcessor fDeltaProcessor= new DeltaProcessor(); @@ -73,6 +73,25 @@ public class CModelManager implements IResourceChangeListener { */ 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 @@ public class CModelManager implements IResourceChangeListener { 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 @@ public class CModelManager implements IResourceChangeListener { } } fire(); - } catch (Exception e) { - e.printStackTrace(); } - } + } catch (Exception e) { + e.printStackTrace(); + } break; } } @@ -795,4 +817,62 @@ public class CModelManager implements IResourceChangeListener { } // 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); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CProject.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CProject.java index acfd4c0daca..b66d8f99666 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CProject.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CProject.java @@ -77,4 +77,10 @@ public class CProject extends CResource implements ICProject { protected CElementInfo createElementInfo() { return new CProjectInfo(this); } + + // CHECKPOINT: CProjects will return the hash code of their underlying IProject + public int hashCode() { + return getProject().hashCode(); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CResource.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CResource.java index d01a1b97623..24e1f5e4086 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CResource.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CResource.java @@ -5,13 +5,19 @@ package org.eclipse.cdt.internal.core.model; * 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 @@ public abstract class CResource extends Parent implements ICResource { } 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 null 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 IOpenable that is known to be closed (no check for + * isOpen()). + */ + 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 + } + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CRoot.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CRoot.java index 41c3b47dcfd..0bfa1209a8d 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CRoot.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CRoot.java @@ -5,6 +5,8 @@ package org.eclipse.cdt.internal.core.model; * 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 class CRoot extends CResource implements ICRoot { } 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 @@ public class CRoot extends CResource implements ICRoot { protected CElementInfo createElementInfo () { return new CRootInfo(this); } + + // CHECKPOINT: Roots will return the hashcode of their resource + public int hashCode() { + return resource.hashCode(); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java new file mode 100644 index 00000000000..42972a4f7a6 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CommitWorkingCopyOperation.java @@ -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. + * + *

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 fForce flag:

+ * + *

The default conflict resolution setting is the force flag is false + * + * 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:

+ */ + + 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; + + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ElementCache.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ElementCache.java new file mode 100644 index 00000000000..f352634fbd9 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/ElementCache.java @@ -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 CElements. + * + * 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. + * + *

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); + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBuffer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBuffer.java new file mode 100644 index 00000000000..d8d03998a99 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBuffer.java @@ -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 + * IOpenable. If a buffer does not have an underlying resource, + * saving the buffer has no effect. Buffers can be read-only. + *

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

+ * Has no effect if this buffer is read-only. + *

+ * A RuntimeException 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. + *

+ * Has no effect if this buffer is read-only. + *

+ * A RuntimeException might be thrown if the buffer is closed. + * + * @param text the String 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. + *

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

+ * A RuntimeException 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 null if + * the buffer has not been initialized. + *

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

+ *

+ * A RuntimeException 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 String. Like all strings, + * the result is an immutable value object., It can also answer null if + * the buffer has not been initialized. + *

+ * A RuntimeException might be thrown if the buffer is closed. + * + * @return the contents of this buffer as a String + */ + public String getContents(); + /** + * Returns number of characters stored in this buffer. + *

+ * A RuntimeException 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. + *

+ * A RuntimeException 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 null if this buffer was not opened on a resource. + * + * @return the underlying resource for this buffer, or null + * 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 true. + * + * @return a boolean indicating presence of unsaved changes (in + * the absence of any underlying resource, it will always return true). + */ + public boolean hasUnsavedChanges(); + /** + * Returns whether this buffer has been closed. + * + * @return a boolean indicating whether this buffer is closed. + */ + public boolean isClosed(); + /** + * Returns whether this buffer is read-only. + * + * @return a boolean 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. + * position and position + length must be in the range [0, getLength()]. + * length must not be negative. + *

+ * A RuntimeException 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. + * position and position + length must be in the range [0, getLength()]. + * length must not be negative. + *

+ * A RuntimeException 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 String + */ + 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. + *

+ * The force parameter controls how this method deals with + * cases where the workbench is not completely in sync with the local file system. + * If false 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 true 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). + *

+ * A RuntimeException might be thrown if the buffer is closed. + * + * @param progress the progress monitor to notify + * @param force a boolean 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. + *

+ * Equivalent to replace(0,getLength(),contents). + *

+ *

+ * Has no effect if this buffer is read-only. + *

+ * A RuntimeException 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 String. + * 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. + *

+ * Equivalent to replace(0,getLength(),contents). + *

+ *

+ * Has no effect if this buffer is read-only. + *

+ * A RuntimeException might be thrown if the buffer is closed. + * + * @param contents the new contents of this buffer as a String + */ + public void setContents(String contents); + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java new file mode 100644 index 00000000000..fbb3c829509 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferChangedListener.java @@ -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 after the buffer has been closed. + * A listener is not notified when a buffer is saved. + *

+ * This interface may be implemented by clients. + *

+ * + * 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); + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferFactory.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferFactory.java new file mode 100644 index 00000000000..559c4d5584a --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IBufferFactory.java @@ -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 IBuffers for CFiles. + *

+ * This interface may be implemented by clients. + *

+ * + * 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 null). + * + * @param owner the owner of the buffer + * @see IBuffer + */ + IBuffer createBuffer(ICOpenable owner); +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java new file mode 100644 index 00000000000..ab1ece8631c --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/IWorkingCopy.java @@ -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; + +/** + *

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

+ *

+ * 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 ICFile and is + * explicitly closed, the working copy's buffer will be thrown away. However, + * clients should not explicitly open and close working copies. + *

+ *

+ * 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 CModelException indicating the + * C element does not exist. + *

+ *

+ * A working copy cannot be created from another working copy. + * Calling getWorkingCopy on a working copy returns the receiver. + *

+ */ +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. + * + *

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 force parameter effects the resolution of + * such a conflict:

+ */ + 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 CModelExceptions. Has + * no effect if this element is not a working copy. + *

+ * If this working copy is shared, it is destroyed only when the number of calls to + * destroy() is the same as the number of calls to + * getSharedWorkingCopy(IProgressMonitor, IBufferFactory). + * A REMOVED CElementDelta is then reported on this working copy. + */ + + void destroy(); + + /** + * Returns the original element this working copy was created from, + * or null 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. + *

+ * If the working copy hasn't changed, then no problem will be detected, + * this is equivalent to IWorkingCopy#reconcile(false, null). + *

+ */ + 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. + *

+ * 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; +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Parent.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Parent.java index 3072d84b5b5..9fc3d06f047 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Parent.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Parent.java @@ -5,15 +5,15 @@ package org.eclipse.cdt.internal.core.model; * 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; @@ -64,6 +64,24 @@ public abstract class Parent extends CElement implements IParent { 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 () { return getElementInfo().hasChildren(); } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/SourceManipulation.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/SourceManipulation.java index 87e479b58b4..26e61b5bfc4 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/SourceManipulation.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/SourceManipulation.java @@ -5,16 +5,16 @@ package org.eclipse.cdt.internal.core.model; * All Rights Reserved. */ +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.core.model.ISourceManipulation; +import org.eclipse.cdt.core.model.ISourceRange; +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; -import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ITranslationUnit; -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; - /** * Abstract class for C elements which implement ISourceReference. */ @@ -105,6 +105,21 @@ public class SourceManipulation extends Parent implements ISourceManipulation, I 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; + } + /** * @see ISourceReference */ diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java index f3e9481fd1f..a8b837f78a7 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java @@ -7,21 +7,25 @@ package org.eclipse.cdt.internal.core.model; import java.io.InputStream; import java.util.ArrayList; +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.IInclude; +import org.eclipse.cdt.core.model.IParent; +import org.eclipse.cdt.core.model.ISourceRange; +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; -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.ISourceRange; -import org.eclipse.cdt.core.model.CModelException; - - /** + * @see ITranslationUnit */ public class TranslationUnit extends CFile implements ITranslationUnit { @@ -180,4 +184,259 @@ public class TranslationUnit extends CFile implements ITranslationUnit { 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. + * + *

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 + } + } + + + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java index 1400aede4ec..565eed14788 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnitInfo.java @@ -5,21 +5,27 @@ package org.eclipse.cdt.internal.core.model; * 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 @@ class TranslationUnitInfo extends CFileInfo { } 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) { @@ -70,6 +57,15 @@ class TranslationUnitInfo extends CFileInfo { } } + 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); + } + } + /* Overide the SourceManipulation for the range. */ protected ISourceRange getSourceRange() { IPath location = ((TranslationUnit)getElement()).getLocation(); diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java index 89f9c8fb8a7..cd5dae3ab3b 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java @@ -5,14 +5,19 @@ package org.eclipse.cdt.internal.core.model; * 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 { @@ -114,4 +119,51 @@ public class Util { 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; + } } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopy.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopy.java new file mode 100644 index 00000000000..952a7169f65 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopy.java @@ -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; + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java new file mode 100644 index 00000000000..a63dff35e2a --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/WorkingCopyInfo.java @@ -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