package jp.aegif.nemaki.cmis.aspect;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import jp.aegif.nemaki.cmis.aspect.type.TypeManager;
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfo;
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap;
import jp.aegif.nemaki.util.PropertyManager;
import jp.aegif.nemaki.util.constant.PropertyKey;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.enums.CapabilityOrderBy;
import org.apache.chemistry.opencmis.commons.enums.PropertyType;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class SortUtil {
private static final Log log = LogFactory.getLog(SortUtil.class);
private TypeManager typeManager;
private RepositoryInfoMap repositoryInfoMap;
private PropertyManager propertyManager;
@SuppressWarnings("unchecked")
public void sort(String repositoryId, List<ObjectData> list, String orderBy) {
//Check empty list
if(CollectionUtils.isEmpty(list)){
return;
}
// Check orderBy argument
if (StringUtils.isEmpty(orderBy)) {
String defaultOrderBy = propertyManager
.readValue(PropertyKey.CAPABILITY_EXTENDED_ORDERBY_DEFAULT);
if (StringUtils.isBlank(defaultOrderBy)) {
return;
} else {
orderBy = defaultOrderBy;
}
}
// Check CapabilityOrderBy
CapabilityOrderBy capabilityOrderBy = repositoryInfoMap.get(repositoryId).getCapabilities().getOrderByCapability();
if (CapabilityOrderBy.NONE == capabilityOrderBy) {
return;
}
// Check the parsed result of orderBy argument
LinkedHashMap<PropertyDefinition<?>, Boolean> _orderBy = parseOrderBy(
orderBy, capabilityOrderBy);
if (MapUtils.isEmpty(_orderBy)) {
return;
}
// Build ComparatorChain
ComparatorChain chain = new ComparatorChain();
for (Entry<PropertyDefinition<?>, Boolean> o : _orderBy.entrySet()) {
PropertyComparator comparator = new PropertyComparator(o.getKey());
chain.addComparator(comparator, o.getValue());
}
// Sort
Collections.sort(list, chain);
}
private class PropertyComparator implements Comparator<ObjectData> {
PropertyDefinition<?> propertyDefinition;
public PropertyComparator(PropertyDefinition<?> propertyDefinition) {
this.propertyDefinition = propertyDefinition;
}
@Override
public int compare(ObjectData o1, ObjectData o2) {
String propertyId = propertyDefinition.getId();
PropertyData<?> pd1 = o1.getProperties().getProperties()
.get(propertyId);
PropertyData<?> pd2 = o2.getProperties().getProperties()
.get(propertyId);
Object val1 = null;
Object val2 = null;
if(pd1 != null){
val1 = pd1.getFirstValue();
}
if(pd2 != null){
val2 = pd2.getFirstValue();
}
// Null values are put to the last
if (val1 == null && val2 == null) {
return 0;
} else if (val1 == null && val2 != null) {
return 1;
} else if (val1 != null && val2 == null) {
return -1;
} else {
PropertyType pType = propertyDefinition.getPropertyType();
if (PropertyType.STRING == pType || PropertyType.ID == pType
|| PropertyType.HTML == pType
|| PropertyType.URI == pType) {
String _val1 = (String) val1;
String _val2 = (String) val2;
return _val1.compareTo(_val2);
} else if (PropertyType.BOOLEAN == pType) {
Boolean _val1 = (Boolean) val1;
Boolean _val2 = (Boolean) val2;
return _val1.compareTo(_val2);
} else if (PropertyType.DATETIME == pType) {
GregorianCalendar _val1 = (GregorianCalendar) val1;
GregorianCalendar _val2 = (GregorianCalendar) val2;
return _val1.compareTo(_val2);
} else if (PropertyType.INTEGER == pType) {
Integer _val1 = (Integer) val1;
Integer _val2 = (Integer) val2;
return _val1.compareTo(_val2);
} else if (PropertyType.DECIMAL == pType) {
BigDecimal _val1 = (BigDecimal) val1;
BigDecimal _val2 = (BigDecimal) val2;
return _val1.compareTo(_val2);
}
}
return 0;
}
}
/**
* Build an ordered map of PropertyDefinition and reverse order flag
*
* @param orderBy
* @return
*/
private LinkedHashMap<PropertyDefinition<?>, Boolean> parseOrderBy(
String orderBy, CapabilityOrderBy capabilityOrderBy) {
if (StringUtils.isBlank(orderBy)) {
return null;
}
String[] orders = StringUtils.split(orderBy, ",");
LinkedHashMap<PropertyDefinition<?>, Boolean> map = new LinkedHashMap<PropertyDefinition<?>, Boolean>();
for (int i = 0; i < orders.length; i++) {
// Split queryName and its modifier(separated with space)
String[] order = orders[i].split("[\\s]+");
// Property definition
PropertyDefinition<?> pdf = convertToPropertyDefinition(order[0]);
if (pdf == null) {
log.warn("Invalid property query name in orderBy parameter is ignored: propertyId="
+ order[0]);
continue;
} else if (capabilityOrderBy == CapabilityOrderBy.COMMON &&
!isCommonProperty(pdf)) {
log.warn("This property query name in orderBy parameter is not supported when capabilityOrderBy=common: propertyId="
+ order[0]);
continue;
// "orderable" can be different even when the same property id
/*}else if(!pdf.isOrderable()){
log.warn("This property query name in orderBy parameter is not orderable and ignored: propertyId="
+ order[0]);*/
}
// Modifier
Boolean desc = false;
if (order.length > 1) {
if ("DESC".equals(order[1])) {
desc = true;
} else if (StringUtils.isNotBlank(order[1])) {
log.warn("Invalid modifier other than DESC in orderBy parameter is ignored: propertyId="
+ order[0]);
}
}
map.put(pdf, desc);
}
return map;
}
/**
* Get a PropertyDefinition cores from queryName
*
* @param queryName
* @return
*/
private PropertyDefinition<?> convertToPropertyDefinition(String queryName) {
String[] _queryName = StringUtils.split(queryName, ".");
if (_queryName.length == 1) {
return typeManager.getPropertyDefinitionCoreForQueryName(queryName);
} else {
return typeManager
.getPropertyDefinitionCoreForQueryName(_queryName[1]);
}
}
private boolean isCommonProperty(PropertyDefinition<?> propertyDefinition) {
String id = propertyDefinition.getId();
return PropertyIds.NAME.equals(id) || PropertyIds.OBJECT_ID.equals(id)
|| PropertyIds.OBJECT_TYPE_ID.equals(id)
|| PropertyIds.BASE_TYPE_ID.equals(id)
|| PropertyIds.CREATED_BY.equals(id)
|| PropertyIds.CREATION_DATE.equals(id)
|| PropertyIds.LAST_MODIFIED_BY.equals(id)
|| PropertyIds.LAST_MODIFICATION_DATE.equals(id)
|| PropertyIds.IS_IMMUTABLE.equals(id)
|| PropertyIds.IS_PRIVATE_WORKING_COPY.equals(id)
|| PropertyIds.IS_LATEST_VERSION.equals(id)
|| PropertyIds.IS_MAJOR_VERSION.equals(id)
|| PropertyIds.IS_LATEST_MAJOR_VERSION.equals(id)
|| PropertyIds.VERSION_LABEL.equals(id)
|| PropertyIds.VERSION_SERIES_ID.equals(id)
|| PropertyIds.IS_VERSION_SERIES_CHECKED_OUT.equals(id)
|| PropertyIds.VERSION_SERIES_CHECKED_OUT_BY.equals(id)
|| PropertyIds.VERSION_SERIES_CHECKED_OUT_ID.equals(id)
|| PropertyIds.CHECKIN_COMMENT.equals(id)
|| PropertyIds.CONTENT_STREAM_LENGTH.equals(id)
|| PropertyIds.CONTENT_STREAM_MIME_TYPE.equals(id)
|| PropertyIds.CONTENT_STREAM_FILE_NAME.equals(id)
|| PropertyIds.CONTENT_STREAM_ID.equals(id)
|| PropertyIds.PARENT_ID.equals(id)
|| PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS.equals(id)
|| PropertyIds.PATH.equals(id);
}
public void setTypeManager(TypeManager typeManager) {
this.typeManager = typeManager;
}
public void setRepositoryInfoMap(RepositoryInfoMap repositoryInfoMap) {
this.repositoryInfoMap = repositoryInfoMap;
}
public void setPropertyManager(PropertyManager propertyManager) {
this.propertyManager = propertyManager;
}
}