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     */
017    package org.apache.commons.functor.core.composite;
018    
019    import java.io.Serializable;
020    
021    import org.apache.commons.functor.BinaryFunction;
022    
023    /**
024     * A {@link BinaryFunction BinaryFunction} composed of
025     * three binary functions, <i>f</i>, <i>g</i> and <i>h</i>,
026     * evaluating the ordered parameters <i>x</i>, <i>y</i>
027     * to <code><i>f</i>(<i>g</i>(<i>x</i>,<i>y</i>),<i>h</i>(<i>x</i>,<i>y</i>))</code>.
028     * <p>
029     * Note that although this class implements
030     * {@link Serializable}, a given instance will
031     * only be truly <code>Serializable</code> if all the
032     * underlying functors are.  Attempts to serialize
033     * an instance whose delegates are not all
034     * <code>Serializable</code> will result in an exception.
035     * </p>
036     * @version $Revision: 1171154 $ $Date: 2011-09-15 17:58:38 +0200 (Thu, 15 Sep 2011) $
037     * @author Rodney Waldhoff
038     */
039    public class BinaryCompositeBinaryFunction<L, R, T> implements BinaryFunction<L, R, T>, Serializable {
040    
041        /**
042         * serialVersionUID declaration.
043         */
044        private static final long serialVersionUID = 2570517284319064043L;
045    
046        /** Base hash integer used to shift hash */
047        private static final int HASH_SHIFT = 4;
048    
049        /**
050         * Type-remembering Helper
051         *
052         * @param <G>
053         * @param <H>
054         */
055        private static class Helper<G, H, L, R, T> implements BinaryFunction<L, R, T>, Serializable {
056            /**
057             * serialVersionUID declaration.
058             */
059            private static final long serialVersionUID = 6013646799505641592L;
060            private BinaryFunction<? super G, ? super H, ? extends T> f;
061            private BinaryFunction<? super L, ? super R, ? extends G> g;
062            private BinaryFunction<? super L, ? super R, ? extends H> h;
063    
064            /**
065             * Create a new Helper.
066             * @param f final BinaryFunction to evaluate
067             * @param g left preceding BinaryFunction
068             * @param h right preceding BinaryFunction
069             */
070            public Helper(BinaryFunction<? super G, ? super H, ? extends T> f,
071                    BinaryFunction<? super L, ? super R, ? extends G> g,
072                    BinaryFunction<? super L, ? super R, ? extends H> h) {
073                this.f = f;
074                this.g = g;
075                this.h = h;
076            }
077    
078            /**
079             * {@inheritDoc}
080             */
081            public T evaluate(L left, R right) {
082                return f.evaluate(g.evaluate(left, right), h.evaluate(left, right));
083            }
084        }
085    
086        private final Helper<?, ?, L, R, T> helper;
087    
088        // constructor
089        // ------------------------------------------------------------------------
090        /**
091         * Create a new BinaryCompositeBinaryFunction.
092         * @param f final BinaryFunction to evaluate
093         * @param g left preceding BinaryFunction
094         * @param h right preceding BinaryFunction
095         */
096        public <G, H> BinaryCompositeBinaryFunction(BinaryFunction<? super G, ? super H, ? extends T> f,
097                BinaryFunction<? super L, ? super R, ? extends G> g, BinaryFunction<? super L, ? super R, ? extends H> h) {
098            if (f == null || g == null || h == null) {
099                throw new IllegalArgumentException("BinaryFunction arguments may not be null");
100            }
101            this.helper = new Helper<G, H, L, R, T>(f, g, h);
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        public final boolean equals(Object that) {
117            return that == this || (that instanceof BinaryCompositeBinaryFunction<?, ?, ?>
118                                        && equals((BinaryCompositeBinaryFunction<?, ?, ?>) that));
119        }
120    
121        /**
122         * Learn whether another BinaryCompositeBinaryFunction is equal to this.
123         * @param that BinaryCompositeBinaryFunction to test
124         * @return boolean
125         */
126        public final boolean equals(BinaryCompositeBinaryFunction<?, ?, ?> that) {
127            return null != that
128                    && helper.f.equals(that.helper.f)
129                    && helper.g.equals(that.helper.g)
130                    && helper.h.equals(that.helper.h);
131        }
132    
133        /**
134         * {@inheritDoc}
135         */
136        public int hashCode() {
137            int hash = "BinaryCompositeBinaryFunction".hashCode();
138                hash <<= HASH_SHIFT;
139                hash ^= helper.f.hashCode();
140                hash <<= HASH_SHIFT;
141                hash ^= helper.g.hashCode();
142                hash <<= HASH_SHIFT;
143                hash ^= helper.h.hashCode();
144            return hash;
145        }
146    
147        /**
148         * {@inheritDoc}
149         */
150        public String toString() {
151            return "BinaryCompositeBinaryFunction<" + helper.f + ";" + helper.g + ";" + helper.h + ">";
152        }
153    
154    }