/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo Framework
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.model.internal.search;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.search.SearchQueryBuilder;
import com.qcadoo.model.api.search.SearchResult;
import com.qcadoo.model.internal.api.DataAccessService;
import com.qcadoo.model.internal.api.InternalDataDefinition;
import org.hibernate.Query;
import org.hibernate.classic.Session;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SearchQueryImpl implements SearchQuery {
private final Pattern pattern = Pattern.compile("#([a-zA-Z0-9]+)_([a-zA-Z0-9]+)");
private final Map<String, String> strings = new HashMap<String, String>();
private final Map<String, Boolean> booleans = new HashMap<String, Boolean>();
private final Map<String, Byte> bytes = new HashMap<String, Byte>();
private final Map<String, Short> shorts = new HashMap<String, Short>();
private final Map<String, Integer> integers = new HashMap<String, Integer>();
private final Map<String, Long> longs = new HashMap<String, Long>();
private final Map<String, Float> floats = new HashMap<String, Float>();
private final Map<String, Double> doubles = new HashMap<String, Double>();
private final Map<String, BigDecimal> bigDecimals = new HashMap<String, BigDecimal>();
private final Map<String, Date> dates = new HashMap<String, Date>();
private final Map<String, Date> times = new HashMap<String, Date>();
private final Map<String, Date> timestamps = new HashMap<String, Date>();
private final Map<String, Object> parameters = new HashMap<String, Object>();
private final Map<String, Collection<? extends Object>> parameterLists = new HashMap<String, Collection<? extends Object>>();
private final Map<String, Object> entities = new HashMap<String, Object>();
private final DataAccessService dataAccessService;
private final InternalDataDefinition sourceDataDefinition;
private final String queryString;
private InternalDataDefinition mainDataDefinition = null;
private int maxResults;
private int firstResult;
private boolean cacheable = false;
public SearchQueryImpl(final InternalDataDefinition dataDefinition, final DataAccessService dataAccessService,
final String queryString) {
this.sourceDataDefinition = dataDefinition;
this.dataAccessService = dataAccessService;
this.queryString = prepareDataDefinitions(prepareQuery(queryString));
}
private String prepareDataDefinitions(final String queryString) {
boolean hasSelectSection = !queryString.startsWith("from");
Matcher matcher = pattern.matcher(queryString);
String newQueryString = queryString;
while (matcher.find()) {
InternalDataDefinition dataDefinition = dataAccessService.getDataDefinition(matcher.group(1), matcher.group(2));
newQueryString = newQueryString.replaceAll(
"#" + dataDefinition.getPluginIdentifier() + "_" + dataDefinition.getName(),
dataDefinition.getFullyQualifiedClassName());
if (!hasSelectSection && mainDataDefinition == null) {
mainDataDefinition = dataDefinition;
}
}
return newQueryString;
}
private String prepareQuery(final String queryString) {
if (!StringUtils.hasText(queryString)) {
return "from #" + sourceDataDefinition.getPluginIdentifier() + "_" + sourceDataDefinition.getName();
}
if (queryString.trim().startsWith("where")) {
return "from #" + sourceDataDefinition.getPluginIdentifier() + "_" + sourceDataDefinition.getName() + " "
+ queryString.trim();
}
return queryString.trim();
}
@Override
public DataDefinition getDataDefinition() {
return mainDataDefinition;
}
@Override
public SearchResult list() {
return dataAccessService.find(this);
}
@Override
public Entity uniqueResult() {
SearchResult results = list();
if (results.getEntities().isEmpty()) {
return null;
} else if (results.getEntities().size() == 1) {
return results.getEntities().get(0);
} else {
throw new IllegalStateException("Too many results, expected one, found " + results.getEntities().size());
}
}
public SearchQueryBuilder setCacheable(final boolean cacheable){
this.cacheable = cacheable;
return this;
}
@Override
public void addCacheable(Query query) {
query.setCacheable(cacheable);
}
@Override
public SearchQueryBuilder setMaxResults(final int maxResults) {
this.maxResults = maxResults;
return this;
}
@Override
public SearchQueryBuilder setFirstResult(final int firstResult) {
this.firstResult = firstResult;
return this;
}
@Override
public SearchQueryBuilder setString(final String name, final String val) {
strings.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setBoolean(final String name, final boolean val) {
booleans.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setByte(final String name, final byte val) {
bytes.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setShort(final String name, final short val) {
shorts.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setInteger(final String name, final int val) {
integers.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setLong(final String name, final long val) {
longs.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setFloat(final String name, final float val) {
floats.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setDouble(final String name, final double val) {
doubles.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setBigDecimal(final String name, final BigDecimal number) {
bigDecimals.put(name, number);
return this;
}
@Override
public SearchQueryBuilder setDate(final String name, final Date date) {
dates.put(name, date);
return this;
}
@Override
public SearchQueryBuilder setTime(final String name, final Date date) {
times.put(name, date);
return this;
}
@Override
public SearchQueryBuilder setTimestamp(final String name, final Date date) {
timestamps.put(name, date);
return this;
}
@Override
public SearchQueryBuilder setEntity(final String name, final Entity entity) {
Object databaseEntity = null;
if (entity != null) {
databaseEntity = dataAccessService.convertToDatabaseEntity(entity);
}
if (databaseEntity == null) {
parameters.put(name, null);
} else {
entities.put(name, databaseEntity);
}
return this;
}
@Override
public SearchQueryBuilder setEntity(final String name, final String pluginIdentifier, final String modelName,
final long entityId) {
return setEntity(name, dataAccessService.get(dataAccessService.getDataDefinition(pluginIdentifier, modelName), entityId));
}
@Override
public SearchQueryBuilder setParameter(final String name, final Object val) {
parameters.put(name, val);
return this;
}
@Override
public SearchQueryBuilder setParameterList(final String name, final Collection<? extends Object> values) {
parameterLists.put(name, values);
return this;
}
@Override
public Query createQuery(final Session session) {
return session.createQuery(queryString);
}
@Override
public void addParameters(final Query query) {
for (Map.Entry<String, String> parameter : strings.entrySet()) {
query.setString(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Boolean> parameter : booleans.entrySet()) {
query.setBoolean(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Byte> parameter : bytes.entrySet()) {
query.setByte(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Short> parameter : shorts.entrySet()) {
query.setShort(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Integer> parameter : integers.entrySet()) {
query.setInteger(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Long> parameter : longs.entrySet()) {
query.setLong(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Float> parameter : floats.entrySet()) {
query.setFloat(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Double> parameter : doubles.entrySet()) {
query.setDouble(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, BigDecimal> parameter : bigDecimals.entrySet()) {
query.setBigDecimal(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Date> parameter : dates.entrySet()) {
query.setDate(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Date> parameter : times.entrySet()) {
query.setTime(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Date> parameter : timestamps.entrySet()) {
query.setTimestamp(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Object> parameter : parameters.entrySet()) {
query.setParameter(parameter.getKey(), parameter.getValue());
}
for (Map.Entry<String, Collection<? extends Object>> parametersList : parameterLists.entrySet()) {
query.setParameterList(parametersList.getKey(), parametersList.getValue());
}
for (Map.Entry<String, Object> parameter : entities.entrySet()) {
query.setEntity(parameter.getKey(), parameter.getValue());
}
}
@Override
public void addFirstAndMaxResults(final Query query) {
if (firstResult > 0) {
query.setFirstResult(firstResult);
}
if (maxResults > 0) {
query.setMaxResults(maxResults);
}
}
@Override
public boolean hasFirstAndMaxResults() {
return firstResult > 0 || maxResults > 0;
}
}