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.file.CmsObject; 031import org.opencms.file.CmsProperty; 032import org.opencms.file.CmsResource; 033import org.opencms.main.CmsException; 034import org.opencms.util.CmsStringUtil; 035import org.opencms.util.CmsUUID; 036 037import java.util.Arrays; 038import java.util.Collections; 039import java.util.Comparator; 040import java.util.HashMap; 041import java.util.List; 042import java.util.Map; 043 044/** 045 * Comparator for sorting resource objects based on dates.<p> 046 * 047 * The comparator can be configured to use any date based on resource attributes or properties. 048 * The user must in the constructor {@link #CmsDateResourceComparator(CmsObject, List, boolean)} 049 * provide a list of one or more <b>date identifiers</b> that should be checked, in the order they 050 * should be checked. This list of dates identifiers must be Strings which tell the comparator which dates to use. 051 * The first valid date identifier that is found for a resource is used as date for 052 * comparing this resource to other resources.<p> 053 * 054 * The following date identifiers can be used 055 * to access the corresponding value of a {@link CmsResource}:<ul> 056 * <li><code>"dateCreated"</code>, which means {@link CmsResource#getDateCreated()}. 057 * <li><code>"dateLastModified"</code>, which means {@link CmsResource#getDateLastModified()}. 058 * <li><code>"dateContent"</code>, which means {@link CmsResource#getDateContent()}. 059 * <li><code>"dateReleased"</code>, which means {@link CmsResource#getDateReleased()}. 060 * <li><code>"dateExpired"</code>, which means {@link CmsResource#getDateExpired()}. 061 * <li>Anything else will be treated as property name, so it will be attempted to read a property 062 * with that name from the resource, and convert that value to a long. Should this fail 063 * for any reason, the next entry from the list of dates will be processed.<p> 064 * <li>If no match is found at all, {@link CmsResource#getDateCreated()} is used as default. 065 * </ul> 066 * 067 * Serves as {@link java.util.Comparator} for resources and as comparator key for the resource 068 * at the same time. Uses lazy initializing of comparator keys for a resource.<p> 069 * 070 * @since 6.0.0 071 */ 072public class CmsDateResourceComparator implements Comparator<CmsResource> { 073 074 /** Possible keywords to read dates from the resource attributes. */ 075 private static final String[] DATE_ATTRIBUTES = { 076 "dateCreated", 077 "dateLastModified", 078 "dateContent", 079 "dateReleased", 080 "dateExpired"}; 081 082 /** Possible keywords to read dates from the resource attributes in a List. */ 083 public static final List<String> DATE_ATTRIBUTES_LIST = Arrays.asList(DATE_ATTRIBUTES); 084 085 /** The date sort order. */ 086 private boolean m_asc; 087 088 /** The current OpenCms user context. */ 089 private CmsObject m_cms; 090 091 /** The date of this comparator key. */ 092 private long m_date; 093 094 /** The list that describes the dates to check, in the order they should be checked. */ 095 private List<String> m_dateIdentifiers; 096 097 /** The internal map of comparator keys. */ 098 private Map<CmsUUID, CmsDateResourceComparator> m_keys; 099 100 /** 101 * Creates a new instance of this comparator key.<p> 102 * 103 * @param cms the current OpenCms user context 104 * @param dateIdentifiers the names of the dates to check 105 * @param asc if true, the date sort order is ascending, otherwise descending 106 */ 107 public CmsDateResourceComparator(CmsObject cms, List<String> dateIdentifiers, boolean asc) { 108 109 m_cms = cms; 110 m_asc = asc; 111 m_dateIdentifiers = dateIdentifiers; 112 if (m_dateIdentifiers == null) { 113 m_dateIdentifiers = Collections.emptyList(); 114 } 115 m_keys = new HashMap<CmsUUID, CmsDateResourceComparator>(); 116 } 117 118 /** 119 * Creates a new, empty instance of this comparator key, used for the calculated map valued.<p> 120 */ 121 private CmsDateResourceComparator() { 122 123 // NOOP 124 } 125 126 /** 127 * Calculates the date to use for comparison of this resource based on the given date identifiers.<p> 128 * 129 * @param cms the current OpenCms user context 130 * @param resource the resource to create the key for 131 * @param dateIdentifiers the date identifiers to use for selecting the date 132 * @param defaultValue the default value to use in case no value can be calculated 133 * 134 * @return the calculated date 135 * 136 * @see CmsDateResourceComparator for a description about how the date identifieres are used 137 */ 138 public static long calculateDate( 139 CmsObject cms, 140 CmsResource resource, 141 List<String> dateIdentifiers, 142 long defaultValue) { 143 144 long result = 0; 145 List<CmsProperty> properties = null; 146 for (int i = 0, size = dateIdentifiers.size(); i < size; i++) { 147 // check all configured comparisons 148 String date = dateIdentifiers.get(i); 149 int pos = DATE_ATTRIBUTES_LIST.indexOf(date); 150 switch (pos) { 151 case 0: // "dateCreated" 152 result = resource.getDateCreated(); 153 break; 154 case 1: // "dateLastModified" 155 result = resource.getDateLastModified(); 156 break; 157 case 2: // "dateContent" 158 if (resource.isFile()) { 159 // date content makes no sense for folders 160 result = resource.getDateContent(); 161 } 162 break; 163 case 3: // "dateReleased" 164 long dr = resource.getDateReleased(); 165 if (dr != CmsResource.DATE_RELEASED_DEFAULT) { 166 // default release date must be ignored 167 result = dr; 168 } 169 break; 170 case 4: // "dateExpired" 171 long de = resource.getDateExpired(); 172 if (de != CmsResource.DATE_EXPIRED_DEFAULT) { 173 // default expiration date must be ignored 174 result = de; 175 } 176 break; 177 default: 178 // of this is not an attribute, assume this is a property 179 if (properties == null) { 180 // we may not have to read the properties since the user may only use attributes, 181 // so use lazy initializing here 182 try { 183 properties = cms.readPropertyObjects(resource, false); 184 } catch (CmsException e) { 185 // use empty list in case of an error, to avoid further re-read tries 186 properties = Collections.emptyList(); 187 } 188 } 189 String propValue = CmsProperty.get(date, properties).getValue(); 190 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(propValue)) { 191 try { 192 result = Long.parseLong(propValue.trim()); 193 } catch (NumberFormatException e) { 194 // maybe we have better luck with the next property 195 } 196 } 197 break; 198 } 199 if (result != 0) { 200 // if a date value has been found, terminate the loop 201 break; 202 } 203 } 204 if (result == 0) { 205 // if nothing else was found, use default 206 result = defaultValue; 207 } 208 return result; 209 } 210 211 /** 212 * Creates a new instance of this comparator key.<p> 213 * 214 * @param cms the current OpenCms user context 215 * @param resource the resource to create the key for 216 * @param dateIdentifiers the date identifiers to use for selecting the date 217 * 218 * @return a new instance of this comparator key 219 */ 220 private static CmsDateResourceComparator create(CmsObject cms, CmsResource resource, List<String> dateIdentifiers) { 221 222 CmsDateResourceComparator result = new CmsDateResourceComparator(); 223 result.m_date = calculateDate(cms, resource, dateIdentifiers, resource.getDateCreated()); 224 return result; 225 } 226 227 /** 228 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 229 */ 230 public int compare(CmsResource res0, CmsResource res1) { 231 232 if (res0 == res1) { 233 return 0; 234 } 235 236 CmsDateResourceComparator key0 = m_keys.get(res0.getStructureId()); 237 CmsDateResourceComparator key1 = m_keys.get(res1.getStructureId()); 238 239 if (key0 == null) { 240 // initialize key if null 241 key0 = CmsDateResourceComparator.create(m_cms, res0, m_dateIdentifiers); 242 m_keys.put(res0.getStructureId(), key0); 243 } 244 if (key1 == null) { 245 // initialize key if null 246 key1 = CmsDateResourceComparator.create(m_cms, res1, m_dateIdentifiers); 247 m_keys.put(res1.getStructureId(), key1); 248 } 249 250 if (m_asc) { 251 // sort in ascending order 252 if (key0.m_date > key1.m_date) { 253 return 1; 254 } 255 if (key0.m_date < key1.m_date) { 256 return -1; 257 } 258 } else { 259 // sort in descending order 260 if (key0.m_date > key1.m_date) { 261 return -1; 262 } 263 if (key0.m_date < key1.m_date) { 264 return 1; 265 } 266 } 267 268 return 0; 269 } 270 271 /** 272 * Returns the date of this resource comparator key.<p> 273 * 274 * @return the date of this resource comparator key 275 */ 276 public long getDate() { 277 278 return m_date; 279 } 280}