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