All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.unkrig.commons.lang.protocol.ProducerUtil Maven / Gradle / Ivy

Go to download

A versatile Java(TM) library that implements many useful container and utility classes.

There is a newer version: 1.1.12
Show newest version

/*
 * 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 delegate, final Predicate 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 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 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 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 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 delegate, final Predicate 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 delegate1, final Producer 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 }" that produces {@code true} iff the current time is after the * given expirationTime (in milliseconds). * * @param expirationTime Milliseconds since Jan 1 1970, UTC */ public static Producer after(final long expirationTime) { return new Producer() { @Override @Nullable public Boolean produce() { return System.currentTimeMillis() > expirationTime; } @Override public String toString() { return "After " + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US); } }; } /** * Wraps the delegate such that its declared exception is caught, ignored, and the * defaultValue is returned. */ public static Producer ignoreExceptions(final Class exceptionClass, final ProducerWhichThrows delegate, final T defaultValue) { return new Producer() { @Override @Nullable public T produce() { try { return delegate.produce(); } catch (RuntimeException re) { if (!exceptionClass.isAssignableFrom(re.getClass())) throw re; } catch (Error e) { // SUPPRESS CHECKSTYLE IllegalCatch if (!exceptionClass.isAssignableFrom(e.getClass())) throw e; } catch (Throwable t) { // SUPPRESS CHECKSTYLE IllegalCatch assert exceptionClass.isAssignableFrom(t.getClass()); } return defaultValue; } }; } }