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    }