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.lang3.Validate;
021
022/**
023 * A {@link BinaryFunction BinaryFunction} composed of
024 * three binary functions, <i>f</i>, <i>g</i> and <i>h</i>,
025 * evaluating the ordered parameters <i>x</i>, <i>y</i>
026 * to <code><i>f</i>(<i>g</i>(<i>x</i>,<i>y</i>),<i>h</i>(<i>x</i>,<i>y</i>))</code>.
027 * @param <L> the function left argument type.
028 * @param <R> the function right argument type.
029 * @param <T> the function returned value type.
030 * @version $Revision: 1537602 $ $Date: 2013-10-31 20:53:09 +0100 (Do, 31 Okt 2013) $
031 */
032public class BinaryCompositeBinaryFunction<L, R, T> implements BinaryFunction<L, R, T> {
033
034    /** Base hash integer used to shift hash. */
035    private static final int HASH_SHIFT = 4;
036
037    /**
038     * Type-remembering Helper.
039     *
040     * @param <G> the function left argument type.
041     * @param <H> the function right argument type.
042     */
043    private static class Helper<G, H, L, R, T> implements BinaryFunction<L, R, T> {
044        /**
045         * Global evaluator.
046         */
047        private BinaryFunction<? super G, ? super H, ? extends T> f;
048        /**
049         * This function evaluation will be the left argument of main evaluator.
050         */
051        private BinaryFunction<? super L, ? super R, ? extends G> g;
052        /**
053         * This function evaluation will be the right argument of main evaluator.
054         */
055        private BinaryFunction<? super L, ? super R, ? extends H> h;
056
057        /**
058         * Create a new Helper.
059         * @param f final BinaryFunction to evaluate
060         * @param g left preceding BinaryFunction
061         * @param h right preceding BinaryFunction
062         */
063        public Helper(BinaryFunction<? super G, ? super H, ? extends T> f,
064                BinaryFunction<? super L, ? super R, ? extends G> g,
065                BinaryFunction<? super L, ? super R, ? extends H> h) {
066            this.f = f;
067            this.g = g;
068            this.h = h;
069        }
070
071        /**
072         * {@inheritDoc}
073         */
074        public T evaluate(L left, R right) {
075            return f.evaluate(g.evaluate(left, right), h.evaluate(left, right));
076        }
077    }
078
079    /**
080     * The helper used for the evaluation.
081     */
082    private final Helper<?, ?, L, R, T> helper;
083
084    // constructor
085    // ------------------------------------------------------------------------
086    /**
087     * Create a new BinaryCompositeBinaryFunction.
088     *
089     * @param <G> the main function left argument type.
090     * @param <H> the main function right argument type.
091     * @param f final BinaryFunction to evaluate
092     * @param g left preceding BinaryFunction
093     * @param h right preceding BinaryFunction
094     */
095    public <G, H> BinaryCompositeBinaryFunction(BinaryFunction<? super G, ? super H, ? extends T> f,
096            BinaryFunction<? super L, ? super R, ? extends G> g, BinaryFunction<? super L, ? super R, ? extends H> h) {
097        this.helper = new Helper<G, H, L, R, T>(
098                Validate.notNull(f, "final BinaryFunction argument must not be null"),
099                Validate.notNull(g, "left preceding BinaryFunction argument must not be null"),
100                Validate.notNull(h, "right preceding BinaryFunction argument must not be null")
101        );
102    }
103
104    // function interface
105    // ------------------------------------------------------------------------
106    /**
107     * {@inheritDoc}
108     */
109    public final T evaluate(L left, R right) {
110        return helper.evaluate(left, right);
111    }
112
113    /**
114     * {@inheritDoc}
115     */
116    @Override
117    public final boolean equals(Object obj) {
118        if (obj == this) {
119            return true;
120        }
121        if (!(obj instanceof BinaryCompositeBinaryFunction<?, ?, ?>)) {
122            return false;
123        }
124        BinaryCompositeBinaryFunction<?, ?, ?> that = (BinaryCompositeBinaryFunction<?, ?, ?>) obj;
125        return this.helper.f.equals(that.helper.f)
126                && this.helper.g.equals(that.helper.g)
127                && this.helper.h.equals(that.helper.h);
128    }
129
130    /**
131     * {@inheritDoc}
132     */
133    @Override
134    public int hashCode() {
135        int hash = "BinaryCompositeBinaryFunction".hashCode();
136            hash <<= HASH_SHIFT;
137            hash ^= helper.f.hashCode();
138            hash <<= HASH_SHIFT;
139            hash ^= helper.g.hashCode();
140            hash <<= HASH_SHIFT;
141            hash ^= helper.h.hashCode();
142        return hash;
143    }
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public String toString() {
150        return "BinaryCompositeBinaryFunction<" + helper.f + ";" + helper.g + ";" + helper.h + ">";
151    }
152
153}