java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractFormatter.java
author František Kučera <franta-hg@frantovo.cz>
Sun, 22 Dec 2013 23:31:55 +0100
branchv_0
changeset 34 9335cf31c0f2
parent 29 d66858b4b563
child 37 9e6f8e5d5f98
permissions -rw-r--r--
first working version
     1 /**
     2  * SQL-DK
     3  * Copyright © 2013 František Kučera (frantovo.cz)
     4  *
     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, either version 3 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License
    16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
    17  */
    18 package info.globalcode.sql.dk.formatting;
    19 
    20 import info.globalcode.sql.dk.Parameter;
    21 import info.globalcode.sql.dk.configuration.DatabaseDefinition;
    22 import java.util.EmptyStackException;
    23 import java.util.EnumSet;
    24 import java.util.List;
    25 import java.util.Stack;
    26 
    27 /**
    28  *
    29  * @author Ing. František Kučera (frantovo.cz)
    30  */
    31 public abstract class AbstractFormatter implements Formatter {
    32 
    33 	private Stack<State> state = new Stack<>();
    34 	private FormatterContext formatterContext;
    35 	private ColumnsHeader currentColumnsHeader;
    36 	private String currentQuery;
    37 	private int currentColumnsCount;
    38 	private int currentRowCount;
    39 
    40 	public AbstractFormatter(FormatterContext formatterContext) {
    41 		this.formatterContext = formatterContext;
    42 		state.push(State.ROOT);
    43 	}
    44 
    45 	/*
    46 	 * root
    47 	 * .database
    48 	 * ..resultSet
    49 	 * ...@query
    50 	 * ...@parameters
    51 	 * ...@columnsHeader
    52 	 * ...row
    53 	 * ....@columnValue
    54 	 * ..updatesResult
    55 	 * ...@query
    56 	 * ...@parameters
    57 	 * ...@updatedRowsCount
    58 	 * ...generatedKeys
    59 	 * ....resultSet (see above)
    60 	 */
    61 	protected enum State {
    62 
    63 		ROOT,
    64 		DATABASE,
    65 		RESULT_SET,
    66 		ROW,
    67 		UPDATES_RESULT,
    68 		GENERATED_KEYS
    69 	}
    70 
    71 	/**
    72 	 * Go down in hierarchy.
    73 	 * Pushes new state and verifies the old one.
    74 	 *
    75 	 * @param current the new state – currently entering
    76 	 * @param expected expected previous states (any of them is valid)
    77 	 * @return previous state
    78 	 * @throws IllegalStateException if previous state was not one from expected
    79 	 */
    80 	private State pushState(State current, EnumSet expected) {
    81 		State previous = state.peek();
    82 
    83 		if (expected.contains(previous)) {
    84 			state.push(current);
    85 			return previous;
    86 		} else {
    87 			throw new IllegalStateException("Formatter was in wrong state: " + previous + " when it should be in one of: " + expected);
    88 		}
    89 	}
    90 
    91 	protected State peekState(EnumSet expected) {
    92 		State current = state.peek();
    93 
    94 		if (expected.contains(current)) {
    95 			return current;
    96 		} else {
    97 			throw new IllegalStateException("Formatter is in wrong state: " + current + " when it should be in one of: " + expected);
    98 		}
    99 
   100 	}
   101 
   102 	/**
   103 	 * Go up in hierarchy.
   104 	 * Pops the superior state/branch.
   105 	 *
   106 	 * @param expected expected superior state
   107 	 * @return the superior state
   108 	 * @throws IllegalStateException if superior state was not one from expected or if there is no
   109 	 * more superior state (we are at root level)
   110 	 */
   111 	private State popState(EnumSet expected) {
   112 		try {
   113 			state.pop();
   114 			State superior = state.peek();
   115 			if (expected.contains(superior)) {
   116 				return superior;
   117 			} else {
   118 				throw new IllegalStateException("Formatter had wrong superior state: " + superior + " when it should be in one of: " + expected);
   119 			}
   120 		} catch (EmptyStackException e) {
   121 			throw new IllegalStateException("Formatter was already at root level – there is nothing above that.", e);
   122 		}
   123 	}
   124 
   125 	@Override
   126 	public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
   127 		pushState(State.DATABASE, EnumSet.of(State.ROOT));
   128 	}
   129 
   130 	@Override
   131 	public void writeEndDatabase() {
   132 		popState(EnumSet.of(State.ROOT));
   133 	}
   134 
   135 	@Override
   136 	public void writeStartResultSet() {
   137 		pushState(State.RESULT_SET, EnumSet.of(State.DATABASE, State.GENERATED_KEYS));
   138 		currentRowCount = 0;
   139 	}
   140 
   141 	@Override
   142 	public void writeEndResultSet() {
   143 		popState(EnumSet.of(State.DATABASE, State.GENERATED_KEYS));
   144 		currentColumnsHeader = null;
   145 	}
   146 
   147 	@Override
   148 	public void writeQuery(String sql) {
   149 		peekState(EnumSet.of(State.RESULT_SET, State.UPDATES_RESULT));
   150 
   151 		if (currentColumnsHeader == null) {
   152 			currentQuery = sql;
   153 		} else {
   154 			throw new IllegalStateException("Query string '" + sql + "' must be set before columns header – was already set: " + currentColumnsHeader);
   155 		}
   156 	}
   157 
   158 	@Override
   159 	public void writeParameters(List<? extends Parameter> parameters) {
   160 		peekState(EnumSet.of(State.RESULT_SET, State.UPDATES_RESULT));
   161 
   162 		if (currentColumnsHeader != null) {
   163 			throw new IllegalStateException("Parameters '" + parameters + "' must be set before columns header – was already set: " + currentColumnsHeader);
   164 		}
   165 
   166 		if (currentQuery == null) {
   167 			throw new IllegalStateException("Parameters '" + parameters + "' must be set after query – was not yet set.");
   168 		}
   169 	}
   170 
   171 	@Override
   172 	public void writeColumnsHeader(ColumnsHeader header) {
   173 		peekState(EnumSet.of(State.RESULT_SET, State.UPDATES_RESULT));
   174 
   175 		if (currentColumnsHeader == null) {
   176 			currentColumnsHeader = header;
   177 		} else {
   178 			throw new IllegalStateException("Columns header can be set only once per result set – was already set: " + currentColumnsHeader);
   179 		}
   180 	}
   181 
   182 	@Override
   183 	public void writeStartRow() {
   184 		pushState(State.ROW, EnumSet.of(State.RESULT_SET));
   185 		currentColumnsCount = 0;
   186 		currentRowCount++;
   187 	}
   188 
   189 	@Override
   190 	public void writeEndRow() {
   191 		popState(EnumSet.of(State.RESULT_SET));
   192 	}
   193 
   194 	@Override
   195 	public void writeColumnValue(Object value) {
   196 		peekState(EnumSet.of(State.ROW));
   197 		currentColumnsCount++;
   198 
   199 		int declaredCount = currentColumnsHeader.getColumnCount();
   200 		if (currentColumnsCount > declaredCount) {
   201 			throw new IllegalStateException("Current columns count is " + currentColumnsCount + " which is more than declared " + declaredCount + " in header.");
   202 		}
   203 	}
   204 
   205 	@Override
   206 	public void writeStartUpdatesResult() {
   207 		pushState(State.RESULT_SET, EnumSet.of(State.DATABASE));
   208 	}
   209 
   210 	@Override
   211 	public void writeEndUpdatesResult() {
   212 		popState(EnumSet.of(State.DATABASE));
   213 		currentColumnsHeader = null;
   214 	}
   215 
   216 	@Override
   217 	public void writeUpdatedRowsCount(int updatedRowsCount) {
   218 		peekState(EnumSet.of(State.UPDATES_RESULT));
   219 	}
   220 
   221 	@Override
   222 	public void writeStartGeneratedKeys() {
   223 		pushState(State.GENERATED_KEYS, EnumSet.of(State.UPDATES_RESULT));
   224 	}
   225 
   226 	@Override
   227 	public void writeEndGeneratedKeys() {
   228 		popState(EnumSet.of(State.UPDATES_RESULT));
   229 	}
   230 
   231 	public FormatterContext getFormatterContext() {
   232 		return formatterContext;
   233 	}
   234 
   235 	protected ColumnsHeader getCurrentColumnsHeader() {
   236 		return currentColumnsHeader;
   237 	}
   238 
   239 	/**
   240 	 * @return column number, 1 = first
   241 	 */
   242 	protected int getCurrentColumnsCount() {
   243 		return currentColumnsCount;
   244 	}
   245 
   246 	protected boolean isCurrentColumnFirst() {
   247 		return currentColumnsCount == 1;
   248 	}
   249 
   250 	protected boolean isCurrentColumnLast() {
   251 		return currentColumnsCount == currentColumnsHeader.getColumnCount();
   252 	}
   253 
   254 	/**
   255 	 * @return row number, 1 = first
   256 	 */
   257 	protected int getCurrentRowCount() {
   258 		return currentRowCount;
   259 	}
   260 	/**
   261 	 * TODO: write SQLWarning
   262 	 */
   263 }