/******************************************************************************* * Copyright 2014 Analog Devices, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ package com.analog.lyric.dimple.model.core; import static java.util.Objects.*; import java.util.AbstractCollection; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.collect.ArrayUtil; import com.analog.lyric.collect.ReleasableIterators; import com.google.common.collect.UnmodifiableIterator; import net.jcip.annotations.NotThreadSafe; /** * Maintains owned nodes of a single type for a FactorGraph * <p> * This class holds the nodes in a simple array indexed by the local id * of the node, which is assigned when the node is added to this collection. * There may be holes in the array, but only if a node is removed. * <p> * @since 0.08 * @author Christopher Barber */ @NotThreadSafe abstract class OwnedArray<T extends IFactorGraphChildWithSetLocalId> extends AbstractCollection<T> { /*------- * State */ private @Nullable T[] _nodes; private int _size; private int _end; /*-------------- * Construction */ OwnedArray() { } /*-------------------- * Collection methods */ @NonNullByDefault(false) @Override public boolean add(T node) { if (containsNode(node)) { return false; } int index = allocate(); requireNonNull(_nodes)[index] = node; node.setLocalId(index|idTypeMask()); return true; } @NonNullByDefault(false) @Override public boolean addAll(Collection<? extends T> nodes) { ensureCapacity(capacity() + nodes.size()); final int prevSize = _size; for (T node : nodes) { add(node); } return _size != prevSize; } @Override public void clear() { if (_size > 0) { Arrays.fill(_nodes, 0, _end, null); } _size = _end = 0; } @Override public boolean contains(@Nullable Object obj) { if (obj instanceof FactorGraphChild) { return containsNode((FactorGraphChild)obj); } return false; } @Override public Iterator<T> iterator() { if (_size == 0) { return ReleasableIterators.emptyIterator(); } final T[] array = requireNonNull(_nodes); return new UnmodifiableIterator<T>() { private int _next = 0; private final T[] _array = array; @Override public boolean hasNext() { for (; _next < _end; ++_next) { if (_array[_next] != null) { return true; } } return false; } @Override public @Nullable T next() { for (; _next < _end; ++_next) { final T node = _array[_next]; if (node != null) { ++_next; return node; } } return null; } }; } @Override public boolean remove(@Nullable Object obj) { if (obj instanceof FactorGraphChild) { return removeNode((FactorGraphChild)obj); } return false; } @Override public int size() { return _size; } /*-------------------- * OwnedArray methods */ int capacity() { final T[] nodes = _nodes; return nodes != null ? nodes.length : 0; } void capacity(int capacity) { if (capacity >= _end) { _nodes = resize(_nodes, capacity); } } boolean containsNode(IFactorGraphChild node) { return node == getByLocalId(node.getLocalId()); } void ensureCapacity(int newCapacity) { if (newCapacity > capacity()) { int nextPowerOfTwo = Integer.highestOneBit(newCapacity); if (nextPowerOfTwo < newCapacity) { nextPowerOfTwo <<= 1; } capacity(nextPowerOfTwo); } } @SuppressWarnings("null") T get(int n) { return _nodes[n]; } /** * Return's nth node in array (skipping null entries). */ T getNth(int n) { if (n >= 0 && n < _size) { final T[] nodes = requireNonNull(_nodes); int nNulls = _end - _size; if (nNulls == 0) { return nodes[n]; } for (T node : nodes) { if (node != null && --n < 0) { return node; } } } throw new IndexOutOfBoundsException(); } @Nullable T getByLocalId(int localId) { final T[] nodes = _nodes; if (nodes != null) { int index = Ids.indexFromLocalId(localId); if (index < _end) { return nodes[index]; } } return null; } boolean removeNode(IFactorGraphChild node) { final T[] nodes = _nodes; if (nodes != null) { final int id = node.getLocalId(); final int index = Ids.indexFromLocalId(id); if (index < _end && nodes[index] == node) { nodes[index] = null; --_size; return true; } } return false; } @Nullable int[] renumber(boolean returnOldToNewMapping) { int[] old2new = null; final T[] nodes = _nodes; if (nodes != null) { int from = 0, to = 0, n = _size; for (; from < n; ++from) { T node = nodes[from]; if (node != null) { if (from != to) { if (returnOldToNewMapping) { if (old2new == null) { old2new = new int[n]; for (int i = 0; i < from; ++i) { old2new[i] = i; } } old2new[from] = to; } nodes[to] = node; nodes[from] = null; renumberNode(node, to); } ++to; } } if (to != from && !returnOldToNewMapping) { old2new = ArrayUtil.EMPTY_INT_ARRAY; } } return old2new; } void renumberNode(T node, int newIndex) { node.setLocalId(newIndex|idTypeMask()); } void trimToSize() { capacity(_size); } /*------------------ * Abstract methods */ abstract int idTypeMask(); abstract T[] resize(@Nullable T[] array, int length); /*----------------- * Private methods */ /** * Allocates a slot for node and returns its index. */ private int allocate() { ++_size; int index = _end++; ensureCapacity(_end); return index; } }