This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit d8e23f1abe2faa0083418c759ce9c9c6c1f69b72 Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu Oct 8 12:55:32 2026 +0200 Add a `TableRowList` internal class and uses it in `PropertyFormat` for better formatting of tabular data. --- .../apache/sis/util/internal/shared/Numerics.java | 2 +- .../sis/util/internal/shared/PropertyFormat.java | 66 ++++++++++--- .../sis/util/internal/shared/TableRowList.java | 103 +++++++++++++++++++++ 3 files changed, 156 insertions(+), 15 deletions(-) diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java index b1b53c0cdc..7d0e261754 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java @@ -657,7 +657,7 @@ public final class Numerics { * @return the result of {@code action}. */ @Workaround(library="JDK", version="10") - public static String useScientificNotationIfNeeded(final Format format, final Object value, final BiFunction<Format,Object,String> action) { + public static String useScientificNotationIfNeeded(final Format format, final Object value, final BiFunction<Format, Object, String> action) { if (value instanceof Number && format instanceof DecimalFormat) { final DecimalFormat df = (DecimalFormat) format; final int maxFD = df.getMaximumFractionDigits(); diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java index 50cfe95def..1012173e36 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java @@ -24,6 +24,7 @@ import java.util.TimeZone; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.charset.Charset; +import java.text.Format; import org.opengis.util.Type; import org.opengis.util.Record; import org.opengis.util.GenericName; @@ -57,7 +58,23 @@ public abstract class PropertyFormat extends LineAppender implements Localized { /** * {@code true} if this method is invoking itself for writing collection values. */ - private transient boolean recursive; + private boolean recursive; + + /** + * Number of collection elements that have already been written. + */ + private byte formattedItemCount; + + /** + * Index of the column to format if formating a table. + */ + private int columnIndex; + + /** + * Supplier of the formats to use for the next values, or {@code null} if none. + * Used for formatting items in a collection. + */ + private TableRowList<?,?> sourceTable; /** * Creates a new instance which will write to the given appendable. @@ -135,7 +152,7 @@ public abstract class PropertyFormat extends LineAppender implements Localized { appendCollection(Arrays.asList((Object[]) value)); return; } else if (value instanceof Map.Entry<?,?>) { - final Map.Entry<?,?> entry = (Map.Entry<?,?>) value; + final var entry = (Map.Entry<?,?>) value; final Object k = entry.getKey(); final Object v = entry.getValue(); if (k == null) { @@ -149,7 +166,12 @@ public abstract class PropertyFormat extends LineAppender implements Localized { } return; } else { - text = toString(value); + final Format v; + if (sourceTable != null && (v = sourceTable.createFormat(getLocale(), columnIndex)) != null) { + text = v.format(value); + } else { + text = toString(value); + } } append(text); } @@ -192,25 +214,41 @@ public abstract class PropertyFormat extends LineAppender implements Localized { */ private void appendCollection(final Iterable<?> values) throws IOException { if (values != null) { - if (recursive) { - append('…'); // Do not format collections inside collections. - } else { - int count = 0; + final var parent = sourceTable; + if (values instanceof TableRowList<?,?>) { + sourceTable = (TableRowList<?,?>) values; + } + final boolean isEnclosed = recursive; + if (isEnclosed) { + append('{'); + } + final int parentIndex = columnIndex; + try { + columnIndex = 0; + recursive = true; for (final Object value : values) { if (value != null) { - if (count != 0) append(", "); - try { - recursive = true; - appendValue(value); - } finally { - recursive = false; + if (columnIndex != 0) { + append(", "); } - if (++count == 10) { // Arbitrary limit. + appendValue(value); + if (++formattedItemCount >= 10) { // Arbitrary limit. append(", …"); break; } + columnIndex++; } } + if (isEnclosed) { + append('}'); + } + } finally { + sourceTable = parent; + columnIndex = parentIndex; + recursive = isEnclosed; + if (!isEnclosed) { + formattedItemCount = 0; + } } } } diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/TableRowList.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/TableRowList.java new file mode 100644 index 0000000000..e39df92d94 --- /dev/null +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/TableRowList.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.sis.util.internal.shared; + +import java.util.AbstractList; +import java.text.Format; +import java.util.Locale; +import org.apache.sis.io.TableAppender; + + +/** + * A list of row items (for example records) which can be separated in finer items (field values). + * This class is not used directly by the utility module, but is defined for allowing transfer of + * information between (for example) GeoTIFF and <abbr>GUI</abbr> modules. + * + * @author Martin Desruisseaux (Geomatys) + * + * @param <E> type of row items in the list. + * @param <V> type of value in columns. + */ +public abstract class TableRowList<E, V> extends AbstractList<E> { + /** + * Creates a new list. + */ + protected TableRowList() { + } + + /** + * Returns the title of each column, in order. + * The length of this array is the number of columns. + * + * @return the column titles. + */ + public abstract String[] columns(); + + /** + * Returns the value in the given column of the given row. + * + * @param row the row, from 0 inclusive to {@link #size()} exclusive. + * @param column the column, from 0 inclusive to {@code columns().length} exclusive. + * @return value in the given column of the given row. + * @throws IndexOutOfBoundsException if the row of column index is invalid. + */ + public abstract V get(int row, int column); + + /** + * Returns the format to use for the given column (optional). + * + * @param locale the locale of the format to create. + * @param column the column for which to get a format. + * @return format configured for the given column, or {@code null} if none. + */ + public Format createFormat(Locale locale, int column) { + return null; + } + + /** + * Formats this list as a table. + * + * @return a string representation of this list formatted as a table. + */ + @Override + public String toString() { + final var locale = Locale.getDefault(); + final var table = new TableAppender(); + final String[] columns = columns(); + final Format[] formats = new Format[columns.length]; + table.setCellAlignment(TableAppender.ALIGN_RIGHT); + table.setMultiLinesCells(true); + table.nextLine('═'); + for (int column = 0; column < columns.length; column++) { + formats[column] = createFormat(locale, column); + table.append(columns[column]); + table.nextColumn(); + } + table.nextLine(); + table.nextLine('─'); + final int size = size(); + for (int row = 0; row < size; row++) { + for (int column = 0; column < columns.length; column++) { + table.append(formats[column].format(get(row, column))); + table.nextColumn(); + } + table.nextLine(); + } + table.nextLine('═'); + return table.toString(); + } +}
