You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2024/01/30 18:50:34 UTC
(sis) 02/03: Fix a formatting error (exception or broken strings) when the WKT contains numbers formatted as a matrix and X364 coloring is enabled.
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
commit bdccdc1014901eda858cd198f958137a6a80a735
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Jan 30 16:11:24 2024 +0100
Fix a formatting error (exception or broken strings) when the WKT contains numbers formatted as a matrix and X364 coloring is enabled.
---
.../main/org/apache/sis/io/wkt/Formatter.java | 96 +++++++++++++---------
1 file changed, 58 insertions(+), 38 deletions(-)
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
index 92fb6d754f..a32da24a3e 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
@@ -280,7 +280,7 @@ public class Formatter implements Localized {
private int colorApplied;
/**
- * The amount of spaces to use in indentation, or {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE}
+ * The number of spaces to use in indentation, or {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE}
* if indentation is disabled.
*
* @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
@@ -288,7 +288,7 @@ public class Formatter implements Localized {
private byte indentation;
/**
- * The amount of space to write on the left side of each line. This amount is increased
+ * The number of space to write on the left side of each line. This amount is increased
* by {@code indentation} every time a {@link FormattableObject} is appended in a new
* indentation level.
*/
@@ -353,7 +353,7 @@ public class Formatter implements Localized {
*
* @param convention the convention to use.
* @param symbols the symbols.
- * @param indentation the amount of spaces to use in indentation for WKT formatting,
+ * @param indentation the number of spaces to use in indentation for WKT formatting,
* or {@link WKTFormat#SINGLE_LINE} for formatting the whole WKT on a single line.
*/
public Formatter(final Convention convention, final Symbols symbols, final int indentation) {
@@ -422,7 +422,7 @@ public class Formatter implements Localized {
* @param colors the syntax coloring, or {@code null} if none.
* @param toUpperCase whether keywords shall be converted to upper cases.
* @param longKeywords {@code -1} for short keywords, {@code +1} for long keywords or 0 for the default.
- * @param indentation the amount of spaces to use in indentation for WKT formatting, or {@link WKTFormat#SINGLE_LINE}.
+ * @param indentation the number of spaces to use in indentation for WKT formatting, or {@link WKTFormat#SINGLE_LINE}.
* @param listSizeLimit maximum number of elements to show in lists, or {@link Integer#MAX_VALUE} if unlimited.
*/
final void configure(Convention convention, final Citation authority, final Colors colors,
@@ -559,7 +559,7 @@ public class Formatter implements Localized {
/**
* Increases or decreases the indentation. A value of {@code +1} increases
- * the indentation by the amount of spaces specified at construction time,
+ * the indentation by the number of spaces specified at construction time,
* and a value of {@code -1} reduces it by the same amount.
*
* @param amount +1 for increasing the indentation, or -1 for decreasing it, or 0 for no-op.
@@ -712,11 +712,19 @@ public class Formatter implements Localized {
final Locale syntax = symbols.getLocale(); // Not the same purpose as `this.locale`.
keyword = (toUpperCase >= 0) ? keyword.toUpperCase(syntax) : keyword.toLowerCase(syntax);
}
+ /*
+ * If the WKT contains errors or non-standard elements, highlight the keyword (if allowed).
+ * Some buffer indices will need to be shifted for spaces occupied by the X364 color codes:
+ * `base`, `keywordSpaceAt`.
+ */
+ int highlightOffset = 0;
if (highlightError && colors != null) {
final String color = colors.getAnsiSequence(ElementKind.ERROR);
if (color != null) {
- buffer.insert(base, color + X364.BACKGROUND_DEFAULT.sequence());
- base += color.length();
+ final String c = color + X364.BACKGROUND_DEFAULT.sequence();
+ highlightOffset = c.length();
+ buffer.insert(base, c);
+ base += color.length(); // Insert keyword before `BACKGROUND_DEFAULT`.
}
}
highlightError = false;
@@ -734,7 +742,7 @@ public class Formatter implements Localized {
final int n = keywordSpaceAt.size();
for (int i=0; i<n;) {
int p = keywordSpaceAt.getInt(i);
- p += (++i * length); // Take in account spaces added previously.
+ p += highlightOffset + (++i * length); // Take in account spaces added previously.
buffer.insert(p, additionalMargin);
}
keywordSpaceAt.clear();
@@ -1276,7 +1284,7 @@ public class Formatter implements Localized {
* If the rows are going to be formatted on many lines, then we will need to put some margin before each row.
* If the first row starts on its own line, then the margin will be the usual indentation. But if the first
* row starts on the same line as previous elements (or the keyword of this element, e.g. "BOX["), then we
- * will need a different amount of spaces if we want to have the numbers properly aligned.
+ * will need a different number of spaces if we want to have the numbers properly aligned.
*/
final int numRows = rows.length;
final boolean isMultiLines = (indentation > WKTFormat.SINGLE_LINE) && (numRows > 1);
@@ -1292,20 +1300,23 @@ public class Formatter implements Localized {
if (Characters.isLineOrParagraphSeparator(c)) break;
i -= Character.charCount(c);
}
- currentLineLength = buffer.codePointCount(i, length);
+ currentLineLength = X364.lengthOfPlain(buffer, i, length);
}
marginBeforeRow = CharSequences.spaces(currentLineLength);
} else {
marginBeforeRow = "";
}
/*
- * `formattedNumberMarks` contains, for each number in each row, positions in the `buffer` where
- * the number starts and position where it ends. Those positions are stored as (start,end) pairs.
- * We compute those marks unconditionally for simplicity, but will ignore them if formatting on
- * a single line.
+ * `formattedNumberMarks` contains, for each number in each row, the index after `base` where
+ * the number starts and the index where the number ends (including X364 colors), together with
+ * the number lengths (ignoring X364 colors). Those information are stored as (start,end,length)
+ * tuples. We compute those values unconditionally for simplicity, but will ignore them if the
+ * WKT is formatted on a single line.
*/
+ final int TUPLE_LENGTH = 3; // Number of elements in each `formattedNumberMarks` tuple.
+ final int base = elementStart; // Needs to be saved here because `elementStart` may be modified.
final int[][] formattedNumberMarks = new int[numRows][];
- int numColumns = 0;
+ int maxNumCols = 0;
for (int j=0; j<numRows; j++) {
if (j == 0) {
appendSeparator(); // It is up to the caller to decide if we begin with a new line.
@@ -1314,63 +1325,72 @@ public class Formatter implements Localized {
}
final Vector numbers = rows[j];
final int numCols = numbers.size();
- numColumns = Math.max(numColumns, numCols); // Store the length of longest row.
- final int[] marks = new int[numCols << 1]; // Positions where numbers are formatted.
+ maxNumCols = Math.max(maxNumCols, numCols); // Store the length of longest row.
+ final int[] marks = new int[numCols*TUPLE_LENGTH]; // Positions where numbers are formatted.
formattedNumberMarks[j] = marks;
- for (int i=0; i<numCols; i++) {
+ for (int i=0,k=0; i<numCols; i++) {
if (i != 0) buffer.append(Symbols.NUMBER_SEPARATOR);
if (i < fractionDigits.length) { // Otherwise, same as previous number.
final int f = fractionDigits[i];
numberFormat.setMaximumFractionDigits(f);
numberFormat.setMinimumFractionDigits(f);
}
- marks[i << 1] = buffer.length(); // Store the start position where number is formatted.
+ marks[k++] = buffer.length() - base; // Store the start position where number is formatted.
setColor(ElementKind.NUMBER);
+ final int s = buffer.length();
final Number n = numbers.get(i);
if (n != null) {
numberFormat.format(n, buffer, dummy);
} else {
buffer.append('…');
}
+ final int e = buffer.length();
resetColor();
- marks[(i << 1) | 1] = buffer.length(); // Store the end position where number is formatted.
+ marks[k++] = buffer.length() - base; // Store the end position where number is formatted.
+ marks[k++] = buffer.codePointCount(s, e); // Note: there is no X364 colors in this range.
}
}
/*
- * If formatting on more than one line, insert the amount of spaces required for aligning numbers.
+ * If formatting on more than one line, insert the number of spaces required for aligning numbers.
* This is possible because we wrote the coordinate values with fixed number of fraction digits.
*/
if (isMultiLines) {
- final int base = elementStart;
- final String toWrite = buffer.substring(base); // Save what we formatted in above loop.
- buffer.setLength(base); // Discard what we formatted - we will rewrite.
- final int[] columnWidths = new int[numColumns];
- for (final int[] marks : formattedNumberMarks) { // Compute the maximal width of each column.
- for (int i=0; i<marks.length; i += 2) {
- final int k = i >> 1;
- final int w = toWrite.codePointCount(marks[i ] -= base,
- marks[i+1] -= base);
+ // Compute the maximal width of each column.
+ final int[] columnWidths = new int[maxNumCols];
+ for (final int[] marks : formattedNumberMarks) {
+ for (int i = TUPLE_LENGTH-1; i < marks.length; i += TUPLE_LENGTH) {
+ final int w = marks[i];
+ final int k = i / TUPLE_LENGTH;
if (w > columnWidths[k]) columnWidths[k] = w;
}
}
- if (needsAlignment && keywordSpaceAt == null) {
- keywordSpaceAt = new IntegerList(formattedNumberMarks.length, Integer.MAX_VALUE);
- }
+ final String toWrite = buffer.substring(base); // Save what we formatted in above loop.
+ buffer.setLength(base); // Discard what we formatted - we will rewrite.
+ int endOfPrevious = 0;
boolean requestAlignment = false;
- int lastPosition = 0;
for (int[] marks : formattedNumberMarks) { // Recopy the formatted text, with more spaces.
for (int i = 0; i<marks.length;) {
- final int w = columnWidths[i >> 1];
+ final int w = columnWidths[i / TUPLE_LENGTH];
final int s = marks[i++];
final int e = marks[i++];
- buffer.append(toWrite, lastPosition, s)
- .append(CharSequences.spaces(w - toWrite.codePointCount(s, e)));
+ final int n = marks[i++];
+ buffer.append(toWrite, endOfPrevious, s).append(CharSequences.spaces(w - n));
+ /*
+ * If we are formatting the first number of a new line except the first line,
+ * we will need to add more spaces than what we added with `marginBeforeRow`.
+ * The number of spaces depends on the number of characters in the keyword to
+ * be written before the values. We do not know that keyword yet, so we need
+ * to remember that more spaces will need to be inserted here.
+ */
if (requestAlignment) {
requestAlignment = false;
+ if (keywordSpaceAt == null) {
+ keywordSpaceAt = new IntegerList(formattedNumberMarks.length, Integer.MAX_VALUE);
+ }
keywordSpaceAt.add(buffer.length());
}
buffer.append(toWrite, s, e);
- lastPosition = e;
+ endOfPrevious = e;
}
requestAlignment = needsAlignment;
}