    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 */
    017package org.apache.commons.lang3.util;
    018
    019import java.io.Serializable;
    020import java.util.BitSet;
    021import java.util.Objects;
    022import java.util.stream.IntStream;
    023
    024/**
    025 * A fluent {@link BitSet} with additional operations.
    026 * <p>
    027 * Originally from Apache Commons VFS with more added to act as a fluent replacement for {@link java.util.BitSet}.
    028 * </p>
    029 * @since 3.13.0
    030 */
    031public final class FluentBitSet implements Cloneable, Serializable {
    032
    033    private static final long serialVersionUID = 1L;
    034
    035    /**
    036     * Working BitSet.
    037     */
    038    private final BitSet bitSet;
    039
    040    /**
    041     * Creates a new bit set. All bits are initially {@code false}.
    042     */
    043    public FluentBitSet() {
    044        this(new BitSet());
    045    }
    046
    047    /**
    048     * Creates a new instance for the given bit set.
    049     *
    050     * @param set The bit set to wrap.
    051     */
    052    public FluentBitSet(final BitSet set) {
    053        this.bitSet = Objects.requireNonNull(set, "set");
    054    }
    055
    056    /**
    057     * Creates a bit set whose initial size is large enough to explicitly represent bits with indices in the range {@code 0}
    058     * through {@code nbits-1}. All bits are initially {@code false}.
    059     *
    060     * @param nbits the initial size of the bit set.
    061     * @throws NegativeArraySizeException if the specified initial size is negative.
    062     */
    063    public FluentBitSet(final int nbits) {
    064        this(new BitSet(nbits));
    065    }
    066
    067    /**
    068     * Performs a logical <b>AND</b> of this target bit set with the argument bit set. This bit set is modified so that each
    069     * bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the
    070     * corresponding bit in the bit set argument also had the value {@code true}.
    071     *
    072     * @param set a bit set.
    073     * @return this.
    074     */
    075    public FluentBitSet and(final BitSet set) {
    076        bitSet.and(set);
    077        return this;
    078    }
    079
    080    /**
    081     * Performs a logical <b>AND</b> of this target bit set with the argument bit set. This bit set is modified so that each
    082     * bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the
    083     * corresponding bit in the bit set argument also had the value {@code true}.
    084     *
    085     * @param set a bit set.
    086     * @return this.
    087     */
    088    public FluentBitSet and(final FluentBitSet set) {
    089        bitSet.and(set.bitSet);
    090        return this;
    091    }
    092
    093    /**
    094     * Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}.
    095     *
    096     * @param set the {@link BitSet} with which to mask this {@link BitSet}.
    097     * @return this.
    098     */
    099    public FluentBitSet andNot(final BitSet set) {
    100        bitSet.andNot(set);
    101        return this;
    102    }
    103
    104    /**
    105     * Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}.
    106     *
    107     * @param set the {@link BitSet} with which to mask this {@link BitSet}.
    108     * @return this.
    109     */
    110    public FluentBitSet andNot(final FluentBitSet set) {
    111        this.bitSet.andNot(set.bitSet);
    112        return this;
    113    }
    114
    115    /**
    116     * Gets the wrapped bit set.
    117     *
    118     * @return the wrapped bit set.
    119     */
    120    public BitSet bitSet() {
    121        return bitSet;
    122    }
    123
    124    /**
    125     * Returns the number of bits set to {@code true} in this {@link BitSet}.
    126     *
    127     * @return the number of bits set to {@code true} in this {@link BitSet}.
    128     */
    129    public int cardinality() {
    130        return bitSet.cardinality();
    131    }
    132
    133    /**
    134     * Sets all of the bits in this BitSet to {@code false}.
    135     *
    136     * @return this.
    137     */
    138    public FluentBitSet clear() {
    139        bitSet.clear();
    140        return this;
    141    }
    142
    143    /**
    144     * Sets the bits specified by the indexes to {@code false}.
    145     *
    146     * @param bitIndexArray the index of the bit to be cleared.
    147     * @throws IndexOutOfBoundsException if the specified index is negative.
    148     * @return this.
    149     */
    150    public FluentBitSet clear(final int... bitIndexArray) {
    151        for (final int e : bitIndexArray) {
    152            this.bitSet.clear(e);
    153        }
    154        return this;
    155    }
    156
    157    /**
    158     * Sets the bit specified by the index to {@code false}.
    159     *
    160     * @param bitIndex the index of the bit to be cleared.
    161     * @throws IndexOutOfBoundsException if the specified index is negative.
    162     * @return this.
    163     */
    164    public FluentBitSet clear(final int bitIndex) {
    165        bitSet.clear(bitIndex);
    166        return this;
    167    }
    168
    169    /**
    170     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
    171     * {@code false}.
    172     *
    173     * @param fromIndex index of the first bit to be cleared.
    174     * @param toIndex index after the last bit to be cleared.
    175     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
    176     *         {@code fromIndex} is larger than {@code toIndex}.
    177     * @return this.
    178     */
    179    public FluentBitSet clear(final int fromIndex, final int toIndex) {
    180        bitSet.clear(fromIndex, toIndex);
    181        return this;
    182    }
    183
    184    /**
    185     * Cloning this {@link BitSet} produces a new {@link BitSet} that is equal to it. The clone of the bit set is another
    186     * bit set that has exactly the same bits set to {@code true} as this bit set.
    187     *
    188     * @return a clone of this bit set
    189     * @see #size()
    190     */
    191    @Override
    192    public Object clone() {
    193        return new FluentBitSet((BitSet) bitSet.clone());
    194    }
    195
    196    @Override
    197    public boolean equals(final Object obj) {
    198        if (this == obj) {
    199            return true;
    200        }
    201        if (!(obj instanceof FluentBitSet)) {
    202            return false;
    203        }
    204        final FluentBitSet other = (FluentBitSet) obj;
    205        return Objects.equals(bitSet, other.bitSet);
    206    }
    207
    208    /**
    209     * Sets the bit at the specified index to the complement of its current value.
    210     *
    211     * @param bitIndex the index of the bit to flip.
    212     * @throws IndexOutOfBoundsException if the specified index is negative.
    213     * @return this.
    214     */
    215    public FluentBitSet flip(final int bitIndex) {
    216        bitSet.flip(bitIndex);
    217        return this;
    218    }
    219
    220    /**
    221     * Sets each bit from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the
    222     * complement of its current value.
    223     *
    224     * @param fromIndex index of the first bit to flip.
    225     * @param toIndex index after the last bit to flip.
    226     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
    227     *         {@code fromIndex} is larger than {@code toIndex}.
    228     * @return this.
    229     */
    230    public FluentBitSet flip(final int fromIndex, final int toIndex) {
    231        bitSet.flip(fromIndex, toIndex);
    232        return this;
    233    }
    234
    235    /**
    236     * Returns the value of the bit with the specified index. The value is {@code true} if the bit with the index
    237     * {@code bitIndex} is currently set in this {@link BitSet}; otherwise, the result is {@code false}.
    238     *
    239     * @param bitIndex the bit index.
    240     * @return the value of the bit with the specified index.
    241     * @throws IndexOutOfBoundsException if the specified index is negative.
    242     */
    243    public boolean get(final int bitIndex) {
    244        return bitSet.get(bitIndex);
    245    }
    246
    247    /**
    248     * Returns a new {@link BitSet} composed of bits from this {@link BitSet} from {@code fromIndex} (inclusive) to
    249     * {@code toIndex} (exclusive).
    250     *
    251     * @param fromIndex index of the first bit to include.
    252     * @param toIndex index after the last bit to include.
    253     * @return a new {@link BitSet} from a range of this {@link BitSet}.
    254     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
    255     *         {@code fromIndex} is larger than {@code toIndex}.
    256     */
    257    public FluentBitSet get(final int fromIndex, final int toIndex) {
    258        return new FluentBitSet(bitSet.get(fromIndex, toIndex));
    259    }
    260
    261    @Override
    262    public int hashCode() {
    263        return bitSet.hashCode();
    264    }
    265
    266    /**
    267     * Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in
    268     * this {@link BitSet}.
    269     *
    270     * @param set {@link BitSet} to intersect with.
    271     * @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}.
    272     */
    273    public boolean intersects(final BitSet set) {
    274        return bitSet.intersects(set);
    275    }
    276
    277    /**
    278     * Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in
    279     * this {@link BitSet}.
    280     *
    281     * @param set {@link BitSet} to intersect with.
    282     * @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}.
    283     */
    284    public boolean intersects(final FluentBitSet set) {
    285        return bitSet.intersects(set.bitSet);
    286    }
    287
    288    /**
    289     * Returns true if this {@link BitSet} contains no bits that are set to {@code true}.
    290     *
    291     * @return boolean indicating whether this {@link BitSet} is empty.
    292     */
    293    public boolean isEmpty() {
    294        return bitSet.isEmpty();
    295    }
    296
    297    /**
    298     * Returns the "logical size" of this {@link BitSet}: the index of the highest set bit in the {@link BitSet} plus one.
    299     * Returns zero if the {@link BitSet} contains no set bits.
    300     *
    301     * @return the logical size of this {@link BitSet}.
    302     */
    303    public int length() {
    304        return bitSet.length();
    305    }
    306
    307    /**
    308     * Returns the index of the first bit that is set to {@code false} that occurs on or after the specified starting index.
    309     *
    310     * @param fromIndex the index to start checking from (inclusive).
    311     * @return the index of the next clear bit.
    312     * @throws IndexOutOfBoundsException if the specified index is negative.
    313     */
    314    public int nextClearBit(final int fromIndex) {
    315        return bitSet.nextClearBit(fromIndex);
    316    }
    317
    318    /**
    319     * Returns the index of the first bit that is set to {@code true} that occurs on or after the specified starting index.
    320     * If no such bit exists then {@code -1} is returned.
    321     * <p>
    322     * To iterate over the {@code true} bits in a {@link BitSet}, use the following loop:
    323     * </p>
    324     *
    325     * <pre>
    326     * {@code
    327     * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
    328     *     // operate on index i here
    329     *     if (i == Integer.MAX_VALUE) {
    330     *         break; // or (i+1) would overflow
    331     *     }
    332     * }}
    333     * </pre>
    334     *
    335     * @param fromIndex the index to start checking from (inclusive).
    336     * @return the index of the next set bit, or {@code -1} if there is no such bit.
    337     * @throws IndexOutOfBoundsException if the specified index is negative.
    338     */
    339    public int nextSetBit(final int fromIndex) {
    340        return bitSet.nextSetBit(fromIndex);
    341    }
    342
    343    /**
    344     * Performs a logical <b>OR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
    345     * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
    346     * the bit set argument has the value {@code true}.
    347     *
    348     * @param set a bit set.
    349     * @return this.
    350     */
    351    public FluentBitSet or(final BitSet set) {
    352        bitSet.or(set);
    353        return this;
    354    }
    355
    356    /**
    357     * Performs a logical <b>OR</b> of this bit set with the bit set arguments. This bit set is modified so that a bit in it
    358     * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
    359     * the bit set argument has the value {@code true}.
    360     *
    361     * @param set a bit set.
    362     * @return this.
    363     */
    364    public FluentBitSet or(final FluentBitSet... set) {
    365        for (final FluentBitSet e : set) {
    366            this.bitSet.or(e.bitSet);
    367        }
    368        return this;
    369    }
    370
    371    /**
    372     * Performs a logical <b>OR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
    373     * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
    374     * the bit set argument has the value {@code true}.
    375     *
    376     * @param set a bit set.
    377     * @return this.
    378     */
    379    public FluentBitSet or(final FluentBitSet set) {
    380        this.bitSet.or(set.bitSet);
    381        return this;
    382    }
    383
    384    /**
    385     * Returns the index of the nearest bit that is set to {@code false} that occurs on or before the specified starting
    386     * index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned.
    387     *
    388     * @param fromIndex the index to start checking from (inclusive).
    389     * @return the index of the previous clear bit, or {@code -1} if there is no such bit.
    390     * @throws IndexOutOfBoundsException if the specified index is less than {@code -1}.
    391     */
    392    public int previousClearBit(final int fromIndex) {
    393        return bitSet.previousClearBit(fromIndex);
    394    }
    395
    396    /**
    397     * Returns the index of the nearest bit that is set to {@code true} that occurs on or before the specified starting
    398     * index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned.
    399     *
    400     * <p>
    401     * To iterate over the {@code true} bits in a {@link BitSet}, use the following loop:
    402     *
    403     * <pre>
    404     *  {@code
    405     * for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
    406     *     // operate on index i here
    407     * }}
    408     * </pre>
    409     *
    410     * @param fromIndex the index to start checking from (inclusive)
    411     * @return the index of the previous set bit, or {@code -1} if there is no such bit
    412     * @throws IndexOutOfBoundsException if the specified index is less than {@code -1}
    413     */
    414    public int previousSetBit(final int fromIndex) {
    415        return bitSet.previousSetBit(fromIndex);
    416    }
    417
    418    /**
    419     * Sets the bit at the specified indexes to {@code true}.
    420     *
    421     * @param bitIndexArray a bit index array.
    422     * @throws IndexOutOfBoundsException if the specified index is negative.
    423     * @return this.
    424     */
    425    public FluentBitSet set(final int... bitIndexArray) {
    426        for (final int e : bitIndexArray) {
    427            bitSet.set(e);
    428        }
    429        return this;
    430    }
    431
    432    /**
    433     * Sets the bit at the specified index to {@code true}.
    434     *
    435     * @param bitIndex a bit index
    436     * @throws IndexOutOfBoundsException if the specified index is negative
    437     * @return this.
    438     */
    439    public FluentBitSet set(final int bitIndex) {
    440        bitSet.set(bitIndex);
    441        return this;
    442    }
    443
    444    /**
    445     * Sets the bit at the specified index to the specified value.
    446     *
    447     * @param bitIndex a bit index.
    448     * @param value a boolean value to set.
    449     * @throws IndexOutOfBoundsException if the specified index is negative.
    450     * @return this.
    451     */
    452    public FluentBitSet set(final int bitIndex, final boolean value) {
    453        bitSet.set(bitIndex, value);
    454        return this;
    455    }
    456
    457    /**
    458     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
    459     * {@code true}.
    460     *
    461     * @param fromIndex index of the first bit to be set.
    462     * @param toIndex index after the last bit to be set.
    463     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
    464     *         {@code fromIndex} is larger than {@code toIndex}.
    465     * @return this.
    466     */
    467    public FluentBitSet set(final int fromIndex, final int toIndex) {
    468        bitSet.set(fromIndex, toIndex);
    469        return this;
    470    }
    471
    472    /**
    473     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the
    474     * specified value.
    475     *
    476     * @param fromIndex index of the first bit to be set.
    477     * @param toIndex index after the last bit to be set.
    478     * @param value value to set the selected bits to.
    479     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
    480     *         {@code fromIndex} is larger than {@code toIndex}.
    481     * @return this.
    482     */
    483    public FluentBitSet set(final int fromIndex, final int toIndex, final boolean value) {
    484        bitSet.set(fromIndex, toIndex, value);
    485        return this;
    486    }
    487
    488    /**
    489     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
    490     * {@code true}.
    491     *
    492     * @param fromIndex index of the first bit to be set
    493     * @param toIndex index of the last bit to be set
    494     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
    495     *         {@code fromIndex} is larger than {@code toIndex}
    496     * @return this.
    497     */
    498    public FluentBitSet setInclusive(final int fromIndex, final int toIndex) {
    499        bitSet.set(fromIndex, toIndex + 1);
    500        return this;
    501    }
    502
    503    /**
    504     * Returns the number of bits of space actually in use by this {@link BitSet} to represent bit values. The maximum
    505     * element in the set is the size - 1st element.
    506     *
    507     * @return the number of bits currently in this bit set.
    508     */
    509    public int size() {
    510        return bitSet.size();
    511    }
    512
    513    /**
    514     * Returns a stream of indices for which this {@link BitSet} contains a bit in the set state. The indices are returned
    515     * in order, from lowest to highest. The size of the stream is the number of bits in the set state, equal to the value
    516     * returned by the {@link #cardinality()} method.
    517     *
    518     * <p>
    519     * The bit set must remain constant during the execution of the terminal stream operation. Otherwise, the result of the
    520     * terminal stream operation is undefined.
    521     * </p>
    522     *
    523     * @return a stream of integers representing set indices.
    524     * @since 1.8
    525     */
    526    public IntStream stream() {
    527        return bitSet.stream();
    528    }
    529
    530    /**
    531     * Returns a new byte array containing all the bits in this bit set.
    532     *
    533     * <p>
    534     * More precisely, if:
    535     * </p>
    536     * <ol>
    537     * <li>{@code byte[] bytes = s.toByteArray();}</li>
    538     * <li>then {@code bytes.length == (s.length()+7)/8} and</li>
    539     * <li>{@code s.get(n) == ((bytes[n/8] & (1<<(n%8))) != 0)}</li>
    540     * <li>for all {@code n < 8 * bytes.length}.</li>
    541     * </ol>
    542     *
    543     * @return a byte array containing a little-endian representation of all the bits in this bit set
    544     */
    545    public byte[] toByteArray() {
    546        return bitSet.toByteArray();
    547    }
    548
    549    /**
    550     * Returns a new byte array containing all the bits in this bit set.
    551     *
    552     * <p>
    553     * More precisely, if:
    554     * </p>
    555     * <ol>
    556     * <li>{@code long[] longs = s.toLongArray();}</li>
    557     * <li>then {@code longs.length == (s.length()+63)/64} and</li>
    558     * <li>{@code s.get(n) == ((longs[n/64] & (1L<<(n%64))) != 0)}</li>
    559     * <li>for all {@code n < 64 * longs.length}.</li>
    560     * </ol>
    561     *
    562     * @return a byte array containing a little-endian representation of all the bits in this bit set
    563     */
    564    public long[] toLongArray() {
    565        return bitSet.toLongArray();
    566    }
    567
    568    @Override
    569    public String toString() {
    570        return bitSet.toString();
    571    }
    572
    573    /**
    574     * Performs a logical <b>XOR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
    575     * has the value {@code true} if and only if one of the following statements holds:
    576     * <ul>
    577     * <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value
    578     * {@code false}.
    579     * <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value
    580     * {@code true}.
    581     * </ul>
    582     *
    583     * @param set a bit set
    584     * @return this.
    585     */
    586    public FluentBitSet xor(final BitSet set) {
    587        bitSet.xor(set);
    588        return this;
    589    }
    590
    591    /**
    592     * Performs a logical <b>XOR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
    593     * has the value {@code true} if and only if one of the following statements holds:
    594     * <ul>
    595     * <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value
    596     * {@code false}.
    597     * <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value
    598     * {@code true}.
    599     * </ul>
    600     *
    601     * @param set a bit set
    602     * @return this.
    603     */
    604    public FluentBitSet xor(final FluentBitSet set) {
    605        bitSet.xor(set.bitSet);
    606        return this;
    607    }
    608
    609}