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}