001/* 002 * Copyright 2008-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.matchingrules; 022 023 024 025import java.text.ParseException; 026import java.text.SimpleDateFormat; 027import java.util.Date; 028import java.util.TimeZone; 029 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.ldap.sdk.LDAPException; 032import com.unboundid.ldap.sdk.ResultCode; 033import com.unboundid.util.ThreadSafety; 034import com.unboundid.util.ThreadSafetyLevel; 035 036import static com.unboundid.ldap.matchingrules.MatchingRuleMessages.*; 037import static com.unboundid.util.Debug.*; 038import static com.unboundid.util.StaticUtils.*; 039 040 041 042/** 043 * This class provides an implementation of a matching rule that performs 044 * equality and ordering comparisons against values that should be timestamps 045 * in the generalized time syntax. Substring matching is not supported. 046 */ 047@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 048public final class GeneralizedTimeMatchingRule 049 extends MatchingRule 050{ 051 /** 052 * The singleton instance that will be returned from the {@code getInstance} 053 * method. 054 */ 055 private static final GeneralizedTimeMatchingRule INSTANCE = 056 new GeneralizedTimeMatchingRule(); 057 058 059 060 /** 061 * The date format that will be used for formatting generalized time values, 062 * assuming that the associated formatter is using the UTC time zone. 063 */ 064 private static final String GENERALIZED_TIME_DATE_FORMAT = 065 "yyyyMMddHHmmss.SSS'Z'"; 066 067 068 069 /** 070 * A reference to the "UTC" time zone. 071 */ 072 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); 073 074 075 076 /** 077 * The name for the generalizedTimeMatch equality matching rule. 078 */ 079 public static final String EQUALITY_RULE_NAME = "generalizedTimeMatch"; 080 081 082 083 /** 084 * The name for the generalizedTimeMatch equality matching rule, formatted in 085 * all lowercase characters. 086 */ 087 static final String LOWER_EQUALITY_RULE_NAME = 088 toLowerCase(EQUALITY_RULE_NAME); 089 090 091 092 /** 093 * The OID for the generalizedTimeMatch equality matching rule. 094 */ 095 public static final String EQUALITY_RULE_OID = "2.5.13.27"; 096 097 098 099 /** 100 * The name for the generalizedTimeOrderingMatch ordering matching rule. 101 */ 102 public static final String ORDERING_RULE_NAME = 103 "generalizedTimeOrderingMatch"; 104 105 106 107 /** 108 * The name for the generalizedTimeOrderingMatch ordering matching rule, 109 * formatted in all lowercase characters. 110 */ 111 static final String LOWER_ORDERING_RULE_NAME = 112 toLowerCase(ORDERING_RULE_NAME); 113 114 115 116 /** 117 * The OID for the generalizedTimeOrderingMatch ordering matching rule. 118 */ 119 public static final String ORDERING_RULE_OID = "2.5.13.28"; 120 121 122 123 /** 124 * The serial version UID for this serializable class. 125 */ 126 private static final long serialVersionUID = -6317451154598148593L; 127 128 129 130 // The thread-local date formatter for this class. 131 private static final ThreadLocal<SimpleDateFormat> dateFormat = 132 new ThreadLocal<SimpleDateFormat>(); 133 134 135 136 /** 137 * Creates a new instance of this generalized time matching rule. 138 */ 139 public GeneralizedTimeMatchingRule() 140 { 141 // No implementation is required. 142 } 143 144 145 146 /** 147 * Retrieves a singleton instance of this matching rule. 148 * 149 * @return A singleton instance of this matching rule. 150 */ 151 public static GeneralizedTimeMatchingRule getInstance() 152 { 153 return INSTANCE; 154 } 155 156 157 158 /** 159 * {@inheritDoc} 160 */ 161 @Override() 162 public String getEqualityMatchingRuleName() 163 { 164 return EQUALITY_RULE_NAME; 165 } 166 167 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override() 173 public String getEqualityMatchingRuleOID() 174 { 175 return EQUALITY_RULE_OID; 176 } 177 178 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override() 184 public String getOrderingMatchingRuleName() 185 { 186 return ORDERING_RULE_NAME; 187 } 188 189 190 191 /** 192 * {@inheritDoc} 193 */ 194 @Override() 195 public String getOrderingMatchingRuleOID() 196 { 197 return ORDERING_RULE_OID; 198 } 199 200 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override() 206 public String getSubstringMatchingRuleName() 207 { 208 return null; 209 } 210 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override() 217 public String getSubstringMatchingRuleOID() 218 { 219 return null; 220 } 221 222 223 224 /** 225 * {@inheritDoc} 226 */ 227 @Override() 228 public boolean valuesMatch(final ASN1OctetString value1, 229 final ASN1OctetString value2) 230 throws LDAPException 231 { 232 final Date d1; 233 try 234 { 235 d1 = decodeGeneralizedTime(value1.stringValue()); 236 } 237 catch (ParseException pe) 238 { 239 debugException(pe); 240 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 241 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 242 } 243 244 final Date d2; 245 try 246 { 247 d2 = decodeGeneralizedTime(value2.stringValue()); 248 } 249 catch (ParseException pe) 250 { 251 debugException(pe); 252 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 253 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 254 } 255 256 return d1.equals(d2); 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override() 265 public boolean matchesSubstring(final ASN1OctetString value, 266 final ASN1OctetString subInitial, 267 final ASN1OctetString[] subAny, 268 final ASN1OctetString subFinal) 269 throws LDAPException 270 { 271 throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING, 272 ERR_GENERALIZED_TIME_SUBSTRING_MATCHING_NOT_SUPPORTED.get()); 273 } 274 275 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override() 281 public int compareValues(final ASN1OctetString value1, 282 final ASN1OctetString value2) 283 throws LDAPException 284 { 285 final Date d1; 286 try 287 { 288 d1 = decodeGeneralizedTime(value1.stringValue()); 289 } 290 catch (ParseException pe) 291 { 292 debugException(pe); 293 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 294 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 295 } 296 297 final Date d2; 298 try 299 { 300 d2 = decodeGeneralizedTime(value2.stringValue()); 301 } 302 catch (ParseException pe) 303 { 304 debugException(pe); 305 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 306 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 307 } 308 309 return d1.compareTo(d2); 310 } 311 312 313 314 /** 315 * {@inheritDoc} 316 */ 317 @Override() 318 public ASN1OctetString normalize(final ASN1OctetString value) 319 throws LDAPException 320 { 321 final Date d; 322 try 323 { 324 d = decodeGeneralizedTime(value.stringValue()); 325 } 326 catch (ParseException pe) 327 { 328 debugException(pe); 329 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 330 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 331 } 332 333 SimpleDateFormat f = dateFormat.get(); 334 if (f == null) 335 { 336 f = new SimpleDateFormat(GENERALIZED_TIME_DATE_FORMAT); 337 f.setTimeZone(UTC_TIME_ZONE); 338 dateFormat.set(f); 339 } 340 341 return new ASN1OctetString(f.format(d)); 342 } 343 344 345 346 /** 347 * {@inheritDoc} 348 */ 349 @Override() 350 public ASN1OctetString normalizeSubstring(final ASN1OctetString value, 351 final byte substringType) 352 throws LDAPException 353 { 354 throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING, 355 ERR_GENERALIZED_TIME_SUBSTRING_MATCHING_NOT_SUPPORTED.get()); 356 } 357}