/******************************************************************************* * Copyright (c) 2014, 2015 Cisco Systems, Inc. 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 * *******************************************************************************/ package com.cisco.yangide.core.model; import java.util.HashMap; import java.util.Map; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import com.cisco.yangide.core.CoreUtil; import com.cisco.yangide.core.IOpenable; import com.cisco.yangide.core.OpenableElementInfo; import com.cisco.yangide.core.YangModelException; import com.cisco.yangide.core.buffer.BufferChangedEvent; import com.cisco.yangide.core.buffer.BufferManager; import com.cisco.yangide.core.buffer.IBuffer; import com.cisco.yangide.core.buffer.IBufferChangedListener; import com.cisco.yangide.core.buffer.NullBuffer; /** * @author Konstantin Zaitsev * @date Jun 24, 2014 */ public abstract class YangElement implements IOpenable, IBufferChangedListener { public static final IOpenable[] NO_ELEMENTS = new IOpenable[0]; private final IOpenable parent; public YangElement(IOpenable parent) { this.parent = parent; } @Override public void bufferChanged(BufferChangedEvent event) { if (event.getBuffer().isClosed()) { YangModelManager.getYangModelManager().getElementsOutOfSynchWithBuffers().remove(this); getBufferManager().removeBuffer(event.getBuffer()); } else { YangModelManager.getYangModelManager().getElementsOutOfSynchWithBuffers().add(this); } } @Override public IBuffer getBuffer() throws YangModelException { if (hasBuffer()) { // ensure element is open Object info = getElementInfo(null); 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; } } public boolean canBeRemovedFromCache() { try { return !hasUnsavedChanges(); } catch (YangModelException e) { return false; } } @Override public boolean exists() { try { getElementInfo(null); return true; } catch (YangModelException e) { // element doesn't exist: return false } return false; } @Override public String toStringWithAncestors() { StringBuffer sb = new StringBuffer(getName()); IOpenable p = getParent(); if (p != null) { sb.append("[").append(p.toStringWithAncestors()).append("]"); } return sb.toString(); } @Override public IOpenable getParent() { return this.parent; } @Override public IPath getPath() { return null; } @Override public IResource getResource() { return null; } @Override public boolean isReadOnly() { return false; } /** * @param buffer * @return */ public boolean canBufferBeRemovedFromCache(IBuffer buffer) { return !buffer.hasUnsavedChanges(); } @Override public void close() throws YangModelException { if (hasBuffer()) { IBuffer buffer = getBufferManager().getBuffer(this); if (buffer != null) { buffer.close(); buffer.removeBufferChangedListener(this); } } YangModelManager.getYangModelManager().removeInfoAndChildren(this); } public boolean hasUnsavedChanges() throws YangModelException { return false; } @Override public boolean isOpen() { return YangModelManager.getYangModelManager().getInfo(this) != null; } @Override public void open(IProgressMonitor progress) throws YangModelException { getElementInfo(progress); } public Object getElementInfo(IProgressMonitor monitor) throws YangModelException { YangModelManager manager = YangModelManager.getYangModelManager(); Object info = manager.getInfo(this); if (info != null) { return info; } return openWhenClosed(createElementInfo(), monitor); } public IOpenable[] getChildren() throws YangModelException { Object elementInfo = getElementInfo(null); if (elementInfo instanceof OpenableElementInfo) { return ((OpenableElementInfo) elementInfo).getChildren(); } else { return NO_ELEMENTS; } } @Override public String getName() { return getPath().toString(); } @Override public int hashCode() { if (this.parent == null) { return super.hashCode(); } return CoreUtil.combineHashCodes(getName().hashCode(), this.parent.hashCode()); } @Override public boolean equals(Object o) { if (this == o) { return true; } // Yang model parent is null if (this.parent == null) { return super.equals(o); } // assume instanceof check is done in subclass YangElement other = (YangElement) o; return getName().equals(other.getName()) && this.parent.equals(other.parent); } protected void generateInfos(OpenableElementInfo info, HashMap<IOpenable, OpenableElementInfo> newElements, IProgressMonitor monitor) throws YangModelException { // open its ancestors if needed openAncestors(newElements, monitor); // validate existence IResource underlResource = getResource(); IStatus status = validateExistence(underlResource); if (!status.isOK()) { if (status.getException() != null) { throw new YangModelException(status.getException(), status.getCode()); } else { throw new YangModelException(status.getMessage()); } } 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 = info; boolean isStructureKnown = buildStructure(openableElementInfo, monitor, newElements, underlResource); openableElementInfo.setIsStructureKnown(isStructureKnown); } catch (YangModelException e) { newElements.remove(this); throw e; } // remove out of sync buffer for this element YangModelManager.getYangModelManager().getElementsOutOfSynchWithBuffers().remove(this); } protected void openAncestors(HashMap<IOpenable, OpenableElementInfo> newElements, IProgressMonitor monitor) throws YangModelException { YangElement openableParent = (YangElement) getParent(); if (openableParent != null && !openableParent.isOpen()) { openableParent.generateInfos(openableParent.createElementInfo(), newElements, monitor); } } /** * 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 YangModelException { return null; } protected OpenableElementInfo createElementInfo() { return new OpenableElementInfo(); } protected boolean hasBuffer() { return false; } /* * Opens an <code>Openable</code> that is known to be closed (no check for * <code>isOpen()</code>). Returns the created element info. */ protected OpenableElementInfo openWhenClosed(OpenableElementInfo info, IProgressMonitor monitor) throws YangModelException { YangModelManager manager = YangModelManager.getYangModelManager(); HashMap<IOpenable, OpenableElementInfo> newElements = new HashMap<IOpenable, OpenableElementInfo>(); generateInfos(info, newElements, monitor); if (info == null) { info = newElements.get(this); } manager.putInfos(this, newElements); return info; } protected BufferManager getBufferManager() { return BufferManager.getDefaultBufferManager(); } /** * 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<IOpenable, OpenableElementInfo> newElements, IResource underlyingResource) throws YangModelException; /* * Validates the existence of this openable. Returns a non ok status if it doesn't exist. */ abstract protected IStatus validateExistence(IResource underlyingResource); /** * @return element type. */ public abstract YangElementType getElementType(); }