sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1411285 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/internal/util/ main/java/org/apache/sis/io/ main/java/org/apache/sis/util/collection/ main/java/org/apache/sis/util/resources/ test/java/org/apache/sis/test/ test/ja...
Date Mon, 19 Nov 2012 16:34:04 GMT
Author: desruisseaux
Date: Mon Nov 19 16:34:02 2012
New Revision: 1411285

URL: http://svn.apache.org/viewvc?rev=1411285&view=rev
Log:
Initial commit of TreeTableFormat.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java   (with props)
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/ColumnConstant.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/Assert.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DefaultTreeTableTest.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/ColumnConstant.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/ColumnConstant.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/ColumnConstant.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/ColumnConstant.java Mon Nov 19 16:34:02 2012
@@ -59,9 +59,10 @@ public final class ColumnConstant<V> imp
             (Class) Class.class, Vocabulary.Keys.Type);
 
     /**
-     * The default set of columns when parsing a table tree.
+     * A map containing only the {@link #NAME} column.
+     * This is the default set of columns when parsing a table tree.
      */
-    public static final Map<TableColumn<?>,Integer> PARSING =
+    public static final Map<TableColumn<?>,Integer> NAME_MAP =
             Collections.<TableColumn<?>,Integer>singletonMap(NAME, 0);
 
     /**

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java Mon Nov 19 16:34:02 2012
@@ -223,7 +223,9 @@ public abstract class CompoundFormat<T> 
     /**
      * Creates an object from the given string representation.
      * The default implementation delegates to {@link #parse(CharSequence, ParsePosition)}
-     * and ensures that the given string has been fully used (ignoring trailing spaces).
+     * and ensures that the given string has been fully used, ignoring trailing
+     * {@linkplain Character#isSpaceChar(int) spaces} and
+     * {@linkplain Character#isISOControl(int) ISO control characters}.
      *
      * @param  text The string representation of the object to parse.
      * @return The parsed object.
@@ -242,7 +244,7 @@ public abstract class CompoundFormat<T> 
                 }
                 c = text.codePointAt(i);
                 n = Character.charCount(c);
-            } while (c < 32 || Character.isSpaceChar(c)); // c<32 is for skipping control characters.
+            } while (Character.isSpaceChar(c) || Character.isISOControl(c));
             pos.setErrorIndex(i);
         }
         throw new LocalizedParseException(locale, getValueType(), text, pos);

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java Mon Nov 19 16:34:02 2012
@@ -285,7 +285,7 @@ public class TableFormatter extends Filt
      * @param  horizontalBorder -1 for left border, +1 for right border,  0 for center.
      * @param  verticalBorder   -1 for top  border, +1 for bottom border, 0 for center.
      * @param  horizontalChar   Character to use for horizontal line.
-     * @throws IOException     if the writing operation failed.
+     * @throws IOException      If the writing operation failed.
      */
     private void writeBorder(final int  horizontalBorder,
                              final int  verticalBorder,
@@ -732,13 +732,14 @@ public class TableFormatter extends Filt
                 for (int j=0; j<currentLine.length; j++) {
                     final boolean isFirstColumn = (j   == 0);
                     final boolean isLastColumn  = (j+1 == currentLine.length);
-                    final Cell cell = currentLine[j];
-                    final int cellWidth = maximalColumnWidths[j];
+                    final Cell    cell          = currentLine[j];
+                    final int     cellWidth     = maximalColumnWidths[j];
+                    final int     cellPadding   = isLastColumn && rightBorder.isEmpty() ? 0 : cellWidth;
                     if (cell == null) {
                         if (isFirstColumn) {
                             out.append(leftBorder);
                         }
-                        repeat(out, SPACE, cellWidth);
+                        repeat(out, SPACE, cellPadding);
                         out.append(isLastColumn ? rightBorder : columnSeparator);
                         continue;
                     }
@@ -792,7 +793,7 @@ public class TableFormatter extends Filt
                         if (isFirstColumn) {
                             writeBorder(-1, verticalBorder, cell.fill);
                         }
-                        repeat(out, cell.fill, cellWidth);
+                        repeat(out, cell.fill, Character.isSpaceChar(cell.fill) ? cellPadding : cellWidth);
                         writeBorder(isLastColumn ? +1 : 0, verticalBorder, cell.fill);
                         continue;
                     }
@@ -811,7 +812,7 @@ public class TableFormatter extends Filt
                         }
                         case ALIGN_LEFT: {
                             tabExpander.append(cellText);
-                            repeat(tabExpander, cell.fill, cellWidth - textLength);
+                            repeat(tabExpander, cell.fill, cellPadding - textLength);
                             break;
                         }
                         case ALIGN_RIGHT: {
@@ -820,10 +821,10 @@ public class TableFormatter extends Filt
                             break;
                         }
                         case ALIGN_CENTER: {
-                            final int rightMargin = (cellWidth - textLength) / 2;
-                            repeat(tabExpander, cell.fill, rightMargin);
+                            final int leftPadding = (cellWidth - textLength) / 2;
+                            repeat(tabExpander, cell.fill, leftPadding);
                             tabExpander.append(cellText);
-                            repeat(tabExpander, cell.fill, (cellWidth - rightMargin) - textLength);
+                            repeat(tabExpander, cell.fill, (cellPadding - leftPadding) - textLength);
                             break;
                         }
                     }
@@ -850,7 +851,8 @@ public class TableFormatter extends Filt
     }
 
     /**
-     * Repeats a character.
+     * Repeats a character. The {@code count} value may be negative,
+     * which is handled as if it was zero.
      *
      * @param out   The stream or buffer where to repeat the character.
      * @param car   Character to write (usually ' ').

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java Mon Nov 19 16:34:02 2012
@@ -315,6 +315,14 @@ public class DefaultTreeTable implements
         private Object[] values;
 
         /**
+         * Creates a new node with the given shared map of columns and the given values.
+         */
+        Node(final Map<TableColumn<?>,Integer> columnIndices, final Object[] values) {
+            this.columnIndices = columnIndices;
+            this.values = values;
+        }
+
+        /**
          * Creates a new node for the given table. The new node will be able to store a value
          * for each {@linkplain TreeTable#getColumns() columns} defined in the given table.
          *
@@ -336,22 +344,34 @@ public class DefaultTreeTable implements
         }
 
         /**
+         * Creates a new node with the given parent. The new node is added at the end of the parent
+         * {@linkplain #getChildren() list of children}. The new node will be able to store values
+         * for the same columns than the parent node.
+         *
+         * @param parent The parent of the new node.
+         */
+        public Node(final Node parent) {
+            ArgumentChecks.ensureNonNull("parent", parent);
+            this.parent = parent;
+            columnIndices = parent.columnIndices;
+            final TreeNodeList addTo = (TreeNodeList) parent.getChildren();
+            addTo.addChild(addTo.size(), this);
+        }
+
+        /**
          * Creates a new node with the given parent. The new node is added to the parent
          * {@linkplain #getChildren() list of children} at the given index. The new node
          * will be able to store values for the same columns than the parent node.
          *
          * @param parent The parent of the new node.
-         * @param index  The index where to add the new node in the parent list of children,
-         *               or -1 for adding the new node at the end of the list.
+         * @param index  The index where to add the new node in the parent list of children.
          */
-        public Node(final Node parent, int index) {
+        public Node(final Node parent, final int index) {
             ArgumentChecks.ensureNonNull("parent", parent);
             this.parent = parent;
             columnIndices = parent.columnIndices;
             final TreeNodeList addTo = (TreeNodeList) parent.getChildren();
-            if (index < 0) {
-                index = addTo.size();
-            }
+            ArgumentChecks.ensureValidIndex(addTo.size() + 1, index);
             addTo.addChild(index, this);
         }
 

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java?rev=1411285&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java Mon Nov 19 16:34:02 2012
@@ -0,0 +1,707 @@
+/*
+ * 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.collection;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.io.IOException;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.text.ParseException;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.sis.io.LineFormatter;
+import org.apache.sis.io.TableFormatter;
+import org.apache.sis.io.CompoundFormat;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.ColumnConstant;
+import org.apache.sis.internal.util.LocalizedParseException;
+
+import static org.apache.sis.util.Characters.NO_BREAK_SPACE;
+
+
+/**
+ * A parser and formatter for {@link TreeTable} instances.
+ * This formatter is given an arbitrary number of {@link TableColumn}s
+ * to use during the formatting. The first column is taken as the node label.
+ * If a {@code TreeTable} is formatted with only that column,
+ * then the {@link String} result is like the following example:
+ *
+ * {@preformat text
+ *   Node #1
+ *   ├───Node #2
+ *   │   └───Node #4
+ *   └───Node #3
+ * }
+ *
+ * If the same {@code TreeTable} is formatted with two columns,
+ * then the {@link String} result is like the following example:
+ *
+ * {@preformat text
+ *   Node #1……………………… More #1
+ *   ├───Node #2…………… More #2
+ *   │   └───Node #4… More #4
+ *   └───Node #3…………… More #3
+ * }
+ *
+ * This representation can be printed to the {@linkplain java.io.Console#writer() console output}
+ * (for example) if the stream uses a monospaced font and supports Unicode characters.
+ * Some formatting characteristics (indentation width, column where to draw the vertical line
+ * below nodes) can be modified by calls to the setter methods defined in this formatter.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.3 (derived from geotk-2.0)
+ * @version 0.3
+ * @module
+ *
+ * @see org.apache.sis.io.TableFormatter
+ */
+@NotThreadSafe
+public class TreeTableFormat extends CompoundFormat<TreeTable> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4476366905386037025L;
+
+    /**
+     * The table columns to format, or {@code null} for formatting all of them.
+     * This map shall not be modified after creation, because it may be shared
+     * by many tables.
+     *
+     * @see #getColumns()
+     * @see #setColumns(TableColumn[])
+     */
+    private Map<TableColumn<?>,Integer> columnIndices;
+
+    /**
+     * The number of characters to add on the left side for each indentation level.
+     * The default value is 4.
+     *
+     * @see #getIndentation()
+     * @see #setIndentation(int)
+     */
+    private int indentation;
+
+    /**
+     * The position of the vertical line, relative to the position of the label of the parent node.
+     * The default value is 0, which means that the vertical line is drawn below the first letter
+     * of the node label.
+     *
+     * @see #getVerticalLinePosition()
+     * @see #setVerticalLinePosition(int)
+     */
+    private int verticalLinePosition;
+
+    /**
+     * The column separator to use at formatting time if there is more than one column.
+     *
+     * @see #getColumnSeparator()
+     * @see #setColumnSeparator(String)
+     */
+    String columnSeparator;
+
+    /**
+     * The line separator to use for formatting the tree.
+     *
+     * @see #getLineSeparator()
+     * @see #setLineSeparator(String)
+     */
+    String lineSeparator;
+
+    /**
+     * The tree symbols to write in the left margin, or {@code null} if not yet computed.
+     * The default symbols are as below:
+     *
+     * <ul>
+     *   <li>{@code treeBlank} = {@code "    "}</li>
+     *   <li>{@code treeLine}  = {@code "│   "}</li>
+     *   <li>{@code treeCross} = {@code "├───"}</li>
+     *   <li>{@code treeEnd}   = {@code "└───"}</li>
+     * </ul>
+     *
+     * @see #clearTreeSymbols()
+     * @see #createTreeSymbols()
+     */
+    private transient String treeBlank, treeLine, treeCross, treeEnd;
+
+    /**
+     * Creates a new tree table format.
+     *
+     * @param locale   The locale to use for numbers, dates and angles formatting.
+     * @param timezone The timezone, or {@code null} for UTC.
+     */
+    public TreeTableFormat(final Locale locale, final TimeZone timezone) {
+        super(locale, timezone);
+        indentation     = 4;
+        columnSeparator = "……";
+        lineSeparator   = System.lineSeparator();
+    }
+
+    /**
+     * Clears the symbols used when writing the tree.
+     * They will be computed again when first needed.
+     *
+     * @see #createTreeSymbols()
+     */
+    private void clearTreeSymbols() {
+        treeBlank = null;
+        treeLine  = null;
+        treeCross = null;
+        treeEnd   = null;
+    }
+
+    /**
+     * Returns the type of object formatted by this class, which is {@link TreeTable}.
+     */
+    @Override
+    public final Class<TreeTable> getValueType() {
+        return TreeTable.class;
+    }
+
+    /**
+     * Returns the table columns to parse and format, or {@code null} for the default list of
+     * columns. The default is:
+     *
+     * <ul>
+     *   <li>On parsing, a single column containing the node label as a {@link String}.</li>
+     *   <li>On formatting, {@linkplain TreeTable#getColumns() all <code>TreeTable</code> columns}.</li>
+     * </ul>
+     *
+     * @return The table columns to parse and format, or {@code null} for the default.
+     */
+    public TableColumn<?>[] getColumns() {
+        return (columnIndices != null) ? DefaultTreeTable.getColumns(columnIndices) : null;
+    }
+
+    /**
+     * Sets the table columns to parse and format. A {@code null} value means to use the default
+     * list of columns, as defined in the {@link #getColumns()} method.
+     *
+     * @param  columns The table columns to parse and format, or {@code null} for the default.
+     * @throws IllegalArgumentException If the given array is empty, contains a null element
+     *         or a duplicated value.
+     */
+    public void setColumns(final TableColumn<?>... columns) throws IllegalArgumentException {
+        if (columns == null) {
+            columnIndices = null;
+        } else {
+            if (columns.length == 0) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.EmptyArgument_1, "columns"));
+            }
+            columnIndices = DefaultTreeTable.createColumnIndices(columns);
+        }
+    }
+
+    /**
+     * Returns the characters used in column separators. This character will be used
+     * only if more than one column is formatted. The default value is {@code "……"}.
+     *
+     * @return The current column separator.
+     */
+    public String getColumnSeparator() {
+        return columnSeparator;
+    }
+
+    /**
+     * Sets the characters to insert between the columns. The separator shall be non-empty and
+     * contains at least one non-space character. The last character will be repeated as many
+     * time as needed for columns alignment.
+     *
+     * <p>In current implementation, the above-cited repeated character must be in the
+     * {@linkplain Character#isBmpCodePoint(int) Basic Multilanguage Plane}.</p>
+     *
+     * @param  separator The new column separator.
+     * @throws IllegalArgumentException If the given separator does not contains at least one
+     *         non-space character, or the last character is not in the BMP plane.
+     */
+    public void setColumnSeparator(final String separator) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonEmpty("separator", separator);
+        final int length = separator.length();
+        if (Character.isBmpCodePoint(separator.codePointBefore(length))) {
+            for (int i=0; i<length; i++) {
+                final int c = separator.codePointAt(i);
+                if (!Character.isSpaceChar(c) && !Character.isISOControl(c)) {
+                    columnSeparator = separator;
+                    return;
+                }
+            }
+        }
+        throw new IllegalArgumentException(Errors.format(
+                Errors.Keys.IllegalArgumentValue_2, "separator", separator));
+    }
+
+    /**
+     * Returns the current line separator. The default value is system-dependent.
+     *
+     * @return The current line separator.
+     */
+    public String getLineSeparator() {
+        return lineSeparator;
+    }
+
+    /**
+     * Sets the line separator.
+     *
+     * @param separator The new line separator.
+     */
+    public void setLineSeparator(final String separator) {
+        ArgumentChecks.ensureNonEmpty("separator", separator);
+        lineSeparator = separator;
+    }
+
+    /**
+     * Returns the number of spaces to add on the left margin for each indentation level.
+     * The default value is 4.
+     *
+     * @return The current indentation.
+     */
+    public int getIndentation() {
+        return indentation;
+    }
+
+    /**
+     * Sets the number of spaces to add on the left margin for each indentation level.
+     * If the new indentation is smaller than the {@linkplain #getVerticalLinePosition()
+     * vertical line position}, then the later is also set to the given indentation value.
+     *
+     * @param  indentation The new indentation.
+     * @throws IllegalArgumentException If the given value is negative.
+     */
+    public void setIndentation(final int indentation) throws IllegalArgumentException {
+        ArgumentChecks.ensurePositive("indentation", indentation);
+        this.indentation = indentation;
+        if (verticalLinePosition > indentation) {
+            verticalLinePosition = indentation;
+        }
+        clearTreeSymbols();
+    }
+
+    /**
+     * Returns the position of the vertical line, relative to the position of the root label.
+     * The default value is 0, which means that the vertical line is drawn below the first
+     * letter of the root label.
+     *
+     * @return The current vertical line position.
+     */
+    public int getVerticalLinePosition() {
+        return verticalLinePosition;
+    }
+
+    /**
+     * Sets the position of the vertical line, relative to the position of the root label.
+     * The given value can not be greater than the {@linkplain #getIndentation() indentation}.
+     *
+     * @param  verticalLinePosition The new vertical line position.
+     * @throws IllegalArgumentException If the given value is negative or greater than the indentation.
+     */
+    public void setVerticalLinePosition(final int verticalLinePosition) throws IllegalArgumentException {
+        ArgumentChecks.ensureBetween("verticalLinePosition", 0, indentation, verticalLinePosition);
+        this.verticalLinePosition = verticalLinePosition;
+        clearTreeSymbols();
+    }
+
+    /**
+     * Returns the formats to use for parsing and formatting the values of each column.
+     * The returned array may contain {@code null} elements, which means that the values
+     * in that column can be stored as {@code String}s.
+     *
+     * @param  mandatoy {@code true} if an exception shall be thrown for unrecognized types,
+     *         or {@code false} for storing a {@code null} value in the array instead.
+     * @throws IllegalStateException If {@code mandatory} is {@code true} and a column
+     *         contains values of an unsupported type.
+     */
+    final Format[] getFormats(final TableColumn<?>[] columns, final boolean mandatory) throws IllegalStateException {
+        final Format[] formats = new Format[columns.length];
+        for (int i=0; i<formats.length; i++) {
+            final Class<?> valueType = columns[i].getElementType();
+            if ((formats[i] = getFormat(valueType)) == null) {
+                if (mandatory && !valueType.isAssignableFrom(String.class)) {
+                    throw new IllegalStateException(Errors.format(
+                            Errors.Keys.UnspecifiedFormatForClass_1, valueType));
+                }
+            }
+        }
+        return formats;
+    }
+
+    /**
+     * Creates a tree from the given character sequence,
+     * or returns {@code null} if an error occurred while parsing the characters.
+     * This method can parse the trees created by the {@code format(…)} methods
+     * defined in this class.
+     *
+     * {@section Parsing rules}
+     * <ul>
+     *   <li>Each node shall be represented by a single line made of two parts, in that order:
+     *     <ol>
+     *       <li>white spaces and tree drawing characters ({@code '│'}, {@code '├'}, {@code '└'} or {@code '─'});</li>
+     *       <li>string representations of node values, separated by the {@linkplain #getColumnSeparator() colunm separator}.</li>
+     *     </ol>
+     *   </li>
+     *   <li>The number of spaces and drawing characters before the node values determines the node
+     *       indentation. This indentation doesn't need to be a factor of the {@link #getIndentation()}
+     *       value, but must be consistent across all the parsed tree.</li>
+     *   <li>The indentation determines the parent of each node.</li>
+     *   <li>Parsing stops at first empty line (ignoring whitespaces), or at the end of the given text.</li>
+     * </ul>
+     *
+     * @param  text The character sequence for the tree to parse.
+     * @param  pos  The position where to start the parsing.
+     * @return The parsed tree, or {@code null} if the given character sequence can not be parsed.
+     * @throws ParseException If an error occurred while parsing a node value.
+     */
+    @Override
+    public TreeTable parse(final CharSequence text, final ParsePosition pos) throws ParseException {
+        final char separator    = columnSeparator.charAt(columnSeparator.length() - 1);
+        final int length        = text.length();
+        int indexOfLineStart    = pos.getIndex();
+        int indentationLevel    = 0;                // Current index in the 'indentations' array.
+        int[] indentations      = new int[16];      // Number of spaces (ignoring drawing characters) for each level.
+        TreeTable.Node lastNode = null;             // Last parsed node, having 'indentation[level]' characters before its content.
+        TreeTable.Node root     = null;             // First node found while parsing.
+        final DefaultTreeTable table = new DefaultTreeTable(columnIndices != null ? columnIndices : ColumnConstant.NAME_MAP);
+        final TableColumn<?>[] columns = DefaultTreeTable.getColumns(table.columnIndices);
+        final Format[] formats = getFormats(columns, true);
+        do {
+            final int startNextLine = CharSequences.indexOfLineStart(text, 1, indexOfLineStart);
+            int endPosition = startNextLine;
+            while (endPosition > indexOfLineStart) {
+                final int c = text.charAt(endPosition-1);
+                if (c != '\r' && c != '\n') break;
+                endPosition--; // Skip trailing '\r' and '\n'.
+            }
+            boolean hasChar = false;
+            int i; // The indentation of current line.
+            for (i=indexOfLineStart; i<endPosition;) {
+                final int c = Character.codePointAt(text, i);
+                if (!Character.isSpaceChar(c)) {
+                    hasChar = true;
+                    if ("─│└├".indexOf(c) < 0) {
+                        break;
+                    }
+                }
+                i += Character.charCount(c);
+            }
+            if (!hasChar) {
+                break; // The line contains only whitespaces.
+            }
+            /*
+             * Go back to the fist non-space character (should be '─'). We do that in case the
+             * user puts some spaces in the text of the node label, since we don't want those
+             * user-spaces to interfer with the calculation of indentation.
+             */
+            int indexOfValue = i;
+            while (i > indexOfLineStart) {
+                final int c = Character.codePointBefore(text, i);
+                if (!Character.isSpaceChar(c)) break;
+                i -= Character.charCount(c);
+            }
+            i -= indexOfLineStart;
+            /*
+             * Found the first character which is not part of the indentation. Create a new root
+             * (without parent for now) and parse the values for each column. Columns with empty
+             * text are not parsed (the value is left to null).
+             */
+            final TreeTable.Node node = new DefaultTreeTable.Node(table);
+            try {
+parseColumns:   for (int ci=0; ci<columns.length; ci++) {
+                    int endOfColumn = CharSequences.indexOf(text, columnSeparator, indexOfValue, endPosition);
+                    if (endOfColumn < 0) {
+                        endOfColumn = endPosition;
+                    }
+                    for (int endOfValue = endOfColumn; endOfValue > indexOfValue;) {
+                        final int c = Character.codePointBefore(text, endOfValue);
+                        if (!Character.isSpaceChar(c)) {
+                            parseValue(node, columns[ci], formats[ci], text.subSequence(indexOfValue, endOfValue).toString());
+                            break;
+                        }
+                        endOfValue -= Character.charCount(c);
+                    }
+                    indexOfValue = endOfColumn;
+                    do if (++indexOfValue >= endPosition) {
+                        break parseColumns;
+                    }
+                    while (text.charAt(indexOfValue) == separator);
+                }
+            } catch (ParseException e) {
+                pos.setErrorIndex(indexOfValue);
+                throw e;
+            }
+            /*
+             * If this is the first node created so far, it will be the root.
+             */
+            if (root == null) {
+                indentations[0] = i;
+                root = node;
+            } else {
+                int p;
+                while (i < (p = indentations[indentationLevel])) {
+                    /*
+                     * Lower indentation level: go up in the tree until we find the new parent.
+                     * Note that lastNode.getParent() should never return null, since only the
+                     * node at 'indentationLevel == 0' has a null parent and we check that case.
+                     */
+                    if (--indentationLevel < 0) {
+                        pos.setErrorIndex(indexOfLineStart);
+                        throw new LocalizedParseException(locale,
+                                Errors.Keys.NodeHasNoParent_1, new Object[] {node}, 0);
+                    }
+                    lastNode = lastNode.getParent();
+                }
+                if (i == p) {
+                    /*
+                     * The node we just created is a sibling of the previous node. This is
+                     * illegal if level==0, in which case we have no parent. Otherwise add
+                     * the sibling to the common parent and let the indentation level unchanged.
+                     */
+                    final TreeTable.Node parent = lastNode.getParent();
+                    if (parent == null) {
+                        pos.setErrorIndex(indexOfLineStart);
+                        throw new LocalizedParseException(locale,
+                                Errors.Keys.NodeHasNoParent_1, new Object[] {node}, 0);
+                    }
+                    parent.getChildren().add(node);
+                } else if (i > p) {
+                    /*
+                     * The node we just created is a child of the previous node.
+                     * Add a new indentation level.
+                     */
+                    lastNode.getChildren().add(node);
+                    if (++indentationLevel == indentations.length) {
+                        indentations = Arrays.copyOf(indentations, indentationLevel*2);
+                    }
+                    indentations[indentationLevel] = i;
+                }
+            }
+            lastNode = node;
+            indexOfLineStart = startNextLine;
+        } while (indexOfLineStart != length);
+        if (root == null) {
+            return null;
+        }
+        pos.setIndex(indexOfLineStart);
+        table.setRoot(root);
+        return table;
+    }
+
+    /**
+     * Parses the given string using a format appropriate for the type of values in
+     * the given column, and stores the value in the given node.
+     *
+     * @param  V        The type of values in the given column.
+     * @param  node     The node in which to set the value.
+     * @param  column   The column in which to set the value.
+     * @param  format   The format to use for parsing the value, or {@code null}.
+     * @param  text     The textual representation of the value.
+     * @throws ParseException If an error occurred while parsing.
+     */
+    private <V> void parseValue(final TreeTable.Node node, final TableColumn<V> column,
+            final Format format, final String text) throws ParseException
+    {
+        final Object value;
+        if (format != null) {
+            value = format.parseObject(text);
+        } else {
+            value = text;
+        }
+        node.setValue(column, column.getElementType().cast(value));
+    }
+
+    /**
+     * Computes the {@code tree*} fields from the {@link #indentation} and
+     * {@link #verticalLinePosition} current values.
+     *
+     * @see #clearTreeSymbols()
+     */
+    private void createTreeSymbols() {
+        final int indentation = this.indentation;
+        final int verticalLinePosition = this.verticalLinePosition;
+        final char[] buffer = new char[indentation];
+        for (int k=0; k<4; k++) {
+            final char vc, hc;
+            if ((k & 2) == 0) {
+                // No horizontal line
+                vc = (k & 1) == 0 ? NO_BREAK_SPACE : '│';
+                hc = NO_BREAK_SPACE;
+            } else {
+                // With a horizontal line
+                vc = (k & 1) == 0 ? '└' : '├';
+                hc = '─';
+            }
+            Arrays.fill(buffer, 0, verticalLinePosition, NO_BREAK_SPACE);
+            buffer[verticalLinePosition] = vc;
+            Arrays.fill(buffer, verticalLinePosition + 1, indentation, hc);
+            final String symbols = String.valueOf(buffer);
+            switch (k) {
+                case 0: treeBlank = symbols; break;
+                case 1: treeLine  = symbols; break;
+                case 2: treeEnd   = symbols; break;
+                case 3: treeCross = symbols; break;
+                default: throw new AssertionError(k);
+            }
+        }
+    }
+
+    /**
+     * Returns the string to write before a node.
+     *
+     * @param isParent {@code true} for a parent node, or {@code false} for the actual node.
+     * @param isLast   {@code true} if the node is the last children of its parent node.
+     */
+    final String getTreeSymbols(final boolean isParent, final boolean isLast) {
+        return(isParent ? (isLast ? treeBlank : treeLine)
+                        : (isLast ? treeEnd   : treeCross));
+    }
+
+    /**
+     * Creates string representation of the node values. Tabulations are replaced by spaces,
+     * and line feeds are replaced by the Pilcrow character. This is necessary in order to
+     * avoid conflict with the characters expected by {@link TableFormatter}.
+     */
+    private final class Writer extends LineFormatter {
+        /**
+         * For each indentation level, {@code true} if the previous levels are writing the last node.
+         * This array will growth as needed.
+         */
+        private boolean[] last;
+
+        /**
+         * The columns to write.
+         */
+        private final TableColumn<?>[] columns;
+
+        /**
+         * The format to use for each column.
+         */
+        private final Format[] formats;
+
+        /**
+         * The column separator character to repeat until the columns are aligned.
+         */
+        private final char toRepeat;
+
+        /**
+         * Creates a new instance which will write in the given appendable.
+         */
+        Writer(final Appendable out, final TableColumn<?>[] columns) {
+            super(columns.length >= 2 ? new TableFormatter(out, "") : out);
+            this.columns  = columns;
+            this.formats  = getFormats(columns, false);
+            this.last     = new boolean[8];
+            this.toRepeat = columnSeparator.charAt(columnSeparator.length() - 1);
+            setTabulationExpanded(true);
+            setLineSeparator(" ¶ ");
+        }
+
+        /**
+         * Appends a textual representation of the given value.
+         *
+         * @param  format The format to use.
+         * @param  value  The value to format (may be {@code null}).
+         */
+        private void formatValue(final Format format, final Object value) throws IOException {
+            final CharSequence text;
+            if (value == null) {
+                text = " "; // String for missing value.
+            } else if (format != null) {
+                if (format instanceof CompoundFormat<?>) {
+                    formatValue((CompoundFormat<?>) format, value);
+                    return;
+                }
+                text = format.format(value);
+            } else {
+                text = String.valueOf(value);
+            }
+            append(text);
+        }
+
+        /**
+         * Work around for the inability to define the variable {@code <V>} locally.
+         */
+        @Workaround(library="JDK", version="1.7")
+        private <V> void formatValue(final CompoundFormat<V> format, final Object value) throws IOException {
+            format.format(format.getValueType().cast(value), this);
+        }
+
+        /**
+         * Appends the string representation of the given node and all its children.
+         * This method invokes itself recursively.
+         *
+         * @param node  The node to format.
+         * @param level Indentation level. The first level is 0.
+         */
+        final void format(final TreeTable.Node node, final int level) throws IOException {
+            for (int i=0; i<level; i++) {
+                out.append(getTreeSymbols(i != level-1, last[i]));
+            }
+            for (int i=0; i<columns.length; i++) {
+                if (i != 0) {
+                    // We have a TableFormatter instance if and only if there is 2 or more columns.
+                    ((TableFormatter) out.append(columnSeparator)).nextColumn(toRepeat);
+                }
+                formatValue(formats[i], node.getValue(columns[i]));
+                clear();
+            }
+            out.append(lineSeparator);
+            if (level >= last.length) {
+                last = Arrays.copyOf(last, level*2);
+            }
+            final List<? extends TreeTable.Node> children = node.getChildren();
+            final int count = children.size();
+            for (int i=0; i<count; i++) {
+                last[level] = (i == count-1);
+                format(children.get(i), level+1);
+            }
+        }
+    }
+
+    /**
+     * Writes a graphical representation of the specified tree table in the given stream or buffer.
+     * This method iterates recursively over all {@linkplain TreeTable.Node#getChildren() children}.
+     * For each {@linkplain #getColumns() column to format} in each node, this method gets a textual
+     * representation of the {@linkplain TreeTable.Node#getValue(TableColumn) value in that column}
+     * using the formatter obtained by a call to {@link #getFormat(Class)}.
+     *
+     * @param  tree        The tree to format.
+     * @param  toAppendTo  Where to format the tree.
+     * @throws IOException If an error occurred while writing in the given appender.
+     *
+     * @see TreeTables#toString(TreeTable)
+     */
+    @Override
+    public void format(final TreeTable tree, final Appendable toAppendTo) throws IOException {
+        ArgumentChecks.ensureNonNull("tree", tree);
+        if (treeBlank == null) {
+            createTreeSymbols();
+        }
+        TableColumn<?>[] columns;
+        if (columnIndices != null) {
+            columns = DefaultTreeTable.getColumns(columnIndices);
+        } else {
+            final List<TableColumn<?>> c = tree.getColumns();
+            columns = c.toArray(new TableColumn<?>[c.size()]);
+        }
+        final Writer out = new Writer(toAppendTo, columns);
+        out.format(tree.getRoot(), 0);
+        out.flush();
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java Mon Nov 19 16:34:02 2012
@@ -227,6 +227,11 @@ public final class Errors extends Indexe
         public static final int UnparsableStringForClass_3 = 32;
 
         /**
+         * No format is specified for objects of class ‘{0}’.
+         */
+        public static final int UnspecifiedFormatForClass_1 = 41;
+
+        /**
          * Can not handle instances of ‘{0}’ because arbitrary implementations are not yet supported.
          */
         public static final int UnsupportedImplementation_1 = 28;

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties Mon Nov 19 16:34:02 2012
@@ -50,6 +50,7 @@ UnmodifiableMetadata            = This m
 UnmodifiableObject_1            = Object \u2018{0}\u2019 is unmodifiable.
 UnparsableStringForClass_2      = Text \u201c{1}\u201d can not be parsed as an object of type \u2018{0}\u2019.
 UnparsableStringForClass_3      = Text \u201c{1}\u201d can not be parsed as an object of type \u2018{0}\u2019, because of the \u201c{2}\u201d characters.
+UnspecifiedFormatForClass_1     = No format is specified for objects of class \u2018{0}\u2019.
 UnsupportedImplementation_1     = Can not handle instances of \u2018{0}\u2019 because arbitrary implementations are not yet supported.
 UnsupportedOperation_1          = The \u2018{0}\u2019 operation is unsupported.
 ValueAlreadyDefined_1           = A value is already defined for \u201c{0}\u201d.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties Mon Nov 19 16:34:02 2012
@@ -48,6 +48,7 @@ UnmodifiableAffineTransform     = Cette 
 UnmodifiableGeometry            = Cette g\u00e9om\u00e9trie n\u2019est pas modifiable.
 UnmodifiableMetadata            = Cette m\u00e9ta-donn\u00e9e n\u2019est pas modifiable.
 UnmodifiableObject_1            = L\u2019objet \u2018{0}\u2019 n\u2019est pas modifiable.
+UnspecifiedFormatForClass_1     = Aucun format n\u2019est sp\u00e9cifi\u00e9 pour les objets de classe \u2018{0}\u2019.
 UnparsableStringForClass_2      = Le texte \u201c{1}\u201d n\u2019est pas reconnu comme un objet de type \u2018{0}\u2019.
 UnparsableStringForClass_3      = Le texte \u201c{1}\u201d n\u2019est pas reconnu comme un objet de type \u2018{0}\u2019, \u00e0 cause des caract\u00e8res \u201c{2}\u201d.
 UnsupportedImplementation_1     = Les instances de \u2018{0}\u2019 ne peuvent pas \u00eatre g\u00e9r\u00e9es parce que les impl\u00e9mentations arbitraires ne sont pas encore support\u00e9es.

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/Assert.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/Assert.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/Assert.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/Assert.java Mon Nov 19 16:34:02 2012
@@ -79,13 +79,7 @@ public strictfp class Assert extends org
      * @param actual   The actual string.
      */
     public static void assertMultilinesEquals(final String message, final CharSequence expected, final CharSequence actual) {
-        final CharSequence[] a1 = CharSequences.split(expected, '\n');
-        final CharSequence[] a2 = CharSequences.split(actual,   '\n');
-        final int length = Math.min(a1.length, a2.length);
-        for (int i=0; i<length; i++) {
-            assertEquals("Line " + (i+1) + ':', a1[i], a2[i]);
-        }
-        assertArrayEquals(message, a1, a2);
+        assertArrayEquals(message, CharSequences.split(expected, '\n'), CharSequences.split(actual, '\n'));
     }
 
     /**

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java Mon Nov 19 16:34:02 2012
@@ -66,6 +66,7 @@ import org.junit.runners.Suite;
   org.apache.sis.internal.util.X364Test.class,
   org.apache.sis.io.LineFormatterTest.class,
   org.apache.sis.io.TableFormatterTest.class,
+  org.apache.sis.util.collection.TreeTableFormatTest.class,
 
   // XML most basic types.
   org.apache.sis.xml.XLinkTest.class,

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DefaultTreeTableTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DefaultTreeTableTest.java?rev=1411285&r1=1411284&r2=1411285&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DefaultTreeTableTest.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DefaultTreeTableTest.java Mon Nov 19 16:34:02 2012
@@ -87,7 +87,7 @@ public final strictfp class DefaultTreeT
          * Create a first child node, which should be added automatically
          * to the root list of children.
          */
-        final DefaultTreeTable.Node node1 = new DefaultTreeTable.Node(root, -1);
+        final DefaultTreeTable.Node node1 = new DefaultTreeTable.Node(root);
         assertSame("Internal table sharing:",  table.columnIndices, node1.columnIndices);
         assertTrue("Initial children list:",   node1.getChildren().isEmpty());
         assertSame("Specified parent:",        root, node1.getParent());

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java?rev=1411285&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java Mon Nov 19 16:34:02 2012
@@ -0,0 +1,67 @@
+/*
+ * 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.collection;
+
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.type.SimpleInternationalString;
+
+
+/**
+ * A trivial implementation of {@link TableColumn} for {@link String} values.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+final class StringColumn implements TableColumn<String> {
+    /**
+     * The column header.
+     */
+    private final InternationalString header;
+
+    /**
+     * Creates a new column for the given header.
+     */
+    StringColumn(final String header) {
+        this.header = new SimpleInternationalString(header);
+    }
+
+    /**
+     * Returns the type of column values, which is {@link String}.
+     */
+    @Override
+    public Class<String> getElementType() {
+        return String.class;
+    }
+
+    /**
+     * Returns the column header.
+     */
+    @Override
+    public InternationalString getHeader() {
+        return header;
+    }
+
+    /**
+     * Returns a string representation o this column for debugging purpose.
+     */
+    @Override
+    public String toString() {
+        return header.toString();
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/StringColumn.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java?rev=1411285&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java Mon Nov 19 16:34:02 2012
@@ -0,0 +1,121 @@
+/*
+ * 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.collection;
+
+import java.text.ParseException;
+import org.junit.Test;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.test.DependsOn;
+
+import static org.apache.sis.test.Assert.*;
+import static org.apache.sis.internal.util.ColumnConstant.*;
+
+
+/**
+ * Tests the {@link TreeTableFormat} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.00)
+ * @version 0.3
+ * @module
+ */
+@DependsOn(org.apache.sis.io.TableFormatterTest.class)
+public final strictfp class TreeTableFormatTest extends TestCase {
+    /**
+     * Creates a node with a single column for object names.
+     */
+    private static DefaultTreeTable.Node createNode(final CharSequence name) {
+        return new DefaultTreeTable.Node(NAME_MAP, new CharSequence[] {name});
+    }
+
+    /**
+     * Tests the formatting as a tree, with control on the indentation.
+     */
+    @Test
+    public void testTreeFormat() {
+        final DefaultTreeTable.Node root   = createNode("Node #1");
+        final DefaultTreeTable.Node branch = createNode("Node #2");
+        root.getChildren().add(branch);
+        root.getChildren().add(createNode("Node #3"));
+        branch.getChildren().add(createNode("Node #4"));
+
+        final TreeTableFormat tf = new TreeTableFormat(null, null);
+        tf.setVerticalLinePosition(2);
+        assertMultilinesEquals(
+                "Node #1\n" +
+                "  ├─Node #2\n" +
+                "  │   └─Node #4\n" +
+                "  └─Node #3\n", tf.format(new DefaultTreeTable(root)));
+    }
+
+    /**
+     * Tests the parsing of a tree. This method parses and reformats a tree,
+     * and performs its check on the assumption that the tree formatting is
+     * accurate.
+     *
+     * @throws ParseException Should never happen.
+     */
+    @Test
+    public void testTreeParse() throws ParseException {
+        final TreeTableFormat tf = new TreeTableFormat(null, null);
+        tf.setVerticalLinePosition(0);
+        final String text =
+                "Node #1\n" +
+                "├───Node #2\n" +
+                "│   └───Node #4\n" +
+                "└───Node #3\n";
+        final TreeTable table = tf.parseObject(text);
+        assertMultilinesEquals(text, tf.format(table));
+    }
+
+    /**
+     * Tests the formatting of a tree table.
+     */
+    @Test
+    public void testTreeTableFormat() {
+        final StringColumn     valueA = new StringColumn("value #1");
+        final StringColumn     valueB = new StringColumn("value #2");
+        final DefaultTreeTable table  = new DefaultTreeTable(NAME, valueA, valueB);
+        final TreeTable.Node   root   = new DefaultTreeTable.Node(table);
+        root.setValue(NAME,   "Node #1");
+        root.setValue(valueA, "Value #1A");
+        root.setValue(valueB, "Value #1B");
+        final TreeTable.Node branch1 = new DefaultTreeTable.Node(table);
+        branch1.setValue(NAME,   "Node #2");
+        branch1.setValue(valueA, "Value #2A");
+        root.getChildren().add(branch1);
+        final TreeTable.Node branch2 = new DefaultTreeTable.Node(table);
+        branch2.setValue(NAME,   "Node #3");
+        branch2.setValue(valueB, "Value #3B");
+        root.getChildren().add(branch2);
+        final TreeTable.Node leaf = new DefaultTreeTable.Node(table);
+        leaf.setValue(NAME,   "Node #4");
+        leaf.setValue(valueA, "Value #4A");
+        leaf.setValue(valueB, "ext #4\tafter tab\nand a new line");
+        branch1.getChildren().add(leaf);
+        table.setRoot(root);
+
+        final TreeTableFormat tf = new TreeTableFormat(null, null);
+        tf.setVerticalLinePosition(1);
+        final String text = tf.format(table);
+        assertMultilinesEquals(
+                "Node #1…………………………Value #1A……Value #1B\n" +
+                " ├──Node #2………………Value #2A……\n" +
+                " │   └──Node #4……Value #4A……ext #4  after tab ¶ and a new line\n" +
+                " └──Node #3……………………………………………Value #3B\n", text);
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message