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