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 java.io.Serializable;
020
021import org.apache.commons.functor.UnaryFunction;
022import org.apache.commons.lang3.Validate;
023
024/**
025 * A {@link UnaryFunction UnaryFunction}
026 * representing the composition of
027 * {@link UnaryFunction UnaryFunctions},
028 * "chaining" the output of one to the input
029 * of another.  For example,
030 * <pre>new CompositeUnaryFunction(f).of(g)</pre>
031 * {@link #evaluate evaluates} to
032 * <code>f.evaluate(g.evaluate(obj))</code>, and
033 * <pre>new CompositeUnaryFunction(f).of(g).of(h)</pre>
034 * {@link #evaluate evaluates} to
035 * <code>f.evaluate(g.evaluate(h.evaluate(obj)))</code>.
036 * <p>
037 * When the collection is empty, this function is
038 * an identity function.
039 * </p>
040 * <p>
041 * Note that although this class implements
042 * {@link Serializable}, a given instance will
043 * only be truly <code>Serializable</code> if all the
044 * underlying functors are.  Attempts to serialize
045 * an instance whose delegates are not all
046 * <code>Serializable</code> will result in an exception.
047 * </p>
048 * @param <A> the argument type.
049 * @param <T> the returned value type.
050 * @version $Revision: 1365329 $ $Date: 2012-07-24 18:34:23 -0400 (Tue, 24 Jul 2012) $
051 */
052public class CompositeUnaryFunction<A, T> implements UnaryFunction<A, T>, Serializable {
053
054    /**
055     * serialVersionUID declaration.
056     */
057    private static final long serialVersionUID = 4945193629275757281L;
058
059    /** Base hash integer used to shift hash. */
060    private static final int HASH_SHIFT = 4;
061
062    /**
063     * Encapsulates a double function evaluation.
064     * @param <A> argument type
065     * @param <X> intermediate type
066     * @param <T> return type
067     */
068    private static class Helper<X, A, T> implements UnaryFunction<A, T>, Serializable {
069        /**
070         * serialVersionUID declaration.
071         */
072        private static final long serialVersionUID = 8167255331321876718L;
073        /**
074         * The last evaluator function.
075         */
076        private UnaryFunction<? super X, ? extends T> following;
077        /**
078         * The first evaluator function.
079         */
080        private UnaryFunction<? super A, ? extends X> preceding;
081
082        /**
083         * Create a new Helper.
084         * @param following UnaryFunction<X, Y>
085         * @param preceding UnaryFunction<Y, Z>
086         */
087        public Helper(UnaryFunction<? super X, ? extends T> following,
088                UnaryFunction<? super A, ? extends X> preceding) {
089            this.following = Validate.notNull(following, "UnaryFunction argument must not be null");
090            this.preceding = Validate.notNull(preceding, "UnaryFunction argument must not be null");
091        }
092
093        /**
094         * {@inheritDoc}
095         */
096        public T evaluate(A obj) {
097            return following.evaluate(preceding.evaluate(obj));
098        }
099
100        /**
101         * {@inheritDoc}
102         */
103        @Override
104        public boolean equals(Object obj) {
105            return obj == this || obj instanceof Helper<?, ?, ?> && equals((Helper<?, ?, ?>) obj);
106        }
107
108        /**
109         * Checks if input helper is equals to this instance.
110         *
111         * @param helper the helper to check
112         * @return true, if helpers are equals, false otherwise
113         */
114        private boolean equals(Helper<?, ?, ?> helper) {
115            return helper.following.equals(following) && helper.preceding.equals(preceding);
116        }
117
118        /**
119         * {@inheritDoc}
120         */
121        @Override
122        public int hashCode() {
123            int result = "CompositeUnaryFunction$Helper".hashCode();
124            result <<= 2;
125            result |= following.hashCode();
126            result <<= 2;
127            result |= preceding.hashCode();
128            return result;
129        }
130
131        /**
132         * {@inheritDoc}
133         */
134        @Override
135        public String toString() {
136            return following.toString() + " of " + preceding.toString();
137        }
138    }
139
140    /**
141     * The adapted function.
142     */
143    private final UnaryFunction<? super A, ? extends T> function;
144
145    /**
146     * Create a new CompositeUnaryFunction.
147     * @param function UnaryFunction to call
148     */
149    public CompositeUnaryFunction(UnaryFunction<? super A, ? extends T> function) {
150        this.function = Validate.notNull(function, "function must not be null");
151    }
152
153    /**
154     * Creates a new {@link CompositeUnaryFunction} instance given the input functions.
155     *
156     * @param <X> the argument type.
157     * @param following The first evaluator function.
158     * @param preceding The last evaluator function.
159     */
160    private <X> CompositeUnaryFunction(UnaryFunction<? super X, ? extends T> following,
161            UnaryFunction<? super A, ? extends X> preceding) {
162        this.function = new Helper<X, A, T>(following, preceding);
163    }
164
165    /**
166     * {@inheritDoc}
167     */
168    public final T evaluate(A obj) {
169        return function.evaluate(obj);
170    }
171
172    /**
173     * Fluently obtain a CompositeUnaryFunction that is "this function" applied to the specified preceding function.
174     * @param <P> argument type of the resulting function.
175     * @param preceding UnaryFunction
176     * @return CompositeUnaryFunction<P, T>
177     */
178    public final <P> CompositeUnaryFunction<P, T> of(UnaryFunction<? super P, ? extends A> preceding) {
179        Validate.notNull(preceding, "preceding function was null");
180        return new CompositeUnaryFunction<P, T>(function, preceding);
181    }
182
183    /**
184     * {@inheritDoc}
185     */
186    @Override
187    public final boolean equals(Object that) {
188        return that == this
189                || (that instanceof CompositeUnaryFunction<?, ?> && equals((CompositeUnaryFunction<?, ?>) that));
190    }
191
192    /**
193     * Learn whether another CompositeUnaryFunction is equal to this.
194     * @param that CompositeUnaryFunction to test
195     * @return boolean
196     */
197    public final boolean equals(CompositeUnaryFunction<?, ?> that) {
198        // by construction, list is never null
199        return null != that && function.equals(that.function);
200    }
201
202    /**
203     * {@inheritDoc}
204     */
205    @Override
206    public int hashCode() {
207        // by construction, list is never null
208        return ("CompositeUnaryFunction".hashCode() << HASH_SHIFT) ^ function.hashCode();
209    }
210
211    /**
212     * {@inheritDoc}
213     */
214    @Override
215    public String toString() {
216        return "CompositeUnaryFunction<" + function + ">";
217    }
218
219}