java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/AbstractFormatter.java
3 * Copyright © 2013 František Kučera (frantovo.cz)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package info.globalcode.sql.dk.formatting;
19 import info.globalcode.sql.dk.Parameter;
20 import info.globalcode.sql.dk.configuration.DatabaseDefinition;
21 import java.util.EmptyStackException;
22 import java.util.EnumSet;
23 import java.util.List;
24 import java.util.Stack;
28 * <li>ensures integrity – if methods are called in correct order and context</li>
29 * <li>provides default implmentations of methods that does not produce any output for given
33 * @author Ing. František Kučera (frantovo.cz)
35 public abstract class AbstractFormatter implements Formatter {
37 private Stack<State> state = new Stack<>();
38 private FormatterContext formatterContext;
39 private ColumnsHeader currentColumnsHeader;
40 private String currentQuery;
41 private int currentColumnsCount;
42 private int currentRowCount;
43 private int resultSetCount;
45 public AbstractFormatter(FormatterContext formatterContext) {
46 this.formatterContext = formatterContext;
47 state.push(State.ROOT);
50 protected String getCurrentRelationName() {
51 if (getFormatterContext().getRelationNames() == null || getFormatterContext().getRelationNames().size() < resultSetCount) {
52 return "r" + resultSetCount;
54 return getFormatterContext().getRelationNames().get(resultSetCount - 1);
70 protected enum State {
81 * Go down in hierarchy.
82 * Pushes new state and verifies the old one.
84 * @param current the new state – currently entering
85 * @param expected expected previous states (any of them is valid)
86 * @return previous state
87 * @throws IllegalStateException if previous state was not one from expected
89 private State pushState(State current, EnumSet expected) {
90 State previous = state.peek();
92 if (expected.contains(previous)) {
96 throw new IllegalStateException("Formatter was in wrong state: " + previous + " when it should be in one of: " + expected);
100 protected State peekState(EnumSet expected) {
101 State current = state.peek();
103 if (expected.contains(current)) {
106 throw new IllegalStateException("Formatter is in wrong state: " + current + " when it should be in one of: " + expected);
112 * Go up in hierarchy.
113 * Pops the superior state/branch.
115 * @param expected expected superior state
116 * @return the superior state
117 * @throws IllegalStateException if superior state was not one from expected or if there is no
118 * more superior state (we are at root level)
120 private State popState(EnumSet expected) {
123 State superior = state.peek();
124 if (expected.contains(superior)) {
127 throw new IllegalStateException("Formatter had wrong superior state: " + superior + " when it should be in one of: " + expected);
129 } catch (EmptyStackException e) {
130 throw new IllegalStateException("Formatter was already at root level – there is nothing above that.", e);
135 public void writeStartBatch() {
136 pushState(State.BATCH, EnumSet.of(State.ROOT));
141 public void writeEndBatch() {
142 popState(EnumSet.of(State.ROOT));
146 public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
147 pushState(State.DATABASE, EnumSet.of(State.BATCH));
151 public void writeEndDatabase() {
152 popState(EnumSet.of(State.BATCH));
156 public void writeStartStatement() {
157 pushState(State.STATEMENT, EnumSet.of(State.DATABASE));
161 public void writeEndStatement() {
162 popState(EnumSet.of(State.DATABASE));
166 public void writeStartResultSet(ColumnsHeader header) {
167 pushState(State.RESULT_SET, EnumSet.of(State.STATEMENT));
170 currentColumnsHeader = header;
174 public void writeEndResultSet() {
175 popState(EnumSet.of(State.STATEMENT));
176 currentColumnsHeader = null;
180 public void writeQuery(String sql) {
181 peekState(EnumSet.of(State.STATEMENT));
183 if (currentColumnsHeader == null) {
186 throw new IllegalStateException("Query string '" + sql + "' must be set before columns header – was already set: " + currentColumnsHeader);
191 public void writeParameters(List<? extends Parameter> parameters) {
192 peekState(EnumSet.of(State.STATEMENT));
194 if (currentColumnsHeader != null) {
195 throw new IllegalStateException("Parameters '" + parameters + "' must be set before columns header – was already set: " + currentColumnsHeader);
198 if (currentQuery == null && parameters != null) {
199 throw new IllegalStateException("Parameters '" + parameters + "' must be set after query – was not yet set.");
204 public void writeStartRow() {
205 pushState(State.ROW, EnumSet.of(State.RESULT_SET));
206 currentColumnsCount = 0;
211 public void writeEndRow() {
212 popState(EnumSet.of(State.RESULT_SET));
216 public void writeColumnValue(Object value) {
217 peekState(EnumSet.of(State.ROW));
218 currentColumnsCount++;
220 int declaredCount = currentColumnsHeader.getColumnCount();
221 if (currentColumnsCount > declaredCount) {
222 throw new IllegalStateException("Current columns count is " + currentColumnsCount + " which is more than declared " + declaredCount + " in header.");
227 public void writeUpdatesResult(int updatedRowsCount) {
228 peekState(EnumSet.of(State.STATEMENT));
232 public void close() throws FormatterException {
235 public FormatterContext getFormatterContext() {
236 return formatterContext;
239 protected ColumnsHeader getCurrentColumnsHeader() {
240 return currentColumnsHeader;
244 * @return column number, 1 = first
246 protected int getCurrentColumnsCount() {
247 return currentColumnsCount;
250 protected boolean isCurrentColumnFirst() {
251 return currentColumnsCount == 1;
254 protected boolean isCurrentColumnLast() {
255 return currentColumnsCount == currentColumnsHeader.getColumnCount();
259 * @return row number, 1 = first
261 protected int getCurrentRowCount() {
262 return currentRowCount;