001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.file.collectors;
029
030import org.opencms.db.CmsDbEntryNotFoundException;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProperty;
033import org.opencms.file.CmsResource;
034import org.opencms.main.CmsException;
035import org.opencms.main.CmsIllegalArgumentException;
036import org.opencms.util.CmsStringUtil;
037import org.opencms.util.CmsUUID;
038
039import java.io.Serializable;
040import java.text.Collator;
041import java.text.NumberFormat;
042import java.text.ParsePosition;
043import java.util.Comparator;
044import java.util.HashMap;
045import java.util.Map;
046
047/**
048 * Comparator for sorting resource objects based on a selected property value.<p>
049 *
050 * Serves as {@link java.util.Comparator} for resources and as comparator key for the resource
051 * at the same time. Uses lazy initializing of comparator keys in a resource.<p>
052 *
053 * @since 8.0.4
054 */
055public class CmsPropertyResourceComparator implements Serializable, Comparator<CmsResource> {
056
057    /** Serial version UID required for safe serialization. */
058    private static final long serialVersionUID = -7943213182160054552L;
059
060    /** The sort order. */
061    private boolean m_asc;
062
063    /** The current OpenCms user context. */
064    private transient CmsObject m_cms;
065
066    /** The internal map of comparator keys. */
067    private Map<CmsUUID, CmsPropertyResourceComparator> m_keys;
068
069    /** The property name of this comparator key. */
070    private String m_property;
071
072    /** The property value of this comparator key. */
073    private String m_propertyValue;
074
075    /**
076     * Creates a new instance of this comparator key.<p>
077     *
078     * @param cms the current OpenCms user context
079     * @param property the name of the sort property (case sensitive)
080     * @param asc the sort order (true=asc, false=desc)
081     */
082    public CmsPropertyResourceComparator(CmsObject cms, String property, boolean asc) {
083
084        m_property = property;
085        m_asc = asc;
086        m_cms = cms;
087        m_keys = new HashMap<CmsUUID, CmsPropertyResourceComparator>();
088    }
089
090    /**
091     * Creates a new instance of this comparator key.<p>
092     *
093     * @param resource the resource to create the key for
094     * @param cms the current OpenCms user context
095     * @param property the name of the sort property (case sensitive)
096     *
097     * @return a new instance of this comparator key
098     */
099    private static CmsPropertyResourceComparator create(CmsResource resource, CmsObject cms, String property) {
100
101        CmsPropertyResourceComparator result = new CmsPropertyResourceComparator(null, null, false);
102        result.init(resource, cms, property);
103        return result;
104    }
105
106    /**
107     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
108     */
109    public int compare(CmsResource res0, CmsResource res1) {
110
111        if (res0 == res1) {
112            return 0;
113        }
114
115        CmsPropertyResourceComparator key0 = m_keys.get(res0.getStructureId());
116        CmsPropertyResourceComparator key1 = m_keys.get(res1.getStructureId());
117
118        if (key0 == null) {
119            // initialize key if null
120            key0 = CmsPropertyResourceComparator.create(res0, m_cms, m_property);
121            m_keys.put(res0.getStructureId(), key0);
122        }
123        if (key1 == null) {
124            // initialize key if null
125            key1 = CmsPropertyResourceComparator.create(res1, m_cms, m_property);
126            m_keys.put(res1.getStructureId(), key1);
127        }
128
129        // select a different sort methode for strings or number values
130        if (isNumeric(key0.getPropertyValue()) && isNumeric(key1.getPropertyValue())) {
131            // double can do it all.... ;-)
132            double dKey0 = Double.parseDouble(key0.getPropertyValue());
133            double dKey1 = Double.parseDouble(key1.getPropertyValue());
134
135            if (m_asc) {
136                // sort in ascending order
137                if (dKey0 > dKey1) {
138                    return 1;
139                }
140                if (dKey0 < dKey1) {
141                    return -1;
142                }
143            } else {
144                // sort in descending order
145                if (dKey0 > dKey1) {
146                    return -1;
147                }
148                if (dKey0 < dKey1) {
149                    return 1;
150                }
151            }
152
153        } else {
154            // sort by property value depending on the locale
155            Collator collator = Collator.getInstance(m_cms.getRequestContext().getLocale());
156            if (m_asc) {
157                // sort in ascending order
158                return collator.compare(key0.getPropertyValue(), key1.getPropertyValue());
159            } else {
160                // sort in descending order
161                return collator.compare(key1.getPropertyValue(), key0.getPropertyValue());
162            }
163        }
164        return 0;
165    }
166
167    /**
168     * Returns the property value of this resource comparator key.<p>
169     *
170     * @return property value of this resource comparator key
171     */
172    public String getPropertyValue() {
173
174        if (!CmsStringUtil.isEmpty(m_propertyValue)) {
175            return m_propertyValue.trim();
176        } else {
177            return "";
178        }
179    }
180
181    /**
182     * Initializes the comparator key based on the member variables.<p>
183     *
184     * @param resource the resource to use
185     * @param cms the current OpenCms user contxt
186     * @param property the name of the sort property (case sensitive)
187     */
188    private void init(CmsResource resource, CmsObject cms, String property) {
189
190        try {
191            cms.readPropertyDefinition(property);
192            CmsProperty prop = cms.readPropertyObject(resource, property, false);
193
194            if (prop == CmsProperty.getNullProperty()) {
195                m_propertyValue = "";
196            } else {
197                m_propertyValue = prop.getValue();
198            }
199        } catch (CmsDbEntryNotFoundException dbe) {
200            // property are not configured
201            throw new CmsIllegalArgumentException(
202                Messages.get().container(Messages.ERR_COLLECTOR_PARAM_PROPERTY_NOT_FOUND_1, property));
203        } catch (CmsException cmse) {
204            // something's gone wrong...
205            cmse.printStackTrace();
206        }
207    }
208
209    /**
210     * Check if a string contains a numeric value.<p>
211     *
212     * @param value string to check if is a numeric value
213     *
214     * @return true for a numeric value in the string
215     */
216    private boolean isNumeric(String value) {
217
218        if (!CmsStringUtil.isEmpty(value)) {
219            NumberFormat formatter = NumberFormat.getInstance();
220            ParsePosition pos = new ParsePosition(0);
221            formatter.parse(value, pos);
222            return value.length() == pos.getIndex();
223        } else {
224            return false;
225        }
226    }
227
228}