package cds.util; /* * This file is part of TAPLibrary. * * TAPLibrary 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, either version 3 of the License, or * (at your option) any later version. * * TAPLibrary 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. * * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS) */ import java.util.ArrayList; /** * An object of this class manages an ascii table: it receives lines to add, * made of columns separated by a given separator char. Columns can be aligned * (RIGHT, LEFT or CENTER) before display * @author Marc Wenger/CDS * @version 1.0 May 2008 Creation<br> * @version 1.1 May 2008 Fix a bug: lines are kept without a newline at the end<br> * @version 1.2 Jun 2008 Add a toString method (items aligned).<br> * Fix a bug in align() when the last line is not full */ public class AsciiTable { public static final int LEFT = 0; public static final int CENTER = 1; public static final int RIGHT = 2; private String SPACES = " "; // extendable (see addSpaces() method) private String HSEP = "--------------------"; private ArrayList<String> lines = new ArrayList<String>(); // list of lines private boolean empty = true; // as long as the list of lines is empty private boolean header = false; // the list begins with a header private String headerPostfix = null; // string to display after the header private int[] sizes; // size of the columns private char csep; // column separator (as char) private String sep; // column separator (as string) /** * Constructor * @param separ character defining the column separator in the input lines */ public AsciiTable(char separ){ csep = separ; sep = String.valueOf(csep); } /** * Add a header line. Several lines of header can be defined. * @param headerline header string */ public void addHeaderLine(String headerline){ header = true; addLine(headerline); } /** * Specifies that the header lines are finished. This call is mandatory * @param postfix String to append after the header lines (i.e. "\n") * @see #endHeaderLine() */ public void endHeaderLine(String postfix){ lines.add(null); headerPostfix = postfix; } /** * Specifies that the header lines are finished. This call is mandatory * @see #endHeaderLine(String) */ public void endHeaderLine(){ lines.add(null); headerPostfix = null; } /** * Add a line to the table * @param line string containing the line with all the columns separated by the column separator. * The line should not end up with a newline char. If it is the case, alignement errors can be experienced * depending on the alignement type of the last column. */ public void addLine(String line){ // compute the number of columns, if we add the first line if (empty){ int p = 0; int nbcol = 1; // at least one column (also: there is one separator less than columns) boolean done = false; while(!done){ p = line.indexOf(sep, p); if (p >= 0) nbcol++; else done = true; p++; } // initialize the result sizes = new int[nbcol]; for(int i = 0; i < sizes.length; i++){ sizes[i] = 0; } empty = false; } // get the max size for each column int p0, p1, col, colsize; p0 = 0; col = 0; while(p0 < line.length()){ p1 = line.indexOf(sep, p0); if (p1 < 0) p1 = line.length(); colsize = p1 - p0; sizes[col] = Math.max(sizes[col], colsize); p0 = p1 + 1; col++; } lines.add(line); } /** * Get all the lines without alignement, as they were entered * @return the array of the lines in the table */ public String[] displayRaw(){ return lines.toArray(new String[0]); } /** * Get all the lines without alignement, as they were entered, with separator control * @param newsep separator to use, replacing the original one * @return the array of the lines in the table */ public String[] displayRaw(char newsep){ if (newsep == csep) return displayRaw(); else{ String[] resu = new String[lines.size()]; for(int i = 0; i < resu.length; i++){ resu[i] = (lines.get(i)).replace(csep, newsep); } return resu; } } /** * Get all the lines in the table, properly aligned. * @param pos array of flags, indicating how each column should be justified. * The array must have as many columns as the table has. Each column can contain * either AsciiTable.LEFT, AsciiTable.CENTER or AsciiTable.RIGHT<br> * if the array contains ONE item, it will be used for every column. * @return an array of the table lines, aligned and justified */ public String[] displayAligned(int[] pos){ return align(pos, '\0'); } /** * Get all the lines in the table, properly aligned. * @param pos array of flags, indicating how each column should be justified. * The array must have as many columns as the table has. Each column can contain * either AsciiTable.LEFT, AsciiTable.CENTER or AsciiTable.RIGHT<br> * if the array contains ONE item, it will be used for every column. * @param newsep separator to use, replacing the original one * @return an array of the table lines, aligned and justified */ public String[] displayAligned(int[] pos, char newsep){ if (newsep == csep) newsep = '\0'; return align(pos, newsep); } /** * Get the array of lines in which all the columns are aligned * @param pos array of flags, indicating how each column should be justified. * The array must have as many columns as the table has. Each column can contain * either AsciiTable.LEFT, AsciiTable.CENTER or AsciiTable.RIGHT<br> * if the array contains ONE item, it will be used for every column. * @param newsep separator to use, replacing the original one (no replacement if '\0') * @return an array of the table lines, aligned and justified */ private String[] align(int[] pos, char newsep){ int nblines = lines.size(); String[] result = new String[nblines]; StringBuffer buf = new StringBuffer(); int p0, p1, col, fldsize, colsize, n1, inserted; boolean inHeader = header; // A header can contain several lines. The end is detected by a line // beginning by the separator char int uniqueJustif = pos.length == 1 ? pos[0] : -1; for(int i = 0; i < nblines; i++){ buf.delete(0, buf.length()); String line = lines.get(i); p0 = 0; col = 0; if (inHeader && line == null){ // end of the header: create the separator line for(int k = 0; k < sizes.length; k++){ if (k > 0) buf.append(csep); addHsep(buf, sizes[k]); } if (headerPostfix != null) buf.append(headerPostfix); inHeader = false; }else{ for(col = 0; col < sizes.length; col++){ if (col > 0) buf.append(sep); p1 = line.indexOf(sep, p0); if (p1 < 0) p1 = line.length(); fldsize = p1 - p0; if (fldsize < 0) break; colsize = sizes[col]; inserted = colsize - fldsize; if (inserted < 0) inserted = 0; int justif = inHeader ? CENTER : (uniqueJustif >= 0 ? uniqueJustif : pos[col]); switch(justif){ case LEFT: default: buf.append(line.substring(p0, p1)); addspaces(buf, inserted); break; case CENTER: n1 = (inserted) / 2; addspaces(buf, n1); buf.append(line.substring(p0, p1)); addspaces(buf, inserted - n1); break; case RIGHT: addspaces(buf, inserted); buf.append(line.substring(p0, p1)); break; } p0 = p1 + 1; } } result[i] = newsep != '\0' ? buf.toString().replace(csep, newsep) : buf.toString(); } return result; } /** * Add nb spaces to the stringbuffer * @param buf StringBuffer to modify * @param nb number of spaces to add */ private void addspaces(StringBuffer buf, int nb){ while(nb > SPACES.length()) SPACES = SPACES + SPACES; buf.append(SPACES.substring(0, nb)); } /** * Add horizontal separator chars to the stringbuffer * @param buf StringBuffer to modify * @param nb number of chars to add */ private void addHsep(StringBuffer buf, int nb){ while(nb > HSEP.length()) HSEP = HSEP + HSEP; buf.append(HSEP.substring(0, nb)); } /** * Display the whole table, with left alignement * @return the table as a unique string */ @Override public String toString(){ StringBuffer buf = new StringBuffer(); String[] ids = displayAligned(new int[]{AsciiTable.LEFT}); for(int i = 0; i < ids.length; i++){ if (i > 0) buf.append("\n"); buf.append(ids[i]); } return buf.toString(); } }