/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.workspace; import java.util.Iterator; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.teiid.designer.core.ModelerCore; /** * OpenableImpl * * @since 8.0 */ public abstract class OpenableImpl extends ModelWorkspaceItemImpl implements Openable, InternalOpenable { /** * Construct an instance of OpenableImpl. */ protected OpenableImpl( final int type, final ModelWorkspaceItem parent, final String name ) { super(type, parent, name); } /** * Return my underlying resource. Elements that may not have a corresponding resource must override this method. * * @see ModelWorkspaceItem */ @Override public IResource getCorrespondingResource() throws ModelWorkspaceException { return getUnderlyingResource(); } /** * @see ModelWorkspaceItem */ @Override public Openable getOpenable() { return this; } /** * @see ModelWorkspaceItem */ @Override public IResource getUnderlyingResource() throws ModelWorkspaceException { IResource parentResource = fParent.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(fName); if (resource == null) { throw newNotPresentException(); } return resource; } return parentResource; } /** * 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( OpenableModelWorkspaceItemInfo info, IProgressMonitor monitor ) throws ModelWorkspaceException { if (monitor != null && monitor.isCanceled()) return; // remove existing (old) infos removeInfo(); info.setIsStructureKnown(generateInfos(info, monitor, getResource())); // 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 ModelWorkspaceManager.getModelWorkspaceManager().putInfo(this, info); } /** * Returns a new element info for this element. */ protected abstract OpenableModelWorkspaceItemInfo createItemInfo(); /** * This element is being closed. Do any necessary cleanup. */ @Override protected void closing( Object info ) { OpenableModelWorkspaceItemInfo openableInfo = (OpenableModelWorkspaceItemInfo)info; closeBuffer(openableInfo); super.closing(info); } /** * Open an <code>Openable</code> that is known to be closed (no check for <code>isOpen()</code>). */ @Override public void openWhenClosed( final IProgressMonitor pm ) throws ModelWorkspaceException { synchronized(ModelWorkspaceManager.getModelWorkspaceManager()) { // If a thread has had to wait before acquiring the lock then another thread may well // have already opened this Openable so check again if it is opened. if (isOpen()) return; try { if (ModelWorkspaceManager.VERBOSE) { System.out.println("OPENING Item (" + 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 OpenableModelWorkspaceItemInfo info = createItemInfo(); // 3) build the structure of the openable buildStructure(info, pm); // 4) anything special opening(info); if (ModelWorkspaceManager.VERBOSE) { System.out.println("-> Package cache size = " + ModelWorkspaceManager.getModelWorkspaceManager().cache.pkgSize()); //$NON-NLS-1$ } // 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 (ModelWorkspaceException e) { ModelWorkspaceManager.getModelWorkspaceManager().removeInfo(this); throw e; } } } /** * 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( OpenableModelWorkspaceItemInfo info, IProgressMonitor pm, IResource underlyingResource ) throws ModelWorkspaceException; @Override public boolean exists() { // ModelPackageFragmentRoot root = this.getPackageFragmentRoot(); // if (root == null || root == this || !root.isArchive()) { // return parentExists() && resourceExists(); // } else { // return super.exists(); // } return parentExists() && resourceExists(); } @Override public boolean hasChildren() throws ModelWorkspaceException { return getChildren().length > 0; } /** * @see Openable */ @Override public boolean hasUnsavedChanges() { if (isReadOnly() || !isOpen()) { return false; } if (hasBuffer()) { ModelBuffer buffer = getBufferManager().getOpenBuffer(this); if (buffer == null) { return false; } if (buffer.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 if (fType == MODEL_FOLDER || fType == MODEL_PROJECT || fType == MODEL_WORKSPACE) { // fix for 1FWNMHH Iterator openBuffers = getBufferManager().getOpenBuffers(); while (openBuffers.hasNext()) { ModelBuffer buffer = (ModelBuffer)openBuffers.next(); if (buffer.hasUnsavedChanges()) { ModelWorkspaceItem owner = (ModelWorkspaceItem)buffer.getOwner(); if (isAncestorOf(owner)) { return true; } } } } return false; } /** * Subclasses must override as required. * * @see Openable */ public boolean isConsistent() { return true; } /** * @see Openable */ @Override public boolean isOpen() { synchronized (ModelWorkspaceManager.getModelWorkspaceManager()) { return ModelWorkspaceManager.getModelWorkspaceManager().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 Openable */ public void makeConsistent( IProgressMonitor pm ) throws ModelWorkspaceException { if (!isConsistent()) { buildStructure((OpenableModelWorkspaceItemInfo)getItemInfo(), pm); } } /** * @see Openable */ @Override public void open( IProgressMonitor pm ) throws ModelWorkspaceException { if (!isOpen()) { // TODO: need to synchronize (Openable.open(IProgressMonitor) is API // TODO: could use getItemInfo instead this.openWhenClosed(pm); } } /** * Open the parent element if necessary */ protected void openParent( IProgressMonitor pm ) throws ModelWorkspaceException { OpenableImpl openableParent = (OpenableImpl)getOpenableParent(); if (openableParent != null) { if (!openableParent.isOpen()) { openableParent.openWhenClosed(pm); } } } /** * Answers true if the parent exists (null parent is answering true) */ protected boolean parentExists() { ModelWorkspaceItem 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 = ModelerCore.getWorkspace(); if (workspace == null) return false; // workaround for http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069 return ModelWorkspaceImpl.getTarget(workspace.getRoot(), this.getPath().makeRelative(), // ensure path is relative (see // http://dev.eclipse.org/bugs/show_bug.cgi?id=22517) true) != null; } /** * @see Openable */ @Override public void save( IProgressMonitor pm, boolean force ) throws ModelWorkspaceException { if (isReadOnly() || ModelUtil.isIResourceReadOnly(getResource())) { throw new ModelWorkspaceException(new ModelStatusImpl(ModelStatusConstants.READ_ONLY, this)); } ModelBuffer buf = getBuffer(); if (buf != null) { // some Openables (like a JavaProject) don't have a buffer buf.save(pm, force); // can't refresh inside this (threading issues) this.makeConsistent(pm); // update the element info of this element ((ModelBufferImpl)buf).refresh(pm); // refresh and update mod stamp here } } /** * Needed an special save method for JDBC Importer in order to create and save a model with the "locked" property in it. * * @param pm the process monitor * @throws ModelWorkspaceException throws exception */ public void forceSave( IProgressMonitor pm) throws ModelWorkspaceException { ModelBuffer buf = getBuffer(); if (buf != null) { // some Openables (like a JavaProject) don't have a buffer buf.save(pm, true); // can't refresh inside this (threading issues) this.makeConsistent(pm); // update the element info of this element ((ModelBufferImpl)buf).refresh(pm); // refresh and update mod stamp here } } // /** // * Find enclosing package fragment root if any // */ // public ModelPackageFragmentRoot getPackageFragmentRoot() { // ModelWorkspaceItem current = this; // do { // if (current instanceof ModelPackageFragmentRoot) return (ModelPackageFragmentRoot)current; // current = current.getParent(); // } while(current != null); // return null; // } // /** * Returns the buffer manager for this element. */ protected ModelBufferManager getBufferManager() { return ModelBufferManager.getDefaultBufferManager(); } // /** // * 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 Openable // */ // public synchronized ModelBuffer getBuffer() throws ModelWorkspaceException { // return getBuffer(false); // } /** * This is a hack to get around a problem with newly saved files not indicating that they exist (the call to refreshLocal), * and a problem with possible deadlock if the call to refreshLocal is placed within the synchronized {@link #getBuffer()} * method. */ public ModelBuffer getBufferHack() throws ModelWorkspaceException { final ModelBuffer buf = getBuffer(); if (!getResource().exists() && getResource().getLocation().toFile().exists()) { try { getResource().refreshLocal(IResource.DEPTH_INFINITE, null); } catch (final CoreException err) { throw new ModelWorkspaceException(err); } } return buf; } /** * 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 Openable */ public synchronized ModelBuffer getBuffer() throws ModelWorkspaceException { if (hasBuffer()) { // ensure element is open if (!isOpen()) { getItemInfo(); } ModelBuffer buffer = getBufferManager().getOpenBuffer(this); if (buffer == null) { // try to (re)open a buffer buffer = openBuffer(null); } return buffer; } return null; } /** * 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. */ @SuppressWarnings( "unused" ) protected ModelBuffer openBuffer( IProgressMonitor pm ) throws ModelWorkspaceException { return null; } // /** // * 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 ModelBuffer openBuffer(IProgressMonitor pm, final boolean force) throws ModelWorkspaceException { // return null; // } /** * Close the buffer associated with this element, if any. */ protected void closeBuffer( OpenableModelWorkspaceItemInfo info ) { if (!hasBuffer()) return; // nothing to do ModelBuffer buffer = getBufferManager().getOpenBuffer(this); if (buffer != null) { buffer.close(); getBufferManager().removeBuffer(buffer); // buffer.removeBufferChangedListener(this); } } /** * Returns true if this element may have an associated source buffer, otherwise false. Subclasses must override as required. */ protected boolean hasBuffer() { return false; } // /** // * 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 emfResourceChanged(BufferChangedEvent event) { // if (event.getBuffer().isClosed()) { // ModelWorkspaceManager.getModelWorkspaceManager().getElementsOutOfSynchWithBuffers().remove(this); // getBufferManager().removeBuffer(event.getBuffer()); // } else { // ModelWorkspaceManager.getModelWorkspaceManager().getElementsOutOfSynchWithBuffers().put(this, this); // } // } }