new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / WorkingCopy.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12  
13 import java.io.ByteArrayInputStream;
14 import java.io.UnsupportedEncodingException;
15 import java.util.ArrayList;
16
17 import net.sourceforge.phpdt.core.IBuffer;
18 import net.sourceforge.phpdt.core.IBufferFactory;
19 import net.sourceforge.phpdt.core.ICompilationUnit;
20 import net.sourceforge.phpdt.core.IJavaElement;
21 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
22 import net.sourceforge.phpdt.core.IJavaProject;
23 import net.sourceforge.phpdt.core.IMethod;
24 import net.sourceforge.phpdt.core.IPackageFragment;
25 import net.sourceforge.phpdt.core.IProblemRequestor;
26 import net.sourceforge.phpdt.core.IType;
27 import net.sourceforge.phpdt.core.JavaModelException;
28 import net.sourceforge.phpeclipse.PHPCore;
29
30 import org.eclipse.core.resources.IFile;
31 import org.eclipse.core.resources.IMarker;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.IProgressMonitor;
35
36
37
38 /**
39  * Implementation of a working copy compilation unit. A working
40  * copy maintains the timestamp of the resource it was created
41  * from.
42  */
43
44 public class WorkingCopy extends CompilationUnit {
45
46         /**
47          * If set, this is the factory that will be used to create the buffer.
48          */
49         protected IBufferFactory bufferFactory;
50
51         /**
52          * If set, this is the problem requestor which will be used to notify problems
53          * detected during reconciling.
54          */
55         protected IProblemRequestor problemRequestor;
56                 
57         /**
58          * A counter of the number of time clients have asked for this 
59          * working copy. It is set to 1, if the working
60          * copy is not managed. When destroyed, this counter is
61          * set to 0. Once destroyed, this working copy cannot be opened
62          * and non-handle info can not be accessed. This is
63          * never true if this compilation unit is not a working
64          * copy.
65          */
66         protected int useCount = 1;
67         
68 /**
69  */
70 protected WorkingCopy(IPackageFragment parent, String name, IBufferFactory bufferFactory) {
71         this(parent, name, bufferFactory, null);
72 }
73 /**
74  */
75 protected WorkingCopy(IPackageFragment parent, String name, IBufferFactory bufferFactory, IProblemRequestor problemRequestor) {
76         super(parent, name);
77         this.bufferFactory = 
78                 bufferFactory == null ? 
79                         this.getBufferManager().getDefaultBufferFactory() :
80                         bufferFactory;
81         this.problemRequestor = problemRequestor;
82 }
83 /**
84  * @see IWorkingCopy
85  */
86 public void commit(boolean force, IProgressMonitor monitor) throws JavaModelException {
87         ICompilationUnit original = (ICompilationUnit)this.getOriginalElement();
88         if (original.exists()) {
89                 CommitWorkingCopyOperation op= new CommitWorkingCopyOperation(this, force);
90                 runOperation(op, monitor);
91         } else {
92                 String encoding = this.getJavaProject().getOption(PHPCore.CORE_ENCODING, true);
93                 String contents = this.getSource();
94                 if (contents == null) return;
95                 try {
96                         byte[] bytes = encoding == null 
97                                 ? contents.getBytes() 
98                                 : contents.getBytes(encoding);
99                         ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
100                         IFile originalRes = (IFile)original.getResource();
101                         if (originalRes.exists()) {
102                                 originalRes.setContents(
103                                         stream, 
104                                         force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, 
105                                         null);
106                         } else {
107                                 originalRes.create(
108                                         stream,
109                                         force,
110                                         monitor);
111                         }
112                 } catch (CoreException e) {
113                         throw new JavaModelException(e);
114                 } catch (UnsupportedEncodingException e) {
115                         throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
116                 }
117         }
118 }
119 /**
120  * Returns a new element info for this element.
121  */
122 protected OpenableElementInfo createElementInfo() {
123         return new WorkingCopyElementInfo();
124 }
125 /**
126  * @see IWorkingCopy
127  */
128 public void destroy() {
129         if (--this.useCount > 0) {
130                 if (SHARED_WC_VERBOSE) {
131                         System.out.println("Decrementing use count of shared working copy " + this.toStringWithAncestors());//$NON-NLS-1$
132                 }
133                 return;
134         }
135         try {
136                 DestroyWorkingCopyOperation op = new DestroyWorkingCopyOperation(this);
137                 runOperation(op, null);
138         } catch (JavaModelException e) {
139                 // do nothing
140         }
141 }
142
143 public boolean exists() {
144         // working copy always exists in the model until it is detroyed
145         return this.useCount != 0;
146 }
147
148
149 /**
150  * Answers custom buffer factory
151  */
152 public IBufferFactory getBufferFactory(){
153
154         return this.bufferFactory;
155 }
156
157 /**
158  * Working copies must be identical to be equal.
159  *
160  * @see Object#equals
161  */
162 public boolean equals(Object o) {
163         return this == o; 
164 }
165
166         /**
167          * Returns the info for this handle.  
168          * If this element is not already open, it and all of its parents are opened.
169          * Does not return null.
170          * NOTE: BinaryType infos are NJOT rooted under JavaElementInfo.
171          * @exception JavaModelException if the element is not present or not accessible
172          */
173 //      public Object getElementInfo() throws JavaModelException {
174 //
175 //              JavaModelManager manager = JavaModelManager.getJavaModelManager();
176 //              boolean shouldPerformProblemDetection = false;
177 //              synchronized(manager){
178 //                      Object info = manager.getInfo(this);
179 //                      if (info == null) {
180 //                              shouldPerformProblemDetection = true;
181 //                      }
182 //              }
183 //              Object info = super.getElementInfo(); // will populate if necessary
184 //
185 //              // perform problem detection outside the JavaModelManager lock
186 //              if (this.problemRequestor != null && shouldPerformProblemDetection && this.problemRequestor.isActive()){
187 //                      this.problemRequestor.beginReporting();
188 //                      CompilationUnitProblemFinder.process(this, this.problemRequestor, null); 
189 //                      this.problemRequestor.endReporting();
190 //              }               
191 //              return info;
192 //      }
193 /**
194  * @see IWorkingCopy
195  */
196 public IJavaElement getOriginal(IJavaElement workingCopyElement) {
197         //not a element contained in a compilation unit
198         int javaElementType = workingCopyElement.getElementType();
199         if (javaElementType < COMPILATION_UNIT || javaElementType == CLASS_FILE) {
200                 return null;
201         }
202 //      if (workingCopyElement instanceof BinaryMember) {
203 //              return null;
204 //      }
205         IJavaElement parent = workingCopyElement.getParent();
206         ArrayList hierarchy = new ArrayList(4);
207         
208         while (parent.getElementType() > COMPILATION_UNIT) {
209                 hierarchy.add(parent);
210                 parent = parent.getParent();
211         }
212         if (parent.getElementType() == COMPILATION_UNIT) {
213                 hierarchy.add(((ICompilationUnit)parent).getOriginalElement());
214         }
215         
216         ICompilationUnit cu = (ICompilationUnit) getOriginalElement();
217         if (javaElementType == COMPILATION_UNIT) {
218                 parent = workingCopyElement;
219         }
220         if (((ICompilationUnit) parent).isWorkingCopy() && !((ICompilationUnit) parent).getOriginalElement().equals(cu)) {
221                 return null;
222         }
223         IType type=null;
224         switch (javaElementType) {
225 //              case PACKAGE_DECLARATION :
226 //                      return cu.getPackageDeclaration(workingCopyElement.getElementName());
227 //              case IMPORT_CONTAINER :
228 //                      return cu.getImportContainer();
229 //              case IMPORT_DECLARATION :
230 //                      return cu.getImport(workingCopyElement.getElementName());
231 //              case TYPE :
232 //                      if (hierarchy.size() == 1) {
233 //                              return cu.getType(workingCopyElement.getElementName());
234 //                      } else {
235 //                              //inner type
236 //                              return getOriginalType(hierarchy).getType(workingCopyElement.getElementName());
237 //                      }
238                 case METHOD :
239                         
240                         if (hierarchy.size() == 2) {
241                                 String typeName = ((IJavaElement) hierarchy.get(0)).getElementName();
242                                 type = cu.getType(typeName);
243 //                      } else {
244 //                              //inner type
245 //                              type = getOriginalType(hierarchy);
246                         }
247                         return type.getMethod(workingCopyElement.getElementName(), ((IMethod) workingCopyElement).getParameterTypes());
248                 case FIELD :
249                         if (hierarchy.size() == 2) {
250                                 String typeName = ((IJavaElement) hierarchy.get(0)).getElementName();
251                                 type = cu.getType(typeName);
252 //                      } else {
253 //                              //inner type
254 //                              type = getOriginalType(hierarchy);
255                         }
256                         return type.getField(workingCopyElement.getElementName());
257 //              case INITIALIZER :
258 //                      if (hierarchy.size() == 2) {
259 //                              String typeName = ((IJavaElement) hierarchy.get(0)).getElementName();
260 //                              type = cu.getType(typeName);
261 //                      } else {
262 //                              //inner type
263 //                              type = getOriginalType(hierarchy);
264 //                      }
265 //                      return type.getInitializer(((Initializer) workingCopyElement).getOccurrenceCount());
266                 case COMPILATION_UNIT :
267                         return cu;
268                 default :
269                         return null;
270         }
271 }
272 /**
273  * @see IWorkingCopy
274  */
275 public IJavaElement getOriginalElement() {
276         return new CompilationUnit((IPackageFragment)getParent(), getElementName());
277 }
278 //protected IType getOriginalType(ArrayList hierarchy) {
279 //      int size = hierarchy.size() - 1;
280 //      ICompilationUnit typeCU = (ICompilationUnit) hierarchy.get(size);
281 //      String typeName = ((IJavaElement) hierarchy.get(size - 1)).getElementName();
282 //      IType type = typeCU.getType(typeName);
283 //      size= size - 2;
284 //      while (size > -1) {
285 //              typeName = ((IJavaElement) hierarchy.get(size)).getElementName();
286 //              type = ((IType) type).getType(typeName);
287 //              size--;
288 //      }
289 //      return type;
290 //}
291
292 /*
293  * @see IJavaElement
294  */
295 public IResource getResource() {
296         return null;
297 }
298
299 /**
300  * @see IWorkingCopy
301  */
302 public IJavaElement getSharedWorkingCopy(IProgressMonitor monitor, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
303         return this;
304 }
305 /**
306  * Returns <code>null<code> - a working copy does not have an underlying resource.
307  *
308  * @see IJavaElement
309  */
310 public IResource getUnderlyingResource() throws JavaModelException {
311         return null;
312 }
313 /**
314  * @see IWorkingCopy
315  */
316 public IJavaElement getWorkingCopy() throws JavaModelException {
317         return this;
318 }
319 /**
320  * @see IWorkingCopy
321  */
322 public IJavaElement getWorkingCopy(IProgressMonitor monitor, IBufferFactory factory, IProblemRequestor problemRequestor) throws JavaModelException {
323         return this;
324 }
325 /**
326  * @see IWorkingCopy
327  */
328 public boolean isBasedOn(IResource resource) {
329         if (resource.getType() != IResource.FILE) {
330                 return false;
331         }
332         if (this.useCount == 0) {
333                 return false;
334         }
335         try {
336                 // if resource got deleted, then #getModificationStamp() will answer IResource.NULL_STAMP, which is always different from the cached
337                 // timestamp
338                 return ((CompilationUnitElementInfo) getElementInfo()).fTimestamp == ((IFile) resource).getModificationStamp();
339         } catch (JavaModelException e) {
340                 return false;
341         }
342 }
343 /**
344  * @see IWorkingCopy
345  */
346 public boolean isWorkingCopy() {
347         return true;
348 }
349
350 /**
351  * @see IOpenable#makeConsistent(IProgressMonitor)
352  */
353 //public void makeConsistent(IProgressMonitor monitor) throws JavaModelException {
354 //      if (!isConsistent()) { // TODO: this code isn't synchronized with regular opening of a working copy (should use getElementInfo)
355 //              super.makeConsistent(monitor);
356 //
357 //              if (monitor != null && monitor.isCanceled()) return;
358 //              if (this.problemRequestor != null && this.problemRequestor.isActive()){
359 //                      this.problemRequestor.beginReporting();
360 //                      CompilationUnitProblemFinder.process(this, this.problemRequestor, monitor); 
361 //                      this.problemRequestor.endReporting();
362 //              }               
363 //      }
364 //}
365
366 /**
367  * @see IOpenable
368  * @see IWorkingCopy
369  *
370  * @exception JavaModelException attempting to open a read only element for something other than navigation
371  *      or if this is a working copy being opened after it has been destroyed.
372  */
373 public void open(IProgressMonitor monitor) throws JavaModelException {
374         if (this.useCount == 0) { // was destroyed
375                 throw newNotPresentException();
376         } else {
377                 super.open(monitor);
378                 
379                 if (monitor != null && monitor.isCanceled()) return;
380                 if (this.problemRequestor != null && this.problemRequestor.isActive()){
381                         this.problemRequestor.beginReporting();
382                         CompilationUnitProblemFinder.process(this, this.problemRequestor, monitor); 
383                         this.problemRequestor.endReporting();
384                 }               
385         }
386 }
387 /**
388  * @see Openable
389  */
390 //protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
391 //
392 //      if (this.useCount == 0) throw newNotPresentException(); // was destroyed
393 //      
394 //      // create buffer - working copies may use custom buffer factory
395 //      IBuffer buffer = getBufferFactory().createBuffer(this);
396 //      if (buffer == null) return null;
397 //
398 //      // set the buffer source if needed
399 //      if (buffer.getCharacters() == null) {
400 //              ICompilationUnit original = (ICompilationUnit)this.getOriginalElement();
401 //              if (original.isOpen()) {
402 //                      buffer.setContents(original.getSource());
403 //              } else {
404 //                      IFile file = (IFile)original.getResource();
405 //                      if (file == null || !file.exists()) {
406 //                              // initialize buffer with empty contents
407 //                              buffer.setContents(CharOperation.NO_CHAR);
408 //                      } else {
409 //                              buffer.setContents(Util.getResourceContentsAsCharArray(file));
410 //                      }
411 //              }
412 //      }
413 //
414 //      // add buffer to buffer cache
415 //      this.getBufferManager().addBuffer(buffer);
416 //
417 //      // listen to buffer changes
418 //      buffer.addBufferChangedListener(this);
419 //
420 //      return buffer;  
421 //}
422 /*
423  * @see Openable#openParent(IProgressMonitor)
424  */
425 protected void openParent(IProgressMonitor pm) throws JavaModelException {
426 //      if (FIX_BUG25184) {
427 //              try {
428 //                      super.openParent(pm);
429 //              } catch(JavaModelException e){
430 //                      // allow parent to not exist for working copies defined outside classpath
431 //                      if (!e.isDoesNotExist()){ 
432 //                              throw e;
433 //                      }
434 //              }
435 //      } else {
436                 super.openParent(pm);
437 //      }
438 }
439
440 /**
441  * @see IWorkingCopy
442  */ 
443 public IMarker[] reconcile() throws JavaModelException {
444         reconcile(false, null);
445         return null;
446 }
447
448 /**
449  * @see IWorkingCopy
450  */ 
451 public void reconcile(boolean forceProblemDetection, IProgressMonitor monitor) throws JavaModelException {
452         ReconcileWorkingCopyOperation op = new ReconcileWorkingCopyOperation(this, forceProblemDetection);
453         runOperation(op, monitor);
454 }
455
456 /**
457  * @see IWorkingCopy
458  */
459 public void restore() throws JavaModelException {
460
461         if (this.useCount == 0) throw newNotPresentException(); //was destroyed
462
463         CompilationUnit original = (CompilationUnit) getOriginalElement();
464         IBuffer buffer = this.getBuffer();
465         if (buffer == null) return;
466         buffer.setContents(original.getContents());
467         updateTimeStamp(original);
468         makeConsistent(null);
469 }
470 /*
471  * @see JavaElement#rootedAt(IJavaProject)
472  */
473 public IJavaElement rootedAt(IJavaProject project) {
474         return
475                 new WorkingCopy(
476                         (IPackageFragment)((JavaElement)fParent).rootedAt(project), 
477                         fName,
478                         this.bufferFactory);
479
480 }
481 /**
482  * @see IOpenable
483  */
484 public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
485         if (isReadOnly()) {
486                 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.READ_ONLY, this));
487         }
488         // no need to save the buffer for a working copy (this is a noop)
489         //IBuffer buf = getBuffer();
490         //if (buf != null) { // some Openables (like a JavaProject) don't have a buffer
491         //      buf.save(pm, force);
492                 this.reconcile();   // not simply makeConsistent, also computes fine-grain deltas
493                                                         // in case the working copy is being reconciled already (if not it would miss
494                                                         // one iteration of deltas).
495         //}
496 }
497
498 /**
499  * @private Debugging purposes
500  */
501 protected void toStringInfo(int tab, StringBuffer buffer, Object info) {
502         buffer.append(this.tabString(tab));
503         buffer.append("[Working copy] "); //$NON-NLS-1$
504         super.toStringInfo(0, buffer, info);
505 }
506 protected void updateTimeStamp(CompilationUnit original) throws JavaModelException {
507         long timeStamp =
508                 ((IFile) original.getResource()).getModificationStamp();
509         if (timeStamp == IResource.NULL_STAMP) {
510                 throw new JavaModelException(
511                         new JavaModelStatus(IJavaModelStatusConstants.INVALID_RESOURCE));
512         }
513         ((CompilationUnitElementInfo) getElementInfo()).fTimestamp = timeStamp;
514 }
515 }