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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
13 import java.text.NumberFormat;
14 import java.util.Enumeration;
15 import java.util.HashMap;
16 import java.util.Iterator;
19 import net.sourceforge.phpdt.core.BufferChangedEvent;
20 import net.sourceforge.phpdt.core.IBuffer;
21 import net.sourceforge.phpdt.core.IBufferChangedListener;
22 import net.sourceforge.phpdt.core.IBufferFactory;
23 import net.sourceforge.phpdt.core.IJavaElement;
24 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
25 import net.sourceforge.phpdt.core.IOpenable;
26 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
27 import net.sourceforge.phpdt.core.JavaModelException;
28 import net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment;
30 import org.eclipse.core.resources.IContainer;
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.resources.IWorkspace;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.IProgressMonitor;
35 import net.sourceforge.phpdt.internal.core.JavaElement;
37 import net.sourceforge.phpdt.internal.core.JavaModelManager;
38 import net.sourceforge.phpdt.internal.core.OpenableElementInfo;
41 * Abstract class for implementations of java elements which are IOpenable.
46 public abstract class Openable extends JavaElement implements IOpenable, IBufferChangedListener {
48 protected Openable(JavaElement parent, String name) {
52 * The buffer associated with this element has changed. Registers
53 * this element as being out of synch with its buffer's contents.
54 * If the buffer has been closed, this element is set as NOT out of
55 * synch with the contents.
57 * @see IBufferChangedListener
59 public void bufferChanged(BufferChangedEvent event) {
60 if (event.getBuffer().isClosed()) {
61 JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
62 getBufferManager().removeBuffer(event.getBuffer());
64 JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().put(this, this);
69 * Builds this element's structure and properties in the given
70 * info object, based on this element's current contents (reuse buffer
71 * contents if this element has an open buffer, or resource contents
72 * if this element does not have an open buffer). Children
73 * are placed in the given newElements table (note, this element
74 * has already been placed in the newElements table). Returns true
75 * if successful, or false if an error is encountered while determining
76 * the structure of this element.
78 protected abstract boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException;
81 // * Updates the info objects for this element and all of its children by
82 // * removing the current infos, generating new infos, and then placing
83 // * the new infos into the Java Model cache tables.
85 //protected void buildStructure(OpenableElementInfo info, IProgressMonitor monitor) throws JavaModelException {
87 // if (monitor != null && monitor.isCanceled()) return;
89 // // remove existing (old) infos
91 // HashMap newElements = new HashMap(11);
92 // info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
93 // JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
94 // for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
95 // IJavaElement key = (IJavaElement) iter.next();
96 // Object value = newElements.get(key);
97 // JavaModelManager.getJavaModelManager().putInfo(key, value);
100 // // add the info for this at the end, to ensure that a getInfo cannot reply null in case the LRU cache needs
101 // // to be flushed. Might lead to performance issues.
102 // // see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary type
103 // JavaModelManager.getJavaModelManager().putInfo(this, info);
106 * Returns whether this element can be removed from the Java model cache to make space.
108 public boolean canBeRemovedFromCache() {
110 return !hasUnsavedChanges();
111 } catch (JavaModelException e) {
116 * Returns whether the buffer of this element can be removed from the Java model cache to make space.
118 public boolean canBufferBeRemovedFromCache(IBuffer buffer) {
119 return !buffer.hasUnsavedChanges();
122 * Close the buffer associated with this element, if any.
124 protected void closeBuffer() {
125 if (!hasBuffer()) return; // nothing to do
126 IBuffer buffer = getBufferManager().getBuffer(this);
127 if (buffer != null) {
129 buffer.removeBufferChangedListener(this);
133 * Close the buffer associated with this element, if any.
135 protected void closeBuffer(OpenableElementInfo info) {
136 if (!hasBuffer()) return; // nothing to do
137 IBuffer buffer = null;
138 buffer = getBufferManager().getBuffer(this);
139 if (buffer != null) {
141 buffer.removeBufferChangedListener(this);
145 * This element is being closed. Do any necessary cleanup.
147 protected void closing(Object info) {
152 // * @see ICodeAssist
154 //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 {
155 // if (requestor == null) {
156 // throw new IllegalArgumentException(Util.bind("codeAssist.nullRequestor")); //$NON-NLS-1$
158 // IBuffer buffer = getBuffer();
159 // if (buffer == null) {
162 // if (position < -1 || position > buffer.getLength()) {
163 // throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
165 // JavaProject project = (JavaProject) getJavaProject();
166 // SearchableEnvironment environment = (SearchableEnvironment) project.getSearchableNameEnvironment();
167 // NameLookup nameLookup = project.getNameLookup();
168 // environment.unitToSkip = unitToSkip;
170 // CompletionEngine engine = new CompletionEngine(environment, new CompletionRequestorWrapper(requestor,nameLookup), project.getOptions(true), project);
171 // engine.complete(cu, position, 0);
172 // environment.unitToSkip = null;
177 //protected IJavaElement[] codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit cu, int offset, int length) throws JavaModelException {
178 // SelectionRequestor requestor= new SelectionRequestor(((JavaProject)getJavaProject()).getNameLookup(), this);
179 // this.codeSelect(cu, offset, length, requestor);
180 // return requestor.getElements();
185 //protected void codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit cu, int offset, int length, ISelectionRequestor requestor) throws JavaModelException {
186 // IBuffer buffer = getBuffer();
187 // if (buffer == null) {
190 // int end= buffer.getLength();
191 // if (offset < 0 || length < 0 || offset + length > end ) {
192 // throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
195 // // fix for 1FVGGKF
196 // JavaProject project = (JavaProject)getJavaProject();
197 // ISearchableNameEnvironment environment = project.getSearchableNameEnvironment();
199 // // fix for 1FVXGDK
200 // SelectionEngine engine = new SelectionEngine(environment, requestor, project.getOptions(true));
201 // engine.select(cu, offset, offset + length - 1);
204 * Returns a new element info for this element.
206 protected Object createElementInfo() {
207 return new OpenableElementInfo();
210 // * Builds this element's structure and properties in the given
211 // * info object, based on this element's current contents (reuse buffer
212 // * contents if this element has an open buffer, or resource contents
213 // * if this element does not have an open buffer). Children
214 // * are placed in the given newElements table (note, this element
215 // * has already been placed in the newElements table). Returns true
216 // * if successful, or false if an error is encountered while determining
217 // * the structure of this element.
219 //protected abstract boolean generateInfos(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException;
221 protected void generateInfos(Object info, HashMap newElements, IProgressMonitor monitor) throws JavaModelException {
223 if (JavaModelManager.VERBOSE){
224 System.out.println("OPENING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
227 // open the parent if necessary
228 openParent(info, newElements, monitor);
229 if (monitor != null && monitor.isCanceled()) return;
231 // puts the info before building the structure so that questions to the handle behave as if the element existed
232 // (case of compilation units becoming working copies)
233 newElements.put(this, info);
235 // build the structure of the openable (this will open the buffer if needed)
237 OpenableElementInfo openableElementInfo = (OpenableElementInfo)info;
238 boolean isStructureKnown = buildStructure(openableElementInfo, monitor, newElements, getResource());
239 openableElementInfo.setIsStructureKnown(isStructureKnown);
240 } catch (JavaModelException e) {
241 newElements.remove(this);
245 // remove out of sync buffer for this element
246 JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
248 if (JavaModelManager.VERBOSE) {
249 System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
250 System.out.println("-> Openable cache filling ratio = " + NumberFormat.getInstance().format(JavaModelManager.getJavaModelManager().cache.openableFillingRatio()) + "%"); //$NON-NLS-1$//$NON-NLS-2$
254 * Note: a buffer with no unsaved changes can be closed by the Java Model
255 * since it has a finite number of buffers allowed open at one time. If this
256 * is the first time a request is being made for the buffer, an attempt is
257 * made to create and fill this element's buffer. If the buffer has been
258 * closed since it was first opened, the buffer is re-created.
262 public IBuffer getBuffer() throws JavaModelException {
264 // ensure element is open
268 IBuffer buffer = getBufferManager().getBuffer(this);
269 if (buffer == null) {
270 // try to (re)open a buffer
271 buffer = openBuffer(null);
280 * Answers the buffer factory to use for creating new buffers
282 public IBufferFactory getBufferFactory(){
283 return getBufferManager().getDefaultBufferFactory();
287 * Returns the buffer manager for this element.
289 protected BufferManager getBufferManager() {
290 return BufferManager.getDefaultBufferManager();
293 * Return my underlying resource. Elements that may not have a
294 * corresponding resource must override this method.
298 public IResource getCorrespondingResource() throws JavaModelException {
299 return getUnderlyingResource();
304 public IOpenable getOpenable() {
313 public IResource getUnderlyingResource() throws JavaModelException {
314 IResource parentResource = parent.getUnderlyingResource();
315 if (parentResource == null) {
318 int type = parentResource.getType();
319 if (type == IResource.FOLDER || type == IResource.PROJECT) {
320 IContainer folder = (IContainer) parentResource;
321 IResource resource = folder.findMember(name);
322 if (resource == null) {
323 throw newNotPresentException();
328 return parentResource;
332 public boolean exists() {
334 IPackageFragmentRoot root = this.getPackageFragmentRoot();
335 if (root == null || root == this || !root.isArchive()) {
336 return parentExists() && resourceExists();
338 return super.exists();
343 * Returns true if this element may have an associated source buffer,
344 * otherwise false. Subclasses must override as required.
346 protected boolean hasBuffer() {
352 public boolean hasChildren() throws JavaModelException {
353 return getChildren().length > 0;
358 public boolean hasUnsavedChanges() throws JavaModelException{
360 if (isReadOnly() || !isOpen()) {
363 IBuffer buf = this.getBuffer();
364 if (buf != null && buf.hasUnsavedChanges()) {
367 // for package fragments, package fragment roots, and projects must check open buffers
368 // to see if they have an child with unsaved changes
369 int elementType = getElementType();
370 if (elementType == PACKAGE_FRAGMENT ||
371 elementType == PACKAGE_FRAGMENT_ROOT ||
372 elementType == JAVA_PROJECT ||
373 elementType == JAVA_MODEL) { // fix for 1FWNMHH
374 Enumeration openBuffers= getBufferManager().getOpenBuffers();
375 while (openBuffers.hasMoreElements()) {
376 IBuffer buffer= (IBuffer)openBuffers.nextElement();
377 if (buffer.hasUnsavedChanges()) {
378 IJavaElement owner= (IJavaElement)buffer.getOwner();
379 if (isAncestorOf(owner)) {
389 * Subclasses must override as required.
393 public boolean isConsistent() throws JavaModelException {
400 public boolean isOpen() {
401 synchronized(JavaModelManager.getJavaModelManager()){
402 return JavaModelManager.getJavaModelManager().getInfo(this) != null;
406 * Returns true if this represents a source element.
407 * Openable source elements have an associated buffer created
408 * when they are opened.
410 protected boolean isSourceElement() {
416 //public void makeConsistent(IProgressMonitor pm) throws JavaModelException {
417 // if (!isConsistent()) {
418 // buildStructure((OpenableElementInfo)getElementInfo(), pm);
424 public void makeConsistent(IProgressMonitor monitor) throws JavaModelException {
425 if (isConsistent()) return;
427 // create a new info and make it the current info
428 // (this will remove the info and its children just before storing the new infos)
429 JavaModelManager manager = JavaModelManager.getJavaModelManager();
430 boolean hadTemporaryCache = manager.hasTemporaryCache();
432 HashMap newElements = manager.getTemporaryCache();
433 openWhenClosed(newElements, monitor);
434 if (newElements.get(this) == null) {
435 // close any buffer that was opened for the new elements
436 Iterator iterator = newElements.keySet().iterator();
437 while (iterator.hasNext()) {
438 IJavaElement element = (IJavaElement)iterator.next();
439 if (element instanceof Openable) {
440 ((Openable)element).closeBuffer();
443 throw newNotPresentException();
445 if (!hadTemporaryCache) {
446 manager.putInfos(this, newElements);
449 if (!hadTemporaryCache) {
450 manager.resetTemporaryCache();
458 public void open(IProgressMonitor pm) throws JavaModelException {
462 * Opens a buffer on the contents of this element, and returns
463 * the buffer, or returns <code>null</code> if opening fails.
464 * By default, do nothing - subclasses that have buffers
465 * must override as required.
467 protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
472 * Open the parent element if necessary.
474 protected void openParent(Object childInfo, HashMap newElements, IProgressMonitor pm) throws JavaModelException {
476 Openable openableParent = (Openable)getOpenableParent();
477 if (openableParent != null && !openableParent.isOpen()){
478 openableParent.generateInfos(openableParent.createElementInfo(), newElements, pm);
483 // * Open an <code>Openable</code> that is known to be closed (no check for <code>isOpen()</code>).
485 //protected void openWhenClosed(IProgressMonitor pm) throws JavaModelException {
488 // if (JavaModelManager.VERBOSE){
489 // System.out.println("OPENING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
492 // // 1) Parent must be open - open the parent if necessary
495 // // 2) create the new element info and open a buffer if needed
496 // OpenableElementInfo info = createElementInfo();
497 // if (isSourceElement()) {
498 // this.openBuffer(pm);
501 // // 3) build the structure of the openable
502 // buildStructure(info, pm);
504 // // 4) anything special
507 //// if (JavaModelManager.VERBOSE) {
508 //// System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
509 //// System.out.println("-> Openable cache filling ratio = " + JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + "%"); //$NON-NLS-1$//$NON-NLS-2$
512 // // if any problems occuring openning the element, ensure that it's info
513 // // does not remain in the cache (some elements, pre-cache their info
514 // // as they are being opened).
515 // } catch (JavaModelException e) {
516 // JavaModelManager.getJavaModelManager().removeInfo(this);
522 * Answers true if the parent exists (null parent is answering true)
525 protected boolean parentExists(){
527 IJavaElement parent = this.getParent();
528 if (parent == null) return true;
529 return parent.exists();
533 * Returns whether the corresponding resource or associated file exists
535 protected boolean resourceExists() {
536 IWorkspace workspace = ResourcesPlugin.getWorkspace();
537 if (workspace == null) return false; // workaround for http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069
541 this.getPath().makeRelative(), // ensure path is relative (see http://dev.eclipse.org/bugs/show_bug.cgi?id=22517)
548 public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
549 if (isReadOnly() || this.getResource().isReadOnly()) {
550 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.READ_ONLY, this));
552 IBuffer buf = getBuffer();
553 if (buf != null) { // some Openables (like a JavaProject) don't have a buffer
555 this.makeConsistent(pm); // update the element info of this element
560 * Find enclosing package fragment root if any
562 public PackageFragmentRoot getPackageFragmentRoot() {
563 IJavaElement current = this;
565 if (current instanceof PackageFragmentRoot) return (PackageFragmentRoot)current;
566 current = current.getParent();
567 } while(current != null);
571 // * @see ICodeAssist
572 // * @deprecated - use codeComplete(ICompilationUnit, ICompilationUnit, int, ICompletionRequestor) instead
574 //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 {
576 // if (requestor == null){
577 // codeComplete(cu, unitToSkip, position, (ICompletionRequestor)null);
584 // new ICompletionRequestor(){
585 // public void acceptAnonymousType(char[] superTypePackageName,char[] superTypeName,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) {
587 // public void acceptClass(char[] packageName, char[] className, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) {
588 // requestor.acceptClass(packageName, className, completionName, modifiers, completionStart, completionEnd);
590 // public void acceptError(IProblem error) {
591 // if (true) return; // was disabled in 1.0
594 // IMarker marker = ResourcesPlugin.getWorkspace().getRoot().createMarker(IJavaModelMarker.TRANSIENT_PROBLEM);
595 // marker.setAttribute(IJavaModelMarker.ID, error.getID());
596 // marker.setAttribute(IMarker.CHAR_START, error.getSourceStart());
597 // marker.setAttribute(IMarker.CHAR_END, error.getSourceEnd() + 1);
598 // marker.setAttribute(IMarker.LINE_NUMBER, error.getSourceLineNumber());
599 // marker.setAttribute(IMarker.MESSAGE, error.getMessage());
600 // marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
601 // requestor.acceptError(marker);
602 // } catch(CoreException e){
605 // public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] name, char[] typePackageName, char[] typeName, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) {
606 // requestor.acceptField(declaringTypePackageName, declaringTypeName, name, typePackageName, typeName, completionName, modifiers, completionStart, completionEnd);
608 // public void acceptInterface(char[] packageName,char[] interfaceName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) {
609 // requestor.acceptInterface(packageName, interfaceName, completionName, modifiers, completionStart, completionEnd);
611 // public void acceptKeyword(char[] keywordName,int completionStart,int completionEnd, int relevance){
612 // requestor.acceptKeyword(keywordName, completionStart, completionEnd);
614 // public void acceptLabel(char[] labelName,int completionStart,int completionEnd, int relevance){
615 // requestor.acceptLabel(labelName, completionStart, completionEnd);
617 // public void acceptLocalVariable(char[] name,char[] typePackageName,char[] typeName,int modifiers,int completionStart,int completionEnd, int relevance){
620 // 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){
621 // // skip parameter names
622 // requestor.acceptMethod(declaringTypePackageName, declaringTypeName, selector, parameterPackageNames, parameterTypeNames, returnTypePackageName, returnTypeName, completionName, modifiers, completionStart, completionEnd);
624 // 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){
627 // public void acceptModifier(char[] modifierName,int completionStart,int completionEnd, int relevance){
628 // requestor.acceptModifier(modifierName, completionStart, completionEnd);
630 // public void acceptPackage(char[] packageName,char[] completionName,int completionStart,int completionEnd, int relevance){
631 // requestor.acceptPackage(packageName, completionName, completionStart, completionEnd);
633 // public void acceptType(char[] packageName,char[] typeName,char[] completionName,int completionStart,int completionEnd, int relevance){
634 // requestor.acceptType(packageName, typeName, completionName, completionStart, completionEnd);
636 // public void acceptVariableName(char[] typePackageName,char[] typeName,char[] name,char[] completionName,int completionStart,int completionEnd, int relevance){