code: package com.quui.tm2;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  import java.util.concurrent.TimeUnit;
  
  import org.apache.log4j.Logger;
  
  import com.quui.tm2.util.TM2Logger;
  
  /**
   * @author Fabian Steeg (fsteeg)
   * @param <T>
   *            The annotation value type exchanged in this interaction
   */
  public interface Analysis<T extends Comparable<T> & Serializable> {
  
  	/**
  	 * @return Returns an unmodifiable view on the source agents of this
  	 *         interaction
  	 */
  	List<Agent<?, T>> sources();
  
  	/**
  	 * @return Returns an unmodifiable view on the target agents of this
  	 *         interaction
  	 */
  	List<Agent<T, ?>> targets();
  
  	/**
  	 * Execute this interaction, using the given blackboard.
  	 * 
  	 * @param blackboard
  	 *            The blackboard to use to retrieve the input annotations for
  	 *            this interaction and to store the result annotations of this
  	 *            interaction
  	 * @return The result of applying this interaction using the given
  	 *         blackboard: the altered blackboard (or a new blackboard with the
  	 *         results added)
  	 */
  	Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> run(
  			final Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> blackboard);
  
  	/**
  	 * Builder for immutable interactions.
  	 * 
  	 * @author Fabian Steeg (fsteeg)
  	 */
  	public static final class Builder<T extends Comparable<T> & Serializable> {
  		/** The source agents. */
  		final List<Agent<?, T>> sources = new ArrayList<Agent<?, T>>();
  		/** The target agents. */
  		final List<Agent<T, ?>> targets = new ArrayList<Agent<T, ?>>();
  
  		/**
  		 * @return The ready interaction
  		 */
  		public Analysis<T> build() {
  			return new ImmutableAnalysis<T>(this);
  		}
  
  		/**
  		 * @param agent
  		 *            The agent to add as a source of this interaction
  		 * @return This builder, for cascaded calls
  		 */
  		public Builder<T> source(final Agent<?, T> agent) {
  			sources.add(agent);
  			return this;
  		}
  
  		/**
  		 * @param agent
  		 *            The agent to add as a target of this interaction
  		 * @return This builder, for cascaded calls
  		 */
  		public Builder<T> target(final Agent<T, ?> agent) {
  			targets.add(agent);
  			return this;
  		}
  	}
  
  	Class<T> getTypeClass();
  
  }
  
  /**
   * <p/>
   * This class is immutable, and can therefore be shared and reused freely and
   * concurrently.
   * <p/>
   * 
   * @author Fabian Steeg (fsteeg)
   * @param <T>
   *            The annotation value type exchanged in this interaction
   */
  final class ImmutableAnalysis<T extends Comparable<T> & Serializable>
  		implements Analysis<T> {
  	/** The source agents. */
  	private final List<Agent<?, T>> sources;
  	/** The target agents. */
  	private final List<Agent<T, ?>> targets;
  
  	/**
  	 * @param builder
  	 *            The builder to construct an interaction from
  	 */
  	ImmutableAnalysis(final Builder<T> builder) {
  		this.sources = builder.sources;
  		this.targets = builder.targets;
  	}
  
  	/**
  	 * {@inheritDoc}
  	 * 
  	 * @see com.quui.tm2.Analysis#sources()
  	 */
  	public synchronized List<Agent<?, T>> sources() {
  		return Collections.unmodifiableList(sources);
  	}
  
  	/**
  	 * {@inheritDoc}
  	 * 
  	 * @see com.quui.tm2.Analysis#targets()
  	 */
  	public synchronized List<Agent<T, ?>> targets() {
  		return Collections.unmodifiableList(targets);
  	}
  
  	/**
  	 * {@inheritDoc}
  	 * 
  	 * @see com.quui.tm2.Analysis#run(java.util.Map)
  	 */
  	public synchronized Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> run(
  			final Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> blackboard) {
  		List<Annotation<T>> input = new ArrayList<Annotation<T>>();
  
  		/* If we have sources, collect their outputs from the blackboard: */
  		for (Agent<?, T> source : sources()) {
  			input.addAll(ImmutableAnalysis.typedAnnotations(source, blackboard));
  		}
  		/*
  		 * If we found neither a corpus nor source annotations, something is
  		 * wrong:
  		 */
  		if (input.size() == 0 && sources.size() > 0) {
  			throw new IllegalStateException("We need results of " + sources
  					+ " but have nothing on the blackboard;");
  		}
  		/*
  		 * If we have collected the input data for the targets, they can process
  		 * it concurrently:
  		 */
  		ExecutorService exec = Executors
  				.newFixedThreadPool(1/* targets().size() */);
  		for (Agent<T, ?> target : targets()) {
  			exec.execute(new InteractionRunnable<T>(input, target, blackboard));
  		}
  		shutdownAndWait(exec);
  		return blackboard;
  	}
  
  	/**
  	 * @return Returns a concise human-readable representation of this
  	 *         interaction. The exact format is subject to change. If you need
  	 *         the individual elements, use the accessors instead {@inheritDoc}
  	 * @see java.lang.Object#toString()
  	 */
  	@Override
  	public String toString() {
  		StringBuilder builder = new StringBuilder();
  		List<Agent<?, T>> s = this.sources;
  		List<Agent<T, ?>> t = this.targets;
  		/* Sources */
  		for (Agent<?, T> agent : s) {
  			builder.append(agent.getClass().getName()).append(" ");
  		}
  		/*
  		 * The "operator" between sources and target: one-to-one (---),
  		 * many-to-one (>--), one-to-many (--<), e.g. Tokenizer --- Gazetteer
  		 * Tokenizer1 Toeknizer2 >-- Gazetteer Tokenizer --< Gazetteer Counter
  		 */
  		builder.append(s.size() > 1 ? ">" : "-").append("-")
  				.append(t.size() > 2 ? "<" : "- ");
  		/* Targets */
  		for (Agent<T, ?> agent : t) {
  			builder.append(agent.getClass().getName()).append(" ");
  		}
  		return builder.toString().trim();
  	}
  
  	// private API:
  
  	/**
  	 * @param agent
  	 *            The agent class
  	 * @param blackboard
  	 *            The blackboard to use
  	 * @return Returns the annotations produced by the agent found on the
  	 *         blackboard
  	 */
  	static <T extends Comparable<T> & Serializable> List<Annotation<T>> typedAnnotations(
  			final Agent<?, T> agent,
  			final Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> blackboard) {
  		/*
  		 * The blackboard is a heterogeneous container for non-reifiable
  		 * elements. As of Java 1.6, there is no way to implement this
  		 * completely type-safe (cf. Bloch 2008 and Neal Gafter's blog on
  		 * limitations on super type tokens). Instead of implementing a
  		 * "Typesafe Heterogenous Container", which will be unsafe for reifiable
  		 * elements anyway, we use the unbounded wirldcard and cast below.
  		 */
  		List<Annotation<?>> produced = blackboard.get(agent.getClass());
  		if (produced == null) {
  			throw new IllegalStateException("We need results of "
  					+ agent.getClass() + " but have nothing on the blackboard;");
  		}
  		List<Annotation<T>> annotations = new ArrayList<Annotation<T>>();
  		for (Annotation<?> o : produced) {
  			try {
  				/*
  				 * This cast should actually be safe, because the annotations
  				 * were produced by an Agent<?,T>, although this could still
  				 * fail if they were incorrectly stored. But currently, this
  				 * seems to be as far as we get in Java regarding heterogeneous
  				 * container for non-reifiable elements. If generics were
  				 * reified (read: when generics will be reified) we could use a
  				 * typesafe heterogeneous container (which is almaost what we do
  				 * anyway), cf. Bloch 2008, Effective Java, 2nd Ed., Item 29.
  				 */
  				@SuppressWarnings("unchecked")
  				ImmutableAnnotation<T> typed = (ImmutableAnnotation<T>) o;
  				annotations.add(typed);
  			} catch (ClassCastException e) {
  				IllegalStateException exec = new IllegalStateException(
  						"Results of "
  								+ agent.getClass().getSimpleName()
  								+ " on the blackboard are not of the agent's output type;");
  				exec.initCause(e);
  				throw exec;
  			}
  		}
  		return annotations;
  	}
  
  	/**
  	 * @param exec
  	 *            The service to shut down
  	 */
  	private void shutdownAndWait(final ExecutorService exec) {
  		exec.shutdown();
  		try {
  			exec.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
  		} catch (InterruptedException e) {
  			e.printStackTrace();
  		}
  	}
  
  	@Override
  	public Class<T> getTypeClass() {
  		return AnnotationReader.getAgentClass((Class<? extends Agent<?, T>>) sources.get(0).getClass());
  	}
  }
  
  /**
   * A runnable that executes an agent with a specified input, storing results on
   * the specified blackboard. The blackboard access is synchronized.
   * 
   * @author fsteeg
   * @param <T>
   *            The type of the input
   */
  final class InteractionRunnable<T extends Comparable<T> & Serializable>
  		implements Runnable {
  	/***/
  	private final List<Annotation<T>> input;
  	/***/
  	private final Logger logger = TM2Logger.singleton();
  	/***/
  	private final Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> blackboard;
  	/***/
  	private final Agent<T, ?> agent;
  
  	/**
  	 * @param input
  	 *            The input to be processed by the agent
  	 * @param target
  	 *            The agent to use for processing the input
  	 * @param blackboard
  	 *            The blackboard to store the results of the agent on
  	 */
  	public InteractionRunnable(
  			final List<Annotation<T>> input,
  			final Agent<T, ?> target,
  			final Map<Class<? extends Agent<?, ?>>, List<Annotation<?>>> blackboard) {
  		this.input = Collections.unmodifiableList(input);
  		this.agent = target;
  		this.blackboard = blackboard;
  	}
  
  	/**
  	 * @see java.lang.Runnable#run()
  	 */
  	public synchronized void run() {
  		/*
  		 * We cast the class of a typed agent <T,?> to the typed class <T,?>,
  		 * this should never go wrong
  		 */
  		@SuppressWarnings("unchecked")
  		Class<Agent<T, ?>> agentClass = (Class<Agent<T, ?>>) agent.getClass();
  		logger.info(System.currentTimeMillis() + ": running agent: "
  				+ agentClass.getSimpleName());
  		/* TODO Is this really the best we get? */
  		List<?> produced = agent.process(input);
  		if (produced == null) {
  			throw new NullPointerException(String.format(
  					"Agent %s did not produce any annotations!", agent));
  		}
  		/* Remember the results: */
  		List<Annotation<?>> annotations = new ArrayList<Annotation<?>>();
  		for (Object o : produced) {
  			annotations.add((Annotation<?>) o);
  		}
  		/*
  		 * We store this agent's results in the map (we write it on the
  		 * blackboard):
  		 */
  		blackboard.put(agentClass, annotations);
  	}
  }