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