001 /*
002 * Copyright 2008-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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 }