/******************************************************************************* * 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.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.tcf.te.runtime.model.interfaces.IContainerModelNode; import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode; /** * A common model container 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 ContainerModelNode extends ModelNode implements IContainerModelNode { // Note: Do _not_ use sorted sets/trees here! The trees get not resorted if the element state // changes. We may loose the possibility to find the element again within the tree! private final List<IModelNode> childList = new ArrayList<IModelNode>(); // Lock to use for synchronization purpose private final Lock childListLock = new ReentrantLock(); // empty array public static final IModelNode[] EMPTY_MODEL_NODE_ARRAY = new IModelNode[0]; /** * Constructor. */ public ContainerModelNode() { super(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#getChildren() */ @Override public IModelNode[] getChildren() { return internalGetChildren(); } /** * Return the real child list. */ protected final IModelNode[] internalGetChildren() { // Create the list that will hold the copy (non-deep copy) List<IModelNode> children = new ArrayList<IModelNode>(); try { // Acquire the lock while copying the child references childListLock.lock(); // Add the children to the returned list copy children.addAll(childList); } finally { // Release the lock childListLock.unlock(); } return children.toArray(new IModelNode[children.size()]); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#getChildren(java.lang.Class) */ @Override @SuppressWarnings("unchecked") public <T> List<T> getChildren(Class<T> instanceOf) { // Create the list that will hold the found children being // a instance of the given class List<T> children = new ArrayList<T>(); try { // Acquire the lock while copying the child references childListLock.lock(); // Walk through all the children and check for the class for (IModelNode child : childList) { if (instanceOf.isInstance(child)) { children.add((T)child); } } } finally { // Release the look childListLock.unlock(); } return children; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#hasChildren() */ @Override public boolean hasChildren() { boolean hasChildren = false; try { childListLock.lock(); hasChildren = !childList.isEmpty(); } finally { childListLock.unlock(); } return hasChildren; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#add(org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode) */ @Override public boolean add(IModelNode node) { if (node != null) { try { childListLock.lock(); // set the parent if not set otherwise before. if (node.getParent() == null) { node.setParent(this); } else { Assert.isTrue(node.getParent() == this, "Attempt to add child node to " + getName() + " with this != node.getParent()!!!"); //$NON-NLS-1$ //$NON-NLS-2$ } childList.add(node); } finally { childListLock.unlock(); } fireChangeEvent(NOTIFY_ADDED, null, new IModelNode[] { node }); return true; } return false; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#remove(org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode, boolean) */ @Override public boolean remove(IModelNode node, boolean recursive) { if (node instanceof IContainerModelNode && recursive) { IContainerModelNode container = (IContainerModelNode)node; container.clear(); } boolean removed = false; // Removes the given node from this container try { childListLock.lock(); removed = childList.remove(node); } finally { childListLock.unlock(); } // Unlink the parent and fire the removed notification if the element got removed if (removed) { fireChangeEvent(NOTIFY_REMOVED, new IModelNode[] { node }, null); } return removed; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#removeAll(java.lang.Class) */ @Override public <T> boolean removeAll(Class<T> nodeType) { boolean removed = false; List<T> children; try { childListLock.lock(); children = getChildren(nodeType); removed |= childList.removeAll(children); } finally { childListLock.unlock(); } if (removed) { fireChangeEvent(NOTIFY_REMOVED, children, null); } return removed; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#clear() */ @Override public boolean clear() { boolean removed = false; boolean changed = setChangeEventsEnabled(false); IModelNode[] children = null; try { childListLock.lock(); children = internalGetChildren(); for (IModelNode element : children) { removed |= remove(element, true); } } finally { childListLock.unlock(); } if (changed) setChangeEventsEnabled(true); if (removed) { fireChangeEvent(NOTIFY_REMOVED, children, null); } return removed; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#size() */ @Override public int size() { int size = 0; try { childListLock.lock(); size = childList.size(); } finally { childListLock.unlock(); } return size; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.properties.PropertiesContainer#isEmpty() */ @Override public boolean isEmpty() { return super.isEmpty() && !hasChildren(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.nodes.IContainerModelNode#contains(org.eclipse.tcf.te.runtime.interfaces.nodes.IModelNode) */ @Override public boolean contains(IModelNode node) { if (node != null) { try { childListLock.lock(); return childList.contains(node); } finally { childListLock.unlock(); } } return false; } /** * Returns weather the scheduling rule of container model nodes is considering * children or not. If recursive scheduling rule locking is enabled, than any job * having a child of this container model node as scheduling rule, will conflict * with this container model node. * * @return <code>True</code> if recursive scheduling rule locking is enabled, <code>false</code> otherwise. */ protected boolean isSchedulingLockedRecursivly() { return true; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.nodes.ModelNode#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) and if recursive // scheduling rule locking is on. if (isSchedulingLockedRecursivly() && rule instanceof IModelNode) { // Iterate through the children and if one of the children // contains the given scheduling rule, this container model // node contains the given scheduling rule as well. try { childListLock.lock(); IModelNode[] children = internalGetChildren(); for (IModelNode child : children) { if (child.contains(rule)) { return true; } } } finally { childListLock.unlock(); } } return super.contains(rule); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.nodes.ModelNode#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) and if recursive // scheduling rule locking is on. if (isSchedulingLockedRecursivly() && rule instanceof IModelNode) { // Iterate through the children and if one of the children // is conflicting with the given scheduling rule, this // container model node is conflicting the given scheduling // rule as well. try { childListLock.lock(); IModelNode[] children = internalGetChildren(); for (IModelNode child : children) { if (child.isConflicting(rule)) { return true; } } } finally { childListLock.unlock(); } } return super.isConflicting(rule); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.nodes.ModelNode#find(java.util.UUID) */ @Override public IModelNode find(UUID uuid) { IModelNode find = super.find(uuid); if (find != null) return find; try { childListLock.lock(); for (IModelNode child : childList) { find = child.find(uuid); if (find != null) { return find; } } } finally { childListLock.unlock(); } return find; } }