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.Iterator; 020 021import org.apache.commons.functor.BinaryFunction; 022import org.apache.commons.lang3.Validate; 023 024/** 025 * A generator for a range of characters. 026 * 027 * @since 1.0 028 * @version $Revision$ $Date$ 029 */ 030public final class CharacterRange extends AbstractRange<Character, Integer> { 031 032 /** 033 * Calculate default step. 034 */ 035 public static final BinaryFunction<Character, Character, Integer> DEFAULT_STEP = 036 new BinaryFunction<Character, Character, Integer>() { 037 038 public Integer evaluate(Character left, Character right) { 039 return left > right ? -1 : 1; 040 } 041 }; 042 043 // constructors 044 // --------------------------------------------------------------- 045 /** 046 * Create a new CharacterRange. 047 * 048 * @param from start 049 * @param to end 050 */ 051 public CharacterRange(char from, char to) { 052 this(from, to, DEFAULT_STEP.evaluate(from, to).intValue()); 053 } 054 055 /** 056 * Create a new CharacterRange. 057 * 058 * @param from start 059 * @param to end 060 * @param step increment 061 */ 062 public CharacterRange(char from, char to, int step) { 063 this(from, BoundType.CLOSED, to, BoundType.CLOSED, step); 064 } 065 066 /** 067 * Create a new CharacterRange. 068 * 069 * @param from start 070 * @param to end 071 * @throws NullPointerException if either {@link Endpoint} is {@code null} 072 */ 073 public CharacterRange(Endpoint<Character> from, Endpoint<Character> to) { 074 this(from, to, DEFAULT_STEP.evaluate(from.getValue(), to.getValue())); 075 } 076 077 /** 078 * Create a new CharacterRange. 079 * 080 * @param from start 081 * @param to end 082 * @param step increment 083 * @throws NullPointerException if either {@link Endpoint} is {@code null} 084 */ 085 public CharacterRange(Endpoint<Character> from, Endpoint<Character> to, int step) { 086 super(from, to, Integer.valueOf(step), new BinaryFunction<Character, Integer, Character>() { 087 088 public Character evaluate(Character left, Integer right) { 089 return Character.valueOf((char) (left.charValue() + right.intValue())); 090 } 091 }); 092 final char f = from.getValue(); 093 final char t = to.getValue(); 094 095 Validate.isTrue(f == t || Integer.signum(step) == Integer.signum(t - f), 096 "Will never reach '%s' from '%s' using step %s", t, f, step); 097 } 098 099 /** 100 * Create a new CharacterRange. 101 * 102 * @param from start 103 * @param leftBoundType type of left bound 104 * @param to end 105 * @param rightBoundType type of right bound 106 * @throws NullPointerException if either bound type is {@code null} 107 */ 108 public CharacterRange(char from, BoundType leftBoundType, char to, BoundType rightBoundType) { 109 this(from, leftBoundType, to, rightBoundType, DEFAULT_STEP.evaluate(from, to)); 110 } 111 112 /** 113 * Create a new CharacterRange. 114 * 115 * @param from start 116 * @param leftBoundType type of left bound 117 * @param to end 118 * @param rightBoundType type of right bound 119 * @param step increment 120 * @throws NullPointerException if either bound type is {@code null} 121 */ 122 public CharacterRange(char from, BoundType leftBoundType, char to, BoundType rightBoundType, int step) { 123 this(new Endpoint<Character>(from, leftBoundType), new Endpoint<Character>(to, rightBoundType), step); 124 } 125 126 // range methods 127 // --------------------------------------------------------------- 128 129 /** 130 * {@inheritDoc} 131 */ 132 public boolean contains(Character obj) { 133 if (obj == null) { 134 return Boolean.FALSE; 135 } 136 char leftValue = this.getLeftEndpoint().getValue().charValue(); 137 char rightValue = this.getRightEndpoint().getValue().charValue(); 138 boolean includeLeft = this.getLeftEndpoint().getBoundType() == BoundType.CLOSED; 139 boolean includeRight = this.getRightEndpoint().getBoundType() == BoundType.CLOSED; 140 int step = this.getStep().intValue(); 141 int value = (int) obj.charValue(); 142 143 int firstValue = 0; 144 int lastValue = 0; 145 146 if (step < 0.0) { 147 firstValue = includeLeft ? leftValue : leftValue + step; 148 lastValue = includeRight ? rightValue : rightValue + 1; 149 if (value > firstValue || value < lastValue) { 150 return Boolean.FALSE; 151 } 152 } else { 153 firstValue = includeLeft ? leftValue : leftValue + step; 154 lastValue = includeRight ? rightValue : rightValue - 1; 155 if (value < firstValue || value > lastValue) { 156 return Boolean.FALSE; 157 } 158 } 159 return ((double) (value - firstValue) / step + 1) % 1.0 == 0.0; 160 } 161 162 /** 163 * {@inheritDoc} 164 */ 165 protected Iterator<Character> createIterator() { 166 return new Iterator<Character>() { 167 private char currentValue; 168 169 { 170 currentValue = leftEndpoint.getValue(); 171 172 if (leftEndpoint.getBoundType() == BoundType.OPEN) { 173 this.currentValue += step; 174 } 175 } 176 177 public void remove() { 178 throw new UnsupportedOperationException(); 179 } 180 181 public Character next() { 182 final int step = getStep(); 183 final char r = currentValue; 184 currentValue += step; 185 return Character.valueOf(r); 186 } 187 188 public boolean hasNext() { 189 final int cmp = Character.valueOf(currentValue).compareTo(rightEndpoint.getValue()); 190 191 if (cmp == 0) { 192 return rightEndpoint.getBoundType() == BoundType.CLOSED; 193 } 194 if (step > 0) { 195 return cmp < 0; 196 } 197 return cmp > 0; 198 } 199 }; 200 } 201 202}