franta-hg@22: /** franta-hg@22: * SQL-DK franta-hg@22: * Copyright © 2013 František Kučera (frantovo.cz) franta-hg@22: * franta-hg@22: * This program is free software: you can redistribute it and/or modify franta-hg@22: * it under the terms of the GNU General Public License as published by franta-hg@22: * the Free Software Foundation, either version 3 of the License, or franta-hg@22: * (at your option) any later version. franta-hg@22: * franta-hg@22: * This program is distributed in the hope that it will be useful, franta-hg@22: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@22: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@22: * GNU General Public License for more details. franta-hg@22: * franta-hg@22: * You should have received a copy of the GNU General Public License franta-hg@22: * along with this program. If not, see . franta-hg@22: */ franta-hg@22: package info.globalcode.sql.dk.formatting; franta-hg@22: franta-hg@22: import info.globalcode.sql.dk.Parameter; franta-hg@29: import info.globalcode.sql.dk.configuration.DatabaseDefinition; franta-hg@22: import java.util.EmptyStackException; franta-hg@22: import java.util.EnumSet; franta-hg@22: import java.util.List; franta-hg@22: import java.util.Stack; franta-hg@22: franta-hg@22: /** franta-hg@22: * franta-hg@22: * @author Ing. František Kučera (frantovo.cz) franta-hg@22: */ franta-hg@22: public abstract class AbstractFormatter implements Formatter { franta-hg@22: franta-hg@22: private Stack state = new Stack<>(); franta-hg@24: private FormatterContext formatterContext; franta-hg@22: private ColumnsHeader currentColumnsHeader; franta-hg@22: private String currentQuery; franta-hg@22: private int currentColumnsCount; franta-hg@25: private int currentRowCount; franta-hg@22: franta-hg@24: public AbstractFormatter(FormatterContext formatterContext) { franta-hg@24: this.formatterContext = formatterContext; franta-hg@22: state.push(State.ROOT); franta-hg@22: } franta-hg@22: franta-hg@22: /* franta-hg@22: * root franta-hg@91: * .batch franta-hg@91: * ..database franta-hg@91: * ...resultSet franta-hg@91: * ....@query franta-hg@91: * ....@parameters franta-hg@91: * ....@columnsHeader franta-hg@91: * ....row franta-hg@91: * .....@columnValue franta-hg@91: * ...updatesResult franta-hg@91: * ....@query franta-hg@91: * ....@parameters franta-hg@91: * ....@updatedRowsCount franta-hg@22: */ franta-hg@22: protected enum State { franta-hg@22: franta-hg@22: ROOT, franta-hg@91: BATCH, franta-hg@22: DATABASE, franta-hg@22: RESULT_SET, franta-hg@22: ROW, franta-hg@41: UPDATES_RESULT franta-hg@22: } franta-hg@22: franta-hg@22: /** franta-hg@22: * Go down in hierarchy. franta-hg@22: * Pushes new state and verifies the old one. franta-hg@22: * franta-hg@22: * @param current the new state – currently entering franta-hg@22: * @param expected expected previous states (any of them is valid) franta-hg@22: * @return previous state franta-hg@22: * @throws IllegalStateException if previous state was not one from expected franta-hg@22: */ franta-hg@22: private State pushState(State current, EnumSet expected) { franta-hg@22: State previous = state.peek(); franta-hg@22: franta-hg@22: if (expected.contains(previous)) { franta-hg@22: state.push(current); franta-hg@22: return previous; franta-hg@22: } else { franta-hg@22: throw new IllegalStateException("Formatter was in wrong state: " + previous + " when it should be in one of: " + expected); franta-hg@22: } franta-hg@22: } franta-hg@22: franta-hg@22: protected State peekState(EnumSet expected) { franta-hg@22: State current = state.peek(); franta-hg@22: franta-hg@22: if (expected.contains(current)) { franta-hg@22: return current; franta-hg@22: } else { franta-hg@22: throw new IllegalStateException("Formatter is in wrong state: " + current + " when it should be in one of: " + expected); franta-hg@22: } franta-hg@22: franta-hg@22: } franta-hg@22: franta-hg@22: /** franta-hg@22: * Go up in hierarchy. franta-hg@22: * Pops the superior state/branch. franta-hg@22: * franta-hg@22: * @param expected expected superior state franta-hg@22: * @return the superior state franta-hg@22: * @throws IllegalStateException if superior state was not one from expected or if there is no franta-hg@22: * more superior state (we are at root level) franta-hg@22: */ franta-hg@22: private State popState(EnumSet expected) { franta-hg@22: try { franta-hg@34: state.pop(); franta-hg@34: State superior = state.peek(); franta-hg@22: if (expected.contains(superior)) { franta-hg@22: return superior; franta-hg@22: } else { franta-hg@22: throw new IllegalStateException("Formatter had wrong superior state: " + superior + " when it should be in one of: " + expected); franta-hg@22: } franta-hg@22: } catch (EmptyStackException e) { franta-hg@22: throw new IllegalStateException("Formatter was already at root level – there is nothing above that.", e); franta-hg@22: } franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@91: public void writeStartBatch() { franta-hg@91: pushState(State.BATCH, EnumSet.of(State.ROOT)); franta-hg@91: } franta-hg@91: franta-hg@91: @Override franta-hg@91: public void writeEndBatch() { franta-hg@91: popState(EnumSet.of(State.ROOT)); franta-hg@91: } franta-hg@91: franta-hg@91: @Override franta-hg@29: public void writeStartDatabase(DatabaseDefinition databaseDefinition) { franta-hg@91: pushState(State.DATABASE, EnumSet.of(State.BATCH)); franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeEndDatabase() { franta-hg@91: popState(EnumSet.of(State.BATCH)); franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeStartResultSet() { franta-hg@41: pushState(State.RESULT_SET, EnumSet.of(State.DATABASE)); franta-hg@25: currentRowCount = 0; franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeEndResultSet() { franta-hg@41: popState(EnumSet.of(State.DATABASE)); franta-hg@22: currentColumnsHeader = null; franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeQuery(String sql) { franta-hg@22: peekState(EnumSet.of(State.RESULT_SET, State.UPDATES_RESULT)); franta-hg@22: franta-hg@22: if (currentColumnsHeader == null) { franta-hg@22: currentQuery = sql; franta-hg@22: } else { franta-hg@22: throw new IllegalStateException("Query string '" + sql + "' must be set before columns header – was already set: " + currentColumnsHeader); franta-hg@22: } franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@34: public void writeParameters(List parameters) { franta-hg@22: peekState(EnumSet.of(State.RESULT_SET, State.UPDATES_RESULT)); franta-hg@22: franta-hg@22: if (currentColumnsHeader != null) { franta-hg@22: throw new IllegalStateException("Parameters '" + parameters + "' must be set before columns header – was already set: " + currentColumnsHeader); franta-hg@22: } franta-hg@22: franta-hg@88: if (currentQuery == null && parameters != null) { franta-hg@22: throw new IllegalStateException("Parameters '" + parameters + "' must be set after query – was not yet set."); franta-hg@22: } franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeColumnsHeader(ColumnsHeader header) { franta-hg@41: peekState(EnumSet.of(State.RESULT_SET)); franta-hg@22: franta-hg@22: if (currentColumnsHeader == null) { franta-hg@22: currentColumnsHeader = header; franta-hg@22: } else { franta-hg@22: throw new IllegalStateException("Columns header can be set only once per result set – was already set: " + currentColumnsHeader); franta-hg@22: } franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeStartRow() { franta-hg@22: pushState(State.ROW, EnumSet.of(State.RESULT_SET)); franta-hg@22: currentColumnsCount = 0; franta-hg@25: currentRowCount++; franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeEndRow() { franta-hg@22: popState(EnumSet.of(State.RESULT_SET)); franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeColumnValue(Object value) { franta-hg@22: peekState(EnumSet.of(State.ROW)); franta-hg@22: currentColumnsCount++; franta-hg@22: franta-hg@22: int declaredCount = currentColumnsHeader.getColumnCount(); franta-hg@22: if (currentColumnsCount > declaredCount) { franta-hg@22: throw new IllegalStateException("Current columns count is " + currentColumnsCount + " which is more than declared " + declaredCount + " in header."); franta-hg@22: } franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeStartUpdatesResult() { franta-hg@37: pushState(State.UPDATES_RESULT, EnumSet.of(State.DATABASE)); franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeEndUpdatesResult() { franta-hg@22: popState(EnumSet.of(State.DATABASE)); franta-hg@22: currentColumnsHeader = null; franta-hg@22: } franta-hg@22: franta-hg@22: @Override franta-hg@22: public void writeUpdatedRowsCount(int updatedRowsCount) { franta-hg@22: peekState(EnumSet.of(State.UPDATES_RESULT)); franta-hg@22: } franta-hg@22: franta-hg@101: @Override franta-hg@101: public void close() throws FormatterException { franta-hg@101: } franta-hg@101: franta-hg@24: public FormatterContext getFormatterContext() { franta-hg@24: return formatterContext; franta-hg@22: } franta-hg@22: franta-hg@22: protected ColumnsHeader getCurrentColumnsHeader() { franta-hg@22: return currentColumnsHeader; franta-hg@22: } franta-hg@22: franta-hg@25: /** franta-hg@25: * @return column number, 1 = first franta-hg@25: */ franta-hg@22: protected int getCurrentColumnsCount() { franta-hg@22: return currentColumnsCount; franta-hg@22: } franta-hg@25: franta-hg@34: protected boolean isCurrentColumnFirst() { franta-hg@34: return currentColumnsCount == 1; franta-hg@34: } franta-hg@34: franta-hg@34: protected boolean isCurrentColumnLast() { franta-hg@34: return currentColumnsCount == currentColumnsHeader.getColumnCount(); franta-hg@34: } franta-hg@34: franta-hg@25: /** franta-hg@25: * @return row number, 1 = first franta-hg@25: */ franta-hg@25: protected int getCurrentRowCount() { franta-hg@25: return currentRowCount; franta-hg@25: } franta-hg@22: }