001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.modeshape.common.collection.ring;
018
019import static java.util.Arrays.copyOf;
020import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
021
022/**
023 * Static methods for atomically modifying an array of {@link Pointer} instances.
024 * 
025 * @author Randall Hauch (rhauch@redhat.com)
026 */
027public class Pointers {
028
029    private Pointers() {
030    }
031
032    public static long getMinimum( Pointer[] pointers,
033                                   long minimum ) {
034        for (int i = 0; i != pointers.length; ++i) {
035            minimum = Math.min(minimum, pointers[i].get());
036        }
037        return minimum;
038    }
039
040    /**
041     * Atomically add the specified {@link Pointer} instance(s) to an array known to the given updator.
042     * 
043     * @param holder the object that holds the array; may not be null
044     * @param updater the updator of the array; may not be null
045     * @param cursor the cursor that the pointers will follow; may be null if none of the pointers are to be changed
046     * @param pointersToAdd the pointer(s) to be added to the existing pointers
047     */
048    public static <T> void add( T holder,
049                                AtomicReferenceFieldUpdater<T, Pointer[]> updater,
050                                Cursor cursor,
051                                Pointer... pointersToAdd ) {
052        long currentPosition = 0L;
053        Pointer[] updatedPointers;
054        Pointer[] currentPointers;
055
056        do {
057            currentPointers = updater.get(holder);
058            updatedPointers = copyOf(currentPointers, currentPointers.length + pointersToAdd.length);
059            if (cursor != null) currentPosition = cursor.getCurrent();
060
061            int index = currentPointers.length;
062            for (Pointer pointer : pointersToAdd) {
063                if (cursor != null) pointer.set(currentPosition);
064                updatedPointers[index++] = pointer;
065            }
066        } while (!updater.compareAndSet(holder, currentPointers, updatedPointers));
067
068        if (cursor != null) {
069            // Set all of the new pointers to the current position ...
070            currentPosition = cursor.getCurrent();
071            for (Pointer pointer : pointersToAdd) {
072                pointer.set(currentPosition);
073            }
074        }
075    }
076
077    /**
078     * Atomically remove the specified {@link Pointer} instance from an array known to the given updator.
079     * 
080     * @param holder the object that holds the array; may not be null
081     * @param updater the updator of the array; may not be null
082     * @param pointer the pointer to be removed from the existing pointers
083     * @return true if the pointer was removed, or false if the array never contained the pointer
084     */
085    public static <T> boolean remove( T holder,
086                                      AtomicReferenceFieldUpdater<T, Pointer[]> updater,
087                                      Pointer pointer ) {
088        int numToRemove;
089        Pointer[] oldPointers;
090        Pointer[] newPointers;
091
092        do {
093            oldPointers = updater.get(holder);
094            numToRemove = countMatching(oldPointers, pointer);
095            if (0 == numToRemove) break;
096
097            final int oldSize = oldPointers.length;
098            newPointers = new Pointer[oldSize - numToRemove];
099
100            // Copy all but the 'pointer' into the new array ...
101            for (int i = 0, pos = 0; i < oldSize; i++) {
102                final Pointer testPointer = oldPointers[i];
103                if (pointer != testPointer) {
104                    newPointers[pos++] = testPointer;
105                }
106            }
107        } while (!updater.compareAndSet(holder, oldPointers, newPointers));
108        return numToRemove != 0;
109    }
110
111    private static <T> int countMatching( final T[] values,
112                                          final T toMatch ) {
113        int numToRemove = 0;
114        for (T value : values) {
115            // Use object identity ...
116            if (value == toMatch) numToRemove++;
117        }
118        return numToRemove;
119    }
120}