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.ICodeAssist;
24 import net.sourceforge.phpdt.core.IJavaElement;
25 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
26 import net.sourceforge.phpdt.core.IOpenable;
27 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
28 import net.sourceforge.phpdt.core.IParent;
29 import net.sourceforge.phpdt.core.JavaModelException;
31 import org.eclipse.core.resources.IContainer;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.resources.IWorkspace;
34 import org.eclipse.core.resources.ResourcesPlugin;
35 import org.eclipse.core.runtime.IProgressMonitor;
38 * Abstract class for implementations of java elements which are IOpenable.
43 public abstract class Openable extends JavaElement implements IOpenable,
44 IBufferChangedListener {
46 protected Openable(JavaElement parent, String name) {
51 * The buffer associated with this element has changed. Registers this
52 * element as being out of synch with its buffer's contents. If the buffer
53 * has been closed, this element is set as NOT out of synch with the
56 * @see IBufferChangedListener
58 public void bufferChanged(BufferChangedEvent event) {
59 if (event.getBuffer().isClosed()) {
60 JavaModelManager.getJavaModelManager()
61 .getElementsOutOfSynchWithBuffers().remove(this);
62 getBufferManager().removeBuffer(event.getBuffer());
64 JavaModelManager.getJavaModelManager()
65 .getElementsOutOfSynchWithBuffers().put(this, this);
70 * Builds this element's structure and properties in the given info object,
71 * based on this element's current contents (reuse buffer contents if this
72 * element has an open buffer, or resource contents if this element does not
73 * have an open buffer). Children are placed in the given newElements table
74 * (note, this element has already been placed in the newElements table).
75 * Returns true if successful, or false if an error is encountered while
76 * determining the structure of this element.
78 protected abstract boolean buildStructure(OpenableElementInfo info,
79 IProgressMonitor pm, Map newElements, IResource underlyingResource)
80 throws JavaModelException;
83 // * Updates the info objects for this element and all of its children by
84 // * removing the current infos, generating new infos, and then placing
85 // * the new infos into the Java Model cache tables.
87 // protected void buildStructure(OpenableElementInfo info, IProgressMonitor
88 // monitor) throws JavaModelException {
90 // if (monitor != null && monitor.isCanceled()) return;
92 // // remove existing (old) infos
94 // HashMap newElements = new HashMap(11);
95 // info.setIsStructureKnown(generateInfos(info, monitor, newElements,
97 // JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this);
98 // for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
99 // IJavaElement key = (IJavaElement) iter.next();
100 // Object value = newElements.get(key);
101 // JavaModelManager.getJavaModelManager().putInfo(key, value);
104 // // add the info for this at the end, to ensure that a getInfo cannot
105 // reply null in case the LRU cache needs
106 // // to be flushed. Might lead to performance issues.
107 // // see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary
109 // JavaModelManager.getJavaModelManager().putInfo(this, info);
112 * Returns whether this element can be removed from the Java model cache to
115 public boolean canBeRemovedFromCache() {
117 return !hasUnsavedChanges();
118 } catch (JavaModelException e) {
124 * Returns whether the buffer of this element can be removed from the Java
125 * model cache to make space.
127 public boolean canBufferBeRemovedFromCache(IBuffer buffer) {
128 return !buffer.hasUnsavedChanges();
132 * Close the buffer associated with this element, if any.
134 protected void closeBuffer() {
136 return; // nothing to do
137 IBuffer buffer = getBufferManager().getBuffer(this);
138 if (buffer != null) {
140 buffer.removeBufferChangedListener(this);
145 * Close the buffer associated with this element, if any.
147 protected void closeBuffer(OpenableElementInfo info) {
149 return; // nothing to do
150 IBuffer buffer = null;
151 buffer = getBufferManager().getBuffer(this);
152 if (buffer != null) {
154 buffer.removeBufferChangedListener(this);
159 * This element is being closed. Do any necessary cleanup.
161 protected void closing(Object info) {
166 // * @see ICodeAssist
169 // codeComplete(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
170 // cu, net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
171 // unitToSkip, int position, ICompletionRequestor requestor) throws
172 // JavaModelException {
173 // if (requestor == null) {
175 // IllegalArgumentException(ProjectPrefUtil.bind("codeAssist.nullRequestor"));
178 // IBuffer buffer = getBuffer();
179 // if (buffer == null) {
182 // if (position < -1 || position > buffer.getLength()) {
183 // throw new JavaModelException(new
184 // JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
186 // JavaProject project = (JavaProject) getJavaProject();
187 // SearchableEnvironment environment = (SearchableEnvironment)
188 // project.getSearchableNameEnvironment();
189 // NameLookup nameLookup = project.getNameLookup();
190 // environment.unitToSkip = unitToSkip;
192 // CompletionEngine engine = new CompletionEngine(environment, new
193 // CompletionRequestorWrapper(requestor,nameLookup),
194 // project.getOptions(true), project);
195 // engine.complete(cu, position, 0);
196 // environment.unitToSkip = null;
201 // protected IJavaElement[]
202 // codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
203 // cu, int offset, int length) throws JavaModelException {
204 // SelectionRequestor requestor= new
205 // SelectionRequestor(((JavaProject)getJavaProject()).getNameLookup(),
207 // this.codeSelect(cu, offset, length, requestor);
208 // return requestor.getElements();
214 // codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
215 // cu, int offset, int length, ISelectionRequestor requestor) throws
216 // JavaModelException {
217 // IBuffer buffer = getBuffer();
218 // if (buffer == null) {
221 // int end= buffer.getLength();
222 // if (offset < 0 || length < 0 || offset + length > end ) {
223 // throw new JavaModelException(new
224 // JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
227 // // fix for 1FVGGKF
228 // JavaProject project = (JavaProject)getJavaProject();
229 // ISearchableNameEnvironment environment =
230 // project.getSearchableNameEnvironment();
232 // // fix for 1FVXGDK
233 // SelectionEngine engine = new SelectionEngine(environment, requestor,
234 // project.getOptions(true));
235 // engine.select(cu, offset, offset + length - 1);
238 * Returns a new element info for this element.
240 protected Object createElementInfo() {
241 return new OpenableElementInfo();
245 // * Builds this element's structure and properties in the given
246 // * info object, based on this element's current contents (reuse buffer
247 // * contents if this element has an open buffer, or resource contents
248 // * if this element does not have an open buffer). Children
249 // * are placed in the given newElements table (note, this element
250 // * has already been placed in the newElements table). Returns true
251 // * if successful, or false if an error is encountered while determining
252 // * the structure of this element.
254 // protected abstract boolean generateInfos(OpenableElementInfo info,
255 // IProgressMonitor pm, Map newElements, IResource underlyingResource)
256 // throws JavaModelException;
258 protected void generateInfos(Object info, HashMap newElements,
259 IProgressMonitor monitor) throws JavaModelException {
261 if (JavaModelManager.VERBOSE) {
263 .println("OPENING Element (" + Thread.currentThread() + "): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
266 // open the parent if necessary
267 openParent(info, newElements, monitor);
268 if (monitor != null && monitor.isCanceled())
271 // puts the info before building the structure so that questions to the
272 // handle behave as if the element existed
273 // (case of compilation units becoming working copies)
274 newElements.put(this, info);
276 // build the structure of the openable (this will open the buffer if
279 OpenableElementInfo openableElementInfo = (OpenableElementInfo) info;
280 boolean isStructureKnown = buildStructure(openableElementInfo,
281 monitor, newElements, getResource());
282 openableElementInfo.setIsStructureKnown(isStructureKnown);
283 } catch (JavaModelException e) {
284 newElements.remove(this);
288 // remove out of sync buffer for this element
289 JavaModelManager.getJavaModelManager()
290 .getElementsOutOfSynchWithBuffers().remove(this);
292 if (JavaModelManager.VERBOSE) {
294 .println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
296 .println("-> Openable cache filling ratio = " + NumberFormat.getInstance().format(JavaModelManager.getJavaModelManager().cache.openableFillingRatio()) + "%"); //$NON-NLS-1$//$NON-NLS-2$
301 * Note: a buffer with no unsaved changes can be closed by the Java Model
302 * since it has a finite number of buffers allowed open at one time. If this
303 * is the first time a request is being made for the buffer, an attempt is
304 * made to create and fill this element's buffer. If the buffer has been
305 * closed since it was first opened, the buffer is re-created.
309 public IBuffer getBuffer() throws JavaModelException {
311 // ensure element is open
315 IBuffer buffer = getBufferManager().getBuffer(this);
316 if (buffer == null) {
317 // try to (re)open a buffer
318 buffer = openBuffer(null);
327 * Answers the buffer factory to use for creating new buffers
329 public IBufferFactory getBufferFactory() {
330 return getBufferManager().getDefaultBufferFactory();
334 * Returns the buffer manager for this element.
336 protected BufferManager getBufferManager() {
337 return BufferManager.getDefaultBufferManager();
341 * Return my underlying resource. Elements that may not have a corresponding
342 * resource must override this method.
346 public IResource getCorrespondingResource() throws JavaModelException {
347 return getUnderlyingResource();
353 public IOpenable getOpenable() {
360 public IResource getUnderlyingResource() throws JavaModelException {
361 IResource parentResource = parent.getUnderlyingResource();
362 if (parentResource == null) {
365 int type = parentResource.getType();
366 if (type == IResource.FOLDER || type == IResource.PROJECT) {
367 IContainer folder = (IContainer) parentResource;
368 IResource resource = folder.findMember(name);
369 if (resource == null) {
370 throw newNotPresentException();
375 return parentResource;
379 public boolean exists() {
381 IPackageFragmentRoot root = this.getPackageFragmentRoot();
382 if (root == null || root == this || !root.isArchive()) {
383 return parentExists() && resourceExists();
385 return super.exists();
390 * Returns true if this element may have an associated source buffer,
391 * otherwise false. Subclasses must override as required.
393 protected boolean hasBuffer() {
400 public boolean hasChildren() throws JavaModelException {
401 return getChildren().length > 0;
407 public boolean hasUnsavedChanges() throws JavaModelException {
409 if (isReadOnly() || !isOpen()) {
412 IBuffer buf = this.getBuffer();
413 if (buf != null && buf.hasUnsavedChanges()) {
416 // for package fragments, package fragment roots, and projects must
417 // check open buffers
418 // to see if they have an child with unsaved changes
419 int elementType = getElementType();
420 if (elementType == PACKAGE_FRAGMENT
421 || elementType == PACKAGE_FRAGMENT_ROOT
422 || elementType == JAVA_PROJECT || elementType == JAVA_MODEL) { // fix
425 Enumeration openBuffers = getBufferManager().getOpenBuffers();
426 while (openBuffers.hasMoreElements()) {
427 IBuffer buffer = (IBuffer) openBuffers.nextElement();
428 if (buffer.hasUnsavedChanges()) {
429 IJavaElement owner = (IJavaElement) buffer.getOwner();
430 if (isAncestorOf(owner)) {
441 * Subclasses must override as required.
445 public boolean isConsistent() throws JavaModelException {
453 public boolean isOpen() {
454 synchronized (JavaModelManager.getJavaModelManager()) {
455 return JavaModelManager.getJavaModelManager().getInfo(this) != null;
460 * Returns true if this represents a source element. Openable source
461 * elements have an associated buffer created when they are opened.
463 protected boolean isSourceElement() {
470 // public void makeConsistent(IProgressMonitor pm) throws JavaModelException
472 // if (!isConsistent()) {
473 // buildStructure((OpenableElementInfo)getElementInfo(), pm);
479 public void makeConsistent(IProgressMonitor monitor)
480 throws JavaModelException {
484 // create a new info and make it the current info
485 // (this will remove the info and its children just before storing the
487 JavaModelManager manager = JavaModelManager.getJavaModelManager();
488 boolean hadTemporaryCache = manager.hasTemporaryCache();
490 HashMap newElements = manager.getTemporaryCache();
491 openWhenClosed(newElements, monitor);
492 if (newElements.get(this) == null) {
493 // close any buffer that was opened for the new elements
494 Iterator iterator = newElements.keySet().iterator();
495 while (iterator.hasNext()) {
496 IJavaElement element = (IJavaElement) iterator.next();
497 if (element instanceof Openable) {
498 ((Openable) element).closeBuffer();
501 throw newNotPresentException();
503 if (!hadTemporaryCache) {
504 manager.putInfos(this, newElements);
507 if (!hadTemporaryCache) {
508 manager.resetTemporaryCache();
516 public void open(IProgressMonitor pm) throws JavaModelException {
521 * Opens a buffer on the contents of this element, and returns the buffer,
522 * or returns <code>null</code> if opening fails. By default, do nothing -
523 * subclasses that have buffers must override as required.
525 protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
530 * Open the parent element if necessary.
532 protected void openParent(Object childInfo, HashMap newElements,
533 IProgressMonitor pm) throws JavaModelException {
535 Openable openableParent = (Openable) getOpenableParent();
536 if (openableParent != null && !openableParent.isOpen()) {
537 openableParent.generateInfos(openableParent.createElementInfo(),
543 // * Open an <code>Openable</code> that is known to be closed (no check for
544 // <code>isOpen()</code>).
546 // protected void openWhenClosed(IProgressMonitor pm) throws
547 // JavaModelException {
550 // if (JavaModelManager.VERBOSE){
551 // System.out.println("OPENING Element ("+ Thread.currentThread()+"): " +
552 // this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
555 // // 1) Parent must be open - open the parent if necessary
558 // // 2) create the new element info and open a buffer if needed
559 // OpenableElementInfo info = createElementInfo();
560 // if (isSourceElement()) {
561 // this.openBuffer(pm);
564 // // 3) build the structure of the openable
565 // buildStructure(info, pm);
567 // // 4) anything special
570 // // if (JavaModelManager.VERBOSE) {
571 // // System.out.println("-> Package cache size = " +
572 // JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$
573 // // System.out.println("-> Openable cache filling ratio = " +
574 // JavaModelManager.getJavaModelManager().cache.openableFillingRatio() +
575 // "%"); //$NON-NLS-1$//$NON-NLS-2$
578 // // if any problems occuring openning the element, ensure that it's info
579 // // does not remain in the cache (some elements, pre-cache their info
580 // // as they are being opened).
581 // } catch (JavaModelException e) {
582 // JavaModelManager.getJavaModelManager().removeInfo(this);
588 * Answers true if the parent exists (null parent is answering true)
591 protected boolean parentExists() {
593 IJavaElement parent = this.getParent();
596 return parent.exists();
600 * Returns whether the corresponding resource or associated file exists
602 protected boolean resourceExists() {
603 IWorkspace workspace = ResourcesPlugin.getWorkspace();
604 if (workspace == null)
605 return false; // workaround for
606 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069
607 return JavaModel.getTarget(workspace.getRoot(), this.getPath()
608 .makeRelative(), // ensure path is relative (see
609 // http://dev.eclipse.org/bugs/show_bug.cgi?id=22517)
616 public void save(IProgressMonitor pm, boolean force)
617 throws JavaModelException {
619 || this.getResource().getResourceAttributes().isReadOnly()) {
620 throw new JavaModelException(new JavaModelStatus(
621 IJavaModelStatusConstants.READ_ONLY, this));
623 IBuffer buf = getBuffer();
624 if (buf != null) { // some Openables (like a JavaProject) don't have a
627 this.makeConsistent(pm); // update the element info of this
633 * Find enclosing package fragment root if any
635 public PackageFragmentRoot getPackageFragmentRoot() {
636 IJavaElement current = this;
638 if (current instanceof PackageFragmentRoot)
639 return (PackageFragmentRoot) current;
640 current = current.getParent();
641 } while (current != null);
645 // * @see ICodeAssist
646 // * @deprecated - use codeComplete(ICompilationUnit, ICompilationUnit, int,
647 // ICompletionRequestor) instead
650 // codeComplete(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
651 // cu, net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
652 // unitToSkip, int position, final ICodeCompletionRequestor requestor)
653 // throws JavaModelException {
655 // if (requestor == null){
656 // codeComplete(cu, unitToSkip, position, (ICompletionRequestor)null);
663 // new ICompletionRequestor(){
664 // public void acceptAnonymousType(char[] superTypePackageName,char[]
665 // superTypeName,char[][] parameterPackageNames,char[][]
666 // parameterTypeNames,char[][] parameterNames,char[] completionName,int
667 // modifiers,int completionStart,int completionEnd, int relevance) {
669 // public void acceptClass(char[] packageName, char[] className, char[]
670 // completionName, int modifiers, int completionStart, int completionEnd,
672 // requestor.acceptClass(packageName, className, completionName, modifiers,
673 // completionStart, completionEnd);
675 // public void acceptError(IProblem error) {
676 // if (true) return; // was disabled in 1.0
680 // ResourcesPlugin.getWorkspace().getRoot().createMarker(IJavaModelMarker.TRANSIENT_PROBLEM);
681 // marker.setAttribute(IJavaModelMarker.ID, error.getID());
682 // marker.setAttribute(IMarker.CHAR_START, error.getSourceStart());
683 // marker.setAttribute(IMarker.CHAR_END, error.getSourceEnd() + 1);
684 // marker.setAttribute(IMarker.LINE_NUMBER, error.getSourceLineNumber());
685 // marker.setAttribute(IMarker.MESSAGE, error.getMessage());
686 // marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
687 // requestor.acceptError(marker);
688 // } catch(CoreException e){
691 // public void acceptField(char[] declaringTypePackageName, char[]
692 // declaringTypeName, char[] name, char[] typePackageName, char[] typeName,
693 // char[] completionName, int modifiers, int completionStart, int
694 // completionEnd, int relevance) {
695 // requestor.acceptField(declaringTypePackageName, declaringTypeName, name,
696 // typePackageName, typeName, completionName, modifiers, completionStart,
699 // public void acceptInterface(char[] packageName,char[]
700 // interfaceName,char[] completionName,int modifiers,int completionStart,int
701 // completionEnd, int relevance) {
702 // requestor.acceptInterface(packageName, interfaceName, completionName,
703 // modifiers, completionStart, completionEnd);
705 // public void acceptKeyword(char[] keywordName,int completionStart,int
706 // completionEnd, int relevance){
707 // requestor.acceptKeyword(keywordName, completionStart, completionEnd);
709 // public void acceptLabel(char[] labelName,int completionStart,int
710 // completionEnd, int relevance){
711 // requestor.acceptLabel(labelName, completionStart, completionEnd);
713 // public void acceptLocalVariable(char[] name,char[] typePackageName,char[]
714 // typeName,int modifiers,int completionStart,int completionEnd, int
718 // public void acceptMethod(char[] declaringTypePackageName,char[]
719 // declaringTypeName,char[] selector,char[][] parameterPackageNames,char[][]
720 // parameterTypeNames,char[][] parameterNames,char[]
721 // returnTypePackageName,char[] returnTypeName,char[] completionName,int
722 // modifiers,int completionStart,int completionEnd, int relevance){
723 // // skip parameter names
724 // requestor.acceptMethod(declaringTypePackageName, declaringTypeName,
725 // selector, parameterPackageNames, parameterTypeNames,
726 // returnTypePackageName, returnTypeName, completionName, modifiers,
727 // completionStart, completionEnd);
729 // public void acceptMethodDeclaration(char[]
730 // declaringTypePackageName,char[] declaringTypeName,char[]
731 // selector,char[][] parameterPackageNames,char[][]
732 // parameterTypeNames,char[][] parameterNames,char[]
733 // returnTypePackageName,char[] returnTypeName,char[] completionName,int
734 // modifiers,int completionStart,int completionEnd, int relevance){
737 // public void acceptModifier(char[] modifierName,int completionStart,int
738 // completionEnd, int relevance){
739 // requestor.acceptModifier(modifierName, completionStart, completionEnd);
741 // public void acceptPackage(char[] packageName,char[] completionName,int
742 // completionStart,int completionEnd, int relevance){
743 // requestor.acceptPackage(packageName, completionName, completionStart,
746 // public void acceptType(char[] packageName,char[] typeName,char[]
747 // completionName,int completionStart,int completionEnd, int relevance){
748 // requestor.acceptType(packageName, typeName, completionName,
749 // completionStart, completionEnd);
751 // public void acceptVariableName(char[] typePackageName,char[]
752 // typeName,char[] name,char[] completionName,int completionStart,int
753 // completionEnd, int relevance){