001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.functor.core.composite;
018
019import org.apache.commons.functor.BinaryFunction;
020import org.apache.commons.functor.Function;
021import org.apache.commons.lang3.Validate;
022
023/**
024 * A {@link BinaryFunction BinaryFunction} composed of
025 * one binary function, <i>f</i>, and two
026 * functions, <i>g</i> and <i>h</i>,
027 * evaluating the ordered parameters <i>x</i>, <i>y</i>
028 * to <code><i>f</i>(<i>g</i>(<i>x</i>),<i>h</i>(<i>y</i>))</code>.
029 * @param <L> the left argument type.
030 * @param <R> the right argument type.
031 * @param <T> the returned value type.
032 * @version $Revision: 1345136 $ $Date: 2012-06-01 09:47:06 -0300 (Fri, 01 Jun 2012) $
033 */
034public class CompositeBinaryFunction<L, R, T> implements BinaryFunction<L, R, T> {
035
036    /** Base hash integer used to shift hash. */
037    private static final int HASH_SHIFT = 4;
038
039    /**
040     *
041     *
042     * @param <G> the adapted function left argument type.
043     * @param <H> the adapted function right argument type.
044     * @param <L> the left argument type.
045     * @param <R> the right argument type.
046     * @param <T> the returned value type.
047     */
048    private static class Helper<G, H, L, R, T> implements BinaryFunction<L, R, T> {
049        /**
050         * The adapted function to receive <code>(output(g), output(h))</code>.
051         */
052        private BinaryFunction<? super G, ? super H, ? extends T> f;
053        /**
054         * The adapted left function.
055         */
056        private Function<? super L, ? extends G> g;
057        /**
058         * The adapted right function.
059         */
060        private Function<? super R, ? extends H> h;
061
062        /**
063         * Create a new Helper.
064         * @param f BinaryFunction to receive <code>(output(g), output(h))</code>
065         * @param g left Function
066         * @param h right Function
067         */
068        public Helper(BinaryFunction<? super G, ? super H, ? extends T> f, Function<? super L, ? extends G> g,
069                Function<? super R, ? extends H> h) {
070            this.f = f;
071            this.g = g;
072            this.h = h;
073        }
074
075        /**
076         * {@inheritDoc}
077         */
078        public T evaluate(L left, R right) {
079            return f.evaluate(g.evaluate(left), h.evaluate(right));
080        }
081    }
082
083    /**
084     * The adapted helper.
085     */
086    private final Helper<?, ?, L, R, T> helper;
087
088    // constructor
089    // ------------------------------------------------------------------------
090    /**
091     * Create a new CompositeBinaryFunction.
092     *
093     * @param <G> the adapted function left argument type.
094     * @param <H> the adapted function right argument type.
095     * @param f BinaryFunction to receive <code>(output(g), output(h))</code>
096     * @param g left Function
097     * @param h right Function
098     */
099    public <G, H> CompositeBinaryFunction(BinaryFunction<? super G, ? super H, ? extends T> f,
100            Function<? super L, ? extends G> g, Function<? super R, ? extends H> h) {
101        this.helper = new Helper<G, H, L, R, T>(
102                Validate.notNull(f, "BinaryFunction must not be null"),
103                Validate.notNull(g, "left Function must not be null"),
104                Validate.notNull(h, "right Function must not be null")
105        );
106    }
107
108    // function interface
109    // ------------------------------------------------------------------------
110    /**
111     * {@inheritDoc}
112     */
113    public T evaluate(L left, R right) {
114        return helper.evaluate(left, right);
115    }
116
117    /**
118     * {@inheritDoc}
119     */
120    @Override
121    public boolean equals(Object obj) {
122        if (obj == this) {
123            return true;
124        }
125        if (!(obj instanceof CompositeBinaryFunction<?, ?, ?>)) {
126            return false;
127        }
128        CompositeBinaryFunction<?, ?, ?> that = (CompositeBinaryFunction<?, ?, ?>) obj;
129        return this.helper.f.equals(that.helper.f)
130                && this.helper.g.equals(that.helper.g)
131                && this.helper.h.equals(that.helper.h);
132    }
133
134    /**
135     * {@inheritDoc}
136     */
137    @Override
138    public int hashCode() {
139        int hash = "CompositeBinaryFunction".hashCode();
140        hash <<= HASH_SHIFT;
141        hash ^= helper.f.hashCode();
142        hash <<= HASH_SHIFT;
143        hash ^= helper.g.hashCode();
144        hash <<= HASH_SHIFT;
145        hash ^= helper.h.hashCode();
146        return hash;
147    }
148
149    /**
150     * {@inheritDoc}
151     */
152    @Override
153    public String toString() {
154        return "CompositeBinaryFunction<" + helper.f + ";" + helper.g + ";" + helper.h + ">";
155    }
156
157}