001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2014 Manuel Gaupp
026 *      Portions Copyright 2014-2015 ForgeRock AS
027 */
028package org.opends.server.controls;
029
030import java.io.IOException;
031import java.util.ArrayList;
032import java.util.List;
033
034import org.forgerock.i18n.LocalizableMessage;
035import org.forgerock.i18n.slf4j.LocalizedLogger;
036import org.forgerock.opendj.io.ASN1Reader;
037import org.forgerock.opendj.io.ASN1Writer;
038import org.forgerock.opendj.ldap.Assertion;
039import org.forgerock.opendj.ldap.ByteString;
040import org.forgerock.opendj.ldap.DecodeException;
041import org.forgerock.opendj.ldap.schema.MatchingRule;
042import org.forgerock.util.Reject;
043import org.opends.server.core.DirectoryServer;
044import org.opends.server.protocols.ldap.LDAPResultCode;
045import org.opends.server.types.AttributeType;
046import org.opends.server.types.LDAPException;
047import org.opends.server.types.RawFilter;
048
049import static org.opends.messages.ProtocolMessages.*;
050import static org.opends.server.protocols.ldap.LDAPConstants.*;
051import static org.opends.server.util.StaticUtils.*;
052
053/**
054 * This class defines a filter that may be used in conjunction with the matched
055 * values control to indicate which particular values of a multivalued attribute
056 * should be returned.  The matched values filter is essentially a subset of an
057 * LDAP search filter, lacking support for AND, OR, and NOT components, and
058 * lacking support for the dnAttributes component of extensible matching
059 * filters.
060 */
061public class MatchedValuesFilter
062{
063  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
064
065  /** The BER type associated with the equalityMatch filter type. */
066  public static final byte EQUALITY_MATCH_TYPE = (byte) 0xA3;
067  /** The BER type associated with the substrings filter type. */
068  public static final byte SUBSTRINGS_TYPE = (byte) 0xA4;
069  /** The BER type associated with the greaterOrEqual filter type. */
070  public static final byte GREATER_OR_EQUAL_TYPE = (byte) 0xA5;
071  /** The BER type associated with the lessOrEqual filter type. */
072  public static final byte LESS_OR_EQUAL_TYPE = (byte) 0xA6;
073  /** The BER type associated with the present filter type. */
074  public static final byte PRESENT_TYPE = (byte) 0x87;
075  /** The BER type associated with the approxMatch filter type. */
076  public static final byte APPROXIMATE_MATCH_TYPE = (byte) 0xA8;
077  /** The BER type associated with the extensibleMatch filter type. */
078  public static final byte EXTENSIBLE_MATCH_TYPE = (byte) 0xA9;
079
080  /** The matching rule ID for this matched values filter. */
081  private final String matchingRuleID;
082  /** Indicates whether the elements of this matched values filter have been fully decoded. */
083  private boolean decoded;
084  /** The match type for this matched values filter. */
085  private final byte matchType;
086
087  /** The raw, unprocessed attribute type for this matched values filter. */
088  private final String rawAttributeType;
089  /** The processed attribute type for this matched values filter. */
090  private AttributeType attributeType;
091
092  /** The matching rule for this matched values filter. */
093  private MatchingRule matchingRule;
094  /** The equality matching rule for this matched values filter. */
095  private MatchingRule equalityMatchingRule;
096  /** The ordering matching rule for this matched values filter. */
097  private MatchingRule orderingMatchingRule;
098  /** The substring matching rule for this matched values filter. */
099  private MatchingRule substringMatchingRule;
100  /** The approximate matching rule for this matched values filter. */
101  private MatchingRule approximateMatchingRule;
102
103  /** The raw, unprocessed assertion value for this matched values filter. */
104  private final ByteString rawAssertionValue;
105  /** The processed assertion value for this matched values filter. */
106  private ByteString assertionValue;
107  /** The assertion created from substring matching rule using values of this filter. */
108  private Assertion substringAssertion;
109  /** The subInitial value for this matched values filter. */
110  private final ByteString subInitial;
111  /** The set of subAny values for this matched values filter. */
112  private final List<ByteString> subAny;
113  /** The subFinal value for this matched values filter. */
114  private final ByteString subFinal;
115
116  /**
117   * Creates a new matched values filter with the provided information.
118   *
119   * @param  matchType          The match type for this matched values filter.
120   * @param  rawAttributeType   The raw, unprocessed attribute type.
121   * @param  rawAssertionValue  The raw, unprocessed assertion value.
122   * @param  subInitial         The subInitial element.
123   * @param  subAny             The set of subAny elements.
124   * @param  subFinal           The subFinal element.
125   * @param  matchingRuleID     The matching rule ID.
126   */
127  private MatchedValuesFilter(byte matchType, String rawAttributeType,
128                              ByteString rawAssertionValue,
129                              ByteString subInitial, List<ByteString> subAny,
130                              ByteString subFinal, String matchingRuleID)
131  {
132    this.matchType         = matchType;
133    this.rawAttributeType  = rawAttributeType;
134    this.rawAssertionValue = rawAssertionValue;
135    this.subInitial        = subInitial;
136    this.subAny            = subAny;
137    this.subFinal          = subFinal;
138    this.matchingRuleID    = matchingRuleID;
139  }
140
141
142
143  /**
144   * Creates a new equalityMatch filter with the provided information.
145   *
146   * @param  rawAttributeType   The raw, unprocessed attribute type.
147   * @param  rawAssertionValue  The raw, unprocessed assertion value.
148   *
149   * @return  The created equalityMatch filter.
150   */
151  public static MatchedValuesFilter createEqualityFilter(
152                                         String rawAttributeType,
153                                         ByteString rawAssertionValue)
154  {
155    Reject.ifNull(rawAttributeType,rawAssertionValue);
156
157    return new MatchedValuesFilter(EQUALITY_MATCH_TYPE, rawAttributeType,
158                                   rawAssertionValue, null, null, null, null);
159  }
160
161
162
163  /**
164   * Creates a new equalityMatch filter with the provided information.
165   *
166   * @param  attributeType   The attribute type.
167   * @param  assertionValue  The assertion value.
168   *
169   * @return  The created equalityMatch filter.
170   */
171  public static MatchedValuesFilter createEqualityFilter(
172                                         AttributeType attributeType,
173                                         ByteString assertionValue)
174  {
175    Reject.ifNull(attributeType, assertionValue);
176    String rawAttributeType = attributeType.getNameOrOID();
177
178    MatchedValuesFilter filter =
179         new MatchedValuesFilter(EQUALITY_MATCH_TYPE, rawAttributeType,
180                                 assertionValue, null, null, null, null);
181    filter.attributeType  = attributeType;
182    filter.assertionValue = assertionValue;
183
184    return filter;
185  }
186
187
188
189  /**
190   * Creates a new substrings filter with the provided information.
191   *
192   * @param  rawAttributeType  The raw, unprocessed attribute type.
193   * @param  subInitial        The subInitial element.
194   * @param  subAny            The set of subAny elements.
195   * @param  subFinal          The subFinal element.
196   *
197   * @return  The created substrings filter.
198   */
199  public static MatchedValuesFilter createSubstringsFilter(
200                                         String rawAttributeType,
201                                         ByteString subInitial,
202                                         List<ByteString> subAny,
203                                         ByteString subFinal)
204  {
205    Reject.ifNull(rawAttributeType);
206    return new MatchedValuesFilter(SUBSTRINGS_TYPE, rawAttributeType, null,
207                                   subInitial, subAny, subFinal, null);
208  }
209
210
211
212  /**
213   * Creates a new substrings filter with the provided information.
214   *
215   * @param  attributeType  The raw, unprocessed attribute type.
216   * @param  subInitial     The subInitial element.
217   * @param  subAny         The set of subAny elements.
218   * @param  subFinal       The subFinal element.
219   *
220   * @return  The created substrings filter.
221   */
222  public static MatchedValuesFilter createSubstringsFilter(
223                                         AttributeType attributeType,
224                                         ByteString subInitial,
225                                         List<ByteString> subAny,
226                                         ByteString subFinal)
227  {
228    Reject.ifNull(attributeType);
229    String rawAttributeType = attributeType.getNameOrOID();
230
231    MatchedValuesFilter filter =
232         new MatchedValuesFilter(SUBSTRINGS_TYPE, rawAttributeType, null,
233                                 subInitial, subAny, subFinal, null);
234    filter.attributeType  = attributeType;
235
236    return filter;
237  }
238
239
240
241  /**
242   * Creates a new greaterOrEqual filter with the provided information.
243   *
244   * @param  rawAttributeType   The raw, unprocessed attribute type.
245   * @param  rawAssertionValue  The raw, unprocessed assertion value.
246   *
247   * @return  The created greaterOrEqual filter.
248   */
249  public static MatchedValuesFilter createGreaterOrEqualFilter(
250                                         String rawAttributeType,
251                                         ByteString rawAssertionValue)
252  {
253   Reject.ifNull(rawAttributeType, rawAssertionValue);
254
255    return new MatchedValuesFilter(GREATER_OR_EQUAL_TYPE, rawAttributeType,
256                                   rawAssertionValue, null, null, null, null);
257  }
258
259
260
261  /**
262   * Creates a new greaterOrEqual filter with the provided information.
263   *
264   * @param  attributeType   The attribute type.
265   * @param  assertionValue  The assertion value.
266   *
267   * @return  The created greaterOrEqual filter.
268   */
269  public static MatchedValuesFilter createGreaterOrEqualFilter(
270                                         AttributeType attributeType,
271                                         ByteString assertionValue)
272  {
273    Reject.ifNull(attributeType, assertionValue);
274
275    String          rawAttributeType  = attributeType.getNameOrOID();
276
277    MatchedValuesFilter filter =
278         new MatchedValuesFilter(GREATER_OR_EQUAL_TYPE, rawAttributeType,
279                                 assertionValue, null, null, null, null);
280    filter.attributeType  = attributeType;
281    filter.assertionValue = assertionValue;
282
283    return filter;
284  }
285
286
287
288  /**
289   * Creates a new lessOrEqual filter with the provided information.
290   *
291   * @param  rawAttributeType   The raw, unprocessed attribute type.
292   * @param  rawAssertionValue  The raw, unprocessed assertion value.
293   *
294   * @return  The created lessOrEqual filter.
295   */
296  public static MatchedValuesFilter createLessOrEqualFilter(
297                                         String rawAttributeType,
298                                         ByteString rawAssertionValue)
299  {
300    Reject.ifNull(rawAttributeType, rawAssertionValue);
301    return new MatchedValuesFilter(LESS_OR_EQUAL_TYPE, rawAttributeType,
302                                   rawAssertionValue, null, null, null, null);
303  }
304
305
306
307  /**
308   * Creates a new lessOrEqual filter with the provided information.
309   *
310   * @param  attributeType   The attribute type.
311   * @param  assertionValue  The assertion value.
312   *
313   * @return  The created lessOrEqual filter.
314   */
315  public static MatchedValuesFilter createLessOrEqualFilter(
316                                         AttributeType attributeType,
317                                         ByteString assertionValue)
318  {
319    Reject.ifNull(attributeType, assertionValue);
320
321    String          rawAttributeType = attributeType.getNameOrOID();
322
323    MatchedValuesFilter filter =
324         new MatchedValuesFilter(LESS_OR_EQUAL_TYPE, rawAttributeType,
325                                 assertionValue, null, null, null, null);
326    filter.attributeType  = attributeType;
327    filter.assertionValue = assertionValue;
328
329    return filter;
330  }
331
332
333
334  /**
335   * Creates a new present filter with the provided information.
336   *
337   * @param  rawAttributeType  The raw, unprocessed attribute type.
338   *
339   * @return  The created present filter.
340   */
341  public static MatchedValuesFilter createPresentFilter(String rawAttributeType)
342  {
343    Reject.ifNull(rawAttributeType) ;
344    return new MatchedValuesFilter(PRESENT_TYPE, rawAttributeType, null, null,
345                                   null, null, null);
346  }
347
348
349
350  /**
351   * Creates a new present filter with the provided information.
352   *
353   * @param  attributeType  The attribute type.
354   *
355   * @return  The created present filter.
356   */
357  public static MatchedValuesFilter createPresentFilter(
358                                         AttributeType attributeType)
359  {
360    Reject.ifNull(attributeType);
361    String rawAttributeType = attributeType.getNameOrOID();
362
363    MatchedValuesFilter filter =
364         new MatchedValuesFilter(PRESENT_TYPE, rawAttributeType, null, null,
365                                 null, null, null);
366    filter.attributeType  = attributeType;
367
368    return filter;
369  }
370
371
372
373  /**
374   * Creates a new approxMatch filter with the provided information.
375   *
376   * @param  rawAttributeType   The raw, unprocessed attribute type.
377   * @param  rawAssertionValue  The raw, unprocessed assertion value.
378   *
379   * @return  The created approxMatch filter.
380   */
381  public static MatchedValuesFilter createApproximateFilter(
382                                         String rawAttributeType,
383                                         ByteString rawAssertionValue)
384  {
385    Reject.ifNull(rawAttributeType,rawAssertionValue);
386
387    return new MatchedValuesFilter(APPROXIMATE_MATCH_TYPE, rawAttributeType,
388                                   rawAssertionValue, null, null, null, null);
389  }
390
391
392
393  /**
394   * Creates a new approxMatch filter with the provided information.
395   *
396   * @param  attributeType   The attribute type.
397   * @param  assertionValue  The assertion value.
398   *
399   * @return  The created approxMatch filter.
400   */
401  public static MatchedValuesFilter createApproximateFilter(
402                                         AttributeType attributeType,
403                                         ByteString assertionValue)
404  {
405    Reject.ifNull(attributeType,assertionValue);
406    String          rawAttributeType  = attributeType.getNameOrOID();
407
408    MatchedValuesFilter filter =
409         new MatchedValuesFilter(APPROXIMATE_MATCH_TYPE, rawAttributeType,
410                                 assertionValue, null, null, null, null);
411    filter.attributeType  = attributeType;
412    filter.assertionValue = assertionValue;
413
414    return filter;
415  }
416
417
418
419  /**
420   * Creates a new extensibleMatch filter with the provided information.
421   *
422   * @param  rawAttributeType   The raw, unprocessed attribute type.
423   * @param  matchingRuleID     The matching rule ID.
424   * @param  rawAssertionValue  The raw, unprocessed assertion value.
425   *
426   * @return  The created extensibleMatch filter.
427   */
428  public static MatchedValuesFilter createExtensibleMatchFilter(
429                                         String rawAttributeType,
430                                         String matchingRuleID,
431                                         ByteString rawAssertionValue)
432  {
433    Reject.ifNull(rawAttributeType, matchingRuleID, rawAssertionValue);
434    return new MatchedValuesFilter(EXTENSIBLE_MATCH_TYPE, rawAttributeType,
435                                   rawAssertionValue, null, null, null,
436                                   matchingRuleID);
437  }
438
439
440
441  /**
442   * Creates a new extensibleMatch filter with the provided information.
443   *
444   * @param  attributeType   The attribute type.
445   * @param  matchingRule    The matching rule.
446   * @param  assertionValue  The assertion value.
447   *
448   * @return  The created extensibleMatch filter.
449   */
450  public static MatchedValuesFilter createExtensibleMatchFilter(
451                                         AttributeType attributeType,
452                                         MatchingRule matchingRule,
453                                         ByteString assertionValue)
454  {
455    Reject.ifNull(attributeType, matchingRule, assertionValue);
456    String rawAttributeType = attributeType.getNameOrOID();
457    String matchingRuleID = matchingRule.getOID();
458
459    MatchedValuesFilter filter =
460         new MatchedValuesFilter(EXTENSIBLE_MATCH_TYPE, rawAttributeType,
461                                 assertionValue, null, null, null,
462                                 matchingRuleID);
463    filter.attributeType  = attributeType;
464    filter.assertionValue = assertionValue;
465    filter.matchingRule   = matchingRule;
466
467    return filter;
468  }
469
470
471
472  /**
473   * Creates a new matched values filter from the provided LDAP filter.
474   *
475   * @param  filter  The LDAP filter to use for this matched values filter.
476   *
477   * @return  The corresponding matched values filter.
478   *
479   * @throws  LDAPException  If the provided LDAP filter cannot be treated as a
480   *                         matched values filter.
481   */
482  public static MatchedValuesFilter createFromLDAPFilter(RawFilter filter)
483         throws LDAPException
484  {
485    switch (filter.getFilterType())
486    {
487      case AND:
488      case OR:
489      case NOT:
490        // These filter types cannot be used in a matched values filter.
491        LocalizableMessage message = ERR_MVFILTER_INVALID_LDAP_FILTER_TYPE.get(
492            filter, filter.getFilterType());
493        throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
494
495
496      case EQUALITY:
497        return new MatchedValuesFilter(EQUALITY_MATCH_TYPE,
498                                       filter.getAttributeType(),
499                                       filter.getAssertionValue(), null, null,
500                                       null, null);
501
502
503      case SUBSTRING:
504        return new MatchedValuesFilter(SUBSTRINGS_TYPE,
505                                       filter.getAttributeType(), null,
506                                       filter.getSubInitialElement(),
507                                       filter.getSubAnyElements(),
508                                       filter.getSubFinalElement(), null);
509
510
511      case GREATER_OR_EQUAL:
512        return new MatchedValuesFilter(GREATER_OR_EQUAL_TYPE,
513                                       filter.getAttributeType(),
514                                       filter.getAssertionValue(), null, null,
515                                       null, null);
516
517
518      case LESS_OR_EQUAL:
519        return new MatchedValuesFilter(LESS_OR_EQUAL_TYPE,
520                                       filter.getAttributeType(),
521                                       filter.getAssertionValue(), null, null,
522                                       null, null);
523
524
525      case PRESENT:
526        return new MatchedValuesFilter(PRESENT_TYPE, filter.getAttributeType(),
527                                       null, null, null, null, null);
528
529
530      case APPROXIMATE_MATCH:
531        return new MatchedValuesFilter(APPROXIMATE_MATCH_TYPE,
532                                       filter.getAttributeType(),
533                                       filter.getAssertionValue(), null, null,
534                                       null, null);
535
536
537      case EXTENSIBLE_MATCH:
538        if (filter.getDNAttributes())
539        {
540          // This cannot be represented in a matched values filter.
541          message = ERR_MVFILTER_INVALID_DN_ATTRIBUTES_FLAG.get(filter);
542          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
543        }
544        else
545        {
546          return new MatchedValuesFilter(EXTENSIBLE_MATCH_TYPE,
547                                         filter.getAttributeType(),
548                                         filter.getAssertionValue(), null, null,
549                                         null, filter.getMatchingRuleID());
550        }
551
552
553      default:
554        message = ERR_MVFILTER_INVALID_LDAP_FILTER_TYPE.get(filter, filter.getFilterType());
555        throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
556    }
557  }
558
559  /**
560   * Encodes this matched values filter as an ASN.1 element.
561   *
562   * @param writer The ASN1Writer to use to encode this matched values filter.
563   * @throws IOException if an error occurs while encoding.
564   */
565  public void encode(ASN1Writer writer) throws IOException
566  {
567    switch (matchType)
568    {
569      case EQUALITY_MATCH_TYPE:
570      case GREATER_OR_EQUAL_TYPE:
571      case LESS_OR_EQUAL_TYPE:
572      case APPROXIMATE_MATCH_TYPE:
573        // These will all be encoded in the same way.
574        writer.writeStartSequence(matchType);
575        writer.writeOctetString(rawAttributeType);
576        writer.writeOctetString(rawAssertionValue);
577        writer.writeEndSequence();
578        return;
579
580      case SUBSTRINGS_TYPE:
581        writer.writeStartSequence(matchType);
582        writer.writeOctetString(rawAttributeType);
583
584        writer.writeStartSequence();
585        if (subInitial != null)
586        {
587          writer.writeOctetString(TYPE_SUBINITIAL, subInitial);
588        }
589
590        if (subAny != null)
591        {
592          for (ByteString s : subAny)
593          {
594            writer.writeOctetString(TYPE_SUBANY, s);
595          }
596        }
597
598        if (subFinal != null)
599        {
600          writer.writeOctetString(TYPE_SUBFINAL, subFinal);
601        }
602        writer.writeEndSequence();
603
604        writer.writeEndSequence();
605        return;
606
607      case PRESENT_TYPE:
608        writer.writeOctetString(matchType, rawAttributeType);
609        return;
610
611      case EXTENSIBLE_MATCH_TYPE:
612        writer.writeStartSequence(matchType);
613        if (matchingRuleID != null)
614        {
615          writer.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRuleID);
616        }
617
618        if (rawAttributeType != null)
619        {
620          writer.writeOctetString(TYPE_MATCHING_RULE_TYPE, rawAttributeType);
621        }
622        writer.writeOctetString(TYPE_MATCHING_RULE_VALUE, rawAssertionValue);
623        writer.writeEndSequence();
624        return;
625
626
627      default:
628    }
629  }
630
631    /**
632   * Decodes the provided ASN.1 element as a matched values filter item.
633   *
634   * @param  reader The ASN.1 reader.
635   *
636   * @return  The decoded matched values filter.
637   *
638   * @throws  LDAPException  If a problem occurs while attempting to decode the
639   *                         filter item.
640   */
641  public static MatchedValuesFilter decode(ASN1Reader reader)
642         throws LDAPException
643  {
644    byte type;
645    try
646    {
647      type = reader.peekType();
648    }
649    catch(Exception e)
650    {
651      // TODO: Need a better message.
652      throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR,
653          ERR_MVFILTER_INVALID_ELEMENT_TYPE.get(e));
654    }
655
656    switch (type)
657    {
658      case EQUALITY_MATCH_TYPE:
659      case GREATER_OR_EQUAL_TYPE:
660      case LESS_OR_EQUAL_TYPE:
661      case APPROXIMATE_MATCH_TYPE:
662        // These will all be decoded in the same manner.  The element must be a
663        // sequence consisting of the attribute type and assertion value.
664        try
665        {
666          reader.readStartSequence();
667          String rawAttributeType = reader.readOctetStringAsString();
668          ByteString rawAssertionValue = reader.readOctetString();
669          reader.readEndSequence();
670          return new MatchedValuesFilter(type, rawAttributeType,
671              rawAssertionValue, null, null, null, null);
672        }
673        catch (Exception e)
674        {
675          logger.traceException(e);
676
677          LocalizableMessage message =
678              ERR_MVFILTER_CANNOT_DECODE_AVA.get(getExceptionMessage(e));
679          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
680        }
681
682
683      case SUBSTRINGS_TYPE:
684        // This must be a sequence of two elements, where the second is a
685        // sequence of substring types.
686        try
687        {
688          reader.readStartSequence();
689          String rawAttributeType = reader.readOctetStringAsString();
690
691          reader.readStartSequence();
692          if(!reader.hasNextElement())
693          {
694            LocalizableMessage message = ERR_MVFILTER_NO_SUBSTRING_ELEMENTS.get();
695            throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
696          }
697
698          ByteString subInitial        = null;
699          ArrayList<ByteString> subAny = null;
700          ByteString subFinal          = null;
701
702          if(reader.hasNextElement() &&
703              reader.peekType() == TYPE_SUBINITIAL)
704          {
705            subInitial = reader.readOctetString();
706          }
707          while(reader.hasNextElement() &&
708              reader.peekType() == TYPE_SUBANY)
709          {
710            if(subAny == null)
711            {
712              subAny = new ArrayList<>();
713            }
714            subAny.add(reader.readOctetString());
715          }
716          if(reader.hasNextElement() &&
717              reader.peekType() == TYPE_SUBFINAL)
718          {
719            subFinal = reader.readOctetString();
720          }
721          reader.readEndSequence();
722
723          reader.readEndSequence();
724
725          return new MatchedValuesFilter(type, rawAttributeType,
726                                         null, subInitial, subAny, subFinal, null);
727        }
728        catch (LDAPException le)
729        {
730          throw le;
731        }
732        catch (Exception e)
733        {
734          logger.traceException(e);
735
736          LocalizableMessage message =
737              ERR_MVFILTER_CANNOT_DECODE_SUBSTRINGS.get(getExceptionMessage(e));
738          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
739        }
740
741
742      case PRESENT_TYPE:
743        // The element must be an ASN.1 octet string holding the attribute type.
744        try
745        {
746          String rawAttributeType = reader.readOctetStringAsString();
747
748          return new MatchedValuesFilter(type, rawAttributeType,
749                                         null, null, null, null, null);
750        }
751        catch (Exception e)
752        {
753          logger.traceException(e);
754
755          LocalizableMessage message = ERR_MVFILTER_CANNOT_DECODE_PRESENT_TYPE.get(
756              getExceptionMessage(e));
757          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
758        }
759
760
761      case EXTENSIBLE_MATCH_TYPE:
762        // This must be a two or three element sequence with an assertion value
763        // as the last element and an attribute type and/or matching rule ID as
764        // the first element(s).
765        try
766        {
767          reader.readStartSequence();
768
769          String     rawAttributeType  = null;
770          String     matchingRuleID    = null;
771
772          if(reader.peekType() == TYPE_MATCHING_RULE_ID)
773          {
774            matchingRuleID = reader.readOctetStringAsString();
775          }
776          if(matchingRuleID == null ||
777              reader.peekType() == TYPE_MATCHING_RULE_TYPE)
778          {
779             rawAttributeType = reader.readOctetStringAsString();
780          }
781          ByteString rawAssertionValue = reader.readOctetString();
782          reader.readEndSequence();
783
784          return new MatchedValuesFilter(type, rawAttributeType,
785                                         rawAssertionValue, null, null, null,
786                                         matchingRuleID);
787        }
788        catch (Exception e)
789        {
790          logger.traceException(e);
791
792          LocalizableMessage message = ERR_MVFILTER_CANNOT_DECODE_EXTENSIBLE_MATCH.get(
793              getExceptionMessage(e));
794          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
795        }
796
797
798      default:
799        LocalizableMessage message =
800            ERR_MVFILTER_INVALID_ELEMENT_TYPE.get(byteToHex(type));
801        throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
802    }
803  }
804
805
806
807  /**
808   * Retrieves the match type for this matched values filter.
809   *
810   * @return  The match type for this matched values filter.
811   */
812  public byte getMatchType()
813  {
814    return matchType;
815  }
816
817
818
819  /**
820   * Retrieves the raw, unprocessed attribute type for this matched values
821   * filter.
822   *
823   * @return  The raw, unprocessed attribute type for this matched values
824   *          filter, or <CODE>null</CODE> if there is none.
825   */
826  public String getRawAttributeType()
827  {
828    return rawAttributeType;
829  }
830
831
832  /**
833   * Retrieves the attribute type for this matched values filter.
834   *
835   * @return  The attribute type for this matched values filter, or
836   *          <CODE>null</CODE> if there is none.
837   */
838  public AttributeType getAttributeType()
839  {
840    if (attributeType == null && rawAttributeType != null)
841    {
842      String lowerName = toLowerCase(rawAttributeType);
843      attributeType = DirectoryServer.getAttributeTypeOrDefault(lowerName, rawAttributeType);
844    }
845    return attributeType;
846  }
847
848
849  /**
850   * Retrieves the raw, unprocessed assertion value for this matched values
851   * filter.
852   *
853   * @return  The raw, unprocessed assertion value for this matched values
854   *          filter, or <CODE>null</CODE> if there is none.
855   */
856  public ByteString getRawAssertionValue()
857  {
858    return rawAssertionValue;
859  }
860
861
862
863  /**
864   * Retrieves the assertion value for this matched values filter.
865   *
866   * @return  The assertion value for this matched values filter, or
867   *          <CODE>null</CODE> if there is none.
868   */
869  public ByteString getAssertionValue()
870  {
871    if (assertionValue == null && rawAssertionValue != null)
872    {
873      assertionValue = rawAssertionValue;
874    }
875    return assertionValue;
876  }
877
878
879
880  /**
881   * Retrieves the subInitial element for this matched values filter.
882   *
883   * @return  The subInitial element for this matched values filter, or
884   *          <CODE>null</CODE> if there is none.
885   */
886  public ByteString getSubInitialElement()
887  {
888    return subInitial;
889  }
890
891  private Assertion getSubstringAssertion() {
892    if (substringAssertion == null)
893    {
894      try
895      {
896        MatchingRule rule = getSubstringMatchingRule();
897        if (rule != null)
898        {
899          substringAssertion = rule.getSubstringAssertion(subInitial, subAny, subFinal);
900        }
901      }
902      catch (DecodeException e)
903      {
904        logger.traceException(e);
905      }
906    }
907    return substringAssertion;
908  }
909
910
911
912  /**
913   * Retrieves the set of subAny elements for this matched values filter.
914   *
915   * @return  The set of subAny elements for this matched values filter.  If
916   *          there are none, then the return value may be either
917   *          <CODE>null</CODE> or an empty list.
918   */
919  public List<ByteString> getSubAnyElements()
920  {
921    return subAny;
922  }
923
924  /**
925   * Retrieves the subFinal element for this matched values filter.
926   *
927   * @return  The subFinal element for this matched values filter, or
928   *          <CODE>null</CODE> if there is none.
929   */
930  public ByteString getSubFinalElement()
931  {
932    return subFinal;
933  }
934
935  /**
936   * Retrieves the matching rule ID for this matched values filter.
937   *
938   * @return  The matching rule ID for this matched values filter, or
939   *          <CODE>null</CODE> if there is none.
940   */
941  public String getMatchingRuleID()
942  {
943    return matchingRuleID;
944  }
945
946
947
948  /**
949   * Retrieves the matching rule for this matched values filter.
950   *
951   * @return  The matching rule for this matched values filter, or
952   *          <CODE>null</CODE> if there is none.
953   */
954  public MatchingRule getMatchingRule()
955  {
956    if (matchingRule == null && matchingRuleID != null)
957    {
958      matchingRule = DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID));
959    }
960    return matchingRule;
961  }
962
963
964
965  /**
966   * Retrieves the approximate matching rule that should be used for this
967   * matched values filter.
968   *
969   * @return  The approximate matching rule that should be used for this matched
970   *          values filter, or <CODE>null</CODE> if there is none.
971   */
972  public MatchingRule getApproximateMatchingRule()
973  {
974    if (approximateMatchingRule == null)
975    {
976      AttributeType attrType = getAttributeType();
977      if (attrType != null)
978      {
979        approximateMatchingRule = attrType.getApproximateMatchingRule();
980      }
981    }
982
983    return approximateMatchingRule;
984  }
985
986
987
988  /**
989   * Retrieves the equality matching rule that should be used for this matched
990   * values filter.
991   *
992   * @return  The equality matching rule that should be used for this matched
993   *          values filter, or <CODE>null</CODE> if there is none.
994   */
995  public MatchingRule getEqualityMatchingRule()
996  {
997    if (equalityMatchingRule == null)
998    {
999      AttributeType attrType = getAttributeType();
1000      if (attrType != null)
1001      {
1002        equalityMatchingRule = attrType.getEqualityMatchingRule();
1003      }
1004    }
1005
1006    return equalityMatchingRule;
1007  }
1008
1009
1010
1011  /**
1012   * Retrieves the ordering matching rule that should be used for this matched
1013   * values filter.
1014   *
1015   * @return  The ordering matching rule that should be used for this matched
1016   *          values filter, or <CODE>null</CODE> if there is none.
1017   */
1018  public MatchingRule getOrderingMatchingRule()
1019  {
1020    if (orderingMatchingRule == null)
1021    {
1022      AttributeType attrType = getAttributeType();
1023      if (attrType != null)
1024      {
1025        orderingMatchingRule = attrType.getOrderingMatchingRule();
1026      }
1027    }
1028
1029    return orderingMatchingRule;
1030  }
1031
1032
1033
1034  /**
1035   * Retrieves the substring matching rule that should be used for this matched
1036   * values filter.
1037   *
1038   * @return  The substring matching rule that should be used for this matched
1039   *          values filter, or <CODE>null</CODE> if there is none.
1040   */
1041  public MatchingRule getSubstringMatchingRule()
1042  {
1043    if (substringMatchingRule == null)
1044    {
1045      AttributeType attrType = getAttributeType();
1046      if (attrType != null)
1047      {
1048        substringMatchingRule = attrType.getSubstringMatchingRule();
1049      }
1050    }
1051
1052    return substringMatchingRule;
1053  }
1054
1055
1056
1057  /**
1058   * Decodes all components of the matched values filter so that they can be
1059   * referenced as member variables.
1060   */
1061  private void fullyDecode()
1062  {
1063    if (! decoded)
1064    {
1065      getAttributeType();
1066      getAssertionValue();
1067      getSubstringAssertion();
1068      getMatchingRule();
1069      getApproximateMatchingRule();
1070      getEqualityMatchingRule();
1071      getOrderingMatchingRule();
1072      getSubstringMatchingRule();
1073      decoded = true;
1074    }
1075  }
1076
1077
1078
1079  /**
1080   * Indicates whether the specified attribute value matches the criteria
1081   * defined in this matched values filter.
1082   *
1083   * @param  type   The attribute type with which the provided value is
1084   *                associated.
1085   * @param  value  The attribute value for which to make the determination.
1086   *
1087   * @return  <CODE>true</CODE> if the specified attribute value matches the
1088   *          criteria defined in this matched values filter, or
1089   *          <CODE>false</CODE> if not.
1090   */
1091  public boolean valueMatches(AttributeType type, ByteString value)
1092  {
1093    fullyDecode();
1094
1095    switch (matchType)
1096    {
1097      case EQUALITY_MATCH_TYPE:
1098        if (attributeType != null
1099            && attributeType.equals(type)
1100            && rawAssertionValue != null
1101            && value != null
1102            && equalityMatchingRule != null)
1103        {
1104          return matches(equalityMatchingRule, value, rawAssertionValue);
1105        }
1106        return false;
1107
1108
1109      case SUBSTRINGS_TYPE:
1110        if (attributeType != null
1111            && attributeType.equals(type)
1112            && substringAssertion != null)
1113        {
1114          try
1115          {
1116            return substringAssertion.matches(substringMatchingRule.normalizeAttributeValue(value)).toBoolean();
1117          }
1118          catch (Exception e)
1119          {
1120            logger.traceException(e);
1121          }
1122        }
1123        return false;
1124
1125
1126      case GREATER_OR_EQUAL_TYPE:
1127        if (attributeType != null
1128            && attributeType.equals(type)
1129            && assertionValue != null
1130            && value != null
1131            && orderingMatchingRule != null)
1132        {
1133          try
1134          {
1135            ByteString normValue = orderingMatchingRule.normalizeAttributeValue(value);
1136            Assertion assertion = orderingMatchingRule.getGreaterOrEqualAssertion(assertionValue);
1137            return assertion.matches(normValue).toBoolean();
1138          }
1139          catch (DecodeException e)
1140          {
1141            logger.traceException(e);
1142          }
1143        }
1144        return false;
1145
1146
1147      case LESS_OR_EQUAL_TYPE:
1148        if (attributeType != null
1149            && attributeType.equals(type)
1150            && assertionValue != null
1151            && value != null
1152            && orderingMatchingRule != null)
1153        {
1154          try
1155          {
1156            ByteString normValue = orderingMatchingRule.normalizeAttributeValue(value);
1157            Assertion assertion = orderingMatchingRule.getLessOrEqualAssertion(assertionValue);
1158            return assertion.matches(normValue).toBoolean();
1159          }
1160          catch (DecodeException e)
1161          {
1162            logger.traceException(e);
1163          }
1164        }
1165        return false;
1166
1167
1168      case PRESENT_TYPE:
1169        return attributeType != null && attributeType.equals(type);
1170
1171
1172      case APPROXIMATE_MATCH_TYPE:
1173        if (attributeType != null
1174            && attributeType.equals(type)
1175            && assertionValue != null
1176            && value != null
1177            && approximateMatchingRule != null)
1178        {
1179          return matches(approximateMatchingRule, value, assertionValue);
1180        }
1181        return false;
1182
1183
1184      case EXTENSIBLE_MATCH_TYPE:
1185        if (attributeType == null)
1186        {
1187          return matches(matchingRule, value, assertionValue);
1188        }
1189        else if (!attributeType.equals(type))
1190        {
1191          return false;
1192        }
1193        return matches(equalityMatchingRule, value, rawAssertionValue);
1194
1195
1196      default:
1197        return false;
1198    }
1199  }
1200
1201  private boolean matches(MatchingRule matchingRule, ByteString value, ByteString assertionValue)
1202  {
1203    if (matchingRule == null || value == null || assertionValue == null)
1204    {
1205      return false;
1206    }
1207
1208    try
1209    {
1210      ByteString normValue = matchingRule.normalizeAttributeValue(value);
1211      Assertion assertion = matchingRule.getAssertion(assertionValue);
1212      return assertion.matches(normValue).toBoolean();
1213    }
1214    catch (DecodeException e)
1215    {
1216      logger.traceException(e);
1217      return false;
1218    }
1219  }
1220
1221  /**
1222   * Retrieves a string representation of this matched values filter, as an RFC
1223   * 2254-compliant filter string.
1224   *
1225   * @return  A string representation of this matched values filter.
1226   */
1227  @Override
1228  public String toString()
1229  {
1230    StringBuilder buffer = new StringBuilder();
1231    toString(buffer);
1232    return buffer.toString();
1233  }
1234
1235
1236
1237  /**
1238   * Appends a string representation of this matched values filter, as an RFC
1239   * 2254-compliant filter string, to the provided buffer.
1240   *
1241   * @param  buffer  The buffer to which the filter string should be appended.
1242   */
1243  public void toString(StringBuilder buffer)
1244  {
1245    switch (matchType)
1246    {
1247      case EQUALITY_MATCH_TYPE:
1248        appendAttributeTypeAndAssertion(buffer, "=");
1249        break;
1250
1251
1252      case SUBSTRINGS_TYPE:
1253        buffer.append("(");
1254        buffer.append(rawAttributeType);
1255        buffer.append("=");
1256        if (subInitial != null)
1257        {
1258          RawFilter.valueToFilterString(buffer, subInitial);
1259        }
1260
1261        if (subAny != null)
1262        {
1263          for (ByteString s : subAny)
1264          {
1265            buffer.append("*");
1266            RawFilter.valueToFilterString(buffer, s);
1267          }
1268        }
1269
1270        buffer.append("*");
1271        if (subFinal != null)
1272        {
1273          RawFilter.valueToFilterString(buffer, subFinal);
1274        }
1275        buffer.append(")");
1276        break;
1277
1278
1279      case GREATER_OR_EQUAL_TYPE:
1280        appendAttributeTypeAndAssertion(buffer, ">=");
1281        break;
1282
1283
1284      case LESS_OR_EQUAL_TYPE:
1285        appendAttributeTypeAndAssertion(buffer, "<=");
1286        break;
1287
1288
1289      case PRESENT_TYPE:
1290        buffer.append("(");
1291        buffer.append(rawAttributeType);
1292        buffer.append("=*)");
1293        break;
1294
1295
1296      case APPROXIMATE_MATCH_TYPE:
1297        appendAttributeTypeAndAssertion(buffer, "~=");
1298        break;
1299
1300
1301      case EXTENSIBLE_MATCH_TYPE:
1302        buffer.append("(");
1303
1304        if (rawAttributeType != null)
1305        {
1306          buffer.append(rawAttributeType);
1307        }
1308
1309        if (matchingRuleID != null)
1310        {
1311          buffer.append(":");
1312          buffer.append(matchingRuleID);
1313        }
1314
1315        buffer.append(":=");
1316        RawFilter.valueToFilterString(buffer, rawAssertionValue);
1317        buffer.append(")");
1318        break;
1319    }
1320  }
1321
1322  private void appendAttributeTypeAndAssertion(StringBuilder buffer, String operator)
1323  {
1324    buffer.append("(");
1325    buffer.append(rawAttributeType);
1326    buffer.append(operator);
1327    RawFilter.valueToFilterString(buffer, rawAssertionValue);
1328    buffer.append(")");
1329  }
1330}
1331