EgtfParallelListener.java

package org.opentrafficsim.draw.egtf;

/**
 * Listener that allows another thread to monitor, report on, and wait for the filtering result.
 * <p>
 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
 * </p>
 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
 */
public class EgtfParallelListener implements EgtfListener
{

    /** Filter results after the EGTF is done. */
    private Filter filter;

    /** Wait lock. */
    private Object lock = new Object();

    /** Progress at the last event. */
    private double progress = 0.0;

    /** Whether the user interrupted the EGTF. */
    private boolean interrupted = false;

    /**
     * Package-private constructor.
     */
    EgtfParallelListener()
    {
        //
    }

    /**
     * Waits until progress has been reached, or timeout was exceeded.
     * @param untilProgress double; progress to wait until
     * @param timeout long; time out in milliseconds
     * @return double; value equal to or above untilProgress, or lower if the timeout was exceeded
     * @throws InterruptedException when the calling thread is interrupted
     */
    public double wait(final double untilProgress, final long timeout) throws InterruptedException
    {
        long t1 = System.currentTimeMillis() + timeout;
        long tOut = timeout;
        double uProgress = Math.min(1.0, untilProgress);
        while (this.progress < uProgress && tOut > 0)
        {
            synchronized (this.lock)
            {
                this.lock.wait(tOut);
            }
            tOut = t1 - System.currentTimeMillis();
        }
        return this.progress;
    }

    /**
     * Set the filter results, this is done by the EGTF.
     * @param filter Filter; filter results
     */
    public void setFilter(final Filter filter)
    {
        this.filter = filter;
        this.progress = 1.0;
        synchronized (this.lock)
        {
            this.lock.notifyAll();
        }
    }

    /**
     * Get the filter results after the EGTF is done.
     * @return Filter; filter results
     */
    public Filter getFilter()
    {
        if (this.filter == null)
        {
            throw new IllegalStateException("Trying to obtain the filter results before filtering is done.");
        }
        return this.filter;
    }

    /** {@inheritDoc} */
    @Override
    public void notifyProgress(final EgtfEvent event)
    {
        if (this.interrupted)
        {
            event.interrupt();
            this.progress = 1.0;
        }
        else
        {
            // set the current progress, but do not allow a value of 1.0 until the filter result is actually set
            this.progress = Math.min(event.getProgress(), 1.0 - 1e-9);
        }
        synchronized (this.lock)
        {
            this.lock.notifyAll();
        }
    }

    /**
     * Interrupts the EGTF.
     */
    public void interrupt()
    {
        this.interrupted = true;
    }

    /** {@inheritDoc} */
    @Override
    public String toString()
    {
        return "EgtfParallelListener [filter=" + this.filter + ", progress=" + this.progress + "]";
    }

}