/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.common.table;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.printing.PrintDialog;
import org.eclipse.swt.printing.Printer;
import org.eclipse.swt.printing.PrinterData;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.teiid.designer.ui.common.InternalUiConstants;
/**
* TablePrinter
* (jh)
*
* @since 8.0
*/
public class TablePrinter {
private Table table;
private GC gc;
private FontData fontData;
private int iAverageCharWidth;
private int iColumnCharFudgeFactor;
private int iLineHeight;
private Rectangle clientArea;
private int iTableRowMargin;
private TableColumn[] columns;
private TableItem[] items;
// each entry in this array is an Arraylist containing a ColumnData for
// each contiguous column that will fit on page in one pass.
private ArrayList[] columnsPerSectionArray;
private int x;
private int y;
// for file save default delimiter to comma
private static final String TAB_DELIM_FILEEXT = ".txt"; //$NON-NLS-1$
private static final String COMMA_DELIM_FILEEXT = ".csv"; //$NON-NLS-1$
private String DEFAULT_FILEEXT = COMMA_DELIM_FILEEXT;
private static final String TAB_CHOOSER_FILEEXT_PATTERN = "*.txt"; //$NON-NLS-1$
private static final String COMMA_CHOOSER_FILEEXT_PATTERN = "*.csv"; //$NON-NLS-1$
private static final char NEWLINE = '\n';
private static final char TAB = '\t';
private static final char COMMA = ',';
private static final char PERIOD = '.';
private char chDelim = COMMA;
// display strings from i18n.properties
private static final String PRINT_JOB_STATUS
= InternalUiConstants.Util.getString("TablePrinter.jobStatus"); //$NON-NLS-1$
private static final String FILE_CHOOSER_DIALOG_TITLE
= InternalUiConstants.Util.getString("TablePrinter.fileChooserDialog.title"); //$NON-NLS-1$
private static final String DEFAULT_FILENAME
= InternalUiConstants.Util.getString("TablePrinter.defaultFileName.text"); //$NON-NLS-1$
private static final String CSV_EXTENSION_DESC
= InternalUiConstants.Util.getString("TablePrinter.csvExtensionDescription.text"); //$NON-NLS-1$
private static final String TAB_EXTENSION_DESC
= InternalUiConstants.Util.getString("TablePrinter.tabExtensionDescription.text"); //$NON-NLS-1$
/**
* Construct an instance of TablePrinter.
*
*/
public TablePrinter() {
super();
}
public void printTable( Table table ) {
this.table = table;
PrintDialog dialog = new PrintDialog( getShell() );
PrinterData data = dialog.open();
if ( data != null ) {
if ( data.printToFile ) {
printToFile( table );
} else {
printToPaper( table, data );
}
}
}
private Shell getShell() {
return table.getShell();
}
private void printToPaper( Table table, PrinterData data ) {
/*
* Strategy: Write only as many column headings as will fit in the page width,
* then write all rows for just those columns,
* then write the next set of columns,
* then write all rows for that set of columns,
* etc., etc.,...
*
*/
Printer printer = new Printer( data );
if ( printer.startJob( PRINT_JOB_STATUS ) ) {
// get a graphics context to write on
gc = new GC( printer );
// apply the font info from the table to the graphics context
fontData = table.getFont().getFontData()[0];
// create fonts to use later, based on the table font
Font fntStandard
= new Font( printer, fontData.getName(), fontData.getHeight(), fontData.getStyle() );
Font fntBold
= new Font( printer, fontData.getName(), fontData.getHeight(), SWT.BOLD );
// capture the average character width and other useful constants
iAverageCharWidth = gc.getFontMetrics().getAverageCharWidth();
iColumnCharFudgeFactor = 3;
iLineHeight = fontData.getHeight() * 4;
iTableRowMargin = iAverageCharWidth * 24;
// get the area to write on
clientArea = printer.getClientArea();
// get the column headings row and the data rows
columns = table.getColumns();
items = table.getItems();
// init x and y
x = clientArea.x;
y = clientArea.y;
// create the data structure that will drive the process. A 'section' is a set of
// contiguous columns that will fit in the page width.
createColumnsPerSectionArray();
// write to the gc, driven by the 'columns per section' array
for ( int iSection = 0; iSection < columnsPerSectionArray.length; iSection++ ) {
// get the Section ArrayList
ArrayList aryl = columnsPerSectionArray[ iSection ];
// The array of ArrayLists may have some empty entries; when we get to one
// we are done
if ( aryl == null ) {
break;
}
// ================
// HEADINGS
// ================
// set font bold for the headings
gc.setFont( fntBold );
// write the col headings
for ( int iEntry = 0; iEntry < aryl.size(); iEntry++ ) {
// get the next ColumnData
ColumnData cdColData = (ColumnData)aryl.get( iEntry );
// write the column heading
gc.drawText( columns[ cdColData.iIndex ].getText(), x, y );
// move x forward
x += cdColData.iWidth;
}
// skip a line
y += iLineHeight * 3;
// reset x
x = clientArea.x;
// ================
// ROWS
// ================
// return font to standard
gc.setFont( fntStandard );
// write the rows
for (int row = 0; row < items.length; row++) {
for ( int iEntry2 = 0; iEntry2 < aryl.size(); iEntry2++ ) {
// get the column data
ColumnData cdColData = (ColumnData)aryl.get( iEntry2 );
// write the column data (a table cell)
gc.drawText( items[row].getText( cdColData.iIndex ), x, y );
// move x forward
x += cdColData.iWidth;
}
// reset x
x = clientArea.x;
// move y forward
y += iLineHeight * 3;
}
// end of section...
// reset x
x = clientArea.x;
// skip 2 lines
y += iLineHeight * 3;
y += iLineHeight * 3;
}
// end
printer.endJob();
}
}
private int calcColumnWidth( int iColumn ) {
// init to width of the column header
int iGreatestColWidth = columns[ iColumn ].getText().length();
String sText = ""; //$NON-NLS-1$
// examine the width of the cells in this column, looking for largest one
for( int iRow = 0; iRow < items.length; iRow++ ) {
sText = items[ iRow ].getText( iColumn );
if ( sText.length() > iGreatestColWidth ) {
iGreatestColWidth = sText.length();
}
}
// convert to pixel count width
iGreatestColWidth = iAverageCharWidth * ( iGreatestColWidth + iColumnCharFudgeFactor );
return iGreatestColWidth;
}
private ArrayList[] createColumnsPerSectionArray() {
int iCurrentSection = 0;
int iCurrentX = 0;
// create an array of ArrayLists
columnsPerSectionArray = new ArrayList[ columns.length ];
// add first ArrayList to it
columnsPerSectionArray[ iCurrentSection ] = new ArrayList();
// walk the columns
for (int iColIndex = 0; iColIndex < columns.length; iColIndex++) {
// calc max width of this col
int iColWidth = calcColumnWidth( iColIndex );
// calc proposed new x value (after adding the new col)
int iNewX = iCurrentX + iColWidth;
// test to see if new col will fit and take approproiate action
if ( iNewX > ( clientArea.width - iTableRowMargin ) ) {
// this col will NOT fit, so end this section and move on
iCurrentSection++;
columnsPerSectionArray[ iCurrentSection ] = new ArrayList();
columnsPerSectionArray[ iCurrentSection ].add( new ColumnData( iColIndex, iColWidth ) );
iCurrentX = 0;
} else {
// this one will fit; add it to current section and increment the current X
columnsPerSectionArray[ iCurrentSection ].add( new ColumnData( iColIndex, iColWidth ) );
iCurrentX += iColWidth;
}
}
return columnsPerSectionArray;
}
/*
*
*/
public void printToFile( Table table /*, IFile file*/ ) {
columns = table.getColumns();
items = table.getItems();
char chDelim = getDelimiter();
StringBuffer sbText = new StringBuffer();
FileWriter fwFileWriter = null;
BufferedWriter bwOutWriter = null;
PrintWriter pwPrintWriter = null;
try {
// get the target file and path from user; this will also tell us what delim to use
String sFileSpec = getTargetPath();
// quit now if user quit the dialog
if ( sFileSpec == null ) { return; }
// otherwise continue...
if ( sFileSpec.endsWith( COMMA_DELIM_FILEEXT ) ) {
chDelim = COMMA;
} else {
chDelim = TAB;
}
// construct the header row
for (int col = 0; col < columns.length; col++) {
// if not the first col, add delim after the prev col
if ( col > 0 ) {
sbText.append( chDelim );
}
// add the next col
// System.out.println( "[TablePrinter.printToFile] about to add: " + columns[col].getText() );
sbText.append( columns[col].getText() );
}
sbText.append( NEWLINE );
// construct and write out the remaining rows
for ( int row = 0; row < items.length; row++ ) {
for ( int col = 0; col < columns.length; col++ ) {
// if not the first col, add delim after the prev col
if ( col > 0 ) {
sbText.append( chDelim );
}
// add the next col
// System.out.println( "[TablePrinter.printToFile] about to add: " + items[row].getText(col) );
sbText.append( items[row].getText(col) );
}
sbText.append( NEWLINE );
}
// save the file
// System.out.println( "[TablePrinter.printToFile] about to create FileWriter with path: " + sFileSpec ); //$NON-NLS-1$
fwFileWriter = new FileWriter( sFileSpec );
bwOutWriter = new BufferedWriter( fwFileWriter );
pwPrintWriter = new PrintWriter( bwOutWriter );
pwPrintWriter.write( sbText.toString() );
} catch ( IOException ioe ) {
InternalUiConstants.Util.log( ioe );
} finally{
if ( pwPrintWriter != null ) {
pwPrintWriter.close();
}
try{
if ( bwOutWriter != null ) {
bwOutWriter.close();
}
} catch(java.io.IOException e){}
try{
if ( fwFileWriter != null ) {
fwFileWriter.close();
}
} catch(java.io.IOException e){}
}
}
public void setDelimiter( char chDelim ) {
this.chDelim = chDelim;
if ( this.chDelim == COMMA ) {
DEFAULT_FILEEXT = COMMA_DELIM_FILEEXT;
}
if ( this.chDelim == TAB ) {
DEFAULT_FILEEXT = TAB_DELIM_FILEEXT;
}
}
public char getDelimiter() {
return chDelim;
}
/**
* Use file chooser dialog to let user choose a filename and path in which to save the file
*/
public String getTargetPath() {
FileDialog dlg = new FileDialog( getShell(), SWT.SAVE | SWT.SINGLE );
dlg.setFilterExtensions( new String[]{ COMMA_CHOOSER_FILEEXT_PATTERN,
TAB_CHOOSER_FILEEXT_PATTERN } );
dlg.setFilterNames( new String[]{ CSV_EXTENSION_DESC,
TAB_EXTENSION_DESC } );
dlg.setText( FILE_CHOOSER_DIALOG_TITLE );
dlg.setFileName( DEFAULT_FILENAME + DEFAULT_FILEEXT );
// present the dialog
String sPath = dlg.open();
// If there is no file extension, add the default file extension
if( sPath != null && sPath.indexOf( PERIOD ) == -1 ) {
sPath += DEFAULT_FILEEXT;
}
return sPath;
}
// =========================================
// Inner class: ColumnData
// =========================================
class ColumnData {
public int iIndex;
public int iWidth;
public ColumnData( int iIndex, int iWidth ) {
this.iIndex = iIndex;
this.iWidth = iWidth;
}
}
}