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