This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new b2cc74dd77 Fix an almost never-ending loop when loging a warning with
a message too long for fitting on a line, and when that line has no place
(space, dash, etc.) where to split.
b2cc74dd77 is described below
commit b2cc74dd77628ce6d0cf25929b602390a071281c
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Jan 26 20:21:33 2024 +0100
Fix an almost never-ending loop when loging a warning with a message too
long for fitting on a line,
and when that line has no place (space, dash, etc.) where to split.
---
.../main/org/apache/sis/console/SIS.java | 14 ++-
.../apache/sis/xml/util/ExternalLinkHandler.java | 1 -
.../apache/sis/storage/event/StoreListeners.java | 1 +
.../main/org/apache/sis/io/LineAppender.java | 118 ++++++++++++++-------
.../apache/sis/util/logging/MonolineFormatter.java | 2 +-
.../test/org/apache/sis/io/AppenderTestCase.java | 4 +-
.../test/org/apache/sis/io/EchoAppendable.java | 2 +-
.../test/org/apache/sis/io/LeftMarginTest.java | 2 +-
.../test/org/apache/sis/io/LineAppenderTest.java | 4 +-
.../test/org/apache/sis/io/TableAppenderTest.java | 2 +-
.../org/apache/sis/io/TabulationExpansionTest.java | 4 +-
.../test/org/apache/sis/io/WordWrapTest.java | 33 ++++--
.../sis/io/WordWrapWithLineSeparatorTest.java | 2 +-
13 files changed, 133 insertions(+), 56 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
index 3b6a099470..26fb629a57 100644
--- a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
+++ b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
@@ -626,7 +626,7 @@ public final class SIS extends Static {
Transform() {super("transform");}
/**
- * Sets Coordinate Reference System of input data.
+ * Sets the Coordinate Reference System of input data.
*
* @param value the EPSG code, WKT or file from which to get the CRS.
* @return a new builder or {@code this}, for method call chaining.
@@ -636,7 +636,7 @@ public final class SIS extends Static {
}
/**
- * Sets Coordinate Reference System of output data.
+ * Sets the Coordinate Reference System of output data.
*
* @param value the EPSG code, WKT or file from which to get the CRS.
* @return a new builder or {@code this}, for method call chaining.
@@ -645,6 +645,16 @@ public final class SIS extends Static {
return set(Option.TARGET_CRS, value);
}
+ /**
+ * Sets the Coordinate Operation to use.
+ *
+ * @param value the EPSG code, WKT or file from which to get the
coordinate operation.
+ * @return a new builder or {@code this}, for method call chaining.
+ */
+ public Transform operation(Object value) {
+ return set(Option.OPERATION, value);
+ }
+
/**
* Sets the locale to use for the command output.
*
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
index 6bb7082bc7..9dbbdc9a22 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
@@ -185,7 +185,6 @@ public class ExternalLinkHandler {
* @param cause the exception that occurred while trying to process the
document.
*/
public static void warningOccured(final Object href, final Exception
cause) {
- Context.warningOccured(Context.current(), ReferenceResolver.class,
"resolve", cause, true);
Context.warningOccured(Context.current(), Level.WARNING,
ReferenceResolver.class, "resolve",
cause, Errors.class, Errors.Keys.CanNotRead_1,
href);
}
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
index a648272f76..c4f7bf04f7 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
@@ -626,6 +626,7 @@ public class StoreListeners implements Localized {
public <E extends StoreEvent> boolean fire(final Class<E> eventType, final
E event) {
ArgumentChecks.ensureNonNull("event", event);
ArgumentChecks.ensureNonNull("eventType", eventType);
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final Set<Class<? extends StoreEvent>> permittedEventTypes =
this.permittedEventTypes;
if (permittedEventTypes != null &&
!permittedEventTypes.contains(eventType)) {
throw illegalEventType(eventType);
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
index 899de1153e..a42e99072f 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
@@ -379,7 +379,6 @@ public class LineAppender extends Appender implements
Flushable {
*
* @throws IOException if an I/O error occurs.
*/
- @SuppressWarnings("fallthrough")
private void write(final int c) throws IOException {
/*
* If the character to write is a EOL sequence, then:
@@ -410,10 +409,10 @@ public class LineAppender extends Appender implements
Flushable {
/*
* If the character to write is a whitespace, then write any pending
characters from
* the buffer to the underlying appendable since we know that those
characters didn't
- * exceeded the line length limit.
+ * exceed the line length limit.
*
* We use `Character.isWhitespace(…)` instead of
`Character.isSpaceChar(…)` because
- * the former returns `true` tabulations (which we want), and returns
`false`
+ * the former returns `true` for tabulations (which we want), and
returns `false`
* for non-breaking spaces (which we also want).
*/
if (Character.isWhitespace(c)) {
@@ -460,48 +459,95 @@ public class LineAppender extends Appender implements
Flushable {
}
/*
* The remaining of this method is executed only if we exceeded the
maximal line length.
- * First, search for the hyphen character, if any. If we find one and
if it is preceeded
- * by a letter, split there. The "letter before" condition is a way to
avoid to split at
- * the minus sign of negative numbers like "-99", assuming that the
minus sign is preceeded
- * by a space. We cannot look at the character after since we may not
know it yet.
+ * First, search for a dash character (hyphen) for splitting the line
after it. If we do
+ * not find a dash character, as a fallback split on any non-letter or
digit characters
+ * except the punctuation starts.
*/
- if (++codePointCount > maximalLineLength) {
-searchHyp: for (int i=buffer.length(); i>0;) {
- final int b = buffer.codePointBefore(i);
- final int n = Character.charCount(b);
- switch (b) {
- case '-': {
- if (i>=n &&
!Character.isLetter(buffer.codePointBefore(i-n))) {
- break; // Continue searching
previous characters.
+ if (++codePointCount < maximalLineLength) {
+ return;
+ }
+ int splitAt = buffer.length(); // Where to separate the line
as two lines.
+ int fallback = splitAt; // Fallback to use if we could
not find a value for `splitAt`.
+ boolean hasFallback = false; // Whether the `fallback`
value has been defined.
+split: for (;;) {
+ if (splitAt <= 0) {
+ splitAt = fallback;
+ break;
+ }
+ int b = buffer.codePointBefore(splitAt);
+ int n = Character.charCount(b);
+ switch (Character.getType(b)) {
+ case Character.UPPERCASE_LETTER:
+ case Character.LOWERCASE_LETTER:
+ case Character.TITLECASE_LETTER:
+ case Character.MODIFIER_LETTER:
+ case Character.OTHER_LETTER:
+ case Character.DECIMAL_DIGIT_NUMBER:
+ case Character.INITIAL_QUOTE_PUNCTUATION:
+ case Character.START_PUNCTUATION: break; // Do
nothing (search another character).
+ case Character.PARAGRAPH_SEPARATOR:
+ case Character.SPACE_SEPARATOR:
+ case Character.LINE_SEPARATOR:
+ case Character.CONTROL: {
+ /*
+ * Split the line before a space (except no-break space)
and discard trailing spaces.
+ * The `isWhitespace(b)` check is necessary for excluding
the no-break spaces.
+ */
+ final int end = splitAt;
+ while (Character.isWhitespace(b)) {
+ if ((splitAt -= n) <= 0) break;
+ b = buffer.codePointBefore(splitAt);
+ n = Character.charCount(b);
+ }
+ if (splitAt == end) break; //
No-break space. Search another character.
+ buffer.delete(splitAt, end);
+ break split; // Split
here (before the space character).
+ }
+ /*
+ * Split the line after a dash character.
+ * The "letter before" condition is a way to avoid splitting
at the minus sign
+ * of negative numbers, assuming that the minus sign is
preceeded by a space.
+ * We cannot look at the character after because it may not be
in the buffer yet.
+ */
+ case Character.DASH_PUNCTUATION: {
+ if (b == '-') {
+ b = splitAt - n;
+ if (b > 0 &&
!Character.isLetter(buffer.codePointBefore(b))) {
+ break; // Continue the search in previous
characters.
}
- // fall through
}
- case Characters.HYPHEN:
- case Characters.SOFT_HYPHEN: {
- transfer(i);
- break searchHyp;
+ break split; // Split here (after the dash
character).
+ }
+ /*
+ * Soft hyphen are not in the dash category, so they need to
be checked here.
+ * Replace soft-hyphen by ordinary (visible) hyphen since the
hyphen is used.
+ */
+ case Character.FORMAT: {
+ if (b == Characters.SOFT_HYPHEN) {
+ buffer.setCharAt(splitAt - n, Characters.HYPHEN);
+ break split; // Split here (after the dash
character).
}
+ break; // Do nothing (search another
character).
}
- i -= n;
- }
- /*
- * At this point, all the remaining content of the buffer must
move on the next line.
- * Skip the leading whitespaces on the new line.
- */
- writeLineSeparator();
- final int length = buffer.length();
- for (int i=0; i<length;) {
- final int s = buffer.codePointAt(i);
- if (!Character.isWhitespace(s)) {
- buffer.delete(0, i);
+ /*
+ * All other categories (e.g. punctuations) may be used as a
split point
+ * if no better location is found.
+ */
+ default: {
+ if (!hasFallback && b != '<') {
+ hasFallback = true;
+ fallback = splitAt;
+ }
break;
}
- i += Character.charCount(s);
}
- printableLength = buffer.length();
- codePointCount = buffer.codePointCount(0, printableLength);
- onLineBegin(true);
+ splitAt -= n;
}
+ transfer(splitAt);
+ writeLineSeparator();
+ printableLength = buffer.length(); // Remaining characters
will be on next line.
+ codePointCount = buffer.codePointCount(0, printableLength);
+ onLineBegin(true);
}
/**
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
index 687d6dcc9b..691986a1d0 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
@@ -792,7 +792,7 @@ loop: for (int i=0; ; i++) {
* Now for the message part, we need to use the LineAppender in
order to replace EOL
* and tabulations.
*/
- writer.setMaximalLineLength(maximalLength);
+ writer.setMaximalLineLength(Math.max(maximalLength - margin - 3,
10));
writer.setCurrentLineLength(X364.lengthOfPlain(buffer, 0,
buffer.length()));
try {
if (message != null) {
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/AppenderTestCase.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/AppenderTestCase.java
index eb868a0973..0648563292 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/AppenderTestCase.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/AppenderTestCase.java
@@ -20,7 +20,7 @@ import java.io.IOException;
// Test dependencies
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.TestCase;
import org.apache.sis.test.DependsOnMethod;
import static org.apache.sis.test.Assertions.assertMultilinesEquals;
@@ -75,7 +75,7 @@ public abstract class AppenderTestCase extends TestCase {
IO.flush(appender);
final String actual = buffer.toString();
assertMultilinesEquals("Ignoring line separators.", expected, actual);
- assertEquals ("Checking line separators.", expected, actual);
+ assertEquals(expected, actual, "Checking line separators.");
}
/**
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/EchoAppendable.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/EchoAppendable.java
index fd7bd7efca..aa7910822d 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/EchoAppendable.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/EchoAppendable.java
@@ -19,7 +19,7 @@ package org.apache.sis.io;
import java.io.IOException;
// Test dependencies
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LeftMarginTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LeftMarginTest.java
index fbe2cb69c5..720bf23727 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LeftMarginTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LeftMarginTest.java
@@ -20,7 +20,7 @@ import java.io.IOException;
// Test dependencies
import org.junit.Before;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.DependsOn;
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
index 865b03b96c..4d79776c26 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
@@ -21,7 +21,7 @@ import java.io.IOException;
// Test dependencies
import org.junit.Before;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.DependsOn;
@@ -62,7 +62,7 @@ public class LineAppenderTest extends AppenderTestCase {
void run(final String lineSeparator) throws IOException {
final Appendable f = appender;
if (f instanceof LineAppender) {
- assertEquals("getLineSeparator", " ", ((LineAppender)
f).getLineSeparator());
+ assertEquals(" ", ((LineAppender) f).getLineSeparator());
}
assertSame(f, f.append("Le vrai" + lineSeparator + "policitien, "));
assertSame(f, f.append("c'est celui\r\nqui\r")); // Line separator
broken on two method calls.
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TableAppenderTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TableAppenderTest.java
index 967d99957c..78028465a7 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TableAppenderTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TableAppenderTest.java
@@ -20,7 +20,7 @@ import java.io.IOException;
// Test dependencies
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.DependsOn;
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
index 6fbc487016..ba661dde6b 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
@@ -20,7 +20,7 @@ import java.io.IOException;
// Test dependencies
import org.junit.Before;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.DependsOn;
@@ -58,7 +58,7 @@ public final class TabulationExpansionTest extends
LineAppenderTest {
void run(final String lineSeparator) throws IOException {
final Appendable f = appender;
if (f instanceof LineAppender) {
- assertEquals("getTabWidth", 8, ((LineAppender)
f).getTabulationWidth());
+ assertEquals(8, ((LineAppender) f).getTabulationWidth());
}
assertSame(f, f.append("12\t8" + lineSeparator));
assertSame(f, f.append("1234\t8" + lineSeparator));
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
index de8c5ece7c..391940dc7c 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
@@ -17,12 +17,13 @@
package org.apache.sis.io;
import java.io.IOException;
+import org.apache.sis.util.Characters;
import org.apache.sis.util.internal.X364;
-import static org.apache.sis.util.Characters.SOFT_HYPHEN;
// Test dependencies
+import org.junit.Test;
import org.junit.Before;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.DependsOn;
@@ -35,6 +36,11 @@ import org.apache.sis.test.DependsOn;
*/
@DependsOn(LineAppenderTest.class)
public class WordWrapTest extends LineAppenderTest {
+ /**
+ * The line length used in the tests.
+ */
+ static final int LINE_LENGTH = 11;
+
/**
* Creates a new test case.
*/
@@ -47,7 +53,7 @@ public class WordWrapTest extends LineAppenderTest {
@Before
@Override
public void createLineAppender() {
- appender = new LineAppender(appender, 10, false);
+ appender = new LineAppender(appender, 11, false);
}
/**
@@ -70,12 +76,12 @@ public class WordWrapTest extends LineAppenderTest {
void run(final String lineSeparator) throws IOException {
final Appendable f = appender;
if (f instanceof LineAppender) {
- assertEquals("getMaximalLineLength", 10, ((LineAppender)
f).getMaximalLineLength());
+ assertEquals(LINE_LENGTH, ((LineAppender)
f).getMaximalLineLength());
}
final String BLUE = X364.FOREGROUND_BLUE .sequence();
final String DEFAULT = X364.FOREGROUND_DEFAULT.sequence();
assertSame(f, f.append("Ah! comme la " + BLUE + "neige" + DEFAULT + "
a neigé!" + lineSeparator));
- assertSame(f, f.append("Ma vitre est un jar" + SOFT_HYPHEN + "din de
givre." + lineSeparator));
+ assertSame(f, f.append("Ma vitre est un jar" + Characters.SOFT_HYPHEN
+ "din de givre." + lineSeparator));
/*
* If our test case is using the wrapper which will send the data once
character at time,
* our LineAppender implementation will not be able to detect the line
separator and
@@ -93,8 +99,23 @@ public class WordWrapTest extends LineAppenderTest {
+ "la " + BLUE + "neige" + DEFAULT + " a" +
insertedLineSeparator
+ "neigé!" +
expectedLineSeparator
+ "Ma vitre" +
insertedLineSeparator
- + "est un jar" + SOFT_HYPHEN +
insertedLineSeparator
+ + "est un jar" + Characters.HYPHEN +
insertedLineSeparator
+ "din de" +
insertedLineSeparator
+ "givre." +
expectedLineSeparator);
}
+
+ /**
+ * Test splitting a long lines into shorter lines.
+ *
+ * @throws IOException should never happen, since we are writing in a
{@link StringBuilder}.
+ */
+ @Test
+ public void testLineSplit() throws IOException {
+ final LineAppender f = (LineAppender) appender;
+ final String ls = expectedLineSeparator("\n");
+ f.setLineSeparator("\n");
+ f.setMaximalLineLength(LINE_LENGTH);
+ f.append("bar foo-biz bla\nThisLineIsTooLong");
+ assertOutputEquals("bar foo-" + ls + "biz bla" + ls + "ThisLineIsT" +
ls + "ooLong");
+ }
}
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapWithLineSeparatorTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapWithLineSeparatorTest.java
index 39198cf619..6e0c8a76f2 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapWithLineSeparatorTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapWithLineSeparatorTest.java
@@ -43,7 +43,7 @@ public final class WordWrapWithLineSeparatorTest extends
WordWrapTest {
@Before
@Override
public void createLineAppender() {
- appender = new LineAppender(new LineAppender(appender, "\r", false),
10, false);
+ appender = new LineAppender(new LineAppender(appender, "\r", false),
WordWrapTest.LINE_LENGTH, false);
}
/**