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