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.range;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Iterator;
022
023import org.apache.commons.functor.BinaryFunction;
024import org.apache.commons.lang3.ObjectUtils;
025import org.apache.commons.lang3.Validate;
026import org.apache.commons.lang3.builder.EqualsBuilder;
027
028/**
029 * Abstract {@link Range}.
030 *
031 * @param <T> type of element
032 * @param <S> type of step
033 */
034public abstract class AbstractRange<T extends Comparable<T>, S> implements Range<T, S> {
035
036    /**
037     * Left limit.
038     */
039    protected final Endpoint<T> leftEndpoint;
040
041    /**
042     * Right limit.
043     */
044    protected final Endpoint<T> rightEndpoint;
045
046    /**
047     * Increment step.
048     */
049    protected final S step;
050
051    /**
052     * Function to implement the taking of a step.
053     */
054    private final BinaryFunction<T, S, T> nextValue;
055
056    /**
057     * Create a new {@link AbstractRange}.
058     *
059     * @param leftEndpoint left endpoint
060     * @param rightEndpoint right endpoint
061     * @param step increment step
062     * @param nextValue function to implement the taking of a step
063     */
064    protected AbstractRange(Endpoint<T> leftEndpoint, Endpoint<T> rightEndpoint, S step,
065            BinaryFunction<T, S, T> nextValue) {
066        super();
067        this.leftEndpoint = Validate.notNull(leftEndpoint, "Left Endpoint argument must not be null");
068        this.rightEndpoint = Validate.notNull(rightEndpoint, "Right Endpoint argument must not be null");
069        this.step = Validate.notNull(step, "step must not be null");
070        this.nextValue = Validate.notNull(nextValue, "next value function");
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    public Endpoint<T> getLeftEndpoint() {
077        return leftEndpoint;
078    }
079
080    /**
081     * {@inheritDoc}
082     */
083    public Endpoint<T> getRightEndpoint() {
084        return rightEndpoint;
085    }
086
087    /**
088     * {@inheritDoc}
089     */
090    public S getStep() {
091        return step;
092    }
093
094    /**
095     * {@inheritDoc}
096     */
097    public boolean containsAll(Collection<T> coll) {
098        if (coll == null || coll.isEmpty()) {
099            return false;
100        }
101        for (T t : coll) {
102            if (!contains(t)) {
103                return false;
104            }
105        }
106        return true;
107    }
108
109    // iterable
110    // ---------------------------------------------------------------
111    /**
112     * {@inheritDoc}
113     */
114    public final Iterator<T> iterator() {
115        // empty range -> empty iterator:
116        if (isEmpty()) {
117            return Collections.<T> emptySet().iterator();
118        }
119        // not empty and same values -> single-value range:
120        if (ObjectUtils.equals(leftEndpoint.getValue(), rightEndpoint.getValue())) {
121            return Collections.singleton(leftEndpoint.getValue()).iterator();
122        }
123        return createIterator();
124    }
125
126    /**
127     * Create a non-empty iterator.
128     *
129     * @return Iterator
130     */
131    protected abstract Iterator<T> createIterator();
132
133    // object methods
134    // ---------------------------------------------------------------
135    /**
136     * {@inheritDoc}
137     */
138    @Override
139    public String toString() {
140        final String pattern = "%s<%s, %s, %s>";
141        return String.format(pattern, getClass().getSimpleName(), leftEndpoint.toLeftString(),
142            rightEndpoint.toRightString(), step);
143    }
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public boolean equals(Object obj) {
150        if (obj == this) {
151            return true;
152        }
153        if (!(obj instanceof AbstractRange<?, ?>)) {
154            return false;
155        }
156        final Range<?, ?> that = (Range<?, ?>) obj;
157        return new EqualsBuilder().append(getLeftEndpoint(), that.getLeftEndpoint())
158            .append(getRightEndpoint(), that.getRightEndpoint()).append(getStep(), that.getStep()).build();
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    public int hashCode() {
166        int hash = getClass().getName().hashCode();
167        hash <<= 2;
168        hash ^= this.leftEndpoint.hashCode();
169        hash <<= 2;
170        hash ^= this.rightEndpoint.hashCode();
171        hash <<= 2;
172        hash ^= this.step.hashCode();
173        return hash;
174    }
175
176    /**
177     * {@inheritDoc}
178     */
179    public final boolean isEmpty() {
180        final T leftValue = leftEndpoint.getValue();
181        final T rightValue = rightEndpoint.getValue();
182
183        final int cmp = ObjectUtils.compare(leftValue, rightValue);
184        final boolean closedLeft = leftEndpoint.getBoundType() == BoundType.CLOSED;
185        final boolean closedRight = rightEndpoint.getBoundType() == BoundType.CLOSED;
186
187        if (cmp == 0 && !(closedLeft && closedRight)) {
188            return true;
189        }
190
191        final T firstValue = closedLeft ? leftValue : nextValue.evaluate(leftValue, step);
192
193        final int cmpFirst = ObjectUtils.compare(firstValue, rightValue);
194
195        if (cmpFirst == 0) {
196            return !closedRight;
197        }
198
199        final boolean ascending = cmp < 0;
200        return ascending ? cmpFirst > 0 : cmpFirst < 0;
201    }
202
203}