/* * Copyright 2009-2011 Collaborative Research Centre SFB 632 * * Licensed 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 annis; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TableFormatter { private final static Logger log = LoggerFactory.getLogger(TableFormatter.class); public String formatAsTable(List<?> list, String... fields) { if (list.isEmpty()) return "(empty)"; // special column handling for lists // discard fields argument and print all list entries instead boolean isList = list.get(0) instanceof List<?>; if (isList) fields = fieldNamesForListItems(list); if (fields.length == 0) return "(no columns to print)"; // turn vararg fields into list, because we may want to remove non-existing fields later List<String> columns = new ArrayList<>(Arrays.asList(fields)); // column sizes have to be pre-computed Map<String, Integer> columnSizes = new HashMap<>(); for (String column : columns) updateColumnSize(columnSizes, column, column); // loop through values to determine sizes // save values, while at it List<Map<String, String>> rows = new ArrayList<>(); for (Object item : list) { Map<String, String> row = new HashMap<>(); // again, special handling for lists if (isList) { List<?> listItem = (List<?>) item; for (int i = 0; i < listItem.size(); ++i) { String column = String.valueOf(i); String value = listItem.get(i).toString(); row.put("#" + column, value); updateColumnSize(columnSizes, column, value); } } else { for (String column : new ArrayList<>(columns)) { String value = fieldValue(item, column, columns); if (value == null) continue; row.put(column, value); updateColumnSize(columnSizes, column, value); } } rows.add(row); } // it is possible that every column was removed earlier, because it was not a field if (columns.isEmpty()) return "(no columns found)"; // print table StringBuffer sb = new StringBuffer(); printHeader(sb, columns, columnSizes); printValues(sb, rows, columns, columnSizes); return sb.toString(); } // determines the maximum size of the list entries and creates as many columns @SuppressWarnings("unchecked") private String[] fieldNamesForListItems(List<?> list) { int size = 0; for (List<?> item : (List<List<?>>) list) { size = Math.max(size, item.size()); } String[] fields = new String[size]; for (int i = 0; i < size; ++i) fields[i] = "#" + String.valueOf(i); return fields; } // updates the size of column with value.length private void updateColumnSize(Map<String, Integer> columnSizes, String column, String value) { int length = value.length(); if ( ! columnSizes.containsKey(column) || columnSizes.get(column) < length) columnSizes.put(column, length); } // get value of item.fieldName private String fieldValue(Object item, String fieldName, List<String> columns) { Class<?> clazz = item.getClass(); String value = null; try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); Object object = field.get(item); value = object != null ? object.toString() : ""; } catch (SecurityException e) { log.warn("can't access " + clazz.getName() + "." + fieldName, e); } catch (NoSuchFieldException e) { log.warn("Can't print " + clazz.getName() + "." + fieldName + ", because it does not exist.", e); columns.remove(fieldName); } catch (IllegalArgumentException e) { log.warn("can't access " + clazz.getName() + "." + fieldName, e); } catch (IllegalAccessException e) { log.warn("can't access " + clazz.getName() + "." + fieldName, e); } return value; } private void printHeader(StringBuffer sb, List<String> columns, Map<String, Integer> columnSizes) { for (String column : columns) { sb.append(pad(column, columnSizes.get(column))); sb.append(" | "); } sb.setLength(sb.length() - " | ".length()); sb.append("\n"); for (String column : columns) { for (int i = 0; i < columnSizes.get(column); ++i) sb.append("-"); sb.append("-+-"); } sb.setLength(sb.length() - "-+-".length()); sb.append("\n"); } private void printValues(StringBuffer sb, List<Map<String, String>> rows, List<String> columns, Map<String, Integer> columnSizes) { for (Map<String, String> row : rows) { for (String column : columns) { sb.append(pad(row.get(column), columnSizes.get(column))); sb.append(" | "); } sb.setLength(sb.length() - " | ".length()); sb.append("\n"); } } private String pad(Object o, int length) { String s = o != null ? o.toString() : ""; if (s.length() >= length) return s; StringBuffer padded = new StringBuffer(); for (int i = 0; i < length - s.length(); ++i) padded.append(" "); padded.append(s); return padded.toString(); } }