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