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

com.dua3.utility.logging.LogBuffer Maven / Gradle / Ivy

There is a newer version: 15.0.2
Show newest version
package com.dua3.utility.logging;

import com.dua3.utility.lang.RingBuffer;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * A log buffer class intended to provide a buffer for log messages to display in GUI applications.
 */
public class LogBuffer implements LogEntryHandler, Externalizable {

    /**
     * The default capacity.
     */
    public static final int DEFAULT_CAPACITY = 10_000;

    private final RingBuffer buffer;
    private final Collection listeners = new ArrayList<>();

    /**
     * Construct a new LogBuffer instance with default capacity.
     */
    public LogBuffer() {
        this(DEFAULT_CAPACITY);
    }

    /**
     * Construct a new LogBuffer instance.
     *
     * @param capacity the initial capacity
     */
    public LogBuffer(int capacity) {
        buffer = new RingBuffer<>(capacity);
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        Object[] entries = buffer.toArray();
        out.write(entries.length);
        for (Object entry : entries) {
            out.writeObject(entry);
        }
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        buffer.clear();
        int n = in.readInt();
        for (int i = 0; i < n; i++) {
            buffer.add((LogEntry) in.readObject());
        }
    }

    /**
     * Add LogBufferListener.
     *
     * @param listener the listener to add
     */
    public void addLogBufferListener(LogBufferListener listener) {
        listeners.add(listener);
    }

    /**
     * Remove LogBufferListener.
     *
     * @param listener the listener to remove
     */
    public void removeLogBufferListener(LogBufferListener listener) {
        listeners.remove(listener);
    }

    @Override
    public void handleEntry(LogEntry entry) {
        synchronized (listeners) {
            boolean replaced;
            synchronized (buffer) {
                replaced = !buffer.put(entry);
            }
            listeners.forEach(listener -> listener.entry(entry, replaced));
        }
    }

    /**
     * Clear the LogBuffer.
     * Synchronized method that clears the buffer and notifies all registered LogBufferListeners to clear their logs as well.
     */
    public void clear() {
        synchronized (listeners) {
            synchronized (buffer) {
                buffer.clear();
            }
            listeners.forEach(LogBufferListener::clear);
        }
    }

    /**
     * Converts the LogBuffer into an array of LogEntry objects.
     *
     * @return an array of LogEntry objects representing the contents of the LogBuffer
     */
    public LogEntry[] toArray() {
        synchronized (buffer) {
            return buffer.toArray(LogEntry[]::new);
        }
    }

    /**
     * Get the LogEntry at the specified index in the LogBuffer.
     *
     * @param i the index of the LogEntry to retrieve
     * @return the LogEntry at the specified index
     */
    public LogEntry get(int i) {
        synchronized (buffer) {
            return buffer.get(i);
        }
    }

    /**
     * Returns the size of the LogBuffer.
     *
     * @return the number of LogEntries in the LogBuffer.
     */
    public int size() {
        return buffer.size();
    }

    /**
     * Returns a view of the portion of this LogBuffer between the specified
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     *
     * @param fromIndex the index of the first LogEntry to be included in the
     *                  returned subList.
     * @param toIndex the index after the last LogEntry to be included in the
     *                returned subList.
     * @return a view of the specified range within this LogBuffer.
     * @throws IndexOutOfBoundsException if {@code fromIndex} or {@code toIndex} is
     *         out of range (fromIndex < 0 || toIndex > size() || fromIndex > toIndex).
     */
    public List subList(int fromIndex, int toIndex) {
        synchronized (buffer) {
            return new ArrayList<>(buffer.subList(fromIndex, toIndex));
        }
    }

    /**
     * Appends all LogEntries in this LogBuffer to the specified Appendable.
     *
     * @param app the Appendable to which the LogEntries will be appended
     * @throws IOException if an I/O error occurs while appending the LogEntries
     */
    public void appendTo(Appendable app) throws IOException {
        for (LogEntry entry : toArray()) {
            app.append(entry.toString()).append("\n");
        }
    }

    /**
     * Interface for Listeners on changes of a {@link LogBuffer} instance's contents.
     */
    public interface LogBufferListener {
        /**
         * Called when an entry was added to the buffer.
         *
         * @param entry    the added entry
         * @param replaced true, if the buffer's capacity was reached and another entry was removed to make space for
         *                 the new one
         */
        default void entry(LogEntry entry, boolean replaced) {
            entries(Collections.singleton(entry), replaced ? 1 : 0);
        }

        /**
         * Called when multiple entries have been added in a batch.
         *
         * @param entries  the added entries
         * @param replaced the number of replaced entries as described in {@link #entry(LogEntry, boolean)}
         */
        void entries(Collection entries, int replaced);

        /**
         * Called after the buffer has been cleared.
         */
        void clear();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy