3m9 compatible;
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / CompilationUnit.java
index 50c264c..56c944e 100644 (file)
@@ -12,7 +12,6 @@ package net.sourceforge.phpdt.internal.core;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 
 import net.sourceforge.phpdt.core.IBuffer;
@@ -36,12 +35,15 @@ import net.sourceforge.phpdt.core.IType;
 import net.sourceforge.phpdt.core.IWorkingCopy;
 import net.sourceforge.phpdt.core.JavaModelException;
 import net.sourceforge.phpdt.core.Signature;
+import net.sourceforge.phpdt.core.WorkingCopyOwner;
 import net.sourceforge.phpdt.core.compiler.CharOperation;
 import net.sourceforge.phpdt.core.jdom.IDOMNode;
 import net.sourceforge.phpdt.internal.compiler.IProblemFactory;
 import net.sourceforge.phpdt.internal.compiler.SourceElementParser;
 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
 import net.sourceforge.phpdt.internal.compiler.problem.DefaultProblemFactory;
+import net.sourceforge.phpdt.internal.core.util.Util;
+import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
@@ -50,30 +52,38 @@ import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.Path;
+import net.sourceforge.phpdt.internal.core.JavaModelManager;
+//import net.sourceforge.phpdt.core.dom.AST;
+import net.sourceforge.phpdt.internal.core.ReconcileWorkingCopyOperation;
 
-/** 
+import net.sourceforge.phpdt.internal.core.DiscardWorkingCopyOperation;
+
+import net.sourceforge.phpdt.internal.core.CompilationUnitElementInfo;
+import net.sourceforge.phpdt.internal.core.JavaModelStatus;
+
+import net.sourceforge.phpdt.internal.core.BufferManager;
+import net.sourceforge.phpdt.internal.core.DefaultWorkingCopyOwner;
+import net.sourceforge.phpdt.internal.core.Openable;
+
+import net.sourceforge.phpdt.internal.core.PackageFragment;
+
+/**  
  * @see ICompilationUnit
  */
 
 public class CompilationUnit extends Openable implements ICompilationUnit, net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit {
-       
-       public static boolean SHARED_WC_VERBOSE = false;
-       
-       // TODO: Remove when we are certain that every client is ready for this fix
-       public static final boolean FIX_BUG25184 = true;
+       public WorkingCopyOwner owner;
 
 /**
  * Constructs a handle to a compilation unit with the given name in the
- * specified package.
+ * specified package for the specified owner
  *
  * @exception IllegalArgumentException if the name of the compilation unit
  * does not end with ".java"
  */
-protected CompilationUnit(IPackageFragment parent, String name) {
-       super(COMPILATION_UNIT, parent, name);
-       if (!Util.isJavaFileName(name)) {
-               throw new IllegalArgumentException(net.sourceforge.phpdt.internal.core.Util.bind("convention.unit.notJavaName")); //$NON-NLS-1$
-       }
+protected CompilationUnit(PackageFragment parent, String name, WorkingCopyOwner owner) {
+       super(parent, name);
+       this.owner = owner;
 }
 /**
  * Accepts the given visitor onto the parsed tree of this compilation unit, after
@@ -92,28 +102,121 @@ protected CompilationUnit(IPackageFragment parent, String name) {
 //public void accept(IAbstractSyntaxTreeVisitor visitor) throws JavaModelException {
 //     CompilationUnitVisitor.visit(this, visitor);
 //} 
+/*
+ * @see ICompilationUnit#becomeWorkingCopy(IProblemRequestor, IProgressMonitor)
+ */
+public void becomeWorkingCopy(IProblemRequestor problemRequestor, IProgressMonitor monitor) throws JavaModelException {
+       JavaModelManager manager = JavaModelManager.getJavaModelManager();
+       JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = manager.getPerWorkingCopyInfo(this, false/*don't create*/, true /*record usage*/, null/*no problem requestor needed*/);
+       if (perWorkingCopyInfo == null) {
+               // close cu and its children
+               close();
 
-protected void buildStructure(OpenableElementInfo info, IProgressMonitor monitor) throws JavaModelException {
+               BecomeWorkingCopyOperation operation = new BecomeWorkingCopyOperation(this, problemRequestor);
+               operation.runOperation(monitor);
+       }
+}
+//protected void buildStructure(OpenableElementInfo info, IProgressMonitor monitor) throws JavaModelException {
+//
+//     if (monitor != null && monitor.isCanceled()) return;
+//
+//     // remove existing (old) infos
+//     removeInfo();
+//
+//     HashMap newElements = new HashMap(11);
+//     info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
+//     JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
+//     for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
+//             IJavaElement key = (IJavaElement) iter.next();
+//             Object value = newElements.get(key);
+//             JavaModelManager.getJavaModelManager().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.
+//     // see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary type
+//     JavaModelManager.getJavaModelManager().putInfo(this, info);     
+//}
+protected boolean buildStructure(OpenableElementInfo info, final IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
 
-       if (monitor != null && monitor.isCanceled()) return;
+       // check if this compilation unit can be opened
+       if (!isWorkingCopy()) { // no check is done on root kind or exclusion pattern for working copies
+               if ( // ((IPackageFragment)getParent()).getKind() == IPackageFragmentRoot.K_BINARY|| 
+                               !isValidCompilationUnit()
+                               || !underlyingResource.isAccessible()) {
+                       throw newNotPresentException();
+               }
+       }
+       
+       // prevents reopening of non-primary working copies (they are closed when they are discarded and should not be reopened)
+       if (!isPrimary() && getPerWorkingCopyInfo() == null) {
+               throw newNotPresentException();
+       }
 
-       // remove existing (old) infos
-       removeInfo();
+       CompilationUnitElementInfo unitInfo = (CompilationUnitElementInfo) info;
 
-       HashMap newElements = new HashMap(11);
-       info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
-       JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
-       for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
-               IJavaElement key = (IJavaElement) iter.next();
-               Object value = newElements.get(key);
-               JavaModelManager.getJavaModelManager().putInfo(key, value);
+       // get buffer contents
+       IBuffer buffer = getBufferManager().getBuffer(CompilationUnit.this);
+       if (buffer == null) {
+               buffer = openBuffer(pm, unitInfo); // open buffer independently from the info, since we are building the info
        }
-       // 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.
-       // see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary type
-       JavaModelManager.getJavaModelManager().putInfo(this, info);     
-}
+       final char[] contents = buffer == null ? null : buffer.getCharacters();
 
+       // generate structure and compute syntax problems if needed
+       CompilationUnitStructureRequestor requestor = new CompilationUnitStructureRequestor(this, unitInfo, newElements);
+       JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = getPerWorkingCopyInfo();
+       IJavaProject project = getJavaProject();
+       boolean computeProblems = JavaProject.hasJavaNature(project.getProject()) && perWorkingCopyInfo != null && perWorkingCopyInfo.isActive();
+       IProblemFactory problemFactory = new DefaultProblemFactory();
+       Map options = project.getOptions(true);
+       SourceElementParser parser = new SourceElementParser(
+               requestor, 
+               problemFactory, 
+               new CompilerOptions(options));
+               //, true/*report local declarations*/);
+       requestor.parser = parser;
+       CompilationUnitDeclaration unit = parser.parseCompilationUnit(new net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit() {
+                       public char[] getContents() {
+                               return contents;
+                       }
+                       public char[] getMainTypeName() {
+                               return CompilationUnit.this.getMainTypeName();
+                       }
+                       public char[][] getPackageName() {
+                               return CompilationUnit.this.getPackageName();
+                       }
+                       public char[] getFileName() {
+                               return CompilationUnit.this.getFileName();
+                       }
+               }, true /*full parse to find local elements*/);
+       
+       // update timestamp (might be IResource.NULL_STAMP if original does not exist)
+       if (underlyingResource == null) {
+               underlyingResource = getResource();
+       }
+       unitInfo.timestamp = ((IFile)underlyingResource).getModificationStamp();
+       
+       // compute other problems if needed
+       CompilationUnitDeclaration compilationUnitDeclaration = null;
+       try {
+               if (computeProblems){
+                       perWorkingCopyInfo.beginReporting();
+                       compilationUnitDeclaration = CompilationUnitProblemFinder.process(unit, this, contents, parser, this.owner, perWorkingCopyInfo, problemFactory, false/*don't cleanup cu*/, pm);
+                       perWorkingCopyInfo.endReporting();
+               }
+               
+//             if (info instanceof ASTHolderCUInfo) {
+//                     int astLevel = ((ASTHolderCUInfo) info).astLevel;
+//                     org.eclipse.jdt.core.dom.CompilationUnit cu = AST.convertCompilationUnit(astLevel, unit, contents, options, pm);
+//                     ((ASTHolderCUInfo) info).ast = cu;
+//             }
+       } finally {
+           if (compilationUnitDeclaration != null) {
+               compilationUnitDeclaration.cleanUp();
+           }
+       }
+       
+       return unitInfo.isStructureKnown();
+}
 ///**
 // * @see ICodeAssist#codeComplete(int, ICompletionRequestor)
 // */
@@ -133,6 +236,13 @@ public void commit(boolean force, IProgressMonitor monitor) throws JavaModelExce
        throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, this));
 }
 /**
+ * @see ICompilationUnit#commitWorkingCopy(boolean, IProgressMonitor)
+ */
+public void commitWorkingCopy(boolean force, IProgressMonitor monitor) throws JavaModelException {
+       CommitWorkingCopyOperation op= new CommitWorkingCopyOperation(this, force);
+       op.runOperation(monitor);
+}
+/**
  * @see ISourceManipulation#copy(IJavaElement, IJavaElement, String, boolean, IProgressMonitor)
  */
 public void copy(IJavaElement container, IJavaElement sibling, String rename, boolean force, IProgressMonitor monitor) throws JavaModelException {
@@ -147,10 +257,11 @@ public void copy(IJavaElement container, IJavaElement sibling, String rename, bo
        }
        getJavaModel().copy(elements, containers, null, renamings, force, monitor);
 }
+
 /**
  * Returns a new element info for this element.
  */
-protected OpenableElementInfo createElementInfo() {
+protected Object createElementInfo() {
        return new CompilationUnitElementInfo();
 }
 ///**
@@ -203,24 +314,35 @@ public void delete(boolean force, IProgressMonitor monitor) throws JavaModelExce
        getJavaModel().delete(elements, force, monitor);
 }
 /**
- * This is not a working copy, do nothing.
- *
  * @see IWorkingCopy#destroy()
+ * @deprecated
  */
 public void destroy() {
+       try {
+               discardWorkingCopy();
+       } catch (JavaModelException e) {
+               e.printStackTrace();
+       }
+}
+/*
+ * @see ICompilationUnit#discardWorkingCopy
+ */
+public void discardWorkingCopy() throws JavaModelException {
+       // discard working copy and its children
+       DiscardWorkingCopyOperation op = new DiscardWorkingCopyOperation(this);
+       op.runOperation(null);
 }
-
 
 /**
  * Returns true if this handle represents the same Java element
  * as the given handle.
  *
- * <p>Compilation units must also check working copy state;
- *
  * @see Object#equals(java.lang.Object)
  */
-public boolean equals(Object o) {
-       return super.equals(o) && !((ICompilationUnit)o).isWorkingCopy();
+public boolean equals(Object obj) {
+       if (!(obj instanceof CompilationUnit)) return false;
+       CompilationUnit other = (CompilationUnit)obj;
+       return this.owner.equals(other.owner) && super.equals(obj);
 }
 /**
  * @see JavaElement#equalsDOMNode(IDOMNode)
@@ -303,25 +425,35 @@ public IType findPrimaryType() {
        }
        return null;
 }
-
 /**
  * @see IWorkingCopy#findSharedWorkingCopy(IBufferFactory)
+ * @deprecated
  */
 public IJavaElement findSharedWorkingCopy(IBufferFactory factory) {
 
        // if factory is null, default factory must be used
        if (factory == null) factory = this.getBufferManager().getDefaultBufferFactory();
-
-       // 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 = JavaModelManager.getJavaModelManager().sharedWorkingCopies;
        
-       Map perFactoryWorkingCopies = (Map) sharedWorkingCopies.get(factory);
-       if (perFactoryWorkingCopies == null) return null;
-       return (WorkingCopy)perFactoryWorkingCopies.get(this);
+       return findWorkingCopy(BufferFactoryWrapper.create(factory));
 }
 
+/**
+ * @see ICompilationUnit#findWorkingCopy(WorkingCopyOwner)
+ */
+public ICompilationUnit findWorkingCopy(WorkingCopyOwner workingCopyOwner) {
+       CompilationUnit cu = new CompilationUnit((PackageFragment)this.parent, getElementName(), workingCopyOwner);
+       if (workingCopyOwner == DefaultWorkingCopyOwner.PRIMARY) {
+               return cu;
+       } else {
+               // must be a working copy
+               JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = cu.getPerWorkingCopyInfo();
+               if (perWorkingCopyInfo != null) {
+                       return perWorkingCopyInfo.getWorkingCopy();
+               } else {
+                       return null;
+               }
+       }
+}
 protected boolean generateInfos(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
 
 //     if (getParent() instanceof JarPackageFragment) {
@@ -342,7 +474,7 @@ protected boolean generateInfos(OpenableElementInfo info, IProgressMonitor pm, M
                if (isWorkingCopy()) {
                        CompilationUnit original = (CompilationUnit) getOriginalElement();
                        // might be IResource.NULL_STAMP if original does not exist
-                       unitInfo.fTimestamp = ((IFile) original.getResource()).getModificationStamp();
+                       unitInfo.timestamp = ((IFile) original.getResource()).getModificationStamp();
                }
                return unitInfo.isStructureKnown();
 //     }
@@ -414,6 +546,12 @@ public IJavaElement getElementAt(int position) throws JavaModelException {
                return e;
        }
 }
+/**
+ * @see IJavaElement
+ */
+public int getElementType() {
+       return COMPILATION_UNIT;
+}
 public char[] getFileName(){
        return getElementName().toCharArray();
 }
@@ -463,21 +601,36 @@ public char[] getMainTypeName(){
        name= name.substring(0, name.length() - 5);
        return name.toCharArray();
 }
+
 /**
- * Returns <code>null</code>, this is not a working copy.
- *
  * @see IWorkingCopy#getOriginal(IJavaElement)
+ * @deprecated
  */
 public IJavaElement getOriginal(IJavaElement workingCopyElement) {
-       return null;
+       // backward compatibility
+       if (!isWorkingCopy()) return null;
+       CompilationUnit cu = (CompilationUnit)workingCopyElement.getAncestor(COMPILATION_UNIT);
+       if (cu == null || !this.owner.equals(cu.owner)) {
+               return null;
+       }
+       
+       return workingCopyElement.getPrimaryElement();
 }
 /**
- * Returns <code>null</code>, this is not a working copy.
- *
  * @see IWorkingCopy#getOriginalElement()
+ * @deprecated
  */
 public IJavaElement getOriginalElement() {
-       return null;
+       // backward compatibility
+       if (!isWorkingCopy()) return null;
+       
+       return getPrimaryElement();
+}
+/*
+ * @see ICompilationUnit#getOwner()
+ */
+public WorkingCopyOwner getOwner() {
+       return isPrimary() || !isWorkingCopy() ? null : this.owner;
 }
 /**
  * @see ICompilationUnit#getPackageDeclaration(String)
@@ -511,6 +664,27 @@ public IPath getPath() {
                return this.getParent().getPath().append(this.getElementName());
        }
 }
+/*
+ * Returns the per working copy info for the receiver, or null if none exist.
+ * Note: the use count of the per working copy info is NOT incremented.
+ */
+public JavaModelManager.PerWorkingCopyInfo getPerWorkingCopyInfo() {
+       return JavaModelManager.getJavaModelManager().getPerWorkingCopyInfo(this, false/*don't create*/, false/*don't record usage*/, null/*no problem requestor needed*/);
+}
+/*
+ * @see ICompilationUnit#getPrimary()
+ */
+public ICompilationUnit getPrimary() {
+       return (ICompilationUnit)getPrimaryElement(true);
+}
+/* 
+ * @see JavaElement#getPrimaryElement(boolean)
+ */
+public IJavaElement getPrimaryElement(boolean checkOwner) {
+       if (checkOwner && isPrimary()) return this;
+       return new CompilationUnit((PackageFragment)getParent(), getElementName(), DefaultWorkingCopyOwner.PRIMARY);
+}
+
 /**
  * @see IJavaElement#getResource()
  */
@@ -552,70 +726,113 @@ public IType[] getTypes() throws JavaModelException {
        list.toArray(array);
        return array;
 }
+/**
+ * @see IJavaElement
+ */
 public IResource getUnderlyingResource() throws JavaModelException {
-       if (FIX_BUG25184) {
-               return super.getUnderlyingResource();
-       } else {
-               return getResource();
-       }
+       if (isWorkingCopy() && !isPrimary()) return null;
+       return super.getUnderlyingResource();
 }
+///**
+// * @see IWorkingCopy#getSharedWorkingCopy(IProgressMonitor, IBufferFactory, IProblemRequestor)
+// */
+//public IJavaElement getSharedWorkingCopy(IProgressMonitor pm, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
+//     
+//     // if factory is null, default factory must be used
+//     if (factory == null) factory = this.getBufferManager().getDefaultBufferFactory();
+//
+//     JavaModelManager manager = JavaModelManager.getJavaModelManager();
+//     
+//     // 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(factory);
+//     if (perFactoryWorkingCopies == null){
+//             perFactoryWorkingCopies = new HashMap();
+//             sharedWorkingCopies.put(factory, perFactoryWorkingCopies);
+//     }
+//     WorkingCopy workingCopy = (WorkingCopy)perFactoryWorkingCopies.get(this);
+//     if (workingCopy != null) {
+//             workingCopy.useCount++;
+//
+//             if (SHARED_WC_VERBOSE) {
+//                     System.out.println("Incrementing use count of shared working copy " + workingCopy.toStringWithAncestors()); //$NON-NLS-1$
+//             }
+//
+//             return workingCopy;
+//     } else {
+//             CreateWorkingCopyOperation op = new CreateWorkingCopyOperation(this, perFactoryWorkingCopies, factory, problemRequestor);
+//             runOperation(op, pm);
+//             return op.getResultElements()[0];
+//     }
+//}
+///**
+// * @see IWorkingCopy#getWorkingCopy()
+// */
+//public IJavaElement getWorkingCopy() throws JavaModelException {
+//     return this.getWorkingCopy(null, null, null);
+//}
+//
+///**
+// * @see IWorkingCopy#getWorkingCopy(IProgressMonitor, IBufferFactory, IProblemRequestor)
+// */
+//public IJavaElement getWorkingCopy(IProgressMonitor pm, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
+//     CreateWorkingCopyOperation op = new CreateWorkingCopyOperation(this, null, factory, problemRequestor);
+//     runOperation(op, pm);
+//     return op.getResultElements()[0];
+//}
 /**
  * @see IWorkingCopy#getSharedWorkingCopy(IProgressMonitor, IBufferFactory, IProblemRequestor)
+ * @deprecated
  */
 public IJavaElement getSharedWorkingCopy(IProgressMonitor pm, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
        
        // if factory is null, default factory must be used
        if (factory == null) factory = this.getBufferManager().getDefaultBufferFactory();
-
-       JavaModelManager manager = JavaModelManager.getJavaModelManager();
        
-       // 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(factory);
-       if (perFactoryWorkingCopies == null){
-               perFactoryWorkingCopies = new HashMap();
-               sharedWorkingCopies.put(factory, perFactoryWorkingCopies);
-       }
-       WorkingCopy workingCopy = (WorkingCopy)perFactoryWorkingCopies.get(this);
-       if (workingCopy != null) {
-               workingCopy.useCount++;
-
-               if (SHARED_WC_VERBOSE) {
-                       System.out.println("Incrementing use count of shared working copy " + workingCopy.toStringWithAncestors()); //$NON-NLS-1$
-               }
-
-               return workingCopy;
-       } else {
-               CreateWorkingCopyOperation op = new CreateWorkingCopyOperation(this, perFactoryWorkingCopies, factory, problemRequestor);
-               runOperation(op, pm);
-               return op.getResultElements()[0];
-       }
+       return getWorkingCopy(BufferFactoryWrapper.create(factory), problemRequestor, pm);
 }
 /**
  * @see IWorkingCopy#getWorkingCopy()
+ * @deprecated
  */
 public IJavaElement getWorkingCopy() throws JavaModelException {
-       return this.getWorkingCopy(null, null, null);
+       return getWorkingCopy(null);
+}
+/**
+ * @see ICompilationUnit#getWorkingCopy(IProgressMonitor)
+ */
+public ICompilationUnit getWorkingCopy(IProgressMonitor monitor) throws JavaModelException {
+       return getWorkingCopy(new WorkingCopyOwner() {/*non shared working copy*/}, null/*no problem requestor*/, monitor);
 }
-
 /**
  * @see IWorkingCopy#getWorkingCopy(IProgressMonitor, IBufferFactory, IProblemRequestor)
+ * @deprecated
  */
-public IJavaElement getWorkingCopy(IProgressMonitor pm, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
-       CreateWorkingCopyOperation op = new CreateWorkingCopyOperation(this, null, factory, problemRequestor);
-       runOperation(op, pm);
-       return op.getResultElements()[0];
+public IJavaElement getWorkingCopy(IProgressMonitor monitor, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
+       return getWorkingCopy(BufferFactoryWrapper.create(factory), problemRequestor, monitor);
 }
-
 /**
- * @see Openable#hasBuffer()
+ * @see ICompilationUnit#getWorkingCopy(WorkingCopyOwner, IProblemRequestor, IProgressMonitor)
  */
-protected boolean hasBuffer() {
-       return true;
+public ICompilationUnit getWorkingCopy(WorkingCopyOwner workingCopyOwner, IProblemRequestor problemRequestor, IProgressMonitor monitor) throws JavaModelException {
+       if (!isPrimary()) return this;
+       
+       JavaModelManager manager = JavaModelManager.getJavaModelManager();
+       
+       CompilationUnit workingCopy = new CompilationUnit((PackageFragment)getParent(), getElementName(), workingCopyOwner);
+       JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = 
+               manager.getPerWorkingCopyInfo(workingCopy, false/*don't create*/, true/*record usage*/, null/*not used since don't create*/);
+       if (perWorkingCopyInfo != null) {
+               return perWorkingCopyInfo.getWorkingCopy(); // return existing handle instead of the one created above
+       }
+       BecomeWorkingCopyOperation op = new BecomeWorkingCopyOperation(workingCopy, problemRequestor);
+       op.runOperation(monitor);
+       return workingCopy;
 }
+
 /**
  * If I am not open, return true to avoid parsing.
  *
@@ -630,41 +847,120 @@ public boolean hasChildren() throws JavaModelException {
   return false;
 }
 /**
- * Returns false, this is not a working copy.
- *
+ * @see Openable#hasBuffer()
+ */
+protected boolean hasBuffer() {
+       return true;
+}
+/*
+ * @see ICompilationUnit#hasResourceChanged()
+ */
+public boolean hasResourceChanged() {
+       if (!isWorkingCopy()) return false;
+       
+       // if resource got deleted, then #getModificationStamp() will answer IResource.NULL_STAMP, which is always different from the cached
+       // timestamp
+       Object info = JavaModelManager.getJavaModelManager().getInfo(this);
+       if (info == null) return false;
+       return ((CompilationUnitElementInfo)info).timestamp != getResource().getModificationStamp();
+}
+/**
  * @see IWorkingCopy#isBasedOn(IResource)
+ * @deprecated
  */
 public boolean isBasedOn(IResource resource) {
-       return false;
+       if (!isWorkingCopy()) return false;
+       if (!getResource().equals(resource)) return false;
+       return !hasResourceChanged();
 }
 /**
  * @see IOpenable#isConsistent()
  */
-public boolean isConsistent() throws JavaModelException {
+public boolean isConsistent() {
        return JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().get(this) == null;
 }
+
+/**
+ * 
+ * @see IOpenable
+ */
+public boolean isOpen() {
+       Object info = JavaModelManager.getJavaModelManager().getInfo(this);
+       return info != null && ((CompilationUnitElementInfo)info).isOpen();
+}
+public boolean isPrimary() {
+       return this.owner == DefaultWorkingCopyOwner.PRIMARY;
+}
 /**
  * @see Openable#isSourceElement()
  */
 protected boolean isSourceElement() {
        return true;
 }
-/**
- * @see IWorkingCopy#isWorkingCopy()
+protected boolean isValidCompilationUnit() {
+       IPackageFragmentRoot root = getPackageFragmentRoot();
+       try {
+               if (root.getKind() != IPackageFragmentRoot.K_SOURCE) return false;
+       } catch (JavaModelException e) {
+               return false;
+       }
+//     IResource resource = getResource();
+//     if (resource != null) {
+//             char[][] inclusionPatterns = ((PackageFragmentRoot)root).fullInclusionPatternChars();
+//             char[][] exclusionPatterns = ((PackageFragmentRoot)root).fullExclusionPatternChars();
+//             if (Util.isExcluded(resource, inclusionPatterns, exclusionPatterns)) return false;
+//     }
+       if (!Util.isValidCompilationUnitName(getElementName())) return false;
+       return true;
+}
+/*
+ * @see ICompilationUnit#isWorkingCopy()
  */
 public boolean isWorkingCopy() {
-       return false;
+       // For backward compatibility, non primary working copies are always returning true; in removal
+       // delta, clients can still check that element was a working copy before being discarded.
+       return !isPrimary() || getPerWorkingCopyInfo() != null;
 }
 /**
  * @see IOpenable#makeConsistent(IProgressMonitor)
  */
 public void makeConsistent(IProgressMonitor monitor) throws JavaModelException {
-       if (!isConsistent()) { // TODO: this code isn't synchronized with regular opening of a working copy
-               // create a new info and make it the current info
-               OpenableElementInfo info = createElementInfo();
-               buildStructure(info, monitor);
-       }
+       makeConsistent(false/*don't create AST*/, 0, monitor);
+}
+public Object makeConsistent(boolean createAST, int astLevel, IProgressMonitor monitor) throws JavaModelException {
+       if (isConsistent()) return null;
+               
+       // create a new info and make it the current info
+       // (this will remove the info and its children just before storing the new infos)
+//     if (createAST) {
+//             ASTHolderCUInfo info = new ASTHolderCUInfo();
+//             info.astLevel = astLevel;
+//             openWhenClosed(info, monitor);
+//             org.eclipse.jdt.core.dom.CompilationUnit result = info.ast;
+//             info.ast = null;
+//             return result;
+//     } else {
+               openWhenClosed(createElementInfo(), monitor);
+               return null;
+//     }
 }
+//public net.sourceforge.phpdt.core.dom.CompilationUnit makeConsistent(boolean createAST, int astLevel, IProgressMonitor monitor) throws JavaModelException {
+//     if (isConsistent()) return null;
+//             
+//     // create a new info and make it the current info
+//     // (this will remove the info and its children just before storing the new infos)
+//     if (createAST) {
+//             ASTHolderCUInfo info = new ASTHolderCUInfo();
+//             info.astLevel = astLevel;
+//             openWhenClosed(info, monitor);
+//             net.sourceforge.phpdt.core.dom.CompilationUnit result = info.ast;
+//             info.ast = null;
+//             return result;
+//     } else {
+//             openWhenClosed(createElementInfo(), monitor);
+//             return null;
+//     }
+//}
 
 /**
  * @see ISourceManipulation#move(IJavaElement, IJavaElement, String, boolean, IProgressMonitor)
@@ -683,24 +979,69 @@ public void move(IJavaElement container, IJavaElement sibling, String rename, bo
        getJavaModel().move(elements, containers, null, renamings, force, monitor);
 }
 
+///**
+// * @see Openable#openBuffer(IProgressMonitor)
+// */
+//protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
+//
+//     // create buffer -  compilation 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){
+//             IFile file = (IFile)this.getResource();
+//             if (file == null || !file.exists()) throw newNotPresentException();
+//             buffer.setContents(Util.getResourceContentsAsCharArray(file));
+//     }
+//
+//     // add buffer to buffer cache
+//     bufManager.addBuffer(buffer);
+//                     
+//     // listen to buffer changes
+//     buffer.addBufferChangedListener(this);
+//     
+//     return buffer;
+//}
 /**
- * @see Openable#openBuffer(IProgressMonitor)
+ * @see Openable#openBuffer(IProgressMonitor, Object)
  */
-protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
+protected IBuffer openBuffer(IProgressMonitor pm, Object info) throws JavaModelException {
 
-       // create buffer -  compilation units only use default buffer factory
-       BufferManager bufManager = getBufferManager();
-       IBuffer buffer = getBufferFactory().createBuffer(this);
+       // create buffer
+       boolean isWorkingCopy = isWorkingCopy();
+       IBuffer buffer = 
+               isWorkingCopy 
+                       ? this.owner.createBuffer(this) 
+                       : BufferManager.getDefaultBufferManager().createBuffer(this);
        if (buffer == null) return null;
        
        // set the buffer source
-       if (buffer.getCharacters() == null){
-               IFile file = (IFile)this.getResource();
-               if (file == null || !file.exists()) throw newNotPresentException();
-               buffer.setContents(Util.getResourceContentsAsCharArray(file));
+       if (buffer.getCharacters() == null) {
+               if (isWorkingCopy) {
+                       ICompilationUnit original;
+                       if (!isPrimary() 
+                                       && (original = new CompilationUnit((PackageFragment)getParent(), getElementName(), DefaultWorkingCopyOwner.PRIMARY)).isOpen()) {
+                               buffer.setContents(original.getSource());
+                       } else {
+                               IFile file = (IFile)getResource();
+                               if (file == null || !file.exists()) {
+                                       // initialize buffer with empty contents
+                                       buffer.setContents(CharOperation.NO_CHAR);
+                               } else {
+                                       buffer.setContents(Util.getResourceContentsAsCharArray(file));
+                               }
+                       }
+               } else {
+                       IFile file = (IFile)this.getResource();
+                       if (file == null || !file.exists()) throw newNotPresentException();
+                       buffer.setContents(Util.getResourceContentsAsCharArray(file));
+               }
        }
 
        // add buffer to buffer cache
+       BufferManager bufManager = getBufferManager();
        bufManager.addBuffer(buffer);
                        
        // listen to buffer changes
@@ -708,44 +1049,67 @@ protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
        
        return buffer;
 }
-protected void openParent(IProgressMonitor pm) throws JavaModelException {
-       if (FIX_BUG25184) {
-               super.openParent(pm);
-       } else {
-               try {
-                       super.openParent(pm);
-               } catch(JavaModelException e){
-                       // allow parent to not exist for compilation units defined outside classpath
-                       if (!e.isDoesNotExist()){ 
-                               throw e;
-                       }
+/*
+ * @see Openable#openParent
+ */
+protected void openParent(Object childInfo, HashMap newElements, IProgressMonitor pm) throws JavaModelException {
+       try {
+               super.openParent(childInfo, newElements, pm);
+       } catch(JavaModelException e){
+               // allow parent to not exist for working copies defined outside classpath
+               if (!isWorkingCopy() && !e.isDoesNotExist()){ 
+                       throw e;
                }
        }
 }
-protected boolean parentExists() {
-       if (FIX_BUG25184) {
-               return super.parentExists();
-       } else {
-               return true; // tolerate units outside classpath
-       }
-}
 
 /**
- * @see IWorkingCopy#reconcile()
+ * @see ICompilationUnit#reconcile()
+ * @deprecated
  */
 public IMarker[] reconcile() throws JavaModelException {
-       // Reconciling is not supported on non working copies
+       reconcile(NO_AST, false/*don't force problem detection*/, null/*use primary owner*/, null/*no progress monitor*/);
        return null;
 }
+/**
+ * @see ICompilationUnit#reconcile(int, boolean, WorkingCopyOwner, IProgressMonitor)
+ */
+public void reconcile(boolean forceProblemDetection, IProgressMonitor monitor) throws JavaModelException {
+       reconcile(NO_AST, forceProblemDetection, null/*use primary owner*/, monitor);
+}
 
 /**
- * @see IWorkingCopy#reconcile(boolean, IProgressMonitor)
+ * @see ICompilationUnit#reconcile(int, boolean, WorkingCopyOwner, IProgressMonitor)
+ * @since 3.0
  */
-public void reconcile(
+//public org.eclipse.jdt.core.dom.CompilationUnit reconcile(
+  public Object reconcile(
+       int astLevel,
        boolean forceProblemDetection,
+       WorkingCopyOwner workingCopyOwner,
        IProgressMonitor monitor)
        throws JavaModelException {
-       // Reconciling is not supported on non working copies
+       
+       if (!isWorkingCopy()) return null; // Reconciling is not supported on non working copies
+       if (workingCopyOwner == null) workingCopyOwner = DefaultWorkingCopyOwner.PRIMARY;
+       
+       boolean createAST = false;
+//     if (astLevel == AST.JLS2) {
+//             // client asking for level 2 AST; these are supported
+//             createAST = true;
+//     } else if (astLevel == AST.JLS3) {
+//             // client asking for level 3 ASTs; these are not supported
+//             // TODO (jerome) - these should also be supported in 1.5 stream
+//             createAST = false;
+//     } else {
+//             // client asking for no AST (0) or unknown ast level
+//             // either way, request denied
+//             createAST = false;
+//     }
+       ReconcileWorkingCopyOperation op = new ReconcileWorkingCopyOperation(this, createAST, astLevel, forceProblemDetection, workingCopyOwner);
+       op.runOperation(monitor);
+//     return op.ast;
+       return null;
 }
 
 /**
@@ -836,14 +1200,25 @@ public void restore () throws JavaModelException {
 //                     }
 //             });
 //}
-/**
- * @see JavaElement#rootedAt(IJavaProject)
+///**
+// * @see JavaElement#rootedAt(IJavaProject)
+// */
+//public IJavaElement rootedAt(IJavaProject project) {
+//     return
+//             new CompilationUnit(
+//                     (IPackageFragment)((JavaElement)parent).rootedAt(project), 
+//                     name);
+//}
+/*
+ * Assume that this is a working copy
  */
-public IJavaElement rootedAt(IJavaProject project) {
-       return
-               new CompilationUnit(
-                       (IPackageFragment)((JavaElement)fParent).rootedAt(project), 
-                       fName);
+protected void updateTimeStamp(CompilationUnit original) throws JavaModelException {
+       long timeStamp =
+               ((IFile) original.getResource()).getModificationStamp();
+       if (timeStamp == IResource.NULL_STAMP) {
+               throw new JavaModelException(
+                       new JavaModelStatus(IJavaModelStatusConstants.INVALID_RESOURCE));
+       }
+       ((CompilationUnitElementInfo) getElementInfo()).timestamp = timeStamp;
 }
-
 }