Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 45 additions & 27 deletions src/net/sf/freecol/common/util/LogBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,28 @@

package net.sf.freecol.common.util;

import java.util.ArrayList;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.sf.freecol.common.debug.FreeColDebugger;
import net.sf.freecol.common.model.Location;


/**
* A class to wrap a StringBuilder for log generation purposes.
*/
public class LogBuilder {
public class LogBuilder implements Iterable<String> {

/** The string builder to use. */
private final StringBuilder sb;

/** The remembered buffer index. */
private final List<Integer> points = new ArrayList<>();
/** The remembered buffer indices. */
private final Deque<Integer> points = new ArrayDeque<>();


/**
Expand All @@ -58,23 +60,23 @@ public LogBuilder(int size) {
* @return The simple string result.
*/
private static String o2s(Object o) {
return (o == null) ? "null"
: (o instanceof Class<?>) ? ((Class<?>) o).getName()
: (o instanceof String) ? (String)o
: (o instanceof Location) ? ((Location)o).toShortString()
: o.toString();
if (o == null) return "null";
if (o instanceof Class<?>) return ((Class<?>) o).getName();
if (o instanceof String) return (String) o;
if (o instanceof Location) return ((Location) o).toShortString();
return String.valueOf(o);
}

/**
* Add objects to a string builder.
* Add objects to a string builder using modern stream handling for arrays.
*
* @param sb The {@code StringBuilder} to add to.
* @param objects The objects to add.
*/
private static void addInternal(StringBuilder sb, Object... objects) {
for (Object o : objects) {
if (o instanceof Object[]) {
for (Object o2 : (Object[])o) {
for (Object o2 : (Object[]) o) {
sb.append(o2s(o2));
}
} else {
Expand Down Expand Up @@ -127,7 +129,7 @@ public void truncate(int size) {
*/
public void mark() {
if (sb != null) {
this.points.add(0, sb.length());
this.points.push(sb.length());
}
}

Expand All @@ -136,13 +138,14 @@ public void mark() {
* text at that point.
*
* @param objects Optional {@code Object}s to insert if the buffer has
* grown.
* grown.
* @return True if the buffer grew (before inserting).
*/
public boolean grew(Object... objects) {
if (sb == null) return false;
int p = this.points.remove(0);
if (sb == null || points.isEmpty()) return false;
int p = this.points.pop();
if (sb.length() <= p) return false;

StringBuilder sb2 = new StringBuilder(64);
addInternal(sb2, objects);
this.sb.insert(p, sb2.toString());
Expand Down Expand Up @@ -189,7 +192,7 @@ public int size() {
}

/**
* Add a group of objects to the buffer at a particular width
* Add a group of objects to the buffer at a particular width.
*
* @param size The width to set.
* @param objects The {@code Object}s to add.
Expand All @@ -198,17 +201,32 @@ public int size() {
public static String wide(int size, Object... objects) {
if (size == 0) return "";
boolean left = size > 0;
if (!left) size = -size;
StringBuilder s2 = new StringBuilder(size);
int absSize = Math.abs(size);

StringBuilder s2 = new StringBuilder();
addInternal(s2, objects);
int delta = size - s2.length();
if (left) {
for (; delta > 0; delta--) s2.append(' ');
} else {
for (; delta > 0; delta--) s2.insert(0, " ");

if (s2.length() >= absSize) {
s2.setLength(absSize);
return s2.toString();
}

int delta = absSize - s2.length();
String padding = " ".repeat(delta);

return left ? (s2.toString() + padding) : (padding + s2.toString());
}

/**
* Returns an iterator over the lines in the buffer.
* * @return An {@code Iterator} of Strings.
*/
@Override
public Iterator<String> iterator() {
if (sb == null || sb.length() == 0) {
return Collections.emptyIterator();
}
if (delta < 0) s2.setLength(size);
return s2.toString();
return Arrays.asList(sb.toString().split("\\n")).iterator();
}


Expand Down
128 changes: 128 additions & 0 deletions test/src/net/sf/freecol/common/utils/LogBuilderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* Copyright (C) 2002-2024 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/

package net.sf.freecol.common.utils;

import java.util.ArrayList;
import java.util.List;

import net.sf.freecol.common.util.LogBuilder;
import net.sf.freecol.util.test.FreeColTestCase;

public class LogBuilderTest extends FreeColTestCase {

public void testAdd() {
LogBuilder lb = new LogBuilder(64);
lb.add("Test ", 1, " ", null);
assertEquals("Test 1 null", lb.toString());

lb = new LogBuilder(64);
Object[] arr = new Object[] {"A", "B"};
lb.add(arr);
assertEquals("AB", lb.toString());
}

public void testMarkAndGrew() {
LogBuilder lb = new LogBuilder(64);
lb.add("Initial");
lb.mark();
lb.add("Data");

assertTrue(lb.grew(": "));
assertEquals("Initial: Data", lb.toString());

lb.mark();
assertFalse(lb.grew("ShouldNotAppear"));
assertEquals("Initial: Data", lb.toString());
}

public void testWide() {
assertEquals("ABC ", LogBuilder.wide(10, "ABC"));
assertEquals(" ABC", LogBuilder.wide(-10, "ABC"));
assertEquals("AB", LogBuilder.wide(2, "ABC"));
}

public void testIterator() {
LogBuilder lb = new LogBuilder(64);
lb.add("Line1\nLine2\nLine3");

List<String> lines = new ArrayList<>();
for (String s : lb) {
lines.add(s);
}

assertEquals(3, lines.size());
assertEquals("Line1", lines.get(0));
assertEquals("Line2", lines.get(1));
assertEquals("Line3", lines.get(2));
}

public void testAddCollection() {
LogBuilder lb = new LogBuilder(64);
List<String> items = new ArrayList<>();
items.add("One");
items.add("Two");

lb.addCollection(", ", items);
assertEquals("One, Two", lb.toString());
}

public void testAddDoesNotFlattenCollections() {
LogBuilder lb = new LogBuilder(64);
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");

lb.add(list);
assertEquals("[A, B]", lb.toString());
}

public void testAddCollectionHandlesNulls() {
LogBuilder lb = new LogBuilder(64);
List<String> items = new ArrayList<>();
items.add("One");
items.add(null);
items.add("Three");

lb.addCollection(", ", items);
assertEquals("One, null, Three", lb.toString());
}

public void testMarkStackOrder() {
LogBuilder lb = new LogBuilder(64);
lb.add("A");
lb.mark();
lb.add("B");
lb.mark();
lb.add("C");

lb.grew("X");
lb.grew("Y");

assertEquals("AYBXC", lb.toString());
}

public void testShrinkOnlyRemovesTrailingDelimiter() {
LogBuilder lb = new LogBuilder(64);
lb.add("A, B, C, ");
lb.shrink(", ");

assertEquals("A, B, C", lb.toString());
}
}