/******************************************************************************* * Copyright (c) 2011, 2014 Wind River 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 * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.runtime.model; import java.util.UUID; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.tcf.te.runtime.activator.CoreBundleActivator; import org.eclipse.tcf.te.runtime.interfaces.tracing.ITraceIds; import org.eclipse.tcf.te.runtime.model.interfaces.IContainerModelNode; import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode; import org.eclipse.tcf.te.runtime.model.interfaces.IModelNodeProvider; import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; /** * A common model node implementation. * <p> * <b>Note:</b> The model node implementation is not thread-safe. Clients requiring * a thread-safe implementation should subclass the properties container and * overwrite {@link #checkThreadAccess()}. */ public class ModelNode extends PropertiesContainer implements IModelNode, IModelNodeProvider { // Reference to the parent model node private IContainerModelNode parent = null; // Flag to remember the dirty state of the model node. private boolean dirty; // Flag to remember the pending state of the model node. private boolean pending; // Flag to control if property change events are suppressed // until the model node is added to a parent container model node. protected boolean suppressEventsOnNullParent = true; // Flag to control if the node parent can change after set // to a non-null value. Default is that the node parent cannot // change after set to a non-null value. protected boolean allowSetParentOnNonNullParent = false; /** * Constructor. */ public ModelNode() { super(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#getParent() */ @Override public final IContainerModelNode getParent() { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ return parent; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.model.interfaces.IModelNode#getParent(java.lang.Class) */ @SuppressWarnings("unchecked") @Override public final <V extends IContainerModelNode> V getParent(Class<V> nodeType) { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ if (this.parent != null) { if (nodeType.isInstance(this.parent)) { return (V)this.parent; } return this.parent.getParent(nodeType); } return null; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#setParent(org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode) */ @Override public final void setParent(IContainerModelNode parent) { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ if (this.parent != null && !allowSetParentOnNonNullParent) { throw new IllegalStateException("Model node already associated with a parent container model node!"); //$NON-NLS-1$ } this.parent = parent; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#move(org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode) */ @Override public final void move(IContainerModelNode newParent) { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ Assert.isNotNull(newParent); // If the node is associated with a parent container, remove the node from // the container node non-recursive (keeping all children if being ourself // a container model node) if (this.parent != null) { // Remove the node from the old parent container if (!this.parent.contains(this) || this.parent.remove(this, false)) { // Unset the parent reference (will enable the add to the new container) this.parent = null; } } // Re-add to the new parent. This may cause an // IllegalStateException if the previous removal from // the old parent container failed. newParent.add(this); return; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#isVisible() */ @Override public boolean isVisible() { return getBooleanProperty(PROPERTY_VISIBLE); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#getError() */ @Override public String getError() { return getStringProperty(PROPERTY_ERROR); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#getName() */ @Override public String getName() { String name = (String)super.getProperty(PROPERTY_NAME); return name != null ? name : ""; //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#getDescription() */ @Override public String[] getDescription() { return new String[]{}; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#getImageId() */ @Override public String getImageId() { return null; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.nodes.PropertiesContainer#toString() */ @Override public String toString() { StringBuilder toString = new StringBuilder(getClass().getName()); toString.append("{"); //$NON-NLS-1$ toString.append(super.toString()); toString.append("}"); //$NON-NLS-1$ return toString.toString(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.nodes.PropertiesContainer#dropEvent(java.lang.Object, java.lang.String, java.lang.Object, java.lang.Object) */ @Override protected boolean dropEvent(Object source, String key, Object oldValue, Object newValue) { boolean drop = super.dropEvent(source, key, oldValue, newValue); if (drop || PROPERTY_IS_GHOST.equals(key)) { if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_EVENTS)) { CoreBundleActivator.getTraceHandler().trace("Drop change event (hidden property)\n\t\t" + //$NON-NLS-1$ "for eventId = " + key, //$NON-NLS-1$ 0, ITraceIds.TRACE_EVENTS, IStatus.WARNING, this); } return true; } // If the parent is null, it must be allowed to fire change events explicitly if (parent == null && suppressEventsOnNullParent) { if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_EVENTS)) { CoreBundleActivator.getTraceHandler().trace("Drop change event (null parent)\n\t\t" + //$NON-NLS-1$ "for eventId = " + key, //$NON-NLS-1$ 0, ITraceIds.TRACE_EVENTS, IStatus.WARNING, this); } return true; } return false; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNodeProvider#getModelNode() */ @Override public final IModelNode getModelNode() { return this; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule) */ @Override public boolean contains(ISchedulingRule rule) { // We deal only with scheduling rules we know about (as the interface // declaration of ISchedulingRule#contains requests). if (rule instanceof IModelNode) { // The IModelNode itself is an leaf node and cannot have children. // Therefore, the IModelNode can contains only itself. return rule == this; } // If we don't know about the scheduling rule, we must return false. return false; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule) */ @Override public boolean isConflicting(ISchedulingRule rule) { // We deal only with scheduling rules we know about (as the interface // declaration of ISchedulingRule#contains requests). if (rule instanceof IModelNode) { // The IModelNode itself is an leaf node and cannot have children. // Therefore, the IModelNode can conflict only with itself. return rule == this; } // If we don't know about the scheduling rule, we must return false. return false; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#setDirty(boolean) */ @Override public final void setDirty(boolean dirty) { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ boolean oldDirtyState = this.dirty; this.dirty = dirty; fireChangeEvent("dirty", Boolean.valueOf(oldDirtyState), Boolean.valueOf(this.dirty)); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#isDirty() */ @Override public final boolean isDirty() { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ return dirty; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#setPending(boolean) */ @Override public final void setPending(boolean pending) { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ boolean oldPendingState = this.pending; this.pending = pending; fireChangeEvent("pending", Boolean.valueOf(oldPendingState), Boolean.valueOf(this.pending)); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#isPending() */ @Override public final boolean isPending() { Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$ return pending; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode#find(java.util.UUID) */ @Override public IModelNode find(UUID uuid) { if (getUUID().equals(uuid)) { return this; } return null; } }