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.BinaryPredicate;
020import org.apache.commons.functor.Function;
021import org.apache.commons.lang3.Validate;
022
023/**
024 * A {@link BinaryPredicate BinaryPredicate} composed of
025 * one binary predicate, <i>p</i>, and two
026 * functions, <i>f</i> and <i>g</i>,
027 * evaluating the ordered parameters <i>x</i>, <i>y</i>
028 * to <code><i>p</i>(<i>f</i>(<i>x</i>),<i>g</i>(<i>y</i>))</code>.
029 * @param <L> the left argument type.
030 * @param <R> the right argument type.
031 * @version $Revision: 1345136 $ $Date: 2012-06-01 09:47:06 -0300 (Fri, 01 Jun 2012) $
032 */
033public class CompositeBinaryPredicate<L, R> implements BinaryPredicate<L, R> {
034
035    /** Base hash integer used to shift hash. */
036    private static final int HASH_SHIFT = 4;
037
038    /**
039     * Internal helper.
040     *
041     * @param <G> right function type.
042     * @param <H> right function type.
043     * @param <L> left function type.
044     * @param <R> left function type.
045     */
046    private static class Helper<G, H, L, R> implements BinaryPredicate<L, R> {
047        /**
048         * BinaryPredicate to test <i>output(</i><code>f</code><i>), output(</i><code>g</code><i>)</i>.
049         */
050        private BinaryPredicate<? super G, ? super H> f;
051        /**
052         * left Function.
053         */
054        private Function<? super L, ? extends G> g;
055        /**
056         * right Function.
057         */
058        private Function<? super R, ? extends H> h;
059
060        /**
061         * Create a new Helper.
062         * @param f BinaryPredicate to test <i>output(</i><code>f</code><i>), output(</i><code>g</code><i>)</i>
063         * @param g left Function
064         * @param h right Function
065         */
066        public Helper(BinaryPredicate<? super G, ? super H> f, Function<? super L, ? extends G> g,
067                Function<? super R, ? extends H> h) {
068            this.f = f;
069            this.g = g;
070            this.h = h;
071        }
072
073        /**
074         * {@inheritDoc}
075         */
076        public boolean test(L left, R right) {
077            return f.test(g.evaluate(left), h.evaluate(right));
078        }
079    }
080
081    // attributes
082    // ------------------------------------------------------------------------
083    /**
084     * The adapted helper.
085     */
086    private final Helper<?, ?, L, R> helper;
087
088    // constructor
089    // ------------------------------------------------------------------------
090    /**
091     * Create a new CompositeBinaryPredicate.
092     *
093     * @param <G> right function type.
094     * @param <H> right function type.
095     * @param f BinaryPredicate to test <i>output(</i><code>f</code><i>), output(</i><code>g</code><i>)</i>
096     * @param g left Function
097     * @param h right Function
098     */
099    public <G, H> CompositeBinaryPredicate(final BinaryPredicate<? super G, ? super H> f,
100            final Function<? super L, ? extends G> g, final Function<? super R, ? extends H> h) {
101        helper = new Helper<G, H, L, R>(
102                Validate.notNull(f, "BinaryPredicate 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 boolean test(L left, R right) {
114        return helper.test(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 CompositeBinaryPredicate<?, ?>)) {
126            return false;
127        }
128        CompositeBinaryPredicate<?, ?> that = (CompositeBinaryPredicate<?, ?>) 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 = "CompositeBinaryPredicate".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 "CompositeBinaryPredicate<" + helper.f + ";" + helper.g + ";" + helper.h + ">";
155    }
156
157}