/******************************************************************************* * Copyright (c) 2000, 2010 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.util.Enumeration; import java.util.Map; import org.eclipse.cdt.core.model.BufferChangedEvent; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.IBuffer; import org.eclipse.cdt.core.model.IBufferChangedListener; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICModelStatusConstants; import org.eclipse.cdt.core.model.IOpenable; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; public abstract class Openable extends Parent implements IOpenable { protected IResource resource; public Openable(ICElement parent, IPath path, int type) { // Check if the file is under the workspace. this(parent, ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(path), path.lastSegment(), type); } public Openable(ICElement parent, IResource resource, int type) { this(parent, resource, resource.getName(), type); } public Openable(ICElement parent, IResource res, String name, int type) { super(parent, name, type); resource = res; } @Override public IResource getResource() { return resource; } /** * 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()) { CModelManager.getDefault().getElementsOutOfSynchWithBuffers().remove(this); getBufferManager().removeBuffer(event.getBuffer()); } else { CModelManager.getDefault().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(OpenableInfo info, IProgressMonitor pm, Map<ICElement, CElementInfo> newElements, IResource underlyingResource) throws CModelException; /** * Close the buffer associated with this element, if any. */ protected void closeBuffer() { 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. */ @Override protected void closing(Object info) throws CModelException { closeBuffer(); } /** * @see org.eclipse.cdt.core.model.IOpenable#getBuffer() */ public IBuffer getBuffer() throws CModelException { 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; } 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(); } /** * Returns true if this element may have an associated source buffer, * otherwise false. Subclasses must override as required. */ protected boolean hasBuffer() { return false; } /** * @see org.eclipse.cdt.core.model.IOpenable#hasUnsavedChanges() */ public boolean hasUnsavedChanges() throws CModelException{ if (isReadOnly() || !isOpen()) { return false; } IBuffer buf = this.getBuffer(); if (buf != null && buf.hasUnsavedChanges()) { return true; } // for roots and projects must check open buffers // to see if they have an child with unsaved changes if (fType == C_MODEL || fType == C_PROJECT) { Enumeration<IBuffer> openBuffers= getBufferManager().getOpenBuffers(); while (openBuffers.hasMoreElements()) { IBuffer buffer= openBuffers.nextElement(); if (buffer.hasUnsavedChanges()) { ICElement owner= (ICElement) buffer.getOwner(); if (isAncestorOf(owner)) { return true; } } } } return false; } /** * Subclasses must override as required. * * @see org.eclipse.cdt.core.model.IOpenable#isConsistent() */ public boolean isConsistent() throws CModelException { return true; } /** * @see org.eclipse.cdt.core.model.IOpenable#isOpen() */ public boolean isOpen() { return CModelManager.getDefault().getInfo(this) != null; } @Override public boolean isStructureKnown() throws CModelException { CElementInfo info = (CElementInfo) CModelManager.getDefault().getInfo(this); return info != null && info.isStructureKnown(); } /** * 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 org.eclipse.cdt.core.model.IOpenable#makeConsistent(IProgressMonitor) */ public void makeConsistent(IProgressMonitor pm) throws CModelException { makeConsistent(pm, false); } public void makeConsistent(IProgressMonitor monitor, boolean forced) throws CModelException { // only translation units can be inconsistent // other openables cannot be inconsistent so default is to do nothing } /** * @see org.eclipse.cdt.core.model.IOpenable#open(IProgressMonitor) */ public void open(IProgressMonitor pm) throws CModelException { 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) throws CModelException { return null; } /** * Open the parent element if necessary. */ protected void openParent(CElementInfo childInfo, Map<ICElement, CElementInfo> newElements, IProgressMonitor pm) throws CModelException { Openable openableParent = (Openable) getOpenableParent(); if (openableParent != null && !openableParent.isOpen()){ openableParent.generateInfos(openableParent.createElementInfo(), newElements, pm); } } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.model.CElement#generateInfos(java.lang.Object, java.util.Map, org.eclipse.core.runtime.IProgressMonitor) */ @Override protected void generateInfos(CElementInfo info, Map<ICElement, CElementInfo> newElements, IProgressMonitor monitor) throws CModelException { if (CModelManager.VERBOSE){ System.out.println("OPENING Element ("+ Thread.currentThread()+"): " + this); //$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 { OpenableInfo openableInfo = (OpenableInfo) info; boolean isStructureKnown = buildStructure(openableInfo, monitor, newElements, getResource()); openableInfo.setIsStructureKnown(isStructureKnown); } catch (CModelException e) { newElements.remove(this); throw e; } // remove out of sync buffer for this element CModelManager.getDefault().getElementsOutOfSynchWithBuffers().remove(this); } /** * @see org.eclipse.cdt.core.model.IOpenable#save(IProgressMonitor, boolean) */ public void save(IProgressMonitor pm, boolean force) throws CModelException { IResource res = getResource(); if (res != null) { ResourceAttributes attributes = res.getResourceAttributes(); if (attributes != null && attributes.isReadOnly()) { throw new CModelException(new CModelStatus(ICModelStatusConstants.READ_ONLY, this)); } } // check also the underlying resource if (isReadOnly()) { throw new CModelException(new CModelStatus(ICModelStatusConstants.READ_ONLY, this)); } IBuffer buf = getBuffer(); if (buf != null) { buf.save(pm, force); this.makeConsistent(pm); // update the element info of this element } } /** * Find enclosing package fragment root if any */ public SourceRoot getSourceRoot() { ICElement current = this; do { if (current instanceof SourceRoot) return (SourceRoot) current; current = current.getParent(); } while (current != null); return null; } }