/* * Copyright (C) 2005-2012 BetaCONCEPT Limited * * This file is part of Astroboa. * * Astroboa is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Astroboa 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.model.impl.query.criteria; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.query.CacheRegion; import org.betaconceptframework.astroboa.api.model.query.Order; import org.betaconceptframework.astroboa.api.model.query.criteria.CmsCriteria; import org.betaconceptframework.astroboa.api.model.query.criteria.Criterion; import org.betaconceptframework.astroboa.model.factory.CriterionFactory; import org.betaconceptframework.astroboa.model.impl.ItemQName; import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem; import org.betaconceptframework.astroboa.model.impl.item.JcrBuiltInItem; import org.betaconceptframework.astroboa.model.impl.query.xpath.OrderByClauseHelper; import org.betaconceptframework.astroboa.model.impl.query.xpath.XPathUtils; import org.betaconceptframework.astroboa.util.CmsConstants; /** * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ abstract class CmsCriteriaImpl extends CmsQueryContextImpl implements CmsCriteria, Serializable{ /** * */ private static final long serialVersionUID = 1063065223169846029L; private String pathCriterion; ItemQName nodeType; private List<Criterion> criteria; private boolean isNodeTypeAChildNode; String xpathQuery; //Default value is NO cache at all private CacheRegion cacheRegion = CacheRegion.NONE; CmsCriteriaImpl() { nodeType = JcrBuiltInItem.NtBase; } /** * Path criterion which is placed at the beginning of the query, for example * find all topics under Default taxonomy Root * /betaconcept:system/betaconcept:taxonomyRoot//element(*, betaconcept:topic)[...] * * */ protected void setPathCriterion(String pathCriterion){ this.pathCriterion = pathCriterion; } public List<Criterion> getCriteria() { return criteria; } public CmsCriteria addCriterion(Criterion criterion) { if (criteria == null) criteria = new ArrayList<Criterion>(); if (criterion != null){ criteria.add(criterion); resetXpathQuery(); } return this; } private void resetXpathQuery() { if (StringUtils.isNotBlank(xpathQuery)) xpathQuery = null; } public String getXPathQuery() { //XPathQuery has been calculated once. if (StringUtils.isNotBlank(xpathQuery)) return xpathQuery; String xpathselect = XPathUtils.createXPathSelect(null,nodeType, isNodeTypeAChildNode); //Add to xpath select ancestor xpath OR path criterion String ancestorXPathQuery = getAncestorQuery(); if (StringUtils.isNotBlank(ancestorXPathQuery)){ //Due to the automatic addition of order by clause we have to remove any //order by clause created if (ancestorXPathQuery.contains(CmsConstants.ORDER_BY)){ ancestorXPathQuery = StringUtils.substringBeforeLast(ancestorXPathQuery, CmsConstants.ORDER_BY); } xpathselect = ancestorXPathQuery+xpathselect; } else if (StringUtils.isNotBlank(pathCriterion)){ xpathselect = pathCriterion + xpathselect; } // Connect each criterion with AND condition //Add default criterion for system built in entity Criterion currentCriterion = null; if (CollectionUtils.isNotEmpty(getCriteria())) { for (Criterion criterion : getCriteria()) { if (currentCriterion == null){ currentCriterion = criterion; } else { currentCriterion = CriterionFactory.and(currentCriterion, criterion); } } } String orderBy = ""; if (MapUtils.isNotEmpty(getOrderProperties())){ OrderByClauseHelper orderByClauseHelper = new OrderByClauseHelper(getOrderProperties()); orderBy = orderByClauseHelper.generateOrderBy(); } else{ //Special case. Due to the fact that the JCR spec does not provide a method //for retrieving the total number of hits regardless of the limit, //we have to use the method provided by Jackrabbit (the JCR reference implementation). //Unfortunately, since version 2.x , this method returns -1 in cases where the size //is unknown even when the queries do not match any object at all. In order to //force the implementation to return the number of total hits we have to provide //an order by property. Therefore, in criteria where no order by property is provided //we explicitly instruct Jackrabbit to order the results by the jcr:score property. OrderByClauseHelper orderByClauseHelper = new OrderByClauseHelper(JcrBuiltInItem.JcrScore.getJcrName(), Order.descending); orderBy = orderByClauseHelper.generateOrderBy(); } String criteriaPath = (currentCriterion == null)? "": currentCriterion.getXPath(); xpathQuery = xpathselect + ((StringUtils.isBlank(criteriaPath) ? "" : CmsConstants.LEFT_BRACKET_WITH_LEADING_AND_TRAILING_SPACE + criteriaPath + CmsConstants.RIGHT_BRACKET_WITH_LEADING_AND_TRAILING_SPACE) + CmsConstants.EMPTY_SPACE + orderBy); return xpathQuery; } abstract String getAncestorQuery(); public void addIdEqualsCriterion(String id) { if (StringUtils.isNotBlank(id)){ addCriterion(CriterionFactory.equals(CmsBuiltInItem.CmsIdentifier.getJcrName(), id)); } } public void setNodeTypeAsAChildNodeOnly() { isNodeTypeAChildNode = true; } @Override public void reset() { pathCriterion = null; criteria = null; isNodeTypeAChildNode =false; xpathQuery =null; } public void addIdNotEqualsCriterion(String id) { if (StringUtils.isNotBlank(id)){ addCriterion(CriterionFactory.notEquals(CmsBuiltInItem.CmsIdentifier.getJcrName(), id)); } } public void setCacheable(CacheRegion cacheRegion) { if (cacheRegion != null && CacheRegion.NONE == cacheRegion) { doNotCacheResults(); } else { if (cacheRegion == null){ this.cacheRegion = CacheRegion.TEN_MINUTES; } else{ this.cacheRegion = cacheRegion; } } } public boolean isCacheable() { return CacheRegion.NONE != cacheRegion; } public void setXPathQuery(String xpathQuery) { this.xpathQuery = xpathQuery; } public CacheRegion getCacheRegion() { return cacheRegion; } public void doNotCacheResults() { this.cacheRegion = CacheRegion.NONE; } }