blob: 7e38da6b615114aa9cac7b9a97f69c6cf1483002 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2021 SAP AG and IBM Corporation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation
*******************************************************************************/
package org.eclipse.mat.report.internal;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.mat.query.refined.Filter;
/**
*/
public abstract class TextEmitter
{
protected static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
protected static final String COLUMN_SEPARATOR = "|"; //$NON-NLS-1$
private Map<Integer, Integer> columnLengths = new HashMap<Integer, Integer>();
protected int order[];
protected int align[];
/**
* Append content to be copied. Implementations should put the content to
* their own storage - buffer, file, etc...
*
* @param string
*/
protected abstract void append(String string);
/**
* The method is called at the end of the copy procedure. It can be used by
* implementations to flush content, move to clipboard, etc...
*/
protected abstract void done();
protected abstract Object[] getItems();
protected abstract Object[] getColumns();
protected abstract boolean shouldAddNextLine(Object item);
protected abstract String getItemValue(Object item, int columnIndex);
protected abstract boolean shouldSuppressLineBreak(Object item);
protected abstract void findColumnOrderAndAlignment(Object[] columns);
protected abstract String getColumnName(Object column);
protected abstract int getColumnLength(Object[] items, Object[] objColumns, int columnNumber);
protected abstract boolean shouldProcessChild(Object child);
protected abstract String getDisplayableRowValue(Object item);
protected abstract String getDisplayableColumnValue(Object item, int index);
protected abstract String getDisplayableColumnValueInSimpleStructure(Object item);
protected abstract boolean isExpanded(Object item);
protected abstract Object[] getChildren(Object item);
protected abstract boolean isAlignmentRight(int alignment);
protected abstract boolean isAlignmentCenter(int alignment);
public void doCopy()
{
Object[] items = getItems();
Object[] columns = getColumns();
int numberOfColumns = columns.length;
if (numberOfColumns == 0)
{
copySimpleStructure(items);
}
else
{
findColumnOrderAndAlignment(columns);
// find the length of columns by running through all the
// entries and finding the longest one
for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++)
{
int columnLength = getColumnLength(items, columns, columnIndex);
Integer existingLength = columnLengths.get(columnIndex);
if (existingLength == null || columnLength > existingLength)
{
columnLengths.put(columnIndex, columnLength);
}
}
// add column names to the result buffer
for (int i = 0; i < order.length; i++)
{
int col = order[i];
if (i != 0)
append(COLUMN_SEPARATOR);
append(align(getColumnName(columns[col]), align[col], columnLengths.get(col),
i + 1 == numberOfColumns));
}
append(LINE_SEPARATOR);
String dashedLine = getDashedLine(numberOfColumns);
append(dashedLine + LINE_SEPARATOR);
for (Object item : items)
{
boolean addLineBreak = true;
for (int i = 0; i < order.length; i++)
{
int columnIndex = order[i];
if (shouldSuppressLineBreak(item))
{
addLineBreak = false;
break;
}
String value = getItemValue(item, columnIndex);
for (String filterName : Filter.FILTER_TYPES)
{
if (value.equals(filterName))
value = "";//$NON-NLS-1$
}
if (i != 0)
append(COLUMN_SEPARATOR);
append(align(value, align[columnIndex], columnLengths.get(columnIndex), i + 1 == numberOfColumns));
}
if (addLineBreak)
append(LINE_SEPARATOR);
if (shouldAddNextLine(item))
{
addNextLine(new StringBuilder(), item, numberOfColumns, columnLengths.get(order[0]));
}
}
columnLengths.clear();
append(dashedLine + LINE_SEPARATOR);
}
done();
}
protected StringBuilder getLevel(StringBuilder level, int length, int counter)
{
int k = level.indexOf("'-"); //$NON-NLS-1$
if (k != -1)
{
level.replace(k, k + 2, " "); //$NON-NLS-1$
}
else
{
k = level.indexOf("|-"); //$NON-NLS-1$
if (k != -1)
{
level.replace(k + 1, k + 2, " "); //$NON-NLS-1$
}
}
if (counter == length - 1)
{
level.append('\'');
}
else
{
level.append('|');
}
level.append("- "); //$NON-NLS-1$
return level;
}
private void addNextLine(StringBuilder level, Object item, int numberOfColumns, int length)
{
Object[] children = getChildren(item);
if (children != null)
{
for (int j = 0; j < children.length; j++)
{
level = getLevel(level, children.length, j);
if (shouldProcessChild(children[j]))
{
if (numberOfColumns < 1)
{
// no append of empty spaces needed
append(level.toString());
append(getDisplayableRowValue(children[j]));
}
else
{
int col = order[0];
// add the first column with the tree branches prefix
append(level.toString());
append(align(getDisplayableColumnValue(children[j], col), align[col], length - level.length(),
numberOfColumns == 1));
// add the rest of the columns
for (int i = 1; i < order.length; i++)
{
col = order[i];
append(COLUMN_SEPARATOR);
append(align(getDisplayableColumnValue(children[j], col), align[col],
columnLengths.get(col), i + 1 == numberOfColumns));
}
}
append(LINE_SEPARATOR);
if (isExpanded(children[j]))
{
addNextLine(level, children[j], numberOfColumns, length);
}
if (level.length() >= 3)
level.delete(level.length() - 3, level.length());
}
}
}
}
protected void copySimpleStructure(Object[] items)
{
for (Object item : items)
{
boolean addLineBreak = true;
if (shouldSuppressLineBreak(item))
{
addLineBreak = false;
continue;
}
String value = getDisplayableColumnValueInSimpleStructure(item);
append(value);
if (addLineBreak)
append(LINE_SEPARATOR);
if (shouldAddNextLine(item))
{
addNextLine(new StringBuilder(), item, 0, getDisplayableRowValue(item).length());
}
}
}
/**
* Align a string in a field. Right aligned fields have a space either side
* of the longest entry
*
* @param s
* the data
* @param alignment
* An integer representing alignment which is seeded by the
* subclass as part of
* {@link #findColumnOrderAndAlignment(Object[])} and interpreted
* using {@link #isAlignmentRight(int)} and
* {@link #isAlignmentCenter(int)}
* @param length
* the size of the field
* @param last
* whether this is the last field in a line, so trailing spaces
* can be omitted
* @return
*/
protected String align(String s, int alignment, int length, boolean last)
{
StringBuilder buf = new StringBuilder(length);
if (s == null)
{
s = ""; //$NON-NLS-1$
if (last)
return s;
}
if (s.length() > length)
{
// For right align, we can fit a slightly bigger string
if (isAlignmentRight(alignment) && s.length() == length + 1)
s = s + ' ';
return s;
}
int blanks = length - s.length();
int left;
int right;
if (isAlignmentRight(alignment))
{
// For right align have a space either side for readability
left = blanks + 1;
right = 1;
}
else if (isAlignmentCenter(alignment))
{
left = blanks / 2;
right = blanks - left;
}
else
{
left = 0;
right = blanks;
}
for (int i = 0; i < left; i++)
buf.append(' ');
buf.append(s);
if (!last)
{
for (int i = 0; i < right; i++)
buf.append(' ');
}
return buf.toString();
}
protected String getDashedLine(int numberOfColumns)
{
StringBuilder dashes = new StringBuilder();
int dashesLength = 0;
for (int i = 0; i < order.length; i++)
{
int col = order[i];
// column separator
if (i != 0)
dashesLength += COLUMN_SEPARATOR.length();
dashesLength = dashesLength + align(null, align[col], columnLengths.get(col), false).length();
}
// length of all the columns included empty spaces
for (int i = 0; i < dashesLength; i++)
dashes.append('-');
return dashes.toString();
}
}