de.unkrig.commons.lang.protocol.ProducerUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of de-unkrig-commons Show documentation
Show all versions of de-unkrig-commons Show documentation
A versatile Java(TM) library that implements many useful container and utility classes.
/*
* de.unkrig.commons - A general-purpose Java class library
*
* Copyright (c) 2011, Arno Unkrig
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package de.unkrig.commons.lang.protocol;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Random;
import de.unkrig.commons.nullanalysis.Nullable;
/**
* Various {@link Producer}-related utility methods.
*/
public final
class ProducerUtil {
private
ProducerUtil() {}
/**
* The returned {@link Producer} calls the {@code delegate} iff the {@code condition} returns {@code true},
* otherwise it returns the previous product of the {@code delegate}, or {@code null} iff the delegate has
* not yet been called.
*
* @param subject The {@code subject} argument for the {@code condition}
* @deprecated Use {@link #cache(ProducerWhichThrows, ProducerWhichThrows)} instead, which has very similar (but
* not identical) semantics.
*/
@Deprecated public static Producer
sparingProducer(final Producer extends T> delegate, final Predicate super ST> condition, final ST subject) {
return new Producer() {
@Nullable private T product;
@Override @Nullable public T
produce() {
if (condition.evaluate(subject)) this.product = delegate.produce();
return this.product;
}
};
}
/**
* Returns a {@code Producer} who's first evaluation result is {@code true}, and each following result is
* {@code true} iff the last {@code true} result was returned at least the given {@code interval} milliseconds ago.
* In other words, the interval between two returned {@code true} values is never shorter than {@code interval}
* milliseconds.
*/
public static Producer
every(final long interval) {
return new Producer() {
private long expirationTime;
@Override @Nullable public synchronized Boolean
produce() {
long now = System.currentTimeMillis();
if (now >= this.expirationTime) {
this.expirationTime = now + interval;
return true;
} else {
return false;
}
}
};
}
/**
* Converts the source into a {@link ProducerWhichThrows ProducerWhichThrows<T, EX>}.
*
* This is always possible, because the source is only allowed to throw {@link RuntimeException}s.
*
*
* Notice {@code Producer} extends {@code ProducerWhichThrows}, thus you don't
* need this method to convert to {@code ProducerWhichThrows}.
*
*
* @param The product type
* @param The target producer's exception
*/
public static ProducerWhichThrows
asProducerWhichThrows(final Producer extends T> source) {
@SuppressWarnings("unchecked") ProducerWhichThrows result = (ProducerWhichThrows) source;
return result;
}
/**
* Converts the source into a {@link Producer Producer<T>}.
*
* This is always possible, because both are only allowed to throw {@link RuntimeException}s.
*
*
* @param The product type
*/
public static Producer
asProducer(final ProducerWhichThrows extends T, ? extends RuntimeException> source) {
@SuppressWarnings("unchecked") Producer result = (Producer) source;
return result;
}
/**
* @return A {@link Producer} that produced the given {@code elements}
*/
public static Producer
fromElements(final T... elements) {
return new Producer() {
int idx;
@Override @Nullable public T
produce() {
return this.idx == elements.length ? null : elements[this.idx++];
}
};
}
/**
* Produces the elements of the {@code delegate} collection, in its iteration order, or {@code null} iff the
* collection is empty. The elements are removed from the collection as they are produced.
*/
public static Producer
fromCollection(final Collection delegate) {
return new Producer() {
@Override @Nullable public T
produce() {
Iterator it = delegate.iterator();
if (!it.hasNext()) return null;
T product = it.next();
it.remove();
return product;
}
};
}
/**
* Produces the elements of the delegate array, in ascending index order, and after that an infinite
* sequence of {@code null}s.
*/
public static FromArrayProducer
fromArray(final T[] delegate) {
return ProducerUtil.fromArray(delegate, 0, delegate.length);
}
/**
* Produces the elements from ... to-1 of the {@code delegate} array, and after that an
* infinite sequence of {@code null}s.
*
* @throws IllegalArgumentException from is less than 0
* @throws IllegalArgumentException to is less than from
* @throws IllegalArgumentException to is greater than delegate{@code .length}
*/
public static FromArrayProducer
fromArray(final T[] delegate, final int from, final int to) {
if (from < 0 || to < from || to > delegate.length) throw new IllegalArgumentException();
return new FromArrayProducer() {
int idx = from;
@Override @Nullable public T
produce() { return this.idx < to ? delegate[this.idx++] : null; }
@Override public int
index() { return this.idx; }
};
}
/**
* Extends the concept of the {@link Producer} by an "index".
*
* @param See {@link Producer}
* @see #index()
*/
public
interface FromArrayProducer extends Producer {
/**
* @return The index of the next element that will be returned by {@link #produce()};
* delegate{@code .length} on end-of-array
*/
int index();
}
/**
* Produces the products of the {@code iterator}, or {@code null} iff the {@code iterator} has no more elements.
*/
public static Producer
fromIterator(final Iterator iterator) {
return new Producer() {
@Override @Nullable public T produce() { return iterator.hasNext() ? iterator.next() : null; }
};
}
/**
* Produces objects based on the number of preceding invocations, i.e. the {@code indexTransformer} is invoked
* with subjects '0', '1', '2', ...
*/
public static Producer
fromIndexTransformer(final Transformer super Integer, T> indexTransformer) {
return new Producer() {
private int index;
@Override @Nullable public T
produce() { return indexTransformer.transform(this.index++); }
};
}
/**
* Produces objects based on the number of preceding invocations, i.e. the {@code indexTransformer} is invoked
* with subjects '0', '1', '2', ...
*/
public static ProducerWhichThrows
fromIndexTransformer(final TransformerWhichThrows super Integer, T, EX> indexTransformer) {
return new ProducerWhichThrows() {
private int index;
@Override @Nullable public T
produce() throws EX { return indexTransformer.transform(this.index++); }
};
}
/**
* @return A producer which produces bytes through {@code new java.util.Random(seed).nextInt(0x100)}
*/
public static Producer
randomByteProducer(final long seed) {
return new Producer() {
final Random r = new Random(seed);
@Override public Byte
produce() { return (byte) this.r.nextInt(0x100); }
};
}
/**
* @return A producer which always produces the {@code constant}
*/
public static Producer
constantProducer(final T constant) {
return new Producer() {
@Override public T produce() { return constant; }
};
}
/**
* Discards the elements that the delegate produces while they are compressable. After that,
* it produces the elements that the delegate produces and are not compressable, and
* reduces sequences of one or more compressable elements to compressed.
* line "".
*/
public static Producer
compress(final Producer extends T> delegate, final Predicate super T> compressable, final T compressed) {
return new Producer() {
boolean initial = true;
@Nullable T lookahead;
@Override @Nullable public T
produce() {
if (this.initial) {
T product;
do {
product = delegate.produce();
if (product == null) return null;
} while (compressable.evaluate(product));
this.initial = false;
return product;
}
{
T tmp = this.lookahead;
if (tmp != null) {
this.lookahead = null;
return tmp;
}
}
T product = delegate.produce();
if (product == null) return null;
if (!compressable.evaluate(product)) return product;
do {
product = delegate.produce();
if (product == null) return null;
} while (compressable.evaluate(product));
this.lookahead = product;
return compressed;
}
};
}
/**
* @return A {@link Producer} that produces first, second, first,
* second, ...
*/
public static Producer
alternate(final T first, final T second) {
return new Producer() {
boolean toggle;
@Override @Nullable public T
produce() {
this.toggle = !this.toggle;
return this.toggle ? first : second;
}
};
}
/**
* @return A {@link Producer} that produces 0, 1, 2, 3, ...
*/
public static Producer
increasing() {
return new Producer() {
int value;
@Override @Nullable public Integer
produce() { return this.value++; }
};
}
/**
* @return A producer that produces the products of delegate1, and, when that produces {@code null},
* the products of delegate2.
*/
public static Producer
concat(final Producer extends T> delegate1, final Producer extends T> delegate2) {
return new Producer() {
boolean second;
@Override @Nullable public T
produce() {
if (this.second) return delegate2.produce();
T product = delegate1.produce();
if (product != null) return product;
this.second = true;
return delegate2.produce();
}
};
}
/**
* The first product is the first product of the delegate; each following product is the next product
* of the delegate if the condition evaluates to {@code true}, otherwise it is the
* previous product.
*
* Example:
*
* ProducerUtil.cache(delegate, ProducerUtil.atMostEvery(milliseconds))
*
* caches the products of the delegate for milliseconds' time.
*
*
* The returned {@link PredicateWhichThrows} is not synchronized and therefore not thread-safe.
*
*
* @see #sparingProducer(Producer, Predicate, Object)
*/
public static ProducerWhichThrows
cache(
final ProducerWhichThrows delegate,
final ProducerWhichThrows condition
) {
return new ProducerWhichThrows() {
@Nullable T cache;
boolean isCached;
@Override @Nullable public T
produce() throws EX {
if (this.isCached && Boolean.TRUE.equals(condition.produce())) return this.cache;
this.isCached = true;
return (this.cache = delegate.produce());
}
};
}
/**
* The first product is the firstProduct; each following product is the next product of the
* delegate iff the condition evaluates to {@code true}, otherwise it is the
* previous product.
*
* The returned {@link PredicateWhichThrows} is not synchronized and therefore not thread-safe.
*
*
* @see #cache(ProducerWhichThrows, ProducerWhichThrows)
*/
public static ProducerWhichThrows
cache(
@Nullable final T firstProduct,
final ProducerWhichThrows delegate,
final ProducerWhichThrows condition
) {
return new ProducerWhichThrows() {
@Nullable T cache = firstProduct;
@Override @Nullable public T
produce() throws EX {
Boolean b = condition.produce();
if (b != null && b) return this.cache;
return (this.cache = delegate.produce());
}
};
}
/**
* Creates and returns a {@link Producer} for which the first product is {@code true}, and, for all
* following products, the time interval between adjacent {@code true} products will (A) be minimal and (B) never
* shorter than milliseconds.
*/
public static Producer
atMostEvery(final long milliseconds) {
return new Producer() {
long next;
@Override @Nullable public Boolean
produce() {
long now = System.currentTimeMillis();
if (now < this.next) {
return false;
}
this.next = now + milliseconds;
return true;
}
@Override public String
toString() {
return (
"At most every "
+ milliseconds
+ " ms; next expiration at "
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US)
);
}
};
}
/**
* Returns a "{@link Producer}{@code