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