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.sdk.controls;
022    
023    
024    
025    import java.io.Serializable;
026    import java.util.ArrayList;
027    import java.util.Arrays;
028    
029    import com.unboundid.asn1.ASN1Element;
030    import com.unboundid.asn1.ASN1OctetString;
031    import com.unboundid.asn1.ASN1Sequence;
032    import com.unboundid.ldap.sdk.Filter;
033    import com.unboundid.ldap.sdk.LDAPException;
034    import com.unboundid.ldap.sdk.ResultCode;
035    import com.unboundid.util.NotMutable;
036    import com.unboundid.util.ThreadSafety;
037    import com.unboundid.util.ThreadSafetyLevel;
038    
039    import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
040    import static com.unboundid.util.Debug.*;
041    import static com.unboundid.util.StaticUtils.*;
042    import static com.unboundid.util.Validator.*;
043    
044    
045    
046    /**
047     * This class provides an implementation of the simple filter item for use with
048     * the {@link MatchedValuesRequestControl} as defined in
049     * <A HREF="http://www.ietf.org/rfc/rfc3876.txt">RFC 3876</A>.  It is similar to
050     * a search filter (see the {@link com.unboundid.ldap.sdk.Filter} class), but
051     * may only contain a single element (i.e., no AND, OR, or NOT components are
052     * allowed), and extensible matching does not allow the use of the dnAttributes
053     * field.
054     */
055    @NotMutable()
056    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
057    public final class MatchedValuesFilter
058           implements Serializable
059    {
060      /**
061       * The match type that will be used for equality match filters.
062       */
063      public static final byte MATCH_TYPE_EQUALITY = (byte) 0xA3;
064    
065    
066    
067      /**
068       * The match type that will be used for substring match filters.
069       */
070      public static final byte MATCH_TYPE_SUBSTRINGS = (byte) 0xA4;
071    
072    
073    
074      /**
075       * The match type that will be used for greater-or-equal match filters.
076       */
077      public static final byte MATCH_TYPE_GREATER_OR_EQUAL = (byte) 0xA5;
078    
079    
080    
081      /**
082       * The match type that will be used for less-or-equal match filters.
083       */
084      public static final byte MATCH_TYPE_LESS_OR_EQUAL = (byte) 0xA6;
085    
086    
087    
088      /**
089       * The match type that will be used for presence match filters.
090       */
091      public static final byte MATCH_TYPE_PRESENT = (byte) 0x87;
092    
093    
094    
095      /**
096       * The match type that will be used for approximate match filters.
097       */
098      public static final byte MATCH_TYPE_APPROXIMATE = (byte) 0xA8;
099    
100    
101    
102      /**
103       * The match type that will be used for extensible match filters.
104       */
105      public static final byte MATCH_TYPE_EXTENSIBLE = (byte) 0xA9;
106    
107    
108    
109      /**
110       * The BER type for the subInitial substring filter element.
111       */
112      private static final byte SUBSTRING_TYPE_SUBINITIAL = (byte) 0x80;
113    
114    
115    
116      /**
117       * The BER type for the subAny substring filter element.
118       */
119      private static final byte SUBSTRING_TYPE_SUBANY = (byte) 0x81;
120    
121    
122    
123      /**
124       * The BER type for the subFinal substring filter element.
125       */
126      private static final byte SUBSTRING_TYPE_SUBFINAL = (byte) 0x82;
127    
128    
129    
130      /**
131       * The BER type for the matching rule ID extensible match filter element.
132       */
133      private static final byte EXTENSIBLE_TYPE_MATCHING_RULE_ID = (byte) 0x81;
134    
135    
136    
137      /**
138       * The BER type for the attribute name extensible match filter element.
139       */
140      private static final byte EXTENSIBLE_TYPE_ATTRIBUTE_NAME = (byte) 0x82;
141    
142    
143    
144      /**
145       * The BER type for the match value extensible match filter element.
146       */
147      private static final byte EXTENSIBLE_TYPE_MATCH_VALUE = (byte) 0x83;
148    
149    
150    
151      /**
152       * An empty array that will be used if there are no subAny elements.
153       */
154      private static final ASN1OctetString[] NO_SUB_ANY = new ASN1OctetString[0];
155    
156    
157    
158      /**
159       * An empty array that will be used if there are no subAny elements.
160       */
161      private static final String[] NO_SUB_ANY_STRINGS = NO_STRINGS;
162    
163    
164    
165      /**
166       * An empty array that will be used if there are no subAny elements.
167       */
168      private static final byte[][] NO_SUB_ANY_BYTES = new byte[0][];
169    
170    
171    
172      /**
173       * The serial version UID for this serializable class.
174       */
175      private static final long serialVersionUID = 8144732301100674661L;
176    
177    
178    
179      // The name of the attribute type to include in this filter, if appropriate.
180      private final ASN1OctetString assertionValue;
181    
182      // The subFinal value for this filter, if appropriate.
183      private final ASN1OctetString subFinalValue;
184    
185      // The subInitial value for this filter, if appropriate.
186      private final ASN1OctetString subInitialValue;
187    
188      // The subAny values for this filter, if appropriate.
189      private final ASN1OctetString[] subAnyValues;
190    
191      // The filter type for this filter.
192      private final byte matchType;
193    
194      // The name of the attribute type to include in this filter, if appropriate.
195      private final String attributeType;
196    
197      // The matching rule ID for this filter, if appropriate.
198      private final String matchingRuleID;
199    
200    
201    
202      /**
203       * Creates a new matched values filter with the provided information.
204       *
205       * @param  matchType        The filter type for this filter.
206       * @param  attributeType    The name of the attribute type.  It may be
207       *                          {@code null} only for extensible match filters and
208       *                          only if a non-{@code null} matching rule ID is
209       *                          provided.
210       * @param  assertionValue   The assertion value for this filter.  It may only
211       *                          be {@code null} for substring and presence
212       *                          filters.
213       * @param  subInitialValue  The subInitial value for this filter.  It may only
214       *                          be provided for substring filters.
215       * @param  subAnyValues     The set of subAny values for this filter.  It may
216       *                          only be provided for substring filters.
217       * @param  subFinalValue    The subFinal value for this filter.  It may only
218       *                          be provided for substring filters.
219       * @param  matchingRuleID   The matching rule ID for this filter.  It may only
220       *                          be provided for extensible match filters.
221       */
222      private MatchedValuesFilter(final byte matchType, final String attributeType,
223                                  final ASN1OctetString assertionValue,
224                                  final ASN1OctetString subInitialValue,
225                                  final ASN1OctetString[] subAnyValues,
226                                  final ASN1OctetString subFinalValue,
227                                  final String matchingRuleID)
228      {
229        this.matchType       = matchType;
230        this.attributeType   = attributeType;
231        this.assertionValue  = assertionValue;
232        this.subInitialValue = subInitialValue;
233        this.subAnyValues    = subAnyValues;
234        this.subFinalValue   = subFinalValue;
235        this.matchingRuleID  = matchingRuleID;
236      }
237    
238    
239    
240      /**
241       * Creates a new matched values filter for equality matching with the provided
242       * information.
243       *
244       * @param  attributeType   The attribute type for the filter.  It must not be
245       *                         {@code null}.
246       * @param  assertionValue  The assertion value for the filter.  It must not be
247       *                         {@code null}.
248       *
249       * @return  The created equality match filter.
250       */
251      public static MatchedValuesFilter createEqualityFilter(
252                                             final String attributeType,
253                                             final String assertionValue)
254      {
255        ensureNotNull(attributeType, assertionValue);
256    
257        return new MatchedValuesFilter(MATCH_TYPE_EQUALITY, attributeType,
258                                       new ASN1OctetString(assertionValue), null,
259                                       NO_SUB_ANY, null, null);
260      }
261    
262    
263    
264      /**
265       * Creates a new matched values filter for equality matching with the provided
266       * information.
267       *
268       * @param  attributeType   The attribute type for the filter.  It must not be
269       *                         {@code null}.
270       * @param  assertionValue  The assertion value for the filter.  It must not be
271       *                         {@code null}.
272       *
273       * @return  The created equality match filter.
274       */
275      public static MatchedValuesFilter createEqualityFilter(
276                                             final String attributeType,
277                                             final byte[] assertionValue)
278      {
279        ensureNotNull(attributeType, assertionValue);
280    
281        return new MatchedValuesFilter(MATCH_TYPE_EQUALITY, attributeType,
282                                       new ASN1OctetString(assertionValue), null,
283                                       NO_SUB_ANY, null, null);
284      }
285    
286    
287    
288      /**
289       * Creates a new matched values filter for substring matching with the
290       * provided information.  At least one substring filter element must be
291       * provided.
292       *
293       * @param  attributeType    The attribute type for the filter.  It must not be
294       *                          {@code null}.
295       * @param  subInitialValue  The subInitial value for the filter, or
296       *                          {@code null} if there is no subInitial element.
297       * @param  subAnyValues     The set of subAny values for the filter, or
298       *                          {@code null} if there are no subAny elements.
299       * @param  subFinalValue    The subFinal value for the filter, or {@code null}
300       *                          if there is no subFinal element.
301       *
302       * @return  The created equality match filter.
303       */
304      public static MatchedValuesFilter createSubstringFilter(
305                                             final String attributeType,
306                                             final String subInitialValue,
307                                             final String[] subAnyValues,
308                                             final String subFinalValue)
309      {
310        ensureNotNull(attributeType);
311        ensureTrue((subInitialValue != null) ||
312                   ((subAnyValues != null) && (subAnyValues.length > 0)) ||
313                   (subFinalValue != null));
314    
315        final ASN1OctetString subInitialOS;
316        if (subInitialValue == null)
317        {
318          subInitialOS = null;
319        }
320        else
321        {
322          subInitialOS = new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL,
323                                             subInitialValue);
324        }
325    
326        final ASN1OctetString[] subAnyOS;
327        if ((subAnyValues == null) || (subAnyValues.length == 0))
328        {
329          subAnyOS = NO_SUB_ANY;
330        }
331        else
332        {
333          subAnyOS = new ASN1OctetString[subAnyValues.length];
334          for (int i=0; i < subAnyValues.length; i++)
335          {
336            subAnyOS[i] = new ASN1OctetString(SUBSTRING_TYPE_SUBANY,
337                                              subAnyValues[i]);
338          }
339        }
340    
341        final ASN1OctetString subFinalOS;
342        if (subFinalValue == null)
343        {
344          subFinalOS = null;
345        }
346        else
347        {
348          subFinalOS = new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, subFinalValue);
349        }
350    
351        return new MatchedValuesFilter(MATCH_TYPE_SUBSTRINGS, attributeType, null,
352                                       subInitialOS, subAnyOS, subFinalOS, null);
353      }
354    
355    
356    
357      /**
358       * Creates a new matched values filter for substring matching with the
359       * provided information.  At least one substring filter element must be
360       * provided.
361       *
362       * @param  attributeType    The attribute type for the filter.  It must not be
363       *                          {@code null}.
364       * @param  subInitialValue  The subInitial value for the filter, or
365       *                          {@code null} if there is no subInitial element.
366       * @param  subAnyValues     The set of subAny values for the filter, or
367       *                          {@code null} if there are no subAny elements.
368       * @param  subFinalValue    The subFinal value for the filter, or {@code null}
369       *                          if there is no subFinal element.
370       *
371       * @return  The created equality match filter.
372       */
373      public static MatchedValuesFilter createSubstringFilter(
374                                             final String attributeType,
375                                             final byte[] subInitialValue,
376                                             final byte[][] subAnyValues,
377                                             final byte[] subFinalValue)
378      {
379        ensureNotNull(attributeType);
380        ensureTrue((subInitialValue != null) ||
381                   ((subAnyValues != null) && (subAnyValues.length > 0)) ||
382                   (subFinalValue != null));
383    
384        final ASN1OctetString subInitialOS;
385        if (subInitialValue == null)
386        {
387          subInitialOS = null;
388        }
389        else
390        {
391          subInitialOS = new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL,
392                                             subInitialValue);
393        }
394    
395        final ASN1OctetString[] subAnyOS;
396        if ((subAnyValues == null) || (subAnyValues.length == 0))
397        {
398          subAnyOS = NO_SUB_ANY;
399        }
400        else
401        {
402          subAnyOS = new ASN1OctetString[subAnyValues.length];
403          for (int i=0; i < subAnyValues.length; i++)
404          {
405            subAnyOS[i] = new ASN1OctetString(SUBSTRING_TYPE_SUBANY,
406                                              subAnyValues[i]);
407          }
408        }
409    
410        final ASN1OctetString subFinalOS;
411        if (subFinalValue == null)
412        {
413          subFinalOS = null;
414        }
415        else
416        {
417          subFinalOS = new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, subFinalValue);
418        }
419    
420        return new MatchedValuesFilter(MATCH_TYPE_SUBSTRINGS, attributeType, null,
421                                       subInitialOS, subAnyOS, subFinalOS, null);
422      }
423    
424    
425    
426      /**
427       * Creates a new matched values filter for greater-or-equal matching with the
428       * provided information.
429       *
430       * @param  attributeType   The attribute type for the filter.  It must not be
431       *                         {@code null}.
432       * @param  assertionValue  The assertion value for the filter.  It must not be
433       *                         {@code null}.
434       *
435       * @return  The created greater-or-equal match filter.
436       */
437      public static MatchedValuesFilter createGreaterOrEqualFilter(
438                                             final String attributeType,
439                                             final String assertionValue)
440      {
441        ensureNotNull(attributeType, assertionValue);
442    
443        return new MatchedValuesFilter(MATCH_TYPE_GREATER_OR_EQUAL, attributeType,
444                                       new ASN1OctetString(assertionValue), null,
445                                       NO_SUB_ANY, null, null);
446      }
447    
448    
449    
450      /**
451       * Creates a new matched values filter for greater-or-equal matching with the
452       * provided information.
453       *
454       * @param  attributeType   The attribute type for the filter.  It must not be
455       *                         {@code null}.
456       * @param  assertionValue  The assertion value for the filter.  It must not be
457       *                         {@code null}.
458       *
459       * @return  The created greater-or-equal match filter.
460       */
461      public static MatchedValuesFilter createGreaterOrEqualFilter(
462                                             final String attributeType,
463                                             final byte[] assertionValue)
464      {
465        ensureNotNull(attributeType, assertionValue);
466    
467        return new MatchedValuesFilter(MATCH_TYPE_GREATER_OR_EQUAL, attributeType,
468                                       new ASN1OctetString(assertionValue), null,
469                                       NO_SUB_ANY, null, null);
470      }
471    
472    
473    
474      /**
475       * Creates a new matched values filter for less-or-equal matching with the
476       * provided information.
477       *
478       * @param  attributeType   The attribute type for the filter.  It must not be
479       *                         {@code null}.
480       * @param  assertionValue  The assertion value for the filter.  It must not be
481       *                         {@code null}.
482       *
483       * @return  The created less-or-equal match filter.
484       */
485      public static MatchedValuesFilter createLessOrEqualFilter(
486                                             final String attributeType,
487                                             final String assertionValue)
488      {
489        ensureNotNull(attributeType, assertionValue);
490    
491        return new MatchedValuesFilter(MATCH_TYPE_LESS_OR_EQUAL, attributeType,
492                                       new ASN1OctetString(assertionValue), null,
493                                       NO_SUB_ANY, null, null);
494      }
495    
496    
497    
498      /**
499       * Creates a new matched values filter for less-or-equal matching with the
500       * provided information.
501       *
502       * @param  attributeType   The attribute type for the filter.  It must not be
503       *                         {@code null}.
504       * @param  assertionValue  The assertion value for the filter.  It must not be
505       *                         {@code null}.
506       *
507       * @return  The created less-or-equal match filter.
508       */
509      public static MatchedValuesFilter createLessOrEqualFilter(
510                                             final String attributeType,
511                                             final byte[] assertionValue)
512      {
513        ensureNotNull(attributeType, assertionValue);
514    
515        return new MatchedValuesFilter(MATCH_TYPE_LESS_OR_EQUAL, attributeType,
516                                       new ASN1OctetString(assertionValue), null,
517                                       NO_SUB_ANY, null, null);
518      }
519    
520    
521    
522      /**
523       * Creates a new matched values filter for presence matching with the provided
524       * information.
525       *
526       * @param  attributeType  The attribute type for the filter.  It must not be
527       *                        {@code null}.
528       *
529       * @return  The created present match filter.
530       */
531      public static MatchedValuesFilter createPresentFilter(
532                                             final String attributeType)
533      {
534        ensureNotNull(attributeType);
535    
536        return new MatchedValuesFilter(MATCH_TYPE_PRESENT, attributeType, null,
537                                       null, NO_SUB_ANY, null, null);
538      }
539    
540    
541    
542      /**
543       * Creates a new matched values filter for approximate matching with the
544       * provided information.
545       *
546       * @param  attributeType   The attribute type for the filter.  It must not be
547       *                         {@code null}.
548       * @param  assertionValue  The assertion value for the filter.  It must not be
549       *                         {@code null}.
550       *
551       * @return  The created approximate match filter.
552       */
553      public static MatchedValuesFilter createApproximateFilter(
554                                             final String attributeType,
555                                             final String assertionValue)
556      {
557        ensureNotNull(attributeType, assertionValue);
558    
559        return new MatchedValuesFilter(MATCH_TYPE_APPROXIMATE, attributeType,
560                                       new ASN1OctetString(assertionValue), null,
561                                       NO_SUB_ANY, null, null);
562      }
563    
564    
565    
566      /**
567       * Creates a new matched values filter for approximate matching with the
568       * provided information.
569       *
570       * @param  attributeType   The attribute type for the filter.  It must not be
571       *                         {@code null}.
572       * @param  assertionValue  The assertion value for the filter.  It must not be
573       *                         {@code null}.
574       *
575       * @return  The created greater-or-equal match filter.
576       */
577      public static MatchedValuesFilter createApproximateFilter(
578                                             final String attributeType,
579                                             final byte[] assertionValue)
580      {
581        ensureNotNull(attributeType, assertionValue);
582    
583        return new MatchedValuesFilter(MATCH_TYPE_APPROXIMATE, attributeType,
584                                       new ASN1OctetString(assertionValue), null,
585                                       NO_SUB_ANY, null, null);
586      }
587    
588    
589    
590      /**
591       * Creates a new matched values filter for extensible matching with the
592       * provided information.  At least one of the attribute type and matching rule
593       * ID must be provided.
594       *
595       * @param  attributeType   The attribute type for the filter, or {@code null}
596       *                         if there is no attribute type.
597       * @param  matchingRuleID  The matching rule ID for the filter, or
598       *                         {@code null} if there is no matching rule ID.
599       * @param  assertionValue  The assertion value for the filter.  It must not be
600       *                         {@code null}.
601       *
602       * @return  The created extensible match filter.
603       */
604      public static MatchedValuesFilter createExtensibleMatchFilter(
605                                             final String attributeType,
606                                             final String matchingRuleID,
607                                             final String assertionValue)
608      {
609        ensureNotNull(assertionValue);
610        ensureTrue((attributeType != null) || (matchingRuleID != null));
611    
612        final ASN1OctetString matchValue =
613             new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, assertionValue);
614    
615        return new MatchedValuesFilter(MATCH_TYPE_EXTENSIBLE, attributeType,
616                                       matchValue, null, NO_SUB_ANY, null,
617                                       matchingRuleID);
618      }
619    
620    
621    
622      /**
623       * Creates a new matched values filter for extensible matching with the
624       * provided information.  At least one of the attribute type and matching rule
625       * ID must be provided.
626       *
627       * @param  attributeType   The attribute type for the filter, or {@code null}
628       *                         if there is no attribute type.
629       * @param  matchingRuleID  The matching rule ID for the filter, or
630       *                         {@code null} if there is no matching rule ID.
631       * @param  assertionValue  The assertion value for the filter.  It must not be
632       *                         {@code null}.
633       *
634       * @return  The created extensible match filter.
635       */
636      public static MatchedValuesFilter createExtensibleMatchFilter(
637                                             final String attributeType,
638                                             final String matchingRuleID,
639                                             final byte[] assertionValue)
640      {
641        ensureNotNull(assertionValue);
642        ensureTrue((attributeType != null) || (matchingRuleID != null));
643    
644        final ASN1OctetString matchValue =
645             new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, assertionValue);
646    
647        return new MatchedValuesFilter(MATCH_TYPE_EXTENSIBLE, attributeType,
648                                       matchValue, null, NO_SUB_ANY, null,
649                                       matchingRuleID);
650      }
651    
652    
653    
654      /**
655       * Creates a new matched values filter from the provided search filter, if
656       * possible.
657       *
658       * @param  filter  The search filter to use to create this matched values
659       *                 filter.
660       *
661       * @return  The search filter that corresponds to this matched values filter.
662       *
663       * @throws  LDAPException  If the provided search filter cannot be represented
664       *                         as a matched values filter.
665       */
666      public static MatchedValuesFilter create(final Filter filter)
667             throws LDAPException
668      {
669        switch (filter.getFilterType())
670        {
671          case Filter.FILTER_TYPE_AND:
672            throw new LDAPException(ResultCode.DECODING_ERROR,
673                                    ERR_MV_FILTER_AND_NOT_SUPPORTED.get());
674    
675          case Filter.FILTER_TYPE_OR:
676            throw new LDAPException(ResultCode.DECODING_ERROR,
677                                    ERR_MV_FILTER_OR_NOT_SUPPORTED.get());
678    
679          case Filter.FILTER_TYPE_NOT:
680            throw new LDAPException(ResultCode.DECODING_ERROR,
681                                    ERR_MV_FILTER_NOT_NOT_SUPPORTED.get());
682    
683          case Filter.FILTER_TYPE_EQUALITY:
684            return createEqualityFilter(filter.getAttributeName(),
685                        filter.getAssertionValueBytes());
686    
687          case Filter.FILTER_TYPE_SUBSTRING:
688            return createSubstringFilter(filter.getAttributeName(),
689                        filter.getSubInitialBytes(), filter.getSubAnyBytes(),
690                        filter.getSubFinalBytes());
691    
692          case Filter.FILTER_TYPE_GREATER_OR_EQUAL:
693            return createGreaterOrEqualFilter(filter.getAttributeName(),
694                        filter.getAssertionValueBytes());
695    
696          case Filter.FILTER_TYPE_LESS_OR_EQUAL:
697            return createLessOrEqualFilter(filter.getAttributeName(),
698                        filter.getAssertionValueBytes());
699    
700          case Filter.FILTER_TYPE_PRESENCE:
701            return createPresentFilter(filter.getAttributeName());
702    
703          case Filter.FILTER_TYPE_APPROXIMATE_MATCH:
704            return createApproximateFilter(filter.getAttributeName(),
705                        filter.getAssertionValueBytes());
706    
707          case Filter.FILTER_TYPE_EXTENSIBLE_MATCH:
708            if (filter.getDNAttributes())
709            {
710              throw new LDAPException(ResultCode.DECODING_ERROR,
711                                      ERR_MV_FILTER_DNATTRS_NOT_SUPPORTED.get());
712            }
713    
714            return createExtensibleMatchFilter(filter.getAttributeName(),
715                        filter.getMatchingRuleID(),
716                        filter.getAssertionValueBytes());
717    
718          default:
719            // This should never happen.
720            throw new LDAPException(ResultCode.DECODING_ERROR,
721                 ERR_MV_FILTER_INVALID_FILTER_TYPE.get(
722                      toHex(filter.getFilterType())));
723        }
724      }
725    
726    
727    
728      /**
729       * Retrieves the match type for this matched values filter.
730       *
731       * @return  The match type for this matched values filter.
732       */
733      public byte getMatchType()
734      {
735        return matchType;
736      }
737    
738    
739    
740      /**
741       * Retrieves the name of the attribute type for this matched values filter,
742       * if available.
743       *
744       * @return  The name of the attribute type for this matched values filter, or
745       *          {@code null} if there is none.
746       */
747      public String getAttributeType()
748      {
749        return attributeType;
750      }
751    
752    
753    
754      /**
755       * Retrieves the string representation of the assertion value for this matched
756       * values filter, if available.
757       *
758       * @return  The string representation of the assertion value for this matched
759       *          values filter, or {@code null} if there is none.
760       */
761      public String getAssertionValue()
762      {
763        if (assertionValue == null)
764        {
765          return null;
766        }
767        else
768        {
769          return assertionValue.stringValue();
770        }
771      }
772    
773    
774    
775      /**
776       * Retrieves the binary representation of the assertion value for this matched
777       * values filter, if available.
778       *
779       * @return  The binary representation of the assertion value for this matched
780       *          values filter, or {@code null} if there is none.
781       */
782      public byte[] getAssertionValueBytes()
783      {
784        if (assertionValue == null)
785        {
786          return null;
787        }
788        else
789        {
790          return assertionValue.getValue();
791        }
792      }
793    
794    
795    
796      /**
797       * Retrieves raw assertion value for this matched values filter, if available.
798       *
799       * @return  The raw assertion value for this matched values filter, or
800       *          {@code null} if there is none.
801       */
802      public ASN1OctetString getRawAssertionValue()
803      {
804        return assertionValue;
805      }
806    
807    
808    
809      /**
810       * Retrieves the string representation of the subInitial element for this
811       * matched values filter, if available.
812       *
813       * @return  The string representation of the subInitial element for this
814       *          matched values filter, or {@code null} if there is none.
815       */
816      public String getSubInitialValue()
817      {
818        if (subInitialValue == null)
819        {
820          return null;
821        }
822        else
823        {
824          return subInitialValue.stringValue();
825        }
826      }
827    
828    
829    
830      /**
831       * Retrieves the binary representation of the subInitial element for this
832       * matched values filter, if available.
833       *
834       * @return  The binary representation of the subInitial element for this
835       *          matched values filter, or {@code null} if there is none.
836       */
837      public byte[] getSubInitialValueBytes()
838      {
839        if (subInitialValue == null)
840        {
841          return null;
842        }
843        else
844        {
845          return subInitialValue.getValue();
846        }
847      }
848    
849    
850    
851      /**
852       * Retrieves the raw subInitial element for this matched values filter, if
853       * available.
854       *
855       * @return  The raw subInitial element for this matched values filter, or
856       *          {@code null} if there is none.
857       */
858      public ASN1OctetString getRawSubInitialValue()
859      {
860        return subInitialValue;
861      }
862    
863    
864    
865      /**
866       * Retrieves the string representations of the subAny elements for this
867       * matched values filter, if available.
868       *
869       * @return  The string representations of the subAny element for this matched
870       *          values filter, or an empty array if there are none.
871       */
872      public String[] getSubAnyValues()
873      {
874        if (subAnyValues.length == 0)
875        {
876          return NO_SUB_ANY_STRINGS;
877        }
878        else
879        {
880          final String[] subAnyStrings = new String[subAnyValues.length];
881          for (int i=0; i < subAnyValues.length; i++)
882          {
883            subAnyStrings[i] = subAnyValues[i].stringValue();
884          }
885    
886          return subAnyStrings;
887        }
888      }
889    
890    
891    
892      /**
893       * Retrieves the binary representations of the subAny elements for this
894       * matched values filter, if available.
895       *
896       * @return  The binary representations of the subAny element for this matched
897       *          values filter, or an empty array if there are none.
898       */
899      public byte[][] getSubAnyValueBytes()
900      {
901        if (subAnyValues.length == 0)
902        {
903          return NO_SUB_ANY_BYTES;
904        }
905        else
906        {
907          final byte[][] subAnyBytes = new byte[subAnyValues.length][];
908          for (int i=0; i < subAnyValues.length; i++)
909          {
910            subAnyBytes[i] = subAnyValues[i].getValue();
911          }
912    
913          return subAnyBytes;
914        }
915      }
916    
917    
918    
919      /**
920       * Retrieves the raw subAny elements for this matched values filter, if
921       * available.
922       *
923       * @return  The raw subAny element for this matched values filter, or an empty
924       *          array if there are none.
925       */
926      public ASN1OctetString[] getRawSubAnyValues()
927      {
928        return subAnyValues;
929      }
930    
931    
932    
933      /**
934       * Retrieves the string representation of the subFinal element for this
935       * matched values filter, if available.
936       *
937       * @return  The string representation of the subFinal element for this
938       *          matched values filter, or {@code null} if there is none.
939       */
940      public String getSubFinalValue()
941      {
942        if (subFinalValue == null)
943        {
944          return null;
945        }
946        else
947        {
948          return subFinalValue.stringValue();
949        }
950      }
951    
952    
953    
954      /**
955       * Retrieves the binary representation of the subFinal element for this
956       * matched values filter, if available.
957       *
958       * @return  The binary representation of the subFinal element for this matched
959       *          values filter, or {@code null} if there is none.
960       */
961      public byte[] getSubFinalValueBytes()
962      {
963        if (subFinalValue == null)
964        {
965          return null;
966        }
967        else
968        {
969          return subFinalValue.getValue();
970        }
971      }
972    
973    
974    
975      /**
976       * Retrieves the raw subFinal element for this matched values filter, if
977       * available.
978       *
979       * @return  The raw subFinal element for this matched values filter, or
980       *          {@code null} if there is none.
981       */
982      public ASN1OctetString getRawSubFinalValue()
983      {
984        return subFinalValue;
985      }
986    
987    
988    
989      /**
990       * Retrieves the matching rule ID for this matched values filter, if
991       * available.
992       *
993       * @return  The matching rule ID for this matched values filter, or
994       *          {@code null} if there is none.
995       */
996      public String getMatchingRuleID()
997      {
998        return matchingRuleID;
999      }
1000    
1001    
1002    
1003      /**
1004       * Encodes this matched values filter for use in the matched values control.
1005       *
1006       * @return  The ASN.1 element containing the encoded representation of this
1007       *          matched values filter.
1008       */
1009      public ASN1Element encode()
1010      {
1011        switch (matchType)
1012        {
1013          case MATCH_TYPE_EQUALITY:
1014          case MATCH_TYPE_GREATER_OR_EQUAL:
1015          case MATCH_TYPE_LESS_OR_EQUAL:
1016          case MATCH_TYPE_APPROXIMATE:
1017            ASN1Element[] elements =
1018            {
1019              new ASN1OctetString(attributeType),
1020              assertionValue
1021            };
1022            return new ASN1Sequence(matchType, elements);
1023    
1024          case MATCH_TYPE_SUBSTRINGS:
1025            final ArrayList<ASN1Element> subElements =
1026                 new ArrayList<ASN1Element>(3);
1027            if (subInitialValue != null)
1028            {
1029              subElements.add(subInitialValue);
1030            }
1031    
1032            if (subAnyValues.length > 0)
1033            {
1034              subElements.addAll(Arrays.asList(subAnyValues));
1035            }
1036    
1037            if (subFinalValue != null)
1038            {
1039              subElements.add(subFinalValue);
1040            }
1041    
1042            elements = new ASN1Element[]
1043            {
1044              new ASN1OctetString(attributeType),
1045              new ASN1Sequence(subElements)
1046            };
1047            return new ASN1Sequence(matchType, elements);
1048    
1049          case MATCH_TYPE_PRESENT:
1050            return new ASN1OctetString(matchType, attributeType);
1051    
1052          case MATCH_TYPE_EXTENSIBLE:
1053            final ArrayList<ASN1Element> extElements =
1054                 new ArrayList<ASN1Element>(3);
1055            if (attributeType != null)
1056            {
1057              extElements.add(new ASN1OctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME,
1058                                                  attributeType));
1059            }
1060    
1061            if (matchingRuleID != null)
1062            {
1063              extElements.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID,
1064                                                  matchingRuleID));
1065            }
1066    
1067            extElements.add(assertionValue);
1068            return new ASN1Sequence(matchType, extElements);
1069    
1070          default:
1071            // This should never happen.
1072            return null;
1073        }
1074      }
1075    
1076    
1077    
1078      /**
1079       * Decodes the provided ASN.1 element as a matched values filter.
1080       *
1081       * @param  element  The ASN.1 element to decode as a matched values filter.
1082       *
1083       * @return  The decoded matched values filter.
1084       *
1085       * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
1086       *                         a matched values filter.
1087       */
1088      public static MatchedValuesFilter decode(final ASN1Element element)
1089             throws LDAPException
1090      {
1091        ASN1OctetString   assertionValue  = null;
1092        ASN1OctetString   subInitialValue = null;
1093        ASN1OctetString   subFinalValue   = null;
1094        ASN1OctetString[] subAnyValues    = NO_SUB_ANY;
1095        final byte        matchType       = element.getType();
1096        String            attributeType   = null;
1097        String            matchingRuleID  = null;
1098    
1099        switch (matchType)
1100        {
1101          case MATCH_TYPE_EQUALITY:
1102          case MATCH_TYPE_GREATER_OR_EQUAL:
1103          case MATCH_TYPE_LESS_OR_EQUAL:
1104          case MATCH_TYPE_APPROXIMATE:
1105            try
1106            {
1107              final ASN1Element[] elements =
1108                   ASN1Sequence.decodeAsSequence(element).elements();
1109              attributeType =
1110                   ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
1111              assertionValue =
1112                   ASN1OctetString.decodeAsOctetString(elements[1]);
1113            }
1114            catch (Exception e)
1115            {
1116              debugException(e);
1117              throw new LDAPException(ResultCode.DECODING_ERROR,
1118                                      ERR_MV_FILTER_NOT_AVA.get(e), e);
1119            }
1120            break;
1121    
1122          case MATCH_TYPE_SUBSTRINGS:
1123            try
1124            {
1125              final ASN1Element[] elements =
1126                   ASN1Sequence.decodeAsSequence(element).elements();
1127              attributeType =
1128                   ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
1129    
1130              ArrayList<ASN1OctetString> subAnyList = null;
1131              final ASN1Element[] subElements =
1132                   ASN1Sequence.decodeAsSequence(elements[1]).elements();
1133              for (final ASN1Element e : subElements)
1134              {
1135                switch (e.getType())
1136                {
1137                  case SUBSTRING_TYPE_SUBINITIAL:
1138                    if (subInitialValue == null)
1139                    {
1140                      subInitialValue = ASN1OctetString.decodeAsOctetString(e);
1141                    }
1142                    else
1143                    {
1144                      throw new LDAPException(ResultCode.DECODING_ERROR,
1145                                     ERR_MV_FILTER_MULTIPLE_SUBINITIAL.get());
1146                    }
1147                    break;
1148    
1149                  case SUBSTRING_TYPE_SUBANY:
1150                    if (subAnyList == null)
1151                    {
1152                      subAnyList =
1153                           new ArrayList<ASN1OctetString>(subElements.length);
1154                    }
1155                    subAnyList.add(ASN1OctetString.decodeAsOctetString(e));
1156                    break;
1157    
1158                  case SUBSTRING_TYPE_SUBFINAL:
1159                    if (subFinalValue == null)
1160                    {
1161                      subFinalValue = ASN1OctetString.decodeAsOctetString(e);
1162                    }
1163                    else
1164                    {
1165                      throw new LDAPException(ResultCode.DECODING_ERROR,
1166                                     ERR_MV_FILTER_MULTIPLE_SUBFINAL.get());
1167                    }
1168                    break;
1169    
1170                  default:
1171                    throw new LDAPException(ResultCode.DECODING_ERROR,
1172                                            ERR_MV_FILTER_INVALID_SUB_TYPE.get(
1173                                                 toHex(e.getType())));
1174                }
1175              }
1176    
1177              if (subAnyList != null)
1178              {
1179                subAnyValues =
1180                     subAnyList.toArray(new ASN1OctetString[subAnyList.size()]);
1181              }
1182            }
1183            catch (LDAPException le)
1184            {
1185              debugException(le);
1186              throw le;
1187            }
1188            catch (Exception e)
1189            {
1190              debugException(e);
1191              throw new LDAPException(ResultCode.DECODING_ERROR,
1192                                      ERR_MV_FILTER_CANNOT_DECODE_SUBSTRING.get(e),
1193                                      e);
1194            }
1195    
1196            if ((subInitialValue == null) && (subAnyValues.length == 0) &&
1197                (subFinalValue == null))
1198            {
1199              throw new LDAPException(ResultCode.DECODING_ERROR,
1200                                      ERR_MV_FILTER_NO_SUBSTRING_ELEMENTS.get());
1201            }
1202            break;
1203    
1204          case MATCH_TYPE_PRESENT:
1205            attributeType =
1206                 ASN1OctetString.decodeAsOctetString(element).stringValue();
1207            break;
1208    
1209          case MATCH_TYPE_EXTENSIBLE:
1210            try
1211            {
1212              final ASN1Element[] elements =
1213                   ASN1Sequence.decodeAsSequence(element).elements();
1214              for (final ASN1Element e : elements)
1215              {
1216                switch (e.getType())
1217                {
1218                  case EXTENSIBLE_TYPE_ATTRIBUTE_NAME:
1219                    if (attributeType == null)
1220                    {
1221                      attributeType =
1222                           ASN1OctetString.decodeAsOctetString(e).stringValue();
1223                    }
1224                    else
1225                    {
1226                      throw new LDAPException(ResultCode.DECODING_ERROR,
1227                                              ERR_MV_FILTER_EXT_MULTIPLE_AT.get());
1228                    }
1229                    break;
1230    
1231                  case EXTENSIBLE_TYPE_MATCHING_RULE_ID:
1232                    if (matchingRuleID == null)
1233                    {
1234                      matchingRuleID =
1235                           ASN1OctetString.decodeAsOctetString(e).stringValue();
1236                    }
1237                    else
1238                    {
1239                      throw new LDAPException(ResultCode.DECODING_ERROR,
1240                                              ERR_MV_FILTER_MULTIPLE_MRID.get());
1241                    }
1242                    break;
1243    
1244                  case EXTENSIBLE_TYPE_MATCH_VALUE:
1245                    if (assertionValue == null)
1246                    {
1247                      assertionValue =
1248                           ASN1OctetString.decodeAsOctetString(e);
1249                    }
1250                    else
1251                    {
1252                      throw new LDAPException(ResultCode.DECODING_ERROR,
1253                                     ERR_MV_FILTER_EXT_MULTIPLE_VALUE.get());
1254                    }
1255                    break;
1256    
1257                  default:
1258                    throw new LDAPException(ResultCode.DECODING_ERROR,
1259                                            ERR_MV_FILTER_EXT_INVALID_TYPE.get(
1260                                                 toHex(e.getType())));
1261                }
1262              }
1263            }
1264            catch (LDAPException le)
1265            {
1266              debugException(le);
1267              throw le;
1268            }
1269            catch (Exception e)
1270            {
1271              debugException(e);
1272              throw new LDAPException(ResultCode.DECODING_ERROR,
1273                                      ERR_MV_FILTER_EXT_NOT_SEQUENCE.get(e), e);
1274            }
1275    
1276            if ((attributeType == null) && (matchingRuleID == null))
1277            {
1278              throw new LDAPException(ResultCode.DECODING_ERROR,
1279                                      ERR_MV_FILTER_NO_ATTR_OR_MRID.get());
1280            }
1281    
1282            if (assertionValue == null)
1283            {
1284              throw new LDAPException(ResultCode.DECODING_ERROR,
1285                                      ERR_MV_FILTER_EXT_NO_VALUE.get());
1286            }
1287            break;
1288    
1289          default:
1290            throw new LDAPException(ResultCode.DECODING_ERROR,
1291                                    ERR_MV_FILTER_INVALID_TYPE.get(
1292                                         toHex(matchType)));
1293        }
1294    
1295        return new MatchedValuesFilter(matchType, attributeType,  assertionValue,
1296                                       subInitialValue, subAnyValues, subFinalValue,
1297                                       matchingRuleID);
1298      }
1299    
1300    
1301    
1302      /**
1303       * Creates a search filter that is the equivalent of this matched values
1304       * filter.
1305       *
1306       * @return  A search filter that is the equivalent of this matched values
1307       *          filter.
1308       */
1309      public Filter toFilter()
1310      {
1311        switch (matchType)
1312        {
1313          case MATCH_TYPE_EQUALITY:
1314            return Filter.createEqualityFilter(attributeType,
1315                        assertionValue.getValue());
1316    
1317          case MATCH_TYPE_SUBSTRINGS:
1318            return Filter.createSubstringFilter(attributeType,
1319                        getSubInitialValueBytes(), getSubAnyValueBytes(),
1320                        getSubFinalValueBytes());
1321    
1322          case MATCH_TYPE_GREATER_OR_EQUAL:
1323            return Filter.createGreaterOrEqualFilter(attributeType,
1324                        assertionValue.getValue());
1325    
1326          case MATCH_TYPE_LESS_OR_EQUAL:
1327            return Filter.createLessOrEqualFilter(attributeType,
1328                        assertionValue.getValue());
1329    
1330          case MATCH_TYPE_PRESENT:
1331            return Filter.createPresenceFilter(attributeType);
1332    
1333          case MATCH_TYPE_APPROXIMATE:
1334            return Filter.createApproximateMatchFilter(attributeType,
1335                        assertionValue.getValue());
1336    
1337          case MATCH_TYPE_EXTENSIBLE:
1338            return Filter.createExtensibleMatchFilter(attributeType, matchingRuleID,
1339                        false, assertionValue.getValue());
1340    
1341          default:
1342            // This should never happen.
1343            return null;
1344        }
1345      }
1346    
1347    
1348    
1349      /**
1350       * Retrieves a string representation of this matched values filter.
1351       *
1352       * @return  A string representation of this matched values filter.
1353       */
1354      @Override()
1355      public String toString()
1356      {
1357        final StringBuilder buffer = new StringBuilder();
1358        toString(buffer);
1359        return buffer.toString();
1360      }
1361    
1362    
1363    
1364      /**
1365       * Appends a string representation of this matched values filter to the
1366       * provided buffer.
1367       *
1368       * @param  buffer  The buffer to which to append the string representation of
1369       *                 this matched values filter.
1370       */
1371      public void toString(final StringBuilder buffer)
1372      {
1373        buffer.append('(');
1374    
1375        switch (matchType)
1376        {
1377          case MATCH_TYPE_EQUALITY:
1378            buffer.append(attributeType);
1379            buffer.append('=');
1380            buffer.append(assertionValue.stringValue());
1381            break;
1382    
1383          case MATCH_TYPE_SUBSTRINGS:
1384            buffer.append(attributeType);
1385            buffer.append('=');
1386    
1387            if (subInitialValue != null)
1388            {
1389              buffer.append(subInitialValue.stringValue());
1390            }
1391    
1392            for (final ASN1OctetString s : subAnyValues)
1393            {
1394              buffer.append('*');
1395              buffer.append(s.stringValue());
1396            }
1397    
1398            buffer.append('*');
1399            if (subFinalValue != null)
1400            {
1401              buffer.append(subFinalValue.stringValue());
1402            }
1403            break;
1404    
1405          case MATCH_TYPE_GREATER_OR_EQUAL:
1406            buffer.append(attributeType);
1407            buffer.append(">=");
1408            buffer.append(assertionValue.stringValue());
1409            break;
1410    
1411          case MATCH_TYPE_LESS_OR_EQUAL:
1412            buffer.append(attributeType);
1413            buffer.append("<=");
1414            buffer.append(assertionValue.stringValue());
1415            break;
1416    
1417          case MATCH_TYPE_PRESENT:
1418            buffer.append(attributeType);
1419            buffer.append("=*");
1420            break;
1421    
1422          case MATCH_TYPE_APPROXIMATE:
1423            buffer.append(attributeType);
1424            buffer.append("~=");
1425            buffer.append(assertionValue.stringValue());
1426            break;
1427    
1428          case MATCH_TYPE_EXTENSIBLE:
1429            if (attributeType != null)
1430            {
1431              buffer.append(attributeType);
1432            }
1433    
1434            if (matchingRuleID != null)
1435            {
1436              buffer.append(':');
1437              buffer.append(matchingRuleID);
1438            }
1439    
1440            buffer.append(":=");
1441            buffer.append(assertionValue.stringValue());
1442            break;
1443        }
1444    
1445        buffer.append(')');
1446      }
1447    }