001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.helpers;
015
016import java.util.ArrayList;
017import java.util.List;
018
019/**
020 * CyclicBuffer holds values in a cyclic array.
021 * 
022 * <p>
023 * It allows read access to any element in the buffer not just the first or last
024 * element.
025 * 
026 * @author Ceki G&uuml;lc&uuml;
027 */
028public class CyclicBuffer<E> {
029
030    E[] ea;
031    int first;
032    int last;
033    int numElems;
034    int maxSize;
035
036    /**
037     * Instantiate a new CyclicBuffer of at most <code>maxSize</code> events.
038     * 
039     * The <code>maxSize</code> argument must a positive integer.
040     * 
041     * @param maxSize The maximum number of elements in the buffer.
042     */
043    public CyclicBuffer(int maxSize) throws IllegalArgumentException {
044        if (maxSize < 1) {
045            throw new IllegalArgumentException("The maxSize argument (" + maxSize + ") is not a positive integer.");
046        }
047        init(maxSize);
048    }
049
050    @SuppressWarnings("unchecked")
051    public CyclicBuffer(CyclicBuffer<E> other) {
052        this.maxSize = other.maxSize;
053        ea = (E[]) new Object[maxSize];
054        System.arraycopy(other.ea, 0, this.ea, 0, maxSize);
055        this.last = other.last;
056        this.first = other.first;
057        this.numElems = other.numElems;
058    }
059
060    @SuppressWarnings("unchecked")
061    private void init(int maxSize) {
062        this.maxSize = maxSize;
063        ea = (E[]) new Object[maxSize];
064        first = 0;
065        last = 0;
066        numElems = 0;
067    }
068
069    /**
070     * Clears the buffer and resets all attributes.
071     */
072    public void clear() {
073        init(this.maxSize);
074    }
075
076    /**
077     * Add an <code>event</code> as the last event in the buffer.
078     * 
079     */
080    public void add(E event) {
081        ea[last] = event;
082        if (++last == maxSize)
083            last = 0;
084
085        if (numElems < maxSize)
086            numElems++;
087        else if (++first == maxSize)
088            first = 0;
089    }
090
091    /**
092     * Get the <i>i</i>th oldest event currently in the buffer. If <em>i</em> is
093     * outside the range 0 to the number of elements currently in the buffer, then
094     * <code>null</code> is returned.
095     */
096    public E get(int i) {
097        if (i < 0 || i >= numElems)
098            return null;
099
100        return ea[(first + i) % maxSize];
101    }
102
103    public int getMaxSize() {
104        return maxSize;
105    }
106
107    /**
108     * Get the oldest (first) element in the buffer. The oldest element is removed
109     * from the buffer.
110     */
111    public E get() {
112        E r = null;
113        if (numElems > 0) {
114            numElems--;
115            r = ea[first];
116            ea[first] = null;
117            if (++first == maxSize)
118                first = 0;
119        }
120        return r;
121    }
122
123    public List<E> asList() {
124        List<E> tList = new ArrayList<E>();
125        for (int i = 0; i < length(); i++) {
126            tList.add(get(i));
127        }
128        return tList;
129    }
130
131    /**
132     * Get the number of elements in the buffer. This number is guaranteed to be in
133     * the range 0 to <code>maxSize</code> (inclusive).
134     */
135    public int length() {
136        return numElems;
137    }
138
139    /**
140     * Resize the cyclic buffer to <code>newSize</code>.
141     * 
142     * @throws IllegalArgumentException if <code>newSize</code> is negative.
143     */
144    @SuppressWarnings("unchecked")
145    public void resize(int newSize) {
146        if (newSize < 0) {
147            throw new IllegalArgumentException("Negative array size [" + newSize + "] not allowed.");
148        }
149        if (newSize == numElems)
150            return; // nothing to do
151
152        //
153        E[] temp = (E[]) new Object[newSize];
154
155        int loopLen = newSize < numElems ? newSize : numElems;
156
157        for (int i = 0; i < loopLen; i++) {
158            temp[i] = ea[first];
159            ea[first] = null;
160            if (++first == numElems)
161                first = 0;
162        }
163        ea = temp;
164        first = 0;
165        numElems = loopLen;
166        maxSize = newSize;
167        if (loopLen == newSize) {
168            last = 0;
169        } else {
170            last = loopLen;
171        }
172    }
173}