001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.core.api.tree;
023
024import java.text.Collator;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.ecm.core.api.DocumentModel;
029import org.nuxeo.ecm.core.api.PropertyException;
030
031/**
032 * Default implementation for document tree sorter.
033 * <p>
034 * Filters on sort property, case insensitively.
035 *
036 * @author Anahide Tchertchian
037 */
038public class DefaultDocumentTreeSorter implements DocumentTreeSorter {
039
040    private static final long serialVersionUID = 1L;
041
042    private static final Log log = LogFactory.getLog(DefaultDocumentTreeSorter.class);
043
044    static final Collator collator = Collator.getInstance();
045
046    static {
047        collator.setStrength(Collator.PRIMARY); // case+accent independent
048    }
049
050    protected String sortPropertyPath;
051
052    @Override
053    public String getSortPropertyPath() {
054        return sortPropertyPath;
055    }
056
057    @Override
058    public void setSortPropertyPath(String sortPropertyPath) {
059        this.sortPropertyPath = sortPropertyPath;
060    }
061
062    @Override
063    public int compare(DocumentModel doc1, DocumentModel doc2) {
064        if (sortPropertyPath == null) {
065            log.error("Cannot sort: no sort property path set");
066            return 0;
067        }
068
069        if (doc1 == null && doc2 == null) {
070            return 0;
071        } else if (doc1 == null) {
072            return -1;
073        } else if (doc2 == null) {
074            return 1;
075        }
076
077        Object v1;
078        try {
079            v1 = doc1.getPropertyValue(sortPropertyPath);
080        } catch (PropertyException e) {
081            v1 = null;
082        }
083        Object v2;
084        try {
085            v2 = doc2.getPropertyValue(sortPropertyPath);
086        } catch (PropertyException e) {
087            v2 = null;
088        }
089        boolean useHash = false;
090        if (v1 == null && v2 == null) {
091            useHash = true;
092        } else if (v1 == null) {
093            return -1;
094        } else if (v2 == null) {
095            return 1;
096        }
097
098        final int cmp;
099        if (v1 instanceof Long && v2 instanceof Long) {
100            cmp = ((Long) v1).compareTo((Long) v2);
101        } else if (v1 instanceof Integer && v2 instanceof Integer) {
102            cmp = ((Integer) v1).compareTo((Integer) v2);
103        } else if (!useHash) { // avoid NPE
104            cmp = collator.compare(v1.toString(), v2.toString());
105        } else {
106            cmp = 0;
107        }
108
109        if (cmp == 0) {
110            useHash = true;
111        }
112        if (useHash) {
113            // everything being equal, provide consistent ordering
114            if (doc1.hashCode() == doc2.hashCode()) {
115                return 0;
116            } else if (doc1.hashCode() < doc2.hashCode()) {
117                return -1;
118            } else {
119                return 1;
120            }
121        }
122        return cmp;
123    }
124
125}