/** * *************************************************************************** * 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.*; import com.qcadoo.model.api.types.BelongsToType; import com.qcadoo.model.internal.api.InternalDataDefinition; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hibernate.Criteria; import org.hibernate.classic.Session; import org.hibernate.criterion.DetachedCriteria; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; public final class SearchCriteriaImpl implements SearchCriteriaBuilder, SearchCriteria { private static final String L_PLEASE_USE_NEW_CRITERIA_API = "Please use new criteria API"; private final DataDefinition sourceDataDefinition; private final DetachedCriteria criteria; private int maxResults = Integer.MAX_VALUE; private int firstResult; private final List<SearchOrder> orders = new ArrayList<SearchOrder>(); private boolean hasProjection; private final Map<String, String> aliases = new HashMap<String, String>(); private boolean cacheable = false; public SearchCriteriaImpl(final DataDefinition dataDefinition) { checkNotNull(dataDefinition); sourceDataDefinition = dataDefinition; criteria = DetachedCriteria.forEntityName(((InternalDataDefinition) dataDefinition).getFullyQualifiedClassName()); } public SearchCriteriaImpl(final DataDefinition dataDefinition, final String alias) { checkNotNull(dataDefinition); sourceDataDefinition = dataDefinition; criteria = DetachedCriteria.forEntityName(((InternalDataDefinition) dataDefinition).getFullyQualifiedClassName(), alias); } private SearchCriteriaImpl(final DetachedCriteria subcriteria) { sourceDataDefinition = null; criteria = subcriteria; } @Override public DataDefinition getDataDefinition() { if (hasProjection) { return null; } return sourceDataDefinition; } @Override public DetachedCriteria getHibernateDetachedCriteria() { return criteria; } @Override public SearchResult list() { return ((InternalDataDefinition) sourceDataDefinition).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()); } } @Override public Criteria createCriteria(final Session session) { Criteria executableCriteria = criteria.getExecutableCriteria(session); return executableCriteria; } @Override public void addFirstAndMaxResults(final Criteria criteria) { criteria.setMaxResults(maxResults).setFirstResult(firstResult); } @Override public void addCacheable(Criteria criteria) { if (cacheable) { criteria.setCacheable(cacheable); } } @Override public void addOrders(final Criteria criteria) { if (orders.isEmpty()) { if (sourceDataDefinition != null && sourceDataDefinition.isPrioritizable()) { criteria.addOrder(org.hibernate.criterion.Order.asc(sourceDataDefinition.getPriorityField().getName())); } else { criteria.addOrder(org.hibernate.criterion.Order.asc("id")); } } else { for (SearchOrder order : orders) { criteria.addOrder(order.getHibernateOrder()); } } } @Override public SearchCriteriaBuilder setMaxResults(final int maxResults) { this.maxResults = maxResults; return this; } @Override public SearchCriteriaBuilder setCacheable(final boolean cacheable) { this.cacheable = cacheable; return this; } @Override public SearchCriteriaBuilder setFirstResult(final int firstResult) { this.firstResult = firstResult; return this; } @Override public SearchCriteriaBuilder setProjection(final SearchProjection projection) { criteria.setProjection(projection.getHibernateProjection()); hasProjection = true; return this; } @Override public SearchCriteriaBuilder add(final SearchCriterion criterion) { criteria.add(criterion.getHibernateCriterion()); return this; } @Override public SearchCriteriaBuilder addOrder(final SearchOrder order) { orders.add(order); return this; } @Override public SearchCriteriaBuilder createAlias(final String associationPath, final String alias) { return createAlias(associationPath, alias, null); } @Override public SearchCriteriaBuilder createAlias(final String associationPath, final String alias, final JoinType joinType) { if (aliases.containsKey(alias)) { if (!associationPath.equals(aliases.get(alias))) { throw new IllegalStateException("Cannot register alias " + alias + " for " + associationPath + ", already exists for " + aliases.get(alias)); } } else { criteria.createAlias(associationPath, alias, getIntValueForJoinType(joinType)); aliases.put(alias, associationPath); } return this; } @Override public SearchCriteriaBuilder createCriteria(final String associationPath, final String alias) { return createCriteria(associationPath, alias, null); } @Override public SearchCriteriaBuilder createCriteria(final String associationPath, final String alias, final JoinType joinType) { DetachedCriteria subcriteria = criteria.createCriteria(associationPath, alias, getIntValueForJoinType(joinType)); return new SearchCriteriaImpl(subcriteria); } private int getIntValueForJoinType(final JoinType joinType) { if (joinType == null) { return JoinType.INNER.getIntValue(); } return joinType.getIntValue(); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(final Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public SearchCriteriaBuilder like(final String fieldName, final String value) { return add(SearchRestrictions.like(addAliasIfNessecary(fieldName), value)); } @Override public SearchCriteriaBuilder isEq(final String fieldName, final Object value) { if (isLikeRestriction(value)) { return add(SearchRestrictions.like(addAliasIfNessecary(fieldName), (String) value)); } return add(SearchRestrictions.eq(addAliasIfNessecary(fieldName), value)); } private boolean isLikeRestriction(final Object value) { return value instanceof String && ((String) value).matches(".*[\\*%\\?_].*"); } @Override public SearchCriteriaBuilder isLe(final String fieldName, final Object value) { return add(SearchRestrictions.le(addAliasIfNessecary(fieldName), value)); } @Override public SearchCriteriaBuilder isLt(final String fieldName, final Object value) { return add(SearchRestrictions.lt(addAliasIfNessecary(fieldName), value)); } @Override public SearchCriteriaBuilder isGe(final String fieldName, final Object value) { return add(SearchRestrictions.ge(addAliasIfNessecary(fieldName), value)); } @Override public SearchCriteriaBuilder isGt(final String fieldName, final Object value) { return add(SearchRestrictions.gt(addAliasIfNessecary(fieldName), value)); } @Override public SearchCriteriaBuilder isNe(final String fieldName, final Object value) { if (isLikeRestriction(value)) { return add(SearchRestrictions.not(SearchRestrictions.like(addAliasIfNessecary(fieldName), (String) value))); } return add(SearchRestrictions.ne(addAliasIfNessecary(fieldName), value)); } @Override public SearchCriteriaBuilder isNotNull(final String fieldName) { return add(SearchRestrictions.isNotNull(addAliasIfNessecary(fieldName))); } @Override public SearchCriteriaBuilder isNull(final String fieldName) { return add(SearchRestrictions.isNull(addAliasIfNessecary(fieldName))); } @Override public SearchCriteriaBuilder openNot() { throw new UnsupportedOperationException(L_PLEASE_USE_NEW_CRITERIA_API); } @Override public SearchCriteriaBuilder closeNot() { throw new UnsupportedOperationException(L_PLEASE_USE_NEW_CRITERIA_API); } @Override public SearchCriteriaBuilder openOr() { throw new UnsupportedOperationException(L_PLEASE_USE_NEW_CRITERIA_API); } @Override public SearchCriteriaBuilder closeOr() { throw new UnsupportedOperationException(L_PLEASE_USE_NEW_CRITERIA_API); } @Override public SearchCriteriaBuilder openAnd() { throw new UnsupportedOperationException(L_PLEASE_USE_NEW_CRITERIA_API); } @Override public SearchCriteriaBuilder closeAnd() { throw new UnsupportedOperationException(L_PLEASE_USE_NEW_CRITERIA_API); } @Override public SearchCriteriaBuilder belongsTo(final String fieldName, final Object entityOrId) { if (entityOrId instanceof Entity) { return add(SearchRestrictions.belongsTo(addAliasIfNessecary(fieldName), (Entity) entityOrId)); } else if (entityOrId == null) { return add(SearchRestrictions.isNull(addAliasIfNessecary(fieldName))); } else { return add(SearchRestrictions.belongsTo(addAliasIfNessecary(fieldName), getDataDefinitionBelongsToForField(fieldName), (Long) entityOrId)); } } @Override public SearchCriteriaBuilder isIdEq(final Long id) { return add(SearchRestrictions.eq("id", id)); } @Override public SearchCriteriaBuilder isIdLe(final Long id) { return add(SearchRestrictions.le("id", id)); } @Override public SearchCriteriaBuilder isIdLt(final Long id) { return add(SearchRestrictions.lt("id", id)); } @Override public SearchCriteriaBuilder isIdGe(final Long id) { return add(SearchRestrictions.ge("id", id)); } @Override public SearchCriteriaBuilder isIdGt(final Long id) { return add(SearchRestrictions.gt("id", id)); } @Override public SearchCriteriaBuilder isIdNe(final Long id) { return add(SearchRestrictions.ne("id", id)); } @Override public SearchCriteriaBuilder orderAscBy(final String fieldName) { return addOrder(SearchOrders.asc(addAliasIfNessecary(fieldName))); } @Override public SearchCriteriaBuilder orderDescBy(final String fieldName) { return addOrder(SearchOrders.desc(addAliasIfNessecary(fieldName))); } private DataDefinition getDataDefinitionBelongsToForField(final String field) { String[] path = field.split("\\."); DataDefinition parentDataDefinition = sourceDataDefinition; for (int i = 0; i < path.length; i++) { parentDataDefinition = ((BelongsToType) parentDataDefinition.getField(path[i]).getType()).getDataDefinition(); } return parentDataDefinition; } private int aliasIndex = 1; private String addAliasIfNessecary(final String field) { String[] path = field.split("\\."); if (path.length == 1) { return field; } else { String lastAlias = ""; for (int i = 0; i < path.length - 1; i++) { createAlias(lastAlias + path[i], path[i] + "_a" + aliasIndex); lastAlias = path[i] + "_a" + aliasIndex + "."; aliasIndex++; } return lastAlias + path[path.length - 1]; } } }