001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.functor.aggregator;
018
019import org.apache.commons.functor.BinaryFunction;
020
021/**
022 * An implementation of an aggregator which doesn't store the data series but
023 * instead it processes the data on the fly, as it arrives in
024 * {@link #add(Object)} and stores the result after each addition. It processes
025 * the data by using a {@link BinaryFunction} which takes the result of the
026 * previous {@link #add(Object)} and the data passed in and returns a new result
027 * which gets stored (for using again in the next call to {@link #add(Object)}.
028 * The call to {@link #evaluate()} simply returns this stored value at any
029 * point. This has a lower memory footprint compared to
030 * {@link AbstractListBackedAggregator} however it only allows for simpler
031 * processing on the data received.
032 *
033 * @param <T>
034 *            Type of object stored.
035 */
036public abstract class AbstractNoStoreAggregator<T> extends AbstractTimedAggregator<T> {
037    /**
038     * Function used to aggregate the data on the fly in {@link #add(Object)}.
039     *
040     * @see #add(Object)
041     * @see #AbstractNoStoreAggregator(BinaryFunction)
042     */
043    private BinaryFunction<T, T, T> aggregationFunction;
044
045    /**
046     * Stores the result of the last {@link #add(Object)} operation.
047     *
048     * @see #add(Object)
049     */
050    private T                       result;
051
052    /**
053     * Similar to {@link #AbstractNoStoreAggregator(BinaryFunction, long)
054     * AbstractNoStoreAggregator(aggregationFunction,0L)}.
055     *
056     * @param aggregationFunction
057     *            Aggregation function to use in {@link #add(Object)}. Throws
058     *            <code>NullPointerException</code> if this is <code>null</code>
059     * @see #add(Object)
060     * @see #aggregationFunction
061     */
062    public AbstractNoStoreAggregator(BinaryFunction<T, T, T> aggregationFunction) {
063        this(aggregationFunction, 0L);
064    }
065
066    /**
067     * Similar to
068     * {@link #AbstractNoStoreAggregator(BinaryFunction, long,boolean)
069     * AbstractNoStoreAggregator(aggregationFunction,0L,false)}.
070     *
071     * @param aggregationFunction
072     *            Aggregation function to use in {@link #add(Object)}. Throws
073     *            <code>NullPointerException</code> if this is <code>null</code>
074     * @param interval
075     *            interval in miliseconds to reset this aggregator
076     * @see #add(Object)
077     * @see #aggregationFunction
078     */
079    public AbstractNoStoreAggregator(BinaryFunction<T, T, T> aggregationFunction, long interval) {
080        this(aggregationFunction, interval, false);
081    }
082
083    /**
084     * Constructs an aggregator which will use the given function, reset itself
085     * at the given interval and will use a shared timer on own private timer.
086     * Simply prepares an aggregator which will use the given aggregation
087     * function each time {@link #add(Object)} is called. Also it initializes
088     * {@link #result} with the value returned by {@link #initialValue()}, thus
089     * allowing subclasses to have a custom way of specifying the start value.
090     *
091     * @param aggregationFunction
092     *            Aggregation function to use in {@link #add(Object)}. Throws
093     *            <code>NullPointerException</code> if this is <code>null</code>
094     * @param interval
095     *            interval in miliseconds to reset this aggregator
096     * @param useSharedTimer
097     *            if set to true, it will use a shared timer, as per
098     *            {@link AbstractTimedAggregator#AbstractTimedAggregator(long, boolean)}
099     *            ; otherwise if it's false it will use its own timer instance
100     * @see AbstractTimedAggregator#AbstractTimedAggregator(long, boolean)
101     */
102    public AbstractNoStoreAggregator(BinaryFunction<T, T, T> aggregationFunction, long interval,
103            boolean useSharedTimer) {
104        super(interval, useSharedTimer);
105        this.aggregationFunction = aggregationFunction;
106        result = initialValue();
107    }
108
109    /**
110     * Receives data to be aggregated/processed on the fly. This implementation
111     * simply calls {@link #aggregationFunction} and stores the result.
112     *
113     * @param data
114     *            Data to aggregate
115     */
116    @Override
117    protected final void doAdd(T data) {
118        result = aggregationFunction.evaluate(result, data);
119    }
120
121    /**
122     * Returns the value already computed and stored in {@link #result}.
123     *
124     * @return Current (aggregated) value stored in {@link #result}
125     * @see Aggregator#evaluate()
126     */
127    @Override
128    protected final T doEvaluate() {
129        return result;
130    }
131
132    /**
133     * Resets the {@link #result} member to the {@link #initialValue()}.
134     *
135     * @see #initialValue()
136     */
137    @Override
138    protected final void doReset() {
139        result = initialValue();
140    }
141
142    /**
143     * Allows subclasses to define the "initial" value. This value will be
144     * stored in {@link #result} when an instance of this class is created or
145     * when {@link #reset()} is called.
146     *
147     * @return Initial value to be used in {@link #result}.
148     */
149    protected abstract T initialValue();
150
151    /**
152     * Getter for {@link #aggregationFunction}.
153     *
154     * @return Current value of the member.
155     */
156    final BinaryFunction<T, T, T> getAggregationFunction() {
157        return aggregationFunction;
158    }
159
160    /**
161     * Getter for {@link #result}. Provided for test purposes only.
162     *
163     * @return Current value of the aggregated data.
164     */
165    final T getResult() {
166        return result;
167    }
168
169    /**
170     * Setter for {@link #result}. Provided for test purposes only.
171     *
172     * @param result
173     *            New value to store in {@link #result}
174     */
175    final void setResult(T result) {
176        this.result = result;
177    }
178
179    /**
180     * This aggregator doesn't store any data, so the data series size is always
181     * 0 (zero).
182     *
183     * @return 0
184     */
185    @Override
186    protected int retrieveDataSize() {
187        return 0;
188    }
189
190    @Override
191    public String toString() {
192        return AbstractNoStoreAggregator.class.getName();
193    }
194}