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    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    }