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