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 */
017 package org.apache.commons.lang;
018
019 import java.io.Serializable;
020
021 /**
022 * <p>A contiguous range of characters, optionally negated.</p>
023 *
024 * <p>Instances are immutable.</p>
025 *
026 * @author Stephen Colebourne
027 * @author Chris Feldhacker
028 * @author Gary Gregory
029 * @since 1.0
030 * @version $Id: CharRange.java 471626 2006-11-06 04:02:09Z bayard $
031 */
032 public final class CharRange implements Serializable {
033
034 /**
035 * Required for serialization support. Lang version 2.0.
036 *
037 * @see java.io.Serializable
038 */
039 private static final long serialVersionUID = 8270183163158333422L;
040
041 /** The first character, inclusive, in the range. */
042 private final char start;
043 /** The last character, inclusive, in the range. */
044 private final char end;
045 /** True if the range is everything except the characters specified. */
046 private final boolean negated;
047
048 /** Cached toString. */
049 private transient String iToString;
050
051 //-----------------------------------------------------------------------
052 /**
053 * <p>Constructs a <code>CharRange</code> over a single character.</p>
054 *
055 * @param ch only character in this range
056 */
057 public CharRange(char ch) {
058 this(ch, ch, false);
059 }
060
061 /**
062 * <p>Constructs a <code>CharRange</code> over a single character,
063 * optionally negating the range.</p>
064 *
065 * <p>A negated range includes everything except the specified char.</p>
066 *
067 * @param ch only character in this range
068 * @param negated true to express everything except the range
069 */
070 public CharRange(char ch, boolean negated) {
071 this(ch, ch, negated);
072 }
073
074 /**
075 * <p>Constructs a <code>CharRange</code> over a set of characters.</p>
076 *
077 * @param start first character, inclusive, in this range
078 * @param end last character, inclusive, in this range
079 */
080 public CharRange(char start, char end) {
081 this(start, end, false);
082 }
083
084 /**
085 * <p>Constructs a <code>CharRange</code> over a set of characters,
086 * optionally negating the range.</p>
087 *
088 * <p>A negated range includes everything except that defined by the
089 * start and end characters.</p>
090 *
091 * <p>If start and end are in the wrong order, they are reversed.
092 * Thus <code>a-e</code> is the same as <code>e-a</code>.</p>
093 *
094 * @param start first character, inclusive, in this range
095 * @param end last character, inclusive, in this range
096 * @param negated true to express everything except the range
097 */
098 public CharRange(char start, char end, boolean negated) {
099 super();
100 if (start > end) {
101 char temp = start;
102 start = end;
103 end = temp;
104 }
105
106 this.start = start;
107 this.end = end;
108 this.negated = negated;
109 }
110
111 // Accessors
112 //-----------------------------------------------------------------------
113 /**
114 * <p>Gets the start character for this character range.</p>
115 *
116 * @return the start char (inclusive)
117 */
118 public char getStart() {
119 return this.start;
120 }
121
122 /**
123 * <p>Gets the end character for this character range.</p>
124 *
125 * @return the end char (inclusive)
126 */
127 public char getEnd() {
128 return this.end;
129 }
130
131 /**
132 * <p>Is this <code>CharRange</code> negated.</p>
133 *
134 * <p>A negated range includes everything except that defined by the
135 * start and end characters.</p>
136 *
137 * @return <code>true</code> is negated
138 */
139 public boolean isNegated() {
140 return negated;
141 }
142
143 // Contains
144 //-----------------------------------------------------------------------
145 /**
146 * <p>Is the character specified contained in this range.</p>
147 *
148 * @param ch the character to check
149 * @return <code>true</code> if this range contains the input character
150 */
151 public boolean contains(char ch) {
152 return (ch >= start && ch <= end) != negated;
153 }
154
155 /**
156 * <p>Are all the characters of the passed in range contained in
157 * this range.</p>
158 *
159 * @param range the range to check against
160 * @return <code>true</code> if this range entirely contains the input range
161 * @throws IllegalArgumentException if <code>null</code> input
162 */
163 public boolean contains(CharRange range) {
164 if (range == null) {
165 throw new IllegalArgumentException("The Range must not be null");
166 }
167 if (negated) {
168 if (range.negated) {
169 return start >= range.start && end <= range.end;
170 } else {
171 return range.end < start || range.start > end;
172 }
173 } else {
174 if (range.negated) {
175 return start == 0 && end == Character.MAX_VALUE;
176 } else {
177 return start <= range.start && end >= range.end;
178 }
179 }
180 }
181
182 // Basics
183 //-----------------------------------------------------------------------
184 /**
185 * <p>Compares two CharRange objects, returning true if they represent
186 * exactly the same range of characters defined in the same way.</p>
187 *
188 * @param obj the object to compare to
189 * @return true if equal
190 */
191 public boolean equals(Object obj) {
192 if (obj == this) {
193 return true;
194 }
195 if (obj instanceof CharRange == false) {
196 return false;
197 }
198 CharRange other = (CharRange) obj;
199 return start == other.start && end == other.end && negated == other.negated;
200 }
201
202 /**
203 * <p>Gets a hashCode compatible with the equals method.</p>
204 *
205 * @return a suitable hashCode
206 */
207 public int hashCode() {
208 return 83 + start + 7 * end + (negated ? 1 : 0);
209 }
210
211 /**
212 * <p>Gets a string representation of the character range.</p>
213 *
214 * @return string representation of this range
215 */
216 public String toString() {
217 if (iToString == null) {
218 StringBuffer buf = new StringBuffer(4);
219 if (isNegated()) {
220 buf.append('^');
221 }
222 buf.append(start);
223 if (start != end) {
224 buf.append('-');
225 buf.append(end);
226 }
227 iToString = buf.toString();
228 }
229 return iToString;
230 }
231
232 }