/*
 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library 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.
 *
 * Contributors:
 *     Nuxeo - initial API and implementation
 *
 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
 */

package org.nuxeo.ecm.directory;

import java.io.Serializable;
import java.text.Collator;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DataModel;
import org.nuxeo.ecm.core.api.DocumentModel;

public abstract class AbstractDirectory implements Directory {

    protected DirectoryFieldMapper fieldMapper;

    protected final Map<String, Reference> references = new HashMap<String, Reference>();

    // simple cache system for entry lookups, disabled by default
    protected final DirectoryCache cache = new DirectoryCache();

    /**
     * Invalidate my cache and the caches of linked directories by references
     * @throws ClientException
     */
    public void invalidateCaches() throws DirectoryException {
        cache.invalidateAll();
        for (Reference ref: references.values()) {
            ref.getTargetDirectory().getCache().invalidateAll();
        }
    }

    public DirectoryFieldMapper getFieldMapper() {
        if (fieldMapper == null) {
            fieldMapper = new DirectoryFieldMapper();
        }
        return fieldMapper;
    }

    public Reference getReference(String referenceFieldName) {
        return references.get(referenceFieldName);
    }

    public boolean isReference(String referenceFieldName) {
        return references.containsKey(referenceFieldName);
    }

    public void addReference(Reference reference) throws ClientException {
        reference.setSourceDirectoryName(getName());
        references.put(reference.getFieldName(), reference);
    }

    public void addReferences(Reference[] references) throws ClientException {
        for (Reference reference : references) {
            addReference(reference);
        }
    }

    public Collection<Reference> getReferences() {
        return references.values();
    }

    /**
     * Helper method to order entries.
     *
     * @param entries the list of entries.
     * @param orderBy an ordered map of field name -> "asc" or "desc".
     * @throws DirectoryException
     */
    public void orderEntries(List<DocumentModel> entries,
            Map<String, String> orderBy) throws DirectoryException {
        Collections.sort(entries, new EntryComparator(getSchema(), orderBy));
    }

    public static final String ORDER_ASC = "asc";

    /**
     * DocumentModel comparator. Uses ordering independent of case or accent. If
     * two values are integers/longs, numbering comparison is used.
     */
    public static class EntryComparator implements Comparator<DocumentModel>,
            Serializable {

        private static final long serialVersionUID = -5027588251188034085L;

        static final Collator collator = Collator.getInstance();

        static {
            collator.setStrength(Collator.PRIMARY); // case+accent independent
        }

        final String schemaName;

        final Map<String, String> orderBy;

        EntryComparator(String schemaName, Map<String, String> orderBy) {
            this.schemaName = schemaName;
            this.orderBy = orderBy;
        }

        public int compare(DocumentModel e1, DocumentModel e2) {
            final DataModel d1 = e1.getDataModel(schemaName);
            final DataModel d2 = e2.getDataModel(schemaName);
            for (Entry<String, String> e : orderBy.entrySet()) {
                final String fieldName = e.getKey();
                final boolean asc = ORDER_ASC.equals(e.getValue());
                final Object v1 = d1.getData(fieldName);
                final Object v2 = d2.getData(fieldName);
                if (v1 == null && v2 == null) {
                    continue;
                } else if (v1 == null) {
                    return asc ? -1 : 1;
                } else if (v2 == null) {
                    return asc ? 1 : -1;
                }
                final int cmp;
                if (v1 instanceof Long && v2 instanceof Long) {
                    cmp = ((Long) v1).compareTo((Long) v2);
                } else if (v1 instanceof Integer && v2 instanceof Integer) {
                    cmp = ((Integer) v1).compareTo((Integer) v2);
                } else {
                    cmp = collator.compare(v1.toString(), v2.toString());
                }
                if (cmp == 0) {
                    continue;
                }
                return asc ? cmp : -cmp;
            }
            // everything being equal, provide consistent ordering
            if (e1.hashCode() == e2.hashCode()) {
                return 0;
            } else if (e1.hashCode() < e2.hashCode()) {
                return -1;
            } else {
                return 1;
            }
        }
    }

    public DirectoryCache getCache() {
        return cache;
    }
}

@@ -783,64 +783,8 @@
 y;%0A%0A
-import java.io.Serializable;%0Aimport java.text.Collator;%0A
 impo
@@ -804,24 +804,24 @@
 Collection;%0A
+
 import java.
@@ -842,37 +842,8 @@
 ns;%0A
-import java.util.Comparator;%0A
 impo
@@ -912,36 +912,8 @@
 Map;
-%0Aimport java.util.Map.Entry;
 %0A%0Aim
@@ -961,49 +961,8 @@
 on;%0A
-import org.nuxeo.ecm.core.api.DataModel;%0A
 impo
@@ -1001,16 +1001,71 @@
 ntModel;
+%0Aimport org.nuxeo.ecm.core.api.DocumentModelComparator;
 %0A%0Apublic
@@ -1479,16 +1479,24 @@
 ferences
+.%0A     *
 %0A     * 
@@ -1499,30 +1499,33 @@
   * @throws 
-Client
+Directory
 Exception%0A  
@@ -1650,16 +1650,17 @@
 ence ref
+ 
 : refere
@@ -3041,2463 +3041,71 @@
 new 
-EntryComparator(getSchema(), orderBy));%0A    %7D%0A%0A    public static final String ORDER_ASC = %22asc%22;%0A%0A    /**%0A     * DocumentModel comparator. Uses ordering independent of case or accent. If%0A     * two values are integers/longs, numbering comparison is used.%0A     */%0A    public static class EntryComparator implements Comparator%3CDocumentModel%3E,%0A            Serializable %7B%0A%0A        private static final long serialVersionUID = -5027588251188034085L;%0A%0A        static final Collator collator = Collator.getInstance();%0A%0A        static %7B%0A            collator.setStrength(Collator.PRIMARY); // case+accent independent%0A        %7D%0A%0A        final String schemaName;%0A%0A        final Map%3CString, String%3E orderBy;%0A%0A        EntryComparator(String schemaName, Map%3CString, String%3E orderBy) %7B%0A            this.schemaName = schemaName;%0A            this.orderBy = orderBy;%0A        %7D%0A%0A        public int compare(DocumentModel e1, DocumentModel e2) %7B%0A            final DataModel d1 = e1.getDataModel(schemaName);%0A            final DataModel d2 = e2.getDataModel(schemaName);%0A            for (Entry%3CString, String%3E e : orderBy.entrySet()) %7B%0A                final String fieldName = e.getKey();%0A                final boolean asc = ORDER_ASC.equals(e.getValue());%0A                final Object v1 = d1.getData(fieldName);%0A                final Object v2 = d2.getData(fieldName);%0A                if (v1 == null && v2 == null) %7B%0A                    continue;%0A                %7D else if (v1 == null) %7B%0A                    return asc ? -1 : 1;%0A                %7D else if (v2 == null) %7B%0A                    return asc ? 1 : -1;%0A                %7D%0A                final int cmp;%0A                if (v1 instanceof Long && v2 instanceof Long) %7B%0A                    cmp = ((Long) v1).compareTo((Long) v2);%0A                %7D else if (v1 instanceof Integer && v2 instanceof Integer) %7B%0A                    cmp = ((Integer) v1).compareTo((Integer) v2);%0A                %7D else %7B%0A                    cmp = collator.compare(v1.toString(), v2.toString());%0A                %7D%0A                if (cmp == 0) %7B%0A                    continue;%0A                %7D%0A                return asc ? cmp : -cmp;%0A            %7D%0A            // everything being equal, provide consistent ordering%0A            if (e1.hashCode() == e2.hashCode()) %7B%0A                return 0;%0A            %7D else if (e1.hashCode() %3C e2.hashCode()) %7B%0A                return -1;%0A            %7D else %7B%0A                return 1;%0A            %7D%0A        %7D
+DocumentModelComparator(getSchema(),%0A                orderBy));
 %0A   

use default document model comparator to sort directory entries