package org.grouplens.lenskit.eval;

import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;

/**
 * Targets in an evaluator project.  This class extends Ant {@linkplain Target targets} with the
 * Future interface, providing access to the value of the last task executed.  Non-eval tasks
 * (standard Ant tasks) have null values.
 *
 * @author <a href="http://www.grouplens.org">GroupLens Research</a>
 */
public class EvalTarget extends Target implements ListenableFuture<Object> {
    protected final SettableFuture<Object> returnValue = SettableFuture.create();
    protected Future<?> lastTaskFuture = null;
    protected Task lastTask = null;
    private transient volatile Logger logger;

    protected Logger getLogger() {
        if (logger == null) {
            String name = String.format("%s(%s)".format(getClass().getName(), getName()));
            logger = LoggerFactory.getLogger(name);
        }
        return logger;
    }

    @Override
    public void addTask(Task task) {
        getLogger().debug("adding task {}", task);
        super.addTask(task);
        lastTask = task;
        if (task instanceof EvalAntTask) {
            lastTaskFuture = ((EvalAntTask) task).getEvalTask();
        } else {
            lastTaskFuture = null;
        }
    }

    @Override
    public void execute() throws BuildException {
        try {
            getLogger().info("beginning execution");
            Stopwatch watch = new Stopwatch().start();
            super.execute();
            watch.stop();
            getLogger().info("execution finished in {}", watch);
            if (lastTaskFuture != null) {
                if (!lastTaskFuture.isDone()) {
                    getLogger().error("{}: future for task {} did not complete", getName(), lastTask);
                    returnValue.setException(new RuntimeException("task future didn't complete"));
                } else {
                    while (!returnValue.isDone()) {
                        try {
                            returnValue.set(lastTaskFuture.get());
                        } catch (ExecutionException ex) {
                            returnValue.setException(ex.getCause());
                        } catch (InterruptedException e) {
                            getLogger().warn("task future get() was interrupted");
                            /* try again */
                        }
                    }
                }
            } else {
                returnValue.set(null);
            }
        } catch (RuntimeException ex) {
            returnValue.setException(ex);
            throw ex;
        }
    }

    //region Future operations
    // These all delegate to the settable future.
    @Override
    public Object get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException {
        return returnValue.get(timeout, unit);
    }

    @Override
    public Object get() throws InterruptedException, ExecutionException {
        return returnValue.get();
    }

    @Override
    public boolean isDone() {
        return returnValue.isDone();
    }

    @Override
    public boolean isCancelled() {
        return returnValue.isCancelled();
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return returnValue.cancel(mayInterruptIfRunning);
    }

    @Override
    public void addListener(Runnable listener, Executor exec) {
        returnValue.addListener(listener, exec);
    }
    //endregion
}

@@ -1,8 +1,1013 @@
+/*%0A * LensKit, an open source recommender systems toolkit.%0A * Copyright 2010-2013 Regents of the University of Minnesota and contributors%0A * Work on LensKit has been funded by the National Science Foundation under%0A * grants IIS 05-34939, 08-08692, 08-12148, and 10-17697.%0A *%0A * This program is free software; you can redistribute it and/or modify%0A * it under the terms of the GNU Lesser General Public License as%0A * published by the Free Software Foundation; either version 2.1 of the%0A * License, or (at your option) any later version.%0A *%0A * This program is distributed in the hope that it will be useful, but WITHOUT%0A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS%0A * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more%0A * details.%0A *%0A * You should have received a copy of the GNU General Public License along with%0A * this program; if not, write to the Free Software Foundation, Inc., 51%0A * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%0A */%0A
 package 

add missing license headers