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.algorithm;
018
019import java.util.NoSuchElementException;
020
021import org.apache.commons.functor.BinaryFunction;
022import org.apache.commons.functor.Predicate;
023import org.apache.commons.functor.Procedure;
024import org.apache.commons.functor.generator.Generator;
025
026/**
027 * Return the first Object in a {@link Generator} matching a {@link Predicate}.
028 *
029 * @param <E> the arguments type.
030 * @version $Revision: 1537906 $ $Date: 2013-11-01 12:47:33 +0100 (Fr, 01 Nov 2013) $
031 */
032public final class FindWithinGenerator<E>
033    implements BinaryFunction<Generator<? extends E>, Predicate<? super E>, E> {
034
035    /**
036     * Basic instance.
037     */
038    public static final FindWithinGenerator<Object> INSTANCE = new FindWithinGenerator<Object>();
039
040    /**
041     * Helper procedure.
042     *
043     * @param <T> the argument type.
044     */
045    private static class FindProcedure<T> implements Procedure<T> {
046        /**
047         * The object found, if any.
048         */
049        private T found;
050        /**
051         * Flag to mark an object has been found.
052         */
053        private boolean wasFound;
054        /**
055         * The adapted predicate.
056         */
057        private Predicate<? super T> pred;
058
059        /**
060         * Create a new FindProcedure.
061         * @param pred the adapted predicate.
062         */
063        public FindProcedure(Predicate<? super T> pred) {
064            this.pred = pred;
065        }
066
067        /**
068         * {@inheritDoc}
069         */
070        public void run(T obj) {
071            if (!wasFound && pred.test(obj)) {
072                wasFound = true;
073                found = obj;
074            }
075        }
076    }
077
078    /**
079     * Flag to mark the {@link FindWithinGenerator#evaluate(Generator, Predicate)} method must return a user
080     * defined object when the adapted procedure does not find any object.
081     */
082    private final boolean useIfNone;
083    /**
084     * Object to be returned in the case the adapted procedure does not find any object.
085     */
086    private final E ifNone;
087
088    /**
089     * Create a new FindWithinGenerator.
090     */
091    public FindWithinGenerator() {
092        super();
093        ifNone = null;
094        useIfNone = false;
095    }
096
097    /**
098     * Create a new FindWithinGenerator.
099     * @param ifNone object to return if the Generator contains no matches.
100     */
101    public FindWithinGenerator(E ifNone) {
102        super();
103        this.ifNone = ifNone;
104        useIfNone = true;
105    }
106
107    /**
108     * {@inheritDoc}
109     * @param left Generator
110     * @param right Predicate
111     */
112    public E evaluate(Generator<? extends E> left, Predicate<? super E> right) {
113        FindProcedure<E> findProcedure = new FindProcedure<E>(right);
114        left.run(findProcedure);
115        if (!findProcedure.wasFound) {
116            if (useIfNone) {
117                return ifNone;
118            }
119            throw new NoSuchElementException("No element matching " + right + " was found.");
120        }
121        return findProcedure.found;
122    }
123
124    /**
125     * {@inheritDoc}
126     */
127    @Override
128    public boolean equals(Object obj) {
129        if (obj == this) {
130            return true;
131        }
132        if (!(obj instanceof FindWithinGenerator<?>)) {
133            return false;
134        }
135        FindWithinGenerator<?> other = (FindWithinGenerator<?>) obj;
136        return other.useIfNone == useIfNone && !useIfNone
137                || (other.ifNone == this.ifNone || other.ifNone != null && other.ifNone.equals(this.ifNone));
138    }
139
140    /**
141     * {@inheritDoc}
142     */
143    @Override
144    public int hashCode() {
145        if (!this.useIfNone) {
146            return System.identityHashCode(INSTANCE);
147        }
148        int result = "FindWithinGenerator".hashCode();
149        result ^= this.ifNone == null ? 0 : this.ifNone.hashCode();
150        return result;
151    }
152
153    /**
154     * {@inheritDoc}
155     */
156    @Override
157    public String toString() {
158        return "FindWithinGenerator<" + ifNone + "," + useIfNone + ">";
159    }
160
161    /**
162     * Get a static {@link FindWithinGenerator} instance.
163     * @return {@link FindWithinGenerator}
164     */
165    public static FindWithinGenerator<Object> instance() {
166        return INSTANCE;
167    }
168}