View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang;
18  
19  import java.io.Serializable;
20  
21  /**
22   * <p>A contiguous range of characters, optionally negated.</p>
23   * 
24   * <p>Instances are immutable.</p>
25   *
26   * @author Stephen Colebourne
27   * @author Chris Feldhacker
28   * @author Gary Gregory
29   * @since 1.0
30   * @version $Id: CharRange.java 471626 2006-11-06 04:02:09Z bayard $
31   */
32  public final class CharRange implements Serializable {
33  
34      /**
35       * Required for serialization support. Lang version 2.0. 
36       * 
37       * @see java.io.Serializable
38       */
39      private static final long serialVersionUID = 8270183163158333422L;
40      
41      /** The first character, inclusive, in the range. */
42      private final char start;
43      /** The last character, inclusive, in the range. */
44      private final char end;
45      /** True if the range is everything except the characters specified. */
46      private final boolean negated;
47      
48      /** Cached toString. */
49      private transient String iToString;
50  
51      //-----------------------------------------------------------------------
52      /**
53       * <p>Constructs a <code>CharRange</code> over a single character.</p>
54       *
55       * @param ch  only character in this range
56       */
57      public CharRange(char ch) {
58          this(ch, ch, false);
59      }
60  
61      /**
62       * <p>Constructs a <code>CharRange</code> over a single character,
63       * optionally negating the range.</p>
64       *
65       * <p>A negated range includes everything except the specified char.</p>
66       *
67       * @param ch  only character in this range
68       * @param negated  true to express everything except the range
69       */
70      public CharRange(char ch, boolean negated) {
71          this(ch, ch, negated);
72      }
73  
74      /**
75       * <p>Constructs a <code>CharRange</code> over a set of characters.</p>
76       *
77       * @param start  first character, inclusive, in this range
78       * @param end  last character, inclusive, in this range
79       */
80      public CharRange(char start, char end) {
81          this(start, end, false);
82      }
83  
84      /**
85       * <p>Constructs a <code>CharRange</code> over a set of characters,
86       * optionally negating the range.</p>
87       *
88       * <p>A negated range includes everything except that defined by the
89       * start and end characters.</p>
90       * 
91       * <p>If start and end are in the wrong order, they are reversed.
92       * Thus <code>a-e</code> is the same as <code>e-a</code>.</p>
93       *
94       * @param start  first character, inclusive, in this range
95       * @param end  last character, inclusive, in this range
96       * @param negated  true to express everything except the range
97       */
98      public CharRange(char start, char end, boolean negated) {
99          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 }