All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.lib.commons.collections.CompactLines Maven / Gradle / Ivy

There is a newer version: 2024.7.2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.conqat.lib.commons.collections;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.BitSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.conqat.lib.commons.filesystem.FileSystemUtils;
import org.conqat.lib.commons.region.LineBasedRegion;
import org.conqat.lib.commons.string.StringUtils;
import org.conqat.lib.commons.test.IndexValueClass;

/**
 * A compact, serializable representation of line numbers using a BitSet. This class is designed to
 * efficiently store and manipulate sets of line numbers, which is particularly useful for tracking
 * coverage information, regions of a text, or any scenario where line-based data needs to be
 * compactly managed.
 * 

* Instances of this class can be created empty, from a collection of integers, or from a string * representation of line number ranges. It supports basic set operations such as addition, removal, * intersection, and union, as well as specialized operations like checking if any line number * within a range or specific region is present. *

* This class also implements {@link Iterable}, allowing for easy iteration over all stored line * numbers. * * @see BitSet */ @IndexValueClass(containedInBackup = true) public class CompactLines implements Serializable, Iterable { private static final long serialVersionUID = 1L; private BitSet bitSet = new BitSet(); public CompactLines() { } public CompactLines(Iterable lines) { for (Integer line : lines) { bitSet.set(line); } } public CompactLines(int... lines) { for (Integer line : lines) { bitSet.set(line); } } /** Returns the number of line numbers in this set. */ public int size() { return bitSet.cardinality(); } /** * Checks if this set of line numbers is empty. * * @return {@code true} if there are no line numbers in this set, {@code false} otherwise. */ public boolean isEmpty() { return bitSet.isEmpty(); } /** * Adds all line numbers from another {@code CompactLines} instance to this one. */ public void addAll(CompactLines lines) { bitSet.or(lines.bitSet); } /** * Checks if a specific line number is present in this set. * * @param line * The line number (1-based) * @return {@code true} if the line number is present, {@code false} otherwise. */ public boolean contains(int line) { return bitSet.get(line); } /** * Checks if any line number within a specified range is present in this set. * * @param start * the start of the range (inclusive, 1-based). * @param end * the end of the range (inclusive, 1-based). * @return {@code true} if any line number within the range is present, {@code false} otherwise. */ public boolean containsAny(int start, int end) { int nextSetBit = bitSet.nextSetBit(start); return nextSetBit != -1 && nextSetBit <= end; } /** * Checks if any line number within a specified range is present in this set. * * @see #containsAny(int, int) */ public boolean containsAny(LineBasedRegion region) { return containsAny(region.getStart(), region.getEnd()); } /** * Checks if this set contains all the line numbers specified in an iterable collection. * * @return {@code true} if every line number in the collection is contained in this set, * {@code false} otherwise. */ public boolean containsAll(Iterable lines) { for (Integer line : lines) { if (!bitSet.get(line)) { return false; } } return true; } /** * Adds a specific line number to this set. * * @param line * The line number (1-based) */ public void add(int line) { bitSet.set(line); } /** * Adds a range of line numbers to this set. * * @param startLine * the starting line number of the range to add (inclusive, 1-based) * @param endLine * the ending line number of the range to add (inclusive, 1-based) */ public void addRange(int startLine, int endLine) { bitSet.set(startLine, endLine + 1); } /** Removes a specific line number from this set. */ public void remove(int line) { bitSet.clear(line); } /** * Removes all line numbers that are present in another {@code CompactLines} instance from this one. */ public void removeAll(CompactLines lines) { bitSet.andNot(lines.bitSet); } /** Clears all line numbers from this set. */ public void clear() { bitSet.clear(); } /** * Retains only the line numbers that are present in both this and another {@code CompactLines} * instance. This basically builds the intersection set between both. */ public void retainAll(CompactLines lines) { bitSet.and(lines.bitSet); } /** * Creates a new {@link CompactLines} object with the intersection of this and the other lines. */ public CompactLines intersection(CompactLines other) { CompactLines intersection = new CompactLines(this); intersection.retainAll(other); return intersection; } /** * Checks if there is any overlap between the line numbers in this and another {@code CompactLines} * instance. * * @return {@code true} if there is at least one common line number, {@code false} otherwise. */ public boolean intersects(CompactLines lines) { return bitSet.intersects(lines.bitSet); } @Override public String toString() { return StringUtils.concat(this, ","); } /** * Gets the highest line number contained in this set or empty if there are no line numbers * contained. */ public Optional getHighestLineNumber() { if (bitSet.isEmpty()) { return Optional.empty(); } return Optional.of(bitSet.previousSetBit(bitSet.length() - 1)); } private void readObject(ObjectInputStream inputStream) throws IOException { byte[] bytes = FileSystemUtils.readStreamBinary(inputStream); bitSet = BitSet.valueOf(bytes); } private void writeObject(ObjectOutputStream outputStream) throws IOException { byte[] bytes = bitSet.toByteArray(); outputStream.write(bytes); } @NonNull @Override public Iterator iterator() { return new Iterator<>() { private int currentIndex = -1; @Override public boolean hasNext() { int nextIndex = bitSet.nextSetBit(currentIndex + 1); return nextIndex != -1; } @Override public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } currentIndex = bitSet.nextSetBit(currentIndex + 1); return currentIndex; } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy