| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core; |
| |
| import java.text.NumberFormat; |
| import java.util.*; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jdt.core.*; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.internal.codeassist.CompletionEngine; |
| import org.eclipse.jdt.internal.codeassist.SelectionEngine; |
| |
| |
| /** |
| * Abstract class for implementations of java elements which are IOpenable. |
| * |
| * @see IJavaElement |
| * @see IOpenable |
| */ |
| public abstract class Openable extends JavaElement implements IOpenable, IBufferChangedListener { |
| |
| protected Openable(JavaElement parent, String name) { |
| super(parent, name); |
| } |
| /** |
| * The buffer associated with this element has changed. Registers |
| * this element as being out of synch with its buffer's contents. |
| * If the buffer has been closed, this element is set as NOT out of |
| * synch with the contents. |
| * |
| * @see IBufferChangedListener |
| */ |
| public void bufferChanged(BufferChangedEvent event) { |
| if (event.getBuffer().isClosed()) { |
| JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this); |
| getBufferManager().removeBuffer(event.getBuffer()); |
| } else { |
| JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().put(this, this); |
| } |
| } |
| /** |
| * Builds this element's structure and properties in the given |
| * info object, based on this element's current contents (reuse buffer |
| * contents if this element has an open buffer, or resource contents |
| * if this element does not have an open buffer). Children |
| * are placed in the given newElements table (note, this element |
| * has already been placed in the newElements table). Returns true |
| * if successful, or false if an error is encountered while determining |
| * the structure of this element. |
| */ |
| protected abstract boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException; |
| /* |
| * Returns whether this element can be removed from the Java model cache to make space. |
| */ |
| public boolean canBeRemovedFromCache() { |
| try { |
| return !hasUnsavedChanges(); |
| } catch (JavaModelException e) { |
| return false; |
| } |
| } |
| /* |
| * Returns whether the buffer of this element can be removed from the Java model cache to make space. |
| */ |
| public boolean canBufferBeRemovedFromCache(IBuffer buffer) { |
| return !buffer.hasUnsavedChanges(); |
| } |
| /** |
| * Close the buffer associated with this element, if any. |
| */ |
| protected void closeBuffer() { |
| if (!hasBuffer()) return; // nothing to do |
| IBuffer buffer = getBufferManager().getBuffer(this); |
| if (buffer != null) { |
| buffer.close(); |
| buffer.removeBufferChangedListener(this); |
| } |
| } |
| /** |
| * This element is being closed. Do any necessary cleanup. |
| */ |
| protected void closing(Object info) { |
| closeBuffer(); |
| } |
| protected void codeComplete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu, org.eclipse.jdt.internal.compiler.env.ICompilationUnit unitToSkip, int position, ICompletionRequestor requestor, WorkingCopyOwner owner) throws JavaModelException { |
| if (requestor == null) { |
| throw new IllegalArgumentException("Completion requestor cannot be null"); //$NON-NLS-1$ |
| } |
| IBuffer buffer = getBuffer(); |
| if (buffer == null) { |
| return; |
| } |
| if (position < -1 || position > buffer.getLength()) { |
| throw new IllegalArgumentException("Completion position "+position+" is not located in supplied source range (0, "+buffer.getLength()+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| JavaProject project = (JavaProject) getJavaProject(); |
| SearchableEnvironment environment = (SearchableEnvironment) project.newSearchableNameEnvironment(owner); |
| |
| // set unit to skip |
| environment.unitToSkip = unitToSkip; |
| |
| // code complete |
| CompletionRequestorWrapper requestorWrapper = new CompletionRequestorWrapper(requestor, environment.nameLookup); |
| CompletionEngine engine = new CompletionEngine(environment, requestorWrapper, project.getOptions(true), project); |
| requestorWrapper.completionEngine = engine; |
| engine.complete(cu, position, 0); |
| } |
| protected IJavaElement[] codeSelect(org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu, int offset, int length, WorkingCopyOwner owner) throws JavaModelException { |
| |
| JavaProject project = (JavaProject)getJavaProject(); |
| SearchableEnvironment environment = (SearchableEnvironment) project.newSearchableNameEnvironment(owner); |
| |
| SelectionRequestor requestor= new SelectionRequestor(environment.nameLookup, this); |
| IBuffer buffer = getBuffer(); |
| if (buffer == null) { |
| return requestor.getElements(); |
| } |
| int end= buffer.getLength(); |
| if (offset < 0 || length < 0 || offset + length > end ) { |
| throw new IllegalArgumentException("Selected range ("+offset+ ", " + (offset+length)+") is not located in supplied source range (0, "+end+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| } |
| |
| // fix for 1FVXGDK |
| SelectionEngine engine = new SelectionEngine(environment, requestor, project.getOptions(true)); |
| engine.select(cu, offset, offset + length - 1); |
| return requestor.getElements(); |
| } |
| /* |
| * Returns a new element info for this element. |
| */ |
| protected Object createElementInfo() { |
| return new OpenableElementInfo(); |
| } |
| /** |
| * @see IJavaElement |
| */ |
| public boolean exists() { |
| JavaModelManager manager = JavaModelManager.getJavaModelManager(); |
| if (manager.getInfo(this) != null) return true; |
| if (!parentExists()) return false; |
| PackageFragmentRoot root = getPackageFragmentRoot(); |
| if (root != null |
| && (root == this || !root.isArchive())) { |
| return resourceExists(); |
| } |
| return super.exists(); |
| } |
| protected void generateInfos(Object info, HashMap newElements, IProgressMonitor monitor) throws JavaModelException { |
| |
| if (JavaModelManager.VERBOSE){ |
| System.out.println("OPENING Element ("+ Thread.currentThread()+"): " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| |
| // open the parent if necessary |
| openParent(info, newElements, monitor); |
| if (monitor != null && monitor.isCanceled()) return; |
| |
| // puts the info before building the structure so that questions to the handle behave as if the element existed |
| // (case of compilation units becoming working copies) |
| newElements.put(this, info); |
| |
| // build the structure of the openable (this will open the buffer if needed) |
| try { |
| OpenableElementInfo openableElementInfo = (OpenableElementInfo)info; |
| boolean isStructureKnown = buildStructure(openableElementInfo, monitor, newElements, getResource()); |
| openableElementInfo.setIsStructureKnown(isStructureKnown); |
| } catch (JavaModelException e) { |
| newElements.remove(this); |
| throw e; |
| } |
| |
| // remove out of sync buffer for this element |
| JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this); |
| |
| if (JavaModelManager.VERBOSE) { |
| System.out.println("-> Package cache size = " + JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$ |
| System.out.println("-> Openable cache filling ratio = " + NumberFormat.getInstance().format(JavaModelManager.getJavaModelManager().cache.openableFillingRatio()) + "%"); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| } |
| /** |
| * Note: a buffer with no unsaved changes can be closed by the Java Model |
| * since it has a finite number of buffers allowed open at one time. If this |
| * is the first time a request is being made for the buffer, an attempt is |
| * made to create and fill this element's buffer. If the buffer has been |
| * closed since it was first opened, the buffer is re-created. |
| * |
| * @see IOpenable |
| */ |
| public IBuffer getBuffer() throws JavaModelException { |
| if (hasBuffer()) { |
| // ensure element is open |
| Object info = getElementInfo(); |
| IBuffer buffer = getBufferManager().getBuffer(this); |
| if (buffer == null) { |
| // try to (re)open a buffer |
| buffer = openBuffer(null, info); |
| } |
| return buffer; |
| } else { |
| return null; |
| } |
| } |
| /** |
| * Answers the buffer factory to use for creating new buffers |
| * @deprecated |
| */ |
| public IBufferFactory getBufferFactory(){ |
| return getBufferManager().getDefaultBufferFactory(); |
| } |
| |
| /** |
| * Returns the buffer manager for this element. |
| */ |
| protected BufferManager getBufferManager() { |
| return BufferManager.getDefaultBufferManager(); |
| } |
| /** |
| * Return my underlying resource. Elements that may not have a |
| * corresponding resource must override this method. |
| * |
| * @see IJavaElement |
| */ |
| public IResource getCorrespondingResource() throws JavaModelException { |
| return getUnderlyingResource(); |
| } |
| /* |
| * @see IJavaElement |
| */ |
| public IOpenable getOpenable() { |
| return this; |
| } |
| |
| |
| |
| /** |
| * @see IJavaElement |
| */ |
| public IResource getUnderlyingResource() throws JavaModelException { |
| IResource parentResource = this.parent.getUnderlyingResource(); |
| if (parentResource == null) { |
| return null; |
| } |
| int type = parentResource.getType(); |
| if (type == IResource.FOLDER || type == IResource.PROJECT) { |
| IContainer folder = (IContainer) parentResource; |
| IResource resource = folder.findMember(this.name); |
| if (resource == null) { |
| throw newNotPresentException(); |
| } else { |
| return resource; |
| } |
| } else { |
| return parentResource; |
| } |
| } |
| |
| /** |
| * Returns true if this element may have an associated source buffer, |
| * otherwise false. Subclasses must override as required. |
| */ |
| protected boolean hasBuffer() { |
| return false; |
| } |
| /** |
| * @see IOpenable |
| */ |
| public boolean hasUnsavedChanges() throws JavaModelException{ |
| |
| if (isReadOnly() || !isOpen()) { |
| return false; |
| } |
| IBuffer buf = this.getBuffer(); |
| if (buf != null && buf.hasUnsavedChanges()) { |
| return true; |
| } |
| // for package fragments, package fragment roots, and projects must check open buffers |
| // to see if they have an child with unsaved changes |
| int elementType = getElementType(); |
| if (elementType == PACKAGE_FRAGMENT || |
| elementType == PACKAGE_FRAGMENT_ROOT || |
| elementType == JAVA_PROJECT || |
| elementType == JAVA_MODEL) { // fix for 1FWNMHH |
| Enumeration openBuffers= getBufferManager().getOpenBuffers(); |
| while (openBuffers.hasMoreElements()) { |
| IBuffer buffer= (IBuffer)openBuffers.nextElement(); |
| if (buffer.hasUnsavedChanges()) { |
| IJavaElement owner= (IJavaElement)buffer.getOwner(); |
| if (isAncestorOf(owner)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| /** |
| * Subclasses must override as required. |
| * |
| * @see IOpenable |
| */ |
| public boolean isConsistent() { |
| return true; |
| } |
| /** |
| * |
| * @see IOpenable |
| */ |
| public boolean isOpen() { |
| return JavaModelManager.getJavaModelManager().getInfo(this) != null; |
| } |
| /** |
| * Returns true if this represents a source element. |
| * Openable source elements have an associated buffer created |
| * when they are opened. |
| */ |
| protected boolean isSourceElement() { |
| return false; |
| } |
| /** |
| * @see IOpenable |
| */ |
| public void makeConsistent(IProgressMonitor monitor) throws JavaModelException { |
| if (isConsistent()) return; |
| |
| // close |
| JavaModelManager manager = JavaModelManager.getJavaModelManager(); |
| manager.removeInfoAndChildren(this); |
| |
| boolean hadTemporaryCache = manager.hasTemporaryCache(); |
| try { |
| HashMap newElements = manager.getTemporaryCache(); |
| openWhenClosed(newElements, monitor); |
| if (newElements.get(this) == null) { |
| // close any buffer that was opened for the new elements |
| Iterator iterator = newElements.keySet().iterator(); |
| while (iterator.hasNext()) { |
| IJavaElement element = (IJavaElement)iterator.next(); |
| if (element instanceof Openable) { |
| ((Openable)element).closeBuffer(); |
| } |
| } |
| throw newNotPresentException(); |
| } |
| if (!hadTemporaryCache) { |
| manager.putInfos(this, newElements); |
| } |
| } finally { |
| if (!hadTemporaryCache) { |
| manager.resetTemporaryCache(); |
| } |
| } |
| } |
| /** |
| * @see IOpenable |
| */ |
| public void open(IProgressMonitor pm) throws JavaModelException { |
| getElementInfo(pm); |
| } |
| |
| /** |
| * Opens a buffer on the contents of this element, and returns |
| * the buffer, or returns <code>null</code> if opening fails. |
| * By default, do nothing - subclasses that have buffers |
| * must override as required. |
| */ |
| protected IBuffer openBuffer(IProgressMonitor pm, Object info) throws JavaModelException { |
| return null; |
| } |
| |
| /** |
| * Open the parent element if necessary. |
| */ |
| protected void openParent(Object childInfo, HashMap newElements, IProgressMonitor pm) throws JavaModelException { |
| |
| Openable openableParent = (Openable)getOpenableParent(); |
| if (openableParent != null && !openableParent.isOpen()){ |
| openableParent.generateInfos(openableParent.createElementInfo(), newElements, pm); |
| } |
| } |
| |
| /** |
| * Answers true if the parent exists (null parent is answering true) |
| * |
| */ |
| protected boolean parentExists(){ |
| |
| IJavaElement parentElement = getParent(); |
| if (parentElement == null) return true; |
| return parentElement.exists(); |
| } |
| |
| /** |
| * Returns whether the corresponding resource or associated file exists |
| */ |
| protected boolean resourceExists() { |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| if (workspace == null) return false; // workaround for http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069 |
| return |
| JavaModel.getTarget( |
| workspace.getRoot(), |
| this.getPath().makeRelative(), // ensure path is relative (see http://dev.eclipse.org/bugs/show_bug.cgi?id=22517) |
| true) != null; |
| } |
| |
| /** |
| * @see IOpenable |
| */ |
| public void save(IProgressMonitor pm, boolean force) throws JavaModelException { |
| if (isReadOnly()) { |
| throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.READ_ONLY, this)); |
| } |
| IBuffer buf = getBuffer(); |
| if (buf != null) { // some Openables (like a JavaProject) don't have a buffer |
| buf.save(pm, force); |
| this.makeConsistent(pm); // update the element info of this element |
| } |
| } |
| |
| /** |
| * Find enclosing package fragment root if any |
| */ |
| public PackageFragmentRoot getPackageFragmentRoot() { |
| IJavaElement current = this; |
| do { |
| if (current instanceof PackageFragmentRoot) return (PackageFragmentRoot)current; |
| current = current.getParent(); |
| } while(current != null); |
| return null; |
| } |
| /** |
| * @see ICodeAssist |
| * @deprecated - use codeComplete(ICompilationUnit, ICompilationUnit, int, ICompletionRequestor) instead |
| */ |
| 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 { |
| |
| if (requestor == null){ |
| codeComplete(cu, unitToSkip, position, null, DefaultWorkingCopyOwner.PRIMARY); |
| return; |
| } |
| codeComplete( |
| cu, |
| unitToSkip, |
| position, |
| new ICompletionRequestor(){ |
| public void acceptAnonymousType(char[] superTypePackageName,char[] superTypeName,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) { |
| // ignore |
| } |
| public void acceptClass(char[] packageName, char[] className, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) { |
| requestor.acceptClass(packageName, className, completionName, modifiers, completionStart, completionEnd); |
| } |
| public void acceptError(IProblem error) { |
| if (true) return; // was disabled in 1.0 |
| |
| try { |
| IMarker marker = ResourcesPlugin.getWorkspace().getRoot().createMarker(IJavaModelMarker.TRANSIENT_PROBLEM); |
| marker.setAttribute(IJavaModelMarker.ID, error.getID()); |
| marker.setAttribute(IMarker.CHAR_START, error.getSourceStart()); |
| marker.setAttribute(IMarker.CHAR_END, error.getSourceEnd() + 1); |
| marker.setAttribute(IMarker.LINE_NUMBER, error.getSourceLineNumber()); |
| marker.setAttribute(IMarker.MESSAGE, error.getMessage()); |
| marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); |
| requestor.acceptError(marker); |
| } catch(CoreException e){ |
| // could not create marker: ignore |
| } |
| } |
| public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] fieldName, char[] typePackageName, char[] typeName, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) { |
| requestor.acceptField(declaringTypePackageName, declaringTypeName, fieldName, typePackageName, typeName, completionName, modifiers, completionStart, completionEnd); |
| } |
| public void acceptInterface(char[] packageName,char[] interfaceName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) { |
| requestor.acceptInterface(packageName, interfaceName, completionName, modifiers, completionStart, completionEnd); |
| } |
| public void acceptKeyword(char[] keywordName,int completionStart,int completionEnd, int relevance){ |
| requestor.acceptKeyword(keywordName, completionStart, completionEnd); |
| } |
| public void acceptLabel(char[] labelName,int completionStart,int completionEnd, int relevance){ |
| requestor.acceptLabel(labelName, completionStart, completionEnd); |
| } |
| public void acceptLocalVariable(char[] localVarName,char[] typePackageName,char[] typeName,int modifiers,int completionStart,int completionEnd, int relevance){ |
| // ignore |
| } |
| 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){ |
| // skip parameter names |
| requestor.acceptMethod(declaringTypePackageName, declaringTypeName, selector, parameterPackageNames, parameterTypeNames, returnTypePackageName, returnTypeName, completionName, modifiers, completionStart, completionEnd); |
| } |
| 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){ |
| // ignore |
| } |
| public void acceptModifier(char[] modifierName,int completionStart,int completionEnd, int relevance){ |
| requestor.acceptModifier(modifierName, completionStart, completionEnd); |
| } |
| public void acceptPackage(char[] packageName,char[] completionName,int completionStart,int completionEnd, int relevance){ |
| requestor.acceptPackage(packageName, completionName, completionStart, completionEnd); |
| } |
| public void acceptType(char[] packageName,char[] typeName,char[] completionName,int completionStart,int completionEnd, int relevance){ |
| requestor.acceptType(packageName, typeName, completionName, completionStart, completionEnd); |
| } |
| public void acceptVariableName(char[] typePackageName,char[] typeName,char[] varName,char[] completionName,int completionStart,int completionEnd, int relevance){ |
| // ignore |
| } |
| }, |
| DefaultWorkingCopyOwner.PRIMARY); |
| } |
| } |