/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 1999-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.io;
import java.io.IOException;
import java.io.Writer;
import javax.swing.text.StyleConstants;
import org.apache.sis.io.TableAppender;
import org.apache.sis.io.IO;
import org.apache.sis.util.CharSequences;
import org.geotoolkit.lang.Decorator;
/**
* A character stream that can be used to format tables. Columns are separated by tabulations
* ({@code '\t'}) and rows are separated by line terminators ({@code '\r'}, {@code '\n'} or
* {@code "\r\n"}). Every table cells are stored in memory until {@link #flush()} is invoked.
* When invoked, {@link #flush()} copy cell contents to the underlying stream while replacing
* tabulations by some amount of spaces. The exact number of spaces is computed from cell widths.
* {@code TableWriter} produces correct output when displayed with a monospaced font.
* <p>
* For example, the following code...
*
* {@preformat java
* TableWriter out = new TableWriter(new OutputStreamWriter(System.out), 3);
* out.write("Prénom\tNom\n");
* out.nextLine('-');
* out.write("Idéphonse\tLaporte\nSarah\tCoursi\nYvan\tDubois");
* out.flush();
* }
*
* ...produces the following output:
*
* {@preformat text
* Prénom Nom
* --------- -------
* Idéphonse Laporte
* Sarah Coursi
* Yvan Dubois
* }
*
* @author Martin Desruisseaux (MPO, IRD)
* @version 3.00
*
* @since 1.0
* @module
*
* @deprecated Moved to Apache SIS as {@link TableAppender}.
*/
@Deprecated
@Decorator(Writer.class)
public class TableWriter extends FilterWriter {
/**
* A possible value for cell alignment. This specifies that the text is aligned
* to the left indent and extra whitespace should be placed on the right.
*/
public static final int ALIGN_LEFT = StyleConstants.ALIGN_LEFT;
/**
* A possible value for cell alignment. This specifies that the text is aligned
* to the right indent and extra whitespace should be placed on the left.
*/
public static final int ALIGN_RIGHT = StyleConstants.ALIGN_RIGHT;
/**
* A possible value for cell alignment. This specifies that the text is aligned
* to the center and extra whitespace should be placed equally on the left and right.
*/
public static final int ALIGN_CENTER = StyleConstants.ALIGN_CENTER;
/**
* A column separator for {@linkplain #TableWriter(Writer,String) constructor}.
*
* @since 2.5
*/
public static final String SINGLE_VERTICAL_LINE = " \u2502 ";
/**
* A column separator for {@linkplain #TableWriter(Writer,String) constructor}.
*
* @since 2.5
*/
public static final String DOUBLE_VERTICAL_LINE = " \u2551 ";
/**
* A line separator for {@link #nextLine(char)}.
*
* @since 2.5
*/
public static final char SINGLE_HORIZONTAL_LINE = '\u2500';
/**
* A line separator for {@link #nextLine(char)}.
*
* @since 2.5
*/
public static final char DOUBLE_HORIZONTAL_LINE = '\u2550';
/**
* The Apache SIS formatter on which to delegate the work.
*/
private final TableAppender formatter;
/**
* Workaround for RFE #4093999 ("Relax constraint on placement of this()/super()
* call in constructors")
*/
private TableWriter(final TableAppender formatter) {
super(IO.asWriter(formatter));
this.formatter = formatter;
}
/**
* Creates a new table writer with a default column separator. The default is a double
* vertical line for the left and right table borders, and a single horizontal line
* between the columns.
*
* {@note This writer may produce bad output on Windows console, unless the underlying stream
* use the correct codepage (e.g. <code>OutputStreamWriter(System.out, "Cp437")</code>). To
* display the appropriate codepage for a Windows console, type <code>chcp</code> on the
* command line.}
*
* @param out Writer object to provide the underlying stream, or {@code null} if there is no
* underlying stream. If {@code out} is null, then the {@link #toString} method is the
* only way to get the table content.
*/
public TableWriter(final Writer out) {
this(out != null ? new TableAppender(out) : new TableAppender());
}
/**
* Creates a new table writer with the specified amount of spaces as column separator.
*
* @param out Writer object to provide the underlying stream, or {@code null} if there is no
* underlying stream. If {@code out} is null, then the {@link #toString} method is the
* only way to get the table content.
* @param spaces Amount of white spaces to use as column separator.
*/
public TableWriter(final Writer out, final int spaces) {
this(out, CharSequences.spaces(spaces).toString());
}
/**
* Creates a new table writer with the specified column separator.
*
* @param out Writer object to provide the underlying stream, or {@code null} if there is no
* underlying stream. If {@code out} is null, then the {@link #toString} method is the
* only way to get the table content.
* @param separator String to write between columns. Drawing box characters are treated
* specially. For example {@code " \\u2502 "} can be used for a single-line box.
*
* @see #SINGLE_VERTICAL_LINE
* @see #DOUBLE_VERTICAL_LINE
*/
public TableWriter(final Writer out, final String separator) {
this(out != null ? new TableAppender(out, separator) : new TableAppender(separator));
}
/**
* Sets the desired behavior for EOL and tabulations characters.
* <ul>
* <li>If {@code true}, EOL (<code>'\r'</code>, <code>'\n'</code> or
* <code>"\r\n"</code>) and tabulations (<code>'\t'</code>) characters
* are copied straight into the current cell, which mean that next write
* operations will continue inside the same cell.</li>
* <li>If {@code false}, then tabulations move to next column and EOL move
* to the first cell of next row (i.e. tabulation and EOL are equivalent to
* {@link #nextColumn()} and {@link #nextLine()} calls respectively).</li>
* </ul>
* The default value is {@code false}.
*
* @param multiLines {@code true} true if EOL are used for line feeds inside
* current cells, or {@code false} if EOL move to the next row.
*/
public void setMultiLinesCells(final boolean multiLines) {
synchronized (lock) {
formatter.setMultiLinesCells(multiLines);
}
}
/**
* Tells if EOL characters are used for line feeds inside current cells.
*
* @return {@code true} if EOL characters are to be write inside the cell.
*/
public boolean isMultiLinesCells() {
synchronized (lock) {
return formatter.isMultiLinesCells();
}
}
/**
* Sets the alignment for all cells in the specified column. This method
* overwrite the alignment for all previous cells in the specified column.
*
* @param column The 0-based column number.
* @param alignment Cell alignment. Must be {@link #ALIGN_LEFT}
* {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER}.
*
* @deprecated Not effective anymore, because no equivalent method in SIS.
*/
@Deprecated
public void setColumnAlignment(final int column, final int alignment) {
// No corresponding method is SIS.
}
/**
* Sets the alignment for current and next cells. Change to the
* alignment doesn't affect the alignment of previous cells and
* previous rows. The default alignment is {@link #ALIGN_LEFT}.
*
* @param alignment Cell alignment. Must be {@link #ALIGN_LEFT}
* {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER}.
*/
public void setAlignment(final int alignment) {
final byte a;
switch (alignment) {
case ALIGN_LEFT: a = TableAppender.ALIGN_LEFT; break;
case ALIGN_RIGHT: a = TableAppender.ALIGN_RIGHT; break;
case ALIGN_CENTER: a = TableAppender.ALIGN_CENTER; break;
default: throw new IllegalArgumentException(String.valueOf(alignment));
}
synchronized (lock) {
formatter.setCellAlignment(a);
}
}
/**
* Returns the alignment for current and next cells.
*
* @return Cell alignment: {@link #ALIGN_LEFT} (the default),
* {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER}.
*/
public int getAlignment() {
final byte alignment;
synchronized (lock) {
alignment = formatter.getCellAlignment();
}
switch (alignment) {
case TableAppender.ALIGN_LEFT: return ALIGN_LEFT;
case TableAppender.ALIGN_RIGHT: return ALIGN_RIGHT;
case TableAppender.ALIGN_CENTER: return ALIGN_CENTER;
default: throw new IllegalArgumentException(String.valueOf(alignment));
}
}
/**
* Returns the number of rows in this table. This count is reset to 0 by {@link #flush}.
*
* @return The number of rows in this table.
*
* @since 2.5
*/
public int getRowCount() {
synchronized (lock) {
return formatter.getRowCount();
}
}
/**
* Returns the number of columns in this table.
*
* @return The number of columns in this table.
*
* @since 2.5
*/
public int getColumnCount() {
synchronized (lock) {
return formatter.getColumnCount();
}
}
/**
* Write a single character. If {@link #isMultiLinesCells()}
* is {@code false} (which is the default), then:
* <ul>
* <li>Tabulations (<code>'\t'</code>) are replaced by {@link #nextColumn()} invocations.</li>
* <li>Line separators (<code>'\r'</code>, <code>'\n'</code> or <code>"\r\n"</code>)
* are replaced by {@link #nextLine()} invocations.</li>
* </ul>
*
* @param c Character to write.
*/
@Override
public void write(final int c) {
try {
super.write(c);
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Writes a string. Tabulations and line separators are interpreted as by {@link #write(int)}.
*
* @param string String to write.
*/
@Override
public void write(final String string) {
try {
super.write(string);
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Writes a portion of a string. Tabulations and line
* separators are interpreted as by {@link #write(int)}.
*
* @param string String to write.
* @param offset Offset from which to start writing characters.
* @param length Number of characters to write.
*/
@Override
public void write(final String string, int offset, int length) {
try {
super.write(string, offset, length);
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Writes an array of characters. Tabulations and line
* separators are interpreted as by {@link #write(int)}.
*
* @param cbuf Array of characters to be written.
*/
@Override
public void write(final char[] cbuf) {
try {
super.write(cbuf);
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Writes a portion of an array of characters. Tabulations and
* line separators are interpreted as by {@link #write(int)}.
*
* @param cbuf Array of characters.
* @param offset Offset from which to start writing characters.
* @param length Number of characters to write.
*/
@Override
public void write(final char[] cbuf, int offset, int length) {
try {
super.write(cbuf, offset, length);
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Writes an horizontal separator.
*/
public void writeHorizontalSeparator() {
synchronized (lock) {
formatter.appendHorizontalSeparator();
}
}
/**
* Moves one column to the right. Next write operations will occur in a new cell on the
* same row.
*/
public void nextColumn() {
synchronized (lock) {
formatter.nextColumn();
}
}
/**
* Moves one column to the right. Next write operations will occur in a new cell on the
* same row. This method fill every remaining space in the current cell with the specified
* character. For example calling {@code nextColumn('*')} from the first character of a cell
* is a convenient way to put a pad value in this cell.
*
* @param fill Character filling the cell (default to whitespace).
*/
public void nextColumn(final char fill) {
synchronized (lock) {
formatter.nextColumn(fill);
}
}
/**
* Moves to the first column on the next row.
* Next write operations will occur on a new row.
*/
public void nextLine() {
synchronized (lock) {
formatter.nextLine();
}
}
/**
* Moves to the first column on the next row. Next write operations will occur on a new
* row. This method fill every remaining cell in the current row with the specified character.
* Calling {@code nextLine('-')} from the first column of a row is a convenient way to fill
* this row with a line separator.
*
* @param fill Character filling the rest of the line (default to whitespace).
* This character may be use as a row separator.
*
* @see #SINGLE_HORIZONTAL_LINE
* @see #DOUBLE_HORIZONTAL_LINE
*/
public void nextLine(final char fill) {
synchronized (lock) {
formatter.nextLine(fill);
}
}
/**
* Returns the table content as a string.
*/
@Override
public String toString() {
synchronized (lock) {
return formatter.toString();
}
}
/**
* For allowing other writers defined in this package to format the table content.
*/
@Override
final String content() {
return toString();
}
}