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 }