Coverage Report - org.apache.commons.lang3.CharRange
 
Classes in this File Line Coverage Branch Coverage Complexity
CharRange
100%
45/45
98%
49/50
2,842
CharRange$1
N/A
N/A
2,842
CharRange$CharacterIterator
96%
30/31
100%
18/18
2,842
 
 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.lang3;
 18  
 
 19  
 import java.io.Serializable;
 20  
 import java.util.Iterator;
 21  
 import java.util.NoSuchElementException;
 22  
 
 23  
 /**
 24  
  * <p>A contiguous range of characters, optionally negated.</p>
 25  
  * 
 26  
  * <p>Instances are immutable.</p>
 27  
  *
 28  
  * <p>#ThreadSafe#</p>
 29  
  * @since 1.0
 30  
  * @version $Id: CharRange.java 1436770 2013-01-22 07:09:45Z ggregory $
 31  
  */
 32  
 // TODO: This is no longer public and will be removed later as CharSet is moved 
 33  
 // to depend on Range.
 34  131100
 final class CharRange implements Iterable<Character>, Serializable {
 35  
 
 36  
     /**
 37  
      * Required for serialization support. Lang version 2.0. 
 38  
      * 
 39  
      * @see java.io.Serializable
 40  
      */
 41  
     private static final long serialVersionUID = 8270183163158333422L;
 42  
     
 43  
     /** The first character, inclusive, in the range. */
 44  
     private final char start;
 45  
     /** The last character, inclusive, in the range. */
 46  
     private final char end;
 47  
     /** True if the range is everything except the characters specified. */
 48  
     private final boolean negated;
 49  
     
 50  
     /** Cached toString. */
 51  
     private transient String iToString;
 52  
 
 53  
     /**
 54  
      * <p>Constructs a {@code CharRange} over a set of characters,
 55  
      * optionally negating the range.</p>
 56  
      *
 57  
      * <p>A negated range includes everything except that defined by the
 58  
      * start and end characters.</p>
 59  
      * 
 60  
      * <p>If start and end are in the wrong order, they are reversed.
 61  
      * Thus {@code a-e} is the same as {@code e-a}.</p>
 62  
      *
 63  
      * @param start  first character, inclusive, in this range
 64  
      * @param end  last character, inclusive, in this range
 65  
      * @param negated  true to express everything except the range
 66  
      */
 67  
     private CharRange(char start, char end, final boolean negated) {
 68  290
         super();
 69  290
         if (start > end) {
 70  19
             final char temp = start;
 71  19
             start = end;
 72  19
             end = temp;
 73  
         }
 74  
         
 75  290
         this.start = start;
 76  290
         this.end = end;
 77  290
         this.negated = negated;
 78  290
     }
 79  
 
 80  
     /**
 81  
      * <p>Constructs a {@code CharRange} over a single character.</p>
 82  
      *
 83  
      * @param ch  only character in this range
 84  
      * @return the new CharRange object
 85  
      * @see CharRange#CharRange(char, char, boolean)
 86  
      * @since 2.5
 87  
      */
 88  
     public static CharRange is(final char ch) {
 89  142
         return new CharRange(ch, ch, false);
 90  
     }
 91  
 
 92  
     /**
 93  
      * <p>Constructs a negated {@code CharRange} over a single character.</p>
 94  
      *
 95  
      * @param ch  only character in this range
 96  
      * @return the new CharRange object
 97  
      * @see CharRange#CharRange(char, char, boolean)
 98  
      * @since 2.5
 99  
      */
 100  
     public static CharRange isNot(final char ch) {
 101  30
         return new CharRange(ch, ch, true);
 102  
     }
 103  
 
 104  
     /**
 105  
      * <p>Constructs a {@code CharRange} over a set of characters.</p>
 106  
      *
 107  
      * @param start  first character, inclusive, in this range
 108  
      * @param end  last character, inclusive, in this range
 109  
      * @return the new CharRange object
 110  
      * @see CharRange#CharRange(char, char, boolean)
 111  
      * @since 2.5
 112  
      */
 113  
     public static CharRange isIn(final char start, final char end) {
 114  85
         return new CharRange(start, end, false);
 115  
     }
 116  
 
 117  
     /**
 118  
      * <p>Constructs a negated {@code CharRange} over a set of characters.</p>
 119  
      *
 120  
      * @param start  first character, inclusive, in this range
 121  
      * @param end  last character, inclusive, in this range
 122  
      * @return the new CharRange object
 123  
      * @see CharRange#CharRange(char, char, boolean)
 124  
      * @since 2.5
 125  
      */
 126  
     public static CharRange isNotIn(final char start, final char end) {
 127  33
         return new CharRange(start, end, true);
 128  
     }
 129  
 
 130  
     // Accessors
 131  
     //-----------------------------------------------------------------------
 132  
     /**
 133  
      * <p>Gets the start character for this character range.</p>
 134  
      * 
 135  
      * @return the start char (inclusive)
 136  
      */
 137  
     public char getStart() {
 138  8
         return this.start;
 139  
     }
 140  
 
 141  
     /**
 142  
      * <p>Gets the end character for this character range.</p>
 143  
      * 
 144  
      * @return the end char (inclusive)
 145  
      */
 146  
     public char getEnd() {
 147  8
         return this.end;
 148  
     }
 149  
 
 150  
     /**
 151  
      * <p>Is this {@code CharRange} negated.</p>
 152  
      * 
 153  
      * <p>A negated range includes everything except that defined by the
 154  
      * start and end characters.</p>
 155  
      *
 156  
      * @return {@code true} if negated
 157  
      */
 158  
     public boolean isNegated() {
 159  22
         return negated;
 160  
     }
 161  
 
 162  
     // Contains
 163  
     //-----------------------------------------------------------------------
 164  
     /**
 165  
      * <p>Is the character specified contained in this range.</p>
 166  
      *
 167  
      * @param ch  the character to check
 168  
      * @return {@code true} if this range contains the input character
 169  
      */
 170  
     public boolean contains(final char ch) {
 171  315
         return (ch >= start && ch <= end) != negated;
 172  
     }
 173  
 
 174  
     /**
 175  
      * <p>Are all the characters of the passed in range contained in
 176  
      * this range.</p>
 177  
      *
 178  
      * @param range  the range to check against
 179  
      * @return {@code true} if this range entirely contains the input range
 180  
      * @throws IllegalArgumentException if {@code null} input
 181  
      */
 182  
     public boolean contains(final CharRange range) {
 183  70
         if (range == null) {
 184  1
             throw new IllegalArgumentException("The Range must not be null");
 185  
         }
 186  69
         if (negated) {
 187  44
             if (range.negated) {
 188  17
                 return start >= range.start && end <= range.end;
 189  
             }
 190  27
             return range.end < start || range.start > end;
 191  
         }
 192  25
         if (range.negated) {
 193  6
             return start == 0 && end == Character.MAX_VALUE;
 194  
         }
 195  19
         return start <= range.start && end >= range.end;
 196  
     }
 197  
 
 198  
     // Basics
 199  
     //-----------------------------------------------------------------------
 200  
     /**
 201  
      * <p>Compares two CharRange objects, returning true if they represent
 202  
      * exactly the same range of characters defined in the same way.</p>
 203  
      * 
 204  
      * @param obj  the object to compare to
 205  
      * @return true if equal
 206  
      */
 207  
     @Override
 208  
     public boolean equals(final Object obj) {
 209  134
         if (obj == this) {
 210  3
             return true;
 211  
         }
 212  131
         if (obj instanceof CharRange == false) {
 213  1
             return false;
 214  
         }
 215  130
         final CharRange other = (CharRange) obj;
 216  130
         return start == other.start && end == other.end && negated == other.negated;
 217  
     }
 218  
 
 219  
     /**
 220  
      * <p>Gets a hashCode compatible with the equals method.</p>
 221  
      * 
 222  
      * @return a suitable hashCode
 223  
      */
 224  
     @Override
 225  
     public int hashCode() {
 226  227
         return 83 + start + 7 * end + (negated ? 1 : 0);
 227  
     }
 228  
     
 229  
     /**
 230  
      * <p>Gets a string representation of the character range.</p>
 231  
      * 
 232  
      * @return string representation of this range
 233  
      */
 234  
     @Override
 235  
     public String toString() {
 236  18
         if (iToString == null) {
 237  14
             final StringBuilder buf = new StringBuilder(4);
 238  14
             if (isNegated()) {
 239  6
                 buf.append('^');
 240  
             }
 241  14
             buf.append(start);
 242  14
             if (start != end) {
 243  8
                 buf.append('-');
 244  8
                 buf.append(end);
 245  
             }
 246  14
             iToString = buf.toString();
 247  
         }
 248  18
         return iToString;
 249  
     }
 250  
 
 251  
     // Expansions
 252  
     //-----------------------------------------------------------------------
 253  
     /**
 254  
      * <p>Returns an iterator which can be used to walk through the characters described by this range.</p>
 255  
      *
 256  
      * <p>#NotThreadSafe# the iterator is not thread-safe</p>
 257  
      * @return an iterator to the chars represented by this range
 258  
      * @since 2.5
 259  
      */
 260  
     @Override
 261  
     public Iterator<Character> iterator() {
 262  6
         return new CharacterIterator(this);
 263  
     }
 264  
 
 265  
     /**
 266  
      * Character {@link Iterator}.
 267  
      * <p>#NotThreadSafe#</p>
 268  
      */
 269  65551
     private static class CharacterIterator implements Iterator<Character> {
 270  
         /** The current character */
 271  
         private char current;
 272  
 
 273  
         private final CharRange range;
 274  
         private boolean hasNext;
 275  
 
 276  
         /**
 277  
          * Construct a new iterator for the character range.
 278  
          *
 279  
          * @param r The character range
 280  
          */
 281  6
         private CharacterIterator(final CharRange r) {
 282  6
             range = r;
 283  6
             hasNext = true;
 284  
 
 285  6
             if (range.negated) {
 286  4
                 if (range.start == 0) {
 287  2
                     if (range.end == Character.MAX_VALUE) {
 288  
                         // This range is an empty set
 289  1
                         hasNext = false;
 290  
                     } else {
 291  1
                         current = (char) (range.end + 1);
 292  
                     }
 293  
                 } else {
 294  2
                     current = 0;
 295  
                 }
 296  
             } else {
 297  2
                 current = range.start;
 298  
             }
 299  6
         }
 300  
 
 301  
         /**
 302  
          * Prepare the next character in the range.
 303  
          */
 304  
         private void prepareNext() {
 305  65542
             if (range.negated) {
 306  65537
                 if (current == Character.MAX_VALUE) {
 307  2
                     hasNext = false;
 308  65535
                 } else if (current + 1 == range.start) {
 309  2
                     if (range.end == Character.MAX_VALUE) {
 310  1
                         hasNext = false;
 311  
                     } else {
 312  1
                         current = (char) (range.end + 1);
 313  
                     }
 314  
                 } else {
 315  65533
                     current = (char) (current + 1);
 316  
                 }
 317  5
             } else if (current < range.end) {
 318  3
                 current = (char) (current + 1);
 319  
             } else {
 320  2
                 hasNext = false;
 321  
             }
 322  65542
         }
 323  
 
 324  
         /**
 325  
          * Has the iterator not reached the end character yet?
 326  
          *
 327  
          * @return {@code true} if the iterator has yet to reach the character date
 328  
          */
 329  
         @Override
 330  
         public boolean hasNext() {
 331  65546
             return hasNext;
 332  
         }
 333  
 
 334  
         /**
 335  
          * Return the next character in the iteration
 336  
          *
 337  
          * @return {@code Character} for the next character
 338  
          */
 339  
         @Override
 340  
         public Character next() {
 341  65545
             if (hasNext == false) {
 342  3
                 throw new NoSuchElementException();
 343  
             }
 344  65542
             final char cur = current;
 345  65542
             prepareNext();
 346  65542
             return Character.valueOf(cur);
 347  
         }
 348  
 
 349  
         /**
 350  
          * Always throws UnsupportedOperationException.
 351  
          *
 352  
          * @throws UnsupportedOperationException
 353  
          * @see java.util.Iterator#remove()
 354  
          */
 355  
         @Override
 356  
         public void remove() {
 357  0
             throw new UnsupportedOperationException();
 358  
         }
 359  
     }
 360  
 }