/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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.
*
* Alfresco 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 Alfresco. If not, see .
*/
package org.alfresco.repo.query;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.query.AbstractCannedQueryFactory;
import org.alfresco.query.CannedQueryPageDetails;
import org.alfresco.query.CannedQuerySortDetails;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.CannedQuerySortDetails.SortOrder;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.query.CannedQueryDAO;
import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An intermediate {@link AbstractCannedQueryFactory} layer, for various
* implementations that need to know about QName IDs and similar
*
* @author Nick Burch
* @since 4.0
*/
public abstract class AbstractQNameAwareCannedQueryFactory extends AbstractCannedQueryFactory
{
private Log logger = LogFactory.getLog(getClass());
protected MethodSecurityBean methodSecurity;
protected NodeDAO nodeDAO;
protected QNameDAO qnameDAO;
protected TenantService tenantService;
protected CannedQueryDAO cannedQueryDAO;
public void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
}
public void setQnameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
public void setCannedQueryDAO(CannedQueryDAO cannedQueryDAO)
{
this.cannedQueryDAO = cannedQueryDAO;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void setMethodSecurity(MethodSecurityBean methodSecurity)
{
this.methodSecurity = methodSecurity;
}
@Override
public void afterPropertiesSet() throws Exception
{
super.afterPropertiesSet();
PropertyCheck.mandatory(this, "methodSecurity", methodSecurity);
PropertyCheck.mandatory(this, "nodeDAO", nodeDAO);
PropertyCheck.mandatory(this, "qnameDAO", qnameDAO);
PropertyCheck.mandatory(this, "cannedQueryDAO", cannedQueryDAO);
PropertyCheck.mandatory(this, "tenantService", tenantService);
}
/**
* Creates a Canned Query sort details, for the given list of properties
* and if they should be Ascending or Descending
*/
protected CannedQuerySortDetails createCQSortDetails(List> sort)
{
List> details = new ArrayList>();
for(Pair sortProp : sort)
{
details.add(new Pair(
sortProp.getFirst(),
(sortProp.getSecond() ? SortOrder.ASCENDING : SortOrder.DESCENDING)
));
}
return new CannedQuerySortDetails(details);
}
protected CannedQueryPageDetails createCQPageDetails(PagingRequest pagingReq)
{
int skipCount = pagingReq.getSkipCount();
if (skipCount == -1)
{
skipCount = CannedQueryPageDetails.DEFAULT_SKIP_RESULTS;
}
int maxItems = pagingReq.getMaxItems();
if (maxItems == -1)
{
maxItems = CannedQueryPageDetails.DEFAULT_PAGE_SIZE;
}
// page details
CannedQueryPageDetails cqpd = new CannedQueryPageDetails(skipCount, maxItems);
return cqpd;
}
protected Long getQNameId(QName qname)
{
Pair qnamePair = qnameDAO.getQName(qname);
if (qnamePair == null)
{
if (logger.isTraceEnabled())
{
logger.trace("QName does not exist: " + qname); // possible ... eg. blg:blogPost if a blog has never been posted externally
}
return -1L;
}
return qnamePair.getFirst();
}
protected Long getNodeId(NodeRef nodeRef)
{
Pair nodePair = nodeDAO.getNodePair(tenantService.getName(nodeRef));
if (nodePair == null)
{
throw new InvalidNodeRefException("Node ref does not exist: " + nodeRef, nodeRef);
}
return nodePair.getFirst();
}
public CannedQuerySortDetails createDateAscendingCQSortDetails()
{
List> sort = new ArrayList>();
sort.add(new Pair(ContentModel.PROP_CREATED, SortOrder.ASCENDING));
sort.add(new Pair(ContentModel.PROP_MODIFIED, SortOrder.ASCENDING));
return new CannedQuerySortDetails(sort);
}
public CannedQuerySortDetails createDateDescendingCQSortDetails()
{
List> sort = new ArrayList>();
sort.add(new Pair(ContentModel.PROP_CREATED, SortOrder.DESCENDING));
sort.add(new Pair(ContentModel.PROP_MODIFIED, SortOrder.DESCENDING));
return new CannedQuerySortDetails(sort);
}
/**
* Utility class to sort Entities on the basis of a Comparable property.
* Comparisons of two null properties are considered 'equal' by this comparator.
* Comparisons involving one null and one non-null property will return the null property as
* being 'before' the non-null property.
*
* Note that it is the responsibility of the calling code to ensure that the specified
* property values actually implement Comparable themselves.
*/
public static abstract class PropertyBasedComparator implements Comparator
{
protected QName comparableProperty;
public PropertyBasedComparator(QName comparableProperty)
{
this.comparableProperty = comparableProperty;
}
@SuppressWarnings("unchecked")
protected abstract Comparable getProperty(R entity);
@SuppressWarnings("unchecked")
@Override
public int compare(R r1, R r2)
{
Comparable prop1 = getProperty(r1);
Comparable prop2 = getProperty(r2);
if (prop1 == null && prop2 == null)
{
return 0;
}
else if (prop1 == null && prop2 != null)
{
return -1;
}
else if (prop1 != null && prop2 == null)
{
return 1;
}
else
{
return prop1.compareTo(prop2);
}
}
}
/**
* An instance of a {@link PropertyBasedComparator} for a {@link NodeBackedEntity}
*/
public static class NodeBackedEntityComparator extends PropertyBasedComparator
{
public NodeBackedEntityComparator(QName comparableProperty)
{
super(comparableProperty);
}
@SuppressWarnings("unchecked")
@Override
protected Comparable getProperty(NodeBackedEntity entity) {
if (comparableProperty.equals(ContentModel.PROP_CREATED))
{
return entity.getCreatedDate();
}
else if (comparableProperty.equals(ContentModel.PROP_MODIFIED))
{
return entity.getModifiedDate();
}
else if (comparableProperty.equals(ContentModel.PROP_CREATOR))
{
return entity.getCreator();
}
else if (comparableProperty.equals(ContentModel.PROP_MODIFIER))
{
return entity.getModifier();
}
else if (comparableProperty.equals(ContentModel.PROP_NAME))
{
return entity.getName();
}
else
{
throw new IllegalArgumentException("Unsupported calendar sort property: "+comparableProperty);
}
}
}
public static class NestedComparator implements Comparator
{
private List, SortOrder>> comparators;
public NestedComparator(List, SortOrder>> comparators)
{
this.comparators = comparators;
}
@Override
public int compare(R entry1, R entry2) {
for(Pair, SortOrder> pc : comparators)
{
int result = pc.getFirst().compare(entry1, entry2);
if(result != 0)
{
// Sorts differ, return
if(pc.getSecond() == SortOrder.ASCENDING)
{
return result;
}
else
{
return 0 - result;
}
}
else
{
// Sorts are the same, try the next along
}
}
// No difference on any
return 0;
}
}
}