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

org.reactfx.util.AccuMap Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
package org.reactfx.util;

import static org.reactfx.util.Tuples.*;

import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Accumulation map.
 *
 * @param  key type
 * @param  type of individual (non-accumulated) values
 * @param  type of accumulated values
 */
public interface AccuMap {

    static  AccuMap empty() {
        return EmptyAccuMap.instance();
    }

    boolean isEmpty();
    Tuple2 peek(AccumulationFacility af);
    AccuMap dropPeeked();
    AccuMap updatePeeked(A newAccumulatedValue);
    AccuMap addAll(Iterator keys, V value, AccumulationFacility af);
}


class EmptyAccuMap implements AccuMap {
    private static final AccuMap INSTANCE = new EmptyAccuMap<>();

    @SuppressWarnings("unchecked")
    static  AccuMap instance() {
        return (AccuMap) INSTANCE;
    }

    // private constructor to prevent instantiation
    private EmptyAccuMap() {}

    @Override
    public boolean isEmpty() {
        return true;
    }

    @Override
    public Tuple2 peek(AccumulationFacility af) {
        throw new NoSuchElementException();
    }

    @Override
    public AccuMap dropPeeked() {
        throw new NoSuchElementException();
    }

    @Override
    public AccuMap updatePeeked(A newAccumulatedValue) {
        throw new NoSuchElementException();
    }

    @Override
    public AccuMap addAll(
            Iterator keys, V value,
            AccumulationFacility af) {
        return new IteratorBasedAccuMap<>(keys, value);
    }
}


class IteratorBasedAccuMap
implements AccuMap {
    private K currentKey = null;
    private A currentAccumulatedValue = null;

    private Iterator it;
    private V value;

    IteratorBasedAccuMap(Iterator keys, V value) {
        this.it = keys;
        this.value = value;
    }

    @Override
    public boolean isEmpty() {
        return currentKey == null && !it.hasNext();
    }

    @Override
    public Tuple2 peek(AccumulationFacility af) {
        if(currentKey == null) {
            currentKey = it.next();
            currentAccumulatedValue = af.initialAccumulator(value);
        }
        return t(currentKey, currentAccumulatedValue);
    }

    @Override
    public AccuMap dropPeeked() {
        checkPeeked();
        currentKey = null;
        currentAccumulatedValue = null;
        return this;
    }

    @Override
    public AccuMap updatePeeked(A newAccumulatedValue) {
        checkPeeked();
        currentAccumulatedValue = newAccumulatedValue;
        return this;
    }

    @Override
    public AccuMap addAll(Iterator keys, V value, AccumulationFacility af) {
        if(isEmpty()) {
            this.it = keys;
            this.value = value;
            return this;
        } else if(!keys.hasNext()) {
            return this;
        } else {
            HashAccuMap res = new HashAccuMap<>();
            if(currentKey != null) {
                res.put(currentKey, currentAccumulatedValue);
            }
            return res
                    .addAll(it, this.value, af)
                    .addAll(keys, value, af);
        }
    }

    private final void checkPeeked() {
        if(currentKey == null) {
            throw new NoSuchElementException("No peeked value present. Use peek() first.");
        }
    }
}

@SuppressWarnings("serial")
class HashAccuMap extends HashMap implements AccuMap {

    @Override
    public Tuple2 peek(AccumulationFacility af) {
        K key = pickKey();
        A acc = this.get(key);
        return t(key, acc);
    }

    @Override
    public AccuMap dropPeeked() {
        K key = pickKey();
        this.remove(key);
        return this;
    }

    @Override
    public AccuMap updatePeeked(A newAccumulatedValue) {
        K key = pickKey();
        this.put(key, newAccumulatedValue);
        return this;
    }

    @Override
    public AccuMap addAll(Iterator keys, V value, AccumulationFacility af) {
        while(keys.hasNext()) {
            K key = keys.next();
            if(this.containsKey(key)) {
                A accum = this.get(key);
                accum = af.reduce(accum, value);
                this.put(key, accum);
            } else {
                A accum = af.initialAccumulator(value);
                this.put(key, accum);
            }
        }
        return this;
    }

    private K pickKey() {
        return this.keySet().iterator().next();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy