79481b118feb0b6094ba7fa9b047baa4239d05ba
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / Openable.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.util.Enumeration;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.Map;
17
18 import net.sourceforge.phpdt.core.BufferChangedEvent;
19 import net.sourceforge.phpdt.core.IBuffer;
20 import net.sourceforge.phpdt.core.IBufferChangedListener;
21 import net.sourceforge.phpdt.core.IBufferFactory;
22 import net.sourceforge.phpdt.core.IJavaElement;
23 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
24 import net.sourceforge.phpdt.core.IOpenable;
25 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
26 import net.sourceforge.phpdt.core.JavaModelException;
27 import net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment;
28
29 import org.eclipse.core.resources.IContainer;
30 import org.eclipse.core.resources.IResource;
31 import org.eclipse.core.resources.IWorkspace;
32 import org.eclipse.core.resources.ResourcesPlugin;
33 import org.eclipse.core.runtime.IProgressMonitor;
34
35 /**
36  * Abstract class for implementations of java elements which are IOpenable.
37  *
38  * @see IJavaElement
39  * @see IOpenable
40  */
41 public abstract class Openable extends JavaElement implements IOpenable, IBufferChangedListener {
42
43 protected Openable(int type, IJavaElement parent, String name) {
44         super(type, parent, name);
45 }
46         /**
47          * The buffer associated with this element has changed. Registers
48          * this element as being out of synch with its buffer's contents.
49          * If the buffer has been closed, this element is set as NOT out of
50          * synch with the contents.
51          *
52          * @see IBufferChangedListener
53          */
54         public void bufferChanged(BufferChangedEvent event) {
55                 if (event.getBuffer().isClosed()) {
56                         JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
57                         getBufferManager().removeBuffer(event.getBuffer());
58                 } else {
59                         JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().put(this, this);
60                 }
61         }       
62 /**
63  * Updates the info objects for this element and all of its children by
64  * removing the current infos, generating new infos, and then placing
65  * the new infos into the Java Model cache tables.
66  */
67 protected void buildStructure(OpenableElementInfo info, IProgressMonitor monitor) throws JavaModelException {
68
69         if (monitor != null && monitor.isCanceled()) return;
70         
71         // remove existing (old) infos
72         removeInfo();
73         HashMap newElements = new HashMap(11);
74         info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
75         JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
76         for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
77                 IJavaElement key = (IJavaElement) iter.next();
78                 Object value = newElements.get(key);
79                 JavaModelManager.getJavaModelManager().putInfo(key, value);
80         }
81                 
82         // add the info for this at the end, to ensure that a getInfo cannot reply null in case the LRU cache needs
83         // to be flushed. Might lead to performance issues.
84         // see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary type
85         JavaModelManager.getJavaModelManager().putInfo(this, info);     
86 }
87 /**
88  * Close the buffer associated with this element, if any.
89  */
90 protected void closeBuffer(OpenableElementInfo info) {
91         if (!hasBuffer()) return; // nothing to do
92         IBuffer buffer = null;
93         buffer = getBufferManager().getBuffer(this);
94         if (buffer != null) {
95                 buffer.close();
96                 buffer.removeBufferChangedListener(this);
97         }
98 }
99 /**
100  * This element is being closed.  Do any necessary cleanup.
101  */
102 protected void closing(Object info) throws JavaModelException {
103         OpenableElementInfo openableInfo = (OpenableElementInfo) info;
104         closeBuffer(openableInfo);
105         super.closing(info);
106 }
107 ///**
108 // * @see ICodeAssist
109 // */
110 //protected void codeComplete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu, org.eclipse.jdt.internal.compiler.env.ICompilationUnit unitToSkip, int position, ICompletionRequestor requestor) throws JavaModelException {
111 //      if (requestor == null) {
112 //              throw new IllegalArgumentException(Util.bind("codeAssist.nullRequestor")); //$NON-NLS-1$
113 //      }
114 //      IBuffer buffer = getBuffer();
115 //      if (buffer == null) {
116 //              return;
117 //      }
118 //      if (position < -1 || position > buffer.getLength()) {
119 //              throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
120 //      }
121 //      JavaProject project = (JavaProject) getJavaProject();
122 //      SearchableEnvironment environment = (SearchableEnvironment) project.getSearchableNameEnvironment();
123 //      NameLookup nameLookup = project.getNameLookup();
124 //      environment.unitToSkip = unitToSkip;
125 //
126 //      CompletionEngine engine = new CompletionEngine(environment, new CompletionRequestorWrapper(requestor,nameLookup), project.getOptions(true), project);
127 //      engine.complete(cu, position, 0);
128 //      environment.unitToSkip = null;
129 //}
130 /**
131  * @see ICodeAssist
132  */
133 //protected IJavaElement[] codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit cu, int offset, int length) throws JavaModelException {
134 //      SelectionRequestor requestor= new SelectionRequestor(((JavaProject)getJavaProject()).getNameLookup(), this);
135 //      this.codeSelect(cu, offset, length, requestor);
136 //      return requestor.getElements();
137 //}
138 /**
139  * @see ICodeAssist
140  */
141 //protected void codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit cu, int offset, int length, ISelectionRequestor requestor) throws JavaModelException {
142 //      IBuffer buffer = getBuffer();
143 //      if (buffer == null) {
144 //              return;
145 //      }
146 //      int end= buffer.getLength();
147 //      if (offset < 0 || length < 0 || offset + length > end ) {
148 //              throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
149 //      }
150 //
151 //      // fix for 1FVGGKF
152 //      JavaProject project = (JavaProject)getJavaProject();
153 //      ISearchableNameEnvironment environment = project.getSearchableNameEnvironment();
154 //      
155 //      // fix for 1FVXGDK
156 //      SelectionEngine engine = new SelectionEngine(environment, requestor, project.getOptions(true));
157 //      engine.select(cu, offset, offset + length - 1);
158 //}
159 /**
160  * Returns a new element info for this element.
161  */
162 protected OpenableElementInfo createElementInfo() {
163         return new OpenableElementInfo();
164 }
165
166 /**
167  * Builds this element's structure and properties in the given
168  * info object, based on this element's current contents (reuse buffer
169  * contents if this element has an open buffer, or resource contents
170  * if this element does not have an open buffer). Children
171  * are placed in the given newElements table (note, this element
172  * has already been placed in the newElements table). Returns true
173  * if successful, or false if an error is encountered while determining
174  * the structure of this element.
175  */
176 protected abstract boolean generateInfos(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException;
177 /**
178  * Note: a buffer with no unsaved changes can be closed by the Java Model
179  * since it has a finite number of buffers allowed open at one time. If this
180  * is the first time a request is being made for the buffer, an attempt is
181  * made to create and fill this element's buffer. If the buffer has been
182  * closed since it was first opened, the buffer is re-created.
183  * 
184  * @see IOpenable
185  */
186 public IBuffer getBuffer() throws JavaModelException {
187         if (hasBuffer()) {
188                 // ensure element is open
189                 if (!isOpen()) {
190                         getElementInfo();
191                 }
192                 IBuffer buffer = getBufferManager().getBuffer(this);
193                 if (buffer == null) {
194                         // try to (re)open a buffer
195                         buffer = openBuffer(null);
196                 }
197                 return buffer;
198         } else {
199                 return null;
200         }
201 }
202
203 /**
204  * Answers the buffer factory to use for creating new buffers
205  */
206 public IBufferFactory getBufferFactory(){
207         return getBufferManager().getDefaultBufferFactory();
208 }
209
210 /**
211  * Returns the buffer manager for this element.
212  */
213 protected BufferManager getBufferManager() {
214         return BufferManager.getDefaultBufferManager();
215 }
216 /**
217  * Return my underlying resource. Elements that may not have a
218  * corresponding resource must override this method.
219  *
220  * @see IJavaElement
221  */
222 public IResource getCorrespondingResource() throws JavaModelException {
223         return getUnderlyingResource();
224 }
225 /*
226  * @see IJavaElement
227  */
228 public IOpenable getOpenable() {
229         return this;    
230 }
231
232
233
234 /**
235  * @see IJavaElement
236  */
237 public IResource getUnderlyingResource() throws JavaModelException {
238         IResource parentResource = fParent.getUnderlyingResource();
239         if (parentResource == null) {
240                 return null;
241         }
242         int type = parentResource.getType();
243         if (type == IResource.FOLDER || type == IResource.PROJECT) {
244                 IContainer folder = (IContainer) parentResource;
245                 IResource resource = folder.findMember(fName);
246                 if (resource == null) {
247                         throw newNotPresentException();
248                 } else {
249                         return resource;
250                 }
251         } else {
252                 return parentResource;
253         }
254 }
255
256 public boolean exists() {
257         
258         IPackageFragmentRoot root = this.getPackageFragmentRoot();
259         if (root == null || root == this || !root.isArchive()) {
260                 return parentExists() && resourceExists();
261         } else {
262                 return super.exists();
263         }
264 }       
265
266 /**
267  * Returns true if this element may have an associated source buffer,
268  * otherwise false. Subclasses must override as required.
269  */
270 protected boolean hasBuffer() {
271         return false;
272 }
273 /**
274  * @see IParent 
275  */
276 public boolean hasChildren() throws JavaModelException {
277         return getChildren().length > 0;
278 }
279 /**
280  * @see IOpenable
281  */
282 public boolean hasUnsavedChanges() throws JavaModelException{
283         
284         if (isReadOnly() || !isOpen()) {
285                 return false;
286         }
287         IBuffer buf = this.getBuffer();
288         if (buf != null && buf.hasUnsavedChanges()) {
289                 return true;
290         }
291         // for package fragments, package fragment roots, and projects must check open buffers
292         // to see if they have an child with unsaved changes
293         if (fLEType == PACKAGE_FRAGMENT ||
294                 fLEType == PACKAGE_FRAGMENT_ROOT ||
295                 fLEType == JAVA_PROJECT ||
296                 fLEType == JAVA_MODEL) { // fix for 1FWNMHH
297                 Enumeration openBuffers= getBufferManager().getOpenBuffers();
298                 while (openBuffers.hasMoreElements()) {
299                         IBuffer buffer= (IBuffer)openBuffers.nextElement();
300                         if (buffer.hasUnsavedChanges()) {
301                                 IJavaElement owner= (IJavaElement)buffer.getOwner();
302                                 if (isAncestorOf(owner)) {
303                                         return true;
304                                 }
305                         }
306                 }
307         }
308         
309         return false;
310 }
311 /**
312  * Subclasses must override as required.
313  *
314  * @see IOpenable
315  */
316 public boolean isConsistent() throws JavaModelException {
317         return true;
318 }
319 /**
320  * 
321  * @see IOpenable
322  */
323 public boolean isOpen() {
324         synchronized(JavaModelManager.getJavaModelManager()){
325                 return JavaModelManager.getJavaModelManager().getInfo(this) != null;
326         }
327 }
328 /**
329  * Returns true if this represents a source element.
330  * Openable source elements have an associated buffer created
331  * when they are opened.
332  */
333 protected boolean isSourceElement() {
334         return false;
335 }
336 /**
337  * @see IOpenable
338  */
339 public void makeConsistent(IProgressMonitor pm) throws JavaModelException {
340         if (!isConsistent()) {
341                 buildStructure((OpenableElementInfo)getElementInfo(), pm);
342         }
343 }
344 /**
345  * @see IOpenable
346  */
347 public void open(IProgressMonitor pm) throws JavaModelException {
348         if (!isOpen()) {
349                 // TODO: need to synchronize (IOpenable.open(IProgressMonitor) is API
350                 // TODO: could use getElementInfo instead
351                 this.openWhenClosed(pm);
352         }
353 }
354
355 /**
356  * Opens a buffer on the contents of this element, and returns
357  * the buffer, or returns <code>null</code> if opening fails.
358  * By default, do nothing - subclasses that have buffers
359  * must override as required.
360  */
361 protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
362         return null;
363 }
364
365 /**
366  *      Open the parent element if necessary
367  * 
368  */
369 protected void openParent(IProgressMonitor pm) throws JavaModelException {
370
371         Openable openableParent = (Openable)getOpenableParent();
372         if (openableParent != null) {
373                 if (!openableParent.isOpen()){
374                         openableParent.openWhenClosed(pm);
375                 }
376         }
377 }
378
379 /**
380  * Open an <code>Openable</code> that is known to be closed (no check for <code>isOpen()</code>).
381  */
382 protected void openWhenClosed(IProgressMonitor pm) throws JavaModelException {
383         try {
384                 
385                 if (JavaModelManager.VERBOSE){
386                         System.out.println("OPENING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
387                 }
388                 
389                 // 1) Parent must be open - open the parent if necessary
390                 openParent(pm);
391
392                 // 2) create the new element info and open a buffer if needed
393                 OpenableElementInfo info = createElementInfo();
394                 if (isSourceElement()) {
395                         this.openBuffer(pm);
396                 } 
397
398                 // 3) build the structure of the openable
399                 buildStructure(info, pm);
400
401                 // 4) anything special
402                 opening(info);
403                 
404 //              if (JavaModelManager.VERBOSE) {
405 //                      System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
406 //                      System.out.println("-> Openable cache filling ratio = " + JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$
407 //              }
408
409                 // if any problems occuring openning the element, ensure that it's info
410                 // does not remain in the cache (some elements, pre-cache their info
411                 // as they are being opened).
412         } catch (JavaModelException e) {
413                 JavaModelManager.getJavaModelManager().removeInfo(this);
414                 throw e;
415         }
416 }
417
418 /**
419  *  Answers true if the parent exists (null parent is answering true)
420  * 
421  */
422 protected boolean parentExists(){
423         
424         IJavaElement parent = this.getParent();
425         if (parent == null) return true;
426         return parent.exists();
427 }
428
429 /**
430  * Returns whether the corresponding resource or associated file exists
431  */
432 protected boolean resourceExists() {
433         IWorkspace workspace = ResourcesPlugin.getWorkspace();
434         if (workspace == null) return false; // workaround for http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069
435         return 
436                 JavaModel.getTarget(
437                         workspace.getRoot(), 
438                         this.getPath().makeRelative(), // ensure path is relative (see http://dev.eclipse.org/bugs/show_bug.cgi?id=22517)
439                         true) != null;
440 }
441
442 /**
443  * @see IOpenable
444  */
445 public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
446         if (isReadOnly() || this.getResource().isReadOnly()) {
447                 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.READ_ONLY, this));
448         }
449         IBuffer buf = getBuffer();
450         if (buf != null) { // some Openables (like a JavaProject) don't have a buffer
451                 buf.save(pm, force);
452                 this.makeConsistent(pm); // update the element info of this element
453         }
454 }
455
456 /**
457  * Find enclosing package fragment root if any
458  */
459 public PackageFragmentRoot getPackageFragmentRoot() {
460         IJavaElement current = this;
461         do {
462                 if (current instanceof PackageFragmentRoot) return (PackageFragmentRoot)current;
463                 current = current.getParent();
464         } while(current != null);
465         return null;
466 }
467 ///**
468 // * @see ICodeAssist
469 // * @deprecated - use codeComplete(ICompilationUnit, ICompilationUnit, int, ICompletionRequestor) instead
470 // */
471 //protected void codeComplete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu, org.eclipse.jdt.internal.compiler.env.ICompilationUnit unitToSkip, int position, final ICodeCompletionRequestor requestor) throws JavaModelException {
472 //
473 //      if (requestor == null){
474 //              codeComplete(cu, unitToSkip, position, (ICompletionRequestor)null);
475 //              return;
476 //      }
477 //      codeComplete(
478 //              cu,
479 //              unitToSkip,
480 //              position,
481 //              new ICompletionRequestor(){
482 //                      public void acceptAnonymousType(char[] superTypePackageName,char[] superTypeName,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) {
483 //                      }
484 //                      public void acceptClass(char[] packageName, char[] className, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) {
485 //                              requestor.acceptClass(packageName, className, completionName, modifiers, completionStart, completionEnd);
486 //                      }
487 //                      public void acceptError(IProblem error) {
488 //                              if (true) return; // was disabled in 1.0
489 //
490 //                              try {
491 //                                      IMarker marker = ResourcesPlugin.getWorkspace().getRoot().createMarker(IJavaModelMarker.TRANSIENT_PROBLEM);
492 //                                      marker.setAttribute(IJavaModelMarker.ID, error.getID());
493 //                                      marker.setAttribute(IMarker.CHAR_START, error.getSourceStart());
494 //                                      marker.setAttribute(IMarker.CHAR_END, error.getSourceEnd() + 1);
495 //                                      marker.setAttribute(IMarker.LINE_NUMBER, error.getSourceLineNumber());
496 //                                      marker.setAttribute(IMarker.MESSAGE, error.getMessage());
497 //                                      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
498 //                                      requestor.acceptError(marker);
499 //                              } catch(CoreException e){
500 //                              }
501 //                      }
502 //                      public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] name, char[] typePackageName, char[] typeName, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) {
503 //                              requestor.acceptField(declaringTypePackageName, declaringTypeName, name, typePackageName, typeName, completionName, modifiers, completionStart, completionEnd);
504 //                      }
505 //                      public void acceptInterface(char[] packageName,char[] interfaceName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) {
506 //                              requestor.acceptInterface(packageName, interfaceName, completionName, modifiers, completionStart, completionEnd);
507 //                      }
508 //                      public void acceptKeyword(char[] keywordName,int completionStart,int completionEnd, int relevance){
509 //                              requestor.acceptKeyword(keywordName, completionStart, completionEnd);
510 //                      }
511 //                      public void acceptLabel(char[] labelName,int completionStart,int completionEnd, int relevance){
512 //                              requestor.acceptLabel(labelName, completionStart, completionEnd);
513 //                      }
514 //                      public void acceptLocalVariable(char[] name,char[] typePackageName,char[] typeName,int modifiers,int completionStart,int completionEnd, int relevance){
515 //                              // ignore
516 //                      }
517 //                      public void acceptMethod(char[] declaringTypePackageName,char[] declaringTypeName,char[] selector,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] returnTypePackageName,char[] returnTypeName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance){
518 //                              // skip parameter names
519 //                              requestor.acceptMethod(declaringTypePackageName, declaringTypeName, selector, parameterPackageNames, parameterTypeNames, returnTypePackageName, returnTypeName, completionName, modifiers, completionStart, completionEnd);
520 //                      }
521 //                      public void acceptMethodDeclaration(char[] declaringTypePackageName,char[] declaringTypeName,char[] selector,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] returnTypePackageName,char[] returnTypeName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance){
522 //                              // ignore
523 //                      }
524 //                      public void acceptModifier(char[] modifierName,int completionStart,int completionEnd, int relevance){
525 //                              requestor.acceptModifier(modifierName, completionStart, completionEnd);
526 //                      }
527 //                      public void acceptPackage(char[] packageName,char[] completionName,int completionStart,int completionEnd, int relevance){
528 //                              requestor.acceptPackage(packageName, completionName, completionStart, completionEnd);
529 //                      }
530 //                      public void acceptType(char[] packageName,char[] typeName,char[] completionName,int completionStart,int completionEnd, int relevance){
531 //                              requestor.acceptType(packageName, typeName, completionName, completionStart, completionEnd);
532 //                      }
533 //                      public void acceptVariableName(char[] typePackageName,char[] typeName,char[] name,char[] completionName,int completionStart,int completionEnd, int relevance){
534 //                              // ignore
535 //                      }
536 //              });
537 //}
538 }