aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar/database/QueryBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/traccar/database/QueryBuilder.java')
-rw-r--r--src/org/traccar/database/QueryBuilder.java310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/org/traccar/database/QueryBuilder.java b/src/org/traccar/database/QueryBuilder.java
new file mode 100644
index 000000000..29a737f20
--- /dev/null
+++ b/src/org/traccar/database/QueryBuilder.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.database;
+
+import java.beans.Introspector;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import javax.sql.DataSource;
+import org.traccar.model.Factory;
+
+public class QueryBuilder {
+
+ private final Map<String, List<Integer>> indexMap;
+ private final PreparedStatement statement;
+
+ private QueryBuilder(DataSource dataSource, String query) throws SQLException {
+ indexMap = new HashMap<String, List<Integer>>();
+ statement = dataSource.getConnection().prepareStatement(
+ parse(query, indexMap), Statement.RETURN_GENERATED_KEYS);
+ }
+
+ private static String parse(String query, Map<String, List<Integer>> paramMap) {
+
+ int length = query.length();
+ StringBuilder parsedQuery = new StringBuilder(length);
+ boolean inSingleQuote = false;
+ boolean inDoubleQuote = false;
+ int index = 1;
+
+ for (int i = 0; i < length; i++) {
+
+ char c = query.charAt(i);
+
+ // String end
+ if (inSingleQuote) {
+ if (c == '\'') {
+ inSingleQuote = false;
+ }
+ } else if (inDoubleQuote) {
+ if (c == '"') {
+ inDoubleQuote = false;
+ }
+ } else {
+
+ // String begin
+ if (c == '\'') {
+ inSingleQuote = true;
+ } else if (c == '"') {
+ inDoubleQuote = true;
+ } else if (c == ':' && i + 1 < length
+ && Character.isJavaIdentifierStart(query.charAt(i + 1))) {
+
+ // Identifier name
+ int j = i + 2;
+ while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) {
+ j++;
+ }
+
+ String name = query.substring(i + 1, j);
+ c = '?';
+ i += name.length();
+ name = name.toLowerCase();
+
+ // Add to list
+ List<Integer> indexList = paramMap.get(name);
+ if (indexList == null) {
+ indexList = new LinkedList<Integer>();
+ paramMap.put(name, indexList);
+ }
+ indexList.add(index);
+
+ index++;
+ }
+ }
+
+ parsedQuery.append(c);
+ }
+
+ return parsedQuery.toString();
+ }
+
+ public static QueryBuilder create(DataSource dataSource, String query) throws SQLException {
+ return new QueryBuilder(dataSource, query);
+ }
+
+ private List<Integer> indexes(String name) {
+ name = name.toLowerCase();
+ List<Integer> result = indexMap.get(name);
+ if (result == null) {
+ result = new LinkedList<Integer>();
+ }
+ return result;
+ }
+
+ public QueryBuilder setBoolean(String name, boolean value) throws SQLException {
+ for (int i : indexes(name)) {
+ statement.setBoolean(i, value);
+ }
+ return this;
+ }
+
+ public QueryBuilder setInteger(String name, int value) throws SQLException {
+ for (int i : indexes(name)) {
+ statement.setInt(i, value);
+ }
+ return this;
+ }
+
+ public QueryBuilder setLong(String name, long value) throws SQLException {
+ for (int i : indexes(name)) {
+ statement.setLong(i, value);
+ }
+ return this;
+ }
+
+ public QueryBuilder setDouble(String name, double value) throws SQLException {
+ for (int i : indexes(name)) {
+ statement.setDouble(i, value);
+ }
+ return this;
+ }
+
+ public QueryBuilder setString(String name, String value) throws SQLException {
+ for (int i : indexes(name)) {
+ if (value == null) {
+ statement.setNull(i, Types.VARCHAR);
+ } else {
+ statement.setString(i, value);
+ }
+ }
+ return this;
+ }
+
+ public QueryBuilder setDate(String name, Date value) throws SQLException {
+ for (int i : indexes(name)) {
+ if (value == null) {
+ statement.setNull(i, Types.TIMESTAMP);
+ } else {
+ statement.setTimestamp(i, new Timestamp(value.getTime()));
+ }
+ }
+ return this;
+ }
+
+ public QueryBuilder setObject(Object object) throws SQLException {
+
+ Method[] methods = object.getClass().getMethods();
+
+ for (Method method : methods) {
+ if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
+ String name = method.getName().substring(3);
+ try {
+ if (method.getReturnType().equals(boolean.class)) {
+ return setBoolean(name, (Boolean) method.invoke(object));
+ } else if (method.getReturnType().equals(int.class)) {
+ return setInteger(name, (Integer) method.invoke(object));
+ } else if (method.getReturnType().equals(long.class)) {
+ return setLong(name, (Long) method.invoke(object));
+ } else if (method.getReturnType().equals(double.class)) {
+ return setDouble(name, (Double) method.invoke(object));
+ } else if (method.getReturnType().equals(String.class)) {
+ return setString(name, (String) method.invoke(object));
+ } else if (method.getReturnType().equals(Date.class)) {
+ return setDate(name, (Date) method.invoke(object));
+ }
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ }
+
+ return this;
+ }
+
+ private interface ResultSetProcessor<T> {
+ public void process(T object, ResultSet resultSet) throws SQLException;
+ }
+
+ public <T extends Factory> Collection<T> executeQuery(T prototype) throws SQLException {
+ List<T> result = new LinkedList<T>();
+
+ List<ResultSetProcessor<T>> processors = new LinkedList<ResultSetProcessor<T>>();
+
+ Method[] methods = prototype.getClass().getMethods();
+
+ for (final Method method : methods) {
+ if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
+
+ final String name = method.getName().substring(3);
+ Class<?> parameterType = method.getParameterTypes()[0];
+
+ if (parameterType.equals(boolean.class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, resultSet.getBoolean(name));
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ });
+ } else if (parameterType.equals(int.class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, resultSet.getInt(name));
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ });
+ } else if (parameterType.equals(long.class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, resultSet.getLong(name));
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ });
+ } else if (parameterType.equals(double.class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, resultSet.getDouble(name));
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ });
+ } else if (parameterType.equals(String.class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, resultSet.getString(name));
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ });
+ } else if (parameterType.equals(Date.class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, new Date(resultSet.getTimestamp(name).getTime()));
+ } catch (IllegalAccessException error) {
+ } catch (InvocationTargetException error) {
+ }
+ }
+ });
+ }
+ }
+ }
+
+ ResultSet resultSet = statement.executeQuery();
+
+ while (resultSet.next()) {
+ T object = (T) prototype.create();
+ for (ResultSetProcessor<T> processor : processors) {
+ processor.process(object, resultSet);
+ }
+ result.add(object);
+ }
+
+ return result;
+ }
+
+ public long executeUpdate() throws SQLException {
+
+ statement.executeUpdate();
+ ResultSet resultSet = statement.getGeneratedKeys();
+ if (resultSet.next()) {
+ return resultSet.getLong(1);
+ }
+ return 0;
+ }
+
+}