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 */
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 import com.unboundid.util.ThreadSafety;
034 import com.unboundid.util.ThreadSafetyLevel;
035
036 import static com.unboundid.ldap.matchingrules.MatchingRuleMessages.*;
037 import static com.unboundid.util.Debug.*;
038 import 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)
048 public 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 }