ResultTransformerDispatcher.java

/***************************************************************************
   Copyright 2014 Emily Estes

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
***************************************************************************/
package net.metanotion.util;


import java.util.Map;

import net.metanotion.functor.Block;
import net.metanotion.functor.Identity;

/** This dispatcher applies a block to the result of the evaluating the message generated by the delegated distpatcher.
	@param <O> The type of object the delegated dispatcher will be evaluating it's message against.
	@param <D> The value type of the pair this dispatcher operates on. Presumably the first half of the pair is
		something like a method name or other string based key used to look up the appropriate block to use to
		transform the result of the message evaluation.
*/
public final class ResultTransformerDispatcher<O, D> implements Dispatcher<O, Map.Entry<String,D>> {
	private final Class<O> klazz;
	private final Map<String,Block> resultTransformers;
	private final Block defaultXfmr;
	private final Dispatcher<O, Map.Entry<String,D>> disp;

	/** Create a new result transformer dispatcher using the "identity function" as a default transformation(i.e. no
		transformation).
		@param klazz The class this dispatcher evaluates it's messages against.
		@param resultTransformers The map of keys to blocks that transform results.
		@param disp The dispatcher to delegate the data to.
	*/
	public ResultTransformerDispatcher(final Class<O> klazz,
			final Map<String,Block> resultTransformers,
			final Dispatcher<O, Map.Entry<String,D>> disp) {
		this(klazz, resultTransformers, Identity.I, disp);
	}

	/** Create a new result transformer dispatcher.
		@param klazz The class this dispatcher evaluates it's messages against.
		@param resultTransformers The map of keys to blocks that transform results.
		@param defaultXfmr The block to use if no block is found in the map.
		@param disp The dispatcher to delegate the data to.
	*/
	public ResultTransformerDispatcher(final Class<O> klazz,
			final Map<String,Block> resultTransformers,
			final Block defaultXfmr,
			final Dispatcher<O, Map.Entry<String,D>> disp) {
		this.klazz = klazz;
		this.resultTransformers = resultTransformers;
		this.defaultXfmr = defaultXfmr;
		this.disp = disp;
	}

	private static final class Msg<O> implements Message<O> {
		private final Class<O> klazz;
		private final Block transformer;
		private final Message<O> msg;
		public Msg(final Class<O> klazz, final Block transformer, final Message<O> msg) {
			this.klazz = klazz;
			this.transformer = transformer;
			this.msg = msg;
		}

		@Override public Object call(final O o) {
			try {
				return this.transformer.eval(this.msg.call(o));
			} catch (Exception e) { throw new RuntimeException(e); }
		}

		@Override public Class<O> receiverType() { return this.klazz; }
	}

	@Override public Message<O> dispatch(final Map.Entry<String,D> data) {
		final String k = data.getKey();
		final Block xfrm = resultTransformers.containsKey(k) ? resultTransformers.get(k) : defaultXfmr;
		return new Msg<O>(klazz, xfrm, disp.dispatch(data));
	}
}