/******************************************************************************* * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.core; import java.text.NumberFormat; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.sourceforge.phpdt.core.BufferChangedEvent; import net.sourceforge.phpdt.core.IBuffer; import net.sourceforge.phpdt.core.IBufferChangedListener; import net.sourceforge.phpdt.core.IBufferFactory; import net.sourceforge.phpdt.core.ICodeAssist; import net.sourceforge.phpdt.core.IJavaElement; import net.sourceforge.phpdt.core.IJavaModelStatusConstants; import net.sourceforge.phpdt.core.IOpenable; import net.sourceforge.phpdt.core.IPackageFragmentRoot; import net.sourceforge.phpdt.core.IParent; import net.sourceforge.phpdt.core.JavaModelException; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; /** * 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; // /** // * Updates the info objects for this element and all of its children by // * removing the current infos, generating new infos, and then placing // * the new infos into the Java Model cache tables. // */ // protected void buildStructure(OpenableElementInfo info, IProgressMonitor // monitor) throws JavaModelException { // // if (monitor != null && monitor.isCanceled()) return; // // // remove existing (old) infos // removeInfo(); // HashMap newElements = new HashMap(11); // info.setIsStructureKnown(generateInfos(info, monitor, newElements, // getResource())); // JavaModelManager.getJavaModelManager().getElementsOutOfSynchWithBuffers().remove(this); // for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) { // IJavaElement key = (IJavaElement) iter.next(); // Object value = newElements.get(key); // JavaModelManager.getJavaModelManager().putInfo(key, value); // } // // // add the info for this at the end, to ensure that a getInfo cannot // reply null in case the LRU cache needs // // to be flushed. Might lead to performance issues. // // see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary // type // JavaModelManager.getJavaModelManager().putInfo(this, info); // } /* * 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); } } /** * Close the buffer associated with this element, if any. */ protected void closeBuffer(OpenableElementInfo info) { if (!hasBuffer()) return; // nothing to do IBuffer buffer = null; 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(); } // /** // * @see ICodeAssist // */ // protected void // codeComplete(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit // cu, net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit // unitToSkip, int position, ICompletionRequestor requestor) throws // JavaModelException { // if (requestor == null) { // throw new // IllegalArgumentException(ProjectPrefUtil.bind("codeAssist.nullRequestor")); // //$NON-NLS-1$ // } // IBuffer buffer = getBuffer(); // if (buffer == null) { // return; // } // if (position < -1 || position > buffer.getLength()) { // throw new JavaModelException(new // JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS)); // } // JavaProject project = (JavaProject) getJavaProject(); // SearchableEnvironment environment = (SearchableEnvironment) // project.getSearchableNameEnvironment(); // NameLookup nameLookup = project.getNameLookup(); // environment.unitToSkip = unitToSkip; // // CompletionEngine engine = new CompletionEngine(environment, new // CompletionRequestorWrapper(requestor,nameLookup), // project.getOptions(true), project); // engine.complete(cu, position, 0); // environment.unitToSkip = null; // } /** * @see ICodeAssist */ // protected IJavaElement[] // codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit // cu, int offset, int length) throws JavaModelException { // SelectionRequestor requestor= new // SelectionRequestor(((JavaProject)getJavaProject()).getNameLookup(), // this); // this.codeSelect(cu, offset, length, requestor); // return requestor.getElements(); // } /** * @see ICodeAssist */ // protected void // codeSelect(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit // cu, int offset, int length, ISelectionRequestor requestor) throws // JavaModelException { // IBuffer buffer = getBuffer(); // if (buffer == null) { // return; // } // int end= buffer.getLength(); // if (offset < 0 || length < 0 || offset + length > end ) { // throw new JavaModelException(new // JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS)); // } // // // fix for 1FVGGKF // JavaProject project = (JavaProject)getJavaProject(); // ISearchableNameEnvironment environment = // project.getSearchableNameEnvironment(); // // // fix for 1FVXGDK // SelectionEngine engine = new SelectionEngine(environment, requestor, // project.getOptions(true)); // engine.select(cu, offset, offset + length - 1); // } /* * Returns a new element info for this element. */ protected Object createElementInfo() { return new OpenableElementInfo(); } // /** // * 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 generateInfos(OpenableElementInfo info, // IProgressMonitor pm, Map newElements, IResource underlyingResource) // throws JavaModelException; 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 if (!isOpen()) { getElementInfo(); } IBuffer buffer = getBufferManager().getBuffer(this); if (buffer == null) { // try to (re)open a buffer buffer = openBuffer(null); } return buffer; } else { return null; } } /** * Answers the buffer factory to use for creating new buffers */ 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 = 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(name); if (resource == null) { throw newNotPresentException(); } else { return resource; } } else { return parentResource; } } public boolean exists() { IPackageFragmentRoot root = this.getPackageFragmentRoot(); if (root == null || root == this || !root.isArchive()) { return parentExists() && resourceExists(); } else { return super.exists(); } } /** * Returns true if this element may have an associated source buffer, * otherwise false. Subclasses must override as required. */ protected boolean hasBuffer() { return false; } /** * @see IParent */ public boolean hasChildren() throws JavaModelException { return getChildren().length > 0; } /** * @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() throws JavaModelException { return true; } /** * * @see IOpenable */ public boolean isOpen() { synchronized (JavaModelManager.getJavaModelManager()) { 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 pm) throws JavaModelException // { // if (!isConsistent()) { // buildStructure((OpenableElementInfo)getElementInfo(), pm); // } // } /** * @see IOpenable */ public void makeConsistent(IProgressMonitor monitor) throws JavaModelException { if (isConsistent()) return; // create a new info and make it the current info // (this will remove the info and its children just before storing the // new infos) JavaModelManager manager = JavaModelManager.getJavaModelManager(); 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 null if opening fails. By default, do nothing - * subclasses that have buffers must override as required. */ protected IBuffer openBuffer(IProgressMonitor pm) 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); } } // /** // * Open an Openable that is known to be closed (no check for // isOpen()). // */ // protected void openWhenClosed(IProgressMonitor pm) throws // JavaModelException { // try { // // if (JavaModelManager.VERBOSE){ // System.out.println("OPENING Element ("+ Thread.currentThread()+"): " + // this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$ // } // // // 1) Parent must be open - open the parent if necessary // openParent(pm); // // // 2) create the new element info and open a buffer if needed // OpenableElementInfo info = createElementInfo(); // if (isSourceElement()) { // this.openBuffer(pm); // } // // // 3) build the structure of the openable // buildStructure(info, pm); // // // 4) anything special // opening(info); // // // if (JavaModelManager.VERBOSE) { // // System.out.println("-> Package cache size = " + // JavaModelManager.getJavaModelManager().cache.pkgSize()); //$NON-NLS-1$ // // System.out.println("-> Openable cache filling ratio = " + // JavaModelManager.getJavaModelManager().cache.openableFillingRatio() + // "%"); //$NON-NLS-1$//$NON-NLS-2$ // // } // // // if any problems occuring openning the element, ensure that it's info // // does not remain in the cache (some elements, pre-cache their info // // as they are being opened). // } catch (JavaModelException e) { // JavaModelManager.getJavaModelManager().removeInfo(this); // throw e; // } // } /** * Answers true if the parent exists (null parent is answering true) * */ protected boolean parentExists() { IJavaElement parent = this.getParent(); if (parent == null) return true; return parent.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() || this.getResource().getResourceAttributes().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(net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit // cu, net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit // unitToSkip, int position, final ICodeCompletionRequestor requestor) // throws JavaModelException { // // if (requestor == null){ // codeComplete(cu, unitToSkip, position, (ICompletionRequestor)null); // 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) { // } // 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){ // } // } // public void acceptField(char[] declaringTypePackageName, char[] // declaringTypeName, char[] name, char[] typePackageName, char[] typeName, // char[] completionName, int modifiers, int completionStart, int // completionEnd, int relevance) { // requestor.acceptField(declaringTypePackageName, declaringTypeName, name, // 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[] name,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[] name,char[] completionName,int completionStart,int // completionEnd, int relevance){ // // ignore // } // }); // } }