RunningJobSet.java

package com.birbit.android.jobqueue;

import com.birbit.android.jobqueue.log.JqLog;
import com.birbit.android.jobqueue.timer.Timer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;

/**
 * a util class that holds running jobs sorted by name and unique.
 * it behaves like CopyOnWriteLists
 */
public class RunningJobSet {
    private ArrayList<String> publicClone;
    private final TreeSet<String> internalSet;
    private final Map<String, Long> groupDelays;
    private long groupDelayTimeout;
    private final Timer timer;

    public RunningJobSet(Timer timer) {
        internalSet = new TreeSet<>();
        groupDelays = new HashMap<>();
        groupDelayTimeout = Long.MAX_VALUE;
        this.timer = timer;
    }

    public synchronized void addGroupUntil(String group, long until) {
        JqLog.d("add group delay to %s until %s", group, until);
        Long current = groupDelays.get(group);
        if (current != null) {
             if (current > until) {
                 return; // nothing to change
             }
        }
        groupDelays.put(group, until);
        groupDelayTimeout = calculateNextDelayForGroups();
        publicClone = null;
    }

    public synchronized Collection<String> getSafe() {
        final long now = timer.nanoTime();
        if(publicClone == null || now > groupDelayTimeout) {
            if (groupDelays.isEmpty()) {
                publicClone = new ArrayList<>(internalSet);
                groupDelayTimeout = Long.MAX_VALUE;
            } else {
                TreeSet<String> tmpClone = new TreeSet<>(internalSet);
                Iterator<Map.Entry<String, Long>> itr = groupDelays.entrySet().iterator();
                while(itr.hasNext()) {
                    Map.Entry<String, Long> entry = itr.next();
                    if (entry.getValue() > now) {
                        if (!tmpClone.contains(entry.getKey())) {
                            tmpClone.add(entry.getKey());
                        }
                    } else {
                        itr.remove();
                    }
                }
                publicClone = new ArrayList<>(tmpClone);
                groupDelayTimeout = calculateNextDelayForGroups();
            }
        }
        return publicClone;
    }
    public Long getNextDelayForGroups() {
        if (groupDelayTimeout == Long.MAX_VALUE) {
            return null;
        }
        return groupDelayTimeout;
    }
    private long calculateNextDelayForGroups() {
        long result = Long.MAX_VALUE;
        for (Long value : groupDelays.values()) {
            if (value < result) {
                result = value;
            }
        }
        return result;
    }

    public synchronized void add(String group) {
        if (group == null) {
            return;
        }
        if(internalSet.add(group)) {
            publicClone = null;//invalidate
        }
    }

    public synchronized void remove(String group) {
        if (group == null) {
            return;
        }
        if(internalSet.remove(group)) {
            publicClone = null;
        }
    }

    public synchronized void clear() {
        internalSet.clear();
        groupDelays.clear();
        publicClone = null;
    }
}