/******************************************************************************* * Copyright (c) 2004, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.jdt.internal.core; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.BufferChangedEvent; import org.eclipse.jdt.core.CompletionRequestor; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IBufferChangedListener; import org.eclipse.jdt.core.IBufferFactory; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.internal.core.JavaModelStatus; import org.eclipse.jdt.internal.core.util.Util; import java.io.File; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * @author Evgen Vidolob */ public abstract class Openable extends JavaElement implements IOpenable, IBufferChangedListener { protected Openable(JavaElement parent, JavaModelManager manager) { super(parent, manager); } // @Override // public String toString() { // return String.valueOf(hashCode()); // } /** * 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()) { manager.getElementsOutOfSynchWithBuffers().remove(this); getBufferManager().removeBuffer(event.getBuffer()); } else { manager.getElementsOutOfSynchWithBuffers().add(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, File 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, CompletionRequestor requestor, WorkingCopyOwner owner, ITypeRoot typeRoot, IProgressMonitor monitor) throws JavaModelException { // if (requestor == null) { // throw new IllegalArgumentException("Completion requestor cannot be null"); //$NON-NLS-1$ // } // PerformanceStats performanceStats = CompletionEngine.PERF // ? PerformanceStats.getStats(JavaModelManager.COMPLETION_PERF, this) // : null; // if(performanceStats != null) { // performanceStats.startRun(new String(cu.getFileName()) + " at " + position); //$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 = project.newSearchableNameEnvironment(owner); // // // set unit to skip // environment.unitToSkip = unitToSkip; // // // code complete // CompletionEngine engine = new CompletionEngine(environment, requestor, project.getOptions(true), project, owner, monitor); // engine.complete(cu, position, 0, typeRoot); // if(performanceStats != null) { // performanceStats.endRun(); // } // if (NameLookup.VERBOSE) { // System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + environment // .nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ // System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + environment // .nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ // } throw new UnsupportedOperationException(); } protected IJavaElement[] codeSelect(org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu, int offset, int length, WorkingCopyOwner owner) throws JavaModelException { // PerformanceStats performanceStats = SelectionEngine.PERF // ? PerformanceStats.getStats(JavaModelManager.SELECTION_PERF, this) // : null; // if(performanceStats != null) { // performanceStats.startRun(new String(cu.getFileName()) + " at [" + offset + "," + length + "]"); //$NON-NLS-1$ // $NON-NLS-2$ //$NON-NLS-3$ // } // // JavaProject project = (JavaProject)getJavaProject(); // SearchableEnvironment environment = 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 JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS)); // } // // // fix for 1FVXGDK // SelectionEngine engine = new SelectionEngine(environment, requestor, project.getOptions(true), owner); // engine.select(cu, offset, offset + length - 1); // // if(performanceStats != null) { // performanceStats.endRun(); // } // if (NameLookup.VERBOSE) { // System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + environment // .nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ // System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + environment // .nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ // } // return requestor.getElements(); throw new UnsupportedOperationException(); } /* * Returns a new element info for this element. */ protected Object createElementInfo() { return new OpenableElementInfo(); } /** * @see IJavaElement */ public boolean exists() { if (manager.getInfo(this) != null) return true; switch (getElementType()) { case IJavaElement.PACKAGE_FRAGMENT: PackageFragmentRoot root = getPackageFragmentRoot(); if (root.isArchive()) { // pkg in a jar -> need to open root to know if this pkg exists JarPackageFragmentRootInfo rootInfo; try { rootInfo = (JarPackageFragmentRootInfo)root.getElementInfo(); } catch (JavaModelException e) { return false; } return rootInfo.rawPackageInfo.containsKey(((PackageFragment)this).names); } break; case IJavaElement.CLASS_FILE: if (getPackageFragmentRoot().isArchive()) { // class file in a jar -> need to open this class file to know if it exists return super.exists(); } break; } return validateExistence(resource()).isOK(); } public String findRecommendedLineSeparator() throws JavaModelException { IBuffer buffer = getBuffer(); String source = buffer == null ? null : buffer.getContents(); return Util.getLineSeparator(source, getJavaProject()); } protected void generateInfos(Object info, HashMap newElements, IProgressMonitor monitor) throws JavaModelException { if (JavaModelCache.VERBOSE) { String element; switch (getElementType()) { case JAVA_PROJECT: element = "project"; //$NON-NLS-1$ break; case PACKAGE_FRAGMENT_ROOT: element = "root"; //$NON-NLS-1$ break; case PACKAGE_FRAGMENT: element = "package"; //$NON-NLS-1$ break; case CLASS_FILE: element = "class file"; //$NON-NLS-1$ break; case COMPILATION_UNIT: element = "compilation unit"; //$NON-NLS-1$ break; default: element = "element"; //$NON-NLS-1$ } System.out.println( Thread.currentThread() + " OPENING " + element + " " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$ } // open its ancestors if needed openAncestors(newElements, monitor); // validate existence File underlResource = resource(); IStatus status = validateExistence(underlResource); if (!status.isOK()) throw newJavaModelException(status); if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); // 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, underlResource); 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 (JavaModelCache.VERBOSE) { System.out.println(manager.cacheToString("-> ")); //$NON-NLS-1$ } } /** * 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); } if (buffer instanceof NullBuffer) { return null; } 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 manager.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(getElementName()); 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 = 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 manager.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 IJavaElement */ public boolean isStructureKnown() throws JavaModelException { return ((OpenableElementInfo)getElementInfo()).isStructureKnown(); } /** * @see IOpenable */ public void makeConsistent(IProgressMonitor monitor) throws JavaModelException { // only compilation units can be inconsistent // other openables cannot be inconsistent so default is to do nothing } /** * @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; } public IResource getResource() { // PackageFragmentRoot root = getPackageFragmentRoot(); // if (root != null) { // if (root.isExternal()) // return null; // if (root.isArchive()) // return root.resource(root); // } // return resource(root); throw new UnsupportedOperationException(); } public File resource() { PackageFragmentRoot root = getPackageFragmentRoot(); if (root != null && root.isArchive()) return root.resource(root); return resource(root); } protected abstract File resource(PackageFragmentRoot root); /** * Returns whether the corresponding resource or associated file exists */ protected boolean resourceExists(File underlyingResource) { return underlyingResource.exists(); } /** * @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); makeConsistent(pm); // update the element info of this element } } /** * Find enclosing package fragment root if any */ public PackageFragmentRoot getPackageFragmentRoot() { return (PackageFragmentRoot)getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); } /* * Validates the existence of this openable. Returns a non ok status if it doesn't exist. */ abstract protected IStatus validateExistence(File underlyingResource); /* * Opens the ancestors of this openable that are not yet opened, validating their existence. */ protected void openAncestors(HashMap newElements, IProgressMonitor monitor) throws JavaModelException { Openable openableParent = (Openable)getOpenableParent(); if (openableParent != null && !openableParent.isOpen()) { openableParent.generateInfos(openableParent.createElementInfo(), newElements, monitor); } } }