first working version v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sun, 22 Dec 2013 23:31:55 +0100
branchv_0
changeset 349335cf31c0f2
parent 33 04db6ccd6c48
child 35 b2ff3b2d58b2
first working version
java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java
java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java
java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java
java/sql-dk/src/info/globalcode/sql/dk/Functions.java
java/sql-dk/src/info/globalcode/sql/dk/SQLCommand.java
java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNamed.java
java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNumbered.java
java/sql-dk/src/info/globalcode/sql/dk/configuration/FormatterDefinition.java
java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractFormatter.java
java/sql-dk/src/info/globalcode/sql/dk/formatting/Formatter.java
java/sql-dk/src/info/globalcode/sql/dk/formatting/FormatterException.java
java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java
     1.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java	Sun Dec 22 22:02:44 2013 +0100
     1.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java	Sun Dec 22 23:31:55 2013 +0100
     1.3 @@ -21,6 +21,7 @@
     1.4  import static info.globalcode.sql.dk.Functions.isEmpty;
     1.5  import static info.globalcode.sql.dk.Functions.equalz;
     1.6  import info.globalcode.sql.dk.SQLCommand.COMMAND_TYPE;
     1.7 +import java.io.OutputStream;
     1.8  import java.util.ArrayList;
     1.9  import java.util.Collection;
    1.10  import java.util.EnumSet;
    1.11 @@ -59,7 +60,7 @@
    1.12  		CONNECTION
    1.13  	}
    1.14  	private COMMAND_TYPE commandType;
    1.15 -	private final Collection<NamedParameter> namedParameters = new ArrayList<>();
    1.16 +	private final List<NamedParameter> namedParameters = new ArrayList<>();
    1.17  	private final List<Parameter> numberedParameters = new ArrayList<>();
    1.18  	private final EnumSet<INFO_TYPE> showInfo = EnumSet.noneOf(INFO_TYPE.class);
    1.19  
    1.20 @@ -207,4 +208,16 @@
    1.21  	public void setDatabaseNameToTest(String databaseNameToTest) {
    1.22  		this.databaseNameToTest = databaseNameToTest;
    1.23  	}
    1.24 +
    1.25 +	public SQLCommand getSQLCommand() {
    1.26 +		if (namedParameters.isEmpty()) {
    1.27 +			return new SQLCommandNumbered(commandType, sql, numberedParameters);
    1.28 +		} else {
    1.29 +			return new SQLCommandNamed(commandType, sql, namedParameters);
    1.30 +		}
    1.31 +	}
    1.32 +
    1.33 +	public OutputStream getOutputStream() {
    1.34 +		return System.out;
    1.35 +	}
    1.36  }
     2.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java	Sun Dec 22 22:02:44 2013 +0100
     2.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java	Sun Dec 22 23:31:55 2013 +0100
     2.3 @@ -21,7 +21,13 @@
     2.4  import info.globalcode.sql.dk.CLIOptions.MODE;
     2.5  import info.globalcode.sql.dk.configuration.Configuration;
     2.6  import info.globalcode.sql.dk.configuration.ConfigurationException;
     2.7 +import info.globalcode.sql.dk.configuration.DatabaseDefinition;
     2.8 +import info.globalcode.sql.dk.configuration.FormatterDefinition;
     2.9 +import info.globalcode.sql.dk.formatting.Formatter;
    2.10 +import info.globalcode.sql.dk.formatting.FormatterContext;
    2.11 +import info.globalcode.sql.dk.formatting.FormatterException;
    2.12  import java.io.IOException;
    2.13 +import java.sql.SQLException;
    2.14  import java.util.logging.Level;
    2.15  import java.util.logging.Logger;
    2.16  import javax.xml.bind.JAXBContext;
    2.17 @@ -54,6 +60,12 @@
    2.18  			log.log(Level.SEVERE, "Unable to parse CLI options", e);
    2.19  		} catch (InvalidOptionsException e) {
    2.20  			log.log(Level.SEVERE, "Invalid CLI options", e);
    2.21 +		} catch (ConfigurationException e) {
    2.22 +			log.log(Level.SEVERE, "Configuration problem", e);
    2.23 +		} catch (SQLException e) {
    2.24 +			log.log(Level.SEVERE, "SQL problem", e);
    2.25 +		} catch (FormatterException e) {
    2.26 +			log.log(Level.SEVERE, "Formatting problem", e);
    2.27  		}
    2.28  	}
    2.29  
    2.30 @@ -61,7 +73,7 @@
    2.31  		this.options = options;
    2.32  	}
    2.33  
    2.34 -	private void process() {
    2.35 +	private void process() throws ConfigurationException, SQLException, FormatterException {
    2.36  		/** Show info */
    2.37  		if (!options.getShowInfo().isEmpty()) {
    2.38  			InfoLister infoLister = new InfoLister(System.err, this);
    2.39 @@ -71,10 +83,13 @@
    2.40  		MODE mode = options.getMode();
    2.41  		switch (mode) {
    2.42  			case QUERY_NOW:
    2.43 +				processQueryNow();
    2.44  				break;
    2.45  			case PREPARE_BATCH:
    2.46 +				processPrepareBatch();
    2.47  				break;
    2.48  			case EXECUTE_BATCH:
    2.49 +				processExecuteBatch();
    2.50  				break;
    2.51  			case JUST_SHOW_INFO:
    2.52  				// already done above
    2.53 @@ -85,6 +100,28 @@
    2.54  		}
    2.55  	}
    2.56  
    2.57 +	private void processQueryNow() throws ConfigurationException, SQLException, FormatterException {
    2.58 +		DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
    2.59 +		if (dd == null) {
    2.60 +			throw new ConfigurationException("Database is not configured: " + options.getDatabaseName());
    2.61 +		} else {
    2.62 +			FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
    2.63 +			if (fd == null) {
    2.64 +				throw new ConfigurationException("Formatter is not configured: " + options.getDatabaseName());
    2.65 +			} else {
    2.66 +				DatabaseConnection c = dd.connect();
    2.67 +				Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream()));
    2.68 +				c.executeQuery(options.getSQLCommand(), f);
    2.69 +			}
    2.70 +		}
    2.71 +	}
    2.72 +
    2.73 +	private void processPrepareBatch() {
    2.74 +	}
    2.75 +
    2.76 +	private void processExecuteBatch() {
    2.77 +	}
    2.78 +
    2.79  	@Override
    2.80  	public Configuration getConfiguration() throws ConfigurationException {
    2.81  		if (configuration == null) {
    2.82 @@ -93,7 +130,7 @@
    2.83  		return configuration;
    2.84  	}
    2.85  
    2.86 -	private void installDefaultConfiguration() {
    2.87 +	private void installDefaultConfiguration() throws ConfigurationException {
    2.88  		Constants.DIR.mkdir();
    2.89  
    2.90  		if (Constants.CONFIG_FILE.exists()) {
    2.91 @@ -102,10 +139,9 @@
    2.92  			try {
    2.93  				Functions.installResource(Constants.EXAMPLE_CONFIG_FILE, Constants.CONFIG_FILE);
    2.94  			} catch (IOException e) {
    2.95 -				log.log(Level.SEVERE, "Unable to write example configuration to " + Constants.CONFIG_FILE, e);
    2.96 +				throw new ConfigurationException("Unable to write example configuration to " + Constants.CONFIG_FILE, e);
    2.97  			}
    2.98  		}
    2.99 -
   2.100  	}
   2.101  
   2.102  	private Configuration loadConfiguration() throws ConfigurationException {
     3.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java	Sun Dec 22 22:02:44 2013 +0100
     3.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java	Sun Dec 22 23:31:55 2013 +0100
     3.3 @@ -19,6 +19,7 @@
     3.4  
     3.5  import info.globalcode.sql.dk.batch.Batch;
     3.6  import info.globalcode.sql.dk.configuration.DatabaseDefinition;
     3.7 +import info.globalcode.sql.dk.formatting.ColumnsHeader;
     3.8  import info.globalcode.sql.dk.formatting.Formatter;
     3.9  import java.sql.Connection;
    3.10  import java.sql.DriverManager;
    3.11 @@ -38,7 +39,7 @@
    3.12  	public DatabaseConnection(DatabaseDefinition databaseDefinition) throws SQLException {
    3.13  		this.databaseDefinition = databaseDefinition;
    3.14  
    3.15 -		connection = DriverManager.getConnection(databaseDefinition.getUrl(), databaseDefinition.getName(), databaseDefinition.getPassword());
    3.16 +		connection = DriverManager.getConnection(databaseDefinition.getUrl(), databaseDefinition.getUserName(), databaseDefinition.getPassword());
    3.17  	}
    3.18  
    3.19  	public void executeQuery(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
    3.20 @@ -72,7 +73,7 @@
    3.21  	private void processQueryCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
    3.22  		formatter.writeStartResultSet();
    3.23  		formatter.writeQuery(sqlCommand.getQuery());
    3.24 -		/** TODO: formatter.writeParameters(null); */
    3.25 +		formatter.writeParameters(sqlCommand.getParameters());
    3.26  		try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
    3.27  			sqlCommand.parametrize(ps);
    3.28  			try (ResultSet rs = ps.executeQuery()) {
    3.29 @@ -86,7 +87,7 @@
    3.30  	private void processUpdateCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
    3.31  		formatter.writeStartUpdatesResult();
    3.32  		formatter.writeQuery(sqlCommand.getQuery());
    3.33 -		/** TODO: formatter.writeParameters(null); */
    3.34 +		formatter.writeParameters(sqlCommand.getParameters());
    3.35  		try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
    3.36  			sqlCommand.parametrize(ps);
    3.37  			int updatedRowsCount = ps.executeUpdate();
    3.38 @@ -104,12 +105,18 @@
    3.39  	}
    3.40  
    3.41  	private void processResultSet(ResultSet rs, Formatter formatter) throws SQLException {
    3.42 -		/** TODO: formatter.writeColumnsHeader(null); */
    3.43 +		formatter.writeColumnsHeader(new ColumnsHeader(rs.getMetaData()));
    3.44 +		int columnCount = rs.getMetaData().getColumnCount();
    3.45 +
    3.46  		while (rs.next()) {
    3.47  			formatter.writeStartRow();
    3.48  
    3.49 -			/** TODO: formatter.writeColumnValue(rs.get); */
    3.50 +			for (int i = 1; i <= columnCount; i++) {
    3.51 +				formatter.writeColumnValue(rs.getObject(i));
    3.52 +			}
    3.53 +
    3.54  			formatter.writeEndRow();
    3.55  		}
    3.56 +
    3.57  	}
    3.58  }
     4.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/Functions.java	Sun Dec 22 22:02:44 2013 +0100
     4.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/Functions.java	Sun Dec 22 23:31:55 2013 +0100
     4.3 @@ -26,7 +26,6 @@
     4.4  import java.util.ArrayList;
     4.5  import java.util.Collection;
     4.6  import java.util.Map;
     4.7 -import java.util.logging.Level;
     4.8  
     4.9  /**
    4.10   *
     5.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/SQLCommand.java	Sun Dec 22 22:02:44 2013 +0100
     5.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/SQLCommand.java	Sun Dec 22 23:31:55 2013 +0100
     5.3 @@ -19,6 +19,8 @@
     5.4  
     5.5  import java.sql.Connection;
     5.6  import java.sql.PreparedStatement;
     5.7 +import java.sql.SQLException;
     5.8 +import java.util.List;
     5.9  
    5.10  /**
    5.11   *
    5.12 @@ -29,9 +31,16 @@
    5.13  	private COMMAND_TYPE commandType;
    5.14  	private String query;
    5.15  
    5.16 -	public abstract PreparedStatement prepareStatement(Connection c);
    5.17 +	public SQLCommand(COMMAND_TYPE commandType, String query) {
    5.18 +		this.commandType = commandType;
    5.19 +		this.query = query;
    5.20 +	}
    5.21  
    5.22 -	public abstract void parametrize(PreparedStatement ps);
    5.23 +	public abstract PreparedStatement prepareStatement(Connection c) throws SQLException;
    5.24 +
    5.25 +	public abstract void parametrize(PreparedStatement ps) throws SQLException;
    5.26 +
    5.27 +	public abstract List<? extends Parameter> getParameters();
    5.28  
    5.29  	public COMMAND_TYPE getCommandType() {
    5.30  		return commandType;
     6.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNamed.java	Sun Dec 22 22:02:44 2013 +0100
     6.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNamed.java	Sun Dec 22 23:31:55 2013 +0100
     6.3 @@ -19,6 +19,8 @@
     6.4  
     6.5  import java.sql.Connection;
     6.6  import java.sql.PreparedStatement;
     6.7 +import java.sql.SQLException;
     6.8 +import java.util.List;
     6.9  
    6.10  /**
    6.11   *
    6.12 @@ -26,13 +28,25 @@
    6.13   */
    6.14  public class SQLCommandNamed extends SQLCommand {
    6.15  
    6.16 +	private List<NamedParameter> parameters;
    6.17 +
    6.18 +	public SQLCommandNamed(COMMAND_TYPE commandType, String query, List<NamedParameter> parameters) {
    6.19 +		super(commandType, query);
    6.20 +		this.parameters = parameters;
    6.21 +	}
    6.22 +
    6.23  	@Override
    6.24 -	public PreparedStatement prepareStatement(Connection c) {
    6.25 +	public PreparedStatement prepareStatement(Connection c) throws SQLException {
    6.26  		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    6.27  	}
    6.28  
    6.29  	@Override
    6.30 -	public void parametrize(PreparedStatement ps) {
    6.31 +	public void parametrize(PreparedStatement ps) throws SQLException {
    6.32  		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    6.33  	}
    6.34 +
    6.35 +	@Override
    6.36 +	public List<NamedParameter> getParameters() {
    6.37 +		return parameters;
    6.38 +	}
    6.39  }
     7.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNumbered.java	Sun Dec 22 22:02:44 2013 +0100
     7.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNumbered.java	Sun Dec 22 23:31:55 2013 +0100
     7.3 @@ -17,8 +17,11 @@
     7.4   */
     7.5  package info.globalcode.sql.dk;
     7.6  
     7.7 +import static info.globalcode.sql.dk.Functions.notNull;
     7.8  import java.sql.Connection;
     7.9  import java.sql.PreparedStatement;
    7.10 +import java.sql.SQLException;
    7.11 +import java.util.List;
    7.12  
    7.13  /**
    7.14   *
    7.15 @@ -26,13 +29,28 @@
    7.16   */
    7.17  public class SQLCommandNumbered extends SQLCommand {
    7.18  
    7.19 -	@Override
    7.20 -	public PreparedStatement prepareStatement(Connection c) {
    7.21 -		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    7.22 +	private List<Parameter> parameters;
    7.23 +
    7.24 +	public SQLCommandNumbered(COMMAND_TYPE commandType, String query, List<Parameter> parameters) {
    7.25 +		super(commandType, query);
    7.26 +		this.parameters = parameters;
    7.27  	}
    7.28  
    7.29  	@Override
    7.30 -	public void parametrize(PreparedStatement ps) {
    7.31 -		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    7.32 +	public PreparedStatement prepareStatement(Connection c) throws SQLException {
    7.33 +		return c.prepareStatement(getQuery());
    7.34 +	}
    7.35 +
    7.36 +	@Override
    7.37 +	public void parametrize(PreparedStatement ps) throws SQLException {
    7.38 +		int i = 1;
    7.39 +		for (Parameter p : notNull(parameters)) {
    7.40 +			ps.setObject(i++, p.getValue(), p.getType());
    7.41 +		}
    7.42 +	}
    7.43 +
    7.44 +	@Override
    7.45 +	public List<Parameter> getParameters() {
    7.46 +		return parameters;
    7.47  	}
    7.48  }
     8.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/FormatterDefinition.java	Sun Dec 22 22:02:44 2013 +0100
     8.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/configuration/FormatterDefinition.java	Sun Dec 22 23:31:55 2013 +0100
     8.3 @@ -21,6 +21,7 @@
     8.4  import info.globalcode.sql.dk.DKException;
     8.5  import info.globalcode.sql.dk.formatting.Formatter;
     8.6  import info.globalcode.sql.dk.formatting.FormatterContext;
     8.7 +import info.globalcode.sql.dk.formatting.FormatterException;
     8.8  import java.lang.reflect.Constructor;
     8.9  import java.lang.reflect.InvocationTargetException;
    8.10  import javax.xml.bind.annotation.XmlElement;
    8.11 @@ -76,7 +77,7 @@
    8.12  	 * @return
    8.13  	 * @throws DKException
    8.14  	 */
    8.15 -	public Formatter getInstance(FormatterContext context) throws DKException {
    8.16 +	public Formatter getInstance(FormatterContext context) throws FormatterException {
    8.17  		try {
    8.18  			Constructor constructor = Class.forName(className).getConstructor(context.getClass());
    8.19  
    8.20 @@ -84,14 +85,14 @@
    8.21  			if (instance instanceof Formatter) {
    8.22  				return (Formatter) instance;
    8.23  			} else {
    8.24 -				throw new DKException("Formatter " + instance + " does not implement the " + Formatter.class.getName() + " interface");
    8.25 +				throw new FormatterException("Formatter " + instance + " does not implement the " + Formatter.class.getName() + " interface");
    8.26  			}
    8.27  		} catch (ClassNotFoundException e) {
    8.28 -			throw new DKException("No formatter class with name: " + className, e);
    8.29 +			throw new FormatterException("No formatter class with name: " + className, e);
    8.30  		} catch (NoSuchMethodException e) {
    8.31 -			throw new DKException("Formatter class with no valid constructor: " + className, e);
    8.32 +			throw new FormatterException("Formatter class with no valid constructor: " + className, e);
    8.33  		} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
    8.34 -			throw new DKException("Formatter's constructor caused an error: " + className, e);
    8.35 +			throw new FormatterException("Formatter's constructor caused an error: " + className, e);
    8.36  		}
    8.37  	}
    8.38  }
     9.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractFormatter.java	Sun Dec 22 22:02:44 2013 +0100
     9.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractFormatter.java	Sun Dec 22 23:31:55 2013 +0100
     9.3 @@ -110,7 +110,8 @@
     9.4  	 */
     9.5  	private State popState(EnumSet expected) {
     9.6  		try {
     9.7 -			State superior = state.pop();
     9.8 +			state.pop();
     9.9 +			State superior = state.peek();
    9.10  			if (expected.contains(superior)) {
    9.11  				return superior;
    9.12  			} else {
    9.13 @@ -155,7 +156,7 @@
    9.14  	}
    9.15  
    9.16  	@Override
    9.17 -	public void writeParameters(List<Parameter> parameters) {
    9.18 +	public void writeParameters(List<? extends Parameter> parameters) {
    9.19  		peekState(EnumSet.of(State.RESULT_SET, State.UPDATES_RESULT));
    9.20  
    9.21  		if (currentColumnsHeader != null) {
    9.22 @@ -242,6 +243,14 @@
    9.23  		return currentColumnsCount;
    9.24  	}
    9.25  
    9.26 +	protected boolean isCurrentColumnFirst() {
    9.27 +		return currentColumnsCount == 1;
    9.28 +	}
    9.29 +
    9.30 +	protected boolean isCurrentColumnLast() {
    9.31 +		return currentColumnsCount == currentColumnsHeader.getColumnCount();
    9.32 +	}
    9.33 +
    9.34  	/**
    9.35  	 * @return row number, 1 = first
    9.36  	 */
    10.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/Formatter.java	Sun Dec 22 22:02:44 2013 +0100
    10.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/Formatter.java	Sun Dec 22 23:31:55 2013 +0100
    10.3 @@ -37,7 +37,7 @@
    10.4  
    10.5  	void writeQuery(String sql);
    10.6  
    10.7 -	void writeParameters(List<Parameter> parameters);
    10.8 +	void writeParameters(List<? extends Parameter> parameters);
    10.9  
   10.10  	void writeColumnsHeader(ColumnsHeader header);
   10.11  
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/FormatterException.java	Sun Dec 22 23:31:55 2013 +0100
    11.3 @@ -0,0 +1,42 @@
    11.4 +/**
    11.5 + * SQL-DK
    11.6 + * Copyright © 2013 František Kučera (frantovo.cz)
    11.7 + *
    11.8 + * This program is free software: you can redistribute it and/or modify
    11.9 + * it under the terms of the GNU General Public License as published by
   11.10 + * the Free Software Foundation, either version 3 of the License, or
   11.11 + * (at your option) any later version.
   11.12 + *
   11.13 + * This program is distributed in the hope that it will be useful,
   11.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   11.16 + * GNU General Public License for more details.
   11.17 + *
   11.18 + * You should have received a copy of the GNU General Public License
   11.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
   11.20 + */
   11.21 +package info.globalcode.sql.dk.formatting;
   11.22 +
   11.23 +import info.globalcode.sql.dk.DKException;
   11.24 +
   11.25 +/**
   11.26 + *
   11.27 + * @author Ing. František Kučera (frantovo.cz)
   11.28 + */
   11.29 +public class FormatterException extends DKException {
   11.30 +
   11.31 +	public FormatterException() {
   11.32 +	}
   11.33 +
   11.34 +	public FormatterException(String message) {
   11.35 +		super(message);
   11.36 +	}
   11.37 +
   11.38 +	public FormatterException(Throwable cause) {
   11.39 +		super(cause);
   11.40 +	}
   11.41 +
   11.42 +	public FormatterException(String message, Throwable cause) {
   11.43 +		super(message, cause);
   11.44 +	}
   11.45 +}
    12.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java	Sun Dec 22 22:02:44 2013 +0100
    12.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java	Sun Dec 22 23:31:55 2013 +0100
    12.3 @@ -17,6 +17,8 @@
    12.4   */
    12.5  package info.globalcode.sql.dk.formatting;
    12.6  
    12.7 +import info.globalcode.sql.dk.ColorfulPrintWriter;
    12.8 +
    12.9  /**
   12.10   *
   12.11   * @author Ing. František Kučera (frantovo.cz)
   12.12 @@ -24,8 +26,28 @@
   12.13  public class TabularFormatter extends AbstractFormatter {
   12.14  
   12.15  	public static final String NAME = "tabular";
   12.16 +	private ColorfulPrintWriter out;
   12.17  
   12.18  	public TabularFormatter(FormatterContext formatterContext) {
   12.19  		super(formatterContext);
   12.20 +		out = new ColorfulPrintWriter(formatterContext.getOutputStream());
   12.21 +	}
   12.22 +
   12.23 +	@Override
   12.24 +	public void writeColumnValue(Object value) {
   12.25 +		super.writeColumnValue(value);
   12.26 +
   12.27 +		if (!isCurrentColumnFirst()) {
   12.28 +			out.print(ColorfulPrintWriter.TerminalColor.Green, " | ");
   12.29 +		}
   12.30 +		
   12.31 +		out.print(ColorfulPrintWriter.TerminalColor.Cyan, String.valueOf(value));
   12.32 +	}
   12.33 +
   12.34 +	@Override
   12.35 +	public void writeEndRow() {
   12.36 +		super.writeEndRow();
   12.37 +		out.println();
   12.38 +		out.flush();
   12.39  	}
   12.40  }