001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
011 *
012 * $Id$
013 */
014
015package org.nuxeo.ecm.core.api.tree;
016
017import java.text.Collator;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021import org.nuxeo.ecm.core.api.DocumentModel;
022import org.nuxeo.ecm.core.api.PropertyException;
023
024/**
025 * Default implementation for document tree sorter.
026 * <p>
027 * Filters on sort property, case insensitively.
028 *
029 * @author Anahide Tchertchian
030 */
031public class DefaultDocumentTreeSorter implements DocumentTreeSorter {
032
033    private static final long serialVersionUID = 1L;
034
035    private static final Log log = LogFactory.getLog(DefaultDocumentTreeSorter.class);
036
037    static final Collator collator = Collator.getInstance();
038
039    static {
040        collator.setStrength(Collator.PRIMARY); // case+accent independent
041    }
042
043    protected String sortPropertyPath;
044
045    @Override
046    public String getSortPropertyPath() {
047        return sortPropertyPath;
048    }
049
050    @Override
051    public void setSortPropertyPath(String sortPropertyPath) {
052        this.sortPropertyPath = sortPropertyPath;
053    }
054
055    @Override
056    public int compare(DocumentModel doc1, DocumentModel doc2) {
057        if (sortPropertyPath == null) {
058            log.error("Cannot sort: no sort property path set");
059            return 0;
060        }
061
062        if (doc1 == null && doc2 == null) {
063            return 0;
064        } else if (doc1 == null) {
065            return -1;
066        } else if (doc2 == null) {
067            return 1;
068        }
069
070        Object v1;
071        try {
072            v1 = doc1.getPropertyValue(sortPropertyPath);
073        } catch (PropertyException e) {
074            v1 = null;
075        }
076        Object v2;
077        try {
078            v2 = doc2.getPropertyValue(sortPropertyPath);
079        } catch (PropertyException e) {
080            v2 = null;
081        }
082        boolean useHash = false;
083        if (v1 == null && v2 == null) {
084            useHash = true;
085        } else if (v1 == null) {
086            return -1;
087        } else if (v2 == null) {
088            return 1;
089        }
090
091        final int cmp;
092        if (v1 instanceof Long && v2 instanceof Long) {
093            cmp = ((Long) v1).compareTo((Long) v2);
094        } else if (v1 instanceof Integer && v2 instanceof Integer) {
095            cmp = ((Integer) v1).compareTo((Integer) v2);
096        } else if (!useHash) { // avoid NPE
097            cmp = collator.compare(v1.toString(), v2.toString());
098        } else {
099            cmp = 0;
100        }
101
102        if (cmp == 0) {
103            useHash = true;
104        }
105        if (useHash) {
106            // everything being equal, provide consistent ordering
107            if (doc1.hashCode() == doc2.hashCode()) {
108                return 0;
109            } else if (doc1.hashCode() < doc2.hashCode()) {
110                return -1;
111            } else {
112                return 1;
113            }
114        }
115        return cmp;
116    }
117
118}